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