added NeonSpam Badword scanner
[NeonServV5.git] / src / modules / NeonSpam.mod / event_neonspam_chanmsg.c
index 4ab7c833b23c72867924953d6419845066df6a3e..b70a7a5f565bcf7d1935640df3dfd5c4bf50a6ef 100644 (file)
@@ -20,14 +20,16 @@ static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser
 static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
 static int neonspam_capsscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
 static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
+static int neonspam_badwordscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message, int punish_now, int uaccess);
 
 static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup);
-static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, unsigned int warn, unsigned int punish);
+static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, char *message, unsigned int warn, unsigned int punish);
 
 struct neonspam_event_chanmsg_cache {
     struct ClientSocket *client;
     struct ChanUser *chanuser;
     struct NeonSpamSettings *settings;
+    char *message;
     unsigned int warn;
     unsigned int punish;
 };
@@ -127,12 +129,28 @@ static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan,
                 break;
         }
     }
+    if((settings->flags & SPAMSETTINGS_BADWORDSCAN)) {
+        if(user->flags & USERFLAG_ISAUTHED) {
+            result = neonspam_badwordscan(client, settings, chanuser, message, 1, getChannelAccess(chanuser->user, chanuser->chan));
+        } else
+            result = neonspam_badwordscan(client, settings, chanuser, message, 0, 0);
+        switch(result) {
+            case SPAMSERV_CHECK_DEAD:
+                return;
+            case SPAMSERV_CHECK_IGNORE:
+                break;
+            case SPAMSERV_CHECK_PUNISH:
+                punish |= SPAMSETTINGS_BADWORDSCAN;
+                needwho = 1;
+                break;
+        }
+    }
     //some other checks?
     
     if(warn || punish) {
         //whois the user to check against exceptlevel
         if(!needwho || (user->flags & USERFLAG_ISAUTHED)) {
-            neonspam_event_chanmsg_punish(client, chanuser, settings, warn, punish);
+            neonspam_event_chanmsg_punish(client, chanuser, settings, message, warn, punish);
         } else {
             struct neonspam_event_chanmsg_cache *cache = malloc(sizeof(*cache));
             if (!cache) {
@@ -142,6 +160,7 @@ static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan,
             cache->client = client;
             cache->chanuser = chanuser;
             cache->settings = settings;
+            cache->message = strdup(message);
             cache->warn = warn;
             cache->punish = punish;
             get_userauth(user, module_id, neonspam_event_chanmsg_nick_lookup, cache);
@@ -156,11 +175,12 @@ static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan,
 
 static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup) {
     struct neonspam_event_chanmsg_cache *cache = data;
-    neonspam_event_chanmsg_punish(cache->client, cache->chanuser, cache->settings, cache->warn, cache->punish);
+    neonspam_event_chanmsg_punish(cache->client, cache->chanuser, cache->settings, cache->message, cache->warn, cache->punish);
+    free(cache->message);
     free(cache);
 }
 
-static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, unsigned int warn, unsigned int punish) {
+static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, char *message, unsigned int warn, unsigned int punish) {
     MYSQL_RES *res;
     MYSQL_ROW row, defaults;
     loadChannelSettings(chanuser->chan);
@@ -223,6 +243,16 @@ static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct Ch
         punishment = atoi((row[0] ? row[0] : defaults[0])) + 1;
         punish_time = atoi((row[1] ? row[1] : defaults[1]));
     }
+    if(!punishment && (punish & SPAMSETTINGS_BADWORDSCAN)) {
+        int result = neonspam_badwordscan(client, settings, chanuser, message, 1, uaccess);
+        switch(result) {
+            case SPAMSERV_CHECK_DEAD:
+                return;
+            case SPAMSERV_CHECK_IGNORE:
+                break;
+        }
+        
+    }
     if(!punishment && (warn & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) {
         sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM);
     }
@@ -390,3 +420,110 @@ static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser
     return SPAMSERV_CHECK_IGNORE;
 }
 
