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