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