1 #ifdef RCS
2 static char rcsid[]="$Id: bans.c,v 1.1.1.1 2000/11/13 02:42:38 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/bans.c,v $
17  * $Revision: 1.1.1.1 $
18  * $Date: 2000/11/13 02:42:38 $
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 "list.h"
30 #include "function.h"
31 #include "transfer.h"
32 #include "user.h"
33 #include "seen.h"
34 #include "bans.h"
35 #include "flood.h"
36 #include "fplrun.h"
37 
38 extern time_t now;
39 
40 extern char channel[];
41 extern char nickname[];
42 extern char myaddr[];
43 extern char banfile[];    /* File name for stored ban info */
44 extern char warnfile[];   /* File name for stored warning info */
45 extern bool botop;        /* Are we chanop? */
46 extern bool reportban;    /* Report SET and BANs in public */
47 extern bool banuserkicks; /* Ban on many user-kicks */
48 extern bool fakenamemode;
49 extern bool mute;
50 
51 extern time_t uptime;
52 extern int defbantime;
53 extern int warnmonths;
54 extern long levels[];
55 
56 extern itemuser *userHead;
57 extern itemguest *guestHead;
58 
59 /* --- Bans ------------------------------------------------------- */
60 
61  /***************************************************************************
62  *  ____                        _          __  __
63  * | __ )  __ _ _ __        ___| |_ _   _ / _|/ _|
64  * |  _ \ / _` | '_ \ _____/ __| __| | | | |_| |_
65  * | |_) | (_| | | | |_____\__ \ |_| |_| |  _|  _|
66  * |____/ \__,_|_| |_|     |___/\__|\__,_|_| |_|
67  *
68  ***************************************************************************/
69 
70  /***************************************************************************
71 
72   USER CONCEPTS:
73 
74   Dancer supports ban-supervising, ban-enforcing, timed bans and unbanning
75   server bans.
76 
77   * supervising
78     It records all bans set in the channel. It knows who set the ban and
79     when. (should try to get the last nick used, and hopefully kick-message
80     if the user was kicked out)
81     Bans that match the bot itself are unbanned and the banner get
82     deopped!
83 
84   * enforcing
85     Some bans are really important ones and meant to be set. These are auto-
86     matically set again if anyone unban them.
87     A 2nd unban of an enforced ban within a short period (2 mins) will make
88     Dancer keep the ban unset and wait X (~30) mins. X == configurable and then
89     sets it back on channel again.
90     High level users (100) remain capable of unbanning enforced bans just like
91     normal unbans.
92 
93   * timed bans
94     General bans set by or with Dancer are timed. They are automatically
95     unbanned after the specified time. Bans that would include the bot itself
96     are ignored. Available bans should be:
97     'ban <nick>'    - regular user ban
98     'ban *<nick>'   - ban the user's site
99     'ban <pattern>' - ban the specified pattern. (pattern must match the form
100                       <nick>!<user>@<site> - where ! and @ must be specified.)
101 
102   * server bans
103    Server bans are automatically unbanned. (see details below)
104 
105  INTERNAL CONCEPTS
106 
107  It keeps track of all bans in a linked list. On start-up time it reads all
108  bans from the banfile and adds them to the list WITHOUT the 'actual' flag
109  set since we don't know for sure they are still set in the channel.
110 
111  The banlist will be requested right after joining, and all bans reported in
112  the channel will be added to the Dancer list. If the ban already is in the
113  list, the 'actual' flag will be set to show it is a "real" ban.
114 
115  Unbanning of 'enforced' bans, will keep the ban in the list until it gets
116  removed properly. (To allow it to get set again later.)
117 
118  After the banlist-completed message is received, all non-enforced bans
119  should get removed from the list. As soon as the bot gets opped, it should
120  make sure that all enforced bans that aren't marked as 'actual' are banned
121  (if 'autobanupdate' is set ON, otherwise the command 'banupdate' must be
122  invoked)!
123  If 18 bans (or more) are already set, the bot should notify that in public
124  when bans are set to alert that the banlist is almost full.
125 
126  If the banlist-completed message is received and *no* bans in the list have
127  been set to 'actual', we put the bot in 'perhaps-we're-on-a-newly-started-
128  server-that-soon-will-netjoin-and-get-a-lot-of-server-bans' position. This
129  means when we get the next server-ban, we will start a timer (~30 secs or
130  so) and within that time *not* unban the server bans. When we are in this
131  position, we should of course still put back the enforced bans. The not-
132  unbanned bans are of course added to the list as any normal bans. The
133  'possibly-newstarted-server' flag timeouts in 30 mins.
134 
135  *MOST PROBLEMATIC CASE*
136  When the bot joins a channel without bans. (This following scenario only
137  applies to this no-bans situation.) Everything else is normal, some
138  persons ban and some unban. Then all of a sudden (they always seem to happen
139  all of a sudden) we get one server-ban (or several ones).
140  How do we know if that server-ban should be unbanned or if we should count
141  that ban as that "first" server-ban?
142 
143  Solution:
144 
145  o Dancer stores all unbans (for at least an hour or so) and if the server-
146    ban matches one of those it will get unbanned and the 'possibly-fresh-
147    server' flag is set off.
148 
149 ****************************************************************************/
150 
151 typedef struct WarnStruct {
152   struct Header h;
153   char *pattern;     /* Pattern to warn for WITHOUT NICK */
154   char *nickpattern; /* Nick part of the pattern */
155   char *nick;        /* Nick name of the added user (if known) */
156   char *warning;     /* Text */
157   char *warner;      /* Adding person (if known) */
158   long warns;        /* Number of caused warnings */
159   time_t settime;    /* Time of the warnadd */
160   time_t lastwarn;   /* Time of the last warning */
161   long flags;        /* To enable different kind of actions, i.e kickban the
162                         really bad ones instead of just alerting */
163   int id;            /* Unique number to avoid trouble */
164 } itemwarn;
165 
166 typedef struct Banstruct {
167   struct Header h;
168   int flags;
169   char *who;
170   char *bannick;
171   char *banpattern;
172   long bansecs;
173   time_t bantime;
174   char *banreason;
175   int id;
176   int unbans;
177   time_t unbantime; /* For enforced-bans checks */
178 } itemban;
179 
180 /* Define flags stored on save */
181 #define BANWRITE(x) ((x) & (BAN_ENFORCE | BAN_ENFTIMEOUT))
182 
183 bool bantimeouts = FALSE;   /* Disable timeout until end of ban-list is
184                                received */
185 bool possiblyfresh = FALSE; /* This might be a restarted server!! */
186 int possiblyfreshtime;      /* Timeout timer for refreshed server netheals */
187 
188 itemban *banHead;
189 itemkick *kickHead;
190 itemwarn *warnHead;
191 
192 
193 static int banId;  /* Used to make unique IDs to every single ban.
194                       This is increased on *all* bans and never decreased! */
195 static int warnId; /* Same for the warning system */
196 
197 /* --- BanKick ---------------------------------------------------- */
198 
BanKick(char * nick,char * banthis,char * message,char * remove)199 void BanKick(char *nick, char *banthis, char *message, char *remove)
200 {
201   itemguest *g;
202 
203   snapshot;
204   if (nick && (g = FindNick(nick))) {
205     if (remove)
206       Mode("-o-b+b %s %s %s", nick, remove, banthis);
207     else
208       Mode("-o+b %s %s", nick, banthis);
209     StickyKick(g, message);
210   }
211   else {
212     if (remove)
213       Mode("-b+b %s %s", remove, banthis);
214     else
215       Mode("+b %s", banthis);
216   }
217 }
218 
219 /* --- NewBan ----------------------------------------------------- */
220 
NewBan(int flags,char * who,char * nick,char * pattern,int secs,time_t when,char * reason)221 itemban *NewBan(int flags,
222                 char *who, char *nick, char *pattern,
223                 int secs, time_t when, char *reason)
224 {
225   itemban *b;
226 
227   snapshot;
228   b = NewEntry(itemban);
229   if (b) {
230     InsertLast(banHead, b);
231     if (who && !StrEqualCase(who, "*"))
232       b->who = StrDuplicate(who);
233     if (nick && !StrEqualCase(nick, "*"))
234       b->bannick = StrDuplicate(nick);
235     b->banpattern = StrDuplicate(pattern);
236     b->bansecs   = secs;
237     b->bantime   = when;
238     b->flags     = flags;
239     b->id        = ++banId;
240     if (reason)
241       b->banreason = StrDuplicate(reason);
242   }
243   return b;
244 }
245 
FreeBan(void * v)246 void FreeBan(void *v)
247 {
248   itemban *b;
249 
250   snapshot;
251   b = (itemban *)v;
252   if (b) {
253     if (b->who)
254       StrFree(b->who);
255     if (b->bannick)
256       StrFree(b->bannick);
257     if (b->banpattern)
258       StrFree(b->banpattern);
259     if (b->banreason)
260       StrFree(b->banreason);
261   }
262 }
263 
264 /* --- AddToBanList ----------------------------------------------- */
265 
266 /*
267  * New return code. Returns the flags of the ban prior to this call.
268  */
269 
AddToBanList(int flags,char * who,char * nick,char * pattern,int secs,char * reason)270 int AddToBanList(int flags, char *who, char *nick, char *pattern,
271                  int secs, char *reason)
272 {
273   int result;
274   itemban *b;
275 
276   snapshot;
277   for (b = First(banHead); b; b = Next(b)) {
278     if (StrEqual(b->banpattern, pattern)) {
279       result = b->flags; /* Return this */
280       if (!(b->flags & BAN_ACTUAL)) {
281         if (b->flags & BAN_UNBAN) {
282           b->flags = flags | (b->flags & BAN_ENFORCE); /* New mode flags */
283           if (secs >= 0) {
284             b->bansecs = secs;
285             b->bantime = now;
286           }
287 
288           if (b->who)
289             StrFree(b->who);
290           b->who = who ? StrDuplicate(who) : NULL;
291 
292           if (b->bannick)
293             StrFree(b->bannick);
294           b->bannick = nick ? StrDuplicate(nick) : NULL;
295 
296           if (reason) {
297             if (b->banreason)
298               StrFree(b->banreason);
299             b->banreason = StrDuplicate(reason);
300           }
301         }
302         else
303           b->flags |= (flags & (BAN_ACTUAL | BAN_ENFORCE)); /* True and real ban */
304       }
305       return result;
306     }
307   }
308   NewBan(flags, who, nick, pattern, (secs < 0) ? 0 : secs, now, reason);
309   return 0; /* Ban didn't exist */
310 }
311 
312 /* --- CountBans -------------------------------------------------- */
313 
CountBans(int flags)314 int CountBans(int flags)
315 {
316   int count = 0;
317   itemban *b;
318 
319   snapshot;
320   for (b = First(banHead); b; b = Next(b)) {
321     if (b->flags & flags)
322       count++;
323   }
324   return count;
325 }
326 
327 /* --- Unbanned --------------------------------------------------- */
328 
Unbanned(char * from,char * pattern,bool bot)329 void Unbanned(char *from, char *pattern, bool bot)
330 {
331   itemguest *g;
332   itemban *b;
333 
334   snapshot;
335   for (b = First(banHead); b; b = Next(b)) {
336     if (StrEqual(b->banpattern, pattern)) {
337       b->flags &= ~(BAN_ACTUAL | BAN_SENTUNBAN | BAN_SENTBAN);
338       b->flags |= BAN_UNBAN;
339       if (b->who)
340         StrFree(b->who);
341       b->who = from ? StrDuplicate(from) : NULL;
342       b->unbans++;
343       if (b->flags & BAN_ENFORCE) {
344         g = FindNick(from);
345         if (bot || (g && g->ident->user &&
346                    (g->ident->user->level >= BAN_ENFORCELEVEL))) {
347           /*
348            * This was unbanned by a power-user. It is then removed just
349            * as any other ban.
350            */
351           b->flags &= ~BAN_ENFORCE;
352         }
353         else if ((now - b->unbantime) < 2*SECINMIN) {
354           /*
355            * This was unbanned *again* within 2 minutes, let's sleep for
356            * BANRETRY_ENFORCE minutes and then ban it again!
357            */
358           b->flags |= BAN_ENFTIMEOUT;
359         }
360         else if (botop) {
361           Mode("+b %s", b->banpattern); /* Enforce! */
362           b->flags |= BAN_SENTBAN;
363         }
364       }
365       b->unbantime = now;
366       return;
367     }
368   }
369 
370   b = NewBan(BAN_UNBAN, from, "*", pattern, 0, now, NULL);
371   if (b)
372     b->unbantime = now;
373 }
374 
375 /* --- ChangeInBanList -------------------------------------------- */
376 
ChangeInBanList(char * who,char * bannick,int newsecs,char * pattern,char flag,char * newreason)377 bool ChangeInBanList(char *who, char *bannick, int newsecs,
378                      char *pattern, char flag, char *newreason)
379 {
380   int id;
381   itemban *b;
382 
383   snapshot;
384   id = atoi(bannick);
385   for (b = First(banHead); b; b = Next(b)) {
386     if ((b->id == id) ||
387         (b->bannick && IRCEqual(b->bannick, bannick) ||
388         StrEqual(b->banpattern, bannick))) {
389       if (b->who)
390         StrFree(b->who);
391       b->who  = StrDuplicate(who);
392       /* b->bantime = now; */
393       if (newsecs >= 0)
394         b->bansecs = newsecs;
395       if (CHBAN_ENFORCE == flag)
396         b->flags |= BAN_ENFORCE; /* Switch on */
397       else if (CHBAN_UNENFORCE == flag)
398         b->flags &= ~BAN_ENFORCE; /* Switch off */
399       else if (CHBAN_REASON == flag) {
400         if (b->banreason)
401           StrFree(b->banreason);
402         b->banreason = StrDuplicate(newreason);
403       }
404       StrCopy(pattern, b->banpattern);
405       return TRUE;
406     }
407   }
408   return FALSE;
409 }
410 
411 /* --- BanInit ---------------------------------------------------- */
412 
413 /*
414  * This should be done at bot startup. None of these bans are *proved*
415  * to be real, but we still want to know about them. Keep all those in the
416  * list and perform actions later. (At end-of-banlist and when opped)
417  */
418 
BanInit(void)419 void BanInit(void)
420 {
421   char buf[MAXLINE];
422   char by[MIDBUFFER];
423   char nick[NICKLEN+1];
424   char pattern[MIDBUFFER];
425   char reason[BIGBUFFER];
426   int secs, when, flags;
427   FILE *f;
428 
429   snapshot;
430   banHead = NewList(itemban);
431 
432   if ((char)0 == banfile[0])
433     return;
434 
435   f = fopen(banfile, "r");
436   if (f) {
437     while (fgets(buf, sizeof(buf), f)) {
438       reason[0] = (char)0;
439       if (6 <= StrScan(buf, "%"MIDBUFFERTXT"s %"NICKLENTXT"s %"MIDBUFFERTXT"s %d %d %d %"BIGBUFFERTXT"[^\n]",
440                        by, nick, pattern, &flags, &secs, &when, reason)) {
441         NewBan(flags | BAN_UNSET, by, nick, pattern, secs, (time_t)when,
442                reason[0] ? reason : NULL);
443       }
444     }
445     fclose(f);
446   }
447 }
448 
449 /* --- BanSave ---------------------------------------------------- */
450 
BanSave(void)451 void BanSave(void)
452 {
453   char tempfile[MIDBUFFER];
454   bool ok = TRUE;
455   itemban *b;
456   FILE *f;
457 
458   snapshot;
459   if ((char)0 == banfile[0])
460     return;
461 
462   StrFormatMax(tempfile, sizeof(tempfile), "%s~", banfile);
463 
464   f = fopen(tempfile, "w");
465   if (f) {
466     for (b = First(banHead); b; b = Next(b)) {
467       if (0 > fprintf(f, "%s %s %s %d %d %d %s\n",
468                       b->who ? b->who : "*", b->bannick ? b->bannick : "*",
469                       b->banpattern, BANWRITE(b->flags), b->bansecs,
470                       b->bantime, b->banreason ? b->banreason : "")) {
471         ok = FALSE;
472         break;
473       }
474     }
475     fclose(f);
476 
477     if (ok)
478       rename(tempfile, banfile);
479   }
480 }
481 
482 /* --- BanList ---------------------------------------------------- */
483 
BanList(char * from,bool all,bool unbanned,char * match)484 int BanList(char *from, bool all, bool unbanned, char *match)
485 {
486   char buffer[BIGBUFFER];
487   int numlisted = 0;
488   itemban *b;
489 
490   snapshot;
491   for (b = First(banHead); b; b = Next(b)) {
492     /* Only show ACTUAL, UNBANned or bans */
493     if (!(b->flags & (BAN_ACTUAL | BAN_UNBAN | BAN_ENFTIMEOUT))) {
494       continue;
495     }
496     if (!unbanned && (b->flags & BAN_UNBAN)) {
497       if (!(b->flags & BAN_ENFTIMEOUT))
498         continue;
499     }
500     if (!Match(b->banpattern, match))
501       continue;
502 
503     /* Display the ban! */
504     ++numlisted;
505     StrFormatMax(buffer, sizeof(buffer), "%d %s", b->id, b->banpattern);
506 
507     if (b->bannick && !StrEqualCase(b->bannick, "*")) {
508       if (b->flags & BAN_GUESSNICK)
509         StrFormatAppendMax(buffer, sizeof(buffer), " {%s}", b->bannick);
510       else
511         StrFormatAppendMax(buffer, sizeof(buffer), " (%s)", b->bannick);
512     }
513     if (b->who && !StrEqualCase(b->who, "*")) {
514       StrFormatAppendMax(buffer, sizeof(buffer), GetText(msg_ban_by), b->who);
515     }
516     if (b->bansecs && !(b->flags & BAN_UNBAN)) {
517       StrFormatAppendMax(buffer, sizeof(buffer), GetText(msg_ban_another),
518                          SecsToString(b->bansecs + b->bantime - now));
519     }
520     if (b->flags & BAN_ENFORCE) {
521       StrAppendMax(buffer, sizeof(buffer), " [ENFORCE]");
522     }
523     if (b->flags & BAN_UNBAN) {
524       StrAppendMax(buffer, sizeof(buffer), " [UNBANNED]");
525     }
526     Send(from, buffer);
527 
528     if (all) {
529       buffer[0] = (char)0;
530       if (b->bantime) {
531         StrFormatAppendMax(buffer, sizeof(buffer), GetText(msg_ban_ago),
532                            TimeAgo(b->bantime));
533       }
534       if (b->flags & BAN_UNBAN) {
535         if (b->unbantime) {
536           StrFormatAppendMax(buffer, sizeof(buffer), GetText(msg_unban_ago),
537                              TimeAgo(b->unbantime));
538         }
539       }
540       else {
541         if (b->bansecs) {
542           StrFormatAppendMax(buffer, sizeof(buffer), GetText(msg_time_ban),
543                              SecsToString(b->bansecs));
544         }
545       }
546       if (b->banreason) {
547         StrFormatAppendMax(buffer, sizeof(buffer), " \"%s\"", b->banreason);
548       }
549       Send(from, buffer);
550     }
551   }
552   return numlisted;
553 }
554 
555 /* --- BanCleanup ------------------------------------------------- */
556 
BanCleanup(void)557 void BanCleanup(void)
558 {
559   snapshot;
560   BanSave();
561   DeleteList(banHead, FreeBan);
562 }
563 
BanDisable(void)564 void BanDisable(void)
565 {
566   itemban *b;
567 
568   snapshot;
569   bantimeouts = FALSE; /* No more timeouts until we get a new banlist */
570   for (b = First(banHead); b; b = Next(b))
571     b->flags &= ~BAN_ACTUAL;
572 }
573 
574 /* ---- BanTimeout ------------------------------------------------ */
575 
BanTimeout(void)576 void BanTimeout(void)
577 {
578   char buffer[BIGBUFFER];
579   int len = 0;
580   int num = 0;
581   itemban *b, *next;
582 
583   snapshot;
584   if (!bantimeouts)
585     return;
586 
587   for (b = First(banHead); b; b = next) {
588     next = Next(b);
589     if (b->flags & BAN_ACTUAL) {
590       if (botop) {
591         /*
592          * This is only done while and if the bot is channel operator.
593          * We only check actual bans
594          */
595         if (b->bansecs &&
596             ((b->bantime + b->bansecs) <= now) &&
597             !(b->flags & BAN_SENTUNBAN)) {
598           /*
599            * This ban has timed out now!
600            */
601           Mode("-b %s", b->banpattern);
602 
603           /*
604            * We mark this ban as sent to the server to prevent it from
605            * getting unbanned again due to server lag.
606            */
607           b->flags |= BAN_SENTUNBAN;
608 
609           if (b->bansecs > 20*SECINMIN) {
610             /*
611              * Only if more than 20 minutes since the ban was called.
612              */
613             if (reportban && !mute) {
614               if (b->bannick && !StrEqualCase(b->bannick, "*")) {
615                 Sayf(GetDefaultText(msg_report_unban_nick),
616                      b->bannick, TimeAgo(b->bantime), b->banreason);
617               }
618               else
619                 Sayf(GetDefaultText(msg_report_unban), TimeAgo(b->bantime));
620 
621               /* RemoveFromBanList(bannick, bandomain);
622                * We intercept the mode change to the channel anyway and store
623                * this unban!
624                */
625             }
626           }
627         }
628       }
629     }
630     else {
631       /*
632        * We check for non-actual non-enforced bans, that have been unbanned
633        * for more than REFRESH_TIMEOUT minutes. They are removed from the
634        * list.
635        */
636       if (!(b->flags & BAN_ENFORCE) &&
637           ((b->bantime + REFRESH_TIMEOUT) <= now) &&
638           ((b->unbantime + REFRESH_TIMEOUT) <= now)) {
639         DeleteEntry(banHead, b, FreeBan); /* Removed */
640         continue;
641       }
642       else if ((b->flags & BAN_ENFORCE) &&
643                (b->flags & BAN_SENTBAN) &&
644                !(b->flags & BAN_ACTUAL) &&
645                ((b->bantime + 10) <= now)) {
646         /*
647          * This ban is:
648          * ENFORCED + SENT and yet not ACTUAL 10 seconds since sending.
649          * In my world that means the ban wasn't possible to set. Remove it
650          * from the internal list.
651          */
652         DeleteEntry(banHead, b, FreeBan); /* Removed */
653         continue;
654       }
655       else if (botop && (b->flags & BAN_ENFORCE) &&
656                        !(b->flags & BAN_SENTBAN)) {
657         if (b->flags & BAN_ENFTIMEOUT) {
658           if ((now - b->unbantime) > BANRETRY_ENFORCE*SECINMIN) {
659             num++;
660             StrFormatMax(&buffer[len], sizeof(buffer) - len, " %s", b->banpattern);
661             len += StrLength(&buffer[len]);
662             b->flags &= ~BAN_ENFTIMEOUT;
663             b->flags |= BAN_SENTBAN;
664           }
665         }
666         else {
667           num++;
668           StrFormatMax(&buffer[len], sizeof(buffer) - len, " %s", b->banpattern);
669           len += StrLength(&buffer[len]);
670           b->flags |= BAN_SENTBAN;
671         }
672         if (3 == num) {
673           Mode("+bbb%s", buffer);
674           num = len = 0;
675         }
676       }
677     }
678   }
679 
680   if (num)
681     Mode("+bbb%s", buffer);
682 
683   if (possiblyfresh && ((now - uptime) > REFRESH_TIMEOUT))
684     possiblyfresh = FALSE;
685 
686   if (possiblyfreshtime) {
687     if ((now - possiblyfreshtime) > SERVERBANTIMEOUT)
688       possiblyfreshtime = 0;
689   }
690 }
691 
FindToUnban(char * what,int flags)692 static itemban *FindToUnban(char *what, int flags)
693 {
694   int id;
695   itemban *b;
696 
697   snapshot;
698   id = atoi(what);
699   for (b = First(banHead); b; b = Next(b)) {
700     if ((b->id == id) ||
701         (b->bannick && IRCEqual(b->bannick, what)) ||
702         StrEqual(b->banpattern, what)) {
703       if (b->flags & flags)
704         return b;
705     }
706   }
707   return NULL;
708 }
709 
UnBan(char * from,char * nick)710 bool UnBan(char *from, char *nick)
711 {
712   itemban *b;
713 
714   snapshot;
715   b = FindToUnban(nick, BAN_ACTUAL);
716   if (NULL == b) {
717     b = FindToUnban(nick, BAN_ALL);
718     if (NULL == b)
719       return FALSE;
720   }
721 
722   b->flags &= ~BAN_ENFORCE;
723   b->bansecs = 0;
724 
725   if (!(b->flags & BAN_ACTUAL)) {
726     if (b->flags & BAN_ENFTIMEOUT) {
727       /*
728        * The ban isn't active in the channel, but is ENFORCED UNBANNED.
729        * Allow removal here.
730        */
731       Unbanned(from, b->banpattern, TRUE);
732     }
733     return TRUE;
734   }
735   Mode("-b %s", b->banpattern); /* This gets removed from the list by the
736                                    normal OnMode() stuff in server.c */
737   b->flags |= BAN_SENTUNBAN;
738   if (reportban && !mute) {
739     if (b->bannick && !StrEqualCase(b->bannick, "*")) {
740       /* This was *not* a "caught" ban */
741       Sayf(GetDefaultText(msg_report_unbanforce_nick), from, b->bannick);
742     }
743     else
744       Sayf(GetDefaultText(msg_report_unbanforce), from);
745   }
746   return TRUE;
747 }
748 
749 /* --- IsUnban ---------------------------------------------------- */
750 
751 /*
752  * Returns TRUE if the specified pattern matches one of the
753  * unbanned ones.
754  */
755 
IsUnban(char * pattern)756 bool IsUnban(char *pattern)
757 {
758   itemban *b;
759 
760   snapshot;
761   for (b = First(banHead); b; b = Next(b)) {
762     if (StrEqual(b->banpattern, pattern) && (b->flags & BAN_UNBAN))
763       return TRUE;
764   }
765   return FALSE;
766 }
767 
768 /* --- IsBan ------------------------------------------------------ */
769 
770 /*
771  * Checks for *actual* bans matching the parameter.
772  */
773 
IsBan(char * checkthis)774 bool IsBan(char *checkthis)
775 {
776   itemban *b;
777 
778   snapshot;
779   for (b = First(banHead); b; b = Next(b)) {
780     if ((b->flags &BAN_ACTUAL) && Match(checkthis, b->banpattern))
781       return TRUE;
782   }
783   return FALSE;
784 }
785 
786 /* --- IsBanListed ------------------------------------------------ */
787 
788 /*
789  * The functions below differ from IsBan() in the way that they doen't
790  * only check for actual bans, but also for to-be bans that is marked to
791  * get added.
792  */
793 
IsBanListed(char * checkthis)794 bool IsBanListed(char *checkthis)
795 {
796   itemban *b;
797 
798   snapshot;
799   for (b = First(banHead); b; b = Next(b)) {
800     if (!(b->flags & (BAN_UNSET | BAN_UNBAN)) && Match(checkthis, b->banpattern))
801       return TRUE;
802   }
803   return FALSE;
804 }
805 
806 /* --- IsMatchBan ------------------------------------------------- */
807 
IsMatchBan(char * checkthis,int * amount)808 char *IsMatchBan(char *checkthis, int *amount)
809 {
810   char *pattern = NULL;
811   int num = 0;
812   itemban *b;
813 
814   snapshot;
815   for (b = First(banHead); b; b = Next(b)) {
816     if (!(b->flags & (BAN_UNSET | BAN_UNBAN)) && Match(b->banpattern, checkthis)) {
817       pattern = b->banpattern;
818       num++;
819     }
820   }
821   *amount = num;
822   return pattern;
823 }
824 
825 /* --- IllegalBan ------------------------------------------------- */
826 
IllegalBan(char * checkthis)827 bool IllegalBan(char *checkthis)
828 {
829   extern bool banprotect;
830   char nickpart[NICKLEN+1], nickmatch[NICKLEN+1] = "*";
831   char *hostpart, *hostmatch;
832   register itemuser *u;
833   register itemlist *t;
834 
835   snapshot;
836   /* Split the pattern into nick and host parts respectively */
837   hostmatch = StrIndex(checkthis, '!');
838   if (hostmatch) {
839     hostmatch++;
840     StrScan(checkthis, "%"NICKLENTXT"[^!]", nickmatch);
841   }
842   else
843     hostmatch = checkthis;
844 
845   if (banprotect) {
846     for (u = First(userHead); u; u = Next(u)) {
847       if (u->flags.banprotect) {
848         for (t = First(u->domainhead); t; t = Next(t)) {
849           hostpart = StrIndex((char *)t->pointer, '!');
850           if (hostpart) {
851             /* Make hostpart point to userhost part of the pattern */
852             hostpart++;
853             if (1 == StrScan((char *)t->pointer, "%"NICKLENTXT"[^!]", nickpart)) {
854               /* If nick part doesn't match, the whole pattern doesn't match */
855               if (!MatchMatch(nickmatch, nickpart))
856                 continue;
857             }
858           }
859           else
860             hostpart = (char *)t->pointer;
861 
862           if (MatchMatch(hostmatch, hostpart))
863             return TRUE;
864 
865           if (IsPrefix(hostmatch))
866             if (MatchMatch(&hostmatch[1], hostpart))
867               return TRUE;
868 
869           if (IsPrefix(hostpart))
870             if (MatchMatch(hostmatch, &hostpart[1]))
871               return TRUE;
872         }
873       }
874     }
875   }
876   return (Match(nickname, nickmatch) && Match(myaddr, hostmatch));
877 }
878 
879 #define SCANBAN_TIMED         1
880 #define SCANBAN_NONENFORCED   2
881 #define SCANBAN_TIMEDENFORCED 3
882 #define SCANBAN_ENFORCED      4
883 
ScanBan(long options)884 static itemban *ScanBan(long options)
885 {
886   int i;
887   int max = 0;
888   itemban *b;
889   itemban *thisone = NULL;
890 
891   snapshot;
892   for (b = First(banHead); b; b = Next(b)) {
893     if (!(b->flags & BAN_ACTUAL) || (b->flags & BAN_SENTUNBAN))
894       continue;
895     switch (options) {
896     case SCANBAN_TIMED:
897       if (b->bansecs && !(b->flags & BAN_ENFORCE)) {
898         i = b->bansecs + b->bantime - now; /* Time left */
899         if (!thisone || (i < max)) {
900           max = i;     /* Max is minimum in this case! */
901           thisone = b; /* Our favourite so far */
902         }
903       }
904       break;
905     case SCANBAN_NONENFORCED:
906       if (!b->bansecs && !(b->flags & BAN_ENFORCE)) {
907         i = b->bantime; /* Age */
908         if (!thisone || (i < max)) {
909           max = i;     /* Max the oldest ban! */
910           thisone = b; /* Our favourite so far */
911         }
912       }
913       break;
914     case SCANBAN_TIMEDENFORCED:
915       if (b->bansecs && (b->flags & BAN_ENFORCE)) {
916         i = b->bansecs + b->bantime - now; /* Time left */
917         if (!thisone || (i < max)) {
918           max = i;     /* Max is minimum in this case! */
919           thisone = b; /* Our favourite so far */
920         }
921       }
922       break;
923     case SCANBAN_ENFORCED:
924       if (!b->bansecs && (b->flags & BAN_ENFORCE)) {
925         i = b->bantime; /* Age */
926         if (!thisone || (i < max)) {
927           max = i;     /* Max the oldest ban! */
928           thisone = b; /* Our favourite so far */
929         }
930       }
931       break;
932     }
933   }
934   return thisone;
935 }
936 
937 /*
938  * UnbanLoprio()
939  *
940  * Unbans the least important ban in the channel. The algo to find that one
941  * is:
942  * 1 - the NON-ENFORCED ban with the least time left
943  * 2 - the oldest NON-ENFORCED ban
944  * 3 - the ENFORCED ban with the least time left
945  * 4 - the oldest ENFORCED ban
946  *
947  * Returns TRUE if a pattern was sent for unban.
948  */
949 
UnbanLoprio(char * from)950 bool UnbanLoprio(char *from)
951 {
952   itemban *target;
953 
954   snapshot;
955   target = ScanBan(SCANBAN_TIMED);
956   if (NULL == target) {
957     target = ScanBan(SCANBAN_NONENFORCED);
958     if (NULL == target) {
959       target = ScanBan(SCANBAN_TIMEDENFORCED);
960       if (NULL == target)
961         target = ScanBan(SCANBAN_ENFORCED);
962     }
963   }
964   if (target)
965     return UnBan(from, target->banpattern);
966   else
967     return FALSE;
968 }
969 
970 /***************************************************************************
971 *  _  ___      _             _          __  __
972 * | |/ (_) ___| | __     ___| |_ _   _ / _|/ _|
973 * | ' /| |/ __| |/ /____/ __| __| | | | |_| |_
974 * | . \| | (__|   <_____\__ \ |_| |_| |  _|  _|
975 * |_|\_\_|\___|_|\_\    |___/\__|\__,_|_| |_|
976 *
977 ***************************************************************************/
978 
979 #define KICK_PERIOD 600 /* Kicks within this period are counted   */
980 #define KICK_NUMBER 15  /* Number of patterns to keep in the list */
981 
982 #define KICKBAN_BOT 3   /* This many bot kicks makes a ban */
983 #define KICKBAN_REG 5   /* This many regular kicks make a ban */
984 
985 /*
986  * Clue-time:
987  * ----------
988  * All kicks are recorded, the last KICK_NUMBER (default 10) patterns are
989  * stored in memory.
990  * Kicks that that bot *intends* to do due to violation of rules are marked
991  * separately, as are regular (non-bot) kicks of trusted users.
992  * 3 bot-marks/kicks or 5 regular+bot kicks make the bot ban the user and
993  * kick all other users joined from the same account-pattern.
994  *
995  */
996 
997 /*
998  * 'ident' is the user that is kicked.
999  * 'kicker' is the full userhost of the person that kicked.
1000  * 'kickmsg' reason/message to assign to the kick
1001  * 'status' is the level of the kicker, 3 levels exist
1002  *
1003  * Returns TRUE if the amount of kicks during the last KICK_PERIOD seconds
1004  * of that same pattern is KICKBAN_REG or more, or if the amount of botkicks
1005  * is KICKBAN_BOT or greater.
1006  *
1007  * This function only stores KICK_NUMBER of kicks in the list, the
1008  * oldest one should be removed from the list when the list gets filled.
1009  */
AddKick(itemident * ident,char * kicker,char * kickmsg,int status)1010 bool AddKick(itemident *ident, char *kicker, char *kickmsg, int status)
1011 {
1012   extern int kickQ;
1013   static time_t failedsiteban = 0;
1014   char kickpattern[MIDBUFFER];
1015   char userpattern[MIDBUFFER];
1016   char sitepattern[MIDBUFFER];
1017   char *banpattern;
1018   bool banthis = FALSE;
1019   bool forget = FALSE;
1020   time_t oldtime = 999999999;
1021   int num = 0;
1022   itemkick *oldkick = NULL;
1023   itemkick *k;
1024 
1025   snapshot;
1026   for (k = First(kickHead); k; k = Next(k)) {
1027     num++;
1028     if (StrEqual(k->pattern, ident->userdomain)) {
1029       if (k->norecurse)
1030         return FALSE;
1031       k->norecurse = TRUE; /* Disable recursion at this point */
1032       if ((k->lastkick + KICK_PERIOD) < now)
1033         k->botkicks = k->kicks = k->trustkicks = 0;
1034       k->totkicks++; /* Total amount, never set to zero */
1035       switch (status) {
1036       case KICK_BOT:
1037         k->botkicks++;
1038         break;
1039       case KICK_COMMON:
1040         k->kicks++;
1041         break;
1042       case KICK_TRUSTEDUSER:
1043         k->trustkicks++;
1044         break;
1045       }
1046       if (!ident->illegalname &&
1047          /* Illegal names won't be banned since we won't have their host mask
1048             properly */
1049 
1050          ((banuserkicks && ((k->kicks + k->trustkicks) >= KICKBAN_REG)) ||
1051           (k->botkicks >= KICKBAN_BOT))) {
1052         /*
1053          * This user has now got kicked enough number of times.
1054          * Wipe him off the channel for good (=ban).
1055          */
1056         char *inthere;
1057         int number;
1058         AlertMode(ALERT_ON);
1059 
1060         NickToPattern(ident->nick, kickpattern, FALSE); /* User match */
1061 
1062         StrFormatMax(userpattern, sizeof(userpattern), "*!%s", kickpattern); /* User ban pattern */
1063         StrFormatMax(sitepattern, sizeof(sitepattern), "*!*@%s", ident->puserdomain); /* Site ban */
1064 
1065         inthere = IsMatchBan(sitepattern, &number);
1066 
1067         if (!inthere || (number > 1) ||
1068             HostISP(ident->host) ||
1069             DontSiteBan(ident->host)) {
1070           /* The site is not previously banned OR
1071            * banned with more than one pattern OR
1072            * joined from a hostisp site OR
1073            * joined from a site-ban-prevented ISP,
1074            * so use a normal ban.
1075            */
1076           banpattern = userpattern;
1077           inthere = NULL; /* DON'T unban the previous one */
1078           if ((number > 1) && !DontSiteBan(ident->host)) {
1079             /* Well, we could still KICK all of them from that site */
1080             StrFormatMax(kickpattern, sizeof(kickpattern), "*@%s", ident->puserdomain); /* Site match */
1081             if ((failedsiteban + TRIGGER_RED_ALERT) > now)
1082               AlertMode(ALERT_RED); /* Go highest level */
1083             failedsiteban = now;
1084           }
1085         }
1086         else {
1087           /* Another account already banned on that site, now ban
1088              the whole site! */
1089           if (!Match(inthere, userpattern)) {
1090             /*
1091              * Exactly this account isn't previously banned. Remove the
1092              * user ban and ban the site.
1093              */
1094             banpattern = sitepattern;
1095             StrFormatMax(kickpattern, sizeof(kickpattern), "*@%s", ident->puserdomain); /* Site match */
1096           }
1097           else {
1098             /*
1099              * This pattern is already banned!
1100              */
1101             forget = TRUE;
1102           }
1103         }
1104         if (botop && !forget) {
1105           if (!(AddToBanList(BAN_SENTBAN,
1106                              kicker, k->nick, banpattern, defbantime,
1107                              k->kickmsg?k->kickmsg:
1108                              GetDefaultText(msg_kick_repeated_misbehavior))
1109               & BANISTHERE)) {
1110             if (inthere)
1111               Mode("-b+b %s %s", inthere, banpattern);
1112             else
1113               Mode("+b %s", banpattern);
1114           }
1115         }
1116         /* kickQ++; - force these kicks to get enqueued */
1117         KickAll(kickpattern, kickmsg?kickmsg:
1118                 GetDefaultText(msg_kick_learn_your_lesson));
1119         banthis = TRUE;
1120       }
1121       k->lastkick = now;
1122       if (k->nick)
1123         StrFree(k->nick);
1124       k->nick = StrDuplicate(ident->nick);
1125 
1126       if (kickmsg) {
1127         if (k->kickmsg)
1128           StrFree(k->kickmsg);
1129         k->kickmsg = StrDuplicate(kickmsg);
1130       }
1131       if (k->kicker)
1132         StrFree(k->kicker);
1133       k->kicker = StrDuplicate(kicker);
1134       k->norecurse = FALSE; /* Enable recursion again */
1135       return banthis;
1136     }
1137     /*
1138      * Remember the oldest item in the list in case we
1139      * wanna remove it.
1140      */
1141     if (oldtime > k->lastkick) {
1142       oldtime = k->lastkick;
1143       oldkick = k;
1144     }
1145   }
1146 
1147   /* After being discussed on the mailing list, initiated by a bug report by
1148      Tero J�nk�, I think this may fix the problem he has experienced during
1149      extensive bot floods. */
1150 
1151   /* Ok, this is the second version -- as proposed by Tero after 4.12 has
1152      been released. This decreases the size of the list in a much quicker
1153      fashion down to the KICK_NUMBER size as soon as possible again after an
1154      extensive attack. */
1155 
1156   while ((KICK_NUMBER <= num--) &&
1157          oldkick &&
1158          !oldkick->norecurse &&
1159          ((now - oldtime) > KICK_PERIOD)) {
1160     /* there are more entries than should fit AND
1161        we have an oldest kick AND
1162        the oldest kick is not being in use AND
1163        it was kicked > KICK_PERIOD seconds ago */
1164 
1165     /* Remove this entry: */
1166     DeleteEntry(kickHead, oldkick, FreeKick);
1167 
1168     /* Do we have more entries to remove? */
1169     if (KICK_NUMBER <= num) {
1170       oldtime = 999999999;
1171       oldkick = NULL;
1172 
1173       /* Find the oldest entry in the list now */
1174       for (k = First(kickHead); k; k = Next(k)) {
1175         if (oldtime > k->lastkick) {
1176           oldtime = k->lastkick;
1177           oldkick = k;
1178         }
1179       }
1180     }
1181     else
1182       break; /* No, we break out of loop */
1183   }
1184 
1185   k = NewEntry(itemkick);
1186   if (k) {
1187     InsertLast(kickHead, k);
1188     switch (status) {
1189     case KICK_BOT:
1190       k->botkicks = 1;
1191       break;
1192     case KICK_COMMON:
1193       k->kicks = 1;
1194       break;
1195     case KICK_TRUSTEDUSER:
1196       k->trustkicks = 1;
1197       break;
1198     }
1199     k->lastkick = now;
1200     k->kicker = StrDuplicate(kicker);
1201 
1202     /*
1203      * This used the NickToPattern() previously which is no good.
1204      * The only thing this "pattern" (that more strictly should be
1205      * considered as a straight string) is for us to recognize when
1206      * a user with the _exact_ same pattern is kicked again.
1207      * We don't need no fancy stuff for that. - Bagder
1208      */
1209     k->pattern = StrDuplicate(ident->userdomain);
1210     k->nick = StrDuplicate(ident->nick);
1211     if (kickmsg)
1212       k->kickmsg = StrDuplicate(kickmsg);
1213   }
1214   return FALSE;
1215 }
1216 
FreeKick(void * v)1217 void FreeKick(void *v)
1218 {
1219   itemkick *k;
1220 
1221   snapshot;
1222   k = (itemkick *)v;
1223   if (k) {
1224     if (k->pattern)
1225       StrFree(k->pattern);
1226     if (k->kicker)
1227       StrFree(k->kicker);
1228     if (k->nick)
1229       StrFree(k->nick);
1230     if (k->kickmsg)
1231       StrFree(k->kickmsg);
1232   }
1233 }
1234 
KickAll(char * pattern,char * msg)1235 void KickAll(char *pattern, char *msg)
1236 {
1237   itemguest *g;
1238 
1239   snapshot;
1240   for (g = First(guestHead); g; g = Next(g)) {
1241     if (g->ident->host &&
1242         Match(g->ident->host, pattern) &&
1243         (g->ident->level < LEVELBOT)) { /* Can't pattern-kick BOT or higher */
1244       StickyKick(g, msg);
1245     }
1246   }
1247 }
1248 
KickInit(void)1249 void KickInit(void)
1250 {
1251   snapshot;
1252   kickHead = NewList(itemkick);
1253 }
1254 
KickList(char * from,char * line)1255 void KickList(char *from, char *line)
1256 {
1257   char showmatch[MIDBUFFER] = "*";
1258   int num = 0;
1259   int total = 0;
1260   itemkick *k;
1261 
1262   snapshot;
1263   if (line) {
1264     StrScan(line, "%"MIDBUFFERTXT"s", showmatch);
1265   }
1266 
1267   for (k = First(kickHead); k; k = Next(k)) {
1268     total++;
1269     if (Match(k->pattern, showmatch)) {
1270       num++;
1271       Sendf(from, GetText(msg_kicklist_item),
1272             k->pattern, k->nick ? k->nick : UNKNOWN,
1273             k->kicker, TimeAgo(k->lastkick),
1274             k->kicks + k->trustkicks);
1275     }
1276   }
1277 
1278   if (0 == num)
1279     Send(from, GetText(msg_no_matching_kick));
1280   else if (total != num)
1281     Sendf(from, GetText(msg_X_out_of_Y_items), num ,total);
1282 }
1283 
1284 /*
1285  * Perform a full *!*@* style matching against all entries in the
1286  * kicklist, and return the matching entry.
1287  */
KickMatch(char * pattern)1288 itemkick *KickMatch(char *pattern)
1289 {
1290   char nickpart[NICKLEN+1];
1291   char hostpart[MIDBUFFER];
1292   itemkick *k;
1293 
1294   snapshot;
1295   if (2 == StrScan(pattern, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
1296                    nickpart, hostpart)) {
1297     for (k = First(kickHead); k; k = Next(k)) {
1298       if (Match(k->nick, nickpart) &&
1299           Match(k->pattern, hostpart))
1300         return k;
1301     }
1302   }
1303   return NULL;
1304 }
1305 
KickCleanup(void)1306 void KickCleanup(void)
1307 {
1308   snapshot;
1309   DeleteList(kickHead, FreeKick);
1310 }
1311 
1312 /*
1313  * KickFromQueue()
1314  *
1315  * Called when there is believed to exist users on the channel that
1316  * are marked to get kicked.
1317  * Performs a kick, with flood-control
1318  *
1319  * Returns TRUE if a kick-marked guest is left in the queue when this
1320  * function quits.
1321  *
1322  * The ircd server does support a sequence like nick1,nick2,nick3,nick4
1323  * to get specified. Although this will give us very high penalty
1324  * in the server. Let's refrain from using that until we have a better
1325  * scheme. I.e we _cannot_ use multi-kick on >1 nick every 2nd second.
1326  */
1327 /* #define MULTIKICK */
1328 
1329 
KickFromQueue(time_t * lastsentp)1330 bool KickFromQueue(time_t *lastsentp)
1331 {
1332 #ifndef MULTIKICK
1333   static int Qkicks = 0;
1334   itemguest *g;
1335 
1336   snapshot;
1337   for (g = First(guestHead); g; g = Next(g)) {
1338     if (g->flags.kick && !g->flags.kicked) {
1339       if ((++Qkicks > 3) && ((*lastsentp + 1) >= now))
1340         return TRUE;
1341       WriteServer("KICK %s %s :%s",
1342                   channel, g->ident->nick,
1343                   (g->kickmsg ? g->kickmsg : nickname));
1344       *lastsentp = now;
1345       Logf(LOGDBUG, "Q-Sent KICK %s to server", g->ident->nick);
1346       g->flags.kicked = TRUE;
1347       return TRUE;
1348     }
1349   }
1350   Qkicks = 0;
1351   return FALSE;
1352 #else
1353 
1354 /*
1355  * This cute define sets the maximum amount of nicks to send for kick
1356  * in each single KICK command. */
1357 #define KICK_THIS_MANY 3
1358 
1359   static int Qkicks = 0;
1360   bool found = FALSE;
1361   itemguest *g;
1362   int amount = 0;
1363   itemguest *victim[KICK_THIS_MANY];
1364 
1365   /* 'evenmore' is TRUE if there are more users left to kick after this
1366      round of multi-kick has been performed */
1367   bool evenmore = FALSE;
1368 
1369   snapshot;
1370   for (g = First(guestHead); g; g = Next(g)) {
1371     if (g->flags.kick && !g->flags.kicked) {
1372       found = TRUE;
1373       if (amount < KICK_THIS_MANY) {
1374         victim[ amount++ ] = g;
1375         continue;
1376       }
1377       evenmore = TRUE;
1378       break;
1379     }
1380   }
1381   if (found) {
1382     char nickbuffer[(NICKLEN+1)*KICK_THIS_MANY+2];
1383     int len = 0;
1384     int i;
1385 
1386     if ((++Qkicks > 3) && ((*lastsentp + 1) >= now))
1387       return TRUE;
1388 
1389     for (i=0; i < amount; i++) {
1390       /* Add name to the buffer */
1391       StrFormatMax(&nickbuffer[len], sizeof(nickbuffer) - len, "%s%s", i ? "," : "", victim[i]->ident->nick);
1392       /* Mark nick that we have sent him for termination */
1393       victim[i]->flags.kicked = TRUE;
1394       /* Set pointer to end of string */
1395       len += StrLength(StrLength(&nickbuffer[len]);
1396     }
1397 
1398     /* Set multi-nick KICK to server, works for ircd2.8 and later */
1399     WriteServer("KICK %s %s :%s",
1400                 channel, nickbuffer,
1401                 (amount > 1) ? "byebye" :
1402                 (victim[0]->kickmsg ? victim[0]->kickmsg : "byebye"));
1403     *lastsentp = now;
1404     Logf(LOGDBUG, "Q-Sent KICK %s to server", nickbuffer);
1405 
1406     if (!evenmore)
1407       Qkicks = 0;
1408     return evenmore;
1409   }
1410   Qkicks = 0;
1411   return FALSE;
1412 #endif
1413 }
1414 
1415 /***********************************************************************
1416  ***********************************************************************
1417  *
1418  * WARNING system functions below this separator
1419  *
1420  ***********************************************************************
1421  **********************************************************************/
1422 
1423 #define WARNING_FREQUENCY 3600 /* minimum time between warnings displayed
1424                                   for the same pattern */
1425 
FreeWarn(void * v)1426 void FreeWarn(void *v)
1427 {
1428   itemwarn *w;
1429 
1430   snapshot;
1431   w = (itemwarn *)v;
1432   if (w) {
1433     if (w->pattern)
1434       StrFree(w->pattern);
1435     if (w->nickpattern)
1436       StrFree(w->nickpattern);
1437     if (w->warner)
1438       StrFree(w->warner);
1439     if (w->warning)
1440       StrFree(w->warning);
1441     if (w->nick)
1442       StrFree(w->nick);
1443   }
1444 }
1445 
WarnCleanup(void)1446 void WarnCleanup(void)
1447 {
1448   snapshot;
1449   WarnSave();
1450   DeleteList(warnHead, FreeWarn);
1451 }
1452 
1453 /* --- FindWarn --------------------------------------------------- */
1454 
FindWarn(char * nick,char * userhost)1455 itemwarn *FindWarn(char *nick, char *userhost)
1456 {
1457   itemwarn *w;
1458 
1459   snapshot;
1460   if (nick && userhost) {
1461     for (w = First(warnHead); w; w = Next(w)) {
1462       if (Match(nick, w->nickpattern) && Match(userhost, w->pattern))
1463         return w;
1464     }
1465   }
1466   return NULL;
1467 }
1468 
1469 /* --- WarnAdd ---------------------------------------------------- */
1470 
WarnAdd(char * nick,char * pattern,char * nickpattern,char * warning,char * adder,int flags)1471 bool WarnAdd(char *nick, /* Nick of the abuser */
1472              char *pattern,
1473              char *nickpattern,
1474              char *warning, /* Text */
1475              char *adder, /* Who added this */
1476              int flags) /* Various options */
1477 {
1478   itemwarn *w;
1479 
1480   snapshot;
1481   w = FindWarn(nickpattern, pattern);
1482   if (w)
1483     return FALSE; /* Already warned for! */
1484 
1485   w = NewEntry(itemwarn);
1486   if (w) {
1487     InsertLast(warnHead, w);
1488     w->pattern = StrDuplicate(pattern);
1489     w->nickpattern = StrDuplicate(nickpattern);
1490     w->warning = StrDuplicate(warning);
1491     w->warner  = adder ? StrDuplicate(adder) : NULL;
1492     w->nick    = nick ? StrDuplicate(nick) : NULL;
1493     w->settime = now;
1494     w->id      = ++warnId;
1495     w->flags   = flags;
1496   }
1497   return TRUE;
1498 }
1499 
1500 /* --- WarnSave --------------------------------------------------- */
1501 
WarnSave(void)1502 void WarnSave(void)
1503 {
1504   char tempfile[MIDBUFFER];
1505   bool ok = TRUE;
1506   itemwarn *w;
1507   FILE *f;
1508 
1509   snapshot;
1510   if ((char)0 == warnfile[0])
1511     return;
1512 
1513   StrFormatMax(tempfile, sizeof(tempfile), "%s~", warnfile);
1514 
1515   f = fopen(tempfile, "w");
1516   if (f) {
1517     for (w = First(warnHead); w; w = Next(w)) {
1518       if ((w->lastwarn && ((now - w->lastwarn) < warnmonths*SECINMONTH)) ||
1519           ((now - w->settime) < warnmonths*SECINMONTH)) {
1520         if (0 > fprintf(f, "%s %s %s!%s %d %d %d %d %s\n",
1521                         w->warner ? w->warner : "*",
1522                         w->nick ? w->nick : "*",
1523                         w->nickpattern,
1524                         w->pattern,
1525                         w->warns, w->settime, w->lastwarn, w->flags,
1526                         w->warning)) {
1527           ok = FALSE;
1528           break;
1529         }
1530       }
1531     }
1532     fclose(f);
1533 
1534     if (ok)
1535       rename(tempfile, warnfile);
1536   }
1537 }
1538 
1539 /* --- WarnDel ---------------------------------------------------- */
1540 
WarnDel(char * from,char * what)1541 bool WarnDel(char *from, char *what) /* Nick or warnId */
1542 {
1543   int id;
1544   itemwarn *w;
1545 
1546   snapshot;
1547   id = atoi(what);
1548   for (w = First(warnHead); w; w = Next(w)) {
1549     if ((w->id == id) ||
1550         (w->nick && IRCEqual(w->nick, what)) ||
1551         StrEqual(w->pattern, what)) {
1552       Sendf(from, GetText(msg_warn_pattern_removed), w->nickpattern, w->pattern);
1553       DeleteEntry(warnHead, w, FreeWarn); /* Removed */
1554       return TRUE;
1555     }
1556   }
1557   return FALSE;
1558 }
1559 
1560 /* --- WarnList --------------------------------------------------- */
1561 
WarnList(char * from,char * match,int flags)1562 bool WarnList(char *from, char *match, int flags)
1563 {
1564   char buffer[MIDBUFFER];
1565   int shown = 0;
1566   itemwarn *w;
1567 
1568   snapshot;
1569   for (w = First(warnHead); w; w = Next(w)) {
1570     StrFormatMax(buffer, sizeof(buffer), "%s!%s", w->nickpattern, w->pattern);
1571     if (Match(buffer, match)) {
1572       if ((flags & WLIST_BAN) &&
1573          !(w->flags & WARNF_KICKBAN))
1574         continue;
1575       shown++;
1576       Sendf(from, "%d %s!%s (%s) %s %s%s",
1577             w->id, w->nickpattern, w->pattern, w->nick ? w->nick : "*",
1578             GetText(msg_is),
1579             w->warning,
1580             (w->flags & WARNF_KICKBAN) ? " [KICKBAN]" : "");
1581       if (flags & WLIST_ALL) {
1582         if (w->lastwarn)
1583           StrFormatMax(buffer, sizeof(buffer), GetText(msg_warn_list_all),
1584                        w->warns, TimeAgo(w->lastwarn));
1585         else
1586           StrCopyMax(buffer, sizeof(buffer), GetText(msg_warn_not_warned));
1587         Sendf(from, GetText(msg_warn_by_when),
1588               w->warner ? w->warner : UNKNOWN, TimeAgo(w->settime), buffer);
1589       }
1590     }
1591   }
1592   return (shown ? TRUE : FALSE);
1593 }
1594 
1595 /* --- WarnInit --------------------------------------------------- */
1596 
WarnInit(void)1597 void WarnInit(void)
1598 {
1599   char buf[MAXLINE];
1600   char by[MIDBUFFER];
1601   char nick[NICKLEN+1];
1602   char bigpattern[MIDBUFFER];
1603   char hostpattern[MIDBUFFER];
1604   char nickpattern[NICKLEN+1];
1605   char reason[BIGBUFFER];
1606   int flags, settime, last, warns;
1607   itemwarn *w;
1608   FILE *f;
1609 
1610   snapshot;
1611   warnHead = NewList(itemwarn);
1612 
1613   if ((char)0 == warnfile[0])
1614     return;
1615 
1616   f = fopen(warnfile, "r");
1617   if (f) {
1618     while (fgets(buf, MAXLINE, f)) {
1619       hostpattern[0] = nickpattern[0] = reason[0] = 0;
1620       if (7 <= StrScan(buf, "%"MIDBUFFERTXT"s %"NICKLENTXT"s %"MIDBUFFERTXT"s %d %d %d %d %"BIGBUFFERTXT"[^\n]",
1621                        by, nick, bigpattern, &warns, &settime, &last, &flags,
1622                        reason)) {
1623         /* Designed like this to work with the old nick-less version: */
1624         if (2 > StrScan(bigpattern, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
1625                          nickpattern, hostpattern)) {
1626           StrCopyMax(hostpattern, sizeof(hostpattern), bigpattern);
1627           StrCopyMax(nickpattern, sizeof(nickpattern), "*");
1628         }
1629 
1630         w = NewEntry(itemwarn);
1631         if (w) {
1632           InsertLast(warnHead, w);
1633           w->pattern = StrDuplicate(hostpattern);
1634           w->nickpattern = StrDuplicate(nickpattern);
1635           w->warning = StrDuplicate(reason);
1636           w->warner  = !StrEqualCase(by, "*") ? StrDuplicate(by) : NULL;
1637           w->nick    = !StrEqualCase(nick, "*") ? StrDuplicate(nick) : NULL;
1638           w->warns   = warns;
1639           w->settime = settime;
1640           w->lastwarn= last;
1641           w->flags   = flags;
1642           w->id      = ++warnId;
1643         }
1644       }
1645     }
1646     fclose(f);
1647   }
1648 }
1649 
1650 /* --- WarnCheck -------------------------------------------------- */
1651 
WarnCheck(char * nick,char * userhost)1652 bool WarnCheck(char *nick, char *userhost)
1653 {
1654   char buffer[MIDBUFFER];
1655   itemwarn *w;
1656 
1657   snapshot;
1658   w = FindWarn(nick, userhost);
1659   if (w) {
1660     AlertMode(ALERT_ON);
1661     StrFormatMax(buffer, sizeof(buffer), "%s!%s",
1662                  w->nickpattern, w->pattern); /* If ban is wanted */
1663     if (botop && (w->flags & WARNF_KICKBAN)) {
1664       w->warns++;
1665       if (AddToBanList(BAN_SENTBAN, nickname, nick, buffer,
1666                        defbantime, w->warning) & BANISTHERE)
1667         Kick(nick, w->warning);
1668       else
1669         BanKick(nick, buffer, w->warning, NULL);
1670       w->lastwarn = now;
1671       Logf(LOGWARN, "Autokickbanned %s matched %s!%s", nick,
1672            w->nickpattern, w->pattern);
1673     }
1674     else if ((now - w->lastwarn) > WARNING_FREQUENCY) {
1675 #ifdef HAVE_LIBFPL
1676       if (runfpl(RUN_ALERT, nick, FPLRUN_PRE))
1677         return TRUE;
1678 #endif
1679       if (!mute) {
1680         w->warns++;
1681         Actionf(GetDefaultText(msg_alerts), nick, w->warning);
1682         w->lastwarn = now;
1683       }
1684       Logf(LOGWARN, "%s matches %s!%s, '%s'", nick, w->nickpattern,
1685            w->pattern, w->warning);
1686       runfpl(RUN_ALERT, nick, FPLRUN_POST);
1687     }
1688     return TRUE;
1689   }
1690   return FALSE;
1691 }
1692 
1693 /* --- CountWarns ------------------------------------------------- */
1694 
CountWarns(int flags)1695 int CountWarns(int flags)
1696 {
1697   int count = 0;
1698   itemwarn *w;
1699 
1700   snapshot;
1701   for (w = First(warnHead); w; w = Next(w)) {
1702     if (flags && !(w->flags & flags))
1703       continue;
1704     count++;
1705   }
1706   return count;
1707 }
1708