+static int neonspam_badwordscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message, int punish_now, int uaccess) {
+    struct NeonSpamBadword *badword;
+    int kick_user = 0;
+    int ban_user = 0;
+    int staticban_user = 0;
+    int ban_duration = 0;
+    int checked_defaults = 0;
+    int apply_default_reaction = 0;
+    for(badword = settings->badwords; badword; badword = badword->next) {
+        if(!match(badword->badword, message)) {
+            if(badword->use_defaults) {
+                if(checked_defaults == 2) continue;
+                else if(!checked_defaults) {
+                    if(!(settings->flags & SPAMSETTINGS_BADWORDSCAN_OPS) && (chanuser->flags & CHANUSERFLAG_OPPED)) {
+                        checked_defaults = 2;
+                        continue;
+                    }
+                    if(!(settings->flags & SPAMSETTINGS_BADWORDSCAN_VOICE) && (chanuser->flags & CHANUSERFLAG_VOICED)) {
+                        checked_defaults = 2;
+                        continue;
+                    }
+                    if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] && settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] < 501) {
+                        if(!punish_now)
+                            return SPAMSERV_CHECK_PUNISH;
+                        if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] <= uaccess) {
+                            checked_defaults = 2;
+                            continue;
+                        }
+                    }
+                    checked_defaults = 1;
+                }
+            } else {
+                if(!badword->scan_ops && (chanuser->flags & CHANUSERFLAG_OPPED)) continue;
+                if(!badword->scan_voice && (chanuser->flags & CHANUSERFLAG_VOICED)) continue;
+                if(badword->exceptlevel && badword->exceptlevel < 501) {
+                    if(!punish_now)
+                        return SPAMSERV_CHECK_PUNISH;
+                    if(badword->exceptlevel <= uaccess) {
+                        checked_defaults = 2;
+                        continue;
+                    }
+                }
+            }
+            if(badword->use_default_reaction) {
+                apply_default_reaction = 1;
+            } else {
+                switch(badword->reaction) {
+                case 2:
+                    staticban_user = 1;
+                    if(badword->reaction_time > ban_duration)
+                        ban_duration = badword->reaction_time;
+                case 1:
+                    ban_user = 1;
+                case 0:
+                    kick_user = 1;
+                }
+            }
+        }
+    }
+    if(apply_default_reaction) {
+        MYSQL_RES *res;
+        MYSQL_ROW row, defaults;
+        loadChannelSettings(chanuser->chan);
+        printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id);
+        res = mysql_use();
+        row = mysql_fetch_row(res);
+        if(!row[0] || !row[1]) {
+            printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'");
+            res = mysql_use();
+            defaults = mysql_fetch_row(res);
+        }
+        int reaction = atoi((row[0] ? row[0] : defaults[0]));
+        int reaction_time = atoi((row[1] ? row[1] : defaults[1]));
+        switch(reaction) {
+        case 2:
+            staticban_user = 1;
+            if(reaction_time > ban_duration)
+                ban_duration = reaction_time;
+        case 1:
+            ban_user = 1;
+        case 0:
+            kick_user = 1;
+        }
+    }
+    if(!kick_user) return SPAMSERV_CHECK_IGNORE;
+    char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3];
+    char *banmask = NULL;
+    if(staticban_user) {
+        banmask = generate_banmask(chanuser->user, banmaskBuf);
+        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));
+        if(ban_duration) {
+            int banid = (int) mysql_insert_id(get_mysql_conn());
+            char nameBuf[MAXLEN];
+            char banidBuf[20];
+            sprintf(nameBuf, "ban_%d", banid);
+            sprintf(banidBuf, "%d", banid);
+            timeq_add_name(nameBuf, ban_duration, module_id, channel_ban_timeout, strdup(banidBuf));
+        }
+    }
+    if(ban_user) {
+        if(!banmask)
+            banmask = generate_banmask(chanuser->user, banmaskBuf);
+        putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask);
+    }
+    putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BADWORD);
+    return SPAMSERV_CHECK_DEAD;
+}