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