Author: Ghostwolf <foxxe@wtfs.net>
[ircu2.10.12-pk.git] / ircd / m_stats.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_stats.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #include "config.h"
83
84 #include "handlers.h"
85 /*
86  * XXX - ack!!!
87  */
88 #include "s_stats.h"
89 #include "channel.h"
90 #include "class.h"
91 #include "client.h"
92 #include "gline.h"
93 #include "hash.h"
94 #include "ircd.h"
95 #include "ircd_alloc.h"
96 #include "ircd_chattr.h"
97 #include "ircd_events.h"
98 #include "ircd_features.h"
99 #include "ircd_policy.h"
100 #include "ircd_reply.h"
101 #include "ircd_string.h"
102 #include "list.h"
103 #include "listener.h"
104 #include "match.h"
105 #include "motd.h"
106 #include "msg.h"
107 #include "numeric.h"
108 #include "numnicks.h"
109 #include "opercmds.h"
110 #include "s_bsd.h"
111 #include "s_conf.h"
112 #include "s_debug.h"
113 #include "s_misc.h"
114 #include "s_serv.h"
115 #include "s_user.h"
116 #include "send.h"
117 #include "struct.h"
118 #include "userload.h"
119
120 #include <assert.h>
121 #include <stdlib.h>
122 #include <string.h>
123
124
125 int report_klines(struct Client* sptr, char* mask, int limit_query)
126 {
127   int   wilds = 0;
128   int   count = 3;
129   char* user  = 0;
130   char* host;
131   const struct DenyConf* conf;
132
133   if (EmptyString(mask)) {
134     if (limit_query)
135       return need_more_params(sptr, "STATS K");
136     else
137       report_deny_list(sptr);
138     return 1;
139   }
140
141   if (!limit_query) {
142     wilds = string_has_wildcards(mask);
143     count = 1000;
144   }
145
146   if ((host = strchr(mask, '@'))) {
147     user = mask;
148     *host++ = '\0';
149   }
150   else {
151     host = mask;
152   }
153
154   for (conf = conf_get_deny_list(); conf; conf = conf->next) {
155     if ((!wilds && ((user || conf->hostmask) &&
156         !match(conf->hostmask, host) &&
157         (!user || !match(conf->usermask, user)))) ||
158         (wilds && !mmatch(host, conf->hostmask) &&
159         (!user || !mmatch(user, conf->usermask))))
160     {
161       send_reply(sptr, RPL_STATSKLINE,
162                  (conf->flags & DENY_FLAGS_IP) ? 'k' : 'K',
163                  conf->hostmask, conf->message, conf->usermask);
164       if (--count == 0)
165         return 1;
166     }
167   }
168   /* send_reply(sptr, RPL_ENDOFSTATS, stat); */
169   return 1;
170 }
171
172
173 /*
174  * m_stats - generic message handler
175  *
176  *    parv[0] = sender prefix
177  *    parv[1] = statistics selector (defaults to Message frequency)
178  *    parv[2] = target server (current server defaulted, if omitted)
179  * And 'stats l' and 'stats' L:
180  *    parv[3] = server mask ("*" defaulted, if omitted)
181  * Or for stats p,P:
182  *    parv[3] = port mask (returns p-lines when its port is matched by this)
183  * Or for stats k,K,i and I:
184  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
185  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
186  *              (defaults to old reply if ommitted, when local or Oper)
187  *              A remote mask (something containing wildcards) is only
188  *              allowed for IRC Operators.
189  * Or for stats M:
190  *    parv[3] = time param
191  *    parv[4] = time param 
192  *    (see report_memleak_stats() in runmalloc.c for details)
193  *
194  * This function is getting really ugly. -Ghostwolf
195  */
196 int m_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
197 {
198   char stat = parc > 1 ? parv[1][0] : '\0';
199   const char **infotext = statsinfo;
200
201   if (hunt_stats(cptr, sptr, parc, parv, stat, HEAD_IN_SAND_REMOTE)
202       != HUNTED_ISME)
203     return 0;
204
205   switch (stat)
206   {
207     case 'L':
208     case 'l':
209 #ifdef HEAD_IN_SAND_STATS_L
210         return m_not_oper(sptr,cptr,parc,parv);
211 #else
212     {
213       struct Client *acptr;
214       int i;
215       int doall = 0;
216       int wilds = 0;
217       char *name = "*";
218
219       if (parc > 3 && !EmptyString(parv[3])) {
220         name = parv[3];
221         wilds = string_has_wildcards(name);
222       }
223       else
224         doall = 1;
225       /*
226        * Send info about connections which match, or all if the
227        * mask matches me.name.  Only restrictions are on those who
228        * are invisible not being visible to 'foreigners' who use
229        * a wild card based search to list it.
230        */
231       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
232                  "SendM SendKBytes RcveM RcveKBytes :Open since");
233       for (i = 0; i <= HighestFd; i++)
234       {
235         if (!(acptr = LocalClientArray[i]))
236           continue;
237         /* Don't return clients when this is a request for `all' */
238         if (doall && IsUser(acptr))
239           continue;
240         /* Don't show invisible people to non opers unless they know the nick */
241         if (IsInvisible(acptr) && (doall || wilds) && !IsAnOper(acptr) && (acptr != sptr))
242           continue;
243         /* Only show the ones that match the given mask - if any */
244         if (!doall && wilds && match(name, cli_name(acptr)))
245           continue;
246         /* Skip all that do not match the specific query */
247         if (!(doall || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
248           continue;
249         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
250                    "%s %u %u %u %u %u :%Tu", (*(cli_name(acptr))) ? cli_name(acptr) : "<unregistered>",
251                    (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
252                    (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
253                    (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
254       }
255     }
256 #endif
257       break;
258
259     case 'C':
260     case 'c':
261 #ifdef HEAD_IN_SAND_STATS_C
262       return m_not_oper(sptr,cptr,parc,parv);
263 #else
264       report_configured_links(sptr, CONF_SERVER);
265 #endif
266       break;
267
268     case 'E':
269     case 'e': /* report engine name */
270 #ifdef HEAD_IN_SAND_STATS_E
271       return m_not_oper(sptr,cptr,parc,parv);
272 #else
273       send_reply(sptr, RPL_STATSENGINE, engine_name());
274 #endif
275       break;
276
277     case 'G':
278     case 'g': /* send glines */
279 #ifdef HEAD_IN_SAND_STATS_G
280       return m_not_oper(sptr,cptr,parc,parv);
281 #else
282       gline_stats(sptr);
283 #endif
284       break;
285
286     case 'H':
287     case 'h':
288 #ifdef HEAD_IN_SAND_STATS_H
289       return m_not_oper(sptr,cptr,parc,parv);
290 #else
291       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
292 #endif
293       break;
294
295     case 'K':
296     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
297 #ifdef HEAD_IN_SAND_STATS_K
298     /* Simple version - if you want to fix it - send in a patch */
299     return m_not_oper(sptr,cptr,parc,parv);
300 #else
301       if (0 == report_klines(sptr, (parc == 4) ? parv[3] : 0, 0))
302         return 0;
303 #endif
304       break;
305     case 'F':
306     case 'f':
307 #ifdef HEAD_IN_SAND_STATS_F
308       return m_not_oper(sptr,cptr,parc,parv);
309 #else
310       feature_report(sptr);
311 #endif
312       break;
313
314     case 'I':
315     case 'i':
316 #ifdef HEAD_IN_SAND_STATS_I
317     /* Simple version - if you want to fix it - send in a patch */
318     return m_not_oper(sptr,cptr,parc,parv);
319 #else
320     {
321       struct ConfItem *aconf;
322       int wilds = 0;
323       int count = 1000;
324       char* host;
325
326       if (parc < 4) {
327         report_configured_links(sptr, CONF_CLIENT);
328         break;
329       }
330       if (EmptyString(parv[3]))
331         return need_more_params(sptr, "STATS I");
332
333       host = parv[3];
334       wilds = string_has_wildcards(host);
335
336       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
337         if (CONF_CLIENT == aconf->status) {
338           if ((!wilds && (!match(aconf->host, host) ||
339               !match(aconf->name, host))) ||
340               (wilds && (!mmatch(host, aconf->host) ||
341               !mmatch(host, aconf->name))))
342           {
343             if (aconf->passwd && IsDigit(*aconf->passwd) && 
344                 (!aconf->passwd[1] || 
345                  (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
346               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->passwd, 
347                         aconf->name, aconf->port, get_conf_class(aconf));
348             else 
349               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
350                          aconf->name, aconf->port, get_conf_class(aconf));
351             
352             if (--count == 0)
353               break;
354           }
355         }
356       }
357     }
358 #endif
359       break;
360
361     case 'M':
362 #ifdef HEAD_IN_SAND_STATS_M
363       return m_not_oper(sptr,cptr,parc,parv);
364 #else
365 #if defined(MDEBUG)
366       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
367                  fda_get_block_count());
368 #endif
369 #endif
370       break;
371
372     case 'm':
373 #ifdef HEAD_IN_SAND_STATS_m
374       return m_not_oper(sptr,cptr,parc,parv);
375 #else
376     {
377       struct Message *mptr;
378
379       for (mptr = msgtab; mptr->cmd; mptr++)
380         if (mptr->count)
381           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
382                      mptr->bytes);
383     }
384 #endif
385       break;
386
387     case 'o':
388     case 'O':
389 #ifdef HEAD_IN_SAND_STATS_O
390       return m_not_oper(sptr,cptr,parc,parv);
391 #else
392       report_configured_links(sptr, CONF_OPS);
393 #endif
394       break;
395
396     case 'p':
397     case 'P':
398       /*
399        * show listener ports
400        * show hidden ports to opers, if there are more than 3 parameters,
401        * interpret the fourth parameter as the port number.
402        */ 
403 #ifdef HEAD_IN_SAND_STATS_P
404       return m_not_oper(sptr,cptr,parc,parv);
405 #else
406       show_ports(sptr, 0, (parc > 3) ? atoi(parv[3]) : 0, 100);
407 #endif
408       break;
409
410     case 'R':
411     case 'r':
412 #ifdef HEAD_IN_SAND_STATS_R
413       return m_not_oper(sptr,cptr,parc,parv);
414 #else
415 #ifdef DEBUGMODE
416       send_usage(sptr, parv[0]);
417 #endif
418 #endif
419       break;
420
421     case 'D':
422 #ifdef HEAD_IN_SAND_STATS_D
423       return m_not_oper(sptr,cptr,parc,parv);
424 #else
425       report_crule_list(sptr, CRULE_ALL);
426 #endif
427       break;
428
429     case 'd':
430 #ifdef HEAD_IN_SAND_STATS_d
431       return m_not_oper(sptr,cptr,parc,parv);
432 #else
433       report_crule_list(sptr, CRULE_MASK);
434 #endif
435       break;
436
437     case 't':
438 #ifdef HEAD_IN_SAND_STATS_t
439       return m_not_oper(sptr,cptr,parc,parv);
440 #else
441       tstats(sptr, parv[0]);
442 #endif
443       break;
444
445     case 'T':
446 #ifdef HEAD_IN_SAND_STATS_T
447       return m_not_oper(sptr,cptr,parc,parv);
448 #else
449       motd_report(sptr);
450 #endif
451       break;
452
453     case 'U':
454 #ifdef HEAD_IN_SAND_STATS_U
455       return m_not_oper(sptr,cptr,parc,parv);
456 #else
457       report_configured_links(sptr, CONF_UWORLD);
458 #endif
459       break;
460
461     case 'u':
462 #ifdef HEAD_IN_SAND_STATS_u
463       return m_not_oper(sptr,cptr,parc,parv);
464 #else
465     {
466       time_t nowr;
467
468       nowr = CurrentTime - cli_since(&me);
469       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
470                  (nowr / 60) % 60, nowr % 60);
471       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
472     }
473 #endif
474       break;
475
476     case 'W':
477     case 'w':
478 #ifdef HEAD_IN_SAND_STATS_W
479       return m_not_oper(sptr,cptr,parc,parv);
480 #else
481       calc_load(sptr);
482 #endif
483       break;
484
485     case 'X':
486     case 'x':
487 #ifdef HEAD_IN_SAND_STATS_X
488       return m_not_oper(sptr,cptr,parc,parv);
489 #else
490 #ifdef  DEBUGMODE
491       class_send_meminfo(sptr);
492       send_listinfo(sptr, parv[0]);
493 #endif
494 #endif
495       break;
496
497     case 'Y':
498     case 'y':
499 #ifdef HEAD_IN_SAND_STATS_Y
500       return m_not_oper(sptr,cptr,parc,parv);
501 #else
502       report_classes(sptr);
503 #endif
504       break;
505
506     case 'Z':
507     case 'z':
508 #ifdef HEAD_IN_SAND_STATS_Z
509       return m_not_oper(sptr,cptr,parc,parv);
510 #else
511       count_memory(sptr, parv[0]);
512 #endif
513       break;
514
515     default:
516       stat = '*';
517       while (*infotext)
518         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
519       break;
520   }
521   send_reply(sptr, RPL_ENDOFSTATS, stat);
522   return 0;
523 }
524
525 /*
526  * ms_stats - server message handler
527  *
528  *    parv[0] = sender prefix
529  *    parv[1] = statistics selector (defaults to Message frequency)
530  *    parv[2] = target server (current server defaulted, if omitted)
531  * And 'stats l' and 'stats' L:
532  *    parv[3] = server mask ("*" defaulted, if omitted)
533  * Or for stats p,P:
534  *    parv[3] = port mask (returns p-lines when its port is matched by this)
535  * Or for stats k,K,i and I:
536  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
537  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
538  *              (defaults to old reply if ommitted, when local or Oper)
539  *              A remote mask (something containing wildcards) is only
540  *              allowed for IRC Operators.
541  * Or for stats M:
542  *    parv[3] = time param
543  *    parv[4] = time param 
544  *    (see report_memleak_stats() in runmalloc.c for details)
545  *
546  * This function is getting really ugly. -Ghostwolf
547  */
548 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
549 {
550   struct Message *mptr;
551   struct Client *acptr;
552   struct ConfItem *aconf;
553   char stat = parc > 1 ? parv[1][0] : '\0';
554   int i;
555
556   if (hunt_stats(cptr, sptr, parc, parv, stat, 0) != HUNTED_ISME)
557     return 0;
558
559   switch (stat)
560   {
561     case 'L':
562     case 'l':
563     {
564       int doall = 0;
565       int wilds = 0;
566       char *name = "*";
567
568       if (parc > 3 && !EmptyString(parv[3])) {
569         name = parv[3];
570         wilds = string_has_wildcards(name);
571       }
572       else
573         doall = 1;
574       /*
575        * Send info about connections which match, or all if the
576        * mask matches me.name.  Only restrictions are on those who
577        * are invisible not being visible to 'foreigners' who use
578        * a wild card based search to list it.
579        */
580       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
581                  "SendM SendKBytes RcveM RcveKBytes :Open since");
582       for (i = 0; i <= HighestFd; i++)
583       {
584         if (!(acptr = LocalClientArray[i]))
585           continue;
586         /* Don't return clients when this is a request for `all' */
587         if (doall && IsUser(acptr))
588           continue;
589         /* Don't show invisible people to unauthorized people when using
590          * wildcards  -- Is this still needed now /stats is oper only ? 
591          * Not here, because ms_stats is specifically a remote command, 
592          * thus the check was removed. -Ghostwolf */
593         /* Only show the ones that match the given mask - if any */
594         if (!doall && wilds && match(name, cli_name(acptr)))
595           continue;
596         /* Skip all that do not match the specific query */
597         if (!(doall || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
598           continue;
599         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
600                    "%s %u %u %u %u %u :%Tu", cli_name(acptr),
601                    (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
602                    (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
603                    (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
604       }
605       break;
606     }
607     case 'C':
608     case 'c':
609       report_configured_links(sptr, CONF_SERVER);
610       break;
611     case 'E':
612     case 'e': /* report engine name */
613       send_reply(sptr, RPL_STATSENGINE, engine_name());
614       break;
615     case 'G':
616     case 'g': /* send glines */
617       gline_stats(sptr);
618       break;
619     case 'H':
620     case 'h':
621       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
622       break;
623     case 'K':
624     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
625       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, !IsOper(sptr)))
626         return 0;
627       break;
628     case 'F':
629     case 'f':
630       feature_report(sptr);
631       break;
632     case 'I':
633     case 'i':
634     {
635       int   wilds = 0;
636       int   count = 3;
637       char* host;
638
639       if (parc < 4 && IsOper(sptr)) {
640         report_configured_links(sptr, CONF_CLIENT);
641         break;
642       }
643       if (parc < 4 || EmptyString(parv[3]))
644         return need_more_params(sptr, "STATS I");
645
646       if (IsOper(sptr)) {
647         wilds = string_has_wildcards(parv[3]);
648         count = 1000;
649       }
650
651       host = parv[3];
652
653       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
654         if (CONF_CLIENT == aconf->status) {
655           if ((!wilds && (!match(aconf->host, host) ||
656               !match(aconf->name, host))) ||
657               (wilds && (!mmatch(host, aconf->host) ||
658               !mmatch(host, aconf->name))))
659           {
660             if (aconf->passwd && IsDigit(*aconf->passwd) &&
661                 (!aconf->passwd[1] ||
662                  (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
663               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host,
664                          aconf->passwd, aconf->name, aconf->port,
665                          get_conf_class(aconf));
666               else
667                 send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
668                            aconf->name, aconf->port, get_conf_class(aconf));
669
670             if (--count == 0)
671               break;
672           }
673         }
674       }
675       break;
676     }
677     case 'M':
678 #if defined(MDEBUG)
679       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
680                  fda_get_block_count());
681 #endif
682       break;
683     case 'm':
684       for (mptr = msgtab; mptr->cmd; mptr++)
685         if (mptr->count)
686           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
687                      mptr->bytes);
688       break;
689     case 'o':
690     case 'O':
691       report_configured_links(sptr, CONF_OPS);
692       break;
693     case 'p':
694     case 'P':
695       /*
696        * show listener ports
697        * show hidden ports to opers, if there are more than 3 parameters,
698        * interpret the fourth parameter as the port number, limit non-local
699        * or non-oper results to 8 ports.
700        */ 
701       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
702       break;
703     case 'R':
704     case 'r':
705 #ifdef DEBUGMODE
706       send_usage(sptr, parv[0]);
707 #endif
708       break;
709     case 'D':
710       report_crule_list(sptr, CRULE_ALL);
711       break;
712     case 'd':
713       report_crule_list(sptr, CRULE_MASK);
714       break;
715     case 't':
716       tstats(sptr, parv[0]);
717       break;
718     case 'T':
719       motd_report(sptr);
720       break;
721     case 'U':
722       report_configured_links(sptr, CONF_UWORLD);
723       break;
724     case 'u':
725     {
726       time_t nowr;
727
728       nowr = CurrentTime - cli_since(&me);
729       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
730                  (nowr / 60) % 60, nowr % 60);
731       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
732       break;
733     }
734     case 'W':
735     case 'w':
736       calc_load(sptr);
737       break;
738     case 'X':
739     case 'x':
740 #ifdef  DEBUGMODE
741       class_send_meminfo(sptr);
742       send_listinfo(sptr, parv[0]);
743 #endif
744       break;
745     case 'Y':
746     case 'y':
747       report_classes(sptr);
748       break;
749     case 'Z':
750     case 'z':
751       count_memory(sptr, parv[0]);
752       break;
753     default:
754       stat = '*';
755       break;
756   }
757   send_reply(sptr, RPL_ENDOFSTATS, stat);
758   return 0;
759 }
760
761 /*
762  * mo_stats - oper message handler
763  *
764  *    parv[0] = sender prefix
765  *    parv[1] = statistics selector (defaults to Message frequency)
766  *    parv[2] = target server (current server defaulted, if omitted)
767  * And 'stats l' and 'stats' L:
768  *    parv[3] = server mask ("*" defaulted, if omitted)
769  * Or for stats p,P:
770  *    parv[3] = port mask (returns p-lines when its port is matched by this)
771  * Or for stats k,K,i and I:
772  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
773  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
774  *              (defaults to old reply if ommitted, when local or Oper)
775  *              A remote mask (something containing wildcards) is only
776  *              allowed for IRC Operators.
777  * Or for stats M:
778  *    parv[3] = time param
779  *    parv[4] = time param 
780  *    (see report_memleak_stats() in runmalloc.c for details)
781  *
782  * This function is getting really ugly. -Ghostwolf
783  */
784 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
785 {
786   struct Message*  mptr;
787   struct Client*   acptr;
788   struct ConfItem* aconf;
789   char             stat = parc > 1 ? parv[1][0] : '\0';
790   const char**     infotext = statsinfo;
791   int              i;
792
793   if (hunt_stats(cptr, sptr, parc, parv, stat, HEAD_IN_SAND_REMOTE)
794       != HUNTED_ISME)
795     return 0;
796
797   switch (stat)
798   {
799     case 'L':
800     case 'l':
801     {
802       int doall = 0, wilds = 0;
803       char* name = "*";
804       if (parc > 3 && !EmptyString(parv[3])) {
805         name = parv[3];
806         wilds = string_has_wildcards(name);
807       }
808       else
809         doall = 1;
810       /*
811        * Send info about connections which match, or all if the
812        * mask matches me.name.  Only restrictions are on those who
813        * are invisible not being visible to 'foreigners' who use
814        * a wild card based search to list it.
815        */
816       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
817                  "SendM SendKBytes RcveM RcveKBytes :Open since");
818       for (i = 0; i <= HighestFd; i++)
819       {
820         if (!(acptr = LocalClientArray[i]))
821           continue;
822         /* Don't return clients when this is a request for `all' */
823         if (doall && IsUser(acptr))
824           continue;
825         /* Only show the ones that match the given mask - if any */
826         if (!doall && wilds && match(name, cli_name(acptr)))
827           continue;
828         /* Skip all that do not match the specific query */
829         if (!(doall || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
830           continue;
831         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
832                    "%s %u %u %u %u %u :%Tu", cli_name(acptr),
833                    (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
834                    (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
835                    (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
836       }
837       break;
838     }
839     case 'C':
840     case 'c':
841       report_configured_links(sptr, CONF_SERVER);
842       break;
843     case 'E':
844     case 'e': /* report engine name */
845       send_reply(sptr, RPL_STATSENGINE, engine_name());
846       break;
847     case 'G':
848     case 'g': /* send glines */
849       gline_stats(sptr);
850       break;
851     case 'H':
852     case 'h':
853       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
854       break;
855     case 'K':
856     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
857       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, 0))
858         return 0;
859       break;
860     case 'F':
861     case 'f':
862       feature_report(sptr);
863       break;
864     case 'I':
865     case 'i':
866       {
867         int   wilds = 0;
868         int   count = 1000;
869         char* host;
870
871         if (parc < 4) {
872           report_configured_links(sptr, CONF_CLIENT);
873           break;
874         }
875         if (EmptyString(parv[3]))
876           return need_more_params(sptr, "STATS I");
877
878         host = parv[3];
879         wilds = string_has_wildcards(host);
880
881         for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
882           if (CONF_CLIENT == aconf->status) {
883             if ((!wilds && (!match(aconf->host, host) ||
884                 !match(aconf->name, host))) ||
885                 (wilds && (!mmatch(host, aconf->host) ||
886                 !mmatch(host, aconf->name))))
887             {
888               if (aconf->passwd && IsDigit(*aconf->passwd) &&
889                   (!aconf->passwd[1] ||
890                    (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
891                 send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, 
892                            aconf->passwd, aconf->name, aconf->port, 
893                            get_conf_class(aconf));
894               else 
895                 send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
896                            aconf->name, aconf->port, get_conf_class(aconf));
897
898               if (--count == 0)
899                 break;
900             }
901           }
902         }
903       }
904       break;
905     case 'M':
906 #if defined(MDEBUG)
907       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
908                  fda_get_block_count());
909 #endif
910       break;
911     case 'm':
912       for (mptr = msgtab; mptr->cmd; mptr++)
913         if (mptr->count)
914           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
915                      mptr->bytes);
916       break;
917     case 'o':
918     case 'O':
919       report_configured_links(sptr, CONF_OPS);
920       break;
921     case 'p':
922     case 'P':
923       /*
924        * show listener ports
925        * show hidden ports to opers, if there are more than 3 parameters,
926        * interpret the fourth parameter as the port number, limit non-local
927        * or non-oper results to 8 ports.
928        */ 
929       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
930       break;
931     case 'R':
932     case 'r':
933 #ifdef DEBUGMODE
934       send_usage(sptr, parv[0]);
935 #endif
936       break;
937     case 'D':
938       report_crule_list(sptr, CRULE_ALL);
939       break;
940     case 'd':
941       report_crule_list(sptr, CRULE_MASK);
942       break;
943     case 't':
944       tstats(sptr, parv[0]);
945       break;
946     case 'T':
947       motd_report(sptr);
948       break;
949     case 'U':
950       report_configured_links(sptr, CONF_UWORLD);
951       break;
952     case 'u':
953     {
954       time_t nowr;
955
956       nowr = CurrentTime - cli_since(&me);
957       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
958                  (nowr / 60) % 60, nowr % 60);
959       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
960       break;
961     }
962     case 'W':
963     case 'w':
964       calc_load(sptr);
965       break;
966     case 'X':
967     case 'x':
968 #ifdef  DEBUGMODE
969       class_send_meminfo(sptr);
970       send_listinfo(sptr, parv[0]);
971 #endif
972       break;
973     case 'Y':
974     case 'y':
975       report_classes(sptr);
976       break;
977     case 'Z':
978     case 'z':
979       count_memory(sptr, parv[0]);
980       break;
981     default:
982       stat = '*';
983       while (*infotext)
984         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
985       break;
986   }
987   send_reply(sptr, RPL_ENDOFSTATS, stat);
988   return 0;
989 }
990