/* Database access module for HTTP server. * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * Parts written by Andrew Kempe and others. * This program is free but copyrighted software; see the file COPYING for * details. */ #include "services.h" #include "modules.h" #include "language.h" #include "conffile.h" #include "modules/operserv/maskdata.h" #include "modules/operserv/akill.h" #include "modules/operserv/news.h" #include "modules/operserv/sessions.h" #include "modules/operserv/sline.h" #include "modules/nickserv/nickserv.h" #include "modules/chanserv/chanserv.h" #include "modules/chanserv/access.h" #include "modules/statserv/statserv.h" #include "modules/misc/xml.h" #include "http.h" /*************************************************************************/ static Module *module; static Module *module_httpd; static Module *module_operserv; static Module *module_operserv_akill; static Module *module_operserv_news; static Module *module_operserv_sessions; static Module *module_operserv_sline; static Module *module_nickserv; static Module *module_chanserv; static Module *module_statserv; static Module *module_xml_export; static char *Prefix; int Prefix_len; /* Note that none of the following are used if the respective module is not * loaded, so it's safe to reference them directly. */ /* Imported from OperServ: */ static char **p_ServicesRoot; #define ServicesRoot (*p_ServicesRoot) /* Imported from NickServ: */ static NickGroupInfo *(*p__get_ngi)(NickInfo *ni, const char *file, int line); static NickGroupInfo *(*p__get_ngi_id)(uint32 id, const char *file, int line); #define _get_ngi (*p__get_ngi) #define _get_ngi_id (*p__get_ngi_id) /* Imported from ChanServ: */ static int *p_CSMaxReg; #define CSMaxReg (*p_CSMaxReg) /* The following macro is used by the NickServ and ChanServ handlers to * make links to various ways of listing nicknames and channels. */ #define PRINT_SELOPT(c,prefix,select,value,text) \ sockprintf((c)->socket, "%s%s%d%s%s%s", prefix, \ (select)==(value) ? "(" : "\">", text, \ (select)==(value) ? ")" : "") /*************************************************************************/ /* Handlers for individual databases: */ static int handle_operserv(Client *c, int *close_ptr, char *path); static int handle_operserv_akill(Client *c, int *close_ptr, char *path); static int handle_operserv_exclude(Client *c, int *close_ptr, char *path); static int handle_operserv_news(Client *c, int *close_ptr, char *path); static int handle_operserv_sessions(Client *c, int*close_ptr, char *path); static int handle_operserv_sline(Client *c, int *close_ptr, char *path); static int handle_nickserv(Client *c, int *close_ptr, char *path); static int handle_chanserv(Client *c, int *close_ptr, char *path); static int handle_statserv(Client *c, int *close_ptr, char *path); static int handle_xml_export(Client *c, int *close_ptr, char *path); /*************************************************************************/ /**************************** Local routines *****************************/ /*************************************************************************/ /* Local utility routine to simplify strftime() calls: */ static int my_strftime(char *buf, int size, time_t t) { char tmp[BUFSIZE]; int retval = strftime(tmp, sizeof(tmp), "%b %d %H:%M:%S %Y", localtime(&t)); tmp[sizeof(tmp)-1] = 0; if (retval == 0) *tmp = 0; http_quote_html(tmp, buf, size); return strlen(buf); } /*************************************************************************/ /******************** Request callback and handlers **********************/ /*************************************************************************/ static int do_request(Client *c, int *close_ptr) { char *subpath; if (strncmp(c->url, Prefix, Prefix_len) != 0) return 0; subpath = c->url + Prefix_len; if (!*subpath) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*subpath != '/') { return 0; } subpath++; if (strncmp(subpath,"operserv",8) == 0) return handle_operserv(c, close_ptr, subpath+8); if (strncmp(subpath,"nickserv",8) == 0) return handle_nickserv(c, close_ptr, subpath+8); if (strncmp(subpath,"chanserv",8) == 0) return handle_chanserv(c, close_ptr, subpath+8); if (strncmp(subpath,"statserv",8) == 0) return handle_statserv(c, close_ptr, subpath+8); if (strncmp(subpath,"xml-export",10) == 0) return handle_xml_export(c, close_ptr, subpath+10); if (!*subpath) { *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); sockprintf(c->socket, "IRC Services database access" "

IRC Services database" " access

"); if (!module_operserv) { /* this implies !nickserv etc. */ sockprintf(c->socket, "No service modules are currently loaded." ""); } else { sockprintf(c->socket, "Please select one of the following:

"); } return 1; } return 0; } /*************************************************************************/ static int handle_operserv(Client *c, int *close_ptr, char *path) { if (!module_operserv) return 0; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; if (strncmp(path,"akill",5) == 0) return handle_operserv_akill(c, close_ptr, path+5); if (strncmp(path,"exclude",7) == 0) return handle_operserv_exclude(c, close_ptr, path+7); if (strncmp(path,"news",4) == 0) return handle_operserv_news(c, close_ptr, path+4); if (strncmp(path,"sessions",6) == 0) return handle_operserv_sessions(c, close_ptr, path+8); if (strncmp(path,"sline",5) == 0) return handle_operserv_sline(c, close_ptr, path+5); if (!*path) { char timebuf[BUFSIZE]; *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); my_strftime(timebuf, sizeof(timebuf), maxusertime); sockprintf(c->socket, "OperServ database access" "

