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