added new multi log system
[NeonServV5.git] / src / modules / NeonSpam.mod / event_neonspam_chanmsg.c
1 /* event_neonspam_chanmsg.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
19 static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser);
20 static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
21 static int neonspam_capsscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
22 static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
23 static int neonspam_badwordscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message, int punish_now, int uaccess);
24
25 static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup);
26 static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, char *message, unsigned int warn, unsigned int punish);
27
28 struct neonspam_event_chanmsg_cache {
29     struct ClientSocket *client;
30     struct ChanUser *chanuser;
31     struct NeonSpamSettings *settings;
32     char *message;
33     unsigned int warn;
34     unsigned int punish;
35 };
36
37
38
39 static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
40     struct ClientSocket *client = getChannelBot(chan, BOTID);
41     if(!client || (user->flags & USERFLAG_ISBOT)) return; //we can't "see" this event
42     loadNeonSpamSettings(chan);
43     struct NeonSpamSettings *settings = chan->spam_settings;
44     struct ChanUser *chanuser = getChanUser(user, chan);
45     if(!settings || !chanuser) return;
46     #define NEONSPAM_CHANMSG_DO_SCANOPS(FLAG) ((settings->flags & FLAG) || !(chanuser->flags & CHANUSERFLAG_OPPED))
47     #define NEONSPAM_CHANMSG_DO_SCANVOICE(FLAG) ((settings->flags & FLAG) || !(chanuser->flags & CHANUSERFLAG_VOICED))
48     #define NEONSPAM_CHANMSG_DO_EXCEPT(INDEX) (settings->exceptlevel[INDEX] != 0)
49     #define NEONSPAM_CHANMSG_NEED_WHO(INDEX) (settings->exceptlevel[INDEX] != 501)
50     //scan the message
51     int result = 0;
52     unsigned int warn = 0;
53     unsigned int punish = 0;
54     int needwho = 0;
55     if((settings->flags & SPAMSETTINGS_SPAMSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_SPAMSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_SPAMSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_SPAMEXCINDEX)) {
56         result = neonspam_spamscan(settings, chanuser, message);
57         switch(result) {
58             case SPAMSERV_CHECK_IGNORE:
59                 break;
60             case SPAMSERV_CHECK_WARN:
61                 warn |= SPAMSETTINGS_SPAMSCAN;
62                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_SPAMEXCINDEX))
63                     needwho = 1;
64                 break;
65             case SPAMSERV_CHECK_PUNISH:
66                 punish |= SPAMSETTINGS_SPAMSCAN;
67                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_SPAMEXCINDEX))
68                     needwho = 1;
69                 break;
70         }
71     }
72     if((settings->flags & SPAMSETTINGS_FLOODSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_FLOODSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_FLOODSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_FLOODEXCINDEX)) {
73         result = neonspam_floodscan(settings, chanuser);
74         switch(result) {
75             case SPAMSERV_CHECK_IGNORE:
76                 break;
77             case SPAMSERV_CHECK_WARN:
78                 warn |= SPAMSETTINGS_FLOODSCAN;
79                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_FLOODEXCINDEX))
80                     needwho = 1;
81                 break;
82             case SPAMSERV_CHECK_PUNISH:
83                 punish |= SPAMSETTINGS_FLOODSCAN;
84                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_FLOODEXCINDEX))
85                     needwho = 1;
86                 break;
87         }
88     }
89     if((settings->flags & SPAMSETTINGS_BOTNETSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_BOTNETSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_BOTNETSCAN_VOICE)) {
90         result = neonspam_botnetscan(client, settings, chanuser, message);
91         switch(result) {
92             case SPAMSERV_CHECK_DEAD:
93                 return;
94             case SPAMSERV_CHECK_IGNORE:
95                 break;
96         }
97     }
98     if((settings->flags & SPAMSETTINGS_CAPSSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_CAPSSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_CAPSSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_CAPSEXCINDEX)) {
99         result = neonspam_capsscan(settings, chanuser, message);
100         switch(result) {
101             case SPAMSERV_CHECK_IGNORE:
102                 break;
103             case SPAMSERV_CHECK_WARN:
104                 warn |= SPAMSETTINGS_CAPSSCAN;
105                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_CAPSEXCINDEX))
106                     needwho = 1;
107                 break;
108             case SPAMSERV_CHECK_PUNISH:
109                 punish |= SPAMSETTINGS_CAPSSCAN;
110                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_CAPSEXCINDEX))
111                     needwho = 1;
112                 break;
113         }
114     }
115     if((settings->flags & SPAMSETTINGS_DIGITSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_DIGITSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_DIGITSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_DIGITEXCINDEX)) {
116         result = neonspam_digitscan(settings, chanuser, message);
117         switch(result) {
118             case SPAMSERV_CHECK_IGNORE:
119                 break;
120             case SPAMSERV_CHECK_WARN:
121                 warn |= SPAMSETTINGS_DIGITSCAN;
122                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_DIGITEXCINDEX))
123                     needwho = 1;
124                 break;
125             case SPAMSERV_CHECK_PUNISH:
126                 punish |= SPAMSETTINGS_DIGITSCAN;
127                 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_DIGITEXCINDEX))
128                     needwho = 1;
129                 break;
130         }
131     }
132     if((settings->flags & SPAMSETTINGS_BADWORDSCAN)) {
133         if(user->flags & USERFLAG_ISAUTHED) {
134             result = neonspam_badwordscan(client, settings, chanuser, message, 1, getChannelAccess(chanuser->user, chanuser->chan));
135         } else
136             result = neonspam_badwordscan(client, settings, chanuser, message, 0, 0);
137         switch(result) {
138             case SPAMSERV_CHECK_DEAD:
139                 return;
140             case SPAMSERV_CHECK_IGNORE:
141                 break;
142             case SPAMSERV_CHECK_PUNISH:
143                 punish |= SPAMSETTINGS_BADWORDSCAN;
144                 needwho = 1;
145                 break;
146         }
147     }
148     //some other checks?
149     
150     if(warn || punish) {
151         //whois the user to check against exceptlevel
152         if(!needwho || (user->flags & USERFLAG_ISAUTHED)) {
153             neonspam_event_chanmsg_punish(client, chanuser, settings, message, warn, punish);
154         } else {
155             struct neonspam_event_chanmsg_cache *cache = malloc(sizeof(*cache));
156             if (!cache) {
157                 printf_log("neonspam", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
158                 return;
159             }
160             cache->client = client;
161             cache->chanuser = chanuser;
162             cache->settings = settings;
163             cache->message = strdup(message);
164             cache->warn = warn;
165             cache->punish = punish;
166             get_userauth(user, module_id, neonspam_event_chanmsg_nick_lookup, cache);
167         }
168         
169     }
170     #undef NEONSPAM_CHANMSG_DO_SCANOPS
171     #undef NEONSPAM_CHANMSG_DO_SCANVOICE
172     #undef NEONSPAM_CHANMSG_DO_EXCEPT
173     #undef NEONSPAM_CHANMSG_NEED_WHO
174 }
175
176 static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup) {
177     struct neonspam_event_chanmsg_cache *cache = data;
178     neonspam_event_chanmsg_punish(cache->client, cache->chanuser, cache->settings, cache->message, cache->warn, cache->punish);
179     free(cache->message);
180     free(cache);
181 }
182
183 static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, char *message, unsigned int warn, unsigned int punish) {
184     MYSQL_RES *res;
185     MYSQL_ROW row, defaults;
186     loadChannelSettings(chanuser->chan);
187     int uaccess = 0;
188     if(chanuser->user->flags & USERFLAG_ISAUTHED)
189         uaccess = getChannelAccess(chanuser->user, chanuser->chan);
190     char reason[MAXLEN];
191     reason[0] = '\0';
192     int punishment = 0;
193     int punish_time = 0;
194     if(!punishment && (punish & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) {
195         printf_mysql_query("SELECT `channel_spam_reaction`, `channel_spam_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
196         res = mysql_use();
197         row = mysql_fetch_row(res);
198         if(!row[0] || !row[1]) {
199             printf_mysql_query("SELECT `channel_spam_reaction`, `channel_spam_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
200             res = mysql_use();
201             defaults = mysql_fetch_row(res);
202         }
203         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM);
204         punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
205         punish_time = atoi((row[1] ? row[1] : defaults[1]));
206     }
207     if(!punishment && (punish & SPAMSETTINGS_FLOODSCAN) && settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] > uaccess) {
208         printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
209         res = mysql_use();
210         row = mysql_fetch_row(res);
211         if(!row[0] || !row[1]) {
212             printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
213             res = mysql_use();
214             defaults = mysql_fetch_row(res);
215         }
216         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_FLOOD);
217         punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
218         punish_time = atoi((row[1] ? row[1] : defaults[1]));
219     }
220     if(!punishment && (punish & SPAMSETTINGS_CAPSSCAN) && settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] > uaccess) {
221         printf_mysql_query("SELECT `channel_caps_reaction`, `channel_caps_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
222         res = mysql_use();
223         row = mysql_fetch_row(res);
224         if(!row[0] || !row[1]) {
225             printf_mysql_query("SELECT `channel_caps_reaction`, `channel_caps_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
226             res = mysql_use();
227             defaults = mysql_fetch_row(res);
228         }
229         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_CAPS);
230         punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
231         punish_time = atoi((row[1] ? row[1] : defaults[1]));
232     }
233     if(!punishment && (punish & SPAMSETTINGS_DIGITSCAN) && settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] > uaccess) {
234         printf_mysql_query("SELECT `channel_digit_reaction`, `channel_digit_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
235         res = mysql_use();
236         row = mysql_fetch_row(res);
237         if(!row[0] || !row[1]) {
238             printf_mysql_query("SELECT `channel_digit_reaction`, `channel_digit_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
239             res = mysql_use();
240             defaults = mysql_fetch_row(res);
241         }
242         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_DIGIT);
243         punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
244         punish_time = atoi((row[1] ? row[1] : defaults[1]));
245     }
246     if(!punishment && (punish & SPAMSETTINGS_BADWORDSCAN)) {
247         int result = neonspam_badwordscan(client, settings, chanuser, message, 1, uaccess);
248         switch(result) {
249             case SPAMSERV_CHECK_DEAD:
250                 return;
251             case SPAMSERV_CHECK_IGNORE:
252                 break;
253         }
254         
255     }
256     if(!punishment && (warn & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) {
257         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM);
258     }
259     if(!punishment && (warn & SPAMSETTINGS_FLOODSCAN) && settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] > uaccess) {
260         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_FLOOD);
261     }
262     if(!punishment && (warn & SPAMSETTINGS_CAPSSCAN) && settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] > uaccess) {
263         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_CAPS);
264     }
265     if(!punishment && (warn & SPAMSETTINGS_DIGITSCAN) && settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] > uaccess) {
266         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_DIGIT);
267     }
268     if(punishment) {
269         char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3];
270         char *banmask = NULL;
271         switch (punishment) {
272             case 3: //TIMEBAN
273                 banmask = generate_banmask(chanuser->user, banmaskBuf);
274                 printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chanuser->chan->channel_id, escape_string(banmask), (unsigned long) (punish_time ? (time(0) + punish_time) : 0), 0, escape_string(reason));
275                 if(punish_time) {
276                     int banid = (int) mysql_insert_id(get_mysql_conn());
277                     char nameBuf[MAXLEN];
278                     char banidBuf[20];
279                     sprintf(nameBuf, "ban_%d", banid);
280                     sprintf(banidBuf, "%d", banid);
281                     timeq_add_name(nameBuf, punish_time, module_id, channel_ban_timeout, strdup(banidBuf));
282                 }
283             case 2: //KICKBAN
284                 if(!banmask)
285                     banmask = generate_banmask(chanuser->user, banmaskBuf);
286                 putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask);
287             case 1: //KICK
288                 putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, reason);
289                 break;
290         }
291     } else if(*reason)
292         reply(client, chanuser->user, "%s", reason);
293 }
294
295 static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
296     //crc32 hash of the message
297     unsigned long msghash = crc32(message);
298     if(chanuser->spamnode) {
299         if(chanuser->spamnode->lastmsg == msghash) {
300             chanuser->spamnode->spamcount++;
301             if(chanuser->spamnode->spamcount == settings->spam_amount)
302                 return SPAMSERV_CHECK_WARN;
303             else if(chanuser->spamnode->spamcount > settings->spam_amount)
304                 return SPAMSERV_CHECK_PUNISH;
305             else
306                 return SPAMSERV_CHECK_IGNORE;
307         }
308     } else
309         createSpamNode(chanuser);
310     chanuser->spamnode->lastmsg = msghash;
311     chanuser->spamnode->spamcount = 1;
312     return SPAMSERV_CHECK_IGNORE;
313 }
314
315 static int neonspam_update_penalty(struct NeonSpamSettings *settings, struct ChanUser *chanuser, int addmsg) {
316     int last_update = time(0) - chanuser->spamnode->last_penalty_update;
317     if(last_update) {
318         if(last_update < MAX_FLOOD_TIME && chanuser->spamnode->floodpenalty) {
319             chanuser->spamnode->floodpenalty -= last_update * (MAX_FLOOD_TIME / settings->sensibility_time[SPAMSETTINGS_FLOODSENINDEX]);
320             if(chanuser->spamnode->floodpenalty < 0)
321                 chanuser->spamnode->floodpenalty = 0;
322         } else
323             chanuser->spamnode->floodpenalty = 0;
324         chanuser->spamnode->last_penalty_update = time(0);
325     }
326     chanuser->spamnode->floodpenalty += MAX_FLOOD_TIME * addmsg;
327     return chanuser->spamnode->floodpenalty / MAX_FLOOD_TIME + ((chanuser->spamnode->floodpenalty % MAX_FLOOD_TIME) ? 1 : 0);
328 }
329
330 static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser) {
331     if(!chanuser->spamnode)
332         createSpamNode(chanuser);
333     int messages_pending = neonspam_update_penalty(settings, chanuser, 1);
334     if(messages_pending == settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX])
335         return SPAMSERV_CHECK_WARN;
336     else if(messages_pending > settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX])
337         return SPAMSERV_CHECK_PUNISH;
338     else
339         return SPAMSERV_CHECK_IGNORE;
340 }
341
342 static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
343     //crc32 hash of the message
344     unsigned long msghash = crc32(message);
345     if((time(0) - settings->lastmsg_time) > BOTNETSCAN_TIME || settings->lastmsg != msghash) {
346         int i;
347         for(i = 0; i < BOTNETSCAN_USERS; i++) {
348             if(settings->botnicks[i]) {
349                 free(settings->botnicks[i]);
350                 settings->botnicks[i] = NULL;
351             }
352         }
353         settings->flags &= ~SPAMSETTINGS_KICKEDBOTQUEUE;
354         settings->lastmsg = msghash;
355     } else if(settings->lastmsg == msghash) {
356         int i;
357         for(i = 0; i < BOTNETSCAN_USERS; i++) {
358             if(!settings->botnicks[i]) {
359                 settings->botnicks[i] = strdup(chanuser->user->nick);
360                 break;
361             } else if(!stricmp(chanuser->user->nick, settings->botnicks[i])) {
362                 return SPAMSERV_CHECK_IGNORE;
363             }
364         }
365         if(i == BOTNETSCAN_USERS) {
366             //BOTNETSCAN_USERS exceeded
367             if(!(settings->flags & SPAMSETTINGS_KICKEDBOTQUEUE)) {
368                 for(i = 0; i < BOTNETSCAN_USERS; i++) {
369                     putsock(client, "KICK %s %s :%s", chanuser->chan->name, settings->botnicks[i], SPAMSERV_MSG_BOTNET);
370                 }
371                 settings->flags |= SPAMSETTINGS_KICKEDBOTQUEUE;
372             }
373             putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BOTNET);
374             return SPAMSERV_CHECK_DEAD;
375         }
376     }
377     settings->lastmsg_time = time(0);
378     return SPAMSERV_CHECK_IGNORE;
379 }
380
381 static int neonspam_capsscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
382     int caps = 0, msglen = strlen(message);
383     int i;
384     if(msglen <= 4) return SPAMSERV_CHECK_IGNORE;
385     for(i = 0; i < msglen; i++) {
386         if(isupper(message[i])) caps++;
387     }
388     caps = 100*caps/msglen;
389     if(caps >= settings->percent[SPAMSETTINGS_CAPSPERCENTINDEX]) {
390         if(!chanuser->spamnode)
391             createSpamNode(chanuser);
392         if(chanuser->spamnode->flags & NEONSPAMNODE_FLAG_CAPSSCAN_WARNED)
393             return SPAMSERV_CHECK_PUNISH;
394         else {
395             chanuser->spamnode->flags |= NEONSPAMNODE_FLAG_CAPSSCAN_WARNED;
396             return SPAMSERV_CHECK_WARN;
397         }
398     }
399     return SPAMSERV_CHECK_IGNORE;
400 }
401
402 static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
403     int digit = 0, msglen = strlen(message);
404     int i;
405     if(msglen <= 4) return SPAMSERV_CHECK_IGNORE;
406     for(i = 0; i < msglen; i++) {
407         if(isdigit(message[i])) digit++;
408     }
409     digit = 100*digit/msglen;
410     if(digit >= settings->percent[SPAMSETTINGS_DIGITPERCENTINDEX]) {
411         if(!chanuser->spamnode)
412             createSpamNode(chanuser);
413         if(chanuser->spamnode->flags & NEONSPAMNODE_FLAG_DIGITSCAN_WARNED)
414             return SPAMSERV_CHECK_PUNISH;
415         else {
416             chanuser->spamnode->flags |= NEONSPAMNODE_FLAG_DIGITSCAN_WARNED;
417             return SPAMSERV_CHECK_WARN;
418         }
419     }
420     return SPAMSERV_CHECK_IGNORE;
421 }
422
423 static int neonspam_badwordscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message, int punish_now, int uaccess) {
424     struct NeonSpamBadword *badword;
425     int kick_user = 0;
426     int ban_user = 0;
427     int staticban_user = 0;
428     int ban_duration = 0;
429     int checked_defaults = 0;
430     int apply_default_reaction = 0;
431     for(badword = settings->badwords; badword; badword = badword->next) {
432         if(!match(badword->badword, message)) {
433             if(badword->use_defaults) {
434                 if(checked_defaults == 2) continue;
435                 else if(!checked_defaults) {
436                     if(!(settings->flags & SPAMSETTINGS_BADWORDSCAN_OPS) && (chanuser->flags & CHANUSERFLAG_OPPED)) {
437                         checked_defaults = 2;
438                         continue;
439                     }
440                     if(!(settings->flags & SPAMSETTINGS_BADWORDSCAN_VOICE) && (chanuser->flags & CHANUSERFLAG_VOICED)) {
441                         checked_defaults = 2;
442                         continue;
443                     }
444                     if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] && settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] < 501) {
445                         if(!punish_now)
446                             return SPAMSERV_CHECK_PUNISH;
447                         if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] <= uaccess) {
448                             checked_defaults = 2;
449                             continue;
450                         }
451                     }
452                     checked_defaults = 1;
453                 }
454             } else {
455                 if(!badword->scan_ops && (chanuser->flags & CHANUSERFLAG_OPPED)) continue;
456                 if(!badword->scan_voice && (chanuser->flags & CHANUSERFLAG_VOICED)) continue;
457                 if(badword->exceptlevel && badword->exceptlevel < 501) {
458                     if(!punish_now)
459                         return SPAMSERV_CHECK_PUNISH;
460                     if(badword->exceptlevel <= uaccess) {
461                         checked_defaults = 2;
462                         continue;
463                     }
464                 }
465             }
466             if(badword->use_default_reaction) {
467                 apply_default_reaction = 1;
468             } else {
469                 switch(badword->reaction) {
470                 case 2:
471                     staticban_user = 1;
472                     if(badword->reaction_time > ban_duration)
473                         ban_duration = badword->reaction_time;
474                 case 1:
475                     ban_user = 1;
476                 case 0:
477                     kick_user = 1;
478                 }
479             }
480         }
481     }
482     if(apply_default_reaction) {
483         MYSQL_RES *res;
484         MYSQL_ROW row, defaults;
485         loadChannelSettings(chanuser->chan);
486         printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
487         res = mysql_use();
488         row = mysql_fetch_row(res);
489         if(!row[0] || !row[1]) {
490             printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
491             res = mysql_use();
492             defaults = mysql_fetch_row(res);
493         }
494         int reaction = atoi((row[0] ? row[0] : defaults[0]));
495         int reaction_time = atoi((row[1] ? row[1] : defaults[1]));
496         switch(reaction) {
497         case 2:
498             staticban_user = 1;
499             if(reaction_time > ban_duration)
500                 ban_duration = reaction_time;
501         case 1:
502             ban_user = 1;
503         case 0:
504             kick_user = 1;
505         }
506     }
507     if(!kick_user) return SPAMSERV_CHECK_IGNORE;
508     char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3];
509     char *banmask = NULL;
510     if(staticban_user) {
511         banmask = generate_banmask(chanuser->user, banmaskBuf);
512         printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chanuser->chan->channel_id, escape_string(banmask), (unsigned long) (ban_duration ? (time(0) + ban_duration) : 0), 0, escape_string(SPAMSERV_MSG_BADWORD));
513         if(ban_duration) {
514             int banid = (int) mysql_insert_id(get_mysql_conn());
515             char nameBuf[MAXLEN];
516             char banidBuf[20];
517             sprintf(nameBuf, "ban_%d", banid);
518             sprintf(banidBuf, "%d", banid);
519             timeq_add_name(nameBuf, ban_duration, module_id, channel_ban_timeout, strdup(banidBuf));
520         }
521     }
522     if(ban_user) {
523         if(!banmask)
524             banmask = generate_banmask(chanuser->user, banmaskBuf);
525         putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask);
526     }
527     putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BADWORD);
528     return SPAMSERV_CHECK_DEAD;
529 }