OperServ database" " access

", usercnt, opcnt, maxusercnt, timebuf); sockprintf(c->socket, "Please select one of the following:"); return 1; } return 0; } /*************************************************************************/ /* Common code for handling MaskData structures. `typename' should be in * all lower case (except for letters that are always capitalized); `a_an' * should be the proper indefinite article for the type name ("a" or "an"). * Presently the code assumes that the plural is formed by simply adding an * "s"; this works for the currently available options (autokill, exclusion, * exception, S-line). */ static int handle_maskdata(Client *c, int *close_ptr, char *path, uint8 type, const char *a_an, const char *typename) { char urlbuf[BUFSIZE*3]; /* *3 because of / -> %2F */ char htmlbuf[BUFSIZE*5]; /* *5 because of & -> & */ MaskData *md; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); sockprintf(c->socket, "%c%s database access" "", toupper(*typename), typename+1); if (!*path) { int count = 0; sockprintf(c->socket, "

%c%s database access

" "

Click on %s %s for detailed information.

" "(Return to previous menu)

%d %s%s.", count, typename, count==1 ? "" : "s"); return 1; } http_unquote_url(path); md = get_maskdata(type, path); http_quote_html(path, htmlbuf, sizeof(htmlbuf)); if (!md) { sockprintf(c->socket, "

%c%s not found

" "

No %s was found for %s.

Return" " to %s list", toupper(*typename), typename+1, typename, htmlbuf, typename); return 1; } sockprintf(c->socket, "

%c%s database access

" "

%s

", toupper(*typename), typename+1, htmlbuf); sockprintf(c->socket, ""); if (type == MD_EXCEPTION) { sockprintf(c->socket, "
Limit: " "%d", md->limit); } sockprintf(c->socket, "
Set by: "); http_quote_html(md->who, htmlbuf, sizeof(htmlbuf)); if (module_nickserv && get_nickinfo(md->who)) { http_quote_url(md->who, urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "%s", urlbuf, htmlbuf); } else { sockprintf(c->socket, "%s", htmlbuf); } http_quote_html(md->reason ? md->reason : "", htmlbuf, sizeof(htmlbuf)); sockprintf(c->socket, "
Reason: %s", htmlbuf); my_strftime(htmlbuf, sizeof(htmlbuf), md->time); sockprintf(c->socket, "
Set on: %s", htmlbuf); sockprintf(c->socket, "
Expires on: "); if (md->expires) { my_strftime(htmlbuf, sizeof(htmlbuf), md->expires); sockprintf(c->socket, "%s", htmlbuf); } else { sockprintf(c->socket, "Does not expire"); } sockprintf(c->socket, "
Last triggered: "); if (md->lastused) { my_strftime(htmlbuf, sizeof(htmlbuf), md->lastused); sockprintf(c->socket, "%s", htmlbuf); } else { sockprintf(c->socket, "Never"); } sockprintf(c->socket, "

Return to %s list" "", typename); return 1; } /*************************************************************************/ static int handle_operserv_akill(Client *c, int *close_ptr, char *path) { if (!module_operserv_akill) return 0; return handle_maskdata(c, close_ptr, path, MD_AKILL, "an", "autokill"); } /*************************************************************************/ static int handle_operserv_exclude(Client *c, int *close_ptr, char *path) { if (!module_operserv_akill) return 0; return handle_maskdata(c, close_ptr, path, MD_EXCLUSION, "an", "autokill exclusion"); } /*************************************************************************/ static int handle_operserv_news(Client *c, int *close_ptr, char *path) { char htmlbuf[BUFSIZE*5]; NewsItem *news; if (!module_operserv_news) return 0; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); sockprintf(c->socket, "News database access" ""); sockprintf(c->socket, "

News database" " access

(Return to previous menu)"); sockprintf(c->socket, "

Logon news

" "
NumAdded byDateText"); for (news = first_news(); news; news = next_news()) { if (news->type != NEWS_LOGON) continue; http_quote_html(news->who, htmlbuf, sizeof(htmlbuf)); sockprintf(c->socket, "
%d%s", news->num, htmlbuf); my_strftime(htmlbuf, sizeof(htmlbuf), news->time); sockprintf(c->socket, "%s", htmlbuf); http_quote_html(news->text ? news->text : "", htmlbuf, sizeof(htmlbuf)); sockprintf(c->socket, "%s", htmlbuf); } sockprintf(c->socket, "

Oper news

" "
NumAdded byDateText"); for (news = first_news(); news; news = next_news()) { if (news->type != NEWS_OPER) continue; http_quote_html(news->who, htmlbuf, sizeof(htmlbuf)); sockprintf(c->socket, "
%d%s", news->num, htmlbuf); my_strftime(htmlbuf, sizeof(htmlbuf), news->time); sockprintf(c->socket, "%s", htmlbuf); http_quote_html(news->text ? news->text : "", htmlbuf, sizeof(htmlbuf)); sockprintf(c->socket, "%s", htmlbuf); } sockprintf(c->socket, "
"); return 1; } /*************************************************************************/ static int handle_operserv_sessions(Client *c, int *close_ptr, char *path) { if (!module_operserv_sessions) return 0; return handle_maskdata(c, close_ptr, path, MD_EXCEPTION, "a", "session exception"); } /*************************************************************************/ static int handle_operserv_sline(Client *c, int *close_ptr, char *path) { char typename[7] = "S.line"; if (!module_operserv_sline) return 0; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; if (!*path) { *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); sockprintf(c->socket, "S-line database access" ""); sockprintf(c->socket, "

