Author: Ghostwolf <foxxe@trms.com>
[ircu2.10.12-pk.git] / ircd / m_stats.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_stats.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #if 0
83 /*
84  * No need to include handlers.h here the signatures must match
85  * and we don't need to force a rebuild of all the handlers everytime
86  * we add a new one to the list. --Bleep
87  */
88 #include "handlers.h"
89 #endif /* 0 */
90 /*
91  * XXX - ack!!!
92  */
93 #include "s_stats.h"
94 #include "channel.h"
95 #include "class.h"
96 #include "client.h"
97 #include "gline.h"
98 #include "hash.h"
99 #include "ircd.h"
100 #include "ircd_alloc.h"
101 #include "ircd_chattr.h"
102 #include "ircd_reply.h"
103 #include "ircd_string.h"
104 #include "list.h"
105 #include "listener.h"
106 #include "match.h"
107 #include "msg.h"
108 #include "numeric.h"
109 #include "numnicks.h"
110 #include "opercmds.h"
111 #include "s_bsd.h"
112 #include "s_conf.h"
113 #include "s_debug.h"
114 #include "s_misc.h"
115 #include "s_serv.h"
116 #include "s_user.h"
117 #include "send.h"
118 #include "struct.h"
119 #include "userload.h"
120
121 #include <assert.h>
122 #include <stdlib.h>
123 #include <string.h>
124
125
126 /*
127  * m_stats - generic message handler
128  *
129  *    parv[0] = sender prefix
130  *    parv[1] = statistics selector (defaults to Message frequency)
131  *    parv[2] = target server (current server defaulted, if omitted)
132  * And 'stats l' and 'stats' L:
133  *    parv[3] = server mask ("*" defaulted, if omitted)
134  * Or for stats p,P:
135  *    parv[3] = port mask (returns p-lines when its port is matched by this)
136  * Or for stats k,K,i and I:
137  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
138  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
139  *              (defaults to old reply if ommitted, when local or Oper)
140  *              A remote mask (something containing wildcards) is only
141  *              allowed for IRC Operators.
142  * Or for stats M:
143  *    parv[3] = time param
144  *    parv[4] = time param 
145  *    (see report_memleak_stats() in runmalloc.c for details)
146  *
147  * This function is getting really ugly. -Ghostwolf
148  */
149 int m_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
150 {
151   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
152       "RcveM RcveKBytes :Open since";
153   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
154   struct Message *mptr;
155   struct Client *acptr;
156   struct ConfItem *aconf;
157   char stat = parc > 1 ? parv[1][0] : '\0';
158   const char **infotext = statsinfo;
159   int i;
160
161   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
162     return 0;
163
164   switch (stat)
165   {
166     case 'L':
167     case 'l':
168     {
169       int doall = 0, wilds = 0;
170       char *name = "*";
171       if (parc > 3 && *parv[3])
172       {
173         char *p;
174         name = parv[3];
175         wilds = (*name == '*' || *name == '?');
176         for (p = name + 1; *p; ++p)
177           if ((*p == '*' || *p == '?') && p[-1] != '\\')
178           {
179             wilds = 1;
180             break;
181           }
182       }
183       else
184         doall = 1;
185       /*
186        * Send info about connections which match, or all if the
187        * mask matches me.name.  Only restrictions are on those who
188        * are invisible not being visible to 'foreigners' who use
189        * a wild card based search to list it.
190        */
191       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
192       for (i = 0; i <= HighestFd; i++)
193       {
194         if (!(acptr = LocalClientArray[i]))
195           continue;
196         /* Don't return clients when this is a request for `all' */
197         if (doall && IsUser(acptr))
198           continue;
199         /* Don't show invisible people to non opers unless they know the nick */
200         if (IsInvisible(acptr) && (doall || wilds) && !IsAnOper(acptr) && (acptr != sptr))
201           continue;
202         /* Only show the ones that match the given mask - if any */
203         if (!doall && wilds && match(name, acptr->name))
204           continue;
205         /* Skip all that do not match the specific query */
206         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
207           continue;
208         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
209                    acptr->name, (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
210                    (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
211                    CurrentTime - acptr->firsttime);
212       }
213       break;
214     }
215     case 'C':
216     case 'c':
217       report_configured_links(sptr, CONF_SERVER);
218       break;
219     case 'G':
220     case 'g': /* send glines */
221       gline_stats(sptr);
222       break;
223     case 'H':
224     case 'h':
225       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
226       break;
227     case 'I':
228     case 'i':
229     case 'K':
230     case 'k':                   /* display CONF_IPKILL as well
231                                    as CONF_KILL -Kev */
232     {
233       int wilds, count;
234       char *user, *host, *p;
235       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
236       if (parc < 4)
237       {
238         report_configured_links(sptr, conf_status);
239         break;
240       }
241
242       wilds = 0;
243       for (p = parv[3]; *p; p++)
244       {
245         if (*p == '\\')
246         {
247           if (!*++p)
248             break;
249           continue;
250         }
251         if (*p == '?' || *p == '*')
252         {
253           wilds = 1;
254           break;
255         }
256       }
257
258       count = 1000;
259
260       if (conf_status == CONF_CLIENT)
261       {
262         user = 0;            /* Not used, but to avoid compiler warning. */
263
264         host = parv[3];
265       }
266       else
267       {
268         if ((host = strchr(parv[3], '@')))
269         {
270           user = parv[3];
271           *host++ = 0;;
272         }
273         else
274         {
275           user = 0;
276           host = parv[3];
277         }
278       }
279       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
280       {
281         if ((aconf->status & conf_status))
282         {
283           if (conf_status == CONF_KLINE)
284           {
285             if ((!wilds && ((user || aconf->host[1]) &&
286                 !match(aconf->host, host) &&
287                 (!user || !match(aconf->name, user)))) ||
288                 (wilds && !mmatch(host, aconf->host) &&
289                 (!user || !mmatch(user, aconf->name))))
290             {
291               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
292                   sptr->name, (aconf->status & CONF_KILL) ? 'K' : 'k',
293                   aconf->host, aconf->passwd, aconf->name,
294                   aconf->port, get_conf_class(aconf));
295               if (--count == 0)
296                 break;
297             }
298           }
299           else if (conf_status == CONF_CLIENT)
300           {
301             if ((!wilds && (!match(aconf->host, host) ||
302                 !match(aconf->name, host))) ||
303                 (wilds && (!mmatch(host, aconf->host) ||
304                 !mmatch(host, aconf->name))))
305             {
306               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
307                   sptr->name, 'I', aconf->host, aconf->name,
308                   aconf->port, get_conf_class(aconf));
309               if (--count == 0)
310                 break;
311             }
312           }
313         }
314       }
315       break;
316     }
317     case 'M':
318 #if !defined(NDEBUG)
319       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
320           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
321 #endif
322
323 #if 0
324 #ifdef MEMSIZESTATS
325       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
326           me.name, parv[0], get_mem_size(), get_alloc_cnt());
327 #endif
328 #ifdef MEMLEAKSTATS
329       report_memleak_stats(sptr, parc, parv);
330 #endif
331 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
332       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
333           "is not enabled on this server", me.name, parv[0]);
334 #endif
335 #endif /* 0 */
336       break;
337     case 'm':
338       for (mptr = msgtab; mptr->cmd; mptr++)
339         if (mptr->count)
340           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
341               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
342       break;
343     case 'o':
344     case 'O':
345       report_configured_links(sptr, CONF_OPS);
346       break;
347     case 'p':
348     case 'P':
349       /*
350        * show listener ports
351        * show hidden ports to opers, if there are more than 3 parameters,
352        * interpret the fourth parameter as the port number.
353        */ 
354       show_ports(sptr, 0, (parc > 3) ? atoi(parv[3]) : 0, 100);
355       break;
356     case 'R':
357     case 'r':
358 #ifdef DEBUGMODE
359       send_usage(sptr, parv[0]);
360 #endif
361       break;
362     case 'D':
363       report_configured_links(sptr, CONF_CRULEALL);
364       break;
365     case 'd':
366       report_configured_links(sptr, CONF_CRULE);
367       break;
368     case 't':
369       tstats(sptr, parv[0]);
370       break;
371     case 'T':
372       report_configured_links(sptr, CONF_TLINES);
373       break;
374     case 'U':
375       report_configured_links(sptr, CONF_UWORLD);
376       break;
377     case 'u':
378     {
379       time_t nowr;
380
381       nowr = CurrentTime - me.since;
382       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
383           nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
384       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
385           max_connection_count, max_client_count);
386       break;
387     }
388     case 'W':
389     case 'w':
390       calc_load(sptr);
391       break;
392     case 'X':
393     case 'x':
394 #ifdef  DEBUGMODE
395       send_listinfo(sptr, parv[0]);
396 #endif
397       break;
398     case 'Y':
399     case 'y':
400       report_classes(sptr);
401       break;
402     case 'Z':
403     case 'z':
404       count_memory(sptr, parv[0]);
405       break;
406     default:
407       stat = '*';
408       while (*infotext) sendto_one(sptr, ":%s NOTICE %s :%s", me.name, parv[0], *infotext++); 
409       break;
410   }
411   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
412   return 0;
413 }
414
415 /*
416  * ms_stats - server message handler
417  *
418  *    parv[0] = sender prefix
419  *    parv[1] = statistics selector (defaults to Message frequency)
420  *    parv[2] = target server (current server defaulted, if omitted)
421  * And 'stats l' and 'stats' L:
422  *    parv[3] = server mask ("*" defaulted, if omitted)
423  * Or for stats p,P:
424  *    parv[3] = port mask (returns p-lines when its port is matched by this)
425  * Or for stats k,K,i and I:
426  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
427  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
428  *              (defaults to old reply if ommitted, when local or Oper)
429  *              A remote mask (something containing wildcards) is only
430  *              allowed for IRC Operators.
431  * Or for stats M:
432  *    parv[3] = time param
433  *    parv[4] = time param 
434  *    (see report_memleak_stats() in runmalloc.c for details)
435  *
436  * This function is getting really ugly. -Ghostwolf
437  */
438 int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
439 {
440   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
441       "RcveM RcveKBytes :Open since";
442   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
443   struct Message *mptr;
444   struct Client *acptr;
445   struct ConfItem *aconf;
446   char stat = parc > 1 ? parv[1][0] : '\0';
447   int i;
448
449   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
450     return 0;
451
452   switch (stat)
453   {
454     case 'L':
455     case 'l':
456     {
457       int doall = 0, wilds = 0;
458       char *name = "*";
459       if (parc > 3 && *parv[3])
460       {
461         char *p;
462         name = parv[3];
463         wilds = (*name == '*' || *name == '?');
464         for (p = name + 1; *p; ++p)
465           if ((*p == '*' || *p == '?') && p[-1] != '\\')
466           {
467             wilds = 1;
468             break;
469           }
470       }
471       else
472         doall = 1;
473       /*
474        * Send info about connections which match, or all if the
475        * mask matches me.name.  Only restrictions are on those who
476        * are invisible not being visible to 'foreigners' who use
477        * a wild card based search to list it.
478        */
479       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
480       for (i = 0; i <= HighestFd; i++)
481       {
482         if (!(acptr = LocalClientArray[i]))
483           continue;
484         /* Don't return clients when this is a request for `all' */
485         if (doall && IsUser(acptr))
486           continue;
487         /* Don't show invisible people to unauthorized people when using
488          * wildcards  -- Is this still needed now /stats is oper only ? 
489          * Not here, because ms_stats is specifically a remote command, 
490          * thus the check was removed. -Ghostwolf */
491         /* Only show the ones that match the given mask - if any */
492         if (!doall && wilds && match(name, acptr->name))
493           continue;
494         /* Skip all that do not match the specific query */
495         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
496           continue;
497         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
498             acptr->name, (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
499             (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
500             CurrentTime - acptr->firsttime);
501       }
502       break;
503     }
504     case 'C':
505     case 'c':
506       report_configured_links(sptr, CONF_SERVER);
507       break;
508     case 'G':
509     case 'g': /* send glines */
510       gline_stats(sptr);
511       break;
512     case 'H':
513     case 'h':
514       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
515       break;
516     case 'I':
517     case 'i':
518     case 'K':
519     case 'k':                   /* display CONF_IPKILL as well
520                                    as CONF_KILL -Kev */
521     {
522       int wilds, count;
523       char *user, *host, *p;
524       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
525       if (IsOper(sptr) && parc < 4)
526       {
527         report_configured_links(sptr, conf_status);
528         break;
529       }
530       if (parc < 4 || *parv[3] == '\0')
531         return need_more_params(sptr,
532                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
533
534       wilds = 0;
535       for (p = parv[3]; *p; p++)
536       {
537         if (*p == '\\')
538         {
539           if (!*++p)
540             break;
541           continue;
542         }
543         if (*p == '?' || *p == '*')
544         {
545           wilds = 1;
546           break;
547         }
548       }
549       if (!IsOper(sptr))
550       {
551         wilds = 0;
552         count = 3;
553       }
554       else
555         count = 1000;
556
557       if (conf_status == CONF_CLIENT)
558       {
559         user = 0;            /* Not used, but to avoid compiler warning. */
560
561         host = parv[3];
562       }
563       else
564       {
565         if ((host = strchr(parv[3], '@')))
566         {
567           user = parv[3];
568           *host++ = 0;;
569         }
570         else
571         {
572           user = 0;
573           host = parv[3];
574         }
575       }
576       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
577       {
578         if ((aconf->status & conf_status))
579         {
580           if (conf_status == CONF_KLINE)
581           {
582             if ((!wilds && ((user || aconf->host[1]) &&
583                 !match(aconf->host, host) &&
584                 (!user || !match(aconf->name, user)))) ||
585                 (wilds && !mmatch(host, aconf->host) &&
586                 (!user || !mmatch(user, aconf->name))))
587             {
588               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
589                   sptr->name, (aconf->status & CONF_KILL) ? 'K' : 'k',
590                   aconf->host, aconf->passwd, aconf->name,
591                   aconf->port, get_conf_class(aconf));
592               if (--count == 0)
593                 break;
594             }
595           }
596           else if (conf_status == CONF_CLIENT)
597           {
598             if ((!wilds && (!match(aconf->host, host) ||
599                 !match(aconf->name, host))) ||
600                 (wilds && (!mmatch(host, aconf->host) ||
601                 !mmatch(host, aconf->name))))
602             {
603               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
604                   sptr->name, 'I', aconf->host, aconf->name,
605                   aconf->port, get_conf_class(aconf));
606               if (--count == 0)
607                 break;
608             }
609           }
610         }
611       }
612       break;
613     }
614     case 'M':
615 #if !defined(NDEBUG)
616       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
617           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
618 #endif
619
620 #if 0
621 #ifdef MEMSIZESTATS
622       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
623           me.name, parv[0], get_mem_size(), get_alloc_cnt());
624 #endif
625 #ifdef MEMLEAKSTATS
626       report_memleak_stats(sptr, parc, parv);
627 #endif
628 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
629       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
630           "is not enabled on this server", me.name, parv[0]);
631 #endif
632 #endif /* 0 */
633       break;
634     case 'm':
635       for (mptr = msgtab; mptr->cmd; mptr++)
636         if (mptr->count)
637           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
638               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
639       break;
640     case 'o':
641     case 'O':
642       report_configured_links(sptr, CONF_OPS);
643       break;
644     case 'p':
645     case 'P':
646       /*
647        * show listener ports
648        * show hidden ports to opers, if there are more than 3 parameters,
649        * interpret the fourth parameter as the port number, limit non-local
650        * or non-oper results to 8 ports.
651        */ 
652       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
653       break;
654     case 'R':
655     case 'r':
656 #ifdef DEBUGMODE
657       send_usage(sptr, parv[0]);
658 #endif
659       break;
660     case 'D':
661       report_configured_links(sptr, CONF_CRULEALL);
662       break;
663     case 'd':
664       report_configured_links(sptr, CONF_CRULE);
665       break;
666     case 't':
667       tstats(sptr, parv[0]);
668       break;
669     case 'T':
670       report_configured_links(sptr, CONF_TLINES);
671       break;
672     case 'U':
673       report_configured_links(sptr, CONF_UWORLD);
674       break;
675     case 'u':
676     {
677       time_t nowr;
678
679       nowr = CurrentTime - me.since;
680       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
681           nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
682       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
683           max_connection_count, max_client_count);
684       break;
685     }
686     case 'W':
687     case 'w':
688       calc_load(sptr);
689       break;
690     case 'X':
691     case 'x':
692 #ifdef  DEBUGMODE
693       send_listinfo(sptr, parv[0]);
694 #endif
695       break;
696     case 'Y':
697     case 'y':
698       report_classes(sptr);
699       break;
700     case 'Z':
701     case 'z':
702       count_memory(sptr, parv[0]);
703       break;
704     default:
705       stat = '*';
706       break;
707   }
708   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
709   return 0;
710 }
711
712 /*
713  * mo_stats - oper message handler
714  *
715  *    parv[0] = sender prefix
716  *    parv[1] = statistics selector (defaults to Message frequency)
717  *    parv[2] = target server (current server defaulted, if omitted)
718  * And 'stats l' and 'stats' L:
719  *    parv[3] = server mask ("*" defaulted, if omitted)
720  * Or for stats p,P:
721  *    parv[3] = port mask (returns p-lines when its port is matched by this)
722  * Or for stats k,K,i and I:
723  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
724  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
725  *              (defaults to old reply if ommitted, when local or Oper)
726  *              A remote mask (something containing wildcards) is only
727  *              allowed for IRC Operators.
728  * Or for stats M:
729  *    parv[3] = time param
730  *    parv[4] = time param 
731  *    (see report_memleak_stats() in runmalloc.c for details)
732  *
733  * This function is getting really ugly. -Ghostwolf
734  */
735 int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
736 {
737   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
738       "RcveM RcveKBytes :Open since";
739   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
740   struct Message *mptr;
741   struct Client *acptr;
742   struct ConfItem *aconf;
743   char stat = parc > 1 ? parv[1][0] : '\0';
744   const char **infotext = statsinfo;
745   int i;
746
747   if (hunt_stats(cptr, sptr, parc, parv, stat) != HUNTED_ISME)
748     return 0;
749
750   switch (stat)
751   {
752     case 'L':
753     case 'l':
754     {
755       int doall = 0, wilds = 0;
756       char *name = "*";
757       if (parc > 3 && *parv[3])
758       {
759         char *p;
760         name = parv[3];
761         wilds = (*name == '*' || *name == '?');
762         for (p = name + 1; *p; ++p)
763           if ((*p == '*' || *p == '?') && p[-1] != '\\')
764           {
765             wilds = 1;
766             break;
767           }
768       }
769       else
770         doall = 1;
771       /*
772        * Send info about connections which match, or all if the
773        * mask matches me.name.  Only restrictions are on those who
774        * are invisible not being visible to 'foreigners' who use
775        * a wild card based search to list it.
776        */
777       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
778       for (i = 0; i <= HighestFd; i++)
779       {
780         if (!(acptr = LocalClientArray[i]))
781           continue;
782         /* Don't return clients when this is a request for `all' */
783         if (doall && IsUser(acptr))
784           continue;
785         /* Only show the ones that match the given mask - if any */
786         if (!doall && wilds && match(name, acptr->name))
787           continue;
788         /* Skip all that do not match the specific query */
789         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
790           continue;
791         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
792             acptr->name, (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
793             (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
794             CurrentTime - acptr->firsttime);
795       }
796       break;
797     }
798     case 'C':
799     case 'c':
800       report_configured_links(sptr, CONF_SERVER);
801       break;
802     case 'G':
803     case 'g': /* send glines */
804       gline_stats(sptr);
805       break;
806     case 'H':
807     case 'h':
808       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
809       break;
810     case 'I':
811     case 'i':
812     case 'K':
813     case 'k':                   /* display CONF_IPKILL as well
814                                    as CONF_KILL -Kev */
815     {
816       int wilds, count;
817       char *user, *host, *p;
818       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
819       if (parc < 4)
820       {
821         report_configured_links(sptr, conf_status);
822         break;
823       }
824
825       wilds = 0;
826       for (p = parv[3]; *p; p++)
827       {
828         if (*p == '\\')
829         {
830           if (!*++p)
831             break;
832           continue;
833         }
834         if (*p == '?' || *p == '*')
835         {
836           wilds = 1;
837           break;
838         }
839       }
840
841       count = 1000;
842
843       if (conf_status == CONF_CLIENT)
844       {
845         user = 0;            /* Not used, but to avoid compiler warning. */
846
847         host = parv[3];
848       }
849       else
850       {
851         if ((host = strchr(parv[3], '@')))
852         {
853           user = parv[3];
854           *host++ = 0;;
855         }
856         else
857         {
858           user = 0;
859           host = parv[3];
860         }
861       }
862       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
863       {
864         if ((aconf->status & conf_status))
865         {
866           if (conf_status == CONF_KLINE)
867           {
868             if ((!wilds && ((user || aconf->host[1]) &&
869                 !match(aconf->host, host) &&
870                 (!user || !match(aconf->name, user)))) ||
871                 (wilds && !mmatch(host, aconf->host) &&
872                 (!user || !mmatch(user, aconf->name))))
873             {
874               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
875                   sptr->name, (aconf->status & CONF_KILL) ? 'K' : 'k',
876                   aconf->host, aconf->passwd, aconf->name,
877                   aconf->port, get_conf_class(aconf));
878               if (--count == 0)
879                 break;
880             }
881           }
882           else if (conf_status == CONF_CLIENT)
883           {
884             if ((!wilds && (!match(aconf->host, host) ||
885                 !match(aconf->name, host))) ||
886                 (wilds && (!mmatch(host, aconf->host) ||
887                 !mmatch(host, aconf->name))))
888             {
889               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
890                   sptr->name, 'I', aconf->host, aconf->name,
891                   aconf->port, get_conf_class(aconf));
892               if (--count == 0)
893                 break;
894             }
895           }
896         }
897       }
898       break;
899     }
900     case 'M':
901 #if !defined(NDEBUG)
902       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
903           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
904 #endif
905
906 #if 0
907 #ifdef MEMSIZESTATS
908       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
909           me.name, parv[0], get_mem_size(), get_alloc_cnt());
910 #endif
911 #ifdef MEMLEAKSTATS
912       report_memleak_stats(sptr, parc, parv);
913 #endif
914 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
915       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
916           "is not enabled on this server", me.name, parv[0]);
917 #endif
918 #endif /* 0 */
919       break;
920     case 'm':
921       for (mptr = msgtab; mptr->cmd; mptr++)
922         if (mptr->count)
923           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
924               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
925       break;
926     case 'o':
927     case 'O':
928       report_configured_links(sptr, CONF_OPS);
929       break;
930     case 'p':
931     case 'P':
932       /*
933        * show listener ports
934        * show hidden ports to opers, if there are more than 3 parameters,
935        * interpret the fourth parameter as the port number, limit non-local
936        * or non-oper results to 8 ports.
937        */ 
938       show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
939       break;
940     case 'R':
941     case 'r':
942 #ifdef DEBUGMODE
943       send_usage(sptr, parv[0]);
944 #endif
945       break;
946     case 'D':
947       report_configured_links(sptr, CONF_CRULEALL);
948       break;
949     case 'd':
950       report_configured_links(sptr, CONF_CRULE);
951       break;
952     case 't':
953       tstats(sptr, parv[0]);
954       break;
955     case 'T':
956       report_configured_links(sptr, CONF_TLINES);
957       break;
958     case 'U':
959       report_configured_links(sptr, CONF_UWORLD);
960       break;
961     case 'u':
962     {
963       time_t nowr;
964
965       nowr = CurrentTime - me.since;
966       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
967           nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
968       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
969           max_connection_count, max_client_count);
970       break;
971     }
972     case 'W':
973     case 'w':
974       calc_load(sptr);
975       break;
976     case 'X':
977     case 'x':
978 #ifdef  DEBUGMODE
979       send_listinfo(sptr, parv[0]);
980 #endif
981       break;
982     case 'Y':
983     case 'y':
984       report_classes(sptr);
985       break;
986     case 'Z':
987     case 'z':
988       count_memory(sptr, parv[0]);
989       break;
990     default:
991       stat = '*';
992       while (*infotext) sendto_one(sptr, ":%s NOTICE %s :%s", me.name, parv[0], *infotext++);
993       break;
994   }
995   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
996   return 0;
997 }
998
999 #if 0
1000 /*
1001  * m_stats
1002  *
1003  *    parv[0] = sender prefix
1004  *    parv[1] = statistics selector (defaults to Message frequency)
1005  *    parv[2] = target server (current server defaulted, if omitted)
1006  * And 'stats l' and 'stats' L:
1007  *    parv[3] = server mask ("*" defaulted, if omitted)
1008  * Or for stats p,P:
1009  *    parv[3] = port mask (returns p-lines when its port is matched by this)
1010  * Or for stats k,K,i and I:
1011  *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
1012  *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
1013  *              (defaults to old reply if ommitted, when local or Oper)
1014  *              A remote mask (something containing wildcards) is only
1015  *              allowed for IRC Operators.
1016  * Or for stats M:
1017  *    parv[3] = time param
1018  *    parv[4] = time param 
1019  *    (see report_memleak_stats() in runmalloc.c for details)
1020  *
1021  * This function is getting really ugly. -Ghostwolf
1022  */
1023 int m_stats(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1024 {
1025   static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
1026       "RcveM RcveKBytes :Open since";
1027   static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
1028   struct Message *mptr;
1029   struct Client *acptr;
1030   struct Gline* gline;
1031   struct ConfItem *aconf;
1032   char stat = parc > 1 ? parv[1][0] : '\0';
1033   int i;
1034
1035 /* m_stats is so obnoxiously full of special cases that the different
1036  * hunt_server() possiblites were becoming very messy. It now uses a
1037  * switch() so as to be easier to read and update as params change. 
1038  * -Ghostwolf 
1039  */
1040   switch (stat)
1041   {
1042       /* open to all, standard # of params */
1043     case 'U':
1044     case 'u':
1045     {
1046       if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1047           != HUNTED_ISME)
1048         return 0;
1049       break;
1050     }
1051
1052       /* open to all, varying # of params */
1053     case 'k':
1054     case 'K':
1055     case 'i':
1056     case 'I':
1057     case 'p':
1058     case 'P':
1059     {
1060       if (parc > 3)
1061       {
1062         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
1063             != HUNTED_ISME)
1064           return 0;
1065       }
1066       else
1067       {
1068         if (hunt_server(0, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1069             != HUNTED_ISME)
1070           return 0;
1071       }
1072       break;
1073     }
1074
1075       /* oper only, varying # of params */
1076     case 'l':
1077     case 'L':
1078     case 'M':
1079     {
1080       if (parc == 4)
1081       {
1082         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s :%s", 2, parc, parv)
1083             != HUNTED_ISME)
1084           return 0;
1085       }
1086       else if (parc > 4)
1087       {
1088         if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s %s %s :%s", 2, parc,
1089             parv) != HUNTED_ISME)
1090           return 0;
1091       }
1092       else if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1093           != HUNTED_ISME)
1094         return 0;
1095       break;
1096     }
1097
1098       /* oper only, standard # of params */
1099     default:
1100     {
1101       if (hunt_server(1, cptr, sptr, "%s%s " TOK_STATS " %s :%s", 2, parc, parv)
1102           != HUNTED_ISME)
1103         return 0;
1104       break;
1105     }
1106   }
1107
1108   switch (stat)
1109   {
1110     case 'L':
1111     case 'l':
1112     {
1113       int doall = 0, wilds = 0;
1114       char *name = "*";
1115       if (parc > 3 && *parv[3])
1116       {
1117         char *p;
1118         name = parv[3];
1119         wilds = (*name == '*' || *name == '?');
1120         for (p = name + 1; *p; ++p)
1121           if ((*p == '*' || *p == '?') && p[-1] != '\\')
1122           {
1123             wilds = 1;
1124             break;
1125           }
1126       }
1127       else
1128         doall = 1;
1129       /*
1130        * Send info about connections which match, or all if the
1131        * mask matches me.name.  Only restrictions are on those who
1132        * are invisible not being visible to 'foreigners' who use
1133        * a wild card based search to list it.
1134        */
1135       sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
1136       for (i = 0; i <= HighestFd; i++)
1137       {
1138         if (!(acptr = LocalClientArray[i]))
1139           continue;
1140         /* Don't return clients when this is a request for `all' */
1141         if (doall && IsUser(acptr))
1142           continue;
1143         /* Don't show invisible people to unauthorized people when using
1144          * wildcards  -- Is this still needed now /stats is oper only ? */
1145         if (IsInvisible(acptr) && (doall || wilds) &&
1146             !(MyConnect(sptr) && IsOper(sptr)) &&
1147             !IsAnOper(acptr) && (acptr != sptr))
1148           continue;
1149         /* Only show the ones that match the given mask - if any */
1150         if (!doall && wilds && match(name, acptr->name))
1151           continue;
1152         /* Skip all that do not match the specific query */
1153         if (!(doall || wilds) && 0 != ircd_strcmp(name, acptr->name))
1154           continue;
1155         sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
1156                    acptr->name,
1157                    (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
1158                    (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
1159                    CurrentTime - acptr->firsttime);
1160       }
1161       break;
1162     }
1163     case 'C':
1164     case 'c':
1165       report_configured_links(sptr, CONF_SERVER);
1166       break;
1167     case 'G':
1168     case 'g': /* send glines */
1169       gline_remove_expired(TStime());
1170       for (gline = GlobalGlineList; gline; gline = gline->next) {
1171         sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
1172                    sptr->name, 'G', gline->name, gline->host,
1173                    gline->expire, gline->reason);
1174       }
1175       break;
1176     case 'H':
1177     case 'h':
1178       report_configured_links(sptr, CONF_HUB | CONF_LEAF);
1179       break;
1180     case 'I':
1181     case 'i':
1182     case 'K':
1183     case 'k':                   /* display CONF_IPKILL as well
1184                                    as CONF_KILL -Kev */
1185     {
1186       int wilds, count;
1187       char *user, *host, *p;
1188       int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
1189       if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
1190       {
1191         report_configured_links(sptr, conf_status);
1192         break;
1193       }
1194       if (parc < 4 || *parv[3] == '\0')
1195         return need_more_params(sptr,
1196                         (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
1197
1198       wilds = 0;
1199       for (p = parv[3]; *p; p++)
1200       {
1201         if (*p == '\\')
1202         {
1203           if (!*++p)
1204             break;
1205           continue;
1206         }
1207         if (*p == '?' || *p == '*')
1208         {
1209           wilds = 1;
1210           break;
1211         }
1212       }
1213       if (!(MyConnect(sptr) || IsOper(sptr)))
1214       {
1215         wilds = 0;
1216         count = 3;
1217       }
1218       else
1219         count = 1000;
1220
1221       if (conf_status == CONF_CLIENT)
1222       {
1223         user = 0;            /* Not used, but to avoid compiler warning. */
1224
1225         host = parv[3];
1226       }
1227       else
1228       {
1229         if ((host = strchr(parv[3], '@')))
1230         {
1231           user = parv[3];
1232           *host++ = 0;;
1233         }
1234         else
1235         {
1236           user = 0;
1237           host = parv[3];
1238         }
1239       }
1240       for (aconf = GlobalConfList; aconf; aconf = aconf->next)
1241       {
1242         if ((aconf->status & conf_status))
1243         {
1244           if (conf_status == CONF_KLINE)
1245           {
1246             if ((!wilds && ((user || aconf->host[1]) &&
1247                 !match(aconf->host, host) &&
1248                 (!user || !match(aconf->name, user)))) ||
1249                 (wilds && !mmatch(host, aconf->host) &&
1250                 (!user || !mmatch(user, aconf->name))))
1251             {
1252               sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
1253                   sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
1254                   aconf->port, get_conf_class(aconf));
1255               if (--count == 0)
1256                 break;
1257             }
1258           }
1259           else if (conf_status == CONF_CLIENT)
1260           {
1261             if ((!wilds && (!match(aconf->host, host) ||
1262                 !match(aconf->name, host))) ||
1263                 (wilds && (!mmatch(host, aconf->host) ||
1264                 !mmatch(host, aconf->name))))
1265             {
1266               sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
1267                   sptr->name, 'I', aconf->host, aconf->name,
1268                   aconf->port, get_conf_class(aconf));
1269               if (--count == 0)
1270                 break;
1271             }
1272           }
1273         }
1274       }
1275       break;
1276     }
1277     case 'M':
1278 #if !defined(NDEBUG)
1279       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
1280           me.name, parv[0], fda_get_byte_count(), fda_get_block_count());
1281 #endif
1282
1283 #if 0
1284 #ifdef MEMSIZESTATS
1285       sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
1286           me.name, parv[0], get_mem_size(), get_alloc_cnt());
1287 #endif
1288 #ifdef MEMLEAKSTATS
1289       report_memleak_stats(sptr, parc, parv);
1290 #endif
1291 #if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
1292       sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
1293           "is not enabled on this server", me.name, parv[0]);
1294 #endif
1295 #endif /* 0 */
1296       break;
1297     case 'm':
1298       for (mptr = msgtab; mptr->cmd; mptr++)
1299         if (mptr->count)
1300           sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
1301               me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
1302       break;
1303     case 'o':
1304     case 'O':
1305       report_configured_links(sptr, CONF_OPS);
1306       break;
1307     case 'p':
1308     case 'P':
1309       /*
1310        * show listener ports
1311        * show hidden ports to opers, if there are more than 3 parameters,
1312        * interpret the fourth parameter as the port number, limit non-local
1313        * or non-oper results to 8 ports.
1314        */ 
1315       show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, 
1316                  (MyUser(sptr) || IsOper(sptr)) ? 100 : 8);
1317       break;
1318     case 'R':
1319     case 'r':
1320 #ifdef DEBUGMODE
1321       send_usage(sptr, parv[0]);
1322 #endif
1323       break;
1324     case 'D':
1325       report_configured_links(sptr, CONF_CRULEALL);
1326       break;
1327     case 'd':
1328       report_configured_links(sptr, CONF_CRULE);
1329       break;
1330     case 't':
1331       tstats(sptr, parv[0]);
1332       break;
1333     case 'T':
1334       report_configured_links(sptr, CONF_TLINES);
1335       break;
1336     case 'U':
1337       report_configured_links(sptr, CONF_UWORLD);
1338       break;
1339     case 'u':
1340     {
1341       time_t nowr;
1342
1343       nowr = CurrentTime - me.since;
1344       sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
1345                  nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
1346       sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
1347                  max_connection_count, max_client_count);
1348       break;
1349     }
1350     case 'W':
1351     case 'w':
1352       calc_load(sptr);
1353       break;
1354     case 'X':
1355     case 'x':
1356 #ifdef  DEBUGMODE
1357       send_listinfo(sptr, parv[0]);
1358 #endif
1359       break;
1360     case 'Y':
1361     case 'y':
1362       report_classes(sptr);
1363       break;
1364     case 'Z':
1365     case 'z':
1366       count_memory(sptr, parv[0]);
1367       break;
1368     default:
1369       stat = '*';
1370       break;
1371   }
1372   sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
1373   return 0;
1374 }
1375 #endif /* 0 */
1376
1377