Author: Run <carlo@alinoe.com>
[ircu2.10.12-pk.git] / ircd / s_misc.c
1 /*
2  * IRC - Internet Relay Chat, ircd/s_misc.c (formerly ircd/date.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
24 #include "sys.h"
25 #include <sys/stat.h>
26 #if HAVE_FCNTL_H
27 #include <fcntl.h>
28 #endif
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef USE_SYSLOG
33 #include <syslog.h>
34 #endif
35 #include "h.h"
36 #include "struct.h"
37 #include "s_serv.h"
38 #include "numeric.h"
39 #include "send.h"
40 #include "s_conf.h"
41 #include "s_misc.h"
42 #include "common.h"
43 #include "match.h"
44 #include "hash.h"
45 #include "s_bsd.h"
46 #include "res.h"
47 #include "list.h"
48 #include "ircd.h"
49 #include "s_ping.h"
50 #include "channel.h"
51 #include "s_err.h"
52 #include "support.h"
53 #include "userload.h"
54 #include "parse.h"
55 #include "s_user.h"
56 #include "numnicks.h"
57 #include "sprintf_irc.h"
58 #include "querycmds.h"
59 #include "IPcheck.h"
60
61 #include <assert.h>
62
63 RCSTAG_CC("$Id$");
64
65 static void exit_one_client(aClient *, char *);
66
67 static char *months[] = {
68   "January", "February", "March", "April",
69   "May", "June", "July", "August",
70   "September", "October", "November", "December"
71 };
72
73 static char *weekdays[] = {
74   "Sunday", "Monday", "Tuesday", "Wednesday",
75   "Thursday", "Friday", "Saturday"
76 };
77
78 /*
79  * stats stuff
80  */
81 struct stats ircst, *ircstp = &ircst;
82
83 char *date(time_t clock)
84 {
85   static char buf[80], plus;
86   Reg1 struct tm *lt, *gm;
87   struct tm gmbuf;
88   int minswest;
89
90   if (!clock)
91     clock = now;
92   gm = gmtime(&clock);
93   memcpy(&gmbuf, gm, sizeof(gmbuf));
94   gm = &gmbuf;
95   lt = localtime(&clock);
96
97   minswest = (gm->tm_hour - lt->tm_hour) * 60 + (gm->tm_min - lt->tm_min);
98   if (lt->tm_yday != gm->tm_yday)
99   {
100     if ((lt->tm_yday > gm->tm_yday && lt->tm_year == gm->tm_year) ||
101         (lt->tm_yday < gm->tm_yday && lt->tm_year != gm->tm_year))
102       minswest -= 24 * 60;
103     else
104       minswest += 24 * 60;
105   }
106
107   plus = (minswest > 0) ? '-' : '+';
108   if (minswest < 0)
109     minswest = -minswest;
110
111   sprintf(buf, "%s %s %d %d -- %02d:%02d %c%02d:%02d",
112       weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday,
113       1900 + lt->tm_year, lt->tm_hour, lt->tm_min,
114       plus, minswest / 60, minswest % 60);
115
116   return buf;
117 }
118
119 /*
120  * myctime
121  *
122  * This is like standard ctime()-function, but it zaps away
123  * the newline from the end of that string. Also, it takes
124  * the time value as parameter, instead of pointer to it.
125  * Note that it is necessary to copy the string to alternate
126  * buffer (who knows how ctime() implements it, maybe it statically
127  * has newline there and never 'refreshes' it -- zapping that
128  * might break things in other places...)
129  */
130 char *myctime(time_t value)
131 {
132   static char buf[28];
133   Reg1 char *p;
134
135   strcpy(buf, ctime(&value));
136   if ((p = strchr(buf, '\n')) != NULL)
137     *p = '\0';
138
139   return buf;
140 }
141
142 /*
143  *  get_client_name
144  *       Return the name of the client for various tracking and
145  *       admin purposes. The main purpose of this function is to
146  *       return the "socket host" name of the client, if that
147  *    differs from the advertised name (other than case).
148  *    But, this can be used to any client structure.
149  *
150  *    Returns:
151  *      "name[user@ip#.port]" if 'showip' is true;
152  *      "name[sockethost]", if name and sockhost are different and
153  *      showip is false; else
154  *      "name".
155  *
156  *  NOTE 1:
157  *    Watch out the allocation of "nbuf", if either sptr->name
158  *    or sptr->sockhost gets changed into pointers instead of
159  *    directly allocated within the structure...
160  *
161  *  NOTE 2:
162  *    Function return either a pointer to the structure (sptr) or
163  *    to internal buffer (nbuf). *NEVER* use the returned pointer
164  *    to modify what it points!!!
165  */
166 char *get_client_name(aClient *sptr, int showip)
167 {
168   static char nbuf[HOSTLEN * 2 + USERLEN + 5];
169
170   if (MyConnect(sptr))
171   {
172     if (IsUnixSocket(sptr))
173     {
174       if (showip)
175         sprintf_irc(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
176       else
177         sprintf_irc(nbuf, "%s[%s]", sptr->name, me.sockhost);
178     }
179     else
180     {
181       if (showip)
182         sprintf_irc(nbuf, "%s[%s@%s]", sptr->name,
183             (!(sptr->flags & FLAGS_GOTID)) ? "" :
184             sptr->username, inetntoa(sptr->ip));
185       else
186       {
187         if (strCasediff(sptr->name, sptr->sockhost))
188           sprintf_irc(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
189         else
190           return sptr->name;
191       }
192     }
193     return nbuf;
194   }
195   return sptr->name;
196 }
197
198 char *get_client_host(aClient *cptr)
199 {
200   static char nbuf[HOSTLEN * 2 + USERLEN + 5];
201
202   if (!MyConnect(cptr))
203     return cptr->name;
204   if (!cptr->hostp)
205     return get_client_name(cptr, FALSE);
206   if (IsUnixSocket(cptr))
207     sprintf_irc(nbuf, "%s[%s]", cptr->name, me.name);
208   else
209     sprintf(nbuf, "%s[%-.*s@%-.*s]", cptr->name, USERLEN,
210         (!(cptr->flags & FLAGS_GOTID)) ? "" : cptr->username,
211         HOSTLEN, cptr->hostp->h_name);
212   return nbuf;
213 }
214
215 /*
216  * Form sockhost such that if the host is of form user@host, only the host
217  * portion is copied.
218  */
219 void get_sockhost(aClient *cptr, char *host)
220 {
221   Reg3 char *s;
222   if ((s = strchr(host, '@')))
223     s++;
224   else
225     s = host;
226   strncpy(cptr->sockhost, s, sizeof(cptr->sockhost) - 1);
227 }
228
229 /*
230  * Return wildcard name of my server name according to given config entry
231  * --Jto
232  */
233 char *my_name_for_link(char *name, aConfItem *aconf)
234 {
235   static char namebuf[HOSTLEN];
236   register int count = aconf->port;
237   register char *start = name;
238
239   if (count <= 0 || count > 5)
240     return start;
241
242   while (count-- && name)
243   {
244     name++;
245     name = strchr(name, '.');
246   }
247   if (!name)
248     return start;
249
250   namebuf[0] = '*';
251   strncpy(&namebuf[1], name, HOSTLEN - 1);
252   namebuf[HOSTLEN - 1] = '\0';
253
254   return namebuf;
255 }
256
257 /*
258  * exit_downlinks - added by Run 25-9-94
259  *
260  * Removes all clients and downlinks (+clients) of any server
261  * QUITs are generated and sent to local users.
262  *
263  * cptr    : server that must have all dependents removed
264  * sptr    : source who thought that this was a good idea
265  * comment : comment sent as sign off message to local clients
266  */
267 static void exit_downlinks(aClient *cptr, aClient *sptr, char *comment)
268 {
269   Reg1 aClient *acptr;
270   Reg2 Dlink *next;
271   Reg3 Dlink *lp;
272   aClient **acptrp;
273   int i;
274
275   /* Run over all its downlinks */
276   for (lp = cptr->serv->down; lp; lp = next)
277   {
278     next = lp->next;
279     acptr = lp->value.cptr;
280     /* Remove the downlinks and client of the downlink */
281     exit_downlinks(acptr, sptr, comment);
282     /* Remove the downlink itself */
283     exit_one_client(acptr, me.name);
284   }
285   /* Remove all clients of this server */
286   acptrp = cptr->serv->client_list;
287   for (i = 0; i <= cptr->serv->nn_mask; ++acptrp, ++i)
288     if (*acptrp)
289       exit_one_client(*acptrp, comment);
290 }
291
292 /*
293  * exit_client, rewritten 25-9-94 by Run
294  *
295  * This function exits a client of *any* type (user, server, etc)
296  * from this server. Also, this generates all necessary prototol
297  * messages that this exit may cause.
298  *
299  * This function implicitly exits all other clients depending on
300  * this connection.
301  *
302  * For convenience, this function returns a suitable value for
303  * m_funtion return value:
304  *
305  *   CPTR_KILLED     if (cptr == bcptr)
306  *   0                if (cptr != bcptr)
307  *
308  * This function can be called in two ways:
309  * 1) From before or in parse(), exitting the 'cptr', in which case it was
310  *    invoked as exit_client(cptr, cptr, &me,...), causing it to always
311  *    return CPTR_KILLED.
312  * 2) Via parse from a m_function call, in which case it was invoked as
313  *    exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client
314  *    that generated the message in a way that we can assume he already
315  *    did remove acptr from memory himself (or in other cases we don't mind
316  *    because he will be delinked.) Or invoked as:
317  *    exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should
318  *    be removed.
319  * In general: No generated SQUIT or QUIT should be sent to source link
320  * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too).
321  *
322  * --Run
323  */
324 int exit_client(aClient *cptr,  /* Connection being handled by
325                                    read_message right now */
326     aClient *bcptr,             /* Client being killed */
327     aClient *sptr,              /* The client that made the decision
328                                    to remove this one, never NULL */
329     char *comment)              /* Reason for the exit */
330 {
331   Reg1 aClient *acptr;
332   Reg3 Dlink *dlp;
333 #ifdef  FNAME_USERLOG
334   time_t on_for;
335 #endif
336   char comment1[HOSTLEN + HOSTLEN + 2];
337
338   if (MyConnect(bcptr))
339   {
340     bcptr->flags |= FLAGS_CLOSING;
341 #ifdef ALLOW_SNO_CONNEXIT
342 #ifdef SNO_CONNEXIT_IP
343     if (IsUser(bcptr))
344     {
345       sprintf_irc(sendbuf,
346           ":%s NOTICE * :*** Notice -- Client exiting: %s (%s@%s) [%s] [%s]",
347           me.name, bcptr->name, bcptr->user->username, bcptr->user->host,
348           comment, inetntoa(bcptr->ip));
349       sendbufto_op_mask(SNO_CONNEXIT);
350     }
351 #else /* SNO_CONNEXIT_IP */
352     if (IsUser(bcptr))
353     {
354       sprintf_irc(sendbuf,
355           ":%s NOTICE * :*** Notice -- Client exiting: %s (%s@%s) [%s]",
356           me.name, bcptr->name, bcptr->user->username, bcptr->user->host,
357           comment);
358       sendbufto_op_mask(SNO_CONNEXIT);
359     }
360 #endif /* SNO_CONNEXIT_IP */
361 #endif /* ALLOW_SNO_CONNEXIT */
362     update_load();
363 #ifdef FNAME_USERLOG
364     on_for = now - bcptr->firsttime;
365 #if defined(USE_SYSLOG) && defined(SYSLOG_USERS)
366     if (IsUser(bcptr))
367       syslog(LOG_NOTICE, "%s (%3d:%02d:%02d): %s@%s (%s)\n",
368           myctime(bcptr->firsttime), on_for / 3600, (on_for % 3600) / 60,
369           on_for % 60, bcptr->user->username, bcptr->sockhost, bcptr->name);
370 #else
371     if (IsUser(bcptr))
372       write_log(FNAME_USERLOG,
373           "%s (%3d:%02d:%02d): %s@%s [%s]\n",
374           myctime(bcptr->firsttime),
375           on_for / 3600, (on_for % 3600) / 60,
376           on_for % 60,
377           bcptr->user->username, bcptr->user->host, bcptr->username);
378 #endif
379 #endif
380     if (bcptr != sptr->from     /* The source knows already */
381         && IsClient(bcptr))     /* Not a Ping struct or Log file */
382     {
383       if (IsServer(bcptr) || IsHandshake(bcptr))
384         sendto_one(bcptr, ":%s SQUIT %s 0 :%s", sptr->name, me.name, comment);
385       else if (!IsConnecting(bcptr))
386         sendto_one(bcptr, "ERROR :Closing Link: %s by %s (%s)",
387             get_client_name(bcptr, FALSE), sptr->name, comment);
388       if ((IsServer(bcptr) || IsHandshake(bcptr) || IsConnecting(bcptr)) &&
389           (sptr == &me || (IsServer(sptr) &&
390           (strncmp(comment, "Leaf-only link", 14) ||
391           strncmp(comment, "Non-Hub link", 12)))))
392       {
393         if (bcptr->serv->user && *bcptr->serv->by &&
394             (acptr = findNUser(bcptr->serv->by)) &&
395             acptr->user == bcptr->serv->user)
396         {
397           if (MyUser(acptr) || Protocol(acptr->from) < 10)
398             sendto_one(acptr,
399                 ":%s NOTICE %s :Link with %s cancelled: %s",
400                 me.name, acptr->name, bcptr->name, comment);
401           else
402             sendto_one(acptr,
403                 "%s NOTICE %s%s :Link with %s cancelled: %s",
404                 NumServ(&me), NumNick(acptr), bcptr->name, comment);
405         }
406         else
407           acptr = NULL;
408         if (sptr == &me)
409           sendto_lops_butone(acptr, "Link with %s cancelled: %s",
410               bcptr->name, comment);
411       }
412     }
413     /*
414      *  Close the Client connection first.
415      */
416     close_connection(bcptr);
417   }
418
419   if (IsServer(bcptr))
420   {
421     strcpy(comment1, bcptr->serv->up->name);
422     strcat(comment1, " ");
423     strcat(comment1, bcptr->name);
424     if (IsUser(sptr))
425       sendto_lops_butone(sptr, "%s SQUIT by %s [%s]:",
426           (sptr->user->server == bcptr ||
427           sptr->user->server == bcptr->serv->up) ? "Local" : "Remote",
428           get_client_name(sptr, TRUE), sptr->user->server->name);
429     else if (sptr != &me && bcptr->serv->up != sptr)
430       sendto_ops("Received SQUIT %s from %s :", bcptr->name,
431           IsServer(sptr) ? sptr->name : get_client_name(sptr, TRUE));
432     sendto_op_mask(SNO_NETWORK, "Net break: %s (%s)", comment1, comment);
433   }
434
435   /*
436    * First generate the needed protocol for the other server links
437    * except the source:
438    */
439   for (dlp = me.serv->down; dlp; dlp = dlp->next)
440     if (dlp->value.cptr != sptr->from && dlp->value.cptr != bcptr)
441     {
442       if (IsServer(bcptr))
443         sendto_one(dlp->value.cptr, ":%s SQUIT %s " TIME_T_FMT " :%s",
444             sptr->name, bcptr->name, bcptr->serv->timestamp, comment);
445       else if (IsUser(bcptr) && (bcptr->flags & FLAGS_KILLED) == 0)
446         sendto_one(dlp->value.cptr, ":%s QUIT :%s", bcptr->name, comment);
447     }
448
449   /* Then remove the client structures */
450   if (IsServer(bcptr))
451     exit_downlinks(bcptr, sptr, comment1);
452   exit_one_client(bcptr, comment);
453
454   /*
455    *  cptr can only have been killed if it was cptr itself that got killed here,
456    *  because cptr can never have been a dependant of bcptr    --Run
457    */
458   return (cptr == bcptr) ? CPTR_KILLED : 0;
459 }
460
461 /*
462  * Exit client with formatted message, added 25-9-94 by Run
463  */
464 int vexit_client_msg(aClient *cptr, aClient *bcptr, aClient *sptr,
465     char *pattern, va_list vl)
466 {
467   char msgbuf[1024];
468   vsprintf_irc(msgbuf, pattern, vl);
469   return exit_client(cptr, bcptr, sptr, msgbuf);
470 }
471
472 int exit_client_msg(aClient *cptr, aClient *bcptr,
473     aClient *sptr, char *pattern, ...)
474 {
475   va_list vl;
476   char msgbuf[1024];
477
478   va_start(vl, pattern);
479   vsprintf_irc(msgbuf, pattern, vl);
480   va_end(vl);
481
482   return exit_client(cptr, bcptr, sptr, msgbuf);
483 }
484
485 /*
486  * Exit one client, local or remote. Assuming for local client that
487  * all dependants already have been removed, and socket is closed.
488  *
489  * Rewritten by Run - 24 sept 94
490  *
491  * bcptr : client being (s)quitted
492  * sptr : The source (prefix) of the QUIT or SQUIT
493  *
494  * --Run
495  */
496 static void exit_one_client(aClient *bcptr, char *comment)
497 {
498   Link *lp;
499
500   if (bcptr->serv && bcptr->serv->client_list)  /* Was SetServerYXX called ? */
501     ClearServerYXX(bcptr);      /* Removes server from server_list[] */
502   if (IsUser(bcptr))
503   {
504     /* Stop a running /LIST clean */
505     if (MyUser(bcptr) && bcptr->listing)
506     {
507       bcptr->listing->chptr->mode.mode &= ~MODE_LISTED;
508       RunFree(bcptr->listing);
509       bcptr->listing = NULL;
510     }
511
512     if (AskedPing(bcptr))
513       cancel_ping(bcptr, NULL);
514     /*
515      * If a person is on a channel, send a QUIT notice
516      * to every client (person) on the same channel (so
517      * that the client can show the "**signoff" message).
518      * (Note: The notice is to the local clients *only*)
519      */
520     sendto_common_channels(bcptr, ":%s QUIT :%s", bcptr->name, comment);
521
522     while ((lp = bcptr->user->channel))
523       remove_user_from_channel(bcptr, lp->value.chptr);
524
525     /* Clean up invitefield */
526     while ((lp = bcptr->user->invited))
527       del_invite(bcptr, lp->value.chptr);
528
529     /* Clean up silencefield */
530     while ((lp = bcptr->user->silence))
531       del_silence(bcptr, lp->value.cp);
532
533     if (IsInvisible(bcptr))
534       --nrof.inv_clients;
535     if (IsOper(bcptr))
536       --nrof.opers;
537     if (MyConnect(bcptr))
538       Count_clientdisconnects(bcptr, nrof);
539     else
540       Count_remoteclientquits(nrof);
541   }
542   else if (IsServer(bcptr))
543   {
544     /* Remove downlink list node of uplink */
545     remove_dlink(&bcptr->serv->up->serv->down, bcptr->serv->updown);
546
547     if (MyConnect(bcptr))
548       Count_serverdisconnects(nrof);
549     else
550       Count_remoteserverquits(nrof);
551   }
552   else if (IsPing(bcptr))       /* Apperently, we are closing ALL links */
553   {
554     del_queries((char *)bcptr);
555     end_ping(bcptr);
556     return;
557   }
558   else if (IsMe(bcptr))
559   {
560     sendto_ops("ERROR: tried to exit me! : %s", comment);
561     return;                     /* ...must *never* exit self! */
562   }
563   else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr))
564     Count_unknowndisconnects(nrof);
565
566   /* Update IPregistry */
567   if (IsIPChecked(bcptr))
568     IPcheck_disconnect(bcptr);
569
570   /* 
571    * Remove from serv->client_list
572    * NOTE: user is *always* NULL if this is a server
573    */
574   if (bcptr->user)
575   {
576     assert(!IsServer(bcptr));
577     /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
578     RemoveYXXClient(bcptr->user->server, bcptr->yxx);
579   }
580
581   /* Remove bcptr from the client list */
582 #ifdef DEBUGMODE
583   if (hRemClient(bcptr) != 0)
584     Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p",
585         bcptr, bcptr->name, bcptr->from ? bcptr->from->sockhost : "??host",
586         bcptr->from, bcptr->next, bcptr->prev, bcptr->fd,
587         bcptr->status, bcptr->user));
588 #else
589   hRemClient(bcptr);
590 #endif
591   remove_client_from_list(bcptr);
592   return;
593 }
594
595 void checklist(void)
596 {
597   Reg1 aClient *acptr;
598   Reg2 int i, j;
599
600   if (!(bootopt & BOOT_AUTODIE))
601     return;
602   for (j = i = 0; i <= highest_fd; i++)
603     if (!(acptr = loc_clients[i]))
604       continue;
605     else if (IsUser(acptr))
606       j++;
607   if (!j)
608   {
609 #ifdef  USE_SYSLOG
610     syslog(LOG_WARNING, "ircd exiting: autodie");
611 #endif
612     exit(0);
613   }
614   return;
615 }
616
617 void initstats(void)
618 {
619   memset(&ircst, 0, sizeof(ircst));
620 }
621
622 void tstats(aClient *cptr, char *name)
623 {
624   Reg1 aClient *acptr;
625   Reg2 int i;
626   Reg3 struct stats *sp;
627   struct stats tmp;
628
629   sp = &tmp;
630   memcpy(sp, ircstp, sizeof(*sp));
631   for (i = 0; i < MAXCONNECTIONS; i++)
632   {
633     if (!(acptr = loc_clients[i]))
634       continue;
635     if (IsServer(acptr))
636     {
637       sp->is_sbs += acptr->sendB;
638       sp->is_sbr += acptr->receiveB;
639       sp->is_sks += acptr->sendK;
640       sp->is_skr += acptr->receiveK;
641       sp->is_sti += now - acptr->firsttime;
642       sp->is_sv++;
643       if (sp->is_sbs > 1023)
644       {
645         sp->is_sks += (sp->is_sbs >> 10);
646         sp->is_sbs &= 0x3ff;
647       }
648       if (sp->is_sbr > 1023)
649       {
650         sp->is_skr += (sp->is_sbr >> 10);
651         sp->is_sbr &= 0x3ff;
652       }
653     }
654     else if (IsUser(acptr))
655     {
656       sp->is_cbs += acptr->sendB;
657       sp->is_cbr += acptr->receiveB;
658       sp->is_cks += acptr->sendK;
659       sp->is_ckr += acptr->receiveK;
660       sp->is_cti += now - acptr->firsttime;
661       sp->is_cl++;
662       if (sp->is_cbs > 1023)
663       {
664         sp->is_cks += (sp->is_cbs >> 10);
665         sp->is_cbs &= 0x3ff;
666       }
667       if (sp->is_cbr > 1023)
668       {
669         sp->is_ckr += (sp->is_cbr >> 10);
670         sp->is_cbr &= 0x3ff;
671       }
672     }
673     else if (IsUnknown(acptr))
674       sp->is_ni++;
675   }
676
677   sendto_one(cptr, ":%s %d %s :accepts %u refused %u",
678       me.name, RPL_STATSDEBUG, name, sp->is_ac, sp->is_ref);
679   sendto_one(cptr, ":%s %d %s :unknown commands %u prefixes %u",
680       me.name, RPL_STATSDEBUG, name, sp->is_unco, sp->is_unpf);
681   sendto_one(cptr, ":%s %d %s :nick collisions %u unknown closes %u",
682       me.name, RPL_STATSDEBUG, name, sp->is_kill, sp->is_ni);
683   sendto_one(cptr, ":%s %d %s :wrong direction %u empty %u",
684       me.name, RPL_STATSDEBUG, name, sp->is_wrdi, sp->is_empt);
685   sendto_one(cptr, ":%s %d %s :numerics seen %u mode fakes %u",
686       me.name, RPL_STATSDEBUG, name, sp->is_num, sp->is_fake);
687   sendto_one(cptr, ":%s %d %s :auth successes %u fails %u",
688       me.name, RPL_STATSDEBUG, name, sp->is_asuc, sp->is_abad);
689   sendto_one(cptr, ":%s %d %s :local connections %u udp packets %u",
690       me.name, RPL_STATSDEBUG, name, sp->is_loc, sp->is_udp);
691   sendto_one(cptr, ":%s %d %s :Client Server", me.name, RPL_STATSDEBUG, name);
692   sendto_one(cptr, ":%s %d %s :connected %u %u",
693       me.name, RPL_STATSDEBUG, name, sp->is_cl, sp->is_sv);
694   sendto_one(cptr, ":%s %d %s :bytes sent %u.%uK %u.%uK",
695       me.name, RPL_STATSDEBUG, name,
696       sp->is_cks, sp->is_cbs, sp->is_sks, sp->is_sbs);
697   sendto_one(cptr, ":%s %d %s :bytes recv %u.%uK %u.%uK",
698       me.name, RPL_STATSDEBUG, name,
699       sp->is_ckr, sp->is_cbr, sp->is_skr, sp->is_sbr);
700   sendto_one(cptr, ":%s %d %s :time connected " TIME_T_FMT " " TIME_T_FMT,
701       me.name, RPL_STATSDEBUG, name, sp->is_cti, sp->is_sti);
702 }