1 /* event_neonspam_chanmsg.c - NeonServ v5.3
2 * Copyright (C) 2011-2012 Philipp Kreil (pk910)
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.
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.
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/>.
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);
24 static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup);
25 static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, unsigned int warn, unsigned int punish);
27 struct neonspam_event_chanmsg_cache {
28 struct ClientSocket *client;
29 struct ChanUser *chanuser;
30 struct NeonSpamSettings *settings;
37 static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
38 struct ClientSocket *client = getChannelBot(chan, BOTID);
39 if(!client || (user->flags & USERFLAG_ISBOT)) return; //we can't "see" this event
40 loadNeonSpamSettings(chan);
41 struct NeonSpamSettings *settings = chan->spam_settings;
42 struct ChanUser *chanuser = getChanUser(user, chan);
43 if(!settings || !chanuser) return;
44 #define NEONSPAM_CHANMSG_DO_SCANOPS(FLAG) ((settings->flags & FLAG) || !(chanuser->flags & CHANUSERFLAG_OPPED))
45 #define NEONSPAM_CHANMSG_DO_SCANVOICE(FLAG) ((settings->flags & FLAG) || !(chanuser->flags & CHANUSERFLAG_VOICED))
46 #define NEONSPAM_CHANMSG_DO_EXCEPT(INDEX) (settings->exceptlevel[INDEX] != 0)
47 #define NEONSPAM_CHANMSG_NEED_WHO(INDEX) (settings->exceptlevel[INDEX] != 501)
50 unsigned int warn = 0;
51 unsigned int punish = 0;
53 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)) {
54 result = neonspam_spamscan(settings, chanuser, message);
56 case SPAMSERV_CHECK_IGNORE:
58 case SPAMSERV_CHECK_WARN:
59 warn |= SPAMSETTINGS_SPAMSCAN;
60 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_SPAMEXCINDEX))
63 case SPAMSERV_CHECK_PUNISH:
64 punish |= SPAMSETTINGS_SPAMSCAN;
65 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_SPAMEXCINDEX))
70 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)) {
71 result = neonspam_floodscan(settings, chanuser);
73 case SPAMSERV_CHECK_IGNORE:
75 case SPAMSERV_CHECK_WARN:
76 warn |= SPAMSETTINGS_FLOODSCAN;
77 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_FLOODEXCINDEX))
80 case SPAMSERV_CHECK_PUNISH:
81 punish |= SPAMSETTINGS_FLOODSCAN;
82 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_FLOODEXCINDEX))
87 if((settings->flags & SPAMSETTINGS_BOTNETSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_BOTNETSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_BOTNETSCAN_VOICE)) {
88 result = neonspam_botnetscan(client, settings, chanuser, message);
90 case SPAMSERV_CHECK_DEAD:
92 case SPAMSERV_CHECK_IGNORE:
96 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)) {
97 result = neonspam_capsscan(settings, chanuser, message);
99 case SPAMSERV_CHECK_IGNORE:
101 case SPAMSERV_CHECK_WARN:
102 warn |= SPAMSETTINGS_CAPSSCAN;
103 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_CAPSEXCINDEX))
106 case SPAMSERV_CHECK_PUNISH:
107 punish |= SPAMSETTINGS_CAPSSCAN;
108 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_CAPSEXCINDEX))
113 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)) {
114 result = neonspam_digitscan(settings, chanuser, message);
116 case SPAMSERV_CHECK_IGNORE:
118 case SPAMSERV_CHECK_WARN:
119 warn |= SPAMSETTINGS_DIGITSCAN;
120 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_DIGITEXCINDEX))
123 case SPAMSERV_CHECK_PUNISH:
124 punish |= SPAMSETTINGS_DIGITSCAN;
125 if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_DIGITEXCINDEX))
133 //whois the user to check against exceptlevel
134 if(!needwho || (user->flags & USERFLAG_ISAUTHED)) {
135 neonspam_event_chanmsg_punish(client, chanuser, settings, warn, punish);
137 struct neonspam_event_chanmsg_cache *cache = malloc(sizeof(*cache));
139 perror("malloc() failed");
142 cache->client = client;
143 cache->chanuser = chanuser;
144 cache->settings = settings;
146 cache->punish = punish;
147 get_userauth(user, neonspam_event_chanmsg_nick_lookup, cache);
151 #undef NEONSPAM_CHANMSG_DO_SCANOPS
152 #undef NEONSPAM_CHANMSG_DO_SCANVOICE
153 #undef NEONSPAM_CHANMSG_DO_EXCEPT
154 #undef NEONSPAM_CHANMSG_NEED_WHO
157 static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup) {
158 struct neonspam_event_chanmsg_cache *cache = data;
159 neonspam_event_chanmsg_punish(cache->client, cache->chanuser, cache->settings, cache->warn, cache->punish);
163 static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, unsigned int warn, unsigned int punish) {
165 MYSQL_ROW row, defaults;
166 loadChannelSettings(chanuser->chan);
168 if(chanuser->user->flags & USERFLAG_ISAUTHED)
169 uaccess = getChannelAccess(chanuser->user, chanuser->chan);
174 if(!punishment && (punish & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) {
175 printf_mysql_query("SELECT `channel_spam_reaction`, `channel_spam_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
177 row = mysql_fetch_row(res);
178 if(!row[0] || !row[1]) {
179 printf_mysql_query("SELECT `channel_spam_reaction`, `channel_spam_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
181 defaults = mysql_fetch_row(res);
183 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM);
184 punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
185 punish_time = atoi((row[1] ? row[1] : defaults[1]));
187 if(!punishment && (punish & SPAMSETTINGS_FLOODSCAN) && settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] > uaccess) {
188 printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
190 row = mysql_fetch_row(res);
191 if(!row[0] || !row[1]) {
192 printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
194 defaults = mysql_fetch_row(res);
196 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_FLOOD);
197 punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
198 punish_time = atoi((row[1] ? row[1] : defaults[1]));
200 if(!punishment && (punish & SPAMSETTINGS_CAPSSCAN) && settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] > uaccess) {
201 printf_mysql_query("SELECT `channel_caps_reaction`, `channel_caps_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
203 row = mysql_fetch_row(res);
204 if(!row[0] || !row[1]) {
205 printf_mysql_query("SELECT `channel_caps_reaction`, `channel_caps_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
207 defaults = mysql_fetch_row(res);
209 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_CAPS);
210 punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
211 punish_time = atoi((row[1] ? row[1] : defaults[1]));
213 if(!punishment && (punish & SPAMSETTINGS_DIGITSCAN) && settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] > uaccess) {
214 printf_mysql_query("SELECT `channel_digit_reaction`, `channel_digit_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
216 row = mysql_fetch_row(res);
217 if(!row[0] || !row[1]) {
218 printf_mysql_query("SELECT `channel_digit_reaction`, `channel_digit_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
220 defaults = mysql_fetch_row(res);
222 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_DIGIT);
223 punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
224 punish_time = atoi((row[1] ? row[1] : defaults[1]));
226 if(!punishment && (warn & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) {
227 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM);
229 if(!punishment && (warn & SPAMSETTINGS_FLOODSCAN) && settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] > uaccess) {
230 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_FLOOD);
232 if(!punishment && (warn & SPAMSETTINGS_CAPSSCAN) && settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] > uaccess) {
233 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_CAPS);
235 if(!punishment && (warn & SPAMSETTINGS_DIGITSCAN) && settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] > uaccess) {
236 sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_DIGIT);
239 char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3];
240 char *banmask = NULL;
241 switch (punishment) {
243 banmask = generate_banmask(chanuser->user, banmaskBuf);
244 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));
246 int banid = (int) mysql_insert_id(get_mysql_conn());
247 char nameBuf[MAXLEN];
249 sprintf(nameBuf, "ban_%d", banid);
250 sprintf(banidBuf, "%d", banid);
251 timeq_add_name(nameBuf, punish_time, channel_ban_timeout, strdup(banidBuf));
255 banmask = generate_banmask(chanuser->user, banmaskBuf);
256 putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask);
258 putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, reason);
262 reply(client, chanuser->user, "%s", reason);
265 static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
266 //crc32 hash of the message
267 unsigned long msghash = crc32(message);
268 if(chanuser->spamnode) {
269 if(chanuser->spamnode->lastmsg == msghash) {
270 chanuser->spamnode->spamcount++;
271 if(chanuser->spamnode->spamcount == settings->spam_amount)
272 return SPAMSERV_CHECK_WARN;
273 else if(chanuser->spamnode->spamcount > settings->spam_amount)
274 return SPAMSERV_CHECK_PUNISH;
276 return SPAMSERV_CHECK_IGNORE;
279 createSpamNode(chanuser);
280 chanuser->spamnode->lastmsg = msghash;
281 chanuser->spamnode->spamcount = 1;
282 return SPAMSERV_CHECK_IGNORE;
285 static int neonspam_update_penalty(struct NeonSpamSettings *settings, struct ChanUser *chanuser, int addmsg) {
286 int last_update = time(0) - chanuser->spamnode->last_penalty_update;
288 if(last_update < MAX_FLOOD_TIME && chanuser->spamnode->floodpenalty) {
289 chanuser->spamnode->floodpenalty -= last_update * (MAX_FLOOD_TIME / settings->sensibility_time[SPAMSETTINGS_FLOODSENINDEX]);
290 if(chanuser->spamnode->floodpenalty < 0)
291 chanuser->spamnode->floodpenalty = 0;
293 chanuser->spamnode->floodpenalty = 0;
294 chanuser->spamnode->last_penalty_update = time(0);
296 chanuser->spamnode->floodpenalty += MAX_FLOOD_TIME * addmsg;
297 return chanuser->spamnode->floodpenalty / MAX_FLOOD_TIME + ((chanuser->spamnode->floodpenalty % MAX_FLOOD_TIME) ? 1 : 0);
300 static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser) {
301 if(!chanuser->spamnode)
302 createSpamNode(chanuser);
303 int messages_pending = neonspam_update_penalty(settings, chanuser, 1);
304 if(messages_pending == settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX])
305 return SPAMSERV_CHECK_WARN;
306 else if(messages_pending > settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX])
307 return SPAMSERV_CHECK_PUNISH;
309 return SPAMSERV_CHECK_IGNORE;
312 static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
313 //crc32 hash of the message
314 unsigned long msghash = crc32(message);
315 if((time(0) - settings->lastmsg_time) > BOTNETSCAN_TIME || settings->lastmsg != msghash) {
317 for(i = 0; i < BOTNETSCAN_USERS; i++) {
318 if(settings->botnicks[i]) {
319 free(settings->botnicks[i]);
320 settings->botnicks[i] = NULL;
323 settings->flags &= ~SPAMSETTINGS_KICKEDBOTQUEUE;
324 settings->lastmsg = msghash;
325 } else if(settings->lastmsg == msghash) {
327 for(i = 0; i < BOTNETSCAN_USERS; i++) {
328 if(!settings->botnicks[i]) {
329 settings->botnicks[i] = strdup(chanuser->user->nick);
331 } else if(!stricmp(chanuser->user->nick, settings->botnicks[i])) {
332 return SPAMSERV_CHECK_IGNORE;
335 if(i == BOTNETSCAN_USERS) {
336 //BOTNETSCAN_USERS exceeded
337 if(!(settings->flags & SPAMSETTINGS_KICKEDBOTQUEUE)) {
338 for(i = 0; i < BOTNETSCAN_USERS; i++) {
339 putsock(client, "KICK %s %s :%s", chanuser->chan->name, settings->botnicks[i], SPAMSERV_MSG_BOTNET);
341 settings->flags |= SPAMSETTINGS_KICKEDBOTQUEUE;
343 putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BOTNET);
344 return SPAMSERV_CHECK_DEAD;
347 settings->lastmsg_time = time(0);
348 return SPAMSERV_CHECK_IGNORE;
351 static int neonspam_capsscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
352 int caps = 0, msglen = strlen(message);
354 if(msglen <= 4) return SPAMSERV_CHECK_IGNORE;
355 for(i = 0; i < msglen; i++) {
356 if(isupper(message[i])) caps++;
358 caps = 100*caps/msglen;
359 if(caps >= settings->percent[SPAMSETTINGS_CAPSPERCENTINDEX]) {
360 if(!chanuser->spamnode)
361 createSpamNode(chanuser);
362 if(chanuser->spamnode->flags & NEONSPAMNODE_FLAG_CAPSSCAN_WARNED)
363 return SPAMSERV_CHECK_PUNISH;
365 chanuser->spamnode->flags |= NEONSPAMNODE_FLAG_CAPSSCAN_WARNED;
366 return SPAMSERV_CHECK_WARN;
369 return SPAMSERV_CHECK_IGNORE;
372 static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
373 int digit = 0, msglen = strlen(message);
375 if(msglen <= 4) return SPAMSERV_CHECK_IGNORE;
376 for(i = 0; i < msglen; i++) {
377 if(isdigit(message[i])) digit++;
379 digit = 100*digit/msglen;
380 if(digit >= settings->percent[SPAMSETTINGS_DIGITPERCENTINDEX]) {
381 if(!chanuser->spamnode)
382 createSpamNode(chanuser);
383 if(chanuser->spamnode->flags & NEONSPAMNODE_FLAG_DIGITSCAN_WARNED)
384 return SPAMSERV_CHECK_PUNISH;
386 chanuser->spamnode->flags |= NEONSPAMNODE_FLAG_DIGITSCAN_WARNED;
387 return SPAMSERV_CHECK_WARN;
390 return SPAMSERV_CHECK_IGNORE;