/************************************************************************
 *   IRC - Internet Relay Chat, modules/m_stats.c
 *
 *   Copyright (C) 2000-2003 TR-IRCD Development
 *
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Co Center
 *
 *   See file AUTHORS in IRC package for additional names of
 *   the programmers.
 *
 *   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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * $Id: m_stats.c,v 1.3 2003/06/14 13:55:51 tr-ircd Exp $ 
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "h.h"
#include "logtype.h"
#include "s_conf.h"
#include "hook.h"
#include "ircsprintf.h"
#include "listener.h"
#include "throttle.h"
#include "event.h"

static char *s_token = TOK1_STATS;
static char *u_token = TOK1_UPTIME;

static int hookid_stats = 0;

static struct Message s_msgtab[] = {
    {MSG_STATS, 0, MAXPARA, M_SLOW, 0L,
     m_unregistered, m_stats, m_stats, m_stats, m_stats}
};

static struct Message u_msgtab[] = {
    {MSG_UPTIME, 0, MAXPARA, M_SLOW, 0L,
     m_unregistered, m_uptime, m_uptime, m_uptime, m_uptime}
};

#ifndef STATIC_MODULES

char *_version = "$Revision: 1.3 $";

void _modinit(void)
{
    mod_add_cmd(s_msgtab);
    tok1_msgtab[(u_char) *s_token].msg = s_msgtab;
    mod_add_cmd(u_msgtab);
    tok1_msgtab[(u_char) *u_token].msg = u_msgtab;
    hookid_stats = hook_add_event("doing stats");
}

void _moddeinit(void)
{
    mod_del_cmd(s_msgtab);
    tok1_msgtab[(u_char) *s_token].msg = NULL;
    mod_del_cmd(u_msgtab);
    tok1_msgtab[(u_char) *u_token].msg = NULL;
    hook_del_event("doing stats");
}
#else
void m_stats_init(void)
{
    mod_add_cmd(s_msgtab);
    tok1_msgtab[(u_char) *s_token].msg = s_msgtab;
    mod_add_cmd(u_msgtab);
    tok1_msgtab[(u_char) *u_token].msg = u_msgtab;
    hookid_stats = hook_add_event("doing stats");
}
#endif

static void stats_permission(aClient *cptr, char **parv)
{
    send_me_numeric(cptr, ERR_NOPRIVILEGES);
}

static void stats_akills(aClient *cptr, char **parv)
{
    report_maskitem_list_primary(cptr, NULL, MASKITEM_AUTOKILL, RPL_STATSKLINE, 'a');
}

static void stats_debugmem(aClient *cptr, char **parv)
{
    count_memory(cptr, parv[0]);
}

static void stats_connects(aClient *cptr, char **parv)
{
    report_connect_blocks(cptr, 1);
}

static void stats_connected(aClient *cptr, char **parv)
{
    report_connect_blocks(cptr, 0);
}

static void stats_auth_ek(aClient *cptr, char **parv)
{
    report_auth_blocks(cptr, CONF_FLAGS_EXEMPTKLINE);
}

static void stats_excludes(aClient *cptr, char **parv)
{
    report_maskitem_list_primary(cptr, NULL, MASKITEM_EXCLUDE, RPL_STATSKLINE, 'e');
}

static void stats_fdlist(aClient *cptr, char **parv)
{
    fd_dump(cptr);
}

static void stats_sglines(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_GECOS, RPL_STATSGLINE, 'g');
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_GECOS_REGEX, RPL_STATSGLINE, 'g');
}

static void stats_glines(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_GECOS_CONFIG, RPL_STATSGLINE, 'G');
}


static void stats_hooks(aClient *cptr, char **parv)
{
    hook_dump(cptr);
}

static void stats_hubs(aClient *cptr, char **parv)
{
    report_connect_blocks_flag(cptr, CONF_FLAGS_HUB, RPL_STATSHLINE, 'H');
}

static void stats_auth(aClient *cptr, char **parv)
{
    report_auth_blocks(cptr, 0);
}

static void stats_jupiters(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_JUPITER, RPL_STATSJLINE, 'j');
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_JUPITER_REGEX, RPL_STATSJLINE, 'j');
}

static void stats_jupes(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_JUPITER_CONFIG, RPL_STATSJLINE, 'J');
}

static void stats_temp_klines(aClient *cptr, char **parv)
{
    report_maskitem_list_primary(cptr, NULL, MASKITEM_KLINE, RPL_STATSKLINE, 'k');
    report_maskitem_list_primary(cptr, NULL, MASKITEM_KLINE_REGEX, RPL_STATSKLINE, 'k');
}

static void stats_klines(aClient *cptr, char **parv)
{
    report_maskitem_list_primary(cptr, NULL, MASKITEM_KLINE_CONFIG, RPL_STATSKLINE, 'K');
}

static void stats_messages(aClient *cptr, char **parv)
{
    report_messages(cptr);
}

static void stats_netstat_std(aClient *cptr, char **parv)
{
    send_me_numeric(cptr, RPL_STATSCOUNT, "User Connects Today: ", Count.today);
}

static void stats_netstat(aClient *cptr, char **parv)
{
    send_me_numeric(cptr, RPL_STATSCOUNT, "User Connects Today: ", Count.today);
    send_me_numeric(cptr, RPL_STATSCOUNT, "User Connects past week: ", Count.weekly);
    send_me_numeric(cptr, RPL_STATSCOUNT, "User Connects past month: ", Count.monthly);
    send_me_numeric(cptr, RPL_STATSCOUNT, "User Conects past year: ", Count.yearly);
}

static void stats_operator(aClient *cptr, char **parv)
{
    report_operator_blocks(cptr);
}

static void stats_opered(aClient *cptr, char **parv)
{
    show_opers(cptr, parv[0]);
}

static void stats_protocol(aClient *cptr, char **parv)
{
    report_protocol(cptr);
}

static void stats_ports(aClient *cptr, char **parv)
{
    show_ports(cptr);
}

static void stats_sqlines(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_QUARANTINE, RPL_STATSQLINE, 'q');
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_QUARANTINE_REGEX, RPL_STATSQLINE, 'q');
}

static void stats_quarantines(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_QUARANTINE_CONFIG, RPL_STATSQLINE, 'Q');
}

static void stats_auth_redir(aClient *cptr, char **parv)
{
    report_auth_blocks(cptr, CONF_FLAGS_REDIR);
}

static void stats_services(aClient *cptr, char **parv)
{
    report_service_blocks(cptr, 1);
}

static void stats_connected_services(aClient *cptr, char **parv)
{
    report_service_blocks(cptr, 0);
}

static void stats_uptime(aClient *cptr, char **parv)
{
    time_t now = timeofday - me.since;
    send_me_numeric(cptr, RPL_STATSUPTIME, now / 86400,
		    (now / 3600) % 24, (now / 60) % 60, now % 60);
}

static void stats_ultimate(aClient *cptr, char **parv)
{
    report_connect_blocks_flag(cptr, CONF_FLAGS_ULTIMATE, RPL_STATSULINE, 'U');
}

static void stats_dccblock(aClient *cptr, char **parv)
{
    int i;
    char tempbuf[500];	/* who knows */
    strcpy(tempbuf, "Blocked extensions are:");
    for (i = 0; exploits_2char[i]; i++) {
	strncat(tempbuf, exploits_2char[i], 2);
	strncat(tempbuf, " ", 1);
    }
    for (i = 0; exploits_3char[i]; i++) {
	strncat(tempbuf, exploits_3char[i], 3);
	strncat(tempbuf, " ", 1);
    }
    for (i = 0; exploits_4char[i]; i++) {
	strncat(tempbuf, exploits_4char[i], 4);
	strncat(tempbuf, " ", 1);
    }
    send_me_debug(cptr, tempbuf);
}

