Author: Alex Badea <vampire@p16.pub.ro>
[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 #include "config.h"
83
84 #include "handlers.h"
85 /*
86  * XXX - ack!!!
87  */
88 #include "s_stats.h"
89 #include "channel.h"
90 #include "class.h"
91 #include "client.h"
92 #include "gline.h"
93 #include "hash.h"
94 #include "ircd.h"
95 #include "ircd_alloc.h"
96 #include "ircd_chattr.h"
97 #include "ircd_events.h"
98 #include "ircd_features.h"
99 #include "ircd_policy.h"
100 #include "ircd_reply.h"
101 #include "ircd_string.h"
102 #include "list.h"
103 #include "listener.h"
104 #include "match.h"
105 #include "motd.h"
106 #include "msg.h"
107 #include "numeric.h"
108 #include "numnicks.h"
109 #include "opercmds.h"
110 #include "s_bsd.h"
111 #include "s_conf.h"
112 #include "s_debug.h"
113 #include "s_misc.h"
114 #include "s_serv.h"
115 #include "s_user.h"
116 #include "send.h"
117 #include "struct.h"
118 #include "userload.h"
119
120 #include <assert.h>
121 #include <stdlib.h>
122 #include <string.h>
123
124
125 int report_klines(struct Client* sptr, char* mask, int limit_query)
126 {
127   int   wilds = 0;
128   int   count = 3;
129   char* user  = 0;
130   char* host;
131   const struct DenyConf* conf;
132
133   if (EmptyString(mask)) {
134     if (limit_query)
135       return need_more_params(sptr, "STATS K");
136     else
137       report_deny_list(sptr);
138     return 1;
139   }
140
141   if (!limit_query) {
142     wilds = string_has_wildcards(mask);
143     count = 1000;
144   }
145
146   if ((host = strchr(mask, '@'))) {
147     user = mask;
148     *host++ = '\0';
149   }
150   else {
151     host = mask;
152   }
153
154   for (conf = conf_get_deny_list(); conf; conf = conf->next) {
155     if ((!wilds && ((user || conf->hostmask) &&
156         !match(conf->hostmask, host) &&
157         (!user || !match(conf->usermask, user)))) ||
158         (wilds && !mmatch(host, conf->hostmask) &&
159         (!user || !mmatch(user, conf->usermask))))
160     {
161       send_reply(sptr, RPL_STATSKLINE,
162                  (conf->flags & DENY_FLAGS_IP) ? 'k' : 'K',
163                  conf->hostmask, conf->message, conf->usermask);
164       if (--count == 0)
165         return 1;
166     }
167   }
168   /* send_reply(sptr, RPL_ENDOFSTATS, stat); */
169   return 1;
170 }
171
172 static int report_servers_verbose(struct Client *sptr, char stat)
173 {
174   struct Client *acptr;
175   int i;
176
177   /* lowercase 'v' is for human-readable,
178    * uppercase 'V' is for machine-readable */
179   if (stat == 'v')
180     send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE,
181                "%-20s Burst Hops Numeric   Lag Clients/Max Proto "
182                "%-10s :Info", "Servername", "LinkTS");
183   for (i = -1; i <= HighestFd; i++) {
184     if (i == -1)
185       /* Hack 'me' in the list as well */
186       acptr = &me;
187     else if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
188       continue;
189     send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, stat == 'v' ?
190                "%-20s %c%c    %4i %s %-4i %5i %5i %5i P%-2i   %Tu :%s" :
191                "%s %c%c %i %s %i %i %i %i P%i %Tu :%s",
192                cli_name(acptr),
193                IsBurst(acptr) ? 'B' : '-',
194                IsBurstAck(acptr) ? 'A' : '-',
195                cli_hopcount(acptr),
196                NumServ(acptr),
197                base64toint(cli_yxx(acptr)),
198                cli_serv(acptr)->lag,
199                cli_serv(acptr)->clients,
200                cli_serv(acptr)->nn_mask,
201                cli_serv(acptr)->prot,
202                cli_serv(acptr)->timestamp,
203                cli_info(acptr));
204   }
205   return 0;
206 }
207
208
209 /*
210  * m_stats - generic message handler
211  *
212  *    parv[0] = sender prefix
213  *    parv[1] = statistics selector (defaults to Message frequency)
214  *    parv[2] = target server (current server defaulted, if omitted)
215  * And 'stats l' and 'stats' L:
216  *    parv[3] = server mask ("*" defaulted, if omitted)
217  * Or for stats p,P:
218  *    parv[3] = port mask (returns p-lines when its port is matched by this)
219  * Or for stats k,K,i and I:
220  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
221  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
222  *              (defaults to old reply if ommitted, when local or Oper)
223  *              A remote mask (something containing wildcards) is only
224  *              allowed for IRC Operators.
225  * Or for stats M:
226  *    parv[3] = time param
227  *    parv[4] = time param 
228  *    (see report_memleak_stats() in runmalloc.c for details)
229  *
230  * This function is getting really ugly. -Ghostwolf
231  */
232 int m_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
233 {
234   char stat = parc > 1 ? parv[1][0] : '\0';
235   const char **infotext = statsinfo;
236
237   if (hunt_stats(cptr, sptr, parc, parv, stat, HEAD_IN_SAND_REMOTE)
238       != HUNTED_ISME)
239     return 0;
240
241   switch (stat)
242   {
243     case 'L':
244     case 'l':
245 #ifdef HEAD_IN_SAND_STATS_L
246         return m_not_oper(sptr,cptr,parc,parv);
247 #else
248     {
249       struct Client *acptr;
250       int i;
251       int doall = 0;
252       int wilds = 0;
253       char *name = "*";
254
255       if (parc > 3 && !EmptyString(parv[3])) {
256         name = parv[3];
257         wilds = string_has_wildcards(name);
258       }
259       else
260         doall = 1;
261       /*
262        * Send info about connections which match, or all if the
263        * mask matches me.name.  Only restrictions are on those who
264        * are invisible not being visible to 'foreigners' who use
265        * a wild card based search to list it.
266        */
267       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
268                  "SendM SendKBytes RcveM RcveKBytes :Open since");
269       for (i = 0; i <= HighestFd; i++)
270       {
271         if (!(acptr = LocalClientArray[i]))
272           continue;
273         /* Don't return clients when this is a request for `all' */
274         if (doall && IsUser(acptr))
275           continue;
276         /* Don't show invisible people to non opers unless they know the nick */
277         if (IsInvisible(acptr) && (doall || wilds) && !IsAnOper(acptr) && (acptr != sptr))
278           continue;
279         /* Only show the ones that match the given mask - if any */
280         if (!doall && wilds && match(name, cli_name(acptr)))
281           continue;
282         /* Skip all that do not match the specific query */
283         if (!(doall || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
284           continue;
285         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
286                    "%s %u %u %u %u %u :%Tu", (*(cli_name(acptr))) ? cli_name(acptr) : "<unregistered>",
287                    (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
288                    (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
289                    (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
290       }
291     }
292 #endif
293       break;
294
295     case 'C':
296     case 'c':
297 #ifdef HEAD_IN_SAND_STATS_C
298       return m_not_oper(sptr,cptr,parc,parv);
299 #else
300       report_configured_links(sptr, CONF_SERVER);
301 #endif
302       break;
303
304     case 'E':
305     case 'e': /* report engine name */
306 #ifdef HEAD_IN_SAND_STATS_E
307       return m_not_oper(sptr,cptr,parc,parv);
308 #else
309       send_reply(sptr, RPL_STATSENGINE, engine_name());
310 #endif
311       break;
312
313     case 'G':
314     case 'g': /* send glines */
315 #ifdef HEAD_IN_SAND_STATS_G
316       return m_not_oper(sptr,cptr,parc,parv);
317 #else
318       gline_stats(sptr);
319 #endif
320       break;
321
322     case 'H':
323     case 'h':
324 #ifdef HEAD_IN_SAND_STATS_H
325       return m_not_oper(sptr,cptr,parc,parv);
326 #else
327       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
328 #endif
329       break;
330
331     case 'K':
332     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
333 #ifdef HEAD_IN_SAND_STATS_K
334     /* Simple version - if you want to fix it - send in a patch */
335     return m_not_oper(sptr,cptr,parc,parv);
336 #else
337       if (0 == report_klines(sptr, (parc == 4) ? parv[3] : 0, 0))
338         return 0;
339 #endif
340       break;
341     case 'F':
342     case 'f':
343 #ifdef HEAD_IN_SAND_STATS_F
344       return m_not_oper(sptr,cptr,parc,parv);
345 #else
346       feature_report(sptr);
347 #endif
348       break;
349
350     case 'I':
351     case 'i':
352 #ifdef HEAD_IN_SAND_STATS_I
353     /* Simple version - if you want to fix it - send in a patch */
354     return m_not_oper(sptr,cptr,parc,parv);
355 #else
356     {
357       struct ConfItem *aconf;
358       int wilds = 0;
359       int count = 1000;
360       char* host;
361
362       if (parc < 4) {
363         report_configured_links(sptr, CONF_CLIENT);
364         break;
365       }
366       if (EmptyString(parv[3]))
367         return need_more_params(sptr, "STATS I");
368
369       host = parv[3];
370       wilds = string_has_wildcards(host);
371
372       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
373         if (CONF_CLIENT == aconf->status) {
374           if ((!wilds && (!match(aconf->host, host) ||
375               !match(aconf->name, host))) ||
376               (wilds && (!mmatch(host, aconf->host) ||
377               !mmatch(host, aconf->name))))
378           {
379             if (aconf->passwd && IsDigit(*aconf->passwd) && 
380                 (!aconf->passwd[1] || 
381                  (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
382               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->passwd, 
383                         aconf->name, aconf->port, get_conf_class(aconf));
384             else 
385               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
386                          aconf->name, aconf->port, get_conf_class(aconf));
387             
388             if (--count == 0)
389               break;
390           }
391         }
392       }
393     }
394 #endif
395       break;
396
397     case 'M':
398 #ifdef HEAD_IN_SAND_STATS_M
399       return m_not_oper(sptr,cptr,parc,parv);
400 #else
401 #if defined(MDEBUG)
402       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
403                  fda_get_block_count());
404 #endif
405 #endif
406       break;
407
408     case 'm':
409 #ifdef HEAD_IN_SAND_STATS_m
410       return m_not_oper(sptr,cptr,parc,parv);
411 #else
412     {
413       struct Message *mptr;
414
415       for (mptr = msgtab; mptr->cmd; mptr++)
416         if (mptr->count)
417           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
418                      mptr->bytes);
419     }
420 #endif
421       break;
422
423     case 'o':
424     case 'O':
425 #ifdef HEAD_IN_SAND_STATS_O
426       return m_not_oper(sptr,cptr,parc,parv);
427 #else
428       report_configured_links(sptr, CONF_OPS);
429 #endif
430       break;
431
432     case 'p':
433     case 'P':
434       /*
435        * show listener ports
436        * show hidden ports to opers, if there are more than 3 parameters,
437        * interpret the fourth parameter as the port number.
438        */ 
439 #ifdef HEAD_IN_SAND_STATS_P
440       return m_not_oper(sptr,cptr,parc,parv);
441 #else
442       show_ports(sptr, 0, (parc > 3) ? atoi(parv[3]) : 0, 100);
443 #endif
444       break;
445
446     case 'R':
447     case 'r':
448 #ifdef HEAD_IN_SAND_STATS_R
449       return m_not_oper(sptr,cptr,parc,parv);
450 #else
451 #ifdef DEBUGMODE
452       send_usage(sptr, parv[0]);
453 #endif
454 #endif
455       break;
456
457     case 'D':
458 #ifdef HEAD_IN_SAND_STATS_D
459       return m_not_oper(sptr,cptr,parc,parv);
460 #else
461       report_crule_list(sptr, CRULE_ALL);
462 #endif
463       break;
464
465     case 'd':
466 #ifdef HEAD_IN_SAND_STATS_d
467       return m_not_oper(sptr,cptr,parc,parv);
468 #else
469       report_crule_list(sptr, CRULE_MASK);
470 #endif
471       break;
472
473     case 't':
474 #ifdef HEAD_IN_SAND_STATS_t
475       return m_not_oper(sptr,cptr,parc,parv);
476 #else
477       tstats(sptr, parv[0]);
478 #endif
479       break;
480
481     case 'T':
482 #ifdef HEAD_IN_SAND_STATS_T
483       return m_not_oper(sptr,cptr,parc,parv);
484 #else
485       motd_report(sptr);
486 #endif
487       break;
488
489     case 'U':
490 #ifdef HEAD_IN_SAND_STATS_U
491       return m_not_oper(sptr,cptr,parc,parv);
492 #else
493       report_configured_links(sptr, CONF_UWORLD);
494 #endif
495       break;
496
497     case 'u':
498 #ifdef HEAD_IN_SAND_STATS_u
499       return m_not_oper(sptr,cptr,parc,parv);
500 #else
501     {
502       time_t nowr;
503
504       nowr = CurrentTime - cli_since(&me);
505       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
506                  (nowr / 60) % 60, nowr % 60);
507       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
508     }
509 #endif
510       break;
511
512     case 'W':
513     case 'w':
514 #ifdef HEAD_IN_SAND_STATS_W
515       return m_not_oper(sptr,cptr,parc,parv);
516 #else
517       calc_load(sptr);
518 #endif
519       break;
520
521     case 'X':
522     case 'x':
523 #ifdef HEAD_IN_SAND_STATS_X
524       return m_not_oper(sptr,cptr,parc,parv);
525 #else
526 #ifdef  DEBUGMODE
527       class_send_meminfo(sptr);
528       send_listinfo(sptr, parv[0]);
529 #endif
530 #endif
531       break;
532
533     case 'Y':
534     case 'y':
535 #ifdef HEAD_IN_SAND_STATS_Y
536       return m_not_oper(sptr,cptr,parc,parv);
537 #else
538       report_classes(sptr);
539 #endif
540       break;
541
542     case 'Z':
543     case 'z':
544 #ifdef HEAD_IN_SAND_STATS_Z
545       return m_not_oper(sptr,cptr,parc,parv);
546 #else
547       count_memory(sptr, parv[0]);
548 #endif
549       break;
550
551     default:
552       stat = '*';
553       while (*infotext)
554         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
555       break;
556   }
557   send_reply(sptr, RPL_ENDOFSTATS, stat);
558   return 0;
559 }
560
561 /*
562  * ms_stats - server message handler
563  *
564  *    parv[0] = sender prefix
565  *    parv[1] = statistics selector (defaults to Message frequency)
566  *    parv[2] = target server (current server defaulted, if omitted)
567  * And 'stats l' and 'stats' L:
568  *    parv[3] = server mask ("*" defaulted, if omitted)
569  * Or for stats p,P:
570  *    parv[3] = port mask (returns p-lines when its port is matched by this)
571  * Or for stats k,K,i and I:
572  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
573  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
574  *              (defaults to old reply if ommitted, when local or Oper)
575  *              A remote mask (something containing wildcards) is only
576  *              allowed for IRC Operators.
577  * Or for stats M:
578  *    parv[3] = time param
579  *    parv[4] = time param 
580  *    (see report_memleak_stats() in runmalloc.c for details)
581  *
582  * This function is getting really ugly. -Ghostwolf
583  */
584 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
585 {
586   struct Message *mptr;
587   struct Client *acptr;
588   struct ConfItem *aconf;
589   char stat = parc > 1 ? parv[1][0] : '\0';
590   int i;
591
592   if (hunt_stats(cptr, sptr, parc, parv, stat, 0) != HUNTED_ISME)
593     return 0;
594
595   switch (stat)
596   {
597     case 'L':
598     case 'l':
599     {
600       int doall = 0;
601       int wilds = 0;
602       char *name = "*";
603
604       if (parc > 3 && !EmptyString(parv[3])) {
605         name = parv[3];
606         wilds = string_has_wildcards(name);
607       }
608       else
609         doall = 1;
610       /*
611        * Send info about connections which match, or all if the
612        * mask matches me.name.  Only restrictions are on those who
613        * are invisible not being visible to 'foreigners' who use
614        * a wild card based search to list it.
615        */
616       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
617                  "SendM SendKBytes RcveM RcveKBytes :Open since");
618       for (i = 0; i <= HighestFd; i++)
619       {
620         if (!(acptr = LocalClientArray[i]))
621           continue;
622         /* Don't return clients when this is a request for `all' */
623         if (doall && IsUser(acptr))
624           continue;
625         /* Don't show invisible people to unauthorized people when using
626          * wildcards  -- Is this still needed now /stats is oper only ? 
627          * Not here, because ms_stats is specifically a remote command, 
628          * thus the check was removed. -Ghostwolf */
629         /* Only show the ones that match the given mask - if any */
630         if (!doall && wilds && match(name, cli_name(acptr)))
631           continue;
632         /* Skip all that do not match the specific query */
633         if (!(doall || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
634           continue;
635         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
636                    "%s %u %u %u %u %u :%Tu", cli_name(acptr),
637                    (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
638                    (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
639                    (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
640       }
641       break;
642     }
643     case 'C':
644     case 'c':
645       report_configured_links(sptr, CONF_SERVER);
646       break;
647     case 'E':
648     case 'e': /* report engine name */
649       send_reply(sptr, RPL_STATSENGINE, engine_name());
650       break;
651     case 'G':
652     case 'g': /* send glines */
653       gline_stats(sptr);
654       break;
655     case 'H':
656     case 'h':
657       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
658       break;
659     case 'K':
660     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
661       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, !IsOper(sptr)))
662         return 0;
663       break;
664     case 'F':
665     case 'f':
666       feature_report(sptr);
667       break;
668     case 'I':
669     case 'i':
670     {
671       int   wilds = 0;
672       int   count = 3;
673       char* host;
674
675       if (parc < 4 && IsOper(sptr)) {
676         report_configured_links(sptr, CONF_CLIENT);
677         break;
678       }
679       if (parc < 4 || EmptyString(parv[3]))
680         return need_more_params(sptr, "STATS I");
681
682       if (IsOper(sptr)) {
683         wilds = string_has_wildcards(parv[3]);
684         count = 1000;
685       }
686
687       host = parv[3];
688
689       for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
690         if (CONF_CLIENT == aconf->status) {
691           if ((!wilds && (!match(aconf->host, host) ||
692               !match(aconf->name, host))) ||
693               (wilds && (!mmatch(host, aconf->host) ||
694               !mmatch(host, aconf->name))))
695           {
696             if (aconf->passwd && IsDigit(*aconf->passwd) &&
697                 (!aconf->passwd[1] ||
698                  (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
699               send_reply(sptr, RPL_STATSILINE, 'I', aconf->host,
700                          aconf->passwd, aconf->name, aconf->port,
701                          get_conf_class(aconf));
702               else
703                 send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
704                            aconf->name, aconf->port, get_conf_class(aconf));
705
706             if (--count == 0)
707               break;
708           }
709         }
710       }
711       break;
712     }
713     case 'M':
714 #if defined(MDEBUG)
715       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
716                  fda_get_block_count());
717 #endif
718       break;
719     case 'm':
720       for (mptr = msgtab; mptr->cmd; mptr++)
721         if (mptr->count)
722           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
723                      mptr->bytes);
724       break;
725     case 'o':
726     case 'O':
727       report_configured_links(sptr, CONF_OPS);
728       break;
729     case 'p':
730     case 'P':
731       /*
732        * show listener ports
733        * show hidden ports to opers, if there are more than 3 parameters,
734        * interpret the fourth parameter as the port number, limit non-local
735        * or non-oper results to 8 ports.
736        */ 
737       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
738       break;
739     case 'R':
740     case 'r':
741 #ifdef DEBUGMODE
742       send_usage(sptr, parv[0]);
743 #endif
744       break;
745     case 'D':
746       report_crule_list(sptr, CRULE_ALL);
747       break;
748     case 'd':
749       report_crule_list(sptr, CRULE_MASK);
750       break;
751     case 't':
752       tstats(sptr, parv[0]);
753       break;
754     case 'T':
755       motd_report(sptr);
756       break;
757     case 'U':
758       report_configured_links(sptr, CONF_UWORLD);
759       break;
760     case 'u':
761     {
762       time_t nowr;
763
764       nowr = CurrentTime - cli_since(&me);
765       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
766                  (nowr / 60) % 60, nowr % 60);
767       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
768       break;
769     }
770     case 'V':
771     case 'v':
772       report_servers_verbose(sptr, stat);
773       break;
774     case 'W':
775     case 'w':
776       calc_load(sptr);
777       break;
778     case 'X':
779     case 'x':
780 #ifdef  DEBUGMODE
781       class_send_meminfo(sptr);
782       send_listinfo(sptr, parv[0]);
783 #endif
784       break;
785     case 'Y':
786     case 'y':
787       report_classes(sptr);
788       break;
789     case 'Z':
790     case 'z':
791       count_memory(sptr, parv[0]);
792       break;
793     default:
794       stat = '*';
795       break;
796   }
797   send_reply(sptr, RPL_ENDOFSTATS, stat);
798   return 0;
799 }
800
801 /*
802  * mo_stats - oper message handler
803  *
804  *    parv[0] = sender prefix
805  *    parv[1] = statistics selector (defaults to Message frequency)
806  *    parv[2] = target server (current server defaulted, if omitted)
807  * And 'stats l' and 'stats' L:
808  *    parv[3] = server mask ("*" defaulted, if omitted)
809  * Or for stats p,P:
810  *    parv[3] = port mask (returns p-lines when its port is matched by this)
811  * Or for stats k,K,i and I:
812  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
813  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
814  *              (defaults to old reply if ommitted, when local or Oper)
815  *              A remote mask (something containing wildcards) is only
816  *              allowed for IRC Operators.
817  * Or for stats M:
818  *    parv[3] = time param
819  *    parv[4] = time param 
820  *    (see report_memleak_stats() in runmalloc.c for details)
821  *
822  * This function is getting really ugly. -Ghostwolf
823  */
824 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
825 {
826   struct Message*  mptr;
827   struct Client*   acptr;
828   struct ConfItem* aconf;
829   char             stat = parc > 1 ? parv[1][0] : '\0';
830   const char**     infotext = statsinfo;
831   int              i;
832
833   if (hunt_stats(cptr, sptr, parc, parv, stat, HEAD_IN_SAND_REMOTE)
834       != HUNTED_ISME)
835     return 0;
836
837   switch (stat)
838   {
839     case 'L':
840     case 'l':
841     {
842       int doall = 0, wilds = 0;
843       char* name = "*";
844       if (parc > 3 && !EmptyString(parv[3])) {
845         name = parv[3];
846         wilds = string_has_wildcards(name);
847       }
848       else
849         doall = 1;
850       /*
851        * Send info about connections which match, or all if the
852        * mask matches me.name.  Only restrictions are on those who
853        * are invisible not being visible to 'foreigners' who use
854        * a wild card based search to list it.
855        */
856       send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
857                  "SendM SendKBytes RcveM RcveKBytes :Open since");
858       for (i = 0; i <= HighestFd; i++)
859       {
860         if (!(acptr = LocalClientArray[i]))
861           continue;
862         /* Don't return clients when this is a request for `all' */
863         if (doall && IsUser(acptr))
864           continue;
865         /* Only show the ones that match the given mask - if any */
866         if (!doall && wilds && match(name, cli_name(acptr)))
867           continue;
868         /* Skip all that do not match the specific query */
869         if (!(doall || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
870           continue;
871         send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
872                    "%s %u %u %u %u %u :%Tu", cli_name(acptr),
873                    (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
874                    (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
875                    (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
876       }
877       break;
878     }
879     case 'C':
880     case 'c':
881       report_configured_links(sptr, CONF_SERVER);
882       break;
883     case 'E':
884     case 'e': /* report engine name */
885       send_reply(sptr, RPL_STATSENGINE, engine_name());
886       break;
887     case 'G':
888     case 'g': /* send glines */
889       gline_stats(sptr);
890       break;
891     case 'H':
892     case 'h':
893       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
894       break;
895     case 'K':
896     case 'k':    /* display CONF_IPKILL as well as CONF_KILL -Kev */
897       if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, 0))
898         return 0;
899       break;
900     case 'F':
901     case 'f':
902       feature_report(sptr);
903       break;
904     case 'I':
905     case 'i':
906       {
907         int   wilds = 0;
908         int   count = 1000;
909         char* host;
910
911         if (parc < 4) {
912           report_configured_links(sptr, CONF_CLIENT);
913           break;
914         }
915         if (EmptyString(parv[3]))
916           return need_more_params(sptr, "STATS I");
917
918         host = parv[3];
919         wilds = string_has_wildcards(host);
920
921         for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
922           if (CONF_CLIENT == aconf->status) {
923             if ((!wilds && (!match(aconf->host, host) ||
924                 !match(aconf->name, host))) ||
925                 (wilds && (!mmatch(host, aconf->host) ||
926                 !mmatch(host, aconf->name))))
927             {
928               if (aconf->passwd && IsDigit(*aconf->passwd) &&
929                   (!aconf->passwd[1] ||
930                    (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
931                 send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, 
932                            aconf->passwd, aconf->name, aconf->port, 
933                            get_conf_class(aconf));
934               else 
935                 send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
936                            aconf->name, aconf->port, get_conf_class(aconf));
937
938               if (--count == 0)
939                 break;
940             }
941           }
942         }
943       }
944       break;
945     case 'M':
946 #if defined(MDEBUG)
947       send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
948                  fda_get_block_count());
949 #endif
950       break;
951     case 'm':
952       for (mptr = msgtab; mptr->cmd; mptr++)
953         if (mptr->count)
954           send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
955                      mptr->bytes);
956       break;
957     case 'o':
958     case 'O':
959       report_configured_links(sptr, CONF_OPS);
960       break;
961     case 'p':
962     case 'P':
963       /*
964        * show listener ports
965        * show hidden ports to opers, if there are more than 3 parameters,
966        * interpret the fourth parameter as the port number, limit non-local
967        * or non-oper results to 8 ports.
968        */ 
969       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
970       break;
971     case 'R':
972     case 'r':
973 #ifdef DEBUGMODE
974       send_usage(sptr, parv[0]);
975 #endif
976       break;
977     case 'D':
978       report_crule_list(sptr, CRULE_ALL);
979       break;
980     case 'd':
981       report_crule_list(sptr, CRULE_MASK);
982       break;
983     case 't':
984       tstats(sptr, parv[0]);
985       break;
986     case 'T':
987       motd_report(sptr);
988       break;
989     case 'U':
990       report_configured_links(sptr, CONF_UWORLD);
991       break;
992     case 'u':
993     {
994       time_t nowr;
995
996       nowr = CurrentTime - cli_since(&me);
997       send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
998                  (nowr / 60) % 60, nowr % 60);
999       send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
1000       break;
1001     }
1002     case 'V':
1003     case 'v':
1004       report_servers_verbose(sptr, stat);
1005       break;
1006     case 'W':
1007     case 'w':
1008       calc_load(sptr);
1009       break;
1010     case 'X':
1011     case 'x':
1012 #ifdef  DEBUGMODE
1013       class_send_meminfo(sptr);
1014       send_listinfo(sptr, parv[0]);
1015 #endif
1016       break;
1017     case 'Y':
1018     case 'y':
1019       report_classes(sptr);
1020       break;
1021     case 'Z':
1022     case 'z':
1023       count_memory(sptr, parv[0]);
1024       break;
1025
1026     default:
1027       stat = '*';
1028       while (*infotext)
1029         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
1030       break;
1031   }
1032   send_reply(sptr, RPL_ENDOFSTATS, stat);
1033   return 0;
1034 }
1035