118d7460115a14a69adaff60c56cf1391960195b
[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 'v':
376       {
377         const struct ConnectionClass* cl = get_class_list();
378
379         for ( ; cl; cl = cl->next) {
380           if (Links(cl) > 0)
381             send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
382         }
383       }
384       break;
385     case 'W':
386     case 'w':
387       calc_load(sptr);
388       break;
389     case 'X':
390     case 'x':
391 #ifdef  DEBUGMODE
392       class_send_meminfo(sptr);
393       send_listinfo(sptr, parv[0]);
394 #endif
395       break;
396     case 'Y':
397     case 'y':
398       report_classes(sptr);
399       break;
400     case 'Z':
401     case 'z':
402       count_memory(sptr, parv[0]);
403       break;
404     default:
405       stat = '*';
406       while (*infotext)
407         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
408       break;
409   }
410   send_reply(sptr, RPL_ENDOFSTATS, stat);
411   return 0;
412 }
413
414 /*
415  * ms_stats - server message handler
416  *
417  *    parv[0] = sender prefix
418  *    parv[1] = statistics selector (defaults to Message frequency)
419  *    parv[2] = target server (current server defaulted, if omitted)
420  * And 'stats l' and 'stats' L:
421  *    parv[3] = server mask ("*" defaulted, if omitted)
422  * Or for stats p,P:
423  *    parv[3] = port mask (returns p-lines when its port is matched by this)
424  * Or for stats k,K,i and I:
425  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
426  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
427  *              (defaults to old reply if ommitted, when local or Oper)
428  *              A remote mask (something containing wildcards) is only
429  *              allowed for IRC Operators.
430  * Or for stats M:
431  *    parv[3] = time param
432  *    parv[4] = time param 
433  *    (see report_memleak_stats() in runmalloc.c for details)
434  *
435  * This function is getting really ugly. -Ghostwolf
436  */
437 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
438 {
439   struct Message *mptr;
440   struct Client *acptr;
441   struct ConfItem *aconf;
442   char stat = parc > 1 ? parv[1][0] : '\0';
443   int i;
444
445   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
446     return 0;
447
448   switch (stat)
449   {
450     case 'L':
451     case 'l':
452     {
453       int doall = 0;
454       int wilds = 0;
455       char *name = "*";
456
457       if (parc > 3 && !EmptyString(parv[3])) {
458         name = parv[3];
459         wilds = string_has_wildcards(name);
460       }
461       else
462         doall = 1;
463       /*
464        * Send info about connections which match, or all if the
465        * mask matches me.name.  Only restrictions are on those who
466        * are invisible not being visible to 'foreigners' who use
467        * a wild card based search to list it.
468        */
469       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
470                  "SendM SendKBytes RcveM RcveKBytes :Open since");
471       for (i = 0; i <= HighestFd; i++)
472       {
473         if (!(acptr = LocalClientArray[i]))
474           continue;
475         /* Don't return clients when this is a request for `all' */
476         if (doall && IsUser(acptr))
477           continue;
478         /* Don't show invisible people to unauthorized people when using
479          * wildcards  -- Is this still needed now /stats is oper only ? 
480          * Not here, because ms_stats is specifically a remote command, 
481          * thus the check was removed. -Ghostwolf */
482         /* Only show the ones that match the given mask - if any */
483         if (!doall && wilds && match(name, acptr->name))
484           continue;
485         /* Skip all that do not match the specific query */
486         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
487           continue;
488         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
489                    "%s %u %u %u %u %u :%Tu", acptr->name,
490                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
491                    (int)acptr->sendK, (int)acptr->receiveM,
492                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
493       }
494       break;
495     }
496     case 'C':
497     case 'c':
498       report_configured_links(sptr, CONF_SERVER);
499       break;
500     case 'G':
501     case 'g': /* send glines */
502       gline_stats(sptr);
503       break;
504     case 'H':
505     case 'h':
506       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
507       break;
508     case 'K':
509     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
510       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, !IsOper(sptr)))
511         return 0;
512       break;
513     case 'I':
514     case 'i':
515     {
516       int   wilds = 0;
517       int   count = 3;
518       char* host;
519
520       if (parc < 4 && IsOper(sptr)) {
521         report_configured_links(sptr, CONF_CLIENT);
522         break;
523       }
524       if (parc < 4 || EmptyString(parv[3]))
525         return need_more_params(sptr, "STATS I");
526
527       if (IsOper(sptr)) {
528         wilds = string_has_wildcards(parv[3]);
529         count = 1000;
530       }
531
532       host = parv[3];
533
534       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
535         if (CONF_CLIENT == aconf->status) {
536           if ((!wilds && (!match(aconf->host, host) ||
537               !match(aconf->name, host))) ||
538               (wilds && (!mmatch(host, aconf->host) ||
539               !mmatch(host, aconf->name))))
540           {
541             send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
542                        aconf->port, get_conf_class(aconf));
543             if (--count == 0)
544               break;
545           }
546         }
547       }
548       break;
549     }
550     case 'M':
551 #if !defined(NDEBUG)
552       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
553                  fda_get_block_count());
554 #endif
555
556 #if 0
557 #ifdef MEMSIZESTATS
558       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
559           me.name, parv[0], get_mem_size(), get_alloc_cnt());
560 #endif
561 #ifdef MEMLEAKSTATS
562       report_memleak_stats(sptr, parc, parv);
563 #endif
564 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
565       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
566           "is not enabled on this server", me.name, parv[0]);
567 #endif
568 #endif /* 0 */
569       break;
570     case 'm':
571       for (mptr = msgtab; mptr->cmd; mptr++)
572         if (mptr->count)
573           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
574                      mptr->bytes);
575       break;
576     case 'o':
577     case 'O':
578       report_configured_links(sptr, CONF_OPS);
579       break;
580     case 'p':
581     case 'P':
582       /*
583        * show listener ports
584        * show hidden ports to opers, if there are more than 3 parameters,
585        * interpret the fourth parameter as the port number, limit non-local
586        * or non-oper results to 8 ports.
587        */ 
588       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
589       break;
590     case 'R':
591     case 'r':
592 #ifdef DEBUGMODE
593       send_usage(sptr, parv[0]);
594 #endif
595       break;
596     case 'D':
597       report_crule_list(sptr, CRULE_ALL);
598       break;
599     case 'd':
600       report_crule_list(sptr, CRULE_MASK);
601       break;
602     case 't':
603       tstats(sptr, parv[0]);
604       break;
605     case 'T':
606       report_motd_list(sptr);
607       break;
608     case 'U':
609       report_configured_links(sptr, CONF_UWORLD);
610       break;
611     case 'u':
612     {
613       time_t nowr;
614
615       nowr = CurrentTime - me.since;
616       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
617                  (nowr / 60) % 60, nowr % 60);
618       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
619       break;
620     }
621     case 'v':
622       {
623         const struct ConnectionClass* cl = get_class_list();
624             
625         for ( ; cl; cl = cl->next) {
626           if (Links(cl) > 0)
627             send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
628         }
629       }
630       break;
631     
632     case 'W':
633     case 'w':
634       calc_load(sptr);
635       break;
636     case 'X':
637     case 'x':
638 #ifdef  DEBUGMODE
639       class_send_meminfo(sptr);
640       send_listinfo(sptr, parv[0]);
641 #endif
642       break;
643     case 'Y':
644     case 'y':
645       report_classes(sptr);
646       break;
647     case 'Z':
648     case 'z':
649       count_memory(sptr, parv[0]);
650       break;
651     default:
652       stat = '*';
653       break;
654   }
655   send_reply(sptr, RPL_ENDOFSTATS, stat);
656   return 0;
657 }
658
659 /*
660  * mo_stats - oper message handler
661  *
662  *    parv[0] = sender prefix
663  *    parv[1] = statistics selector (defaults to Message frequency)
664  *    parv[2] = target server (current server defaulted, if omitted)
665  * And 'stats l' and 'stats' L:
666  *    parv[3] = server mask ("*" defaulted, if omitted)
667  * Or for stats p,P:
668  *    parv[3] = port mask (returns p-lines when its port is matched by this)
669  * Or for stats k,K,i and I:
670  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
671  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
672  *              (defaults to old reply if ommitted, when local or Oper)
673  *              A remote mask (something containing wildcards) is only
674  *              allowed for IRC Operators.
675  * Or for stats M:
676  *    parv[3] = time param
677  *    parv[4] = time param 
678  *    (see report_memleak_stats() in runmalloc.c for details)
679  *
680  * This function is getting really ugly. -Ghostwolf
681  */
682 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
683 {
684   struct Message*  mptr;
685   struct Client*   acptr;
686   struct ConfItem* aconf;
687   char             stat = parc > 1 ? parv[1][0] : '\0';
688   const char**     infotext = statsinfo;
689   int              i;
690
691   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
692     return 0;
693
694   switch (stat)
695   {
696     case 'L':
697     case 'l':
698     {
699       int doall = 0, wilds = 0;
700       char* name = "*";
701       if (parc > 3 && !EmptyString(parv[3])) {
702         name = parv[3];
703         wilds = string_has_wildcards(name);
704       }
705       else
706         doall = 1;
707       /*
708        * Send info about connections which match, or all if the
709        * mask matches me.name.  Only restrictions are on those who
710        * are invisible not being visible to 'foreigners' who use
711        * a wild card based search to list it.
712        */
713       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
714                  "SendM SendKBytes RcveM RcveKBytes :Open since");
715       for (i = 0; i <= HighestFd; i++)
716       {
717         if (!(acptr = LocalClientArray[i]))
718           continue;
719         /* Don't return clients when this is a request for `all' */
720         if (doall && IsUser(acptr))
721           continue;
722         /* Only show the ones that match the given mask - if any */
723         if (!doall && wilds && match(name, acptr->name))
724           continue;
725         /* Skip all that do not match the specific query */
726         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
727           continue;
728         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
729                    "%s %u %u %u %u %u :%Tu", acptr->name,
730                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
731                    (int)acptr->sendK, (int)acptr->receiveM,
732                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
733       }
734       break;
735     }
736     case 'C':
737     case 'c':
738       report_configured_links(sptr, CONF_SERVER);
739       break;
740     case 'G':
741     case 'g': /* send glines */
742       gline_stats(sptr);
743       break;
744     case 'H':
745     case 'h':
746       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
747       break;
748     case 'K':
749     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
750       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, 0))
751         return 0;
752       break;
753     case 'I':
754     case 'i':
755       {
756         int   wilds = 0;
757         int   count = 1000;
758         char* host;
759
760         if (parc < 4) {
761           report_configured_links(sptr, CONF_CLIENT);
762           break;
763         }
764         if (EmptyString(parv[3]))
765           return need_more_params(sptr, "STATS I");
766
767         host = parv[3];
768         wilds = string_has_wildcards(host);
769
770         for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
771           if (CONF_CLIENT == aconf->status) {
772             if ((!wilds && (!match(aconf->host, host) ||
773                 !match(aconf->name, host))) ||
774                 (wilds && (!mmatch(host, aconf->host) ||
775                 !mmatch(host, aconf->name))))
776             {
777               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
778                          aconf->port, get_conf_class(aconf));
779               if (--count == 0)
780                 break;
781             }
782           }
783         }
784       }
785       break;
786     case 'M':
787 #if !defined(NDEBUG)
788       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
789                  fda_get_block_count());
790 #endif
791
792 #if 0
793 #ifdef MEMSIZESTATS
794       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
795           me.name, parv[0], get_mem_size(), get_alloc_cnt());
796 #endif
797 #ifdef MEMLEAKSTATS
798       report_memleak_stats(sptr, parc, parv);
799 #endif
800 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
801       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
802           "is not enabled on this server", me.name, parv[0]);
803 #endif
804 #endif /* 0 */
805       break;
806     case 'm':
807       for (mptr = msgtab; mptr->cmd; mptr++)
808         if (mptr->count)
809           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
810                      mptr->bytes);
811       break;
812     case 'o':
813     case 'O':
814       report_configured_links(sptr, CONF_OPS);
815       break;
816     case 'p':
817     case 'P':
818       /*
819        * show listener ports
820        * show hidden ports to opers, if there are more than 3 parameters,
821        * interpret the fourth parameter as the port number, limit non-local
822        * or non-oper results to 8 ports.
823        */ 
824       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
825       break;
826     case 'R':
827     case 'r':
828 #ifdef DEBUGMODE
829       send_usage(sptr, parv[0]);
830 #endif
831       break;
832     case 'D':
833       report_crule_list(sptr, CRULE_ALL);
834       break;
835     case 'd':
836       report_crule_list(sptr, CRULE_MASK);
837       break;
838     case 't':
839       tstats(sptr, parv[0]);
840       break;
841     case 'T':
842       report_motd_list(sptr);
843       break;
844     case 'U':
845       report_configured_links(sptr, CONF_UWORLD);
846       break;
847     case 'u':
848     {
849       time_t nowr;
850
851       nowr = CurrentTime - me.since;
852       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
853                  (nowr / 60) % 60, nowr % 60);
854       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
855       break;
856     }
857     case 'v':
858       {
859         const struct ConnectionClass* cl = get_class_list();
860             
861         for ( ; cl; cl = cl->next) {
862           if (Links(cl) > 0)
863             send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
864         }
865       }
866       break;
867     case 'W':
868     case 'w':
869       calc_load(sptr);
870       break;
871     case 'X':
872     case 'x':
873 #ifdef  DEBUGMODE
874       class_send_meminfo(sptr);
875       send_listinfo(sptr, parv[0]);
876 #endif
877       break;
878     case 'Y':
879     case 'y':
880       report_classes(sptr);
881       break;
882     case 'Z':
883     case 'z':
884       count_memory(sptr, parv[0]);
885       break;
886     default:
887       stat = '*';
888       while (*infotext)
889         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
890       break;
891   }
892   send_reply(sptr, RPL_ENDOFSTATS, stat);
893   return 0;
894 }
895