static void stats_autoconnect(aClient *cptr, char **parv)
{
    report_connect_blocks_flag(cptr, CONF_FLAGS_ALLOW_AUTO_CONN, RPL_STATSSLINE, 'V');
}

static void stats_eventdump(aClient *cptr, char **parv)
{
    show_events(cptr);
}

static void stats_servers(aClient *cptr, char **parv)
{
    show_servers(cptr, parv[0]);
}

static void stats_throttle(aClient *cptr, char **parv)
{
    throttle_stats(cptr, parv[0]);
}

static void stats_classes(aClient *cptr, char **parv)
{
    report_classes(cptr);
}

static void stats_szlines(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_ZAPLINE, RPL_STATSZLINE, 'z');
}

static void stats_zaplines(aClient *cptr, char **parv)
{
    report_maskitem_list_secondary(cptr, NULL, MASKITEM_ZAPLINE_CONFIG, RPL_STATSZLINE, 'Z');
}

static void stats_servinfo(aClient *cptr, char **parv)
{
    serv_info(cptr, parv[0]);
}

static void stats_usage(aClient *cptr, char **parv)
{ 
    send_usage(cptr, parv[0]);
}

static void stats_stats(aClient *cptr, char **parv)
{
    tstats(cptr, parv[0]);
}

static void stats_logevents(aClient *cptr, char **parv)
{
    dump_logevents(cptr);
}

