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