1 #ifdef RCS
2 static char rcsid[]="$Id: server.c,v 1.2 2001/03/13 05:04:47 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/server.c,v $
17  * $Revision: 1.2 $
18  * $Date: 2001/03/13 05:04:47 $
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 "numeric.h" /* From the irc server source */
30 #include "list.h"
31 #include "function.h"
32 #include "user.h"
33 #include "command.h"
34 #include "ctcp.h"
35 #include "transfer.h"
36 #include "seen.h"
37 #include "server.h"
38 #include "bans.h"
39 #include "link.h"
40 #include "servfunc.h"
41 #include "fplrun.h"
42 
43 /* --- Global ----------------------------------------------------- */
44 
45 extern time_t now;
46 
47 extern char cmodes[], chanmodes[], ckey[], chankey[];
48 extern char channel[], nickname[], servername[];
49 extern char botmatch[], myaddr[];
50 extern char askforops[];
51 extern char *errfrom;
52 
53 extern bool nickflag, connected, restart, moderateflag;
54 extern bool lockmode, lockkey, locklimit;
55 extern bool netsplitmode;
56 extern bool deopprotect;
57 extern bool talkative;
58 extern bool warnmode;
59 extern bool multimode;
60 extern bool cleanup;
61 extern bool invitable;
62 extern bool autojoin;
63 extern bool autoop;
64 extern bool dispcomment;
65 extern bool autoop;
66 extern bool kickbans;
67 extern bool autounban;
68 extern bool strictopmode;
69 extern bool fakenamemode;
70 extern bool opprotect;
71 extern bool oplevel;
72 extern bool mute;
73 extern bool possiblyfresh;
74 extern bool bantimeouts;
75 extern bool chat;
76 extern bool public;
77 
78 extern int possiblyfreshtime;
79 extern int pubbantime;
80 extern int retry;
81 
82 extern long climit;
83 extern long levels[];
84 
85 extern itemident *current;
86 
87 
88 time_t topictime = 0;
89 time_t lastservertime = 0;
90 
91 char topic[BIGBUFFER];    /* Topic of the channel */
92 char topicwho[BIGBUFFER]; /* Who made the topic?  */
93 
94 bool botop = FALSE;       /* TRUE if the bot has channel operator status */
95 
96 long numofbotmodes;
97 long modecount[11] = { 0,0,0,0,0,0,0,0,0,0,0 };
98 long autoopswaiting = 0;
99 long limitc = 0;
100 
101 #define MINMODEPARAMS 3
102 static int maxmodeparams;
103 
104 /* ---------------------------------------------------------------- */
105 
106 struct
107 {
108   char *name;
109   void (*function)(char *a, char *b);
110 } messages[] =
111 {
112   {"PRIVMSG", OnPrivmsg}, {"MODE",    OnMode},   {"JOIN",    OnJoin},
113   {"PART",    OnPart},    {"QUIT",    OnQuit},   {"NICK",    OnNick},
114   {"KICK",    OnKick},    {"PING",    OnPing},   {"PONG",    OnPong},
115   {"TOPIC",   OnTopic},   {"NOTICE",  OnNotice}, {"INVITE",  OnInvite},
116   {"ERROR",   OnError},
117   {"", (void(*)())(NULL)}
118 };
119 
120 #ifndef HAVE_MEMMOVE
memmove(char * to,char * from,int len)121 void memmove(char *to, char *from, int len)
122 {
123   int i;
124 
125   if (to > from) {
126     for (i = len - 1; i >= 0; i--)
127       to[i] = from[i];
128   }
129   else {
130     for (i=0; i > len; i++)
131       to[i] = from[i];
132   }
133 }
134 #endif
135 
136 /* --- ParseServer ------------------------------------------------ */
137 
138 #include "server_gperf.c"
139 
ParseServer(char * line)140 void ParseServer(char *line)
141 {
142   char *from, *command, *rest, *end;
143 
144   snapshot;
145   if (':' == line[0]) {
146     from = line + 1;
147     command = StrIndex(from, ' ');
148     if (NULL == command)
149       return;
150     *command++ = (char)0;
151   }
152   else {
153     from = servername;
154     command = line;
155   }
156 
157   rest = StrIndex(command, ' ');
158   if (NULL == rest)
159     return;
160   *rest++ = (char)0;
161 
162   end = StrIndex(rest, '\n');
163   if (end) {
164     if ('\r' == end[-1])
165       end--;
166     *end = (char)0;
167   }
168 
169   if (isdigit(command[0])) {
170     OnNumeric(from, atoi(command), rest);
171   }
172   else {
173     struct Servercmds *p;
174 
175     p = FindServerKey(command, StrLength(command));
176     if (p) {
177       lastservertime = now;
178       p->function(from, rest);
179     }
180   }
181 }
182 
183 /* --- OnPrivmsg -------------------------------------------------- */
184 
OnPrivmsg(char * from,char * line)185 void OnPrivmsg(char *from, char *line)
186 {
187   char nick[NICKLEN+1], userhost[MIDBUFFER];
188   char target[MIDBUFFER], buffer[BIGBUFFER];
189   char *xs;
190 
191   snapshot;
192   if ('$' == line[0])  /* Ignore global messages */
193     return;
194 
195   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
196                     nick, userhost)) &&
197       (2 == StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
198                     target, buffer))) {
199 
200     errfrom = nick;
201     public = IsChannel(target);
202     chat = FALSE;
203 
204     xs = StrIndex(buffer, '\001');
205     if (xs) {
206       char ctcpfrom[MIDBUFFER];
207       char ctcpbuffer[BIGBUFFER];
208       char *ys;
209       int len;
210 
211       do {
212         ys = StrIndex(xs + 1, '\001');
213         if (NULL == ys)
214           break;  /* do - while */
215         len = ys - xs - 1;
216         if (len > 0) {
217           memmove(ctcpbuffer, xs + 1, len);
218           ctcpbuffer[len] = (char)0;
219           len = StrLength(ys + 1);
220           if (len > 0 ) {
221             memmove(xs, ys + 1, len);
222             xs[len] = (char)0;
223           }
224           else
225             *xs = (char)0;
226 
227           StrCopyMax(ctcpfrom, sizeof(ctcpfrom), from);
228           if (public)
229             PubCTCP(ctcpfrom, ctcpbuffer);
230           else
231             CTCP(ctcpfrom, ctcpbuffer);
232         }
233         else
234           xs += 2; /* Empty ctcp */
235       } while (xs = StrIndex(xs, '\001'));
236     }
237 
238     if (buffer[0]) {
239       if (public)
240         PubCommand(nick, userhost, buffer);
241       else
242         Command(nick, userhost, buffer);
243     }
244   }
245   else
246     Debug("Parse error in OnPrivmsg(from = \"%s\", line = \"%s\")", from, line);
247 }
248 
249 /* --- OnNotice --------------------------------------------------- */
250 
OnNotice(char * from,char * line)251 void OnNotice(char *from, char *line)
252 {
253   char target[MIDBUFFER], buffer[BIGBUFFER];
254 
255   snapshot;
256   if (2 == StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
257                    target, buffer)) {
258 
259     if (IsServer(from)) {  /* Server notices */
260       char bywho[MIDBUFFER];
261       char channelbuffer[MIDBUFFER];
262       char modebuffer[BIGBUFFER];
263 
264       if (3 == StrScan(buffer, "Fake: %"MIDBUFFERTXT"s MODE %"MIDBUFFERTXT"s %"BIGBUFFERTXT"[^\n]",
265                        bywho, channelbuffer, modebuffer)) {
266         StrFormatMax(buffer, sizeof(buffer), "Fake: \"%s\" on channel %s by %s [%s]",
267                      modebuffer, channelbuffer, bywho, from);
268         Log(LOG, buffer);
269         Multicast(REPORTCAST, buffer);
270       }
271     }
272 #if 0
273     else {
274       char nick[NICKLEN+1];
275       char userhost[MIDBUFFER];
276       itemguest *g;
277 
278       if (2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
279                        nick, userhost)) {
280         g = FindNick(nick);
281         if (g) {
282           floodcheck();
283         }
284       }
285     }
286 #endif
287   }
288   else
289     Debug("Parse error in OnNotice(from = \"%s\", line = \"%s\")", from, line);
290 }
291 
292 /* --- OnTopic ---------------------------------------------------- */
293 
OnTopic(char * from,char * line)294 void OnTopic(char *from, char *line)
295 {
296   char nick[NICKLEN+1], userhost[MIDBUFFER];
297   char target[MIDBUFFER], buffer[BIGBUFFER] = "";
298 
299   snapshot;
300   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
301                     nick, userhost)) &&
302       (1 <= StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
303                     target, buffer))) {
304 
305     Logf(LOGTOPIC, "\"%s\" by %s (%s)", buffer, nick, userhost);
306 
307 #ifdef HAVE_LIBFPL
308     if (runfpl(RUN_TOPIC, line, FPLRUN_PRE))
309       return;
310 #endif
311 
312     StrCopyMax(topic, sizeof(topic), buffer);
313     StrCopyMax(topicwho, sizeof(topicwho), from);
314     topictime = now;
315 
316     runfpl(RUN_TOPIC, line, FPLRUN_POST);
317   }
318   else
319     Debug("Parse error in OnTopic(from = \"%s\", line = \"%s\")", from, line);
320 }
321 
322 /* --- OnInvite --------------------------------------------------- */
323 
OnInvite(char * from,char * line)324 void OnInvite(char *from, char *line)
325 {
326   char nick[NICKLEN+1], userhost[MIDBUFFER];
327   char target[MIDBUFFER];
328   itemuser *u;
329 
330   snapshot;
331   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
332                     nick, userhost)) &&
333       (1 == StrScan(line, "%*s :%"MIDBUFFERTXT"s", target))) {
334 
335     Logf(LOG, "Invited to %s by %s", target, from);
336 
337 #ifdef HAVE_LIBFPL
338     if (runfpl(RUN_INVITE, target, FPLRUN_PRE))
339       return;
340 #endif
341 
342     u = FindUser(nick, userhost);
343     if (u && (u->level >= LEVELTRUST)) {
344       if (invitable) {
345         errfrom = nick;
346         CmdJoin(nick, target);
347       }
348       else
349         Send(nick, GetDefaultText(msg_no_invitation));
350     }
351     runfpl(RUN_INVITE, target, FPLRUN_POST);
352   }
353   else
354     Debug("Parse error in OnInvite(from = \"%s\", line = \"%s\")", from, line);
355 }
356 
357 /* --- OnMode ----------------------------------------------------- */
358 
359 #define MAXMODEPARAMS 6
360 #define MAXSOLOMODES 8
361 
362 struct Modeparamlist {
363   char *param[MAXMODEPARAMS];
364   int index;
365 };
366 
OnMode(char * from,char * line)367 void OnMode(char *from, char *line)
368 {
369   char flagbuffer[MINIBUFFER], parambuffer[BIGBUFFER];
370   char domode[MAXSOLOMODES+1], undomode[MAXSOLOMODES+1];
371   char *userhost, *modes, *param;
372   char c, cursign = '+';
373   bool servermode, botmode;
374   bool keymode = FALSE, limitmode = FALSE;
375   bool dirty_hack = FALSE;
376   int params = 0, i = 0, j = 0;
377   itemguest *pc, *who;
378   struct Modeparamlist chopon, chopoff, voiceon, voiceoff;
379   struct Modeparamlist banon, banoff;
380   struct Modeparamlist bxcepton, bxceptoff, ixcepton, ixceptoff;
381   struct Modeparamlist temp;
382 
383   snapshot;
384   StrTokenize(from, "!");
385   userhost = StrTokenize(NULL, "");
386 
387   Logf(LOGMODE, "\"%s\" by %s%s%s%s", line, from,
388        userhost ? " (" : "",
389        userhost ? userhost : "",
390        userhost ? ")" : "");
391 
392   if (!IsChannel(NextWord(line)))
393     return; /* Ignore user mode changes */
394 
395   servermode = IsServer(from);
396   if (servermode) {
397     botmode = FALSE;
398     who = NULL;
399   }
400   else {
401     botmode = StrEqualCase(from, nickname);
402     who = FindNick(from); /* Who did the mode change? */
403   }
404 
405   modes = NextWord(line);
406   if (NULL == modes)
407     return; /* Parse error! */
408 
409   chopon.index = chopoff.index = voiceon.index = voiceoff.index = \
410   banon.index = banoff.index = bxcepton.index = bxceptoff.index = \
411   ixcepton.index = ixceptoff.index = 0;
412 
413   while (c = *modes++) {
414     switch (c) {
415 
416       case '+':
417       case '-':
418         cursign = c;
419         break;
420 
421       case 'o':
422         param = NextWord(line);
423         if (param) {
424           params++;
425 
426           if ('+' == cursign) {
427             modecount[0]++;
428             if (MAXMODEPARAMS > chopon.index)
429               chopon.param[chopon.index++] = param;
430           }
431           else {
432             if (MAXMODEPARAMS > chopoff.index)
433               chopoff.param[chopoff.index++] = param;
434           }
435 
436           if (StrEqualCase(param, nickname)) {
437             /* This is us! */
438             if (talkative && !mute) {
439               if ('+' == cursign) {
440                 if (!botop)
441                   Actionf(GetDefaultText(msg_thanks_for_ops), from);
442               }
443               else {
444                 if (botop)
445                   Actionf(GetDefaultText(msg_slaps_for_deop), from);
446               }
447             }
448 
449             botop = ('+' == cursign);
450           }
451 
452           if (botmode) {
453             pc = FindNick(param);
454             if (pc) {
455               pc->flags.chanop = ('+' == cursign);
456               pc->flags.splitop = FALSE;
457             }
458           }
459         }
460         break;
461 
462       case 'b':
463         param = NextWord(line);
464         if (param) {
465           params++;
466 
467           if ('+' == cursign) {
468             modecount[1]++;
469             if (MAXMODEPARAMS > banon.index)
470               banon.param[banon.index++] = param;
471           }
472           else {
473             if (MAXMODEPARAMS > banoff.index)
474               banoff.param[banoff.index++] = param;
475           }
476 
477           if (servermode) {
478             if ('+' == cursign)
479               AddToBanList(BAN_ACTUAL, from, NULL, param, 0, NULL);
480           }
481           else {
482             if ('+' == cursign) {
483               char *nick = NULL;
484 
485               if (who) {
486                 who->bans++;
487                 if (!botmode &&
488                     (who->ident->level < LEVELBOT) &&
489                     IllegalBan(param)) {
490                   /*
491                    * Not set by bot AND the banner has too little level AND
492                    * matches the bot or a ban-proctected person (when
493                    * banprotection is on).
494                    */
495                   Mode("%s-b%s%s %s",
496                        dirty_hack ? "" : "-o",
497                        dirty_hack ? "" : " ",
498                        dirty_hack ? "" : from,
499                        param);
500 
501                   /* Lets not deop the abuser again */
502                   dirty_hack = TRUE;
503                 }
504               }
505 
506               /*
507                * We make an attempt to see if we can find a nick to attach
508                * with the specified ban pattern. This works [pretty good]
509                * in all cases when the ban is set before the kick...
510                */
511               pc = FindHost(param);
512               if (pc) {
513                 nick = pc->ident->nick;
514               }
515               else {
516                 /*
517                  * Ok, no user matched the banpattern that was set. We're
518                  * going for more casual guesses. Now, let's take a quick look
519                  * and see if any of the recent kicks match the pattern.
520                  *
521                  * We don't try the SEEN functions since they could be very
522                  * extensive and take quite some time (a few months in #Amiga
523                  * gives a lot more than 10000 hosts and 20000 nicks).
524                  */
525                 itemkick *k;
526 
527                 k = KickMatch(param);
528                 if (k)
529                   nick = k->nick;
530               }
531 
532               /* If we found the nick the 'guess-way', we set the GUESS bit */
533               AddToBanList(BAN_ACTUAL | (nick ? BAN_GUESSNICK : 0),
534                            from, nick, param,
535                            botmode ? -1 : pubbantime, NULL);
536             }
537             else
538               Unbanned(from, param, botmode);
539           }
540 
541           if ('+' == cursign) {
542             int num;
543 
544             num = CountBans(BAN_ACTUAL);
545             if (BANLIST_ALERTSIZE <= num) {
546               static time_t lastwarn = 0;
547 
548               if (!autounban || !botop || !UnbanLoprio(from)) {
549                 /*
550                  * The bot can be told to automatically unban the least important
551                  * ban when reaching this amount. If it isn't, or it isn't
552                  * opped, or the unban failed for some reason, we procede
553                  * (and make the ALERT output).
554                  */
555 
556                 if ((now - lastwarn) > 10*SECINMIN) {
557                   /* Max-rate is every 10th minute! */
558                   if (!mute)
559                     Actionf(GetDefaultText(msg_alert_banlist_full), num);
560 
561                   Logf(LOGWARN, "Banlist contains %d bans!", num);
562                   lastwarn = now;
563                 }
564               }
565             }
566           }
567         }
568         break;
569 
570       case 'e':
571         param = NextWord(line);
572         if (param) {
573           params++;
574 
575           if ('+' == cursign) {
576             if (MAXMODEPARAMS > bxcepton.index)
577               bxcepton.param[bxcepton.index++] = param;
578           }
579           else {
580             if (MAXMODEPARAMS > bxceptoff.index)
581               bxceptoff.param[bxceptoff.index++] = param;
582           }
583         }
584         break;
585 
586       case 'I':
587         param = NextWord(line);
588         if (param) {
589           params++;
590 
591           if ('+' == cursign) {
592             if (MAXMODEPARAMS > ixcepton.index)
593               ixcepton.param[ixcepton.index++] = param;
594           }
595           else {
596             if (MAXMODEPARAMS > ixceptoff.index)
597               ixceptoff.param[ixceptoff.index++] = param;
598           }
599         }
600         break;
601 
602       case 'k':
603         param = NextWord(line);
604         if (param) {
605           params++;
606 
607           if ('+' == cursign) {
608             modecount[2]++;
609             StrCopyMax(chankey, 32, param);
610           }
611           else {
612             chankey[0] = (char)0;
613           }
614         }
615 
616         keymode = TRUE;
617         break;
618 
619       case 'l':
620         if ('+' == cursign) {
621           modecount[3]++;
622 
623           param = NextWord(line);
624           if (param) {
625             params++;
626             limitc = atoi(param);
627           }
628         }
629         else {
630           limitc = 0;
631         }
632 
633         limitmode = TRUE;
634         break;
635 
636       case 'v':
637         param = NextWord(line);
638         if (param) {
639           params++;
640 
641           if ('+' == cursign)
642             modecount[4]++;
643 
644           pc = FindNick(param);
645           if (pc)
646             pc->flags.voice = ('+' == cursign);
647         }
648         break;
649 
650       case 'p':
651       case 's':
652       case 'i':
653       case 't':
654       case 'n':
655       case 'm': /* undo changes */
656         if (StrIndex(cmodes, c)) {
657           if ('-' == cursign) {
658             if (MAXSOLOMODES > i)
659               domode[i++] = c;
660           }
661         }
662         else {
663           if ('+' == cursign) {
664             if (MAXSOLOMODES > j)
665               undomode[j++] = c;
666           }
667         }
668 
669         if ('+' == cursign) {
670           switch(c) {
671             case 'p': modecount[5]++; break;
672             case 's': modecount[6]++; break;
673             case 'i': modecount[7]++; break;
674             case 't': modecount[8]++; break;
675             case 'n': modecount[9]++; break;
676             case 'm': modecount[10]++; break;
677           }
678         }
679         break;
680     } /* switch */
681   } /* while */
682 
683   domode[i] = undomode[j] = (char)0;
684 
685   if (maxmodeparams < params) {
686     maxmodeparams = params;
687   }
688 
689   /* Reacts to non-bot modes only */
690   if (botmode) {
691     numofbotmodes++; /* Bot changed a mode in the channel */
692   }
693   else {
694     flagbuffer[0] = parambuffer[0] = (char)0;
695     params = 0;
696 
697     /* Handle netsplit hacks, strictop and opprotect */
698     for (i = temp.index = 0; i < chopon.index; i++) {
699       pc = FindNick(chopon.param[i]);
700       if (pc) {
701         if (!pc->flags.chanop &&
702             (pc->ident->level < MAX(LEVELRECOG, oplevel))) {
703           if (servermode) {
704             if (netsplitmode && !pc->flags.splitop) {
705               temp.param[temp.index++] = chopon.param[i];
706             }
707           }
708           else {
709             /* Not by server */
710             if (strictopmode) {
711               if (!StrEqualCase(chopon.param[i], nickname) &&
712                   who && (who->ident->level < MAX(LEVELBOT, oplevel))) {
713                 temp.param[temp.index++] = chopon.param[i];
714               }
715             }
716           }
717         }
718 
719         /* The guest *IS* chanop right now */
720         pc->flags.chanop = TRUE;
721       }
722     }
723 
724     if (botop && temp.index) {
725       /* We won't inform mass-oppers */
726       if (!servermode && (1 == temp.index) && !mute) {
727         SendNickf(from, GetDefaultText(msg_no_ops_allowed), temp.param[0]);
728       }
729 
730       StrCopyMax(flagbuffer, sizeof(flagbuffer), "-");
731 
732       if (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)) &&
733           !dirty_hack) {
734         dirty_hack = TRUE;
735         StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
736         StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", from);
737         params++;
738       }
739 
740       for (i = 0; i < temp.index; i++) {
741         if (maxmodeparams <= params) {
742           Mode("%s%s", flagbuffer, parambuffer);
743           StrCopyMax(flagbuffer, sizeof(flagbuffer), "-");
744           parambuffer[0] = (char)0;
745           params = 0;
746         }
747 
748         StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
749         StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", temp.param[i]);
750         params++;
751       }
752     }
753 
754     for (i = temp.index = 0; i < chopoff.index; i++) {
755       pc = FindNick(chopoff.param[i]);
756       if (pc) {
757         if (pc->flags.chanop &&
758             (pc->ident->level >= MAX(LEVELCHANOP, oplevel))) {
759           if (servermode) {
760             if (netsplitmode) {
761               temp.param[temp.index++] = chopoff.param[i];
762             }
763           }
764           else {
765             if (opprotect) {
766               if (who && (who->ident->level < MAX(LEVELCHANOP, oplevel))) {
767                 temp.param[temp.index++] = chopoff.param[i];
768               }
769             }
770           }
771         }
772 
773         /* Guest *IS NOT* a chanop anymore */
774         pc->flags.chanop = pc->flags.splitop = FALSE;
775       }
776     }
777 
778     if (botop && temp.index) {
779       if (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)) &&
780           !dirty_hack) {
781         dirty_hack = TRUE;
782 
783         if (maxmodeparams <= params) {
784           Mode("%s%s", flagbuffer, parambuffer);
785           params = 0;
786         }
787 
788         if (0 == params) {
789           StrCopyMax(flagbuffer, sizeof(flagbuffer), "-");
790           parambuffer[0] = (char)0;
791         }
792 
793         StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
794         StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", from);
795         params++;
796       }
797 
798       if (maxmodeparams > params) {
799         StrAppendMax(flagbuffer, sizeof(flagbuffer), "+");
800       }
801 
802       for (i = 0; i < temp.index; i++) {
803         if (maxmodeparams <= params) {
804           Mode("%s%s", flagbuffer, parambuffer);
805           StrCopyMax(flagbuffer, sizeof(flagbuffer), "+");
806           parambuffer[0] = (char)0;
807           params = 0;
808         }
809 
810         StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
811         StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", temp.param[i]);
812         params++;
813       }
814     }
815 
816     if (botop) {
817       if (flagbuffer[0]) {
818         Mode("%s%s", flagbuffer, parambuffer);
819       }
820 
821       /* Only LEVELBOT or higher users are allowed to set/remove exceptions */
822       if (who && (who->ident->level < MAX(LEVELBOT, oplevel))) {
823         flagbuffer[0] = parambuffer[0] = (char)0;
824 
825         if (bxcepton.index || ixcepton.index) {
826           StrAppendMax(flagbuffer, sizeof(flagbuffer), "-");
827         }
828 
829         /* Remove user +e's */
830         for (i = 0; i < bxcepton.index; i++) {
831           StrAppendMax(flagbuffer, sizeof(flagbuffer), "e");
832           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
833                              bxcepton.param[i]);
834         }
835 
836         /* Remove user +I's */
837         for (i = 0; i < ixcepton.index; i++) {
838           StrAppendMax(flagbuffer, sizeof(flagbuffer), "I");
839           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
840                              ixcepton.param[i]);
841         }
842 
843 #if 0
844 /* We need to keep a list of exceptions to compare with */
845         if (bxceptoff.index || ixceptoff.index) {
846           StrAppendMax(flagbuffer, sizeof(flagbuffer), "+");
847         }
848 
849         /* Reset user -e's */
850         for (i = 0; i < bxceptoff.index; i++) {
851           StrAppendMax(flagbuffer, sizeof(flagbuffer), "e");
852           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
853                              bxceptoff.param[i]);
854         }
855 
856         /* Reset user -I's */
857         for (i = 0; i < ixceptoff.index; i++) {
858           StrAppendMax(flagbuffer, sizeof(flagbuffer), "I");
859           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
860                              ixceptoff.param[i]);
861         }
862 #endif
863 
864         if (flagbuffer[0]) {
865           if (who->ident->level < MAX(LEVELCHANOP, oplevel))
866             Mode("-o %s", from);
867           Mode("%s%s", flagbuffer, parambuffer);
868         }
869       }
870 
871       /* React to channel key changes */
872       if (lockkey && keymode) {
873         if (servermode || (who && (who->ident->level < MAX(LEVELBOT, oplevel)))) {
874           if (!StrEqualCase(ckey, chankey)) {
875             if (chankey[0])
876               Mode("-k %s", chankey);
877             if (ckey[0])
878               Mode("+k %s", ckey);
879           }
880         }
881       }
882 
883       /* React to channel mode changes */
884       if (lockmode && (domode[0] || undomode[0])) {
885         if (servermode || (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)))) {
886           Mode("%s%s%s%s", domode[0] ? "+" : "", domode,
887                undomode[0] ? "-" : "", undomode);
888         }
889       }
890 
891       /* React to channel limit changes */
892       if (locklimit && limitmode) {
893         if (servermode || (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)))) {
894           if (climit != limitc) {
895             /* We got a limit we don't like */
896             if (0 == climit)
897               Mode("-l");
898             else
899               Mode("+l %d", climit);
900           }
901         }
902       }
903     }
904 
905     if (servermode) {
906       /* Server actions are only here on net-heals! */
907       NetHeal();
908 
909       /* Remove bans set by server */
910       if (possiblyfreshtime &&
911           ((now - possiblyfreshtime) < SERVERBANTIMEOUT)) {
912 
913         for (i = 0; i < banon.index; i++) {
914           if (IsUnban(banon.param[i])) {
915             /* If one of them bans matched an unbanned one! It means that we
916                don't consider this a "fresh" split! */
917             possiblyfreshtime = 0;
918             break;
919           }
920         }
921 
922         if (possiblyfreshtime) {
923           /* Don't unban serverbans */
924           banon.index = 0;
925         }
926       }
927 
928       if (botop) {
929         flagbuffer[0] = parambuffer[0] = (char)0;
930 
931         /* Remove +b server modes */
932         for (i = 0; i < banon.index; i++) {
933           StrAppendMax(flagbuffer, sizeof(flagbuffer), "b");
934           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
935                              banon.param[i]);
936         }
937 
938         /* Remove all +e server modes for the moment */
939         for (i = 0; i < bxcepton.index; i++) {
940           StrAppendMax(flagbuffer, sizeof(flagbuffer), "e");
941           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
942                              bxcepton.param[i]);
943         }
944 
945         /* Remove all +I server for the moment */
946         for (i = 0; i < ixcepton.index; i++) {
947           StrAppendMax(flagbuffer, sizeof(flagbuffer), "I");
948           StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
949                              ixcepton.param[i]);
950         }
951 
952         if (flagbuffer[0])
953           Mode("-%s%s", flagbuffer, parambuffer);
954       }
955     }
956     else {
957       /* Not server */
958       if (deopprotect && (temp.index > 1) && botop && who)
959         Warning(who, "deoppers", "Mass-deop detected");
960     }
961   }
962 }
963 
964 /* --- OnJoin ----------------------------------------------------- */
965 
OnJoin(char * from,char * line)966 void OnJoin(char *from, char *line)
967 {
968   char nick[NICKLEN+1], userhost[MIDBUFFER];
969   char target[MIDBUFFER];
970   char *servermodes, *pointer;
971   extern char unetserv[];
972 
973   snapshot;
974   switch (line[0]) {
975 
976     case ':':
977       pointer = &line[1];
978       break;
979 
980     default:
981       pointer = line;
982       break;
983 
984   }
985 
986   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
987                     nick, userhost)) &&
988       (1 == StrScan(pointer, "%"MIDBUFFERTXT"s", target))) {
989 
990     runfpl(RUN_JOIN, nick, FPLRUN_PRE);
991 
992     /* ircd2.9 uses '\a' to append modes (server ops/voice) */
993     servermodes = StrIndex(target, '\a');
994     if (servermodes)
995       *servermodes++ = (char)0;
996 
997     if (StrEqualCase(nick, nickname)) {
998       StrCopyMax(channel, 200, target);
999 #ifdef NICKSERV
1000       WriteServer("CHANSERV op %s %s", channel, nickname);
1001 #endif
1002 
1003 #ifdef UNDERNET
1004       WriteServer("PRIVMSG %s :OP %s %s", unetserv, channel, nickname);
1005 #endif
1006       WriteServer("WHO %s", target);    /* Who are on the channel */
1007       WriteServer("MODE %s", target);   /* What are the channel modes */
1008       WriteServer("MODE %s b", target); /* What are the bans */
1009     }
1010     else {
1011       bool nethealjoin = FALSE;
1012       itemguest *g;
1013 
1014       if (AddGuest(nick, userhost, FALSE, FALSE, FALSE, &g)) {
1015         if (moderateflag && botop)
1016           Mode("+v %s", nick);
1017       }
1018       else {
1019         /* Netjoin or error */
1020         nethealjoin = TRUE;
1021       }
1022 
1023       if (g) {
1024         current = g->ident;
1025 
1026         if (fakenamemode && g->ident->illegalname)
1027           StickyKick(g, GetDefaultText(msg_illegal_name));
1028 
1029         if (kickbans) {
1030           /* Check done even if not opped for the purpose of being better aware
1031              of the situation when later opped */
1032           if (IsBan(from))
1033             StickyKick(g, "Go away, you're banned here!");
1034         }
1035 
1036         if (servermodes) {
1037           /* Make a fake mode change */
1038           char xfrom[MIDBUFFER], xline[MIDBUFFER];
1039 
1040           StrCopyMax(xfrom, sizeof(xfrom), servername);
1041           StrFormatMax(xline, sizeof(xline), "%s +%s %s %s",
1042                        target, servermodes, nick,
1043                        (servermodes[1] ? nick : ""));
1044           OnMode(xfrom, xline);
1045         }
1046 
1047         if (autoop && IsAutoOp(g)) {
1048           /* DONT op anyone at this join, when several bots/people run services
1049              like this the channel simply gets flooded after long lasting netheals.
1050              Let's wait a (random) while before checking this user if it is op,
1051              and if it still isn't, op the poor thing. */
1052           g->op_this_person = now + Rnd()*10+10;
1053           autoopswaiting++;
1054         }
1055 
1056         if (multimode && (g->ident->level < LEVELEXPERT) && !g->flags.bot) {
1057           /* Do this check even if not opped, the knowledge can be good if we
1058              are suddenly opped in the middle of an attack */
1059           MultiCheck(g->ident->userdomain);
1060         }
1061 
1062         if (warnmode)
1063           WarnCheck(nick, userhost);
1064 
1065         if (dispcomment && !nethealjoin)
1066           DispComment(g);
1067       }
1068     }
1069 
1070     runfpl(RUN_JOIN, nick, FPLRUN_POST);
1071   }
1072   else
1073     Debug("Parse error in OnJoin(from = \"%s\", line = \"%s\")", from, line);
1074 }
1075 
1076 /* --- OnPart ----------------------------------------------------- */
1077 
OnPart(char * from,char * line)1078 void OnPart(char *from, char *line)
1079 {
1080   char nick[NICKLEN+1], userhost[MIDBUFFER];
1081   char target[MIDBUFFER], buffer[BIGBUFFER] = "";
1082 
1083   snapshot;
1084   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
1085                     nick, userhost)) &&
1086       (1 <= StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
1087                     target, buffer))) {
1088 
1089     Logf(LOGPART, "%s%s%s%s", nick,
1090          buffer[0] ? " ("   : "",
1091          buffer[0] ? buffer : "",
1092          buffer[0] ? ")"    : "");
1093 
1094     runfpl(RUN_LEAVE, nick, FPLRUN_PRE);
1095 
1096     if (StrEqualCase(nick, nickname)) {  /* I left */
1097       SeenInsertAll(SEENLEFT, NULL, NULL);
1098       DeleteGuests();
1099       BanDisable(); /* No valid bans anymore */
1100     }
1101     else {
1102       itemguest *g;
1103 
1104       g = FindNick(nick);
1105       if (g) {
1106         SeenInsert(g, SEENLEAVE, NULL,
1107                    (buffer[0] && !StrEqualCase(buffer, nick)) ? buffer : NULL);
1108         RemoveGuest(g);
1109       }
1110       else
1111         Debug("Internal confusion. Unknown user \"%s\" left.", from);
1112     }
1113 
1114     runfpl(RUN_LEAVE, nick, FPLRUN_POST);
1115   }
1116   else
1117     Debug("Parse error in OnPart(from = \"%s\", line = \"%s\")", from, line);
1118 }
1119 
1120 /* --- OnQuit ----------------------------------------------------- */
1121 
OnQuit(char * from,char * line)1122 void OnQuit(char *from, char *line)
1123 {
1124   char nick[NICKLEN+1], userhost[MIDBUFFER];
1125   char buffer[BIGBUFFER] = "";
1126 
1127   snapshot;
1128   /* There might be no reason at all, ie. only ":\000" */
1129   StrScan(line, ":%"BIGBUFFERTXT"[^\n]", buffer);
1130 
1131   if (2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
1132                    nick, userhost)) {
1133 
1134     Logf(LOGQUIT, "%s (%s)", nick, buffer);
1135 
1136     runfpl(RUN_QUIT, nick, FPLRUN_PRE);
1137 
1138     if (StrEqualCase(nick, nickname)) { /* I quitted */
1139       SeenInsertAll(SEENQUITED, NULL, NULL);
1140       DeleteGuests();
1141       BanDisable();
1142     }
1143     else {
1144       bool split = FALSE;
1145       itemguest *g;
1146 
1147       g = FindNick(nick);
1148       if (g) {
1149         /* Enhanced split detection to trap even more faked ones */
1150         if (PatternExist(buffer, "^[-A-Z0-9a-z.*]+[.][A-Za-z]+ [-A-Z0-9a-z.*]+[.][A-Za-z]+")) {
1151           char name1[MIDBUFFER];
1152           char name2[MIDBUFFER];
1153           char *p;
1154 
1155           if (2 == StrScan(buffer, "%"MIDBUFFERTXT"s %"MIDBUFFERTXT"s",
1156                            name1, name2)) {
1157             p = StrIndexLast(name1, '.');
1158             if (p && FindByCode(&p[1])) {
1159               p = StrIndexLast(name2, '.');
1160               if (p && FindByCode(&p[1]))
1161                 split = TRUE;
1162             }
1163           }
1164         }
1165 
1166         SeenInsert(g, SEENQUIT, NULL, buffer);
1167         if (split)
1168           AddSplitter(g, buffer);
1169         else
1170           RemoveGuest(g);
1171       }
1172       else
1173         Debug("Internal confusion. Unknown user \"%s\" quit.", from);
1174     }
1175 
1176     runfpl(RUN_QUIT, nick, FPLRUN_POST);
1177   }
1178   else
1179     Debug("Parse error in OnQuit(from = \"%s\", line = \"%s\")", from, line);
1180 }
1181 
1182 /* --- OnKick ----------------------------------------------------- */
1183 
OnKick(char * from,char * line)1184 void OnKick(char *from, char *line)
1185 {
1186   char nick[NICKLEN+1], userhost[MIDBUFFER];
1187   char target[MIDBUFFER], victim[NICKLEN+1], buffer[BIGBUFFER];
1188 
1189   snapshot;
1190   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
1191                     nick, userhost)) &&
1192       (3 == StrScan(line, "%"MIDBUFFERTXT"s %"NICKLENTXT"s :%"BIGBUFFERTXT"[^\n]",
1193                     target, victim, buffer))) {
1194 
1195     Logf(LOGKICK, "(%s) by %s", line, from);
1196 
1197     runfpl(RUN_KICK, line, FPLRUN_PRE);
1198 
1199     if (StrEqualCase(victim, nickname)) { /* I was kicked */
1200       SeenInsertAll(SEENKICKED, from, NULL);
1201       DeleteGuests();
1202       BanDisable(); /* No valid bans anymore */
1203       if (autojoin)
1204         WriteServer("JOIN %s %s", channel, chankey);
1205 #ifdef HAVE_LIBFPL
1206       if (!runfpl(RUN_KICKBOT, from, FPLRUN_PRE))
1207         runfpl(RUN_KICKBOT, from, FPLRUN_POST);
1208 #endif
1209     }
1210     else {
1211       itemguest *g, *w;
1212 
1213       w = FindNick(nick);
1214       if (w)
1215         w->kicks++;
1216 
1217       g = FindNick(victim); /* This _CAN_ return NULL */
1218       if (g) {
1219         if (StrEqualCase(nick, nickname)) { /* Bot kick */
1220           AddKick(g->ident, from, buffer, KICK_COMMON);
1221         }
1222         else { /* Not a bot kick */
1223           AddKick(g->ident, from, buffer,
1224                   (g->ident->level < LEVELTRUST) ? KICK_COMMON : KICK_TRUSTEDUSER);
1225 
1226           if (opprotect) {
1227             if (botop && w &&
1228                 (w->ident->level < MAX(LEVELCHANOP, oplevel)) &&
1229                 (g->ident->level >= MAX(LEVELCHANOP, oplevel))) {
1230               Mode("-o %s", nick);
1231             }
1232           }
1233         }
1234         SeenInsert(g, SEENKICK, from, buffer);
1235         RemoveGuest(g);
1236       }
1237       else
1238         Debug("Internal confusion. Unknown user \"%s\" was kicked.", victim);
1239     }
1240 
1241     runfpl(RUN_KICK, target, FPLRUN_POST);
1242   }
1243   else
1244     Debug("Parse error in OnKick(from = \"%s\", line = \"%s\")", from, line);
1245 }
1246 
1247 /* --- OnNick ----------------------------------------------------- */
1248 
OnNick(char * from,char * line)1249 void OnNick(char *from, char *line)
1250 {
1251   char nick[NICKLEN+1], userhost[MIDBUFFER];
1252   char newnick[NICKLEN+1];
1253 
1254   snapshot;
1255   if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
1256                     nick, userhost)) &&
1257       (1 == StrScan(line, ":%"NICKLENTXT"s", newnick))) {
1258 
1259     Logf(LOGNICK, "%s is now known as %s (%s)", nick, newnick, userhost);
1260 
1261     runfpl(RUN_NICK, nick, FPLRUN_PRE);
1262 
1263     if (StrEqualCase(nick, nickname)) {
1264       StrCopy(nickname, newnick);
1265       StrFormatMax(botmatch, MIDBUFFER, "%s!%s", newnick, myaddr);
1266       SendLinkAll(IBCP_NICKNAME, "%s", newnick);
1267     }
1268     ChangeGuest(nick, newnick);
1269 
1270     runfpl(RUN_NICK, newnick, FPLRUN_POST);
1271   }
1272   else
1273     Debug("Parse error in OnNick(from = \"%s\", line = \"%s\")", from, line);
1274 }
1275 
1276 /* --- OnPing ----------------------------------------------------- */
1277 
OnPing(char * from,char * line)1278 void OnPing(char *from, char *line)
1279 {
1280   char forward[MIDBUFFER];
1281 
1282   snapshot;
1283   if (1 == StrScan(line, "%*s %"MIDBUFFERTXT"s", forward))
1284     WriteServer("PONG %s", forward);
1285   else
1286     WriteServer("PONG %s", (':' == line[0]) ? &line[1] : line);
1287 }
1288 
1289 /* --- OnPong ----------------------------------------------------- */
1290 
1291 int pendingpings = 0;
1292 struct timeval pingval;
1293 struct timeval pongval;
1294 struct timeval delayval;
1295 
OnPong(char * from,char * line)1296 void OnPong(char *from, char *line)
1297 {
1298   snapshot;
1299   pendingpings--;
1300   gettimeofday(&pongval, NULL);
1301   delayval.tv_sec = pongval.tv_sec - pingval.tv_sec;
1302   delayval.tv_usec = pongval.tv_usec - pingval.tv_usec;
1303 
1304   /* Normalize the time values */
1305   while (delayval.tv_usec > MILLION) {
1306     delayval.tv_usec -= MILLION;
1307     delayval.tv_sec++;
1308   }
1309   while (delayval.tv_usec < 0) {
1310     delayval.tv_usec += MILLION;
1311     delayval.tv_sec--;
1312   }
1313 }
1314 
PingServer(void)1315 void PingServer(void)
1316 {
1317   snapshot;
1318   pendingpings++;
1319   gettimeofday(&pingval, NULL);
1320   WriteServer("PING :%s", servername);
1321 }
1322 
1323 /* --- OnError ---------------------------------------------------- */
1324 
OnError(char * from,char * line)1325 void OnError(char *from, char *line)
1326 {
1327   char buffer[BIGBUFFER];
1328 
1329   snapshot;
1330 #ifdef HAVE_LIBFPL
1331   if (!runfpl(RUN_ERROR, line, FPLRUN_PRE))
1332     runfpl(RUN_ERROR, line, FPLRUN_POST);
1333 #endif
1334 
1335   StrFormatMax(buffer, sizeof(buffer), "ERROR: (%s) from %s",
1336                (':' == line[0]) ? &line[1] : line, from);
1337   Log(LOG, buffer);
1338   DisconnectServ(buffer);
1339   connected = FALSE;
1340 
1341   SeenInsertAll(SEENERROR, line, NULL);
1342   DeleteGuests();
1343   BanDisable();
1344 
1345   restart = cleanup = TRUE;
1346 }
1347 
1348 /* --- OnKill ----------------------------------------------------- */
1349 
1350 #if 0
1351 void OnKill(char *from, char *line)
1352 {
1353   snapshot;
1354   Logf(LOGKILL, "(%s) by %s", line, from);
1355 
1356 #ifdef HAVE_LIBFPL
1357   if (!runfpl(RUN_KILL, line, FPLRUN_PRE))
1358     runfpl(RUN_KILL, line, FPLRUN_POST);
1359 #endif
1360 
1361   DisconnectServ(line);
1362   connected = FALSE;
1363 
1364   SeenInsertAll(SEENKILL, from, line);
1365   DeleteGuests();
1366   BanDisable();
1367 
1368   restart = cleanup = TRUE;
1369 }
1370 #endif
1371 
1372 #ifdef NICKSERV
1373 extern char nickpasswd[];
1374 #endif
1375 
1376 #ifdef UNDERNET
1377 extern char unetpasswd[];
1378 extern char unetnick[];
1379 extern char unetserv[];
1380 #endif
1381 
1382 /* --- OnNumeric -------------------------------------------------- */
1383 
OnNumeric(char * from,int num,char * line)1384 void OnNumeric(char *from, int num, char *line)
1385 {
1386   char buffer[BIGBUFFER];
1387   char number[22];
1388 
1389   snapshot;
1390   StrFormatMax(number, sizeof(number), "%d", num);
1391   runfpl(RUN_NUMERIC, number, FPLRUN_PRE);
1392 
1393   switch (num) {
1394 
1395     case RPL_WELCOME: /* Welcomming message */
1396       connected = TRUE;
1397       StrCopyMax(servername, MIDBUFFER, from);
1398       StrScan(line, "%"NICKLENTXT"s", nickname);
1399 #ifndef DBUG
1400       WriteServer("MODE %s +is", nickname);
1401 #else
1402       WriteServer("MODE %s +i", nickname);
1403 #endif
1404 #ifdef NICKSERV
1405       WriteServer("NICKSERV identify %s", nickpasswd);
1406 #endif
1407 #ifdef UNDERNET
1408       WriteServer("PRIVMSG %s :LOGIN %s %s", unetserv, unetnick, unetpasswd);
1409 #endif
1410       WriteServer("JOIN %s %s", channel, chankey);
1411       gettimeofday(&pongval, NULL);
1412       retry = 0;
1413       maxmodeparams = MINMODEPARAMS;
1414       break;
1415 
1416     case ERR_NICKNAMEINUSE: /* Nickname already in use */
1417     case ERR_ERRONEUSNICKNAME: /* or wrong nickname */
1418     case ERR_UNAVAILRESOURCE: /* Nick/channel is temporarily unavailable */
1419       if (!nickflag)
1420         nickflag = TRUE;
1421       else {
1422         NewNick();
1423         WriteServer("NICK %s", nickname);
1424       }
1425       break;
1426 
1427     case ERR_NICKCOLLISION: /* Nickname collision */
1428       Log(LOG, "Nickname collision");
1429       restart = cleanup = TRUE;
1430       break;
1431 
1432     case ERR_NOTREGISTERED: /* You have not registered */
1433       Hello(NULL);
1434       break;
1435 
1436     case RPL_WHOREPLY:  /* Users on channel at join time */
1437       NewGuest_Who(line);
1438       break;
1439 
1440     case RPL_TOPIC:  /* The topic on join */
1441       topictime = now;
1442       topic[0] = (char)0;
1443       StrScan(line, "%*s %*s :%"BIGBUFFERTXT"[^\n]", topic);
1444       StrCopyMax(topicwho, sizeof(topicwho), from);
1445       break;
1446 
1447     case RPL_NOTOPIC:
1448       topictime = 0;
1449       break;
1450 
1451     case RPL_CHANNELMODEIS:
1452       chanmodes[0] = (char)0;
1453       StrScan(line, "%*s %*s +%15s", chanmodes);
1454       break;
1455 
1456     case RPL_BANLIST: /* get 3rd word */
1457       if (1 == StrScan(line, "%*s %*s %"BIGBUFFERTXT"s", buffer)) {
1458         /* fprintf(stderr, "BANLIST ENTRY: '%s'\n", buffer); */
1459         AddToBanList(BAN_ACTUAL, NULL, NULL, buffer, -1, NULL);
1460       }
1461       break;
1462 
1463     case RPL_ENDOFBANLIST:
1464       /* we're at the end of banlist */
1465       bantimeouts = TRUE; /* enable timeouts now */
1466       if (!CountBans(BAN_ACTUAL)) {
1467         /* no actual bans in the channel */
1468         possiblyfresh = TRUE; /* restarted server? */
1469         /* fprintf(stderr, "### no bans in channel! ###\n"); */
1470       }
1471       break;
1472 
1473     case ERR_NOSUCHNICK: /* Flush messages if target has disappeared */
1474     case ERR_NOSUCHCHANNEL:
1475       if (1 == StrScan(line, "%*s %"NICKLENTXT"s", buffer))
1476         MessageReaper(buffer);
1477       break;
1478 
1479     case ERR_CANNOTSENDTOCHAN:  /* Check for channel desync */
1480     case ERR_CHANOPRIVSNEEDED:
1481       if (!StrEqual(from, servername)) {
1482         StrFormatMax(buffer, sizeof(buffer), "Desync: Server %s rejected request", from);
1483         Log(LOG, buffer);
1484         Multicast(REPORTCAST, buffer);
1485       }
1486       break;
1487 
1488     case RPL_ENDOFNAMES: /* End of /NAMES list */
1489       if (*askforops && !botop)
1490         Action(askforops);
1491       break;
1492 
1493     case ERR_CHANNELISFULL:
1494       StrFormatMax(buffer, sizeof(buffer), "Channel %s is full", channel);
1495       Log(LOG, buffer);
1496       Multicast(REPORTCAST, buffer);
1497       break;
1498 
1499     case ERR_INVITEONLYCHAN:
1500       StrFormatMax(buffer, sizeof(buffer), "Channel %s is invite-only", channel);
1501       Log(LOG, buffer);
1502       Multicast(REPORTCAST, buffer);
1503       break;
1504 
1505     case ERR_BANNEDFROMCHAN:
1506       StrFormatMax(buffer, sizeof(buffer), "Banned from channel %s", channel);
1507       Log(LOG, buffer);
1508       Multicast(REPORTCAST, buffer);
1509       break;
1510 
1511     case ERR_BADCHANNELKEY:
1512       StrFormatMax(buffer, sizeof(buffer), "Bad channel key for %s", channel);
1513       Log(LOG, buffer);
1514       Multicast(REPORTCAST, buffer);
1515       break;
1516 
1517     case ERR_NOPERMFORHOST: /* Not I-lined */
1518       Logf(LOG, "No permission to connect to server %s", servername);
1519       break;
1520 
1521     case ERR_YOUREBANNEDCREEP: /* K-lined */
1522       Logf(LOG, "Banned from server %s", servername);
1523       break;
1524 
1525 #if 0
1526       /* Messages we definetely ignore: */
1527 
1528       /* MOTD junk */
1529     case RPL_MOTDSTART:
1530     case RPL_MOTD:
1531     case RPL_ENDOFMOTD:
1532 
1533       /* Inital crap info: */
1534     case RPL_YOURHOST: /* 'Darxide :Your host is irc.ludd.luth.se, running
1535                            version 2.9.2+Cr8' */
1536     case RPL_CREATED: /* 'Darxide :This server was created Sun Jan 26
1537                          1997 at 16:54:21 MET' */
1538     case RPL_MYINFO: /* 'Darxide irc.ludd.luth.se 2.9.2+Cr8 oirw abiklmnopqstv' */
1539 
1540       /* Luser statistics: */
1541     case RPL_LUSERCLIENT: /* 'Darxide :There are 11523 users and 0 services
1542                              on 61 servers' */
1543     case RPL_LUSEROP: /* 'Darxide 88 :operators online' */
1544     case RPL_LUSERUNKNOWN: /* 'Darxide 2 :unknown connections' */
1545     case RPL_LUSERCHANNELS: /* 'Darxide 4319 :channels formed' */
1546     case RPL_LUSERME: /* 'Darxide :I have 309 clients, 0 services and 3
1547                          servers' */
1548 
1549     case RPL_NAMREPLY: /* the name list we get automatically when we join a
1550                           channel */
1551 
1552     case RPL_ENDOFWHO: /* End of /WHO list */
1553 
1554 #endif
1555 
1556     default: /* Recommended ending of a switch() */
1557 
1558       /* case ERR_RESTRICTED:  we're restricted here, could try to change to a
1559          different server */
1560 
1561       if (num >= ERR_NOSUCHNICK) {
1562         /* Log errors only */
1563         Logf(LOG, "Server message %d: '%s'", num, line);
1564       }
1565       break;
1566   }
1567   runfpl(RUN_NUMERIC, number, FPLRUN_POST);
1568 }
1569