static void stats_adns(aClient *cptr, char **parv)
{
    report_adns_servers(cptr);
}

static void stats_more(aClient *cptr, char **parv)
{
    struct hook_data thisdata;
   
    thisdata.client_p = cptr;
    thisdata.parv = parv;

    hook_call_event(hookid_stats, &thisdata);
}

static void stats_linkinfo(aClient *cptr, char **parv)
{
    static char Lformat[] = ":%C %N %s %s %u %u %u %u %u :%u %u %s";
    static char Sformat[] =
	":%C %N %s Name SendQ SendM SendBytes RcveM RcveBytes :Open_since Idle TS";

    aClient *acptr;
    int doall = 0, wilds = 0;
    char *name;
    time_t sincetime;
    dlink_node *ptr;

    if (parv[2]) {
	name = parv[2];
	if (!irc_strcmp(name, me.name))
	    doall = 2;
	else if (match(name, me.name) == 0)
	    doall = 1;
	if (strchr(name, '*') || strchr(name, '?'))
	    wilds = 1;
    } else
	name = me.name;

    sendto_one(cptr, Sformat, &me, RPL_STATSLINKINFO, parv[0]);
    if ((parv[2]) && !(doall || wilds)) {	/* Single client lookup
						 */
	if (!IsAnOper(cptr)) {
	    send_me_numeric(cptr, ERR_NOPRIVILEGES);
	    return;
	}
	if (!(acptr = find_person(name)))
	    return;
	/*
	 * sincetime might be greater than timeofday,
	 * store a new value here to avoid sending
	 * negative since-times. -Rak
	 */
	sincetime = (acptr->since > timeofday) ? 0 : timeofday - acptr->since;
	sendto_one(cptr, Lformat, &me, RPL_STATSLINKINFO, parv[0],
		   get_client_name(acptr, TRUE),
		   (int) linebuf_len(&acptr->sendQ), (int) acptr->sendM,
		   (int) acptr->sendK, (int) acptr->receiveM,
		   (int) acptr->receiveK, timeofday - acptr->firsttime,
		   sincetime, IsServer(acptr) ? (DoesTS(acptr) ? "TS" : "NoTS") : "-");
    } else {
	for (ptr = lclient_list.head; ptr; ptr = ptr->next) {
	    if (!(acptr = ptr->data))
		continue;
	    if (!IsServer(acptr))
		continue;	/* nothing but servers */
	    if (ServicesConf.hide_ulined_servers && IsULine(acptr) && !IsSeeHidden(cptr))
		continue;
	    sincetime = (acptr->since > timeofday) ? 0 : timeofday - acptr->since;
	    sendto_one(cptr, Lformat, &me, RPL_STATSLINKINFO,
		       parv[0], get_client_name(acptr, HIDEME),
		       (int) linebuf_len(&acptr->sendQ),
		       (int) acptr->sendM, (int) acptr->sendK,
		       (int) acptr->receiveM, (int) acptr->receiveK,
		       timeofday - acptr->firsttime, sincetime,
		       IsServer(acptr) ? (DoesTS(acptr) ? "TS" : "NoTS")
		       : "-");
	}
    }
}

/*
 * /stats command has merely almost 52 different ways of usage.
 * these are listed in this table.
 * -TimeMr14C
 */

struct StatsFunc {

    int exists;

    void (*sf_user) ();
    void (*sf_oper) ();
};

