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 'v':
386     {
387         struct ConfClass *cltmp;
388         
389         for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp)) {
390                 if (Links(cltmp) > 0)
391                         send_reply(sptr,RPL_TRACECLASS, ConClass(cltmp), Links(cltmp));
392         }
393         break;
394     }
395     case 'W':
396     case 'w':
397       calc_load(sptr);
398       break;
399     case 'X':
400     case 'x':
401 #ifdef  DEBUGMODE
402       send_listinfo(sptr, parv[0]);
403 #endif
404       break;
405     case 'Y':
406     case 'y':
407       report_classes(sptr);
408       break;
409     case 'Z':
410     case 'z':
411       count_memory(sptr, parv[0]);
412       break;
413     default:
414       stat = '*';
415       while (*infotext)
416         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
417       break;
418   }
419   send_reply(sptr, RPL_ENDOFSTATS, stat);
420   return 0;
421 }
422
423 /*
424  * ms_stats - server message handler
425  *
426  *    parv[0] = sender prefix
427  *    parv[1] = statistics selector (defaults to Message frequency)
428  *    parv[2] = target server (current server defaulted, if omitted)
429  * And 'stats l' and 'stats' L:
430  *    parv[3] = server mask ("*" defaulted, if omitted)
431  * Or for stats p,P:
432  *    parv[3] = port mask (returns p-lines when its port is matched by this)
433  * Or for stats k,K,i and I:
434  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
435  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
436  *              (defaults to old reply if ommitted, when local or Oper)
437  *              A remote mask (something containing wildcards) is only
438  *              allowed for IRC Operators.
439  * Or for stats M:
440  *    parv[3] = time param
441  *    parv[4] = time param 
442  *    (see report_memleak_stats() in runmalloc.c for details)
443  *
444  * This function is getting really ugly. -Ghostwolf
445  */
446 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
447 {
448   struct Message *mptr;
449   struct Client *acptr;
450   struct ConfItem *aconf;
451   char stat = parc > 1 ? parv[1][0] : '\0';
452   int i;
453
454   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
455     return 0;
456
457   switch (stat)
458   {
459     case 'L':
460     case 'l':
461     {
462       int doall = 0, wilds = 0;
463       char *name = "*";
464       if (parc > 3 && *parv[3])
465       {
466         char *p;
467         name = parv[3];
468         wilds = (*name == '*' || *name == '?');
469         for (p = name + 1; *p; ++p)
470           if ((*p == '*' || *p == '?') && p[-1] != '\\')
471           {
472             wilds = 1;
473             break;
474           }
475       }
476       else
477         doall = 1;
478       /*
479        * Send info about connections which match, or all if the
480        * mask matches me.name.  Only restrictions are on those who
481        * are invisible not being visible to 'foreigners' who use
482        * a wild card based search to list it.
483        */
484       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
485                  "SendM SendKBytes RcveM RcveKBytes :Open since");
486       for (i = 0; i <= HighestFd; i++)
487       {
488         if (!(acptr = LocalClientArray[i]))
489           continue;
490         /* Don't return clients when this is a request for `all' */
491         if (doall && IsUser(acptr))
492           continue;
493         /* Don't show invisible people to unauthorized people when using
494          * wildcards  -- Is this still needed now /stats is oper only ? 
495          * Not here, because ms_stats is specifically a remote command, 
496          * thus the check was removed. -Ghostwolf */
497         /* Only show the ones that match the given mask - if any */
498         if (!doall && wilds && match(name, acptr->name))
499           continue;
500         /* Skip all that do not match the specific query */
501         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
502           continue;
503         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
504                    "%s %u %u %u %u %u :%Tu", acptr->name,
505                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
506                    (int)acptr->sendK, (int)acptr->receiveM,
507                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
508       }
509       break;
510     }
511     case 'C':
512     case 'c':
513       report_configured_links(sptr, CONF_SERVER);
514       break;
515     case 'G':
516     case 'g': /* send glines */
517       gline_stats(sptr);
518       break;
519     case 'H':
520     case 'h':
521       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
522       break;
523     case 'I':
524     case 'i':
525     case 'K':
526     case 'k':                   /* display CONF_IPKILL as well
527                                    as CONF_KILL -Kev */
528     {
529       int wilds, count;
530       char *user, *host, *p;
531       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
532       if (IsOper(sptr) && parc < 4)
533       {
534         report_configured_links(sptr, conf_status);
535         break;
536       }
537       if (parc < 4 || *parv[3] == '\0')
538         return need_more_params(sptr,
539                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
540
541       wilds = 0;
542       for (p = parv[3]; *p; p++)
543       {
544         if (*p == '\\')
545         {
546           if (!*++p)
547             break;
548           continue;
549         }
550         if (*p == '?' || *p == '*')
551         {
552           wilds = 1;
553           break;
554         }
555       }
556       if (!IsOper(sptr))
557       {
558         wilds = 0;
559         count = 3;
560       }
561       else
562         count = 1000;
563
564       if (conf_status == CONF_CLIENT)
565       {
566         user = 0;            /* Not used, but to avoid compiler warning. */
567
568         host = parv[3];
569       }
570       else
571       {
572         if ((host = strchr(parv[3], '@')))
573         {
574           user = parv[3];
575           *host++ = 0;;
576         }
577         else
578         {
579           user = 0;
580           host = parv[3];
581         }
582       }
583       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
584       {
585         if ((aconf->status & conf_status))
586         {
587           if (conf_status == CONF_KLINE)
588           {
589             if ((!wilds && ((user || aconf->host[1]) &&
590                 !match(aconf->host, host) &&
591                 (!user || !match(aconf->name, user)))) ||
592                 (wilds && !mmatch(host, aconf->host) &&
593                 (!user || !mmatch(user, aconf->name))))
594             {
595               send_reply(sptr, RPL_STATSKLINE,
596                          (aconf->status & CONF_KILL) ? 'K' : 'k', aconf->host,
597                          aconf->passwd, aconf->name, aconf->port,
598                          get_conf_class(aconf));
599               if (--count == 0)
600                 break;
601             }
602           }
603           else if (conf_status == CONF_CLIENT)
604           {
605             if ((!wilds && (!match(aconf->host, host) ||
606                 !match(aconf->name, host))) ||
607                 (wilds && (!mmatch(host, aconf->host) ||
608                 !mmatch(host, aconf->name))))
609             {
610               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
611                          aconf->port, get_conf_class(aconf));
612               if (--count == 0)
613                 break;
614             }
615           }
616         }
617       }
618       break;
619     }
620     case 'M':
621 #if !defined(NDEBUG)
622       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
623                  fda_get_block_count());
624 #endif
625
626 #if 0
627 #ifdef MEMSIZESTATS
628       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
629           me.name, parv[0], get_mem_size(), get_alloc_cnt());
630 #endif
631 #ifdef MEMLEAKSTATS
632       report_memleak_stats(sptr, parc, parv);
633 #endif
634 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
635       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
636           "is not enabled on this server", me.name, parv[0]);
637 #endif
638 #endif /* 0 */
639       break;
640     case 'm':
641       for (mptr = msgtab; mptr->cmd; mptr++)
642         if (mptr->count)
643           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
644                      mptr->bytes);
645       break;
646     case 'o':
647     case 'O':
648       report_configured_links(sptr, CONF_OPS);
649       break;
650     case 'p':
651     case 'P':
652       /*
653        * show listener ports
654        * show hidden ports to opers, if there are more than 3 parameters,
655        * interpret the fourth parameter as the port number, limit non-local
656        * or non-oper results to 8 ports.
657        */ 
658       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
659       break;
660     case 'R':
661     case 'r':
662 #ifdef DEBUGMODE
663       send_usage(sptr, parv[0]);
664 #endif
665       break;
666     case 'D':
667       report_configured_links(sptr, CONF_CRULEALL);
668       break;
669     case 'd':
670       report_configured_links(sptr, CONF_CRULE);
671       break;
672     case 't':
673       tstats(sptr, parv[0]);
674       break;
675     case 'T':
676       report_configured_links(sptr, CONF_TLINES);
677       break;
678     case 'U':
679       report_configured_links(sptr, CONF_UWORLD);
680       break;
681     case 'u':
682     {
683       time_t nowr;
684
685       nowr = CurrentTime - me.since;
686       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
687                  (nowr / 60) % 60, nowr % 60);
688       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
689       break;
690     }
691     case 'v':
692     {
693         struct ConfClass *cltmp;
694         
695         for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp)) {
696                 if (Links(cltmp) > 0)
697                         send_reply(sptr,RPL_TRACECLASS, ConClass(cltmp), Links(cltmp));
698         }
699         break;
700     }
701     
702     case 'W':
703     case 'w':
704       calc_load(sptr);
705       break;
706     case 'X':
707     case 'x':
708 #ifdef  DEBUGMODE
709       send_listinfo(sptr, parv[0]);
710 #endif
711       break;
712     case 'Y':
713     case 'y':
714       report_classes(sptr);
715       break;
716     case 'Z':
717     case 'z':
718       count_memory(sptr, parv[0]);
719       break;
720     default:
721       stat = '*';
722       break;
723   }
724   send_reply(sptr, RPL_ENDOFSTATS, stat);
725   return 0;
726 }
727
728 /*
729  * mo_stats - oper message handler
730  *
731  *    parv[0] = sender prefix
732  *    parv[1] = statistics selector (defaults to Message frequency)
733  *    parv[2] = target server (current server defaulted, if omitted)
734  * And 'stats l' and 'stats' L:
735  *    parv[3] = server mask ("*" defaulted, if omitted)
736  * Or for stats p,P:
737  *    parv[3] = port mask (returns p-lines when its port is matched by this)
738  * Or for stats k,K,i and I:
739  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
740  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
741  *              (defaults to old reply if ommitted, when local or Oper)
742  *              A remote mask (something containing wildcards) is only
743  *              allowed for IRC Operators.
744  * Or for stats M:
745  *    parv[3] = time param
746  *    parv[4] = time param 
747  *    (see report_memleak_stats() in runmalloc.c for details)
748  *
749  * This function is getting really ugly. -Ghostwolf
750  */
751 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
752 {
753   struct Message *mptr;
754   struct Client *acptr;
755   struct ConfItem *aconf;
756   char stat = parc > 1 ? parv[1][0] : '\0';
757   const char **infotext = statsinfo;
758   int i;
759
760   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
761     return 0;
762
763   switch (stat)
764   {
765     case 'L':
766     case 'l':
767     {
768       int doall = 0, wilds = 0;
769       char *name = "*";
770       if (parc > 3 && *parv[3])
771       {
772         char *p;
773         name = parv[3];
774         wilds = (*name == '*' || *name == '?');
775         for (p = name + 1; *p; ++p)
776           if ((*p == '*' || *p == '?') && p[-1] != '\\')
777           {
778             wilds = 1;
779             break;
780           }
781       }
782       else
783         doall = 1;
784       /*
785        * Send info about connections which match, or all if the
786        * mask matches me.name.  Only restrictions are on those who
787        * are invisible not being visible to 'foreigners' who use
788        * a wild card based search to list it.
789        */
790       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
791                  "SendM SendKBytes RcveM RcveKBytes :Open since");
792       for (i = 0; i <= HighestFd; i++)
793       {
794         if (!(acptr = LocalClientArray[i]))
795           continue;
796         /* Don't return clients when this is a request for `all' */
797         if (doall && IsUser(acptr))
798           continue;
799         /* Only show the ones that match the given mask - if any */
800         if (!doall && wilds && match(name, acptr->name))
801           continue;
802         /* Skip all that do not match the specific query */
803         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
804           continue;
805         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
806                    "%s %u %u %u %u %u :%Tu", acptr->name,
807                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
808                    (int)acptr->sendK, (int)acptr->receiveM,
809                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
810       }
811       break;
812     }
813     case 'C':
814     case 'c':
815       report_configured_links(sptr, CONF_SERVER);
816       break;
817     case 'G':
818     case 'g': /* send glines */
819       gline_stats(sptr);
820       break;
821     case 'H':
822     case 'h':
823       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
824       break;
825     case 'I':
826     case 'i':
827     case 'K':
828     case 'k':                   /* display CONF_IPKILL as well
829                                    as CONF_KILL -Kev */
830     {
831       int wilds, count;
832       char *user, *host, *p;
833       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
834       if (parc < 4)
835       {
836         report_configured_links(sptr, conf_status);
837         break;
838       }
839
840       wilds = 0;
841       for (p = parv[3]; *p; p++)
842       {
843         if (*p == '\\')
844         {
845           if (!*++p)
846             break;
847           continue;
848         }
849         if (*p == '?' || *p == '*')
850         {
851           wilds = 1;
852           break;
853         }
854       }
855
856       count = 1000;
857
858       if (conf_status == CONF_CLIENT)
859       {
860         user = 0;            /* Not used, but to avoid compiler warning. */
861
862         host = parv[3];
863       }
864       else
865       {
866         if ((host = strchr(parv[3], '@')))
867         {
868           user = parv[3];
869           *host++ = 0;;
870         }
871         else
872         {
873           user = 0;
874           host = parv[3];
875         }
876       }
877       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
878       {
879         if ((aconf->status & conf_status))
880         {
881           if (conf_status == CONF_KLINE)
882           {
883             if ((!wilds && ((user || aconf->host[1]) &&
884                 !match(aconf->host, host) &&
885                 (!user || !match(aconf->name, user)))) ||
886                 (wilds && !mmatch(host, aconf->host) &&
887                 (!user || !mmatch(user, aconf->name))))
888             {
889               send_reply(sptr, RPL_STATSKLINE,
890                          (aconf->status & CONF_KILL) ? 'K' : 'k', aconf->host,
891                          aconf->passwd, aconf->name, aconf->port,
892                          get_conf_class(aconf));
893               if (--count == 0)
894                 break;
895             }
896           }
897           else if (conf_status == CONF_CLIENT)
898           {
899             if ((!wilds && (!match(aconf->host, host) ||
900                 !match(aconf->name, host))) ||
901                 (wilds && (!mmatch(host, aconf->host) ||
902                 !mmatch(host, aconf->name))))
903             {
904               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
905                          aconf->port, get_conf_class(aconf));
906               if (--count == 0)
907                 break;
908             }
909           }
910         }
911       }
912       break;
913     }
914     case 'M':
915 #if !defined(NDEBUG)
916       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
917                  fda_get_block_count());
918 #endif
919
920 #if 0
921 #ifdef MEMSIZESTATS
922       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
923           me.name, parv[0], get_mem_size(), get_alloc_cnt());
924 #endif
925 #ifdef MEMLEAKSTATS
926       report_memleak_stats(sptr, parc, parv);
927 #endif
928 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
929       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
930           "is not enabled on this server", me.name, parv[0]);
931 #endif
932 #endif /* 0 */
933       break;
934     case 'm':
935       for (mptr = msgtab; mptr->cmd; mptr++)
936         if (mptr->count)
937           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
938                      mptr->bytes);
939       break;
940     case 'o':
941     case 'O':
942       report_configured_links(sptr, CONF_OPS);
943       break;
944     case 'p':
945     case 'P':
946       /*
947        * show listener ports
948        * show hidden ports to opers, if there are more than 3 parameters,
949        * interpret the fourth parameter as the port number, limit non-local
950        * or non-oper results to 8 ports.
951        */ 
952       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
953       break;
954     case 'R':
955     case 'r':
956 #ifdef DEBUGMODE
957       send_usage(sptr, parv[0]);
958 #endif
959       break;
960     case 'D':
961       report_configured_links(sptr, CONF_CRULEALL);
962       break;
963     case 'd':
964       report_configured_links(sptr, CONF_CRULE);
965       break;
966     case 't':
967       tstats(sptr, parv[0]);
968       break;
969     case 'T':
970       report_configured_links(sptr, CONF_TLINES);
971       break;
972     case 'U':
973       report_configured_links(sptr, CONF_UWORLD);
974       break;
975     case 'u':
976     {
977       time_t nowr;
978
979       nowr = CurrentTime - me.since;
980       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
981                  (nowr / 60) % 60, nowr % 60);
982       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
983       break;
984     }
985     case 'W':
986     case 'w':
987       calc_load(sptr);
988       break;
989     case 'X':
990     case 'x':
991 #ifdef  DEBUGMODE
992       send_listinfo(sptr, parv[0]);
993 #endif
994       break;
995     case 'Y':
996     case 'y':
997       report_classes(sptr);
998       break;
999     case 'Z':
1000     case 'z':
1001       count_memory(sptr, parv[0]);
1002       break;
1003     default:
1004       stat = '*';
1005       while (*infotext)
1006         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
1007       break;
1008   }
1009   send_reply(sptr, RPL_ENDOFSTATS, stat);
1010   return 0;
1011 }
1012
1013 #if 0
1014 /*
1015  * m_stats
1016  *
1017  *    parv[0] = sender prefix
1018  *    parv[1] = statistics selector (defaults to Message frequency)
1019  *    parv[2] = target server (current server defaulted, if omitted)
1020  * And 'stats l' and 'stats' L:
1021  *    parv[3] = server mask ("*" defaulted, if omitted)
1022  * Or for stats p,P:
1023  *    parv[3] = port mask (returns p-lines when its port is matched by this)
1024  * Or for stats k,K,i and I:
1025  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
1026  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
1027  *              (defaults to old reply if ommitted, when local or Oper)
1028  *              A remote mask (something containing wildcards) is only
1029  *              allowed for IRC Operators.
1030  * Or for stats M:
1031  *    parv[3] = time param
1032  *    parv[4] = time param 
1033  *    (see report_memleak_stats() in runmalloc.c for details)
1034  *
1035  * This function is getting really ugly. -Ghostwolf
1036  */
1037 int m_stats(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1038 {
1039   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
1040       "RcveM RcveKBytes :Open since";
1041   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
1042   struct Message *mptr;
1043   struct Client *acptr;
1044   struct Gline* gline;
1045   struct ConfItem *aconf;
1046   char stat = parc > 1 ? parv[1][0] : '\0';
1047   int i;
1048
1049 /* m_stats is so obnoxiously full of special cases that the different
1050  * hunt_server() possiblites were becoming very messy. It now uses a
1051  * switch() so as to be easier to read and update as params change. 
1052  * -Ghostwolf 
1053  */
1054   switch (stat)
1055   {
1056       /* open to all, standard # of params */
1057     case 'U':
1058     case 'u':
1059     {
1060       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1061           != HUNTED_ISME)
1062         return 0;
1063       break;
1064     }
1065
1066       /* open to all, varying # of params */
1067     case 'k':
1068     case 'K':
1069     case 'i':
1070     case 'I':
1071     case 'p':
1072     case 'P':
1073     {
1074       if (parc > 3)
1075       {
1076         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv) /* XXX DEAD */
1077             != HUNTED_ISME)
1078           return 0;
1079       }
1080       else
1081       {
1082         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1083             != HUNTED_ISME)
1084           return 0;
1085       }
1086       break;
1087     }
1088
1089       /* oper only, varying # of params */
1090     case 'l':
1091     case 'L':
1092     case 'M':
1093     {
1094       if (parc == 4)
1095       {
1096         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv) /* XXX DEAD */
1097             != HUNTED_ISME)
1098           return 0;
1099       }
1100       else if (parc > 4)
1101       {
1102         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc, /* XXX DEAD */
1103             parv) != HUNTED_ISME)
1104           return 0;
1105       }
1106       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1107           != HUNTED_ISME)
1108         return 0;
1109       break;
1110     }
1111
1112       /* oper only, standard # of params */
1113     default:
1114     {
1115       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1116           != HUNTED_ISME)
1117         return 0;
1118       break;
1119     }
1120   }
1121
1122   switch (stat)
1123   {
1124     case 'L':
1125     case 'l':
1126     {
1127       int doall = 0, wilds = 0;
1128       char *name = "*";
1129       if (parc > 3 && *parv[3])
1130       {
1131         char *p;
1132         name = parv[3];
1133         wilds = (*name == '*' || *name == '?');
1134         for (p = name + 1; *p; ++p)
1135           if ((*p == '*' || *p == '?') && p[-1] != '\\')
1136           {
1137             wilds = 1;
1138             break;
1139           }
1140       }
1141       else
1142         doall = 1;
1143       /*
1144        * Send info about connections which match, or all if the
1145        * mask matches me.name.  Only restrictions are on those who
1146        * are invisible not being visible to 'foreigners' who use
1147        * a wild card based search to list it.
1148        */
1149       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]); /* XXX DEAD */
1150       for (i = 0; i <= HighestFd; i++)
1151       {
1152         if (!(acptr = LocalClientArray[i]))
1153           continue;
1154         /* Don't return clients when this is a request for `all' */
1155         if (doall && IsUser(acptr))
1156           continue;
1157         /* Don't show invisible people to unauthorized people when using
1158          * wildcards  -- Is this still needed now /stats is oper only ? */
1159         if (IsInvisible(acptr) && (doall || wilds) &&
1160             !(MyConnect(sptr) && IsOper(sptr)) &&
1161             !IsAnOper(acptr) && (acptr != sptr))
1162           continue;
1163         /* Only show the ones that match the given mask - if any */
1164         if (!doall && wilds && match(name, acptr->name))
1165           continue;
1166         /* Skip all that do not match the specific query */
1167         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
1168           continue;
1169         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0], /* XXX DEAD */
1170                    acptr->name,
1171                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
1172                    (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
1173                    CurrentTime - acptr->firsttime);
1174       }
1175       break;
1176     }
1177     case 'C':
1178     case 'c':
1179       report_configured_links(sptr, CONF_SERVER);
1180       break;
1181     case 'G':
1182     case 'g': /* send glines */
1183       gline_remove_expired(TStime());
1184       for (gline = GlobalGlineList; gline; gline = gline->next) {
1185         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name, /* XXX DEAD */
1186                    sptr->name, 'G', gline->name, gline->host,
1187                    gline->expire, gline->reason);
1188       }
1189       break;
1190     case 'H':
1191     case 'h':
1192       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
1193       break;
1194     case 'I':
1195     case 'i':
1196     case 'K':
1197     case 'k':                   /* display CONF_IPKILL as well
1198                                    as CONF_KILL -Kev */
1199     {
1200       int wilds, count;
1201       char *user, *host, *p;
1202       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
1203       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
1204       {
1205         report_configured_links(sptr, conf_status);
1206         break;
1207       }
1208       if (parc < 4 || *parv[3] == '\0')
1209         return need_more_params(sptr,
1210                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
1211
1212       wilds = 0;
1213       for (p = parv[3]; *p; p++)
1214       {
1215         if (*p == '\\')
1216         {
1217           if (!*++p)
1218             break;
1219           continue;
1220         }
1221         if (*p == '?' || *p == '*')
1222         {
1223           wilds = 1;
1224           break;
1225         }
1226       }
1227       if (!(MyConnect(sptr) || IsOper(sptr)))
1228       {
1229         wilds = 0;
1230         count = 3;
1231       }
1232       else
1233         count = 1000;
1234
1235       if (conf_status == CONF_CLIENT)
1236       {
1237         user = 0;            /* Not used, but to avoid compiler warning. */
1238
1239         host = parv[3];
1240       }
1241       else
1242       {
1243         if ((host = strchr(parv[3], '@')))
1244         {
1245           user = parv[3];
1246           *host++ = 0;;
1247         }
1248         else
1249         {
1250           user = 0;
1251           host = parv[3];
1252         }
1253       }
1254       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
1255       {
1256         if ((aconf->status & conf_status))
1257         {
1258           if (conf_status == CONF_KLINE)
1259           {
1260             if ((!wilds && ((user || aconf->host[1]) &&
1261                 !match(aconf->host, host) &&
1262                 (!user || !match(aconf->name, user)))) ||
1263                 (wilds && !mmatch(host, aconf->host) &&
1264                 (!user || !mmatch(user, aconf->name))))
1265             {
1266               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name, /* XXX DEAD */
1267                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
1268                   aconf->port, get_conf_class(aconf));
1269               if (--count == 0)
1270                 break;
1271             }
1272           }
1273           else if (conf_status == CONF_CLIENT)
1274           {
1275             if ((!wilds && (!match(aconf->host, host) ||
1276                 !match(aconf->name, host))) ||
1277                 (wilds && (!mmatch(host, aconf->host) ||
1278                 !mmatch(host, aconf->name))))
1279             {
1280               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name, /* XXX DEAD */
1281                   sptr->name, 'I', aconf->host, aconf->name,
1282                   aconf->port, get_conf_class(aconf));
1283               if (--count == 0)
1284                 break;
1285             }
1286           }
1287         }
1288       }
1289       break;
1290     }
1291     case 'M':
1292 #if !defined(NDEBUG)
1293       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
1294           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
1295 #endif
1296
1297 #if 0
1298 #ifdef MEMSIZESTATS
1299       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
1300           me.name, parv[0], get_mem_size(), get_alloc_cnt());
1301 #endif
1302 #ifdef MEMLEAKSTATS
1303       report_memleak_stats(sptr, parc, parv);
1304 #endif
1305 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
1306       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
1307           "is not enabled on this server", me.name, parv[0]);
1308 #endif
1309 #endif /* 0 */
1310       break;
1311     case 'm':
1312       for (mptr = msgtab; mptr->cmd; mptr++)
1313         if (mptr->count)
1314           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS), /* XXX DEAD */
1315               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
1316       break;
1317     case 'o':
1318     case 'O':
1319       report_configured_links(sptr, CONF_OPS);
1320       break;
1321     case 'p':
1322     case 'P':
1323       /*
1324        * show listener ports
1325        * show hidden ports to opers, if there are more than 3 parameters,
1326        * interpret the fourth parameter as the port number, limit non-local
1327        * or non-oper results to 8 ports.
1328        */ 
1329       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
1330                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
1331       break;
1332     case 'R':
1333     case 'r':
1334 #ifdef DEBUGMODE
1335       send_usage(sptr, parv[0]);
1336 #endif
1337       break;
1338     case 'D':
1339       report_configured_links(sptr, CONF_CRULEALL);
1340       break;
1341     case 'd':
1342       report_configured_links(sptr, CONF_CRULE);
1343       break;
1344     case 't':
1345       tstats(sptr, parv[0]);
1346       break;
1347     case 'T':
1348       report_configured_links(sptr, CONF_TLINES);
1349       break;
1350     case 'U':
1351       report_configured_links(sptr, CONF_UWORLD);
1352       break;
1353     case 'u':
1354     {
1355       time_t nowr;
1356
1357       nowr = CurrentTime - me.since;
1358       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0], /* XXX DEAD */
1359                  nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
1360       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0], /* XXX DEAD */
1361                  max_connection_count, max_client_count);
1362       break;
1363     }
1364     case 'W':
1365     case 'w':
1366       calc_load(sptr);
1367       break;
1368     case 'X':
1369     case 'x':
1370 #ifdef  DEBUGMODE
1371       send_listinfo(sptr, parv[0]);
1372 #endif
1373       break;
1374     case 'Y':
1375     case 'y':
1376       report_classes(sptr);
1377       break;
1378     case 'Z':
1379     case 'z':
1380       count_memory(sptr, parv[0]);
1381       break;
1382     default:
1383       stat = '*';
1384       break;
1385   }
1386   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat); /* XXX DEAD */
1387   return 0;
1388 }
1389 #endif /* 0 */
1390
1391