1 #ifdef RCS
2 static char rcsid[]="$Id: transfer.c,v 1.1.1.1 2000/11/13 02:42:49 holsta Exp $";
3 #endif
4 /******************************************************************************
5  *                    Internetting Cooperating Programmers
6  * ----------------------------------------------------------------------------
7  *
8  *  ____    PROJECT
9  * |  _ \  __ _ _ __   ___ ___ _ __
10  * | | | |/ _` | '_ \ / __/ _ \ '__|
11  * | |_| | (_| | | | | (_|  __/ |
12  * |____/ \__,_|_| |_|\___\___|_|   the IRC bot
13  *
14  * All files in this archive are subject to the GNU General Public License.
15  *
16  * $Source: /cvsroot/dancer/dancer/src/transfer.c,v $
17  * $Revision: 1.1.1.1 $
18  * $Date: 2000/11/13 02:42:49 $
19  * $Author: holsta $
20  * $State: Exp $
21  * $Locker:  $
22  *
23  * ---------------------------------------------------------------------------
24  *****************************************************************************/
25 
26 #include "dancer.h"
27 #include "trio.h"
28 #include "strio.h"
29 #include "function.h"
30 #include "user.h"
31 #include "transfer.h"
32 #include "netstuff.h"
33 #include "seen.h"
34 #include "servfunc.h"
35 #include "link.h"
36 #include "flood.h"
37 #include "bans.h"
38 #include "command.h"
39 #include "ctcp.h"
40 
41 #include <stdarg.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 
47 extern time_t now;
48 
49 extern char nickname[];
50 extern char channel[];
51 extern char logfile[];
52 extern char botmatch[];
53 extern bool botop;
54 extern bool chat;
55 extern bool connected;
56 extern bool cleanup;
57 extern bool restart;
58 extern bool execprotect;
59 extern int lastpost;
60 extern fd_set rdset;
61 extern itemclient *client;
62 extern itemclient *clientHead;
63 extern itemguest *guestHead;
64 extern itemident *current;
65 
66 struct Execstruct ExecHead = {
67   NULL, NULL, NULL, NULL, -1, NULL, FALSE
68 };
69 
70 struct Msgstruct MsgHead = {
71   NULL, NULL, NULL, NULL
72 };
73 
74 int kickQ = 0; /* Number of kicks in the queue */
75 
76 itemexec *execHead = &ExecHead;
77 itemmsg *msgHead = &MsgHead;
78 
79 
80 /* --- SendNick --------------------------------------------------- */
81 /* Send directly to nick */
82 
SendNick(char * nick,char * msg)83 inline void SendNick(char *nick, char *msg)
84 {
85   itemmsg *m;
86 
87   snapshot;
88   m = NewEntry(itemmsg);
89   if (m) {
90     InsertLast(msgHead, m);
91     m->nick = StrDuplicate(nick);
92     m->msg = StrDuplicate(msg);
93   }
94 }
95 
SendNickf(char * nick,const char * format,...)96 void SendNickf(char *nick, const char *format, ...)
97 {
98   char buffer[BIGBUFFER];
99   va_list args;
100 
101   snapshot;
102 
103   va_start(args, format);
104   trio_vsnprintf(buffer, sizeof(buffer), format, args);
105   va_end(args);
106 
107   SendNick(nick, buffer);
108 }
109 
110 /* --- Send ------------------------------------------------------- */
111 /* Send to client if possible, else to nick */
112 
Send(char * nick,char * msg)113 void Send(char *nick, char *msg)
114 {
115   snapshot;
116   if (msg) {
117     if (chat) {
118       if (client->status == CL_CONNECTED) {
119 	/* Ship immediately to current client */
120 	WriteSocket(client->socket, msg);
121       }
122       else {
123 	SendNick(client->ident->nick, msg);
124       }
125     }
126     else if (nick) {
127       /* This is only /msg, don't flood */
128       SendNick(nick, msg);
129     }
130   }
131 }
132 
Sendf(char * nick,const char * format,...)133 void Sendf(char *nick, const char *format, ...)
134 {
135   char buffer[BIGBUFFER];
136   va_list args;
137 
138   snapshot;
139 
140   va_start(args, format);
141   trio_vsnprintf(buffer, sizeof(buffer), format, args);
142   va_end(args);
143 
144   Send(nick, buffer);
145 }
146 
147 /* --- SendMulti -------------------------------------------------- */
148 
SendMulti(char * nick,const char * format,...)149 void SendMulti(char *nick, const char *format, ...)
150 {
151   char *buffer, *pointer;
152   size_t size = MAXLINE - 10; /* Preserve space for CR-LF and some extra */
153   va_list args;
154 
155   snapshot;
156 
157   va_start(args, format);
158   buffer = trio_vaprintf(format, args);
159   va_end(args);
160 
161   if (buffer) {
162     if (nick) {
163       /* Preserve space for ":<botmatch> NOTICE <from> :" */
164       size -= (StrLength(botmatch) + StrLength(nick) + 20);
165     }
166 
167     for (pointer = StrSplitMax(buffer, size); pointer;
168          pointer = StrSplitMax(NULL, size)) {
169       Send(nick, pointer);
170     }
171 
172     free(buffer);
173   }
174 }
175 
176 /* --- SendCtcp --------------------------------------------------- */
177 
ReplyCtcp(char * nick,char * msg)178 void ReplyCtcp(char *nick, char *msg)
179 {
180   char buffer[BIGBUFFER];
181 
182   snapshot;
183   CtcpQuote(buffer, sizeof(buffer), msg);
184   WriteServer("NOTICE %s :\001%s\001", nick, buffer);
185 }
186 
ReplyCtcpf(char * nick,const char * format,...)187 void ReplyCtcpf(char *nick, const char *format, ...)
188 {
189   char buffer[BIGBUFFER];
190   va_list args;
191 
192   snapshot;
193 
194   va_start(args, format);
195   trio_vsnprintf(buffer, sizeof(buffer), format, args);
196   va_end(args);
197 
198   ReplyCtcp(nick, buffer);
199 }
200 
SendCtcp(char * nick,char * msg)201 void SendCtcp(char *nick, char *msg)
202 {
203   char buffer[BIGBUFFER];
204 
205   snapshot;
206   CtcpQuote(buffer, sizeof(buffer), msg);
207   WriteServer("PRIVMSG %s :\001%s\001", nick, buffer);
208 }
209 
SendCtcpf(char * nick,const char * format,...)210 void SendCtcpf(char *nick, const char *format, ...)
211 {
212   char buffer[BIGBUFFER];
213   va_list args;
214 
215   snapshot;
216 
217   va_start(args, format);
218   trio_vsnprintf(buffer, sizeof(buffer), format, args);
219   va_end(args);
220 
221   SendCtcp(nick, buffer);
222 }
223 
224 /* --- Multicast -------------------------------------------------- */
225 /* types: see *CAST in header file */
226 
MulticastLocal(int type,char * msg)227 void MulticastLocal(int type, char *msg)
228 {
229   itemclient *p;
230 
231   for (p = First(clientHead); p; p = Next(p)) {
232     if ((type&CHATCAST) &&         /* this is a CHAT message */
233         current &&
234         current->client &&         /* the current user has a client connected */
235         (current->client == p) &&  /* this client is the current user's */
236         !p->chatecho)              /* this client has turned off echo */
237       continue; /* don't send anything to this client */
238     if ((p->status == CL_CONNECTED) && (p->flags & type)) {
239       WriteSocket(p->socket, msg);
240     }
241   }
242 }
243 
MulticastLocalf(int type,const char * format,...)244 void MulticastLocalf(int type, const char *format, ...)
245 {
246   char buffer[BIGGERBUFFER];
247   va_list args;
248 
249   va_start(args, format);
250   trio_vsnprintf(buffer, sizeof(buffer), format, args);
251   va_end(args);
252 
253   MulticastLocal(type, buffer);
254 }
255 
Multicast(int type,char * msg)256 void Multicast(int type, char *msg)
257 {
258   MulticastLocal(type, msg);
259   SendLinkAll(IBCP_MULTICAST, "%d %s", type, msg);
260 }
261 
Multicastf(int type,const char * format,...)262 void Multicastf(int type, const char *format, ...)
263 {
264   char buffer[BIGGERBUFFER];
265   va_list args;
266 
267   va_start(args, format);
268   trio_vsnprintf(buffer, sizeof(buffer), format, args);
269   va_end(args);
270 
271   Multicast(type, buffer);
272 }
273 
274 /* --- FreeMessage ------------------------------------------------ */
275 
FreeMessage(void * v)276 void FreeMessage(void *v)
277 {
278   itemmsg *m;
279 
280   snapshot;
281   m = (itemmsg *)v;
282   if (m) {
283     if (m->nick)
284       StrFree(m->nick);
285     if (m->msg)
286       StrFree(m->msg);
287   }
288 }
289 
FlushMessages(void)290 void FlushMessages(void)
291 {
292   FlushList(msgHead, FreeMessage);
293 }
294 
295 /* --- MessageQueue ----------------------------------------------- */
296 /* Used to send delayed messages */
297 
MessageQueue(void)298 void MessageQueue(void)
299 {
300   static time_t lastsent = 0;
301   static int sentmsgs = 0;
302   itemmsg *m;
303 
304   snapshot;
305   if (connected) {
306     if (kickQ && botop) {
307       if (!KickFromQueue(&lastsent))
308         kickQ = 0; /* cleared */
309     }
310     else {
311       AlertMode(ALERT_OFF); /* Switches off alert mode with timeout */
312       /*
313        * Send queued messages if present and we haven't sent too many too fast
314        */
315       for (m = First(msgHead); m && (sentmsgs < 3); m = First(msgHead), sentmsgs++) {
316         WriteNick(m->nick, m->msg);
317         lastsent = now;
318         DeleteEntry(msgHead, m, FreeMessage);
319       }
320     }
321 
322     if ((sentmsgs > 0) && ((lastsent + 2) <= now)) {
323       sentmsgs--;
324     }
325   }
326 }
327 
328 /* --- MessageReaper ---------------------------------------------- */
329 
MessageReaper(char * target)330 void MessageReaper(char *target) /* Removes all messages to 'target' */
331 {
332   itemmsg *m, *next;
333 
334   snapshot;
335   for (m = First(msgHead); m; m = next) {
336     next = Next(m);
337 
338     if (StrEqual(m->nick, target))
339       DeleteEntry(msgHead, m, FreeMessage);
340   }
341 }
342 
343 /* --- Execute ---------------------------------------------------- */
344 /* Remember to quote ALL user input to avoid cracking.
345  * Remember to redirect stderr to stdout by appending "2>&1"
346  */
347 
Exec(char * nick,char * cmd)348 itemexec *Exec(char *nick, char *cmd)
349 {
350   itemexec *px;
351   FILE *f;
352 
353   snapshot;
354   f = popen(cmd, "r");
355   if (f) {
356     px = NewEntry(itemexec);
357     if (px) {
358       InsertLast(execHead, px);
359       px->client = client;
360       if (nick)
361         px->nick = StrDuplicate(nick);
362       px->pipe = f;
363       px->socket = fileno(px->pipe);
364 
365       FD_SET(px->socket, &rdset);
366       return px;
367     }
368   }
369   return NULL;
370 }
371 
IsQualifier(char c)372 bool IsQualifier(char c)
373 {
374   snapshot;
375   switch (c) {
376     case '-': case '+': case ' ': case '#': case '.':
377     case '0': case '1': case '2': case '3': case '4':
378     case '5': case '6': case '7': case '8': case '9':
379     case 'h': case 'l': case 'L':
380       return TRUE;
381   }
382   return FALSE;
383 }
384 
Execute(char * nick,char * format,...)385 itemexec *Execute(char *nick, char *format, ...)
386 {
387   char buffer[BIGGERBUFFER];
388   char *pointer, *p;
389   bool wrong = FALSE;
390   va_list args;
391 
392   snapshot;
393   if (execprotect) {
394     Send(nick, GetText(msg_execprotect_on));
395     return NULL;
396   }
397 
398   /*
399    * Search string arguments for end-quotes.
400    * These checks are done to avoid cracking.
401    * Note: if user input isn't quoted they can
402    *       access ';', '&', '|' and '^' special
403    *       characters.
404    */
405   va_start(args, format);
406   for (pointer = format; *pointer; pointer++) {
407     if ('%' == *pointer) {
408       for (pointer++; IsQualifier(*pointer); pointer++);
409 
410       if ((char)0 == *pointer)
411         break;
412 
413       switch (*pointer) {
414 
415         /* Examine strings */
416         case 's':
417           p = va_arg(args, char *);
418           while (*p) {
419             switch (*p++) {
420 
421               case '"':
422               case '`':
423 #if 0
424               case ';':
425               case '&':
426               case '|':
427               case '^':
428 #endif
429 
430                 wrong = TRUE;
431                 break;
432 
433               default:
434                 break;
435             }
436           }
437           break;
438 
439         /* Ignore the rest */
440         case 'e': case 'E':
441         case 'g': case 'G':
442         case 'f':
443           va_arg(args, double);
444           break;
445 
446         case 'd': case 'i':
447         case 'o': case 'u':
448         case 'x': case 'X':
449         case 'c':
450           va_arg(args, int);
451           break;
452 
453         case 'p':
454           va_arg(args, void *);
455           break;
456 
457         default:
458           va_arg(args, void *);
459           break;
460       }
461     }
462   }
463 
464   if (wrong) {
465     Send(nick, GetText(msg_cannot_do_that));
466   }
467   else {
468     va_start(args, format);
469     trio_vsnprintf(buffer, sizeof(buffer), format, args);
470     va_end(args);
471 
472     return Exec(nick, buffer);
473   }
474 
475   return NULL;
476 }
477 
FreeExec(void * v)478 void FreeExec(void *v)
479 {
480   itemexec *px;
481 
482   px = (itemexec *)v;
483   if (v) {
484     if (px->nick)
485       StrFree(px->nick);
486     if ((0 <= px->socket) && FD_ISSET(px->socket, &rdset))
487       FD_CLR(px->socket, &rdset);
488     if (px->pipe)
489       pclose(px->pipe);
490   }
491 }
492 
493 /* --- GotExec ---------------------------------------------------- */
494 /* Ought to check if socket is still active */
495 
GotExec(itemexec * px)496 void GotExec(itemexec *px)
497 {
498   char buffer[MAXLINE];
499 
500   snapshot;
501   chat = (px->client != NULL);
502 
503   if (fgets(buffer, sizeof(buffer), px->pipe)) {
504     if (StrTokenize(buffer, "\r\n") == buffer) {
505       client = px->client;
506       Send(px->nick, buffer);
507     }
508   }
509   else {
510     if (px->notify) {
511       /* This is a file transfer client, we should therefor set the END OF
512          TRANSFER bit in that struct to let it know */
513       itemclient *c;
514 
515       for (c = First(clientHead); c; c = Next(c)) {
516         if (c->ident && StrEqual(c->ident->nick, px->nick) &&
517             (OUT_SEND == c->linksort)) { /* Get the send, nothing else */
518           c->fileflags |= CLF_FILECOMPLETE;
519           break;
520         }
521       }
522     }
523 
524     client = px->client;
525     if (client) {
526       Send(NULL, GetText(msg_done));
527     }
528     else if (px->nick && !IsChannel(px->nick)) {
529       Send(px->nick, GetText(msg_done));
530     }
531 
532     DeleteEntry(execHead, px, FreeExec);
533   }
534 }
535 
536 /* --- SnoopCommand ----------------------------------------------- */
537 
SnoopCommand(char * from,char * cmd,char * param)538 void SnoopCommand(char *from, char *cmd, char *param)
539 {
540   char buffer[BIGGERBUFFER];
541   char *who;
542   struct Command *command;
543 
544   snapshot;
545 
546   /* Skip if stealth user or no clients are attached */
547   if (First(clientHead) && current && ((current->user &&
548       !current->user->flags.stealth) || !current->user)) {
549 
550     who = (from ? from : client->ident->nick);
551     if (cmd && *cmd) {
552       command = FindCommand(cmd);
553       /* Filter certain command from spylink */
554       if (command && command->hide) {
555         StrFormatMax(buffer, sizeof(buffer), GetDefaultText(msg_spy_hidden_command),
556                      who, cmd, GetDefaultText(msg_snoop_prevented));
557       }
558       else {
559         StrFormatMax(buffer, sizeof(buffer), GetDefaultText(msg_spy_who_did_what),
560                      who, cmd, *param ? " " : "", param);
561       }
562       Multicast(SPYCAST, buffer);
563     }
564   }
565 }
566 
567 /* --- Say -------------------------------------------------------- */
568 
Say(char * msg)569 void Say(char *msg)
570 {
571   snapshot;
572   WriteServer("PRIVMSG %s :%s", channel, msg);
573   lastpost = now;
574 }
575 
Sayf(const char * format,...)576 void Sayf(const char *format, ...)
577 {
578   char buffer[BIGBUFFER];
579   va_list args;
580 
581   snapshot;
582 
583   va_start(args, format);
584   trio_vsnprintf(buffer, sizeof(buffer), format, args);
585   va_end(args);
586 
587   Say(buffer);
588 }
589 
590 /* --- Action ----------------------------------------------------- */
591 
Action(char * msg)592 void Action(char *msg)
593 {
594   snapshot;
595 
596   /* If we don't send this raw we can't use i.e control codes etc */
597   WriteServer("PRIVMSG %s :\001ACTION %s\001", channel, msg);
598   lastpost = now;
599 }
600 
Actionf(const char * format,...)601 void Actionf(const char *format, ...)
602 {
603   char buffer[BIGBUFFER];
604   va_list args;
605 
606   snapshot;
607 
608   va_start(args, format);
609   trio_vsnprintf(buffer, sizeof(buffer), format, args);
610   va_end(args);
611 
612   Action(buffer);
613 }
614 
615 /* --- Mode ------------------------------------------------------- */
616 
Mode(const char * format,...)617 void Mode(const char *format, ...)
618 {
619   char buffer[BIGBUFFER];
620   va_list args;
621 
622   snapshot;
623 
624   va_start(args, format);
625   trio_vsnprintf(buffer, sizeof(buffer), format, args);
626   va_end(args);
627 
628   WriteServer("MODE %s %s", channel, buffer);
629   Logf(LOGDBUG, "sent MODE %s %s", channel, buffer);
630 }
631 
632 /* --- Kick ------------------------------------------------------- */
633 
634 time_t kicklast = 0;
635 int kickcount = 0;
636 
Kick(char * nick,char * msg)637 void Kick(char *nick, char *msg)
638 {
639   itemguest *w;
640 
641   snapshot;
642   if ((kicklast + 1) >= now)
643     kickcount++;
644   else
645     kickcount = 0;
646 
647   w = FindNick(nick);
648   if (NULL == w)
649     return;
650 
651   w->flags.kick = TRUE;
652   w->kicktime = now;
653 
654   if (!botop || kickQ || (kickcount > 2)) {
655     kickQ++; /* One more in the pipe */
656 
657     if (NULL == w->kickmsg) /* Only get the first message */
658       w->kickmsg = StrDuplicate(msg);
659 
660     /*
661      * Stress situation, stop replying to CTCPs for a little while!
662      * We do this is we're not chanops too, since then we'll be able to
663      * kick this person as soon as we get opped! ;)
664      */
665     CTCPignore();
666     Logf(LOGDBUG, "Enqueued KICK %s", nick);
667   }
668   else {
669     if (!w->flags.kicked) {
670       Logf(LOGDBUG, "Sent KICK %s to server", nick);
671       WriteServer("KICK %s %s :%s", channel, nick, (msg ? msg : nickname));
672       w->flags.kicked = TRUE;
673     }
674     kicklast = now;
675   }
676 }
677 
StickyKick(itemguest * w,char * msg)678 void StickyKick(itemguest *w, char *msg)
679 {
680   snapshot;
681   AddKick(w->ident, nickname, msg, KICK_BOT);
682 
683   if (!w->flags.kick)
684     Kick(w->ident->nick, msg);
685 #if 0
686   else
687     Logf(LOGDBUG, "Held back KICK %s (already issued)", w->ident->nick);
688 #endif
689 }
690 
691 /* --- Invite ----------------------------------------------------- */
692 
Invite(char * who)693 void Invite(char *who)
694 {
695   snapshot;
696   WriteServer("INVITE %s %s", who, channel);
697 }
698 
699 /* --- Log -------------------------------------------------------- */
700 
701 int lastlogday = -1;
702 int logdays = 10;
703 ulong activelog = -1;
704 
RenameAndDelete(void)705 static void RenameAndDelete(void)
706 {
707   char buffer[BIGBUFFER];
708   time_t then = now;
709   struct tm *t;
710 
711   snapshot;
712   then -= (SECINDAY); /* 24 hours ago we were in yesterday land */
713   t = localtime(&then);
714   StrFormatMax(buffer, sizeof(buffer), "%s.%d%02d%02d",
715                logfile, 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday);
716   rename(logfile, buffer); /* Rename yesterday's logfile */
717 
718   then -= (SECINDAY*logdays); /* Even further back */
719   t = localtime(&then); /* Get the date to delete */
720   StrFormatMax(buffer, sizeof(buffer), "%s.%d%02d%02d",
721                logfile, 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday);
722   remove(buffer); /* Remove the old one */
723 }
724 
LogInit(void)725 void LogInit(void)
726 {
727   struct stat stbuf;
728   struct tm *t;
729 
730   snapshot;
731   t = localtime(&now);
732   lastlogday = t->tm_yday;
733 
734   if (logfile[0]) {
735     if (-1 != stat(logfile, &stbuf)) {
736       t = localtime(&stbuf.st_mtime);
737       if (t->tm_yday != lastlogday) /* Backup logfile */
738         RenameAndDelete();
739     }
740   }
741 }
742 
743 /* See enum in header */
744 char *logtypes[] = {
745   "***",    "Join",   "Part",
746   "Quit",   "Nick",   "Mode",
747   "Kick",   ">>>",    "#>>",
748   "-->",    "Ctcp",   "Client",
749   "Warn",   "NSplit", "NJoin",
750   "NHeal",  "DEBUG",  "DBUG",
751   "INIT",   "#",
752   "Kill",   "Topic",
753   NULL
754 };
755 
Log(int type,char * buffer)756 void Log(int type, char *buffer)
757 {
758   extern char servername[];
759   extern time_t uptime;
760   bool logfile_exists;
761   struct tm *t;
762   FILE *f;
763 
764   t = localtime(&now);
765 
766 #if defined(DBUG)
767   printf("%02d.%02d.%02d %-7s %s\n",
768          t->tm_hour, t->tm_min, t->tm_sec, logtypes[type], buffer);
769 
770 #else /* !DBUG */
771 
772   if ((activelog & (1 << type)) && (-1 != lastlogday) && logfile[0]) {
773     logfile_exists = FileExist(logfile);
774     if (lastlogday != t->tm_yday) {
775       lastlogday = t->tm_yday;
776       if (logfile_exists) {
777         RenameAndDelete();   /* Timestamp logfile */
778         t = localtime(&now); /* struct tm is static date that the
779                                 RenameAndDelete() ruined */
780         logfile_exists = FALSE;
781       }
782     }
783 
784     f = fopen(logfile, "a");
785     if (f) {
786       if (!logfile_exists) {
787         fprintf(f,
788                 "\n"
789                 "--- Log for %02d.%02d.%d  Server: %s  Channel: %s\n"
790                 "--- Nick: %s%s  Version: %s  Started: %s ago\n"
791                 "\n",
792                 t->tm_mday, t->tm_mon + 1, t->tm_year,
793                 servername[0] ? servername : "Not connected",
794                 (servername[0] && channel[0]) ? channel : "None joined",
795                 botop ? "@" : "",
796                 nickname[0] ? nickname : "No nick",
797                 VERSIONMSG, TimeAgo(uptime));
798       }
799 
800       fprintf(f, "%02d.%02d.%02d %-7s %s\n",
801               t->tm_hour, t->tm_min, t->tm_sec, logtypes[type], buffer);
802 
803       fclose(f);
804     }
805   }
806   else if (-1 == lastlogday) {
807     fprintf(stderr, "%02d.%02d.%02d %-7s %s\n",
808             t->tm_hour, t->tm_min, t->tm_sec, logtypes[type], buffer);
809   }
810 #endif
811 }
812 
Logf(int type,const char * format,...)813 void Logf(int type, const char *format, ...)
814 {
815   char buffer[BIGGERBUFFER];
816   va_list args;
817 
818   va_start(args, format);
819   trio_vsnprintf(buffer, sizeof(buffer), format, args);
820   va_end(args);
821 
822   Log(type, buffer);
823 }
824 
825 /* --- Snapshot --------------------------------------------------- */
826 
827 #ifdef DEBUG
828 #define MAXSNAP 20
829 
830 static int snapnum = MAXSNAP - 1;
831 
832 static struct snapstructure {
833   char *file;
834   int line;
835   int hits;
836 } snap[MAXSNAP];
837 
MakeSnapshot(char * file,int line)838 void MakeSnapshot(char *file, int line)
839 {
840   if ((snap[snapnum].line != line) ||
841       (snap[snapnum].file != file)) {
842     if (++snapnum >= MAXSNAP)
843       snapnum = 0;
844     snap[snapnum].file = file;
845     snap[snapnum].line = line;
846     snap[snapnum].hits = 1;
847   }
848   else
849     snap[snapnum].hits++;
850 }
851 
LogSnapshots(void)852 void LogSnapshots(void)
853 {
854   char *buffer;
855   int fd, amount, index, i;
856 
857   if (logfile[0]) {
858     fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
859     if (0 <= fd) {
860       for (amount = 0; (amount < MAXSNAP) && snap[i].file; amount++);
861 
862       write(fd,       "\n--- List of recent snapshots\n",
863             StrLength("\n--- List of recent snapshots\n"));
864 
865       for (i = 1, index = snapnum; i <= amount; index--, i++) {
866         if (0 > index) {
867           index = MAXSNAP - 1;
868         }
869 
870         buffer = trio_aprintf("#%-2d in %s line %d [%d]\n",
871                               i,
872                               snap[index].file,
873                               snap[index].line,
874                               snap[index].hits);
875         if (buffer) {
876           write(fd, buffer, StrLength(buffer));
877           free(buffer);
878         }
879       }
880 
881       write(fd,       "--- End of list\n\n",
882             StrLength("--- End of list\n\n"));
883 
884       close(fd);
885     }
886   }
887 }
888 
GetSnapFile(void)889 char *GetSnapFile(void)
890 {
891   return snap[snapnum].file;
892 }
893 
GetSnapLine(void)894 int GetSnapLine(void)
895 {
896   return snap[snapnum].line;
897 }
898 #endif /* DEBUG */
899 
900 /* --- Debug ------------------------------------------------------ */
901 
Debug(const char * format,...)902 void Debug(const char *format, ...)
903 {
904   char buffer[BIGGERBUFFER];
905   va_list args;
906 
907   va_start(args, format);
908   trio_vsnprintf(buffer, sizeof(buffer), format, args);
909   va_end(args);
910 
911 #ifdef DEBUG
912   StrFormatAppendMax(buffer, sizeof(buffer), " (snapshot: %s line %d)",
913                      GetSnapFile(), GetSnapLine());
914 #endif
915 
916   Log(LOGDEBUG, buffer);
917   Multicastf(DEBUGCAST, "DEBUG: %s", buffer);
918 }
919 
920 /* --- Quit ------------------------------------------------------- */
921 
Quit(char * from,char * reason)922 void Quit(char *from, char *reason)
923 {
924   snapshot;
925 
926   /* Seen & Delete also handled by OnQuit, but how about restart/cleanup? */
927   SeenInsertAll(SEENQUITED, NULL, NULL);
928   DeleteGuests();
929 
930   WriteServer("QUIT :%s", ((reason && *reason) ? reason : NOREASON));
931   DisconnectServ("QUIT %s", ((reason && *reason) ? reason : NOREASON));
932 
933   restart = FALSE;
934   cleanup = TRUE;
935 }
936