Please select one of the following:

"); return 1; } else if (*path != 'G' && *path != 'Q' && *path != 'Z') { return 0; } typename[1] = *path; return handle_maskdata(c, close_ptr, path+1, *path, "an", typename); } /*************************************************************************/ static struct { int32 mask, flags; const char *text; } nickopts[] = { { NF_KILLPROTECT | NF_KILL_QUICK | NF_KILL_IMMED, NF_KILLPROTECT | NF_KILL_QUICK | NF_KILL_IMMED, "kill protection (immediate)" }, { NF_KILLPROTECT | NF_KILL_QUICK | NF_KILL_IMMED, NF_KILLPROTECT | NF_KILL_QUICK, "kill protection (quick)" }, { NF_KILLPROTECT | NF_KILL_QUICK | NF_KILL_IMMED, NF_KILLPROTECT, "kill protection" }, { NF_SECURE, NF_SECURE, "secure" }, { NF_MEMO_SIGNON, NF_MEMO_SIGNON, "memo notify on logon" }, { NF_MEMO_RECEIVE, NF_MEMO_RECEIVE, "memo notify on receive" }, { NF_PRIVATE, NF_PRIVATE, "private" }, { NF_HIDE_EMAIL, NF_HIDE_EMAIL, "hide E-mail address" }, { NF_HIDE_MASK, NF_HIDE_MASK, "hide user@host mask" }, { NF_HIDE_QUIT, NF_HIDE_QUIT, "hide quit message" }, { NF_MEMO_FWD | NF_MEMO_FWDCOPY, NF_MEMO_FWD | NF_MEMO_FWDCOPY, "copy and forward memos" }, { NF_MEMO_FWD | NF_MEMO_FWDCOPY, NF_MEMO_FWD, "forward memos" }, { 0 } }; static int handle_nickserv(Client *c, int *close_ptr, char *path) { char nickurl[NICKMAX*3]; char nickhtml[NICKMAX*5]; NickInfo *ni; NickGroupInfo *ngi; if (!module_nickserv) return 0; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); if (!*path) { int count = 0; char *select_var = http_get_variable(c, "select"); enum {SEL_ALL, SEL_FORBIDDEN,SEL_SUSPENDED,SEL_NOEXPIRE,SEL_NOAUTH} select = SEL_ALL; if (select_var) select = atoi(select_var); sockprintf(c->socket, "Nickname database access" "

Nickname database" " access

Click to display:"); PRINT_SELOPT(c, " ", select, SEL_ALL, "All nicknames"); PRINT_SELOPT(c, " | ", select, SEL_FORBIDDEN, "Forbidden nicknames"); PRINT_SELOPT(c, " | ", select, SEL_SUSPENDED, "Suspended nicknames"); PRINT_SELOPT(c, " | ", select, SEL_NOEXPIRE, "Non-expiring nicknames"); PRINT_SELOPT(c, " | ", select, SEL_NOAUTH, "Not-yet-authorized nicknames"); sockprintf(c->socket, "
Or click on a nickname for detailed information." "

(Return to previous menu)

%d %snickname%s %s.", count, select==SEL_NOEXPIRE ? "non-expiring " : "", count==1 ? "" : "s", select==SEL_FORBIDDEN ? "forbidden" : select==SEL_SUSPENDED ? "suspended" : select==SEL_NOAUTH ? "not yet authorized" : "registered"); return 1; } http_unquote_url(path); ni = get_nickinfo(path); http_quote_html(path, nickhtml, sizeof(nickhtml)); sockprintf(c->socket, "Information on nickname \"%s\"" "

Information on nickname" " \"%s\"

", nickhtml, nickhtml); if (!ni) { sockprintf(c->socket, "

