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