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