e775d4c3a93b09fe5e2bef21c7c260f6366d0372
[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         const struct ConnectionClass* cl = get_class_list();
388
389         for ( ; cl; cl = cl->next) {
390           if (Links(cl) > 0)
391             send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
392         }
393       }
394       break;
395     case 'W':
396     case 'w':
397       calc_load(sptr);
398       break;
399     case 'X':
400     case 'x':
401 #ifdef  DEBUGMODE
402       class_send_meminfo(sptr);
403       send_listinfo(sptr, parv[0]);
404 #endif
405       break;
406     case 'Y':
407     case 'y':
408       report_classes(sptr);
409       break;
410     case 'Z':
411     case 'z':
412       count_memory(sptr, parv[0]);
413       break;
414     default:
415       stat = '*';
416       while (*infotext)
417         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
418       break;
419   }
420   send_reply(sptr, RPL_ENDOFSTATS, stat);
421   return 0;
422 }
423
424 /*
425  * ms_stats - server message handler
426  *
427  *    parv[0] = sender prefix
428  *    parv[1] = statistics selector (defaults to Message frequency)
429  *    parv[2] = target server (current server defaulted, if omitted)
430  * And 'stats l' and 'stats' L:
431  *    parv[3] = server mask ("*" defaulted, if omitted)
432  * Or for stats p,P:
433  *    parv[3] = port mask (returns p-lines when its port is matched by this)
434  * Or for stats k,K,i and I:
435  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
436  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
437  *              (defaults to old reply if ommitted, when local or Oper)
438  *              A remote mask (something containing wildcards) is only
439  *              allowed for IRC Operators.
440  * Or for stats M:
441  *    parv[3] = time param
442  *    parv[4] = time param 
443  *    (see report_memleak_stats() in runmalloc.c for details)
444  *
445  * This function is getting really ugly. -Ghostwolf
446  */
447 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
448 {
449   struct Message *mptr;
450   struct Client *acptr;
451   struct ConfItem *aconf;
452   char stat = parc > 1 ? parv[1][0] : '\0';
453   int i;
454
455   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
456     return 0;
457
458   switch (stat)
459   {
460     case 'L':
461     case 'l':
462     {
463       int doall = 0, wilds = 0;
464       char *name = "*";
465       if (parc > 3 && *parv[3])
466       {
467         char *p;
468         name = parv[3];
469         wilds = (*name == '*' || *name == '?');
470         for (p = name + 1; *p; ++p)
471           if ((*p == '*' || *p == '?') && p[-1] != '\\')
472           {
473             wilds = 1;
474             break;
475           }
476       }
477       else
478         doall = 1;
479       /*
480        * Send info about connections which match, or all if the
481        * mask matches me.name.  Only restrictions are on those who
482        * are invisible not being visible to 'foreigners' who use
483        * a wild card based search to list it.
484        */
485       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
486                  "SendM SendKBytes RcveM RcveKBytes :Open since");
487       for (i = 0; i <= HighestFd; i++)
488       {
489         if (!(acptr = LocalClientArray[i]))
490           continue;
491         /* Don't return clients when this is a request for `all' */
492         if (doall && IsUser(acptr))
493           continue;
494         /* Don't show invisible people to unauthorized people when using
495          * wildcards  -- Is this still needed now /stats is oper only ? 
496          * Not here, because ms_stats is specifically a remote command, 
497          * thus the check was removed. -Ghostwolf */
498         /* Only show the ones that match the given mask - if any */
499         if (!doall && wilds && match(name, acptr->name))
500           continue;
501         /* Skip all that do not match the specific query */
502         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
503           continue;
504         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
505                    "%s %u %u %u %u %u :%Tu", acptr->name,
506                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
507                    (int)acptr->sendK, (int)acptr->receiveM,
508                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
509       }
510       break;
511     }
512     case 'C':
513     case 'c':
514       report_configured_links(sptr, CONF_SERVER);
515       break;
516     case 'G':
517     case 'g': /* send glines */
518       gline_stats(sptr);
519       break;
520     case 'H':
521     case 'h':
522       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
523       break;
524     case 'I':
525     case 'i':
526     case 'K':
527     case 'k':                   /* display CONF_IPKILL as well
528                                    as CONF_KILL -Kev */
529     {
530       int wilds, count;
531       char *user, *host, *p;
532       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
533       if (IsOper(sptr) && parc < 4)
534       {
535         report_configured_links(sptr, conf_status);
536         break;
537       }
538       if (parc < 4 || *parv[3] == '\0')
539         return need_more_params(sptr,
540                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
541
542       wilds = 0;
543       for (p = parv[3]; *p; p++)
544       {
545         if (*p == '\\')
546         {
547           if (!*++p)
548             break;
549           continue;
550         }
551         if (*p == '?' || *p == '*')
552         {
553           wilds = 1;
554           break;
555         }
556       }
557       if (!IsOper(sptr))
558       {
559         wilds = 0;
560         count = 3;
561       }
562       else
563         count = 1000;
564
565       if (conf_status == CONF_CLIENT)
566       {
567         user = 0;            /* Not used, but to avoid compiler warning. */
568
569         host = parv[3];
570       }
571       else
572       {
573         if ((host = strchr(parv[3], '@')))
574         {
575           user = parv[3];
576           *host++ = 0;;
577         }
578         else
579         {
580           user = 0;
581           host = parv[3];
582         }
583       }
584       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
585       {
586         if ((aconf->status & conf_status))
587         {
588           if (conf_status == CONF_KLINE)
589           {
590             if ((!wilds && ((user || aconf->host[1]) &&
591                 !match(aconf->host, host) &&
592                 (!user || !match(aconf->name, user)))) ||
593                 (wilds && !mmatch(host, aconf->host) &&
594                 (!user || !mmatch(user, aconf->name))))
595             {
596               send_reply(sptr, RPL_STATSKLINE,
597                          (aconf->status & CONF_KILL) ? 'K' : 'k', aconf->host,
598                          aconf->passwd, aconf->name, aconf->port,
599                          get_conf_class(aconf));
600               if (--count == 0)
601                 break;
602             }
603           }
604           else if (conf_status == CONF_CLIENT)
605           {
606             if ((!wilds && (!match(aconf->host, host) ||
607                 !match(aconf->name, host))) ||
608                 (wilds && (!mmatch(host, aconf->host) ||
609                 !mmatch(host, aconf->name))))
610             {
611               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
612                          aconf->port, get_conf_class(aconf));
613               if (--count == 0)
614                 break;
615             }
616           }
617         }
618       }
619       break;
620     }
621     case 'M':
622 #if !defined(NDEBUG)
623       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
624                  fda_get_block_count());
625 #endif
626
627 #if 0
628 #ifdef MEMSIZESTATS
629       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
630           me.name, parv[0], get_mem_size(), get_alloc_cnt());
631 #endif
632 #ifdef MEMLEAKSTATS
633       report_memleak_stats(sptr, parc, parv);
634 #endif
635 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
636       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
637           "is not enabled on this server", me.name, parv[0]);
638 #endif
639 #endif /* 0 */
640       break;
641     case 'm':
642       for (mptr = msgtab; mptr->cmd; mptr++)
643         if (mptr->count)
644           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
645                      mptr->bytes);
646       break;
647     case 'o':
648     case 'O':
649       report_configured_links(sptr, CONF_OPS);
650       break;
651     case 'p':
652     case 'P':
653       /*
654        * show listener ports
655        * show hidden ports to opers, if there are more than 3 parameters,
656        * interpret the fourth parameter as the port number, limit non-local
657        * or non-oper results to 8 ports.
658        */ 
659       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
660       break;
661     case 'R':
662     case 'r':
663 #ifdef DEBUGMODE
664       send_usage(sptr, parv[0]);
665 #endif
666       break;
667     case 'D':
668       report_crule_list(sptr, CRULE_ALL);
669       break;
670     case 'd':
671       report_crule_list(sptr, CRULE_MASK);
672       break;
673     case 't':
674       tstats(sptr, parv[0]);
675       break;
676     case 'T':
677       report_motd_list(sptr);
678       break;
679     case 'U':
680       report_configured_links(sptr, CONF_UWORLD);
681       break;
682     case 'u':
683     {
684       time_t nowr;
685
686       nowr = CurrentTime - me.since;
687       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
688                  (nowr / 60) % 60, nowr % 60);
689       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
690       break;
691     }
692     case 'v':
693       {
694         const struct ConnectionClass* cl = get_class_list();
695             
696         for ( ; cl; cl = cl->next) {
697           if (Links(cl) > 0)
698             send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
699         }
700       }
701       break;
702     
703     case 'W':
704     case 'w':
705       calc_load(sptr);
706       break;
707     case 'X':
708     case 'x':
709 #ifdef  DEBUGMODE
710       class_send_meminfo(sptr);
711       send_listinfo(sptr, parv[0]);
712 #endif
713       break;
714     case 'Y':
715     case 'y':
716       report_classes(sptr);
717       break;
718     case 'Z':
719     case 'z':
720       count_memory(sptr, parv[0]);
721       break;
722     default:
723       stat = '*';
724       break;
725   }
726   send_reply(sptr, RPL_ENDOFSTATS, stat);
727   return 0;
728 }
729
730 /*
731  * mo_stats - oper message handler
732  *
733  *    parv[0] = sender prefix
734  *    parv[1] = statistics selector (defaults to Message frequency)
735  *    parv[2] = target server (current server defaulted, if omitted)
736  * And 'stats l' and 'stats' L:
737  *    parv[3] = server mask ("*" defaulted, if omitted)
738  * Or for stats p,P:
739  *    parv[3] = port mask (returns p-lines when its port is matched by this)
740  * Or for stats k,K,i and I:
741  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
742  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
743  *              (defaults to old reply if ommitted, when local or Oper)
744  *              A remote mask (something containing wildcards) is only
745  *              allowed for IRC Operators.
746  * Or for stats M:
747  *    parv[3] = time param
748  *    parv[4] = time param 
749  *    (see report_memleak_stats() in runmalloc.c for details)
750  *
751  * This function is getting really ugly. -Ghostwolf
752  */
753 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
754 {
755   struct Message *mptr;
756   struct Client *acptr;
757   struct ConfItem *aconf;
758   char stat = parc > 1 ? parv[1][0] : '\0';
759   const char **infotext = statsinfo;
760   int i;
761
762   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
763     return 0;
764
765   switch (stat)
766   {
767     case 'L':
768     case 'l':
769     {
770       int doall = 0, wilds = 0;
771       char *name = "*";
772       if (parc > 3 && *parv[3])
773       {
774         char *p;
775         name = parv[3];
776         wilds = (*name == '*' || *name == '?');
777         for (p = name + 1; *p; ++p)
778           if ((*p == '*' || *p == '?') && p[-1] != '\\')
779           {
780             wilds = 1;
781             break;
782           }
783       }
784       else
785         doall = 1;
786       /*
787        * Send info about connections which match, or all if the
788        * mask matches me.name.  Only restrictions are on those who
789        * are invisible not being visible to 'foreigners' who use
790        * a wild card based search to list it.
791        */
792       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
793                  "SendM SendKBytes RcveM RcveKBytes :Open since");
794       for (i = 0; i <= HighestFd; i++)
795       {
796         if (!(acptr = LocalClientArray[i]))
797           continue;
798         /* Don't return clients when this is a request for `all' */
799         if (doall && IsUser(acptr))
800           continue;
801         /* Only show the ones that match the given mask - if any */
802         if (!doall && wilds && match(name, acptr->name))
803           continue;
804         /* Skip all that do not match the specific query */
805         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
806           continue;
807         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
808                    "%s %u %u %u %u %u :%Tu", acptr->name,
809                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
810                    (int)acptr->sendK, (int)acptr->receiveM,
811                    (int)acptr->receiveK, CurrentTime - acptr->firsttime);
812       }
813       break;
814     }
815     case 'C':
816     case 'c':
817       report_configured_links(sptr, CONF_SERVER);
818       break;
819     case 'G':
820     case 'g': /* send glines */
821       gline_stats(sptr);
822       break;
823     case 'H':
824     case 'h':
825       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
826       break;
827     case 'I':
828     case 'i':
829     case 'K':
830     case 'k':                   /* display CONF_IPKILL as well
831                                    as CONF_KILL -Kev */
832     {
833       int wilds, count;
834       char *user, *host, *p;
835       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
836       if (parc < 4)
837       {
838         report_configured_links(sptr, conf_status);
839         break;
840       }
841
842       wilds = 0;
843       for (p = parv[3]; *p; p++)
844       {
845         if (*p == '\\')
846         {
847           if (!*++p)
848             break;
849           continue;
850         }
851         if (*p == '?' || *p == '*')
852         {
853           wilds = 1;
854           break;
855         }
856       }
857
858       count = 1000;
859
860       if (conf_status == CONF_CLIENT)
861       {
862         user = 0;            /* Not used, but to avoid compiler warning. */
863
864         host = parv[3];
865       }
866       else
867       {
868         if ((host = strchr(parv[3], '@')))
869         {
870           user = parv[3];
871           *host++ = 0;;
872         }
873         else
874         {
875           user = 0;
876           host = parv[3];
877         }
878       }
879       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
880       {
881         if ((aconf->status & conf_status))
882         {
883           if (conf_status == CONF_KLINE)
884           {
885             if ((!wilds && ((user || aconf->host[1]) &&
886                 !match(aconf->host, host) &&
887                 (!user || !match(aconf->name, user)))) ||
888                 (wilds && !mmatch(host, aconf->host) &&
889                 (!user || !mmatch(user, aconf->name))))
890             {
891               send_reply(sptr, RPL_STATSKLINE,
892                          (aconf->status & CONF_KILL) ? 'K' : 'k', aconf->host,
893                          aconf->passwd, aconf->name, aconf->port,
894                          get_conf_class(aconf));
895               if (--count == 0)
896                 break;
897             }
898           }
899           else if (conf_status == CONF_CLIENT)
900           {
901             if ((!wilds && (!match(aconf->host, host) ||
902                 !match(aconf->name, host))) ||
903                 (wilds && (!mmatch(host, aconf->host) ||
904                 !mmatch(host, aconf->name))))
905             {
906               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->name,
907                          aconf->port, get_conf_class(aconf));
908               if (--count == 0)
909                 break;
910             }
911           }
912         }
913       }
914       break;
915     }
916     case 'M':
917 #if !defined(NDEBUG)
918       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
919                  fda_get_block_count());
920 #endif
921
922 #if 0
923 #ifdef MEMSIZESTATS
924       sendto_one(sptr, rpl_str(RPL_STATMEMTOT), /* XXX DEAD */
925           me.name, parv[0], get_mem_size(), get_alloc_cnt());
926 #endif
927 #ifdef MEMLEAKSTATS
928       report_memleak_stats(sptr, parc, parv);
929 #endif
930 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
931       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring " /* XXX DEAD */
932           "is not enabled on this server", me.name, parv[0]);
933 #endif
934 #endif /* 0 */
935       break;
936     case 'm':
937       for (mptr = msgtab; mptr->cmd; mptr++)
938         if (mptr->count)
939           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
940                      mptr->bytes);
941       break;
942     case 'o':
943     case 'O':
944       report_configured_links(sptr, CONF_OPS);
945       break;
946     case 'p':
947     case 'P':
948       /*
949        * show listener ports
950        * show hidden ports to opers, if there are more than 3 parameters,
951        * interpret the fourth parameter as the port number, limit non-local
952        * or non-oper results to 8 ports.
953        */ 
954       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
955       break;
956     case 'R':
957     case 'r':
958 #ifdef DEBUGMODE
959       send_usage(sptr, parv[0]);
960 #endif
961       break;
962     case 'D':
963       report_crule_list(sptr, CRULE_ALL);
964       break;
965     case 'd':
966       report_crule_list(sptr, CRULE_MASK);
967       break;
968     case 't':
969       tstats(sptr, parv[0]);
970       break;
971     case 'T':
972       report_motd_list(sptr);
973       break;
974     case 'U':
975       report_configured_links(sptr, CONF_UWORLD);
976       break;
977     case 'u':
978     {
979       time_t nowr;
980
981       nowr = CurrentTime - me.since;
982       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
983                  (nowr / 60) % 60, nowr % 60);
984       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
985       break;
986     }
987     case 'v':
988       {
989         const struct ConnectionClass* cl = get_class_list();
990             
991         for ( ; cl; cl = cl->next) {
992           if (Links(cl) > 0)
993             send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
994         }
995       }
996       break;
997     case 'W':
998     case 'w':
999       calc_load(sptr);
1000       break;
1001     case 'X':
1002     case 'x':
1003 #ifdef  DEBUGMODE
1004       class_send_meminfo(sptr);
1005       send_listinfo(sptr, parv[0]);
1006 #endif
1007       break;
1008     case 'Y':
1009     case 'y':
1010       report_classes(sptr);
1011       break;
1012     case 'Z':
1013     case 'z':
1014       count_memory(sptr, parv[0]);
1015       break;
1016     default:
1017       stat = '*';
1018       while (*infotext)
1019         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
1020       break;
1021   }
1022   send_reply(sptr, RPL_ENDOFSTATS, stat);
1023   return 0;
1024 }
1025