Author: Isomer <isomer@coders.net>
[ircu2.10.12-pk.git] / ircd / m_stats.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_stats.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #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 "msg.h"
108 #include "numeric.h"
109 #include "numnicks.h"
110 #include "opercmds.h"
111 #include "s_bsd.h"
112 #include "s_conf.h"
113 #include "s_debug.h"
114 #include "s_misc.h"
115 #include "s_serv.h"
116 #include "s_user.h"
117 #include "send.h"
118 #include "struct.h"
119 #include "userload.h"
120
121 #include <assert.h>
122 #include <stdlib.h>
123 #include <string.h>
124
125
126 int report_klines(struct Client* sptr, char* mask, int limit_query)
127 {
128   int   wilds = 0;
129   int   count = 3;
130   char* user  = 0;
131   char* host;
132   const struct DenyConf* conf;
133
134   if (EmptyString(mask)) {
135     if (limit_query)
136       return need_more_params(sptr, "STATS K");
137     else
138       report_deny_list(sptr);
139     return 1;
140   }
141
142   if (!limit_query) {
143     wilds = string_has_wildcards(mask);
144     count = 1000;
145   }
146
147   if ((host = strchr(mask, '@'))) {
148     user = mask;
149     *host++ = '\0';
150   }
151   else {
152     host = mask;
153   }
154
155   for (conf = conf_get_deny_list(); conf; conf = conf->next) {
156     if ((!wilds && ((user || conf->hostmask) &&
157         !match(conf->hostmask, host) &&
158         (!user || !match(conf->usermask, user)))) ||
159         (wilds && !mmatch(host, conf->hostmask) &&
160         (!user || !mmatch(user, conf->usermask))))
161     {
162       send_reply(sptr, RPL_STATSKLINE, (conf->ip_kill) ? '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   struct Message *mptr;
199   struct Client *acptr;
200   struct ConfItem *aconf;
201   char stat = parc > 1 ? parv[1][0] : '\0';
202   const char **infotext = statsinfo;
203   int i;
204
205   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
206     return 0;
207
208   switch (stat)
209   {
210     case 'L':
211     case 'l':
212     {
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, acptr->name))
243           continue;
244         /* Skip all that do not match the specific query */
245         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
246           continue;
247         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
248                    "%s %u %u %u %u %u :%Tu", (*acptr->name) ? acptr->name : "<unregistered>",
249                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
250                    (int)acptr->sendK, (int)acptr->receiveM,
251                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
252       }
253       break;
254     }
255     case 'C':
256     case 'c':
257       report_configured_links(sptr, CONF_SERVER);
258       break;
259     case 'G':
260     case 'g': /* send glines */
261       gline_stats(sptr);
262       break;
263     case 'H':
264     case 'h':
265       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
266       break;
267     case 'K':
268     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
269       if (0 == report_klines(sptr, (parc == 4) ? parv[3] : 0, 0))
270         return 0;
271       break;
272     case 'I':
273     case 'i':
274     {
275       int wilds = 0;
276       int count = 1000;
277       char* host;
278
279       if (parc < 4) {
280         report_configured_links(sptr, CONF_CLIENT);
281         break;
282       }
283       if (EmptyString(parv[3]))
284         return need_more_params(sptr, "STATS I");
285
286       host = parv[3];
287       wilds = string_has_wildcards(host);
288
289       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
290         if (CONF_CLIENT == aconf->status) {
291           if ((!wilds && (!match(aconf->host, host) ||
292               !match(aconf->name, host))) ||
293               (wilds && (!mmatch(host, aconf->host) ||
294               !mmatch(host, aconf->name))))
295           {
296             send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
297                        aconf->port, get_conf_class(aconf));
298             if (--count == 0)
299               break;
300           }
301         }
302       }
303       break;
304     }
305     case 'M':
306 #if !defined(NDEBUG)
307       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
308                  fda_get_block_count());
309 #endif
310
311 #if 0
312 #ifdef MEMSIZESTATS
313       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
314           me.name, parv[0], get_mem_size(), get_alloc_cnt());
315 #endif
316 #ifdef MEMLEAKSTATS
317       report_memleak_stats(sptr, parc, parv);
318 #endif
319 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
320       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
321           "is not enabled on this server", me.name, parv[0]);
322 #endif
323 #endif /* 0 */
324       break;
325     case 'm':
326       for (mptr = msgtab; mptr->cmd; mptr++)
327         if (mptr->count)
328           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
329                      mptr->bytes);
330       break;
331     case 'o':
332     case 'O':
333       report_configured_links(sptr, CONF_OPS);
334       break;
335     case 'p':
336     case 'P':
337       /*
338        * show listener ports
339        * show hidden ports to opers, if there are more than 3 parameters,
340        * interpret the fourth parameter as the port number.
341        */ 
342       show_ports(sptr, 0, (parc > 3) ? atoi(parv[3]) : 0, 100);
343       break;
344     case 'R':
345     case 'r':
346 #ifdef DEBUGMODE
347       send_usage(sptr, parv[0]);
348 #endif
349       break;
350     case 'D':
351       report_crule_list(sptr, CRULE_ALL);
352       break;
353     case 'd':
354       report_crule_list(sptr, CRULE_MASK);
355       break;
356     case 't':
357       tstats(sptr, parv[0]);
358       break;
359     case 'T':
360       report_motd_list(sptr);
361       break;
362     case 'U':
363       report_configured_links(sptr, CONF_UWORLD);
364       break;
365     case 'u':
366     {
367       time_t nowr;
368
369       nowr = CurrentTime - me.since;
370       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
371                  (nowr / 60) % 60, nowr % 60);
372       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
373       break;
374     }
375     case 'W':
376     case 'w':
377       calc_load(sptr);
378       break;
379     case 'X':
380     case 'x':
381 #ifdef  DEBUGMODE
382       class_send_meminfo(sptr);
383       send_listinfo(sptr, parv[0]);
384 #endif
385       break;
386     case 'Y':
387     case 'y':
388       report_classes(sptr);
389       break;
390     case 'Z':
391     case 'z':
392       count_memory(sptr, parv[0]);
393       break;
394     default:
395       stat = '*';
396       while (*infotext)
397         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
398       break;
399   }
400   send_reply(sptr, RPL_ENDOFSTATS, stat);
401   return 0;
402 }
403
404 /*
405  * ms_stats - server message handler
406  *
407  *    parv[0] = sender prefix
408  *    parv[1] = statistics selector (defaults to Message frequency)
409  *    parv[2] = target server (current server defaulted, if omitted)
410  * And 'stats l' and 'stats' L:
411  *    parv[3] = server mask ("*" defaulted, if omitted)
412  * Or for stats p,P:
413  *    parv[3] = port mask (returns p-lines when its port is matched by this)
414  * Or for stats k,K,i and I:
415  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
416  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
417  *              (defaults to old reply if ommitted, when local or Oper)
418  *              A remote mask (something containing wildcards) is only
419  *              allowed for IRC Operators.
420  * Or for stats M:
421  *    parv[3] = time param
422  *    parv[4] = time param 
423  *    (see report_memleak_stats() in runmalloc.c for details)
424  *
425  * This function is getting really ugly. -Ghostwolf
426  */
427 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
428 {
429   struct Message *mptr;
430   struct Client *acptr;
431   struct ConfItem *aconf;
432   char stat = parc > 1 ? parv[1][0] : '\0';
433   int i;
434
435   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
436     return 0;
437
438   switch (stat)
439   {
440     case 'L':
441     case 'l':
442     {
443       int doall = 0;
444       int wilds = 0;
445       char *name = "*";
446
447       if (parc > 3 && !EmptyString(parv[3])) {
448         name = parv[3];
449         wilds = string_has_wildcards(name);
450       }
451       else
452         doall = 1;
453       /*
454        * Send info about connections which match, or all if the
455        * mask matches me.name.  Only restrictions are on those who
456        * are invisible not being visible to 'foreigners' who use
457        * a wild card based search to list it.
458        */
459       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
460                  "SendM SendKBytes RcveM RcveKBytes :Open since");
461       for (i = 0; i <= HighestFd; i++)
462       {
463         if (!(acptr = LocalClientArray[i]))
464           continue;
465         /* Don't return clients when this is a request for `all' */
466         if (doall && IsUser(acptr))
467           continue;
468         /* Don't show invisible people to unauthorized people when using
469          * wildcards  -- Is this still needed now /stats is oper only ? 
470          * Not here, because ms_stats is specifically a remote command, 
471          * thus the check was removed. -Ghostwolf */
472         /* Only show the ones that match the given mask - if any */
473         if (!doall && wilds && match(name, acptr->name))
474           continue;
475         /* Skip all that do not match the specific query */
476         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
477           continue;
478         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
479                    "%s %u %u %u %u %u :%Tu", acptr->name,
480                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
481                    (int)acptr->sendK, (int)acptr->receiveM,
482                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
483       }
484       break;
485     }
486     case 'C':
487     case 'c':
488       report_configured_links(sptr, CONF_SERVER);
489       break;
490     case 'G':
491     case 'g': /* send glines */
492       gline_stats(sptr);
493       break;
494     case 'H':
495     case 'h':
496       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
497       break;
498     case 'K':
499     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
500       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, !IsOper(sptr)))
501         return 0;
502       break;
503     case 'I':
504     case 'i':
505     {
506       int   wilds = 0;
507       int   count = 3;
508       char* host;
509
510       if (parc < 4 && IsOper(sptr)) {
511         report_configured_links(sptr, CONF_CLIENT);
512         break;
513       }
514       if (parc < 4 || EmptyString(parv[3]))
515         return need_more_params(sptr, "STATS I");
516
517       if (IsOper(sptr)) {
518         wilds = string_has_wildcards(parv[3]);
519         count = 1000;
520       }
521
522       host = parv[3];
523
524       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
525         if (CONF_CLIENT == aconf->status) {
526           if ((!wilds && (!match(aconf->host, host) ||
527               !match(aconf->name, host))) ||
528               (wilds && (!mmatch(host, aconf->host) ||
529               !mmatch(host, aconf->name))))
530           {
531             send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
532                        aconf->port, get_conf_class(aconf));
533             if (--count == 0)
534               break;
535           }
536         }
537       }
538       break;
539     }
540     case 'M':
541 #if !defined(NDEBUG)
542       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
543                  fda_get_block_count());
544 #endif
545
546 #if 0
547 #ifdef MEMSIZESTATS
548       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
549           me.name, parv[0], get_mem_size(), get_alloc_cnt());
550 #endif
551 #ifdef MEMLEAKSTATS
552       report_memleak_stats(sptr, parc, parv);
553 #endif
554 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
555       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
556           "is not enabled on this server", me.name, parv[0]);
557 #endif
558 #endif /* 0 */
559       break;
560     case 'm':
561       for (mptr = msgtab; mptr->cmd; mptr++)
562         if (mptr->count)
563           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
564                      mptr->bytes);
565       break;
566     case 'o':
567     case 'O':
568       report_configured_links(sptr, CONF_OPS);
569       break;
570     case 'p':
571     case 'P':
572       /*
573        * show listener ports
574        * show hidden ports to opers, if there are more than 3 parameters,
575        * interpret the fourth parameter as the port number, limit non-local
576        * or non-oper results to 8 ports.
577        */ 
578       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
579       break;
580     case 'R':
581     case 'r':
582 #ifdef DEBUGMODE
583       send_usage(sptr, parv[0]);
584 #endif
585       break;
586     case 'D':
587       report_crule_list(sptr, CRULE_ALL);
588       break;
589     case 'd':
590       report_crule_list(sptr, CRULE_MASK);
591       break;
592     case 't':
593       tstats(sptr, parv[0]);
594       break;
595     case 'T':
596       report_motd_list(sptr);
597       break;
598     case 'U':
599       report_configured_links(sptr, CONF_UWORLD);
600       break;
601     case 'u':
602     {
603       time_t nowr;
604
605       nowr = CurrentTime - me.since;
606       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
607                  (nowr / 60) % 60, nowr % 60);
608       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
609       break;
610     }
611     case 'W':
612     case 'w':
613       calc_load(sptr);
614       break;
615     case 'X':
616     case 'x':
617 #ifdef  DEBUGMODE
618       class_send_meminfo(sptr);
619       send_listinfo(sptr, parv[0]);
620 #endif
621       break;
622     case 'Y':
623     case 'y':
624       report_classes(sptr);
625       break;
626     case 'Z':
627     case 'z':
628       count_memory(sptr, parv[0]);
629       break;
630     default:
631       stat = '*';
632       break;
633   }
634   send_reply(sptr, RPL_ENDOFSTATS, stat);
635   return 0;
636 }
637
638 /*
639  * mo_stats - oper message handler
640  *
641  *    parv[0] = sender prefix
642  *    parv[1] = statistics selector (defaults to Message frequency)
643  *    parv[2] = target server (current server defaulted, if omitted)
644  * And 'stats l' and 'stats' L:
645  *    parv[3] = server mask ("*" defaulted, if omitted)
646  * Or for stats p,P:
647  *    parv[3] = port mask (returns p-lines when its port is matched by this)
648  * Or for stats k,K,i and I:
649  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
650  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
651  *              (defaults to old reply if ommitted, when local or Oper)
652  *              A remote mask (something containing wildcards) is only
653  *              allowed for IRC Operators.
654  * Or for stats M:
655  *    parv[3] = time param
656  *    parv[4] = time param 
657  *    (see report_memleak_stats() in runmalloc.c for details)
658  *
659  * This function is getting really ugly. -Ghostwolf
660  */
661 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
662 {
663   struct Message*  mptr;
664   struct Client*   acptr;
665   struct ConfItem* aconf;
666   char             stat = parc > 1 ? parv[1][0] : '\0';
667   const char**     infotext = statsinfo;
668   int              i;
669
670   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
671     return 0;
672
673   switch (stat)
674   {
675     case 'L':
676     case 'l':
677     {
678       int doall = 0, wilds = 0;
679       char* name = "*";
680       if (parc > 3 && !EmptyString(parv[3])) {
681         name = parv[3];
682         wilds = string_has_wildcards(name);
683       }
684       else
685         doall = 1;
686       /*
687        * Send info about connections which match, or all if the
688        * mask matches me.name.  Only restrictions are on those who
689        * are invisible not being visible to 'foreigners' who use
690        * a wild card based search to list it.
691        */
692       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
693                  "SendM SendKBytes RcveM RcveKBytes :Open since");
694       for (i = 0; i <= HighestFd; i++)
695       {
696         if (!(acptr = LocalClientArray[i]))
697           continue;
698         /* Don't return clients when this is a request for `all' */
699         if (doall && IsUser(acptr))
700           continue;
701         /* Only show the ones that match the given mask - if any */
702         if (!doall && wilds && match(name, acptr->name))
703           continue;
704         /* Skip all that do not match the specific query */
705         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
706           continue;
707         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
708                    "%s %u %u %u %u %u :%Tu", acptr->name,
709                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
710                    (int)acptr->sendK, (int)acptr->receiveM,
711                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
712       }
713       break;
714     }
715     case 'C':
716     case 'c':
717       report_configured_links(sptr, CONF_SERVER);
718       break;
719     case 'G':
720     case 'g': /* send glines */
721       gline_stats(sptr);
722       break;
723     case 'H':
724     case 'h':
725       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
726       break;
727     case 'K':
728     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
729       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, 0))
730         return 0;
731       break;
732     case 'I':
733     case 'i':
734       {
735         int   wilds = 0;
736         int   count = 1000;
737         char* host;
738
739         if (parc < 4) {
740           report_configured_links(sptr, CONF_CLIENT);
741           break;
742         }
743         if (EmptyString(parv[3]))
744           return need_more_params(sptr, "STATS I");
745
746         host = parv[3];
747         wilds = string_has_wildcards(host);
748
749         for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
750           if (CONF_CLIENT == aconf->status) {
751             if ((!wilds && (!match(aconf->host, host) ||
752                 !match(aconf->name, host))) ||
753                 (wilds && (!mmatch(host, aconf->host) ||
754                 !mmatch(host, aconf->name))))
755             {
756               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
757                          aconf->port, get_conf_class(aconf));
758               if (--count == 0)
759                 break;
760             }
761           }
762         }
763       }
764       break;
765     case 'M':
766 #if !defined(NDEBUG)
767       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
768                  fda_get_block_count());
769 #endif
770
771 #if 0
772 #ifdef MEMSIZESTATS
773       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
774           me.name, parv[0], get_mem_size(), get_alloc_cnt());
775 #endif
776 #ifdef MEMLEAKSTATS
777       report_memleak_stats(sptr, parc, parv);
778 #endif
779 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
780       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
781           "is not enabled on this server", me.name, parv[0]);
782 #endif
783 #endif /* 0 */
784       break;
785     case 'm':
786       for (mptr = msgtab; mptr->cmd; mptr++)
787         if (mptr->count)
788           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
789                      mptr->bytes);
790       break;
791     case 'o':
792     case 'O':
793       report_configured_links(sptr, CONF_OPS);
794       break;
795     case 'p':
796     case 'P':
797       /*
798        * show listener ports
799        * show hidden ports to opers, if there are more than 3 parameters,
800        * interpret the fourth parameter as the port number, limit non-local
801        * or non-oper results to 8 ports.
802        */ 
803       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
804       break;
805     case 'R':
806     case 'r':
807 #ifdef DEBUGMODE
808       send_usage(sptr, parv[0]);
809 #endif
810       break;
811     case 'D':
812       report_crule_list(sptr, CRULE_ALL);
813       break;
814     case 'd':
815       report_crule_list(sptr, CRULE_MASK);
816       break;
817     case 't':
818       tstats(sptr, parv[0]);
819       break;
820     case 'T':
821       report_motd_list(sptr);
822       break;
823     case 'U':
824       report_configured_links(sptr, CONF_UWORLD);
825       break;
826     case 'u':
827     {
828       time_t nowr;
829
830       nowr = CurrentTime - me.since;
831       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
832                  (nowr / 60) % 60, nowr % 60);
833       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
834       break;
835     }
836     case 'W':
837     case 'w':
838       calc_load(sptr);
839       break;
840     case 'X':
841     case 'x':
842 #ifdef  DEBUGMODE
843       class_send_meminfo(sptr);
844       send_listinfo(sptr, parv[0]);
845 #endif
846       break;
847     case 'Y':
848     case 'y':
849       report_classes(sptr);
850       break;
851     case 'Z':
852     case 'z':
853       count_memory(sptr, parv[0]);
854       break;
855     default:
856       stat = '*';
857       while (*infotext)
858         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
859       break;
860   }
861   send_reply(sptr, RPL_ENDOFSTATS, stat);
862   return 0;
863 }
864