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