struct StatsFunc statstab[] = {
    {0, NULL, NULL},		/* #0 */
    {0, NULL, NULL},		/* #1 */
    {0, NULL, NULL},		/* #2 */
    {0, NULL, NULL},		/* #3 */
    {0, NULL, NULL},		/* #4 */
    {0, NULL, NULL},		/* #5 */
    {0, NULL, NULL},		/* #6 */
    {0, NULL, NULL},		/* #7 */
    {0, NULL, NULL},		/* #8 */
    {0, NULL, NULL},		/* #9 */
    {0, NULL, NULL},		/* #10 */
    {0, NULL, NULL},		/* #11 */
    {0, NULL, NULL},		/* #12 */
    {0, NULL, NULL},		/* #13 */
    {0, NULL, NULL},		/* #14 */
    {0, NULL, NULL},		/* #15 */
    {0, NULL, NULL},		/* #16 */
    {0, NULL, NULL},		/* #17 */
    {0, NULL, NULL},		/* #18 */
    {0, NULL, NULL},		/* #19 */
    {0, NULL, NULL},		/* #20 */
    {0, NULL, NULL},		/* #21 */
    {0, NULL, NULL},		/* #22 */
    {0, NULL, NULL},		/* #23 */
    {0, NULL, NULL},		/* #24 */
    {0, NULL, NULL},		/* #25 */
    {0, NULL, NULL},		/* #26 */
    {0, NULL, NULL},		/* #27 */
    {0, NULL, NULL},		/* #28 */
    {0, NULL, NULL},		/* #29 */
    {0, NULL, NULL},		/* #30 */
    {0, NULL, NULL},		/* #31 */
    {0, NULL, NULL},		/* #32 */
    {0, NULL, NULL},		/* ! */
    {0, NULL, NULL},		/* " */
    {0, NULL, NULL},		/* # */
    {0, NULL, NULL},		/* $ */
    {0, NULL, NULL},		/* % */
    {0, NULL, NULL},		/* & */
    {0, NULL, NULL},		/* ' */
    {0, NULL, NULL},		/* ( */
    {0, NULL, NULL},		/* ) */
    {0, NULL, NULL},		/* * */
    {0, NULL, NULL},		/* + */
    {0, NULL, NULL},		/* , */
    {0, NULL, NULL},		/* - */
    {0, NULL, NULL},		/* . */
    {0, NULL, NULL},		/* / */
    {0, NULL, NULL},		/* 0 */
    {0, NULL, NULL},		/* 1 */
    {0, NULL, NULL},		/* 2 */
    {0, NULL, NULL},		/* 3 */
    {0, NULL, NULL},		/* 4 */
    {0, NULL, NULL},		/* 5 */
    {0, NULL, NULL},		/* 6 */
    {0, NULL, NULL},		/* 7 */
    {0, NULL, NULL},		/* 8 */
    {0, NULL, NULL},		/* 9 */
    {0, NULL, NULL},		/* : */
    {0, NULL, NULL},		/* ; */
    {0, NULL, NULL},		/* < */
    {0, NULL, NULL},		/* = */
    {0, NULL, NULL},		/* > */
    {1, stats_servinfo, stats_servinfo},	/* ? */
    {0, NULL, NULL},				/* @ */
    {1, stats_permission, stats_akills},	/* A */
    {1, stats_permission, stats_dccblock},	/* B */
    {1, stats_permission, stats_connects},	/* C */
    {1, stats_dccblock, stats_debugmem},	/* D */
    {1, stats_excludes, stats_excludes},	/* E */
    {1, stats_permission, stats_fdlist},	/* F */
    {1, stats_glines, stats_glines},		/* G */
    {1, stats_permission, stats_hubs},		/* H */
    {1, stats_auth, stats_auth},		/* I */
    {1, stats_jupes, stats_jupes},		/* J */
    {1, stats_permission, stats_klines},	/* K */
    {1, stats_permission, stats_logevents},	/* L */
    {1, stats_messages, stats_messages},	/* M */
    {1, stats_netstat_std, stats_netstat},	/* N */
    {1, stats_opered, stats_opered},		/* O */
    {1, stats_permission, stats_ports},		/* P */
    {1, stats_quarantines, stats_quarantines},	/* Q */
    {1, stats_permission, stats_auth_redir},	/* R */
    {1, stats_permission, stats_connected_services},	/* S */
    {1, stats_permission, stats_stats},		/* T */
    {1, stats_ultimate, stats_ultimate},	/* U */
    {1, stats_servers, stats_servers},		/* V */
    {1, stats_more, stats_more},		/* W */
    {1, stats_permission, stats_eventdump},	/* X */
    {1, stats_classes, stats_classes},		/* Y */
    {1, stats_permission, stats_zaplines},	/* Z */
    {0, NULL, NULL},				/* [ */
    {0, NULL, NULL},				/* \ */
    {0, NULL, NULL},				/* ] */
    {0, NULL, NULL},				/* ^ */
    {0, NULL, NULL},				/* _ */
    {0, NULL, NULL},				/* ` */
    {1, stats_permission, stats_akills},	/* a */
    {1, stats_permission, stats_usage},		/* b */
    {1, stats_connected, stats_connected},	/* c */
    {1, stats_permission, stats_debugmem},	/* d */
    {1, stats_permission, stats_auth_ek},	/* e */
    {1, stats_permission, stats_fdlist},	/* f */
    {1, stats_sglines, stats_sglines},		/* g */
    {1, stats_permission, stats_hooks},		/* h */
    {1, stats_auth, stats_auth},		/* i */
    {1, stats_jupiters, stats_jupiters},	/* j */
    {1, stats_permission, stats_temp_klines},	/* k */
    {1, stats_linkinfo, stats_linkinfo},	/* l */
    {1, stats_messages, stats_messages},	/* m */
    {1, stats_netstat_std, stats_netstat_std},	/* n */
    {1, stats_permission, stats_operator},	/* o */
    {1, stats_protocol, stats_protocol},	/* p */
    {1, stats_sqlines, stats_sqlines},		/* q */
    {1, stats_permission, stats_auth_redir},	/* r */
    {1, stats_permission, stats_services},	/* s */
    {1, stats_permission, stats_throttle},	/* t */
    {1, stats_uptime, stats_uptime},		/* u */
    {1, stats_permission, stats_autoconnect},	/* v */
    {1, stats_more, stats_more},                /* w */ 
    {1, stats_permission, stats_eventdump},	/* x */
    {1, stats_classes, stats_classes},		/* y */
    {1, stats_permission, stats_szlines},	/* z */
    {0, NULL, NULL},				/* { */
    {0, NULL, NULL},				/* | */
    {0, NULL, NULL},				/* } */
    {1, stats_permission, stats_adns},		/* ~ */
    {0, NULL, NULL},				/* #127 */
};

