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