Author: Bleep <helveytw@home.com>
[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_crule_list(sptr, CRULE_ALL);
362       break;
363     case 'd':
364       report_crule_list(sptr, CRULE_MASK);
365       break;
366     case 't':
367       tstats(sptr, parv[0]);
368       break;
369     case 'T':
370       report_motd_list(sptr);
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_crule_list(sptr, CRULE_ALL);
668       break;
669     case 'd':
670       report_crule_list(sptr, CRULE_MASK);
671       break;
672     case 't':
673       tstats(sptr, parv[0]);
674       break;
675     case 'T':
676       report_motd_list(sptr);
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_crule_list(sptr, CRULE_ALL);
962       break;
963     case 'd':
964       report_crule_list(sptr, CRULE_MASK);
965       break;
966     case 't':
967       tstats(sptr, parv[0]);
968       break;
969     case 'T':
970       report_motd_list(sptr);
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 'v':
986     {
987         struct ConfClass *cltmp;
988         
989         for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp)) {
990                 if (Links(cltmp) > 0)
991                         send_reply(sptr,RPL_TRACECLASS, ConClass(cltmp), Links(cltmp));
992         }
993         break;
994     }
995     case 'W':
996     case 'w':
997       calc_load(sptr);
998       break;
999     case 'X':
1000     case 'x':
1001 #ifdef  DEBUGMODE
1002       send_listinfo(sptr, parv[0]);
1003 #endif
1004       break;
1005     case 'Y':
1006     case 'y':
1007       report_classes(sptr);
1008       break;
1009     case 'Z':
1010     case 'z':
1011       count_memory(sptr, parv[0]);
1012       break;
1013     default:
1014       stat = '*';
1015       while (*infotext)
1016         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
1017       break;
1018   }
1019   send_reply(sptr, RPL_ENDOFSTATS, stat);
1020   return 0;
1021 }
1022
1023 #if 0
1024 /*
1025  * m_stats
1026  *
1027  *    parv[0] = sender prefix
1028  *    parv[1] = statistics selector (defaults to Message frequency)
1029  *    parv[2] = target server (current server defaulted, if omitted)
1030  * And 'stats l' and 'stats' L:
1031  *    parv[3] = server mask ("*" defaulted, if omitted)
1032  * Or for stats p,P:
1033  *    parv[3] = port mask (returns p-lines when its port is matched by this)
1034  * Or for stats k,K,i and I:
1035  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
1036  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
1037  *              (defaults to old reply if ommitted, when local or Oper)
1038  *              A remote mask (something containing wildcards) is only
1039  *              allowed for IRC Operators.
1040  * Or for stats M:
1041  *    parv[3] = time param
1042  *    parv[4] = time param 
1043  *    (see report_memleak_stats() in runmalloc.c for details)
1044  *
1045  * This function is getting really ugly. -Ghostwolf
1046  */
1047 int m_stats(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1048 {
1049   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
1050       "RcveM RcveKBytes :Open since";
1051   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
1052   struct Message *mptr;
1053   struct Client *acptr;
1054   struct Gline* gline;
1055   struct ConfItem *aconf;
1056   char stat = parc > 1 ? parv[1][0] : '\0';
1057   int i;
1058
1059 /* m_stats is so obnoxiously full of special cases that the different
1060  * hunt_server() possiblites were becoming very messy. It now uses a
1061  * switch() so as to be easier to read and update as params change. 
1062  * -Ghostwolf 
1063  */
1064   switch (stat)
1065   {
1066       /* open to all, standard # of params */
1067     case 'U':
1068     case 'u':
1069     {
1070       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1071           != HUNTED_ISME)
1072         return 0;
1073       break;
1074     }
1075
1076       /* open to all, varying # of params */
1077     case 'k':
1078     case 'K':
1079     case 'i':
1080     case 'I':
1081     case 'p':
1082     case 'P':
1083     {
1084       if (parc > 3)
1085       {
1086         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv) /* XXX DEAD */
1087             != HUNTED_ISME)
1088           return 0;
1089       }
1090       else
1091       {
1092         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1093             != HUNTED_ISME)
1094           return 0;
1095       }
1096       break;
1097     }
1098
1099       /* oper only, varying # of params */
1100     case 'l':
1101     case 'L':
1102     case 'M':
1103     {
1104       if (parc == 4)
1105       {
1106         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv) /* XXX DEAD */
1107             != HUNTED_ISME)
1108           return 0;
1109       }
1110       else if (parc > 4)
1111       {
1112         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc, /* XXX DEAD */
1113             parv) != HUNTED_ISME)
1114           return 0;
1115       }
1116       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1117           != HUNTED_ISME)
1118         return 0;
1119       break;
1120     }
1121
1122       /* oper only, standard # of params */
1123     default:
1124     {
1125       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv) /* XXX DEAD */
1126           != HUNTED_ISME)
1127         return 0;
1128       break;
1129     }
1130   }
1131
1132   switch (stat)
1133   {
1134     case 'L':
1135     case 'l':
1136     {
1137       int doall = 0, wilds = 0;
1138       char *name = "*";
1139       if (parc > 3 && *parv[3])
1140       {
1141         char *p;
1142         name = parv[3];
1143         wilds = (*name == '*' || *name == '?');
1144         for (p = name + 1; *p; ++p)
1145           if ((*p == '*' || *p == '?') && p[-1] != '\\')
1146           {
1147             wilds = 1;
1148             break;
1149           }
1150       }
1151       else
1152         doall = 1;
1153       /*
1154        * Send info about connections which match, or all if the
1155        * mask matches me.name.  Only restrictions are on those who
1156        * are invisible not being visible to 'foreigners' who use
1157        * a wild card based search to list it.
1158        */
1159       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]); /* XXX DEAD */
1160       for (i = 0; i <= HighestFd; i++)
1161       {
1162         if (!(acptr = LocalClientArray[i]))
1163           continue;
1164         /* Don't return clients when this is a request for `all' */
1165         if (doall && IsUser(acptr))
1166           continue;
1167         /* Don't show invisible people to unauthorized people when using
1168          * wildcards  -- Is this still needed now /stats is oper only ? */
1169         if (IsInvisible(acptr) && (doall || wilds) &&
1170             !(MyConnect(sptr) && IsOper(sptr)) &&
1171             !IsAnOper(acptr) && (acptr != sptr))
1172           continue;
1173         /* Only show the ones that match the given mask - if any */
1174         if (!doall && wilds && match(name, acptr->name))
1175           continue;
1176         /* Skip all that do not match the specific query */
1177         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
1178           continue;
1179         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0], /* XXX DEAD */
1180                    acptr->name,
1181                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
1182                    (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
1183                    CurrentTime - acptr->firsttime);
1184       }
1185       break;
1186     }
1187     case 'C':
1188     case 'c':
1189       report_configured_links(sptr, CONF_SERVER);
1190       break;
1191     case 'G':
1192     case 'g': /* send glines */
1193       gline_remove_expired(TStime());
1194       for (gline = GlobalGlineList; gline; gline = gline->next) {
1195         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name, /* XXX DEAD */
1196                    sptr->name, 'G', gline->name, gline->host,
1197                    gline->expire, gline->reason);
1198       }
1199       break;
1200     case 'H':
1201     case 'h':
1202       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
1203       break;
1204     case 'I':
1205     case 'i':
1206     case 'K':
1207     case 'k':                   /* display CONF_IPKILL as well
1208                                    as CONF_KILL -Kev */
1209     {
1210       int wilds, count;
1211       char *user, *host, *p;
1212       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
1213       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
1214       {
1215         report_configured_links(sptr, conf_status);
1216         break;
1217       }
1218       if (parc < 4 || *parv[3] == '\0')
1219         return need_more_params(sptr,
1220                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
1221
1222       wilds = 0;
1223       for (p = parv[3]; *p; p++)
1224       {
1225         if (*p == '\\')
1226         {
1227           if (!*++p)
1228             break;
1229           continue;
1230         }
1231         if (*p == '?' || *p == '*')
1232         {
1233           wilds = 1;
1234           break;
1235         }
1236       }
1237       if (!(MyConnect(sptr) || IsOper(sptr)))
1238       {
1239         wilds = 0;
1240         count = 3;
1241       }
1242       else
1243         count = 1000;
1244
1245       if (conf_status == CONF_CLIENT)
1246       {
1247         user = 0;            /* Not used, but to avoid compiler warning. */
1248
1249         host = parv[3];
1250       }
1251       else
1252       {
1253         if ((host = strchr(parv[3], '@')))
1254         {
1255           user = parv[3];
1256           *host++ = 0;;
1257         }
1258         else
1259         {
1260           user = 0;
1261           host = parv[3];
1262         }
1263       }
1264       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
1265       {
1266         if ((aconf->status & conf_status))
1267         {
1268           if (conf_status == CONF_KLINE)
1269           {
1270             if ((!wilds && ((user || aconf->host[1]) &&
1271                 !match(aconf->host, host) &&
1272                 (!user || !match(aconf->name, user)))) ||
1273                 (wilds && !mmatch(host, aconf->host) &&
1274                 (!user || !mmatch(user, aconf->name))))
1275             {
1276               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name, /* XXX DEAD */
1277                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
1278                   aconf->port, get_conf_class(aconf));
1279               if (--count == 0)
1280                 break;
1281             }
1282           }
1283           else if (conf_status == CONF_CLIENT)
1284           {
1285             if ((!wilds && (!match(aconf->host, host) ||
1286                 !match(aconf->name, host))) ||
1287                 (wilds && (!mmatch(host, aconf->host) ||
1288                 !mmatch(host, aconf->name))))
1289             {
1290               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name, /* XXX DEAD */
1291                   sptr->name, 'I', aconf->host, aconf->name,
1292                   aconf->port, get_conf_class(aconf));
1293               if (--count == 0)
1294                 break;
1295             }
1296           }
1297         }
1298       }
1299       break;
1300     }
1301     case 'M':
1302 #if !defined(NDEBUG)
1303       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
1304           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
1305 #endif
1306
1307 #if 0
1308 #ifdef MEMSIZESTATS
1309       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
1310           me.name, parv[0], get_mem_size(), get_alloc_cnt());
1311 #endif
1312 #ifdef MEMLEAKSTATS
1313       report_memleak_stats(sptr, parc, parv);
1314 #endif
1315 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
1316       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
1317           "is not enabled on this server", me.name, parv[0]);
1318 #endif
1319 #endif /* 0 */
1320       break;
1321     case 'm':
1322       for (mptr = msgtab; mptr->cmd; mptr++)
1323         if (mptr->count)
1324           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS), /* XXX DEAD */
1325               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
1326       break;
1327     case 'o':
1328     case 'O':
1329       report_configured_links(sptr, CONF_OPS);
1330       break;
1331     case 'p':
1332     case 'P':
1333       /*
1334        * show listener ports
1335        * show hidden ports to opers, if there are more than 3 parameters,
1336        * interpret the fourth parameter as the port number, limit non-local
1337        * or non-oper results to 8 ports.
1338        */ 
1339       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
1340                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
1341       break;
1342     case 'R':
1343     case 'r':
1344 #ifdef DEBUGMODE
1345       send_usage(sptr, parv[0]);
1346 #endif
1347       break;
1348     case 'D':
1349       report_crule_list(sptr, CRULE_ALL);
1350       break;
1351     case 'd':
1352       report_crule_list(sptr, CRULE_MASK);
1353       break;
1354     case 't':
1355       tstats(sptr, parv[0]);
1356       break;
1357     case 'T':
1358       report_motd_list(sptr);
1359       break;
1360     case 'U':
1361       report_configured_links(sptr, CONF_UWORLD);
1362       break;
1363     case 'u':
1364     {
1365       time_t nowr;
1366
1367       nowr = CurrentTime - me.since;
1368       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0], /* XXX DEAD */
1369                  nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
1370       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0], /* XXX DEAD */
1371                  max_connection_count, max_client_count);
1372       break;
1373     }
1374     case 'W':
1375     case 'w':
1376       calc_load(sptr);
1377       break;
1378     case 'X':
1379     case 'x':
1380 #ifdef  DEBUGMODE
1381       send_listinfo(sptr, parv[0]);
1382 #endif
1383       break;
1384     case 'Y':
1385     case 'y':
1386       report_classes(sptr);
1387       break;
1388     case 'Z':
1389     case 'z':
1390       count_memory(sptr, parv[0]);
1391       break;
1392     default:
1393       stat = '*';
1394       break;
1395   }
1396   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat); /* XXX DEAD */
1397   return 0;
1398 }
1399 #endif /* 0 */
1400
1401