/*
 * 
 * m_stats 
 *      parv[0] = sender prefix 
 *      parv[1] = statistics selector (defaults to Message frequency) 
 *      parv[2] = server name (current server defaulted, if omitted) 
 */

int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char stat = parc > 1 ? parv[1][0] : '\0';
    int s = stat;
    struct StatsFunc *thisfunc;

    if (hunt_server(cptr, sptr, ":%s %s %s :%s", TOK1_STATS, 2, parc, parv) != HUNTED_ISME)
	return 0;

    if (stat != (char) 0)
	sendto_lev(SPY_LEV, "STATS %c requested by %^C [%s]", stat, sptr, sptr->user->server);

    if (s > 127 || s <= 0) {
	send_me_numeric(sptr, RPL_ENDOFSTATS, stat);
    	return 0;
    }

    thisfunc = &statstab[(int) stat];

    if (thisfunc->exists) {
	if (IsAnOper(sptr))
	    (*thisfunc->sf_oper) (sptr, parv);
	else
	    (*thisfunc->sf_user) (sptr, parv);
    }

    send_me_numeric(sptr, RPL_ENDOFSTATS, stat);
    return 0;
}

int m_uptime(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    if (hunt_server(cptr, sptr, ":%s %s %s", TOK1_UPTIME, 1, parc, parv) != HUNTED_ISME)
        return 0;

    sendto_lev(SPY_LEV, "UPTIME requested by %^C [%s]", sptr, sptr->user->server);

    stats_uptime(sptr, parv);

    send_me_numeric(sptr, RPL_ENDOFSTATS, 'u');
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1