Nickname \"%s\" is not registered.", nickhtml); } else if (ni->status & NS_VERBOTEN) { sockprintf(c->socket, "

Nickname \"%s\" is forbidden.", nickhtml); } else if (!(ngi = get_ngi(ni))) { sockprintf(c->socket, "

Error retrieving information for nickname \"%s\".", nickhtml); } else { char buf[BUFSIZE*5], urlbuf[BUFSIZE*3]; int need_comma = 0, i; sockprintf(c->socket, ""); http_quote_html(ni->last_realname ? ni->last_realname : "", buf, sizeof(buf)); sockprintf(c->socket, "
Registered" " to: %s", buf); my_strftime(buf, sizeof(buf), ni->time_registered); sockprintf(c->socket, "
Time" " registered: %s", buf); http_quote_html(ni->last_realmask ? ni->last_realmask : "", buf, sizeof(buf)); if (get_user(ni->nick)) { sockprintf(c->socket, "
Is" " online from: %s", buf); sockprintf(c->socket, "
Authorization" " status: %s", nick_identified(ni) ? "Identified" : nick_recognized(ni) ? "Recognized (via access list)" : "Not recognized"); } else { sockprintf(c->socket, "
Last seen" " address: %s", buf); my_strftime(buf, sizeof(buf), ni->last_seen); sockprintf(c->socket, "
Last seen" " on: %s", buf); } if (ni->last_quit) { http_quote_html(ni->last_quit, buf, sizeof(buf)); sockprintf(c->socket, "
Last quit" " message: %s", buf); } sockprintf(c->socket, "

"); if (ngi->info) { http_quote_html(ngi->info, buf, sizeof(buf)); sockprintf(c->socket, "
" "Information: %s", buf); } if (ngi->url) { http_quote_html(ngi->url, buf, sizeof(buf)); http_quote_url(ngi->url, urlbuf, sizeof(urlbuf), 0); sockprintf(c->socket, "
URL: " "%s", urlbuf, buf); } if (ngi->email) { http_quote_html(ngi->email, buf, sizeof(buf)); http_quote_url(ngi->email, urlbuf, sizeof(urlbuf), 0); sockprintf(c->socket, "
E-mail address: " "%s", urlbuf, buf); } sockprintf(c->socket, "
Options: "); if (ni->status & NS_NOEXPIRE) { sockprintf(c->socket, "Will not expire"); need_comma++; } for (i = 0; nickopts[i].mask; i++) { if ((ngi->flags & nickopts[i].mask) == nickopts[i].flags) { http_quote_html(nickopts[i].text, buf, sizeof(buf)); if (!need_comma) *buf = toupper(*buf); sockprintf(c->socket, "%s%s", need_comma++ ? ", " : "", buf); } } if (!need_comma) sockprintf(c->socket, "None"); sockprintf(c->socket, "
OperServ" " privilege level:"); if (irc_stricmp(ni->nick, ServicesRoot) == 0) sockprintf(c->socket, "Services super-user"); else if (ngi->os_priv >= NP_SERVADMIN) sockprintf(c->socket, "Services administrator"); else if (ngi->os_priv >= NP_SERVOPER) sockprintf(c->socket, "Services operator"); else sockprintf(c->socket, "None"); sockprintf(c->socket, "

"); if (ngi->authcode) { sockprintf(c->socket, "
" "This nickname's E-mail address has" " not yet been authorized."); sockprintf(c->socket, "
Authorization code:" " %d", ngi->authcode); my_strftime(buf, sizeof(buf), ngi->authset); sockprintf(c->socket, "
Code set at: " "%s", buf); sockprintf(c->socket, "

"); } if (ngi->suspendinfo) { sockprintf(c->socket, "
" "This nickname group is" " suspended."); my_strftime(buf, sizeof(buf), ngi->suspendinfo->suspended); sockprintf(c->socket, "
" "Suspended on: %s", buf); http_quote_html(ngi->suspendinfo->who, buf, sizeof(buf)); http_quote_url(ngi->suspendinfo->who, urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "
" "Suspended by: %s", urlbuf, buf); http_quote_html(ngi->suspendinfo->reason ? ngi->suspendinfo->reason : "", buf, sizeof(buf)); sockprintf(c->socket, "
" "Reason for suspension: %s", buf); if (ngi->suspendinfo->expires) my_strftime(buf, sizeof(buf), ngi->suspendinfo->expires); else strscpy(buf, "Never", sizeof(buf)); sockprintf(c->socket, "
" "Suspension expires on: %s", buf); sockprintf(c->socket, "

"); } sockprintf(c->socket, "
Linked nicks:"); if (ngi->nicks_count == 1) { sockprintf(c->socket, "-"); } else { int count = 0; ARRAY_FOREACH (i, ngi->nicks) { if (irc_stricmp(ngi->nicks[i], path) == 0) continue; if (count > 0) sockprintf(c->socket, "
"); if (i == ngi->mainnick) sockprintf(c->socket, ""); http_quote_html(ngi->nicks[i], buf, sizeof(buf)); sockprintf(c->socket, "%s", buf); if (i == ngi->mainnick) sockprintf(c->socket, ""); count++; } } sockprintf(c->socket, "

"); sockprintf(c->socket, "
Channels registered:"); if (!ngi->channels_count) { sockprintf(c->socket, "None"); } else { int i; ARRAY_FOREACH (i, ngi->channels) { if (i > 0) sockprintf(c->socket, "
"); http_quote_html(ngi->channels[i], buf, sizeof(buf)); if (module_chanserv) { http_quote_url(ngi->channels[i]+1, urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "" "%s", urlbuf, buf); } else { sockprintf(c->socket, "%s", buf); } } } sockprintf(c->socket, "
Channel" " registration limit:"); if (ngi->channelmax == CHANMAX_DEFAULT) { if (module_chanserv) sockprintf(c->socket, "Default (%d)", CSMaxReg); else sockprintf(c->socket, "Default"); } else if (ngi->channelmax == CHANMAX_UNLIMITED) { sockprintf(c->socket, "None"); } else { sockprintf(c->socket, "%d", ngi->channelmax); } sockprintf(c->socket, "

"); sockprintf(c->socket, "
Access list:"); if (!ngi->access_count) { sockprintf(c->socket, "None"); } else { int i; ARRAY_FOREACH (i, ngi->access) { if (i > 0) sockprintf(c->socket, "
"); http_quote_html(ngi->access[i], buf, sizeof(buf)); sockprintf(c->socket, "%s", buf); } } sockprintf(c->socket, "
"); } sockprintf(c->socket, "

Return to nickname list" ""); return 1; } /*************************************************************************/ static struct { int32 mask, flags; const char *text; } chanopts[] = { { CI_KEEPTOPIC, CI_KEEPTOPIC, "topic retention" }, { CI_SECUREOPS, CI_SECUREOPS, "secure ops" }, { CI_PRIVATE, CI_PRIVATE, "private" }, { CI_TOPICLOCK, CI_TOPICLOCK, "topic lock" }, { CI_RESTRICTED, CI_RESTRICTED, "restricted" }, { CI_LEAVEOPS, CI_LEAVEOPS, "leave ops" }, { CI_SECURE, CI_SECURE, "secure" }, { CI_OPNOTICE, CI_OPNOTICE, "op-notice" }, { CI_ENFORCE, CI_ENFORCE, "enforce" }, { 0 } }; static int handle_chanserv(Client *c, int *close_ptr, char *path) { char chanurl[CHANMAX*3]; char chanhtml[CHANMAX*5]; char chantmp[CHANMAX]; char buf[BUFSIZE*5], urlbuf[BUFSIZE*3]; int i; char *s; ChannelInfo *ci; NickGroupInfo *ngi; enum {MODE_INFO, MODE_LEVELS, MODE_ACCESS, MODE_AUTOKICK} mode = MODE_INFO; if (!module_chanserv) return 0; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; if (!*path) { int count = 0; char *select_var = http_get_variable(c, "select"); enum {SEL_ALL, SEL_FORBIDDEN,SEL_SUSPENDED,SEL_NOEXPIRE} select = SEL_ALL; if (select_var) select = atoi(select_var); *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); sockprintf(c->socket, "Channel database access" "

Channel database" " access

Click to display:"); PRINT_SELOPT(c, " ", select, SEL_ALL, "All channels"); PRINT_SELOPT(c, " | ", select, SEL_FORBIDDEN, "Forbidden channels"); PRINT_SELOPT(c, " | ", select, SEL_SUSPENDED, "Suspended channels"); PRINT_SELOPT(c, " | ", select, SEL_NOEXPIRE, "Non-expiring channels"); sockprintf(c->socket, "
Or click on a channel for detailed information." "

(Return to previous menu)

%d %schannel%s %s.", count, select==SEL_NOEXPIRE ? "non-expiring " : "", count==1 ? "" : "s", select==SEL_FORBIDDEN ? "forbidden" : select==SEL_SUSPENDED ? "suspended" : "registered"); return 1; } s = strchr(path, '/'); if (s) { *s++ = 0; if (strcmp(s, "levels") == 0) mode = MODE_LEVELS; else if (strcmp(s, "access") == 0) mode = MODE_ACCESS; else if (strcmp(s, "autokick") == 0) mode = MODE_AUTOKICK; else if (*s) return 0; else { /* ".../chanserv/channel-name/" */ http_send_response(c, HTTP_R_FOUND); /* Note that we just modified c->url above */ sockprintf(c->socket, "Location: %s\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } } http_unquote_url(path); /* URL has # stripped out, so put it back */ snprintf(chantmp, sizeof(chantmp), "#%s", path); ci = get_channelinfo(chantmp); http_quote_html(chantmp, chanhtml, sizeof(chanhtml)); http_quote_url(chantmp+1, chanurl, sizeof(chanurl), 1); *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); if (!ci) { sockprintf(c->socket, "

Channel \"%s\" is not registered.", chanhtml); } else if (ci->flags & CI_VERBOTEN) { sockprintf(c->socket, "

Channel \"%s\" is forbidden.", chanhtml); } else if (mode == MODE_LEVELS) { LevelInfo *(*get_levelinfo)(void), *levelinfo; /* from ChanServ */ get_levelinfo = get_module_symbol(module_chanserv, "get_levelinfo"); sockprintf(c->socket, "Access levels for channel \"%s\"" "

Access levels" " for channel \"%s\"

", chanhtml, chanhtml); if (!get_levelinfo || !(levelinfo = get_levelinfo())) { module_log("Unable to retrieve ChanServ level data"); sockprintf(c->socket, "

Error accessing" " level data!"); } else { sockprintf(c->socket, "

" "
NameLevelDescription
"); for (i = 0; levelinfo[i].what >= 0; i++) { char buf2[BUFSIZE*5]; int level = ci->levels ? ci->levels[levelinfo[i].what] : levelinfo[i].defval; if (!*levelinfo[i].name) /* Empty name -> dummy level */ continue; http_quote_html(levelinfo[i].name, buf, sizeof(buf)); http_quote_html(getstring(NULL,levelinfo[i].desc), buf2, sizeof(buf2)); if (level == ACCLEV_FOUNDER) sockprintf(c->socket, "
%s" "(Founder only)%s", buf, buf2); else if (level == ACCLEV_INVALID) sockprintf(c->socket, "
%s" "(Disabled)%s", buf, buf2); else sockprintf(c->socket, "
%s%d " "%s", buf, level, buf2); } sockprintf(c->socket, "
"); } sockprintf(c->socket, "

Return to channel" " information", chanurl); } else if (mode == MODE_ACCESS) { sockprintf(c->socket, "Access list for channel \"%s\"" "

Access list for channel" " \"%s\"

", chanhtml, chanhtml); ARRAY_FOREACH (i, ci->access) { if (ci->access[i].nickgroup) break; } if (i >= ci->access_count) { sockprintf(c->socket, "

Access list is empty."); } else { int count = 0; sockprintf(c->socket, "

"); if (!ci->levels) { sockprintf(c->socket, "

Note: This channel's" " levels are set to the built-in defaults.

"); } sockprintf(c->socket, "" "
NicknameLevel
"); ARRAY_FOREACH (i, ci->access) { if (!ci->access[i].nickgroup) continue; sockprintf(c->socket, "
"); ngi = get_ngi_id(ci->access[i].nickgroup); if (ngi) { http_quote_html(ngi_mainnick(ngi), buf, sizeof(buf)); http_quote_url(ngi_mainnick(ngi), urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "%s" "", urlbuf, buf); } else { sockprintf(c->socket, "(Error)"); } sockprintf(c->socket, "%d", ci->access[i].level); count++; } sockprintf(c->socket, "

%d entries.", count); } sockprintf(c->socket, "

Return to channel" " information", chanurl); } else if (mode == MODE_AUTOKICK) { sockprintf(c->socket, "Autokick list for channel \"%s\"" "

Autokick list" " for channel \"%s\"

", chanhtml, chanhtml); ARRAY_FOREACH (i, ci->akick) { if (ci->akick[i].mask) break; } if (i >= ci->akick_count) { sockprintf(c->socket, "

Autokick list is empty."); } else { int count = 0; sockprintf(c->socket, "

" "
MaskSet bySet atLast usedReason" "
"); ARRAY_FOREACH (i, ci->akick) { if (!ci->akick[i].mask) continue; http_quote_html(ci->akick[i].mask, buf, sizeof(buf)); sockprintf(c->socket, "
%s", buf); http_quote_html(ci->akick[i].who, buf, sizeof(buf)); if (get_nickinfo(ci->akick[i].who)) { http_quote_url(ci->akick[i].who, urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "%s", urlbuf, buf); } else { sockprintf(c->socket, "%s", buf); } my_strftime(buf, sizeof(buf), ci->akick[i].set); sockprintf(c->socket, "%s", buf); if (ci->akick[i].lastused) { my_strftime(buf, sizeof(buf), ci->akick[i].lastused); sockprintf(c->socket, "%s", buf); } else { sockprintf(c->socket, "Never"); } if (ci->akick[i].reason) http_quote_html(ci->akick[i].reason, buf, sizeof(buf)); else strcpy(buf, " "); /* to ensure the cell is drawn */ sockprintf(c->socket, "%s", buf); count++; } sockprintf(c->socket, "

%d entries.", count); } sockprintf(c->socket, "

Return to channel" " information", chanurl); } else { int need_comma = 0; sockprintf(c->socket, "Information on channel \"%s\"" "

Information on channel" " \"%s\"

", chanhtml, chanhtml); sockprintf(c->socket, ""); sockprintf(c->socket, "
Founder: "); ngi = get_ngi_id(ci->founder); if (ngi) { http_quote_html(ngi_mainnick(ngi), buf, sizeof(buf)); http_quote_url(ngi_mainnick(ngi), urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "%s", urlbuf, buf); } else { sockprintf(c->socket, "(Error)"); } sockprintf(c->socket, "
Successor: "); if (ci->successor) { ngi = get_ngi_id(ci->successor); if (ngi) { http_quote_html(ngi_mainnick(ngi), buf, sizeof(buf)); http_quote_url(ngi_mainnick(ngi), urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "%s", urlbuf, buf); } else { sockprintf(c->socket, "(Error)"); } } else { sockprintf(c->socket, "(None)"); } http_quote_html(ci->desc, buf, sizeof(buf)); sockprintf(c->socket, "
Description:" " %s", buf); if (ci->url) { http_quote_html(ci->url, buf, sizeof(buf)); http_quote_url(ci->url, urlbuf, sizeof(urlbuf), 0); sockprintf(c->socket, "
URL: " "%s", urlbuf, buf); } if (ci->email) { http_quote_html(ci->email, buf, sizeof(buf)); http_quote_url(ci->email, urlbuf, sizeof(urlbuf), 0); sockprintf(c->socket, "
E-mail address: " "%s", urlbuf, buf); } my_strftime(buf, sizeof(buf), ci->time_registered); sockprintf(c->socket, "
Time" " registered: %s", buf); my_strftime(buf, sizeof(buf), ci->last_used); sockprintf(c->socket, "
Last used:" " %s", buf); sockprintf(c->socket, "
Mode lock: "); if (ci->mlock_on) { http_quote_html(mode_flags_to_string(ci->mlock_on,MODE_CHANNEL), buf, sizeof(buf)); sockprintf(c->socket, "+%s", buf); } if (ci->mlock_off) { http_quote_html(mode_flags_to_string(ci->mlock_off,MODE_CHANNEL), buf, sizeof(buf)); sockprintf(c->socket, "-%s", buf); } if (!ci->mlock_on && !ci->mlock_off) sockprintf(c->socket, "(None)"); sockprintf(c->socket, "
Options: "); if (ci->flags & CI_NOEXPIRE) { sockprintf(c->socket, "Will not expire"); need_comma++; } for (i = 0; chanopts[i].mask; i++) { if ((ci->flags & chanopts[i].mask) == chanopts[i].flags) { http_quote_html(chanopts[i].text, buf, sizeof(buf)); if (!need_comma) *buf = toupper(*buf); sockprintf(c->socket, "%s%s", need_comma++ ? ", " : "", buf); } } if (!need_comma) sockprintf(c->socket, "None"); sockprintf(c->socket, "

"); if ((ci->flags & CI_KEEPTOPIC) && ci->last_topic) { http_quote_html(ci->last_topic, buf, sizeof(buf)); sockprintf(c->socket, "
Last" " topic:%s", buf); http_quote_html(ci->last_topic_setter, buf, sizeof(buf)); sockprintf(c->socket, "
Topic" " set by:%s", buf); my_strftime(buf, sizeof(buf), ci->last_topic_time); sockprintf(c->socket, "
Topic" " set on: %s", buf); sockprintf(c->socket, "

"); } if (ci->suspendinfo) { sockprintf(c->socket, "
" "This channel is suspended." ""); my_strftime(buf, sizeof(buf), ci->suspendinfo->suspended); sockprintf(c->socket, "
" "Suspended on: %s", buf); http_quote_html(ci->suspendinfo->who, buf, sizeof(buf)); http_quote_url(ci->suspendinfo->who, urlbuf, sizeof(urlbuf), 1); sockprintf(c->socket, "
" "Suspended by: %s", urlbuf, buf); http_quote_html(ci->suspendinfo->reason ? ci->suspendinfo->reason : "", buf, sizeof(buf)); sockprintf(c->socket, "
" "Reason for suspension: %s", buf); if (ci->suspendinfo->expires) my_strftime(buf, sizeof(buf), ci->suspendinfo->expires); else strscpy(buf, "Never", sizeof(buf)); sockprintf(c->socket, "
" "Suspension expires on: %s", buf); sockprintf(c->socket, "

"); } sockprintf(c->socket, "
" "View access level settings", chanurl); sockprintf(c->socket, "
" "View access list", chanurl); sockprintf(c->socket, "
" "View autokick list", chanurl); sockprintf(c->socket, "

Return to channel list"); } sockprintf(c->socket, ""); return 1; } /*************************************************************************/ static int handle_statserv(Client *c, int *close_ptr, char *path) { ServerStats *ss; char servurl[BUFSIZE*3]; char servhtml[BUFSIZE*5]; if (!module_statserv) return 0; if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; *close_ptr = 1; http_send_response(c, HTTP_S_OK); sockprintf(c->socket, "Content-Type: text/html\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); if (!*path) { int count = 0; sockprintf(c->socket, "StatServ database access" "

StatServ database" " access

Click on a server for detailed information." "

(Return to previous menu)

%d server%s found.", count, count==1 ? "" : "s"); return 1; } http_unquote_url(path); ss = get_serverstats(path); http_quote_html(path, servhtml, sizeof(servhtml)); sockprintf(c->socket, "Information on server \"%s\"" "

Information on server" " \"%s\"

", servhtml, servhtml); if (!ss) { sockprintf(c->socket, "

Server \"%s\" is not known.", servhtml); } else { sockprintf(c->socket, "

Server is currently %sline" ".", ss->t_join > ss->t_quit ? "green" : "red", ss->t_join > ss->t_quit ? "on" : "off"); sockprintf(c->socket, ""); if (ss->t_join > ss->t_quit) { sockprintf(c->socket, "
Current" " user count: %d", ss->usercnt); sockprintf(c->socket, "
Current" " operator count: %d", ss->opercnt); } my_strftime(servhtml, sizeof(servhtml), ss->t_join); sockprintf(c->socket, "
Time of last" " join: %s", ss->t_join ? servhtml : "none"); my_strftime(servhtml, sizeof(servhtml), ss->t_quit); sockprintf(c->socket, "
Time of last" " quit: %s", ss->t_quit ? servhtml : "none"); http_quote_html(ss->quit_message ? ss->quit_message : "", servhtml, sizeof(servhtml)); sockprintf(c->socket, "
Last quit" " message: %s", servhtml); sockprintf(c->socket, "
"); } sockprintf(c->socket, "

Return to server list"); return 1; } /*************************************************************************/ static int handle_xml_export(Client *c, int *close_ptr, char *path) { void (*p_xml_export)(xml_writefunc_t writefunc, void *data); if (!module_xml_export) return 0; if (!(p_xml_export = get_module_symbol(module_xml_export, "xml_export"))) { module_log("Unable to resolve symbol `xml_export' in module" " `misc/xml-export'"); module_xml_export = NULL; return 0; } if (!*path) { http_send_response(c, HTTP_R_FOUND); sockprintf(c->socket, "Location: %s/\r\n", c->url); sockprintf(c->socket, "Content-Length: 0\r\n\r\n"); return 1; } else if (*path != '/') { return 0; } path++; if (*path) return 0; http_send_response(c, HTTP_S_OK); /* Use "text/plain" to keep browsers from trying to do anything funny * with the data. MSIE can go to hell. */ sockprintf(c->socket, "Content-Type: text/plain\r\n"); sockprintf(c->socket, "Connection: close\r\n\r\n"); *close_ptr = 1; if (c->method != METHOD_HEAD) (*p_xml_export)((xml_writefunc_t)sockprintf, c->socket); return 1; } /*************************************************************************/ /***************************** Module stuff ******************************/ /*************************************************************************/ const int32 module_version = MODULE_VERSION_CODE; ConfigDirective module_config[] = { { "Prefix", { { CD_STRING, CF_DIRREQ, &Prefix } } }, { NULL } }; /*************************************************************************/ static int do_load_module(Module *mod, const char *modname) { if (strcmp(modname, "operserv/main") == 0) { p_ServicesRoot = get_module_symbol(mod, "ServicesRoot"); if (p_ServicesRoot) { module_operserv = mod; } else { module_log("Cannot resolve symbol `ServicesRoot' in module" " `operserv/main'"); } module_operserv = mod; } else if (strcmp(modname, "operserv/akill") == 0) { module_operserv_akill = mod; } else if (strcmp(modname, "operserv/news") == 0) { module_operserv_news = mod; } else if (strcmp(modname, "operserv/sessions") == 0) { module_operserv_sessions = mod; } else if (strcmp(modname, "operserv/sline") == 0) { module_operserv_sline = mod; } else if (strcmp(modname, "nickserv/main") == 0) { p__get_ngi = get_module_symbol(mod, "_get_ngi"); p__get_ngi_id = get_module_symbol(mod, "_get_ngi_id"); if (p__get_ngi && p__get_ngi_id) { module_nickserv = mod; } else { module_log("Cannot resolve symbol `_get_ngi%s' in module" " `nickserv/main'; nickname information will not be" " available", !p__get_ngi ? "" : "_id"); p__get_ngi = NULL; p__get_ngi_id = NULL; } } else if (strcmp(modname, "chanserv/main") == 0) { p_CSMaxReg = get_module_symbol(mod, "CSMaxReg"); if (p_CSMaxReg) { module_chanserv = mod; } else { module_log("Cannot resolve symbol `CSMaxReg' in module" " `chanserv/main'; channel information will not be" " available"); } } else if (strcmp(modname, "statserv/main") == 0) { module_statserv = mod; } else if (strcmp(modname, "misc/xml-export") == 0) { module_xml_export = mod; } return 0; } /*************************************************************************/ static int do_unload_module(Module *mod) { if (mod == module_operserv) { p_ServicesRoot = NULL; module_operserv = NULL; } else if (mod == module_operserv_akill) { module_operserv_akill = NULL; } else if (mod == module_operserv_news) { module_operserv_news = NULL; } else if (mod == module_operserv_sessions) { module_operserv_sessions = NULL; } else if (mod == module_operserv_sline) { module_operserv_sline = NULL; } else if (mod == module_nickserv) { p__get_ngi = NULL; p__get_ngi_id = NULL; module_nickserv = NULL; } else if (mod == module_chanserv) { p_CSMaxReg = NULL; module_chanserv = NULL; } else if (mod == module_statserv) { module_statserv = NULL; } else if (mod == module_xml_export) { module_xml_export = NULL; } return 0; } /*************************************************************************/ int init_module(Module *module_) { Module *tmpmod; module = module_; module_httpd = NULL; Prefix_len = strlen(Prefix); while (Prefix_len > 0 && Prefix[Prefix_len-1] == '/') Prefix_len--; module_httpd = find_module("httpd/main"); if (!module_httpd) { module_log("Main httpd module not loaded"); exit_module(0); return 0; } use_module(module_httpd); if (!add_callback(NULL, "load module", do_load_module) || !add_callback(NULL, "unload module", do_unload_module) || !add_callback(module_httpd, "request", do_request) ) { module_log("Unable to add callbacks"); exit_module(0); return 0; } tmpmod = find_module("operserv/main"); if (tmpmod) do_load_module(tmpmod, "operserv/main"); tmpmod = find_module("operserv/akill"); if (tmpmod) do_load_module(tmpmod, "operserv/akill"); tmpmod = find_module("operserv/news"); if (tmpmod) do_load_module(tmpmod, "operserv/news"); tmpmod = find_module("operserv/sessions"); if (tmpmod) do_load_module(tmpmod, "operserv/sessions"); tmpmod = find_module("operserv/sline"); if (tmpmod) do_load_module(tmpmod, "operserv/sline"); tmpmod = find_module("nickserv/main"); if (tmpmod) do_load_module(tmpmod, "nickserv/main"); tmpmod = find_module("chanserv/main"); if (tmpmod) do_load_module(tmpmod, "chanserv/main"); tmpmod = find_module("statserv/main"); if (tmpmod) do_load_module(tmpmod, "statserv/main"); tmpmod = find_module("misc/xml-export"); if (tmpmod) do_load_module(tmpmod, "misc/xml-export"); return 1; } /*************************************************************************/ int exit_module(int shutdown_unused) { #ifdef CLEAN_COMPILE shutdown_unused = shutdown_unused; #endif remove_callback(NULL, "unload module", do_unload_module); remove_callback(NULL, "load module", do_load_module); if (module_httpd) { remove_callback(module_httpd, "request", do_request); unuse_module(module_httpd); module_httpd = NULL; } return 1; } /*************************************************************************/