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