src/http.c
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- http_html_prelude
- rfc1122_date
- small_date
- http_file_prelude
- http_emit_header
- http_emit_footer
- http_bad_url
- url_get_file
- render_idx
- html_news
- html_td
- html_td_news
- html_tdb
- html_tdl
- add_i
- html_tdi
- add_f
- html_tdf
- html_tdpercent
- html_tdd
- html_tdt
- html_newsgroup
- MAC
- MAC
- MAC
- MAC
- MAC
- MAC_BIG
- MAC
- MAC
- server_downtime
- server_uptime
- MAC
- MAC
- MAC
- MAC
- MAC
- MAC
- MAC
- macro_call
- macro_parse
- http_macro
- url_rewrite
- http_get
- http_cmd
- httpHandler
/* $Id: http.c,v 1.6 2002/03/26 11:18:35 proff Exp $
* $Copyright$
*/
#include "nglobal.h"
#include "acc.h"
#include "group.h"
#include "nlist.h"
#include "ll.h"
#include "confused_runtime.h"
#include "http.h"
#define HTTP "HTTP/1.0"
#define TABLE "<table border=2>\n"
#define ENDTABLE "</table>\n"
struct macro_func
{
char *name;
void (*func)(struct strStack *out, int argc, char **argv);
int min_args;
};
static void http_html_prelude (char *msg)
/* [<][>][^][v][top][bottom][index][help] */
{
emitf("\
%s %s\r\n\
Server: NNTPCache %s\r\n\
Connection: close\r\n\
Content-Type: text/html\r\n\r\n", HTTP, msg, VERSION);
}
EXPORT char *rfc1122_date (time_t ti)
/* [<][>][^][v][top][bottom][index][help] */
{
static char buf[80];
struct tm *tm;
tm = gmtime(&ti);
if (!tm)
{
loge (("gmtime() failed"));
return "time error"; /* XXX */
}
strftime (buf, sizeof buf, "%d %b %Y %H:%M:%S %Z", tm);
return buf;
}
static char *small_date (time_t ti)
/* [<][>][^][v][top][bottom][index][help] */
{
static char buf[80];
struct tm *tm;
tm = localtime(&ti);
if (!tm)
{
loge (("localtime() failed"));
return "time error"; /* XXX */
}
strftime (buf, sizeof buf, (time(NULL)-ti > 3600*24*180)? "%d %b %y %H:%M:%S" : "%d %b %H:%M:%S", tm);
return buf;
}
static void http_file_prelude (char *url, int len, time_t modified)
/* [<][>][^][v][top][bottom][index][help] */
{
char *p;
char *content = "text/plain";
char *pragma;
p = strrchr (url, '.');
if (p)
{
p++;
if (strCaseEq (p, "html") || strCaseEq (p, "htm"))
content = "text/html";
else
if (strCaseEq (p, "gif"))
content = "image/gif";
else
if (strCaseEq (p, "jpg") || strCaseEq (p, "jpeg"))
content = "image/jpeg";
}
if (modified == 0)
{
modified = time(NULL);
pragma = "Pragma: NoCache\r\n";
}
else
{
pragma = "";
}
emitf("\
%s 200 ok\r\n\
Server: NNTPCache %s\r\n\
Content-Type: %s\r\n\
Content-Length: %d\r\n\
Connection: close\r\n\
%s", HTTP, VERSION, content, len, pragma);
emitf("Last-Modified: %s\r\n", rfc1122_date(modified));
emitf("Date: %s\r\n\r\n", rfc1122_date(time(NULL)));
}
static void http_emit_header (char *status, char *title)
/* [<][>][^][v][top][bottom][index][help] */
{
http_html_prelude (status);
emitf ("\
<HTML>\n\
<HEAD>\n\
<TITLE>%s</TITLE>\n\
</HEAD>\n\
<BODY>\n", title);
}
static void http_emit_footer ()
/* [<][>][^][v][top][bottom][index][help] */
{
emitf ("\
</BODY>\n\
</HTML>\n");
}
static void http_bad_url (char *url)
/* [<][>][^][v][top][bottom][index][help] */
{
http_emit_header (HTTP_STATUS_NOTFOUND, "File not found");
emitf ("<H1>%s</H1>\nThe requested URL %s was not found on this server\n", "File not found", url);
http_emit_footer ();
}
static char *url_get_file (char *url, int *len, char **outfn, time_t *modified)
/* [<][>][^][v][top][bottom][index][help] */
{
struct stat st;
int fd = -1;
char *p;
char *fn;
if (strnEq (url, "../", 3) || strstr (url, "/.."))
{
logwn (("hack attempt -- URL contains \"/..\" or \"../\": '%.128s'", url));
return NULL;
}
for (fn = url; *fn && *fn == '/'; fn++) {}
if (fn[0] == '\0')
fn = "index.html";
*outfn = fn;
if (stat (fn, &st) != 0 || st.st_size < 1)
return NULL;
if (st.st_mode & S_IFDIR)
{
static char path[MAX_PATH]; /* note static */
snprintf(path, sizeof path, "%s/index.html", fn);
*outfn = fn = path;
if (stat (fn, &st) != 0 || st.st_size < 1)
return NULL;
}
*outfn = fn;
fd = open(fn, O_RDONLY);
if (fd<0)
return NULL;
p = Smalloc (st.st_size);
if (read (fd, p, st.st_size) != st.st_size)
{
loge (("read ('%s') failed", fn));
free (p);
close (fd);
return NULL;
}
*len = st.st_size;
*modified = st.st_mtime;
return p;
}
static char *render_idx(struct confused_idx *idx, char **t)
/* [<][>][^][v][top][bottom][index][help] */
{
static char buf[MAX_LINE] = "unknown";
struct strList *sl;
char *p;
int l;
char *ret = buf;
switch (idx->type)
{
case cf_string:
*t = "string";
ret = *(char**)idx->data;
break;
case cf_stringl:
*t = "list";
for (p=buf, sl = *(struct strList**)idx->data; sl; sl=sl->next)
{
l = strlen(sl->data);
if (!l)
continue;
if (p != buf)
{
memcpy(p, ", ", 3);
p += 2;
}
memcpy(p, sl->data, l+1);
p += l;
}
break;
case cf_bool:
*t = "bool";
ret = (*(bool*)idx->data)? "true": "false";
break;
case cf_int:
*t = "int";
sprintf(buf, "%d", *(int*)idx->data);
break;
case cf_time:
*t = "time";
ret = nnitod(*(long*)idx->data);
break;
default:
*t = "unknown";
break;
}
return ret;
}
static void html_news(struct strStack *out, char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
strStackAdd(out, "<a href=\"news:");
strStackAdd(out, s);
strStackAdd(out, "\">");
strStackAdd(out, s);
strStackAdd(out, "</a>");
}
static void html_td(struct strStack *out, char *s, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
strStackAdd(out, "<td align=");
strStackAdd(out, align);
strStackAdd(out, ">");
if (s && s[0])
strStackAdd(out, s);
strStackAdd(out, "</td>");
}
static void html_td_news(struct strStack *out, char *s, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
strStackAdd(out, "<td align=");
strStackAdd(out, align);
strStackAdd(out, ">");
if (s)
html_news(out, s);
strStackAdd(out, "</td>");
}
static void html_tdb(struct strStack *out, big_t big, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
if (big == 0)
html_td(out, NULL, align);
else
html_td(out, bigToStr(big), align);
}
static void html_tdl(struct strStack *out, big_t big, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
if (big == 0)
html_td(out, NULL, align);
else
html_td(out, (conv(big)[0] == '0')? "": conv(big), align);
}
static void add_i(struct strStack *out, int i)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf [128];
sprintf(buf, "%d", i);
strStackAdd(out, buf);
}
static void html_tdi(struct strStack *out, int i, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf [32];
if (i == 0)
{
html_td(out, NULL, align);
return;
}
sprintf(buf, "%d", i);
html_td(out, buf, align);
}
static void add_f(struct strStack *out, float f)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf [128];
sprintf(buf, "%.02f", f);
strStackAdd(out, buf);
}
static void html_tdf(struct strStack *out, float f, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf [128];
if (f == 0.0)
{
html_td(out, NULL, align);
return;
}
sprintf(buf, "%.02f", f);
html_td(out, buf, align);
}
static void html_tdpercent(struct strStack *out, float f, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf [128];
if (f == 0.0)
{
html_td(out, NULL, align);
return;
}
sprintf(buf, "%.2f%%", f*100.0);
html_td(out, buf, align);
}
static void html_tdd(struct strStack *out, time_t ti, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
if (ti == 0)
{
html_td(out, NULL, align);
return;
}
html_td(out, small_date(ti), align);
}
static void html_tdt(struct strStack *out, long i, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
if (i == 0)
html_td(out, NULL, align);
else
html_td(out, nnitod(i), align);
}
/* argv[0] == first newsgroup */
static void html_newsgroup(struct strStack *out, int argc, char **argv, struct newsgroup *ng)
/* [<][>][^][v][top][bottom][index][help] */
{
int n;
strStackAdd(out, "<tr>");
for (n=0; n<argc; n++)
{
char *s = argv[n];
char *p = strchr(s, '-');
if (p)
*p = '\0';
if (strCaseEq(s, "Group")) html_td_news(out, ng->group, "left"); else
if (strCaseEq(s, "Messages")) html_tdi(out, ng->msgs, "right"); else
if (strCaseEq(s, "Lo")) html_tdi(out, ng->lo, "right"); else
if (strCaseEq(s, "LoServer")) html_tdi(out, ng->lo_server, "right"); else
if (strCaseEq(s, "Hi")) html_tdi(out, ng->hi, "right"); else
if (strCaseEq(s, "HiServer")) html_tdi(out, ng->hi_server, "right"); else
if (strCaseEq(s, "LoXover")) html_tdi(out, ng->lo_xover, "right"); else
if (strCaseEq(s, "HiXover")) html_tdi(out, ng->hi_xover, "right"); else
if (strCaseEq(s, "Mod")) {char b[2]="y"; b[0]=ng->moderation; html_td(out, b, "center");} else
if (strCaseEq(s, "Creator")) html_td(out, ng->creator, "center"); else
if (strCaseEq(s, "Creation")) html_tdd(out, ng->creation_time, "right"); else
if (strCaseEq(s, "Rebuild")) html_tdd(out, ng->last_rebuild, "right"); else
if (strCaseEq(s, "GroupTime")) html_tdd(out, ng->group_time, "right"); else
if (strCaseEq(s, "GroupChange")) html_tdd(out, ng->group_change_time, "right"); else
if (strCaseEq(s, "ListGroup")) html_tdd(out, ng->listgroup_time, "right"); else
if (strCaseEq(s, "Description")) html_td(out, ng->desc, "left"); else
if (strCaseEq(s, "Server")) {struct server_cfg *scfg = getServerGroup(ng->group); html_td(out, scfg? scfg->host: NULL, "right");} else
{logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
if (p)
*p = '-';
}
strStackAdd(out, "</tr>\n");
}
#define MAC(x) \
static void macro_ ## x (struct strStack *out, int argc, char **argv)
#define add(x) (strStackAdd(out, (x)))
#define MAC_BIG(x) MAC(x) {if (Stats->x) add(bigToStr(Stats->x));}
#define MAC_STR(x) MAC(x) {add(x);}
#define MAC_LEN(x) MAC(x) {add((conv(Stats->x)[0] == '0')? "": conv(Stats->x));}
#define MAC_TIM(x) MAC(x) {add(small_date((Stats->x)));}
#define CPU2BIG(x) ((x)/(CLK_TCK))
MAC(version) {add(VERSION);}
/* [<][>][^][v][top][bottom][index][help] */
MAC(hostname) {add(Host);}
/* [<][>][^][v][top][bottom][index][help] */
MAC(date) {add(small_date(time(NULL)));}
/* [<][>][^][v][top][bottom][index][help] */
MAC(clienthost) {add(ClientHost);}
/* [<][>][^][v][top][bottom][index][help] */
MAC(efficiency) {add_f(out, (1.0-((float)(Stats->serverFromBytes+Stats->serverToBytes+1)/(float)(Stats->clientToBytes+Stats->clientFromBytes+1)))*100.0);}
/* [<][>][^][v][top][bottom][index][help] */
MAC_BIG(IPCfromChild)
/* [<][>][^][v][top][bottom][index][help] */
MAC_LEN(IPCfromChildBytes)
MAC_BIG(IPCtoChild)
MAC_LEN(IPCtoChildBytes)
MAC_BIG(articlesExpired)
MAC_BIG(clientConnects)
MAC_BIG(clientConnectsFailed)
MAC_LEN(clientFromBytes)
MAC_LEN(clientToBytes)
MAC_BIG(clientsActive)
MAC_BIG(crossposts)
MAC_LEN(crosspostsBytes)
MAC_BIG(groupsCached)
MAC_BIG(groupsExpired)
MAC_BIG(xoversExpired)
MAC_BIG(historyFetches)
MAC_LEN(historySize)
MAC_BIG(historyStores)
MAC_TIM(masterStarted)
MAC_BIG(posts)
MAC_BIG(postsCross)
MAC_LEN(postsBytes)
MAC_BIG(postsFailed)
MAC_BIG(serverConnects)
MAC_BIG(serverConnectsFailed)
MAC_LEN(serverFromBytes)
MAC_LEN(serverToBytes)
MAC_BIG(invocations)
MAC_TIM(statsStarted)
MAC(conftable)
{
struct confused_idx *idx;
strStackAdd(out, TABLE);
strStackAdd(out, "<tr><th align=left>Type</th><th align=left>Name</th><th align=left>Value</th></tr>\n");
for (idx = nnconf_idx; idx->name; idx++)
{
char *type;
char *val;
strStackAdd(out, "<tr>");
val = render_idx (idx, &type);
html_td(out, type, "left");
html_td(out, idx->name, "left");
html_td(out, val, "left");
strStackAdd(out, "</tr>\n");
}
strStackAdd(out, ENDTABLE);
}
MAC(html_th)
/* [<][>][^][v][top][bottom][index][help] */
{
int n;
strStackAdd(out, "<tr>");
for (n=0; n<argc; n++)
{
char *p;
strStackAdd(out, "<th align=center>");
p = strchr(argv[n], '-');
strStackAdd(out, p? p+1: argv[n]);
strStackAdd(out, "</th>");
}
strStackAdd(out, "</tr>\n");
}
MAC(html_table)
/* [<][>][^][v][top][bottom][index][help] */
{
strStackAdd(out, TABLE);
macro_html_th(out, argc, argv);
}
static int server_downtime(struct server_cfg *p)
/* [<][>][^][v][top][bottom][index][help] */
{
if (p->share->server_down > p->share->server_up)
return p->share->server_down_time + (time(NULL) - p->share->server_down);
else
return p->share->server_down_time;
}
static int server_uptime(struct server_cfg *p)
/* [<][>][^][v][top][bottom][index][help] */
{
return p->share->server_up_time + ((p->share->server_up > p->share->server_down)? time(NULL) - p->share->server_up: 0);
}
MAC(servers)
/* [<][>][^][v][top][bottom][index][help] */
{
struct server_cfg *p;
macro_html_table(out, argc-1, argv+1);
for (p = ServerList; p; p = p->next)
{
int n;
strStackAdd(out, "<tr>");
for (n=1; n<argc; n++)
{
char *s = argv[n];
char *s2 = strchr(s, '-');
if (s2)
*s2 = '\0';
if (strCaseEq(s, "ActiveBytes")) html_tdl(out, p->share->list[l_active].bytes, "right"); else
if (strCaseEq(s, "ActiveEntries")) html_tdb(out, p->share->list[l_active].entries, "right"); else
if (strCaseEq(s, "ActiveLines")) html_tdb(out, p->share->list[l_active].lines, "right"); else
if (strCaseEq(s, "ActiveRebuildFail")) html_tdd(out, p->share->list[l_active].rebuild_fail, "right"); else
if (strCaseEq(s, "ActiveRebuildGood")) html_tdd(out, p->share->list[l_active].rebuild_good, "right"); else
if (strCaseEq(s, "ActiveRebuildRefused")) html_tdd(out, p->share->list[l_active].rebuild_refused, "right"); else
if (strCaseEq(s, "ActiveTimesBytes")) html_tdl(out, p->share->list[l_active_times].bytes, "right"); else
if (strCaseEq(s, "ActiveTimesEntries")) html_tdb(out, p->share->list[l_active_times].entries, "right"); else
if (strCaseEq(s, "ActiveTimesLines")) html_tdb(out, p->share->list[l_active_times].lines, "right"); else
if (strCaseEq(s, "ActiveTimesRebuildFail")) html_tdd(out, p->share->list[l_active_times].rebuild_fail, "right"); else
if (strCaseEq(s, "ActiveTimesRebuildGood")) html_tdd(out, p->share->list[l_active_times].rebuild_good, "right"); else
if (strCaseEq(s, "ActiveTimesRebuildRefused")) html_tdd(out, p->share->list[l_active_times].rebuild_refused, "right"); else
if (strCaseEq(s, "Host")) html_td(out, p->host, "left"); else
if (strCaseEq(s, "NewsgroupsBytes")) html_tdl(out, p->share->list[l_newsgroups].bytes, "right"); else
if (strCaseEq(s, "NewsgroupsEntries")) html_tdb(out, p->share->list[l_newsgroups].entries, "right"); else
if (strCaseEq(s, "NewsgroupsRebuildGood")) html_tdd(out, p->share->list[l_newsgroups].rebuild_good, "right"); else
if (strCaseEq(s, "NewsgroupsLines")) html_tdb(out, p->share->list[l_newsgroups].lines, "right"); else
if (strCaseEq(s, "NewsgroupsRebuildFail")) html_tdd(out, p->share->list[l_newsgroups].rebuild_fail, "right"); else
if (strCaseEq(s, "NewsgroupsRebuildRefused")) html_tdd(out, p->share->list[l_newsgroups].rebuild_refused, "right"); else
if (strCaseEq(s, "OverviewFmtBytes")) html_tdl(out, p->share->list[l_overview_fmt].bytes, "right"); else
if (strCaseEq(s, "OverviewFmtEntries")) html_tdb(out, p->share->list[l_overview_fmt].entries, "right"); else
if (strCaseEq(s, "OverviewFmtLines")) html_tdb(out, p->share->list[l_overview_fmt].lines, "right"); else
if (strCaseEq(s, "OverviewFmtRebuildFail")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_fail, "right"); else
if (strCaseEq(s, "OverviewFmtRebuildGood")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_good, "right"); else
if (strCaseEq(s, "OverviewFmtRebuildRefused")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_refused, "right"); else
if (strCaseEq(s, "ArticleFail")) html_tdb(out, p->share->article_fail, "right"); else
if (strCaseEq(s, "ArticleGood")) html_tdb(out, p->share->article_good, "right"); else
if (strCaseEq(s, "BodyFail")) html_tdb(out, p->share->body_fail, "right"); else
if (strCaseEq(s, "BodyGood")) html_tdb(out, p->share->body_good, "right"); else
if (strCaseEq(s, "BytesTo")) html_tdl(out, p->share->bytes_to, "right"); else
if (strCaseEq(s, "ConnectFail")) html_tdb(out, p->share->connect_fail, "right"); else
if (strCaseEq(s, "GroupFail")) html_tdb(out, p->share->group_fail, "right"); else
if (strCaseEq(s, "GroupGood")) html_tdb(out, p->share->group_good, "right"); else
if (strCaseEq(s, "HeadFail")) html_tdb(out, p->share->head_fail, "right"); else
if (strCaseEq(s, "HeadGood")) html_tdb(out, p->share->head_good, "right"); /* mmm. good head */ else
if (strCaseEq(s, "IhaveFail")) html_tdb(out, p->share->ihave_fail, "right"); else
if (strCaseEq(s, "IhaveGood")) html_tdb(out, p->share->ihave_good, "right"); else
if (strCaseEq(s, "ListgroupFail")) html_tdb(out, p->share->listgroup_fail, "right"); else
if (strCaseEq(s, "ListgroupGood")) html_tdb(out, p->share->listgroup_good, "right"); else
if (strCaseEq(s, "NewnewsFail")) html_tdb(out, p->share->newnews_fail, "right"); else
if (strCaseEq(s, "NewnewsGood")) html_tdb(out, p->share->newnews_good, "right"); else
if (strCaseEq(s, "NewsgroupsRebuild")) html_tdd(out, p->share->list[l_newsgroups].rebuild_good, "right"); else
if (strCaseEq(s, "OverviewFmtRebuild")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_good, "right"); else
if (strCaseEq(s, "PostFail")) html_tdb(out, p->share->post_fail, "right"); else
if (strCaseEq(s, "PostGood")) html_tdb(out, p->share->post_good, "right"); else
if (strCaseEq(s, "RelayFail")) html_tdb(out, p->share->relay_fail, "right"); else
if (strCaseEq(s, "RelayGood")) html_tdb(out, p->share->relay_good, "right"); else
if (strCaseEq(s, "ServerDown")) html_tdd(out, p->share->server_down, "right"); else
if (strCaseEq(s, "ServerUp")) html_tdd(out, p->share->server_up, "right"); else
if (strCaseEq(s, "StatFail")) html_tdb(out, p->share->stat_fail, "right"); else
if (strCaseEq(s, "StatGood")) html_tdb(out, p->share->stat_good, "right"); else
if (strCaseEq(s, "Us")) html_td(out, p->us, "left"); else
if (strCaseEq(s, "XhdrFail")) html_tdb(out, p->share->xhdr_fail, "right"); else
if (strCaseEq(s, "XhdrGood")) html_tdb(out, p->share->xhdr_good, "right"); else
if (strCaseEq(s, "XoverFail")) html_tdb(out, p->share->xover_fail, "right"); else
if (strCaseEq(s, "XoverGood")) html_tdb(out, p->share->xover_good, "right"); else
if (strCaseEq(s, "ConnectGood")) html_tdb(out, p->share->connect_good, "right"); else
if (strCaseEq(s, "Availability")) {float f = (float)server_downtime(p)+(float)server_uptime(p); html_tdpercent(out, (f>0.0)? (float)server_uptime(p)/f: 0.0, "right");} else
if (strCaseEq(s, "BytesFrom")) html_tdl(out, p->share->bytes_from, "right"); else
if (strCaseEq(s, "DownTime")) html_tdt(out, server_downtime(p), "right"); else
if (strCaseEq(s, "UpTime")) html_tdt(out, server_uptime(p), "right"); else
{logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
if (s2)
*s2 = '-';
}
strStackAdd(out, "</tr>\n");
}
strStackAdd(out, ENDTABLE);
}
MAC(cacheStats)
/* [<][>][^][v][top][bottom][index][help] */
{
int m;
macro_html_table(out, argc-1, argv+1);
for (m = 0; m<c_none; m++)
{
int n;
struct cache_stats *p = &Stats->cache_stats[m];
strStackAdd(out, "<tr>");
for (n=1; n<argc; n++)
{
char *s = argv[n];
char *s2 = strchr(s, '-');
if (s2)
*s2 = '\0';
if (strCaseEq(s, "Type")) {struct command *c=commands;for(;c->cmd;c++) if (c->val == m) {html_td(out, c->cmd, "left");break;}} else
if (strCaseEq(s, "Requests")) html_tdb(out, p->requests, "right"); else
if (strCaseEq(s, "RequestsGood")) html_tdb(out, p->requests_good, "right"); else
if (strCaseEq(s, "RequestsFailed")) html_tdb(out, p->requests_failed, "right"); else
if (strCaseEq(s, "FilterBlocked")) html_tdb(out, p->filter_blocked, "right"); else
if (strCaseEq(s, "AuthBlocked")) html_tdb(out, p->auth_blocked, "right"); else
if (strCaseEq(s, "NocemBlocked")) html_tdb(out, p->nocem_blocked, "right"); else
if (strCaseEq(s, "ServerFromBytes")) html_tdl(out, p->serverFromBytes, "right"); else
if (strCaseEq(s, "ClientToBytes")) html_tdl(out, p->clientToBytes, "right"); else
if (strCaseEq(s, "ServerToBytes")) html_tdl(out, p->serverToBytes, "right"); else
if (strCaseEq(s, "ClientFromBytes")) html_tdl(out, p->clientFromBytes, "right"); else
if (strCaseEq(s, "ByMsgid")) html_tdb(out, p->by_msgid, "right"); else
if (strCaseEq(s, "ByArtnum")) html_tdb(out, p->by_artnum, "right"); else
{logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); add("<td></td>");}
if (s2)
*s2 = '-';
}
add("</tr>\n");
}
add(ENDTABLE);
}
MAC(nocem)
/* [<][>][^][v][top][bottom][index][help] */
{
int n;
struct strList *sl;
macro_html_table(out, argc-1, argv+1);
for (n=0, sl = con->nocemGroups; sl && n<MAX_NOCEM; sl=sl->next, n++)
{
struct nocem_stats *p = &Stats->nocem_stats[n];
strStackAdd(out, "<tr>");
for (n=1; n<argc; n++)
{
char *s = argv[n];
char *s2 = strchr(s, '-');
if (s2)
*s2 = '\0';
if (strCaseEq(s, "Group")) html_td(out, sl->data, "left"); else
if (strCaseEq(s, "ArtHi")) html_tdi(out, p->art_hi, "right"); else
if (strCaseEq(s, "ArtGood")) html_tdb(out, p->art_good, "right"); else
if (strCaseEq(s, "ArtFail")) html_tdb(out, p->art_fail, "right"); else
if (strCaseEq(s, "MsgidGood")) html_tdb(out, p->msgid_good, "right"); else
if (strCaseEq(s, "MsgidDup")) html_tdb(out, p->msgid_dup, "right"); else
if (strCaseEq(s, "MsgidFail")) html_tdb(out, p->msgid_fail, "right"); else
if (strCaseEq(s, "LastScan")) html_tdd(out, p->last_scan, "right"); else
if (strCaseEq(s, "ArtSkip")) html_tdb(out, p->art_skip, "right"); else
if (strCaseEq(s, "BytesFrom")) html_tdl(out, p->bytes_from, "right"); else
if (strCaseEq(s, "PGPgood")) html_tdb(out, p->pgp_good, "right"); else
if (strCaseEq(s, "PGPfail")) html_tdb(out, p->pgp_fail, "right"); else
{logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
if (s2)
*s2 = '-';
}
strStackAdd(out, "</tr>\n");
}
strStackAdd(out, ENDTABLE);
}
MAC(tasklist)
/* [<][>][^][v][top][bottom][index][help] */
{
int tn;
struct task_info *task;
macro_html_table(out, argc-1, argv+1);
for (tn=0; tn<=Stats->task_high; tn++)
{
int n;
task = &TaskList[tn]; /* XXX atomic locking */
if (task->ti_state == nc_none)
continue;
strStackAdd(out, "<tr>");
for (n=1; n<argc; n++)
{
char *s = argv[n];
char *s2 = strchr(s, '-');
if (s2)
*s2 = '\0';
if (strCaseEq(s, "Type")) html_td(out, task->ti_name, "left"); else
if (strCaseEq(s, "Pid")) html_tdi(out, task->ti_pid, "right"); else
if (strCaseEq(s, "Started")) html_tdd(out, task->ti_started, "right"); else
if (strCaseEq(s, "Credentials")) html_td(out, task->ti_client_host, "right"); else
if (strCaseEq(s, "Server")) html_td(out, task->ti_CurrentScfg? task->ti_CurrentScfg->host: NULL, "right"); else
if (strCaseEq(s, "Group")) html_td(out, task->ti_CurrentGroup, "left"); else
if (strCaseEq(s, "Arts")) html_tdi(out, task->ti_ArtRead, "right"); else
if (strCaseEq(s, "Groups")) html_tdi(out, task->ti_GroupsEntered, "right"); else
if (strCaseEq(s, "Status")) html_td(out, task->ti_status_line, "left"); else
{logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
if (s2)
*s2 = '-';
}
strStackAdd(out, "</tr>\n");
}
strStackAdd(out, ENDTABLE);
}
MAC(newsgroups)
/* [<][>][^][v][top][bottom][index][help] */
{
struct newsgroup *n;
char *pat;
pat = argv[1];
macro_html_table (out, argc-2, argv+2);
for (n=Ni->newsgroup_head; n; n=n->next)
{
newsgroupLockRead(n);
if (match (pat, n->group, 1, 0))
html_newsgroup(out, argc-2, argv+2, n);
newsgroupUnlockRead(n);
}
strStackAdd(out, ENDTABLE);
}
MAC(lists)
/* [<][>][^][v][top][bottom][index][help] */
{
int n;
macro_html_table(out, argc-1, argv+1);
add("<tr>\n");
add("<th align=center>List</th>");
add("<th align=center>Entries</th>");
add("<th align=center>Length</th>");
add("<th align=center>Requests</th>");
for (n = 0; lists[n].name; n++)
{
struct list_stats *t = &Stats->list_stats[lists[n].type];
add("<tr>");
html_td(out, lists[n].name, "left");
html_tdb(out, t->entries, "right");
html_tdl(out, t->len, "right");
html_tdb(out, t->cache_stats.requests, "right");
add("</tr>\n");
}
add(ENDTABLE);
}
MAC(cputab)
/* [<][>][^][v][top][bottom][index][help] */
{
int n;
macro_html_table(out, argc-1, argv+1);
add("<tr>\n");
add("<th align=center>Type</th>");
add("<th align=center>Num</th>");
add("<th align=center>Real</th>");
add("<th align=center>CPU</th>");
add("<th align=center>User</th>");
add("<th align=center>System</th>");
for (n = nc_none+1; n<nc_last; n++)
{
struct task_stats *t = &Stats->task_stats[n];
add("<tr>");
html_td(out, task_desc[n], "left");
html_tdb(out, t->invocations, "right");
html_tdt(out, t->elapsed, "right");
html_tdt(out, CPU2BIG(t->cpu_user+t->cpu_system), "right");
html_tdt(out, CPU2BIG(t->cpu_user), "right");
html_tdt(out, CPU2BIG(t->cpu_system), "right");
add("</tr>\n");
}
add(ENDTABLE);
}
struct macro_func macro[] =
{
#define S(x,y) {#x , macro_ ##x , y}
S(IPCfromChild, 0),
S(IPCfromChildBytes, 0),
S(IPCtoChild, 0),
S(IPCtoChildBytes, 0),
S(articlesExpired, 0),
S(clientConnects, 0),
S(clientConnectsFailed, 0),
S(clientFromBytes, 0),
S(clientToBytes, 0),
S(clienthost, 0),
S(clientsActive, 0),
S(conftable, 0),
S(cputab, 0),
S(crossposts, 0),
S(crosspostsBytes, 0),
S(date, 0),
S(efficiency, 0),
S(groupsCached, 0),
S(groupsExpired, 0),
S(xoversExpired, 0),
S(historyFetches, 0),
S(historySize, 0),
S(historyStores, 0),
S(hostname, 0),
S(invocations, 0),
S(lists, 0),
S(masterStarted, 0),
S(newsgroups, 2),
S(nocem, 1),
S(posts, 0),
S(postsBytes, 0),
S(postsCross, 0),
S(postsFailed, 0),
S(serverConnects, 0),
S(serverConnectsFailed, 0),
S(serverFromBytes, 0),
S(serverToBytes, 0),
S(servers, 1),
S(statsStarted, 0),
S(tasklist, 1),
S(version, 0),
S(cacheStats, 1),
{NULL, NULL, 0}
#undef S
};
static void macro_call (struct strStack *out, int argc, char **argv)
/* [<][>][^][v][top][bottom][index][help] */
{
struct macro_func *s;
for (s = macro; s->name; s++)
if (strCaseEq(argv[0], s->name))
{
if (s->min_args+1>argc)
{
logen (("nntpcache macro @@%.128s@@ requires a minimum of %d argument%s",
argv[0], s->min_args, (s->min_args == 1)? "": "s"));
return;
}
s->func(out, argc, argv);
return;
}
logen (("nntpcache macro @@%.128s@@ not recognised", argv[0]));
}
/*
* returns NULL if document contained no @@'s
*/
static struct strStack *macro_parse (char *in, int len, int *expansions)
/* [<][>][^][v][top][bottom][index][help] */
{
struct strStack *out = NULL;
int argc = 0;
char *p;
*expansions = 0;
for (p = in;;)
{
struct strStack *argvs;
char *p2;
p2 = strstr(p, "@@");
if (!p2)
{
out = strnStackAdd(out, p, len - (p-in));
break;
}
(*expansions)++;
if (p2 - p > 0)
out = strnStackAdd(out, p, p2-p);
p2 += 2;
p = strstr(p2, "@@");
if (!p)
{
logen (("unbalanced @@ macro sequence"));
break;
}
if (p == p2) /* @@@@ -> @@ */
{
out = strStackAdd(out, "@@");
p += 2;
continue;
}
*p = '\0';
p += 2;
for (argvs = NULL, argc=0; *p2;)
{
SKIPWHITE(p2);
if (!*p2)
break;
argvs = strnStackAdd (argvs, (char*)&p2, sizeof p2); /* accepts binary data */
argc++;
SKIPNOWHITE(p2);
if (!*p2)
break;
*p2++ = '\0';
}
if (!argvs)
continue;
p2 = NULL;
argvs = strnStackAdd (argvs, (char*)&p2, sizeof p2);
macro_call (out, argc, (char**)(argvs->data));
strStackFree(argvs);
}
return out;
}
static bool http_macro (char *url)
/* [<][>][^][v][top][bottom][index][help] */
{
int len;
char *in;
struct strStack *out;
char *fn;
char *p;
int expands;
time_t modified;
in = url_get_file (url, &len, &fn, &modified);
if (!in)
{
bad:
http_bad_url (fn);
return FALSE;
}
p = strrchr (fn, '.');
if (!p++ || !(strCaseEq (p, "html") || strCaseEq (p, "htm")))
{
http_file_prelude (fn, len, modified);
fwriteClient (in, len);
free (in);
return TRUE;
}
out = macro_parse (in, len, &expands);
free (in);
if (!out)
goto bad;
http_file_prelude (fn, out->used, (expands>0)? 0: modified);
fwriteClient (out->data, out->used);
strStackFree (out);
return TRUE;
}
static char *url_rewrite (char *url)
/* [<][>][^][v][top][bottom][index][help] */
{
if (strnCaseEq (url, "http://", sizeof ("http://") -1))
{
char *p;
url+=sizeof("http://") -1;
p = strchr (url, '/');
if (p)
url = p+1;
else
url = "";
}
return url;
}
static bool http_get (char *url)
/* [<][>][^][v][top][bottom][index][help] */
{
return http_macro (url_rewrite(url));
}
static bool http_cmd ()
/* [<][>][^][v][top][bottom][index][help] */
{
char buf[MAX_URL]; /* security: make sure these are all the same length */
char cmd[MAX_URL];
char url[MAX_URL];
settaskinfo("%s: waiting for http input", ClientHost);
if (!Get (buf, sizeof buf))
{
logd (("http client disconencted before GET"));
settaskinfo("%s diconnected before GET", ClientHost);
return FALSE;
}
if (sscanf (buf, "%s %s", cmd, url) != 2)
{
http_emit_header (HTTP_STATUS_BADREQUEST,"Bad Request");
emitf ("<H1>Bad Request</H1> Your browser sent a query that this server could not understand.<P>");
http_emit_footer ();
return FALSE;
}
settaskinfo("%s: %.80s %.80s", ClientHost, cmd, url);
if (strCaseEq (cmd, "GET"))
return http_get (url);
http_emit_header (HTTP_STATUS_BADREQUEST,"Bad Request");
emitf ("<H1>Bad Request</H1> Your browser sent a query that this server could not understand.<P>");
http_emit_footer ();
return FALSE;
}
EXPORT bool httpHandler()
/* [<][>][^][v][top][bottom][index][help] */
{
bool ret;
while (HoldForksHttp) {}
alarm(con->idleTimeout);
settaskinfo("authenticating http client");
if (chdir (con->httpFiles) !=0)
{
loge (("chdir ('%s') failed", con->httpFiles));
http_emit_header (HTTP_STATUS_FORBIDDEN,"Access Forbidden");
emitf ("<H1>Access denied</H1> Access Forbidden-- '%s' inaccessable<P>\r\n", con->httpFiles);
http_emit_footer ();
flush ();
return FALSE;
}
if (!fillAuth(fileno(clientin), "<http>"))
{
http_emit_header (HTTP_STATUS_FORBIDDEN,"Access Forbidden");
emitf ("<H1>Access Forbidden</H1> [%s], you do not have connect permissions in the %s file.<P>\r\n", ClientHost, con->accessFile);
flush ();
log (("refused connect from %s (%s)", ClientHost, ClientHostAddr));
return FALSE;
}
log (("connect from %s (%s)", ClientHost, ClientHostAddr));
settaskinfo("%s: http starting", ClientHost);
ret = http_cmd ();
flush ();
return ret;
}