/* Database access module for HTTP server.
*
* IRC Services is copyright (c) 1996-2007 Andrew Church.
* E-mail: <achurch@achurch.org>
* 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) ? "<!--" : "<a href=\"./?select=", (value),\
(select)==(value) ? "-->(" : "\">", text, \
(select)==(value) ? ")" : "</a>")
/*************************************************************************/
/* 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,
"<html><head><title>IRC Services database access</title>"
"</head><body><h1 align=center>IRC Services database"
" access</h1><p>");
if (!module_operserv) { /* this implies !nickserv etc. */
sockprintf(c->socket,
"No service modules are currently loaded.</body>"
"</html>");
} else {
sockprintf(c->socket, "Please select one of the following:<ul>");
sockprintf(c->socket,
"<li><a href=operserv/>OperServ data</a>");
if (module_nickserv)
sockprintf(c->socket,
"<li><a href=nickserv/>List of registered"
" nicknames</a>");
if (module_chanserv)
sockprintf(c->socket,
"<li><a href=chanserv/>List of registered"
" channels</a>");
if (module_statserv)
sockprintf(c->socket,
"<li><a href=statserv/>Network statistics</a>");
if (module_xml_export)
sockprintf(c->socket, "<li><a href=xml-export/>"
"XML database download</a>");
sockprintf(c->socket, "</ul></body></html>");
}
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,
"<html><head><title>OperServ database access</title>"
"</head><body><h1 align=center>OperServ database"
" access</h1><p><ul><li>Current number of users:"
" <b>%d</b> (%d ops)<li>Maximum user count: <b>%d</b>"
" (reached at %s)</ul>",
usercnt, opcnt, maxusercnt, timebuf);
sockprintf(c->socket, "Please select one of the following:<ul>");
if (module_operserv_akill
|| module_operserv_news
|| module_operserv_sessions
|| module_operserv_sline
) {
if (module_operserv_akill)
sockprintf(c->socket,
"<li><a href=akill/>List of autokills</a><li>"
"<a href=exclude/>List of autokill exclusions</a>");
if (module_nickserv)
sockprintf(c->socket,
"<li><a href=news/>List of news items</a>");
if (module_chanserv)
sockprintf(c->socket,
"<li><a href=sessions/>List of session"
" exceptions</a>");
if (module_statserv)
sockprintf(c->socket,
"<li><a href=sline/>List of S-lines</a>");
}
sockprintf(c->socket, "<li><a href=../>Return to previous menu</a>");
sockprintf(c->socket, "</ul></body></html>");
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, "<html><head><title>%c%s database access</title>"
"</head><body>", toupper(*typename), typename+1);
if (!*path) {
int count = 0;
sockprintf(c->socket, "<h1 align=center>%c%s database access</h1>"
"<p>Click on %s %s for detailed information.<p>"
"<a href=../>(Return to previous menu)</a><p><ul>",
toupper(*typename), typename+1, a_an, typename);
for (md = first_maskdata(type); md; md = next_maskdata(type)) {
http_quote_html(md->mask, htmlbuf, sizeof(htmlbuf));
http_quote_url(md->mask, urlbuf, sizeof(urlbuf), 1);
sockprintf(c->socket, "<li><a href=\"%s\">%s</a>",
urlbuf, htmlbuf);
if (type == MD_EXCEPTION)
sockprintf(c->socket, " (%d)", md->limit);
count++;
}
sockprintf(c->socket, "</ul><p>%d %s%s.</body></html>",
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, "<h1 align=center>%c%s not found</h1>"
"<p>No %s was found for <b>%s</b>.<p><a href=./>Return"
" to %s list</a></body></html>", toupper(*typename),
typename+1, typename, htmlbuf, typename);
return 1;
}
sockprintf(c->socket, "<h1 align=center>%c%s database access</h1>"
"<h2 align=center>%s</h2><div align=center>",
toupper(*typename), typename+1, htmlbuf);
sockprintf(c->socket, "<table border=0 cellspacing=4>");
if (type == MD_EXCEPTION) {
sockprintf(c->socket, "<tr><th align=right valign=top>Limit: "
"<td>%d", md->limit);
}
sockprintf(c->socket, "<tr><th align=right valign=top>Set by: <td>");
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, "<a href=\"../../nickserv/%s\">%s</a>",
urlbuf, htmlbuf);
} else {
sockprintf(c->socket, "%s", htmlbuf);
}
http_quote_html(md->reason ? md->reason : "", htmlbuf, sizeof(htmlbuf));
sockprintf(c->socket, "<tr><th align=right valign=top>Reason: <td>%s",
htmlbuf);
my_strftime(htmlbuf, sizeof(htmlbuf), md->time);
sockprintf(c->socket, "<tr><th align=right valign=top>Set on: <td>%s",
htmlbuf);
sockprintf(c->socket,
"<tr><th align=right valign=top>Expires on: <td>");
if (md->expires) {
my_strftime(htmlbuf, sizeof(htmlbuf), md->expires);
sockprintf(c->socket, "%s", htmlbuf);
} else {
sockprintf(c->socket, "<font color=green>Does not expire</font>");
}
sockprintf(c->socket,
"<tr><th align=right valign=top>Last triggered: <td>");
if (md->lastused) {
my_strftime(htmlbuf, sizeof(htmlbuf), md->lastused);
sockprintf(c->socket, "%s", htmlbuf);
} else {
sockprintf(c->socket, "<font color=red>Never</font>");
}
sockprintf(c->socket, "</table></div><p><a href=./>Return to %s list"
"</a></body></html>", 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, "<html><head><title>News database access"
"</title></head><body>");
sockprintf(c->socket, "<h1 align=center>News database"
" access</h1><p><a href=../>(Return to previous menu)</a>");
sockprintf(c->socket, "<h2 align=center>Logon news</h2><p>"
"<table border=2><tr><th>Num<th>Added by<th>Date<th>Text");
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, "<tr><td>%d<td>%s", news->num, htmlbuf);
my_strftime(htmlbuf, sizeof(htmlbuf), news->time);
sockprintf(c->socket, "<td>%s", htmlbuf);
http_quote_html(news->text ? news->text : "",
htmlbuf, sizeof(htmlbuf));
sockprintf(c->socket, "<td>%s", htmlbuf);
}
sockprintf(c->socket, "</table><h2 align=center>Oper news</h2><p>"
"<table border=2><tr><th>Num<th>Added by<th>Date<th>Text");
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, "<tr><td>%d<td>%s", news->num, htmlbuf);
my_strftime(htmlbuf, sizeof(htmlbuf), news->time);
sockprintf(c->socket, "<td>%s", htmlbuf);
http_quote_html(news->text ? news->text : "",
htmlbuf, sizeof(htmlbuf));
sockprintf(c->socket, "<td>%s", htmlbuf);
}
sockprintf(c->socket, "</table></body></html>");
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, "<html><head><title>S-line database access"
"</title></head><body>");
sockprintf(c->socket, "<p>Please select one of the following:<ul>"
"<li><a href=G/>List of SGlines</a>"
"<li><a href=Q/>List of SQlines</a>"
"<li><a href=Z/>List of SZlines</a>"
"<li><a href=../>Return to previous menu</a>"
"</ul></body></html>");
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,
"<html><head><title>Nickname database access</title>"
"</head><body><h1 align=center>Nickname database"
" access</h1><p>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,
"<br>Or click on a nickname for detailed information."
"<p><a href=../>(Return to previous menu)</a><p><ul>");
for (ni = first_nickinfo(); ni; ni = next_nickinfo()) {
NickGroupInfo *ngi = NULL;
if ((select==SEL_SUSPENDED || select==SEL_NOAUTH) && ni->nickgroup)
ngi = get_ngi(ni);
if ((select==SEL_FORBIDDEN && !(ni->status & NS_VERBOTEN))
|| (select==SEL_SUSPENDED && (!ngi || !ngi->suspendinfo))
|| (select==SEL_NOEXPIRE && !(ni->status & NS_NOEXPIRE))
|| (select==SEL_NOAUTH && (!ngi || !ngi->authcode))
) {
continue;
}
http_quote_html(ni->nick, nickhtml, sizeof(nickhtml));
http_quote_url(ni->nick, nickurl, sizeof(nickurl), 1);
sockprintf(c->socket, "<li><a href=\"%s\">%s</a>",
nickurl, nickhtml);
count++;
}
sockprintf(c->socket,
"</ul><p>%d %snickname%s %s.</body></html>", 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,
"<html><head><title>Information on nickname \"%s\"</title>"
"</head><body><h1 align=center>Information on nickname"
" \"%s\"</h1><div align=center>", nickhtml, nickhtml);
if (!ni) {
sockprintf(c->socket, "<p>Nickname \"%s\" is not registered.",
nickhtml);
} else if (ni->status & NS_VERBOTEN) {
sockprintf(c->socket, "<p>Nickname \"%s\" is <b>forbidden</b>.",
nickhtml);
} else if (!(ngi = get_ngi(ni))) {
sockprintf(c->socket,
"<p>Error retrieving information for nickname \"%s\".",
nickhtml);
} else {
char buf[BUFSIZE*5], urlbuf[BUFSIZE*3];
int need_comma = 0, i;
sockprintf(c->socket, "<table border=0 cellspacing=4>");
http_quote_html(ni->last_realname ? ni->last_realname : "", buf,
sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>Registered"
" to: <td>%s", buf);
my_strftime(buf, sizeof(buf), ni->time_registered);
sockprintf(c->socket, "<tr><th align=right valign=top>Time"
" registered: <td>%s", buf);
http_quote_html(ni->last_realmask ? ni->last_realmask : "", buf,
sizeof(buf));
if (get_user(ni->nick)) {
sockprintf(c->socket,
"<tr><th align=right valign=top><font color=green>Is"
" online from:</font> <td>%s", buf);
sockprintf(c->socket,
"<tr><th align=right valign=top>Authorization"
" status: <td>%s",
nick_identified(ni) ? "Identified" :
nick_recognized(ni) ? "Recognized (via access list)" :
"Not recognized");
} else {
sockprintf(c->socket, "<tr><th align=right valign=top>Last seen"
" address: <td>%s", buf);
my_strftime(buf, sizeof(buf), ni->last_seen);
sockprintf(c->socket, "<tr><th align=right valign=top>Last seen"
" on: <td>%s", buf);
}
if (ni->last_quit) {
http_quote_html(ni->last_quit, buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>Last quit"
" message: <td>%s", buf);
}
sockprintf(c->socket, "<tr><td colspan=2><hr>");
if (ngi->info) {
http_quote_html(ngi->info, buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Information: <td>%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,
"<tr><th align=right valign=top>URL: "
"<td><a href=\"%s\">%s</a>", 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,
"<tr><th align=right valign=top>E-mail address: "
"<td><a href=\"mailto:%s\">%s</a>", urlbuf, buf);
}
sockprintf(c->socket,
"<tr><th align=right valign=top>Options: <td>");
if (ni->status & NS_NOEXPIRE) {
sockprintf(c->socket, "<b>Will not expire</b>");
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, "<tr><th align=right valign=top>OperServ"
" privilege level:");
if (irc_stricmp(ni->nick, ServicesRoot) == 0)
sockprintf(c->socket, "<td>Services super-user");
else if (ngi->os_priv >= NP_SERVADMIN)
sockprintf(c->socket, "<td>Services administrator");
else if (ngi->os_priv >= NP_SERVOPER)
sockprintf(c->socket, "<td>Services operator");
else
sockprintf(c->socket, "<td>None");
sockprintf(c->socket, "<tr><td colspan=2><hr>");
if (ngi->authcode) {
sockprintf(c->socket, "<tr><td colspan=2 align=center>"
"<font color=red>This nickname's E-mail address has"
" not yet been authorized.</font>");
sockprintf(c->socket, "<tr><th align=right>Authorization code:"
" <td>%d", ngi->authcode);
my_strftime(buf, sizeof(buf), ngi->authset);
sockprintf(c->socket, "<tr><th align=right>Code set at: "
"<td>%s", buf);
sockprintf(c->socket, "<tr><td colspan=2><hr>");
}
if (ngi->suspendinfo) {
sockprintf(c->socket, "<tr><td colspan=2 align=center>"
"<font color=red>This nickname group is"
" <b>suspended</b>.</font>");
my_strftime(buf, sizeof(buf), ngi->suspendinfo->suspended);
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Suspended on: <td>%s", buf);
http_quote_html(ngi->suspendinfo->who, buf, sizeof(buf));
http_quote_url(ngi->suspendinfo->who, urlbuf, sizeof(urlbuf), 1);
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Suspended by: <td><a href=\"%s\">%s</a>",
urlbuf, buf);
http_quote_html(ngi->suspendinfo->reason ? ngi->suspendinfo->reason
: "", buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Reason for suspension: <td>%s", buf);
if (ngi->suspendinfo->expires)
my_strftime(buf, sizeof(buf), ngi->suspendinfo->expires);
else
strscpy(buf, "<b>Never</b>", sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Suspension expires on: <td>%s", buf);
sockprintf(c->socket, "<tr><td colspan=2><hr>");
}
sockprintf(c->socket,
"<tr><th align=right valign=top>Linked nicks:<td>");
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, "<br>");
if (i == ngi->mainnick)
sockprintf(c->socket, "<b>");
http_quote_html(ngi->nicks[i], buf, sizeof(buf));
sockprintf(c->socket, "%s", buf);
if (i == ngi->mainnick)
sockprintf(c->socket, "</b>");
count++;
}
}
sockprintf(c->socket, "<tr><td colspan=2><hr>");
sockprintf(c->socket,
"<tr><th align=right valign=top>Channels registered:<td>");
if (!ngi->channels_count) {
sockprintf(c->socket, "None");
} else {
int i;
ARRAY_FOREACH (i, ngi->channels) {
if (i > 0)
sockprintf(c->socket, "<br>");
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, "<a href=\"../chanserv/%s\">"
"%s</a>", urlbuf, buf);
} else {
sockprintf(c->socket, "%s", buf);
}
}
}
sockprintf(c->socket, "<tr><th align=right valign=top>Channel"
" registration limit:<td>");
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, "<tr><td colspan=2><hr>");
sockprintf(c->socket,
"<tr><th align=right valign=top>Access list:<td>");
if (!ngi->access_count) {
sockprintf(c->socket, "None");
} else {
int i;
ARRAY_FOREACH (i, ngi->access) {
if (i > 0)
sockprintf(c->socket, "<br>");
http_quote_html(ngi->access[i], buf, sizeof(buf));
sockprintf(c->socket, "%s", buf);
}
}
sockprintf(c->socket, "</table>");
}
sockprintf(c->socket,
"</div><p><a href=./>Return to nickname list</a></body>"
"</html>");
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,
"<html><head><title>Channel database access</title>"
"</head><body><h1 align=center>Channel database"
" access</h1><p>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,
"<br>Or click on a channel for detailed information."
"<p><a href=../>(Return to previous menu)</a><p><ul>");
for (ci = first_channelinfo(); ci; ci = next_channelinfo()) {
if ((select==SEL_FORBIDDEN && !(ci->flags & CI_VERBOTEN))
|| (select==SEL_SUSPENDED && !ci->suspendinfo)
|| (select==SEL_NOEXPIRE && !(ci->flags & CI_NOEXPIRE))
) {
continue;
}
http_quote_html(ci->name, chanhtml, sizeof(chanhtml));
http_quote_url(ci->name+1, chanurl, sizeof(chanurl), 1);
sockprintf(c->socket, "<li><a href=\"%s\">%s</a>",
chanurl, chanhtml);
count++;
}
sockprintf(c->socket,
"</ul><p>%d %schannel%s %s.</body></html>", 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, "<p>Channel \"%s\" is not registered.",
chanhtml);
} else if (ci->flags & CI_VERBOTEN) {
sockprintf(c->socket, "<p>Channel \"%s\" is <b>forbidden</b>.",
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,
"<html><head><title>Access levels for channel \"%s\""
"</title></head><body><h1 align=center>Access levels"
" for channel \"%s\"</h1>", chanhtml, chanhtml);
if (!get_levelinfo || !(levelinfo = get_levelinfo())) {
module_log("Unable to retrieve ChanServ level data");
sockprintf(c->socket, "<p><font color=red><b>Error accessing"
" level data!</b></font>");
} else {
sockprintf(c->socket, "<div align=center><table border=1><tr>"
"<th>Name<th>Level<th>Description<tr><td height=2>");
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, "<tr><td>%s<td align=center>"
"(Founder only)<td>%s", buf, buf2);
else if (level == ACCLEV_INVALID)
sockprintf(c->socket, "<tr><td>%s<td align=center>"
"(Disabled)<td>%s", buf, buf2);
else
sockprintf(c->socket, "<tr><td>%s<td align=right>%d "
"<td>%s", buf, level, buf2);
}
sockprintf(c->socket, "</table></div>");
}
sockprintf(c->socket, "<p><a href=\"../%s\">Return to channel"
" information</a>", chanurl);
} else if (mode == MODE_ACCESS) {
sockprintf(c->socket,
"<html><head><title>Access list for channel \"%s\"</title>"
"</head><body><h1 align=center>Access list for channel"
" \"%s\"</h1>", chanhtml, chanhtml);
ARRAY_FOREACH (i, ci->access) {
if (ci->access[i].nickgroup)
break;
}
if (i >= ci->access_count) {
sockprintf(c->socket, "<p>Access list is empty.");
} else {
int count = 0;
sockprintf(c->socket, "<div align=center>");
if (!ci->levels) {
sockprintf(c->socket, "<p><b>Note:</b> This channel's"
" levels are set to the built-in defaults.<p>");
}
sockprintf(c->socket, "<table border=1><tr>"
"<th>Nickname<th>Level<tr><td height=2>");
ARRAY_FOREACH (i, ci->access) {
if (!ci->access[i].nickgroup)
continue;
sockprintf(c->socket, "<tr><td>");
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, "<a href=\"../../nickserv/%s\">%s"
"</a>", urlbuf, buf);
} else {
sockprintf(c->socket, "<font color=red>(Error)</font>");
}
sockprintf(c->socket, "<td>%d", ci->access[i].level);
count++;
}
sockprintf(c->socket, "</table></div><p>%d entries.", count);
}
sockprintf(c->socket, "<p><a href=\"../%s\">Return to channel"
" information</a>", chanurl);
} else if (mode == MODE_AUTOKICK) {
sockprintf(c->socket,
"<html><head><title>Autokick list for channel \"%s\""
"</title></head><body><h1 align=center>Autokick list"
" for channel \"%s\"</h1>", chanhtml, chanhtml);
ARRAY_FOREACH (i, ci->akick) {
if (ci->akick[i].mask)
break;
}
if (i >= ci->akick_count) {
sockprintf(c->socket, "<p>Autokick list is empty.");
} else {
int count = 0;
sockprintf(c->socket, "<div align=center><table border=1><tr>"
"<th>Mask<th>Set by<th>Set at<th>Last used<th>Reason"
"<tr><td height=2>");
ARRAY_FOREACH (i, ci->akick) {
if (!ci->akick[i].mask)
continue;
http_quote_html(ci->akick[i].mask, buf, sizeof(buf));
sockprintf(c->socket, "<tr><td>%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,
"<td><a href=\"../../nickserv/%s\">%s</a>",
urlbuf, buf);
} else {
sockprintf(c->socket, "<td>%s", buf);
}
my_strftime(buf, sizeof(buf), ci->akick[i].set);
sockprintf(c->socket, "<td>%s", buf);
if (ci->akick[i].lastused) {
my_strftime(buf, sizeof(buf), ci->akick[i].lastused);
sockprintf(c->socket, "<td>%s", buf);
} else {
sockprintf(c->socket, "<td><font color=red>Never</font>");
}
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, "<td>%s", buf);
count++;
}
sockprintf(c->socket, "</table></div><p>%d entries.", count);
}
sockprintf(c->socket, "<p><a href=\"../%s\">Return to channel"
" information</a>", chanurl);
} else {
int need_comma = 0;
sockprintf(c->socket,
"<html><head><title>Information on channel \"%s\"</title>"
"</head><body><h1 align=center>Information on channel"
" \"%s\"</h1><div align=center>", chanhtml, chanhtml);
sockprintf(c->socket, "<table border=0 cellspacing=4>");
sockprintf(c->socket,
"<tr><th align=right valign=top>Founder: <td>");
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, "<a href=\"../nickserv/%s\">%s</a>",
urlbuf, buf);
} else {
sockprintf(c->socket, "<font color=red>(Error)</font>");
}
sockprintf(c->socket,
"<tr><th align=right valign=top>Successor: <td>");
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, "<a href=\"../nickserv/%s\">%s</a>",
urlbuf, buf);
} else {
sockprintf(c->socket, "<font color=red>(Error)</font>");
}
} else {
sockprintf(c->socket, "(None)");
}
http_quote_html(ci->desc, buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>Description:"
" <td>%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,
"<tr><th align=right valign=top>URL: "
"<td><a href=\"%s\">%s</a>", 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,
"<tr><th align=right valign=top>E-mail address: "
"<td><a href=\"mailto:%s\">%s</a>", urlbuf, buf);
}
my_strftime(buf, sizeof(buf), ci->time_registered);
sockprintf(c->socket, "<tr><th align=right valign=top>Time"
" registered: <td>%s", buf);
my_strftime(buf, sizeof(buf), ci->last_used);
sockprintf(c->socket, "<tr><th align=right valign=top>Last used:"
" <td>%s", buf);
sockprintf(c->socket,
"<tr><th align=right valign=top>Mode lock: <td>");
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,
"<tr><th align=right valign=top>Options: <td>");
if (ci->flags & CI_NOEXPIRE) {
sockprintf(c->socket, "<b>Will not expire</b>");
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, "<tr><td colspan=2><hr>");
if ((ci->flags & CI_KEEPTOPIC) && ci->last_topic) {
http_quote_html(ci->last_topic, buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>Last"
" topic:<td>%s", buf);
http_quote_html(ci->last_topic_setter, buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>Topic"
" set by:<td>%s", buf);
my_strftime(buf, sizeof(buf), ci->last_topic_time);
sockprintf(c->socket, "<tr><th align=right valign=top>Topic"
" set on: <td>%s", buf);
sockprintf(c->socket, "<tr><td colspan=2><hr>");
}
if (ci->suspendinfo) {
sockprintf(c->socket, "<tr><td colspan=2 align=center>"
"<font color=red>This channel is <b>suspended</b>."
"</font>");
my_strftime(buf, sizeof(buf), ci->suspendinfo->suspended);
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Suspended on: <td>%s", buf);
http_quote_html(ci->suspendinfo->who, buf, sizeof(buf));
http_quote_url(ci->suspendinfo->who, urlbuf, sizeof(urlbuf), 1);
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Suspended by: <td><a href=\"%s\">%s</a>",
urlbuf, buf);
http_quote_html(ci->suspendinfo->reason ? ci->suspendinfo->reason
: "", buf, sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Reason for suspension: <td>%s", buf);
if (ci->suspendinfo->expires)
my_strftime(buf, sizeof(buf), ci->suspendinfo->expires);
else
strscpy(buf, "<b>Never</b>", sizeof(buf));
sockprintf(c->socket, "<tr><th align=right valign=top>"
"Suspension expires on: <td>%s", buf);
sockprintf(c->socket, "<tr><td colspan=2><hr>");
}
sockprintf(c->socket, "<tr><th colspan=2><a href=\"%s/levels\">"
"View access level settings</a>", chanurl);
sockprintf(c->socket, "<tr><th colspan=2><a href=\"%s/access\">"
"View access list</a>", chanurl);
sockprintf(c->socket, "<tr><th colspan=2><a href=\"%s/autokick\">"
"View autokick list</a>", chanurl);
sockprintf(c->socket,
"</table></div><p><a href=./>Return to channel list</a>");
}
sockprintf(c->socket, "</body></html>");
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,
"<html><head><title>StatServ database access</title>"
"</head><body><h1 align=center>StatServ database"
" access</h1><p>Click on a server for detailed information."
"<p><a href=../>(Return to previous menu)</a><p><ul>");
for (ss = first_serverstats(); ss; ss = next_serverstats()) {
http_quote_html(ss->name, servhtml, sizeof(servhtml));
http_quote_url(ss->name, servurl, sizeof(servurl), 1);
sockprintf(c->socket, "<li><a href=\"%s\">%s (<font color=%s>"
"%sline</font>)</a>", servurl, servhtml,
ss->t_join > ss->t_quit ? "green" : "red",
ss->t_join > ss->t_quit ? "on" : "off");
count++;
}
sockprintf(c->socket, "</ul><p>%d server%s found.</body></html>",
count, count==1 ? "" : "s");
return 1;
}
http_unquote_url(path);
ss = get_serverstats(path);
http_quote_html(path, servhtml, sizeof(servhtml));
sockprintf(c->socket,
"<html><head><title>Information on server \"%s\"</title>"
"</head><body><h1 align=center>Information on server"
" \"%s\"</h1><div align=center>", servhtml, servhtml);
if (!ss) {
sockprintf(c->socket, "<p>Server \"%s\" is not known.", servhtml);
} else {
sockprintf(c->socket, "<p>Server is currently <font color=%s>%sline"
"</font>.", ss->t_join > ss->t_quit ? "green" : "red",
ss->t_join > ss->t_quit ? "on" : "off");
sockprintf(c->socket, "<table border=0 cellspacing=4>");
if (ss->t_join > ss->t_quit) {
sockprintf(c->socket, "<tr><th align=right valign=top>Current"
" user count: <td>%d", ss->usercnt);
sockprintf(c->socket, "<tr><th align=right valign=top>Current"
" operator count: <td>%d", ss->opercnt);
}
my_strftime(servhtml, sizeof(servhtml), ss->t_join);
sockprintf(c->socket, "<tr><th align=right valign=top>Time of last"
" join: <td>%s", ss->t_join ? servhtml : "none");
my_strftime(servhtml, sizeof(servhtml), ss->t_quit);
sockprintf(c->socket, "<tr><th align=right valign=top>Time of last"
" quit: <td>%s", ss->t_quit ? servhtml : "none");
http_quote_html(ss->quit_message ? ss->quit_message : "",
servhtml, sizeof(servhtml));
sockprintf(c->socket, "<tr><th align=right valign=top>Last quit"
" message: <td>%s", servhtml);
sockprintf(c->socket, "</table>");
}
sockprintf(c->socket,
"</div><p><a href=./>Return to server list</a></body></html>");
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;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1