/*
* Copyright (C) 2000,2001 Florian Sander
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
static struct dcc_table LIVESTATS_LISTEN =
{
"LIVESTATS_LISTEN",
DCT_VALIDIDX,
eof_livestats,
livestats_accept,
0,
timeout_listen_livestats,
display_livestats_accept,
0,
NULL,
0
};
static struct dcc_table LIVESTATS =
{
"LIVESTATS",
DCT_VALIDIDX,
eof_livestats,
livestats_activity,
&livestats_timeout,
timeout_livestats,
display_livestats,
expmem_livestats,
kill_livestats,
#ifdef OLDBOT
out_livestats,
#else
out_livestats,
outdone_livestats
#endif
};
static int inactivechan(char *chan)
{
struct chanset_t *ch;
ch = findchan_by_dname(chan);
if (!ch)
return 1;
if (ch->status & CHAN_INACTIVE)
return 1;
return 0;
}
static int nostats(char *chan)
{
#if EGG_IS_MIN_VER(10503)
if (ngetudef("nostats", chan))
return 1;
#endif
return 0;
}
static void start_listen_livestats(int port)
{
int i, zz;
char tmp[50];
stop_listen_livestats();
sprintf(tmp, "set my-ip \"%s\";", livestats_ip);
do_tcl("livestats-hack-start",
"set my-ip-livestats-backup ${my-ip};"
"set my-hostname-livestats-backup ${my-hostname};"
"set my-hostname \"\"");
do_tcl("livestats-hack-setip", tmp);
zz = open_listen(&port);
do_tcl("livestats-hack-end",
"set my-ip ${my-ip-livestats-backup};"
"set my-hostname ${my-hostname-livestats-backup}");
if (zz == (-1)) {
putlog(LOG_MISC, "*", "ERROR! Cannot open listening socket for livestats!");
return;
}
if ((i = new_dcc(&LIVESTATS_LISTEN, 0)) == -1) {
putlog(LOG_MISC, "*", "ERROR! Cannot open listening socket for livestats! DCC table is full!");
return;
}
dcc[i].sock = zz;
dcc[i].addr = (IP) (-559026163);
dcc[i].port = port;
strcpy(dcc[i].nick, "livestats");
strcpy(dcc[i].host, "*");
dcc[i].timeval = now;
putlog(LOG_MISC, "*", "Now listening for livestats connections on port %d", port);
}
static void stop_listen_livestats()
{
int i;
for (i = 0; i < dcc_total; i++) {
if (dcc[i].type == &LIVESTATS_LISTEN) {
putlog(LOG_MISC, "*",
"no longer listening for livestats connections on port %d",
dcc[i].port);
killsock(dcc[i].sock);
lostdcc(i);
} else if (dcc[i].type == &LIVESTATS) {
putlog(LOG_MISC, "*", "killing livestats connection from %s", dcc[i].host);
killsock(dcc[i].sock);
lostdcc(i);
}
}
}
static void livestats_activity(int idx, char *buf, int len)
{
char *cmd, *path, *newpath, *imask;
int lev;
imask = nmalloc(strlen(dcc[idx].host) + 13);
sprintf(imask, "livestats!*@%s", dcc[idx].host);
if (match_ignore(imask)) {
debug1("Ignoring livestats access from %s", dcc[idx].host);
stats_info_access(idx)->code = 401;
if (livestats_ignore_msg[0])
dprintf(idx, "HTTP/1.0 401 Access Forbidden\nContent-Type: text/html\n\n%s", livestats_ignore_msg);
killsock(dcc[idx].sock);
lostdcc(idx);
nfree(imask);
return;
}
nfree(imask);
if (!strncasecmp(buf, "GET ", 4)) {
if (!stats_info_access(idx)->cmd) {
stats_info_access(idx)->cmd = nmalloc(strlen(buf) + 1);
strcpy(stats_info_access(idx)->cmd, buf);
stats_info_access(idx)->code = 200;
}
}
cmd = newsplit(&buf);
if (!strcmp(cmd, "GET")) {
if (livestats_flood()) {
stats_info_access(idx)->code = 401;
killsock(dcc[idx].sock);
lostdcc(idx);
return;
}
lev = logmodes(stats_loglevel);
if (lev)
putlog(lev, "*", "%s: GET %s", dcc[idx].host, buf);
path = newsplit(&buf);
if (!strncasecmp(path, "/robots.txt", 11)) {
dprintf(idx, "HTTP/1.1 404 Not Found\nServer: stats.mod/%s\nConnection: close", MODULE_VERSION);
dprintf(idx, "Content-Type: text/html; charset=iso-8859-1\n\n");
dprintf(idx, "
404 Not FoundNot Found
");
dprintf(idx, "The requested document was not found on this server.");
stats_info_access(idx)->code = 404;
return;
}
if (path[strlen(path) - 1] != '/') {
newpath = nmalloc(strlen(path) + 2);
strcpy(newpath, path);
newpath[strlen(path)] = '/';
newpath[strlen(path) + 1] = 0;
dprintf(idx, "HTTP/1.1 301 Moved Permanently\nServer: stats.mod/%s\n", MODULE_VERSION);
dprintf(idx, "Location: %s\nConnection: close\nContent-Type: text/html\n\n", newpath);
dprintf(idx, "The concluding \"/\" is important!
");
dprintf(idx, "%s
", newpath, newpath);
stats_info_access(idx)->code = 301;
nfree(newpath);
return;
}
dprintf(idx, "HTTP/1.0 200 OK\nServer: stats.mod/%s\nContent-Type: text/html\n\n", MODULE_VERSION);
send_livestats(idx, path);
} else if (!strcasecmp(cmd, "User-Agent:")) {
if (stats_info_access(idx)->browser)
return;
stats_info_access(idx)->browser = nmalloc(strlen(buf) + 1);
strcpy(stats_info_access(idx)->browser, buf);
} else if (!strcasecmp(cmd, "Referer:")) {
if (stats_info_access(idx)->referer)
return;
stats_info_access(idx)->referer = nmalloc(strlen(buf) + 1);
strcpy(stats_info_access(idx)->referer, buf);
} else if (!buf[0]) {
dcc[idx].status = 1;
#ifndef OLDBOT
/* If there's no data in our socket, we immediately get rid of it.
*/
if (!sock_has_data(SOCK_DATA_OUTGOING, dcc[idx].sock)) {
killsock(dcc[idx].sock);
lostdcc(idx);
}
#endif
}
}
#ifndef OLDBOT
static void outdone_livestats(int idx)
{
if (dcc[idx].status) {
killsock(dcc[idx].sock);
lostdcc(idx);
} else
dcc[idx].status = 1;
}
#endif
static void display_livestats(int idx, char *buf)
{
sprintf(buf, "livestats");
}
static void display_livestats_accept(int idx, char *buf)
{
sprintf(buf, "lstn port");
}
static void timeout_livestats(int idx)
{
killsock(dcc[idx].sock);
lostdcc(idx);
}
static void timeout_listen_livestats(int idx)
{
debug0("timeout listen");
killsock(dcc[idx].sock);
lostdcc(idx);
}
static void kill_livestats(int idx, void *x)
{
register struct stats_clientinfo *p = (struct stats_clientinfo *) x;
char ts[41], test[11];
time_t tt;
FILE *f;
Context;
tt = now;
if (!p) {
putlog(LOG_MISC, "*", "Can't kill clientinfo, no pointer. This should not happen!");
return;
}
ctime(&tt);
/* 05/Feb/2000:12:08:17 +0100 */
strftime(test, 19, "%z", localtime(&tt));
if (test[0] != 'z')
strftime(ts, 40, "%d/%b/%Y:%H:%M:%S %z", localtime(&tt));
else
strftime(ts, 40, "%d/%b/%Y:%H:%M:%S", localtime(&tt));
if (livestats_log[0]) {
f = fopen(livestats_log, "a");
if (f == NULL)
putlog(LOG_MISC, "*", "ERROR writing livestats log.");
else {
if (test[0] != 'z')
fprintf(f,
"%s - - [%s] \"%s\" %d %d \"%s\" \"%s\"\n", dcc[idx].host, ts,
p->cmd ? p->cmd : "", p->code, p->traffic,
p->referer ? p->referer : "-", p->browser ? p->browser : "");
else
fprintf(f,
"%s - - [%s %+05d] \"%s\" %d %d \"%s\" \"%s\"\n", dcc[idx].host,
ts, offset * (-1) * 100, p->cmd ? p->cmd : "", p->code ,p->traffic,
p->referer ? p->referer : "-", p->browser ? p->browser : "");
fclose(f);
}
}
if (p->browser)
nfree(p->browser);
if (p->referer)
nfree(p->referer);
if (p->cmd)
nfree(p->cmd);
nfree(p);
}
static int expmem_livestats(void *x)
{
register struct stats_clientinfo *p = (struct stats_clientinfo *) x;
int tot = sizeof(struct stats_clientinfo);
Context;
if (!p) {
putlog(LOG_MISC, "*", "Can't expmem clientinfo, no pointer. This should not happen!");
return 0;
}
if (p->browser)
tot += strlen(p->browser) + 1;
if (p->referer)
tot += strlen(p->referer) + 1;
if (p->cmd)
tot += strlen(p->cmd) + 1;
return tot;
}
static void out_livestats(int idx, char *buf, void *x)
{
register struct stats_clientinfo *p = (struct stats_clientinfo *) x;
if (!p) {
putlog(LOG_MISC, "*", "No stats_clientinfo pointer. This should not happen!");
return;
}
p->traffic += strlen(buf);
tputs(dcc[idx].sock, buf, strlen(buf));
}
static void livestats_accept(int idx, char *buf, int len)
{
unsigned long ip;
unsigned short port;
int j = 0, sock, i;
char s[UHOSTLEN];
Context;
if (dcc_total + 1 >= max_dcc) {
j = answer(dcc[idx].sock, s, &ip, &port, 0);
if (j != -1) {
dprintf(-j, "Sorry, too many connections already.\r\n");
killsock(j);
}
return;
}
sock = answer(dcc[idx].sock, s, &ip, &port, 0);
if (sock < 0) {
neterror(s);
putlog(LOG_MISC, "*", "Stats.mod: Error accepting livestats connection: %s", s);
return;
}
if ((i = new_dcc(&LIVESTATS, sizeof(struct stats_clientinfo))) == (-1)) {
putlog(LOG_MISC, "*", "Error accepting livestats connection. DCC table is full.");
killsock(sock);
return;
}
dcc[i].sock = sock;
dcc[i].addr = ip;
dcc[i].port = port;
strcpy(dcc[i].nick, "httpstats");
#ifndef OLDBOT
sprintf(s, "%s", iptostr(my_htonl(ip)));
#else
sprintf(s, "%lu.%lu.%lu.%lu", (ip >> 24) & 0xff, (ip >> 16) & 0xff,
(ip >> 8) & 0xff, ip & 0xff); /* dw */
#endif
strcpy(dcc[i].host, s);
dcc[i].timeval = now;
dcc[i].status = 0;
((struct stats_clientinfo *) dcc[i].u.other)->traffic = 0;
((struct stats_clientinfo *) dcc[i].u.other)->code = 200;
((struct stats_clientinfo *) dcc[i].u.other)->browser = NULL;
((struct stats_clientinfo *) dcc[i].u.other)->referer = NULL;
((struct stats_clientinfo *) dcc[i].u.other)->cmd = NULL;
}
static int mlstat_time = 0, mlstat_thr = 0;
static int livestats_flood()
{
if (!maxlivestats_thr || !maxlivestats_time)
return 0;
if ((now - mlstat_time) > maxlivestats_time) {
mlstat_time = now;
mlstat_thr = 0;
}
mlstat_thr++;
if (mlstat_thr > maxlivestats_thr)
return 1;
return 0;
}
static void eof_livestats(int idx)
{
debug0("eof accept");
killsock(dcc[idx].sock);
lostdcc(idx);
}
static void send_livestats(int idx, char *buf)
{
char *channel, *command;
char what[512], *pwhat, *type, *stoday, *user, prefix[4];
struct chanset_t *chan;
int today = 0;
globstats *gs;
locstats *ls, *ls2;
time_t tt, ttbuf;
struct userrec *u;
int itype, nr, i, ii;
wordstats *ws;
quotestr *qs;
unsigned long x;
int wert;
float r, g, b;
float r2, g2, b2;
float rstep, gstep, bstep;
struct stats_userlist *suser;
ttbuf = now;
tt = now;
if (!strcmp(buf, "/")) {
setslglobs(NULL, 0, 0, 0);
dprintf(idx, "%s\n%s\n\n", ROOTTITLE, SLCSS);
dprintf(idx, "%s\n", SLBODYTAG);
long_dprintf(idx, SLHEADER);
dprintf(idx, "
\n");
for (gs = sdata; gs; gs = gs->next) {
if (!inactivechan(gs->chan) && !secretchan(gs->chan) && !nostats(gs->chan)) {
if (gs->chan[0] == '!')
strcpy(prefix, "!");
else if (gs->chan[0] == '+')
strcpy(prefix, "+");
else if (gs->chan[0] == '&')
strcpy(prefix, "&");
else
prefix[0] = 0;
#ifndef OLDBOT
chan = findchan_by_dname(gs->chan);
#else
chan = findchan(gs->chan);
#endif
i = 7;
if (!show_userlist)
i--;
if (!show_usersonchan)
i--;
dprintf(idx, "", i);
if (chan && chan->channel.topic)
dprintf(idx, "%s (%s) | ",
prefix, gs->chan + 1, gs->chan, filt2(chan->channel.topic));
else
dprintf(idx, "%s",
prefix, gs->chan + 1, gs->chan);
dprintf(idx, "%s | ", SLTOP);
setslglobs(gs->chan, gs->peak[S_TOTAL], countstatmembers(gs), gs->started);
dprintf(idx, "%s | ",
prefix, gs->chan + 1, SLTOTAL);
dprintf(idx, "
\n");
dprintf(idx, "| %s | ",
prefix, gs->chan + 1, SLDAILY);
dprintf(idx, "
\n");
dprintf(idx, "| %s | ",
prefix, gs->chan + 1, SLWEEKLY);
dprintf(idx, "
\n");
dprintf(idx, "| %s | ",
prefix, gs->chan + 1, SLMONTHLY);
dprintf(idx, "
\n");
if (show_userlist)
dprintf(idx, "| %s |
",
prefix, gs->chan + 1, SLUSERS);
if (show_usersonchan)
dprintf(idx, "| %s |
",
prefix, gs->chan + 1, SLONCHAN);
dprintf(idx, "| %s | ",
prefix, gs->chan + 1, SLMISCSTATS);
dprintf(idx, "
| |
\n");
}
}
dprintf(idx, "| ");
dprintf(idx, "");
dprintf(idx, "Stats.mod v%s |
|---|
\n", MODULE_VERSION);
long_dprintf(idx, SLFOOTER);
dprintf(idx, "\n");
return;
}
if (buf[0] == '/') {
if (!strncasecmp(buf, "/e/", 3)) {
buf += 2;
buf[0] = '!';
} else if (!strncasecmp(buf, "/p/", 3)) {
buf += 2;
buf[0] = '+';
} else if (!strncasecmp(buf, "/a/", 3)) {
buf += 2;
buf[0] = '&';
} else if (buf[1] && strchr("+&!", buf[1]))
buf++;
else
buf[0] = '#';
}
if (buf[strlen(buf) - 1] == '/')
buf[strlen(buf) - 1] = 0;
channel = splitpath(&buf);
#ifndef OLDBOT
chan = findchan_by_dname(channel);
#else
chan = findchan(channel);
#endif
if (!chan) {
dprintf(idx, "no such channel %s", channel);
if (idx >= 0)
stats_info_access(idx)->code = 404;
return;
}
gs = findglobstats(channel);
if (gs)
setslglobs(gs->chan, gs->peak[S_TOTAL], countstatmembers(gs), gs->started);
else
setslglobs(channel, 0, 0, 0);
if (!strcmp(buf, "/") || !buf[0]) {
dprintf(idx, "%s\n%s\n%s", SLINDEXTITEL, SLCSS, SLBODYTAG);
long_dprintf(idx, SLHEADER);
dprintf(idx, "
\n");
dprintf(idx, "\n");
dprintf(idx, "| %s |
", channel);
dprintf(idx, "| %s | ", SLTOP);
dprintf(idx, "%s | ", SLTOTAL);
dprintf(idx, "
\n");
dprintf(idx, "| %s | ", SLDAILY);
dprintf(idx, "
\n");
dprintf(idx, "| %s | ", SLWEEKLY);
dprintf(idx, "
\n");
dprintf(idx, "| %s | ", SLMONTHLY);
dprintf(idx, "
\n");
if (show_userlist)
dprintf(idx, "| %s |
", SLUSERS);
if (show_usersonchan)
dprintf(idx, "| %s |
", SLONCHAN);
dprintf(idx, "| %s |
", SLMISCSTATS);
dprintf(idx, "| |
\n");
dprintf(idx, "| ");
dprintf(idx, "");
dprintf(idx, "Stats.mod v%s |
|---|
\n", MODULE_VERSION);
dprintf(idx, "\n");
long_dprintf(idx, SLFOOTER);
dprintf(idx, "\n");
return;
}
command = splitpath(&buf);
if (!strcasecmp(command, "top")) {
if (!strcmp(buf, "/") || !buf[0]) {
dprintf(idx, "");
dprintf(idx, "| total |
");
dprintf(idx, "| today |
");
dprintf(idx, "
");
return;
}
stoday = splitpath(&buf);
if (!strcasecmp(stoday, "today") || !strcasecmp(stoday, "daily"))
today = S_DAILY;
else if (!strcasecmp(stoday, "weekly"))
today = S_WEEKLY;
else if (!strcasecmp(stoday, "monthly"))
today = S_MONTHLY;
else if (!strcasecmp(stoday, "total"))
today = 0;
else {
dprintf(idx, "Error, invalid value %s", stoday);
dprintf(idx, "| total |
");
dprintf(idx, "| daily |
");
dprintf(idx, "| weekly |
");
dprintf(idx, "| monthly |
");
dprintf(idx, "
");
Assert(idx >= 0);
stats_info_access(idx)->code = 404;
return;
}
if (!strcmp(buf, "/") || !buf[0]) {
dprintf(idx, "");
sprintf(what, "%s", webstats);
pwhat = what;
while (strlen(pwhat) > 0) {
type = newsplit(&pwhat);
itype = typetoi(type);
if (itype >= 0) {
dprintf(idx, "| %s |
", type, type);
}
}
dprintf(idx, "
");
return;
}
gs = findglobstats(channel);
if (!gs) {
debug1("Error! Can't find global stats for %s", channel);
dprintf(idx, "Error! Can't find global stats for %s", channel);
if (idx >= 0)
stats_info_access(idx)->code = 404;
return;
}
if (!strncasecmp(buf, "graph", 5)) {
do_graphs(idx, today, gs, channel);
return;
}
do_toptalkers(idx, today, gs, channel, buf);
return;
} else if (!strcasecmp(command, "onchan")) {
display_users_on_chan(idx, channel, chan);
} else if (!strcasecmp(command, "users")) {
gs = findglobstats(channel);
if (!gs) {
debug1("Error! Can't find global stats for %s", channel);
dprintf(idx, "Error! Can't find global stats for %s", channel);
return;
}
setslglobs(gs->chan, gs->peak[S_TOTAL], countstatmembers(gs), gs->started);
if (!strcmp(buf, "/") || !buf[0]) {
if (!show_userlist)
return;
sort_stats_alphabetically(gs);
dprintf(idx, "%s\n%s\n%s",
SLUSERSTITLE, SLCSS, SLBODYTAG);
long_dprintf(idx, SLHEADER);
dprintf(idx, "\n");
dprintf(idx, "\n| User | Info |
\n", table_border);
i = 0;
for (ls = gs->local; ls; ls = ls->next)
i++;
wert = table_color;
b = wert & 0xff; g = (wert & 0xff00) >> 8; r = (wert & 0xff0000) >> 16;
wert = fade_table_to;
b2 = wert & 0xff; g2 = (wert & 0xff00) >> 8; r2 = (wert & 0xff0000) >> 16;
rstep = (r2 - r) / i;
gstep = (g2 - g) / i;
bstep = (b2 - b) / i;
for (ls = gs->local; ls; ls = ls->next) {
u = get_user_by_handle(userlist, ls->user);
what[0] = 0;
get_handle_chaninfo(ls->user, channel, what);
pwhat = what;
if (!what[0])
pwhat = get_user(&USERENTRY_INFO, u);
if (!pwhat) {
sprintf(what, " ");
pwhat = what;
}
dprintf(idx, "| %s | %s |
\n",
(int) r, (int) g, (int) b, ls->user, ls->user, filt2(pwhat));
r += rstep;
g += gstep;
b += bstep;
}
/*
* dprintf(idx, "| ");
* dprintf(idx, "Stats.mod v%s |
\n", MODULE_VERSION);
*/
dprintf(idx, "\n");
dprintf(idx, "
\n");
dprintf(idx, "\n");
dprintf(idx, "\n");
dprintf(idx, "| <%sa "
"href=\"../onchan/\">%s | \n",
ISLINK(show_usersonchan), ISTEXT(show_usersonchan, SLONCHAN));
dprintf(idx, "top%d | \n", webnr);
dprintf(idx, "%s | \n", SLMISCSTATS);
dprintf(idx, "%s | \n", SLOTHERCHANS);
dprintf(idx, "
|
\n");
dprintf(idx, "
\n");
dprintf(idx, "Created by Stats.mod v%s