Author: Bleep <tomh@inxpress.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 "channel.h"
94 #include "class.h"
95 #include "client.h"
96 #include "gline.h"
97 #include "hash.h"
98 #include "ircd.h"
99 #include "ircd_alloc.h"
100 #include "ircd_chattr.h"
101 #include "ircd_reply.h"
102 #include "ircd_string.h"
103 #include "list.h"
104 #include "listener.h"
105 #include "match.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  * m_stats - generic message handler
126  *
127  *    parv[0] = sender prefix
128  *    parv[1] = statistics selector (defaults to Message frequency)
129  *    parv[2] = target server (current server defaulted, if omitted)
130  * And 'stats l' and 'stats' L:
131  *    parv[3] = server mask ("*" defaulted, if omitted)
132  * Or for stats p,P:
133  *    parv[3] = port mask (returns p-lines when its port is matched by this)
134  * Or for stats k,K,i and I:
135  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
136  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
137  *              (defaults to old reply if ommitted, when local or Oper)
138  *              A remote mask (something containing wildcards) is only
139  *              allowed for IRC Operators.
140  * Or for stats M:
141  *    parv[3] = time param
142  *    parv[4] = time param 
143  *    (see report_memleak_stats() in runmalloc.c for details)
144  *
145  * This function is getting really ugly. -Ghostwolf
146  */
147 int m_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
148 {
149   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
150       "RcveM RcveKBytes :Open since";
151   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
152   struct Message *mptr;
153   struct Client *acptr;
154   struct Gline* gline;
155   struct ConfItem *aconf;
156   char stat = parc > 1 ? parv[1][0] : '\0';
157   int i;
158
159 /* m_stats is so obnoxiously full of special cases that the different
160  * hunt_server() possiblites were becoming very messy. It now uses a
161  * switch() so as to be easier to read and update as params change. 
162  * -Ghostwolf 
163  */
164   switch (stat)
165   {
166       /* open to all, standard # of params */
167     case 'U':
168     case 'u':
169     {
170       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
171           != HUNTED_ISME)
172         return 0;
173       break;
174     }
175
176       /* open to all, varying # of params */
177     case 'k':
178     case 'K':
179     case 'i':
180     case 'I':
181     case 'p':
182     case 'P':
183     {
184       if (parc > 3)
185       {
186         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
187             != HUNTED_ISME)
188           return 0;
189       }
190       else
191       {
192         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
193             != HUNTED_ISME)
194           return 0;
195       }
196       break;
197     }
198
199       /* oper only, varying # of params */
200     case 'l':
201     case 'L':
202     case 'M':
203     {
204       if (parc == 4)
205       {
206         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
207             != HUNTED_ISME)
208           return 0;
209       }
210       else if (parc > 4)
211       {
212         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc,
213             parv) != HUNTED_ISME)
214           return 0;
215       }
216       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
217           != HUNTED_ISME)
218         return 0;
219       break;
220     }
221
222       /* oper only, standard # of params */
223     default:
224     {
225       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
226           != HUNTED_ISME)
227         return 0;
228       break;
229     }
230   }
231
232   switch (stat)
233   {
234     case 'L':
235     case 'l':
236     {
237       int doall = 0, wilds = 0;
238       char *name = "*";
239       if (parc > 3 && *parv[3])
240       {
241         char *p;
242         name = parv[3];
243         wilds = (*name == '*' || *name == '?');
244         for (p = name + 1; *p; ++p)
245           if ((*p == '*' || *p == '?') && p[-1] != '\\')
246           {
247             wilds = 1;
248             break;
249           }
250       }
251       else
252         doall = 1;
253       /*
254        * Send info about connections which match, or all if the
255        * mask matches me.name.  Only restrictions are on those who
256        * are invisible not being visible to 'foreigners' who use
257        * a wild card based search to list it.
258        */
259       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
260       for (i = 0; i <= HighestFd; i++)
261       {
262         if (!(acptr = LocalClientArray[i]))
263           continue;
264         /* Don't return clients when this is a request for `all' */
265         if (doall && IsUser(acptr))
266           continue;
267         /* Don't show invisible people to unauthorized people when using
268          * wildcards  -- Is this still needed now /stats is oper only ? */
269         if (IsInvisible(acptr) && (doall || wilds) &&
270             !(MyConnect(sptr) && IsOper(sptr)) &&
271             !IsAnOper(acptr) && (acptr != sptr))
272           continue;
273         /* Only show the ones that match the given mask - if any */
274         if (!doall && wilds && match(name, acptr->name))
275           continue;
276         /* Skip all that do not match the specific query */
277         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
278           continue;
279         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
280                    acptr->name, (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
281                    (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
282                    CurrentTime - acptr->firsttime);
283       }
284       break;
285     }
286     case 'C':
287     case 'c':
288       report_configured_links(sptr, CONF_SERVER);
289       break;
290     case 'G':
291     case 'g': /* send glines */
292       gline_remove_expired(TStime());
293       for (gline = GlobalGlineList; gline; gline = gline->next) {
294         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
295                    sptr->name, 'G', gline->name, gline->host,
296                    gline->expire, gline->reason);
297       }
298       break;
299     case 'H':
300     case 'h':
301       break;
302     case 'I':
303     case 'i':
304     case 'K':
305     case 'k':                   /* display CONF_IPKILL as well
306                                    as CONF_KILL -Kev */
307     {
308       int wilds, count;
309       char *user, *host, *p;
310       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
311       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
312       {
313         report_configured_links(sptr, conf_status);
314         break;
315       }
316       if (parc < 4 || *parv[3] == '\0')
317         return need_more_params(sptr,
318                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
319
320       wilds = 0;
321       for (p = parv[3]; *p; p++)
322       {
323         if (*p == '\\')
324         {
325           if (!*++p)
326             break;
327           continue;
328         }
329         if (*p == '?' || *p == '*')
330         {
331           wilds = 1;
332           break;
333         }
334       }
335       if (!(MyConnect(sptr) || IsOper(sptr)))
336       {
337         wilds = 0;
338         count = 3;
339       }
340       else
341         count = 1000;
342
343       if (conf_status == CONF_CLIENT)
344       {
345         user = 0;            /* Not used, but to avoid compiler warning. */
346
347         host = parv[3];
348       }
349       else
350       {
351         if ((host = strchr(parv[3], '@')))
352         {
353           user = parv[3];
354           *host++ = 0;;
355         }
356         else
357         {
358           user = 0;
359           host = parv[3];
360         }
361       }
362       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
363       {
364         if ((aconf->status & conf_status))
365         {
366           if (conf_status == CONF_KLINE)
367           {
368             if ((!wilds && ((user || aconf->host[1]) &&
369                 !match(aconf->host, host) &&
370                 (!user || !match(aconf->name, user)))) ||
371                 (wilds && !mmatch(host, aconf->host) &&
372                 (!user || !mmatch(user, aconf->name))))
373             {
374               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
375                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
376                   aconf->port, get_conf_class(aconf));
377               if (--count == 0)
378                 break;
379             }
380           }
381           else if (conf_status == CONF_CLIENT)
382           {
383             if ((!wilds && (!match(aconf->host, host) ||
384                 !match(aconf->name, host))) ||
385                 (wilds && (!mmatch(host, aconf->host) ||
386                 !mmatch(host, aconf->name))))
387             {
388               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
389                   sptr->name, 'I', aconf->host, aconf->name,
390                   aconf->port, get_conf_class(aconf));
391               if (--count == 0)
392                 break;
393             }
394           }
395         }
396       }
397       break;
398     }
399     case 'M':
400 #if !defined(NDEBUG)
401       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
402           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
403 #endif
404
405 #if 0
406 #ifdef MEMSIZESTATS
407       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
408           me.name, parv[0], get_mem_size(), get_alloc_cnt());
409 #endif
410 #ifdef MEMLEAKSTATS
411       report_memleak_stats(sptr, parc, parv);
412 #endif
413 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
414       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
415           "is not enabled on this server", me.name, parv[0]);
416 #endif
417 #endif /* 0 */
418       break;
419     case 'm':
420       for (mptr = msgtab; mptr->cmd; mptr++)
421         if (mptr->count)
422           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
423               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
424       break;
425     case 'o':
426     case 'O':
427       report_configured_links(sptr, CONF_OPS);
428       break;
429     case 'p':
430     case 'P':
431       /*
432        * show listener ports
433        * show hidden ports to opers, if there are more than 3 parameters,
434        * interpret the fourth parameter as the port number, limit non-local
435        * or non-oper results to 8 ports.
436        */ 
437       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
438                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
439       break;
440     case 'R':
441     case 'r':
442 #ifdef DEBUGMODE
443       send_usage(sptr, parv[0]);
444 #endif
445       break;
446     case 'D':
447       report_configured_links(sptr, CONF_CRULEALL);
448       break;
449     case 'd':
450       report_configured_links(sptr, CONF_CRULE);
451       break;
452     case 't':
453       tstats(sptr, parv[0]);
454       break;
455     case 'T':
456       report_configured_links(sptr, CONF_TLINES);
457       break;
458     case 'U':
459       report_configured_links(sptr, CONF_UWORLD);
460       break;
461     case 'u':
462     {
463       time_t nowr;
464
465       nowr = CurrentTime - me.since;
466       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
467           nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
468       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
469           max_connection_count, max_client_count);
470       break;
471     }
472     case 'W':
473     case 'w':
474       calc_load(sptr);
475       break;
476     case 'X':
477     case 'x':
478 #ifdef  DEBUGMODE
479       send_listinfo(sptr, parv[0]);
480 #endif
481       break;
482     case 'Y':
483     case 'y':
484       report_classes(sptr);
485       break;
486     case 'Z':
487     case 'z':
488       count_memory(sptr, parv[0]);
489       break;
490     default:
491       stat = '*';
492       break;
493   }
494   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
495   return 0;
496 }
497
498 /*
499  * ms_stats - server message handler
500  *
501  *    parv[0] = sender prefix
502  *    parv[1] = statistics selector (defaults to Message frequency)
503  *    parv[2] = target server (current server defaulted, if omitted)
504  * And 'stats l' and 'stats' L:
505  *    parv[3] = server mask ("*" defaulted, if omitted)
506  * Or for stats p,P:
507  *    parv[3] = port mask (returns p-lines when its port is matched by this)
508  * Or for stats k,K,i and I:
509  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
510  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
511  *              (defaults to old reply if ommitted, when local or Oper)
512  *              A remote mask (something containing wildcards) is only
513  *              allowed for IRC Operators.
514  * Or for stats M:
515  *    parv[3] = time param
516  *    parv[4] = time param 
517  *    (see report_memleak_stats() in runmalloc.c for details)
518  *
519  * This function is getting really ugly. -Ghostwolf
520  */
521 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
522 {
523   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
524       "RcveM RcveKBytes :Open since";
525   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
526   struct Message *mptr;
527   struct Client *acptr;
528   struct Gline* gline;
529   struct ConfItem *aconf;
530   char stat = parc > 1 ? parv[1][0] : '\0';
531   int i;
532
533 /* m_stats is so obnoxiously full of special cases that the different
534  * hunt_server() possiblites were becoming very messy. It now uses a
535  * switch() so as to be easier to read and update as params change. 
536  * -Ghostwolf 
537  */
538   switch (stat)
539   {
540       /* open to all, standard # of params */
541     case 'U':
542     case 'u':
543     {
544       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
545           != HUNTED_ISME)
546         return 0;
547       break;
548     }
549
550       /* open to all, varying # of params */
551     case 'k':
552     case 'K':
553     case 'i':
554     case 'I':
555     case 'p':
556     case 'P':
557     {
558       if (parc > 3)
559       {
560         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
561             != HUNTED_ISME)
562           return 0;
563       }
564       else
565       {
566         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
567             != HUNTED_ISME)
568           return 0;
569       }
570       break;
571     }
572
573       /* oper only, varying # of params */
574     case 'l':
575     case 'L':
576     case 'M':
577     {
578       if (parc == 4)
579       {
580         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
581             != HUNTED_ISME)
582           return 0;
583       }
584       else if (parc > 4)
585       {
586         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc,
587             parv) != HUNTED_ISME)
588           return 0;
589       }
590       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
591           != HUNTED_ISME)
592         return 0;
593       break;
594     }
595
596       /* oper only, standard # of params */
597     default:
598     {
599       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
600           != HUNTED_ISME)
601         return 0;
602       break;
603     }
604   }
605
606   switch (stat)
607   {
608     case 'L':
609     case 'l':
610     {
611       int doall = 0, wilds = 0;
612       char *name = "*";
613       if (parc > 3 && *parv[3])
614       {
615         char *p;
616         name = parv[3];
617         wilds = (*name == '*' || *name == '?');
618         for (p = name + 1; *p; ++p)
619           if ((*p == '*' || *p == '?') && p[-1] != '\\')
620           {
621             wilds = 1;
622             break;
623           }
624       }
625       else
626         doall = 1;
627       /*
628        * Send info about connections which match, or all if the
629        * mask matches me.name.  Only restrictions are on those who
630        * are invisible not being visible to 'foreigners' who use
631        * a wild card based search to list it.
632        */
633       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
634       for (i = 0; i <= HighestFd; i++)
635       {
636         if (!(acptr = LocalClientArray[i]))
637           continue;
638         /* Don't return clients when this is a request for `all' */
639         if (doall && IsUser(acptr))
640           continue;
641         /* Don't show invisible people to unauthorized people when using
642          * wildcards  -- Is this still needed now /stats is oper only ? */
643         if (IsInvisible(acptr) && (doall || wilds) &&
644             !(MyConnect(sptr) && IsOper(sptr)) &&
645             !IsAnOper(acptr) && (acptr != sptr))
646           continue;
647         /* Only show the ones that match the given mask - if any */
648         if (!doall && wilds && match(name, acptr->name))
649           continue;
650         /* Skip all that do not match the specific query */
651         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
652           continue;
653         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
654             acptr->name, (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
655             (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
656             CurrentTime - acptr->firsttime);
657       }
658       break;
659     }
660     case 'C':
661     case 'c':
662       if (IsAnOper(sptr))
663         report_configured_links(sptr, CONF_SERVER);
664       break;
665     case 'G':
666     case 'g': /* send glines */
667       gline_remove_expired(TStime());
668       for (gline = GlobalGlineList; gline; gline = gline->next) {
669         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
670                    sptr->name, 'G', gline->name, gline->host,
671                    gline->expire, gline->reason);
672       }
673       break;
674     case 'H':
675     case 'h':
676       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
677       break;
678     case 'I':
679     case 'i':
680     case 'K':
681     case 'k':                   /* display CONF_IPKILL as well
682                                    as CONF_KILL -Kev */
683     {
684       int wilds, count;
685       char *user, *host, *p;
686       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
687       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
688       {
689         report_configured_links(sptr, conf_status);
690         break;
691       }
692       if (parc < 4 || *parv[3] == '\0')
693         return need_more_params(sptr,
694                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
695
696       wilds = 0;
697       for (p = parv[3]; *p; p++)
698       {
699         if (*p == '\\')
700         {
701           if (!*++p)
702             break;
703           continue;
704         }
705         if (*p == '?' || *p == '*')
706         {
707           wilds = 1;
708           break;
709         }
710       }
711       if (!(MyConnect(sptr) || IsOper(sptr)))
712       {
713         wilds = 0;
714         count = 3;
715       }
716       else
717         count = 1000;
718
719       if (conf_status == CONF_CLIENT)
720       {
721         user = 0;            /* Not used, but to avoid compiler warning. */
722
723         host = parv[3];
724       }
725       else
726       {
727         if ((host = strchr(parv[3], '@')))
728         {
729           user = parv[3];
730           *host++ = 0;;
731         }
732         else
733         {
734           user = 0;
735           host = parv[3];
736         }
737       }
738       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
739       {
740         if ((aconf->status & conf_status))
741         {
742           if (conf_status == CONF_KLINE)
743           {
744             if ((!wilds && ((user || aconf->host[1]) &&
745                 !match(aconf->host, host) &&
746                 (!user || !match(aconf->name, user)))) ||
747                 (wilds && !mmatch(host, aconf->host) &&
748                 (!user || !mmatch(user, aconf->name))))
749             {
750               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
751                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
752                   aconf->port, get_conf_class(aconf));
753               if (--count == 0)
754                 break;
755             }
756           }
757           else if (conf_status == CONF_CLIENT)
758           {
759             if ((!wilds && (!match(aconf->host, host) ||
760                 !match(aconf->name, host))) ||
761                 (wilds && (!mmatch(host, aconf->host) ||
762                 !mmatch(host, aconf->name))))
763             {
764               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
765                   sptr->name, 'I', aconf->host, aconf->name,
766                   aconf->port, get_conf_class(aconf));
767               if (--count == 0)
768                 break;
769             }
770           }
771         }
772       }
773       break;
774     }
775     case 'M':
776 #if !defined(NDEBUG)
777       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
778           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
779 #endif
780
781 #if 0
782 #ifdef MEMSIZESTATS
783       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
784           me.name, parv[0], get_mem_size(), get_alloc_cnt());
785 #endif
786 #ifdef MEMLEAKSTATS
787       report_memleak_stats(sptr, parc, parv);
788 #endif
789 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
790       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
791           "is not enabled on this server", me.name, parv[0]);
792 #endif
793 #endif /* 0 */
794       break;
795     case 'm':
796       for (mptr = msgtab; mptr->cmd; mptr++)
797         if (mptr->count)
798           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
799               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
800       break;
801     case 'o':
802     case 'O':
803       report_configured_links(sptr, CONF_OPS);
804       break;
805     case 'p':
806     case 'P':
807       /*
808        * show listener ports
809        * show hidden ports to opers, if there are more than 3 parameters,
810        * interpret the fourth parameter as the port number, limit non-local
811        * or non-oper results to 8 ports.
812        */ 
813       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
814                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
815       break;
816     case 'R':
817     case 'r':
818 #ifdef DEBUGMODE
819       send_usage(sptr, parv[0]);
820 #endif
821       break;
822     case 'D':
823       report_configured_links(sptr, CONF_CRULEALL);
824       break;
825     case 'd':
826       report_configured_links(sptr, CONF_CRULE);
827       break;
828     case 't':
829       tstats(sptr, parv[0]);
830       break;
831     case 'T':
832       report_configured_links(sptr, CONF_TLINES);
833       break;
834     case 'U':
835       report_configured_links(sptr, CONF_UWORLD);
836       break;
837     case 'u':
838     {
839       time_t nowr;
840
841       nowr = CurrentTime - me.since;
842       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
843           nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
844       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
845           max_connection_count, max_client_count);
846       break;
847     }
848     case 'W':
849     case 'w':
850       calc_load(sptr);
851       break;
852     case 'X':
853     case 'x':
854 #ifdef  DEBUGMODE
855       send_listinfo(sptr, parv[0]);
856 #endif
857       break;
858     case 'Y':
859     case 'y':
860       report_classes(sptr);
861       break;
862     case 'Z':
863     case 'z':
864       count_memory(sptr, parv[0]);
865       break;
866     default:
867       stat = '*';
868       break;
869   }
870   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
871   return 0;
872 }
873
874 /*
875  * mo_stats - oper message handler
876  *
877  *    parv[0] = sender prefix
878  *    parv[1] = statistics selector (defaults to Message frequency)
879  *    parv[2] = target server (current server defaulted, if omitted)
880  * And 'stats l' and 'stats' L:
881  *    parv[3] = server mask ("*" defaulted, if omitted)
882  * Or for stats p,P:
883  *    parv[3] = port mask (returns p-lines when its port is matched by this)
884  * Or for stats k,K,i and I:
885  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
886  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
887  *              (defaults to old reply if ommitted, when local or Oper)
888  *              A remote mask (something containing wildcards) is only
889  *              allowed for IRC Operators.
890  * Or for stats M:
891  *    parv[3] = time param
892  *    parv[4] = time param 
893  *    (see report_memleak_stats() in runmalloc.c for details)
894  *
895  * This function is getting really ugly. -Ghostwolf
896  */
897 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
898 {
899   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
900       "RcveM RcveKBytes :Open since";
901   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
902   struct Message *mptr;
903   struct Client *acptr;
904   struct Gline* gline;
905   struct ConfItem *aconf;
906   char stat = parc > 1 ? parv[1][0] : '\0';
907   int i;
908
909 /* m_stats is so obnoxiously full of special cases that the different
910  * hunt_server() possiblites were becoming very messy. It now uses a
911  * switch() so as to be easier to read and update as params change. 
912  * -Ghostwolf 
913  */
914   switch (stat)
915   {
916       /* open to all, standard # of params */
917     case 'U':
918     case 'u':
919     {
920       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
921           != HUNTED_ISME)
922         return 0;
923       break;
924     }
925
926       /* open to all, varying # of params */
927     case 'k':
928     case 'K':
929     case 'i':
930     case 'I':
931     case 'p':
932     case 'P':
933     {
934       if (parc > 3)
935       {
936         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
937             != HUNTED_ISME)
938           return 0;
939       }
940       else
941       {
942         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
943             != HUNTED_ISME)
944           return 0;
945       }
946       break;
947     }
948
949       /* oper only, varying # of params */
950     case 'l':
951     case 'L':
952     case 'M':
953     {
954       if (parc == 4)
955       {
956         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
957             != HUNTED_ISME)
958           return 0;
959       }
960       else if (parc > 4)
961       {
962         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc,
963             parv) != HUNTED_ISME)
964           return 0;
965       }
966       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
967           != HUNTED_ISME)
968         return 0;
969       break;
970     }
971
972       /* oper only, standard # of params */
973     default:
974     {
975       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
976           != HUNTED_ISME)
977         return 0;
978       break;
979     }
980   }
981
982   switch (stat)
983   {
984     case 'L':
985     case 'l':
986     {
987       int doall = 0, wilds = 0;
988       char *name = "*";
989       if (parc > 3 && *parv[3])
990       {
991         char *p;
992         name = parv[3];
993         wilds = (*name == '*' || *name == '?');
994         for (p = name + 1; *p; ++p)
995           if ((*p == '*' || *p == '?') && p[-1] != '\\')
996           {
997             wilds = 1;
998             break;
999           }
1000       }
1001       else
1002         doall = 1;
1003       /*
1004        * Send info about connections which match, or all if the
1005        * mask matches me.name.  Only restrictions are on those who
1006        * are invisible not being visible to 'foreigners' who use
1007        * a wild card based search to list it.
1008        */
1009       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
1010       for (i = 0; i <= HighestFd; i++)
1011       {
1012         if (!(acptr = LocalClientArray[i]))
1013           continue;
1014         /* Don't return clients when this is a request for `all' */
1015         if (doall && IsUser(acptr))
1016           continue;
1017         /* Don't show invisible people to unauthorized people when using
1018          * wildcards  -- Is this still needed now /stats is oper only ? */
1019         if (IsInvisible(acptr) && (doall || wilds) &&
1020             !(MyConnect(sptr) && IsOper(sptr)) &&
1021             !IsAnOper(acptr) && (acptr != sptr))
1022           continue;
1023         /* Only show the ones that match the given mask - if any */
1024         if (!doall && wilds && match(name, acptr->name))
1025           continue;
1026         /* Skip all that do not match the specific query */
1027         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
1028           continue;
1029         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
1030             acptr->name, (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
1031             (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
1032             CurrentTime - acptr->firsttime);
1033       }
1034       break;
1035     }
1036     case 'C':
1037     case 'c':
1038       report_configured_links(sptr, CONF_SERVER);
1039       break;
1040     case 'G':
1041     case 'g': /* send glines */
1042       gline_remove_expired(TStime());
1043       for (gline = GlobalGlineList; gline; gline = gline->next) {
1044         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
1045                    sptr->name, 'G', gline->name, gline->host,
1046                    gline->expire, gline->reason);
1047       }
1048       break;
1049     case 'H':
1050     case 'h':
1051       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
1052       break;
1053     case 'I':
1054     case 'i':
1055     case 'K':
1056     case 'k':                   /* display CONF_IPKILL as well
1057                                    as CONF_KILL -Kev */
1058     {
1059       int wilds, count;
1060       char *user, *host, *p;
1061       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
1062       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
1063       {
1064         report_configured_links(sptr, conf_status);
1065         break;
1066       }
1067       if (parc < 4 || *parv[3] == '\0')
1068         return need_more_params(sptr,
1069                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
1070
1071       wilds = 0;
1072       for (p = parv[3]; *p; p++)
1073       {
1074         if (*p == '\\')
1075         {
1076           if (!*++p)
1077             break;
1078           continue;
1079         }
1080         if (*p == '?' || *p == '*')
1081         {
1082           wilds = 1;
1083           break;
1084         }
1085       }
1086       if (!(MyConnect(sptr) || IsOper(sptr)))
1087       {
1088         wilds = 0;
1089         count = 3;
1090       }
1091       else
1092         count = 1000;
1093
1094       if (conf_status == CONF_CLIENT)
1095       {
1096         user = 0;            /* Not used, but to avoid compiler warning. */
1097
1098         host = parv[3];
1099       }
1100       else
1101       {
1102         if ((host = strchr(parv[3], '@')))
1103         {
1104           user = parv[3];
1105           *host++ = 0;;
1106         }
1107         else
1108         {
1109           user = 0;
1110           host = parv[3];
1111         }
1112       }
1113       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
1114       {
1115         if ((aconf->status & conf_status))
1116         {
1117           if (conf_status == CONF_KLINE)
1118           {
1119             if ((!wilds && ((user || aconf->host[1]) &&
1120                 !match(aconf->host, host) &&
1121                 (!user || !match(aconf->name, user)))) ||
1122                 (wilds && !mmatch(host, aconf->host) &&
1123                 (!user || !mmatch(user, aconf->name))))
1124             {
1125               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
1126                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
1127                   aconf->port, get_conf_class(aconf));
1128               if (--count == 0)
1129                 break;
1130             }
1131           }
1132           else if (conf_status == CONF_CLIENT)
1133           {
1134             if ((!wilds && (!match(aconf->host, host) ||
1135                 !match(aconf->name, host))) ||
1136                 (wilds && (!mmatch(host, aconf->host) ||
1137                 !mmatch(host, aconf->name))))
1138             {
1139               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
1140                   sptr->name, 'I', aconf->host, aconf->name,
1141                   aconf->port, get_conf_class(aconf));
1142               if (--count == 0)
1143                 break;
1144             }
1145           }
1146         }
1147       }
1148       break;
1149     }
1150     case 'M':
1151 #if !defined(NDEBUG)
1152       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
1153           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
1154 #endif
1155
1156 #if 0
1157 #ifdef MEMSIZESTATS
1158       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
1159           me.name, parv[0], get_mem_size(), get_alloc_cnt());
1160 #endif
1161 #ifdef MEMLEAKSTATS
1162       report_memleak_stats(sptr, parc, parv);
1163 #endif
1164 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
1165       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
1166           "is not enabled on this server", me.name, parv[0]);
1167 #endif
1168 #endif /* 0 */
1169       break;
1170     case 'm':
1171       for (mptr = msgtab; mptr->cmd; mptr++)
1172         if (mptr->count)
1173           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
1174               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
1175       break;
1176     case 'o':
1177     case 'O':
1178       report_configured_links(sptr, CONF_OPS);
1179       break;
1180     case 'p':
1181     case 'P':
1182       /*
1183        * show listener ports
1184        * show hidden ports to opers, if there are more than 3 parameters,
1185        * interpret the fourth parameter as the port number, limit non-local
1186        * or non-oper results to 8 ports.
1187        */ 
1188       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
1189                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
1190       break;
1191     case 'R':
1192     case 'r':
1193 #ifdef DEBUGMODE
1194       send_usage(sptr, parv[0]);
1195 #endif
1196       break;
1197     case 'D':
1198       report_configured_links(sptr, CONF_CRULEALL);
1199       break;
1200     case 'd':
1201       report_configured_links(sptr, CONF_CRULE);
1202       break;
1203     case 't':
1204       tstats(sptr, parv[0]);
1205       break;
1206     case 'T':
1207       report_configured_links(sptr, CONF_TLINES);
1208       break;
1209     case 'U':
1210       report_configured_links(sptr, CONF_UWORLD);
1211       break;
1212     case 'u':
1213     {
1214       time_t nowr;
1215
1216       nowr = CurrentTime - me.since;
1217       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
1218           nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
1219       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
1220           max_connection_count, max_client_count);
1221       break;
1222     }
1223     case 'W':
1224     case 'w':
1225       calc_load(sptr);
1226       break;
1227     case 'X':
1228     case 'x':
1229 #ifdef  DEBUGMODE
1230       send_listinfo(sptr, parv[0]);
1231 #endif
1232       break;
1233     case 'Y':
1234     case 'y':
1235       report_classes(sptr);
1236       break;
1237     case 'Z':
1238     case 'z':
1239       count_memory(sptr, parv[0]);
1240       break;
1241     default:
1242       stat = '*';
1243       break;
1244   }
1245   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
1246   return 0;
1247 }
1248
1249 #if 0
1250 /*
1251  * m_stats
1252  *
1253  *    parv[0] = sender prefix
1254  *    parv[1] = statistics selector (defaults to Message frequency)
1255  *    parv[2] = target server (current server defaulted, if omitted)
1256  * And 'stats l' and 'stats' L:
1257  *    parv[3] = server mask ("*" defaulted, if omitted)
1258  * Or for stats p,P:
1259  *    parv[3] = port mask (returns p-lines when its port is matched by this)
1260  * Or for stats k,K,i and I:
1261  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
1262  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
1263  *              (defaults to old reply if ommitted, when local or Oper)
1264  *              A remote mask (something containing wildcards) is only
1265  *              allowed for IRC Operators.
1266  * Or for stats M:
1267  *    parv[3] = time param
1268  *    parv[4] = time param 
1269  *    (see report_memleak_stats() in runmalloc.c for details)
1270  *
1271  * This function is getting really ugly. -Ghostwolf
1272  */
1273 int m_stats(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1274 {
1275   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
1276       "RcveM RcveKBytes :Open since";
1277   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
1278   struct Message *mptr;
1279   struct Client *acptr;
1280   struct Gline* gline;
1281   struct ConfItem *aconf;
1282   char stat = parc > 1 ? parv[1][0] : '\0';
1283   int i;
1284
1285 /* m_stats is so obnoxiously full of special cases that the different
1286  * hunt_server() possiblites were becoming very messy. It now uses a
1287  * switch() so as to be easier to read and update as params change. 
1288  * -Ghostwolf 
1289  */
1290   switch (stat)
1291   {
1292       /* open to all, standard # of params */
1293     case 'U':
1294     case 'u':
1295     {
1296       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1297           != HUNTED_ISME)
1298         return 0;
1299       break;
1300     }
1301
1302       /* open to all, varying # of params */
1303     case 'k':
1304     case 'K':
1305     case 'i':
1306     case 'I':
1307     case 'p':
1308     case 'P':
1309     {
1310       if (parc > 3)
1311       {
1312         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
1313             != HUNTED_ISME)
1314           return 0;
1315       }
1316       else
1317       {
1318         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1319             != HUNTED_ISME)
1320           return 0;
1321       }
1322       break;
1323     }
1324
1325       /* oper only, varying # of params */
1326     case 'l':
1327     case 'L':
1328     case 'M':
1329     {
1330       if (parc == 4)
1331       {
1332         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
1333             != HUNTED_ISME)
1334           return 0;
1335       }
1336       else if (parc > 4)
1337       {
1338         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc,
1339             parv) != HUNTED_ISME)
1340           return 0;
1341       }
1342       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1343           != HUNTED_ISME)
1344         return 0;
1345       break;
1346     }
1347
1348       /* oper only, standard # of params */
1349     default:
1350     {
1351       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1352           != HUNTED_ISME)
1353         return 0;
1354       break;
1355     }
1356   }
1357
1358   switch (stat)
1359   {
1360     case 'L':
1361     case 'l':
1362     {
1363       int doall = 0, wilds = 0;
1364       char *name = "*";
1365       if (parc > 3 && *parv[3])
1366       {
1367         char *p;
1368         name = parv[3];
1369         wilds = (*name == '*' || *name == '?');
1370         for (p = name + 1; *p; ++p)
1371           if ((*p == '*' || *p == '?') && p[-1] != '\\')
1372           {
1373             wilds = 1;
1374             break;
1375           }
1376       }
1377       else
1378         doall = 1;
1379       /*
1380        * Send info about connections which match, or all if the
1381        * mask matches me.name.  Only restrictions are on those who
1382        * are invisible not being visible to 'foreigners' who use
1383        * a wild card based search to list it.
1384        */
1385       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
1386       for (i = 0; i <= HighestFd; i++)
1387       {
1388         if (!(acptr = LocalClientArray[i]))
1389           continue;
1390         /* Don't return clients when this is a request for `all' */
1391         if (doall && IsUser(acptr))
1392           continue;
1393         /* Don't show invisible people to unauthorized people when using
1394          * wildcards  -- Is this still needed now /stats is oper only ? */
1395         if (IsInvisible(acptr) && (doall || wilds) &&
1396             !(MyConnect(sptr) && IsOper(sptr)) &&
1397             !IsAnOper(acptr) && (acptr != sptr))
1398           continue;
1399         /* Only show the ones that match the given mask - if any */
1400         if (!doall && wilds && match(name, acptr->name))
1401           continue;
1402         /* Skip all that do not match the specific query */
1403         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
1404           continue;
1405         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
1406                    acptr->name,
1407                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
1408                    (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
1409                    CurrentTime - acptr->firsttime);
1410       }
1411       break;
1412     }
1413     case 'C':
1414     case 'c':
1415       report_configured_links(sptr, CONF_SERVER);
1416       break;
1417     case 'G':
1418     case 'g': /* send glines */
1419       gline_remove_expired(TStime());
1420       for (gline = GlobalGlineList; gline; gline = gline->next) {
1421         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
1422                    sptr->name, 'G', gline->name, gline->host,
1423                    gline->expire, gline->reason);
1424       }
1425       break;
1426     case 'H':
1427     case 'h':
1428       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
1429       break;
1430     case 'I':
1431     case 'i':
1432     case 'K':
1433     case 'k':                   /* display CONF_IPKILL as well
1434                                    as CONF_KILL -Kev */
1435     {
1436       int wilds, count;
1437       char *user, *host, *p;
1438       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
1439       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
1440       {
1441         report_configured_links(sptr, conf_status);
1442         break;
1443       }
1444       if (parc < 4 || *parv[3] == '\0')
1445         return need_more_params(sptr,
1446                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
1447
1448       wilds = 0;
1449       for (p = parv[3]; *p; p++)
1450       {
1451         if (*p == '\\')
1452         {
1453           if (!*++p)
1454             break;
1455           continue;
1456         }
1457         if (*p == '?' || *p == '*')
1458         {
1459           wilds = 1;
1460           break;
1461         }
1462       }
1463       if (!(MyConnect(sptr) || IsOper(sptr)))
1464       {
1465         wilds = 0;
1466         count = 3;
1467       }
1468       else
1469         count = 1000;
1470
1471       if (conf_status == CONF_CLIENT)
1472       {
1473         user = 0;            /* Not used, but to avoid compiler warning. */
1474
1475         host = parv[3];
1476       }
1477       else
1478       {
1479         if ((host = strchr(parv[3], '@')))
1480         {
1481           user = parv[3];
1482           *host++ = 0;;
1483         }
1484         else
1485         {
1486           user = 0;
1487           host = parv[3];
1488         }
1489       }
1490       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
1491       {
1492         if ((aconf->status & conf_status))
1493         {
1494           if (conf_status == CONF_KLINE)
1495           {
1496             if ((!wilds && ((user || aconf->host[1]) &&
1497                 !match(aconf->host, host) &&
1498                 (!user || !match(aconf->name, user)))) ||
1499                 (wilds && !mmatch(host, aconf->host) &&
1500                 (!user || !mmatch(user, aconf->name))))
1501             {
1502               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
1503                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
1504                   aconf->port, get_conf_class(aconf));
1505               if (--count == 0)
1506                 break;
1507             }
1508           }
1509           else if (conf_status == CONF_CLIENT)
1510           {
1511             if ((!wilds && (!match(aconf->host, host) ||
1512                 !match(aconf->name, host))) ||
1513                 (wilds && (!mmatch(host, aconf->host) ||
1514                 !mmatch(host, aconf->name))))
1515             {
1516               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
1517                   sptr->name, 'I', aconf->host, aconf->name,
1518                   aconf->port, get_conf_class(aconf));
1519               if (--count == 0)
1520                 break;
1521             }
1522           }
1523         }
1524       }
1525       break;
1526     }
1527     case 'M':
1528 #if !defined(NDEBUG)
1529       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
1530           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
1531 #endif
1532
1533 #if 0
1534 #ifdef MEMSIZESTATS
1535       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
1536           me.name, parv[0], get_mem_size(), get_alloc_cnt());
1537 #endif
1538 #ifdef MEMLEAKSTATS
1539       report_memleak_stats(sptr, parc, parv);
1540 #endif
1541 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
1542       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
1543           "is not enabled on this server", me.name, parv[0]);
1544 #endif
1545 #endif /* 0 */
1546       break;
1547     case 'm':
1548       for (mptr = msgtab; mptr->cmd; mptr++)
1549         if (mptr->count)
1550           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
1551               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
1552       break;
1553     case 'o':
1554     case 'O':
1555       report_configured_links(sptr, CONF_OPS);
1556       break;
1557     case 'p':
1558     case 'P':
1559       /*
1560        * show listener ports
1561        * show hidden ports to opers, if there are more than 3 parameters,
1562        * interpret the fourth parameter as the port number, limit non-local
1563        * or non-oper results to 8 ports.
1564        */ 
1565       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
1566                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
1567       break;
1568     case 'R':
1569     case 'r':
1570 #ifdef DEBUGMODE
1571       send_usage(sptr, parv[0]);
1572 #endif
1573       break;
1574     case 'D':
1575       report_configured_links(sptr, CONF_CRULEALL);
1576       break;
1577     case 'd':
1578       report_configured_links(sptr, CONF_CRULE);
1579       break;
1580     case 't':
1581       tstats(sptr, parv[0]);
1582       break;
1583     case 'T':
1584       report_configured_links(sptr, CONF_TLINES);
1585       break;
1586     case 'U':
1587       report_configured_links(sptr, CONF_UWORLD);
1588       break;
1589     case 'u':
1590     {
1591       time_t nowr;
1592
1593       nowr = CurrentTime - me.since;
1594       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
1595                  nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
1596       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
1597                  max_connection_count, max_client_count);
1598       break;
1599     }
1600     case 'W':
1601     case 'w':
1602       calc_load(sptr);
1603       break;
1604     case 'X':
1605     case 'x':
1606 #ifdef  DEBUGMODE
1607       send_listinfo(sptr, parv[0]);
1608 #endif
1609       break;
1610     case 'Y':
1611     case 'y':
1612       report_classes(sptr);
1613       break;
1614     case 'Z':
1615     case 'z':
1616       count_memory(sptr, parv[0]);
1617       break;
1618     default:
1619       stat = '*';
1620       break;
1621   }
1622   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
1623   return 0;
1624 }
1625 #endif /* 0 */