restricted kill and gline command from SpamServ
[srvx.git] / src / spamserv.c
index fadcfa49c2abda105dbfe549c097f8666daa8b91..a6fcc606db3dcb45e6fc62e91645e689413739ae 100644 (file)
 #define KEY_INFO                     "info"
 #define KEY_EXCEPTLEVEL              "exceptlevel"
 #define KEY_EXPIRY                   "expiry"
-#define KEY_LASTBADWORDID                       "last_badword_id"
+#define KEY_LASTBADWORDID            "last_badword_id"
 #define KEY_BADWORDS                 "badwords"
 
-#define KEY_BADWORD_MASK                        "mask"
-#define KEY_BADWORD_TRIGGERED           "count"
-#define KEY_BADWORD_ACTION                      "action"
-#define KEY_BADWORDID                           "badwordid"
+#define KEY_BADWORD_MASK             "mask"
+#define KEY_BADWORD_TRIGGERED        "count"
+#define KEY_BADWORD_ACTION           "action"
+#define KEY_BADWORDID                "badwordid"
 
 #define KEY_DEBUG_CHANNEL            "debug_channel"
+#define KEY_OPER_CHANNEL            "oper_channel"
 #define KEY_GLOBAL_EXCEPTIONS        "global_exceptions"
 #define KEY_NETWORK_RULES            "network_rules"
 #define KEY_TRIGGER                  "trigger"
@@ -76,7 +77,8 @@ dict_t connected_users_dict;
 dict_t killed_users_dict;
 
 #define spamserv_notice(target, format...) send_message(target , spamserv , ## format)
-#define spamserv_debug(format...) do { if(spamserv_conf.debug_channel) send_channel_notice(spamserv_conf.debug_channel , spamserv , ## format); } while(0)
+#define spamserv_debug(format...) do { if(spamserv_conf.debug_channel) send_channel_message(spamserv_conf.debug_channel , spamserv , ## format); } while(0)
+#define spamserv_oper_message(format...) do { if(spamserv_conf.oper_channel) send_channel_message(spamserv_conf.oper_channel , spamserv , ## format); } while(0)
 #define ss_reply(format...)    send_message(user , spamserv , ## format)
 
 #define SET_SUBCMDS_SIZE 10
@@ -128,14 +130,16 @@ static const struct message_entry msgtab[] = {
     { "SSMSG_STATUS_MEMORY",           "$bMemory Information:$b" },
     { "SSMSG_STATUS_CHANNEL_LIST",     "$bRegistered Channels:$b" },
     { "SSMSG_STATUS_NO_CHANNEL",       "No channels registered." },
-
+    
     { "SSMSG_BADWORD_ALREADY_ADDED",   "$b%s$b is already added. (ID: %s)" },
-    { "SSMSG_BADWORD_ADDED",                      "added '$b%s$b' to the badword list with ID %s." },
-    { "SSMSG_BADWORD_SET_DONE",           "Done." },
-       { "SSMSG_BADWORD_SET_INVALID",     "Invalid Option for setting %s" },
-       { "SSMSG_BADWORD_SET",             "Settings for BadWord entry $b%s$b" },
-       { "SSMSG_BADWORD_SET_MASK",        "$bMASK$b:   %s" },
-       { "SSMSG_BADWORD_SET_ACTION",      "$bACTION$b: %s" },
+    { "SSMSG_BADWORD_ADDED",           "added '$b%s$b' to the badword list with ID %s." },
+    { "SSMSG_BADWORD_SET_DONE",        "Done." },
+    { "SSMSG_BADWORD_SET_INVALID",     "Invalid Option for setting %s" },
+    { "SSMSG_BADWORD_SET",             "Settings for BadWord entry $b%s$b" },
+    { "SSMSG_BADWORD_SET_MASK",        "$bMASK$b:   %s" },
+    { "SSMSG_BADWORD_SET_ACTION",      "$bACTION$b: %s" },
+    { "SSMSG_BADWORD_NOT_FOUND",       "badword with ID %s does not exist." },
+    { "SSMSG_BADWORD_REMOVED",         "badword ID $b%s$b has been removed (mask: '%s')" },
        { NULL, NULL }
 };
 
@@ -153,10 +157,13 @@ static const struct message_entry msgtab[] = {
 #define SSMSG_WARNING_RULES           "%s is against the network rules. Read the network rules at %s"
 #define SSMSG_WARNING_RULES_2         "You are violating the network rules. Read the network rules at %s"
 #define SSMSG_BADWORD_DETECTED           "Your message contained a forbidden word."
+#define SSMSG_CHANNEL_REGISTERED      "%s (channel %s) registered by %s."
+#define SSMSG_CHANNEL_UNREGISTERED    "%s (channel %s) unregistered by %s."
 
 static struct
 {
        struct chanNode *debug_channel;
+       struct chanNode *oper_channel;
        struct string_list *global_exceptions;
        const char *network_rules;
        unsigned char trigger;
@@ -217,8 +224,6 @@ spamserv_register_channel(struct chanNode *channel, struct string_list *exceptio
     cInfo->exceptlevel = 400;
        safestrncpy(cInfo->info, info, sizeof(cInfo->info));
        cInfo->suspend_expiry = 0;
-       cInfo->badwords = dict_new();
-       cInfo->last_badword_id = 0;
        dict_insert(registered_channels_dict, cInfo->channel->name, cInfo);
 
        return cInfo;
@@ -302,7 +307,8 @@ spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct c
                else
                        snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
 
-               global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
+               /*global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);*/
+               spamserv_oper_message("%s", reason);
                return 1;
        }
 
@@ -338,7 +344,7 @@ spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_
                        spamserv_part_channel(channel, partmsg);
                
                spamserv_unregister_channel(cInfo);
-               global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, global);
+               spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
        }
 }
 
@@ -588,7 +594,11 @@ spamserv_user_join(struct modeNode *mNode)
 
        if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))
                return 0;
-        
+
+       if(IsOper(user))
+       {
+               return 0;
+       }
         
     if(!CHECK_CHANOPS(cInfo))
        {
@@ -1093,7 +1103,6 @@ static
 SPAMSERV_FUNC(cmd_register)
 {
        struct chanInfo *cInfo;
-       char reason[MAXLEN];
 
        if(!channel || !channel->channel_info)
        {
@@ -1127,8 +1136,7 @@ SPAMSERV_FUNC(cmd_register)
 
        spamserv_join_channel(cInfo->channel);
        
-       snprintf(reason, sizeof(reason), "%s (channel %s) registered by %s.", spamserv->nick, channel->name, user->handle_info->handle);
-       global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
+       spamserv_oper_message(SSMSG_CHANNEL_REGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
        ss_reply("SSMSG_REG_SUCCESS", channel->name);
 
        return 1;
@@ -1177,8 +1185,7 @@ SPAMSERV_FUNC(cmd_unregister)
        
        spamserv_unregister_channel(cInfo);     
 
-       snprintf(reason, sizeof(reason), "%s (channel %s) unregistered by %s.", spamserv->nick, channel->name, user->handle_info->handle);
-       global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
+       spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
        ss_reply("SSMSG_UNREG_SUCCESS", channel->name);
 
        return 1;
@@ -1522,6 +1529,7 @@ static SPAMSERV_FUNC(cmd_delbad)
 {
         struct chanInfo *cInfo = get_chanInfo(channel->name);
         struct userData *uData;
+        unsigned int n;
 
         if(!cInfo)
         {
@@ -1540,6 +1548,17 @@ static SPAMSERV_FUNC(cmd_delbad)
                 ss_reply("SSMSG_NO_ACCESS");
                 return 0;
         }
+
+        for (n=1; n<argc; n++) {
+                struct badword *badword = dict_find(cInfo->badwords, argv[n], NULL);
+                if (!badword) {
+                        reply("SSMSG_BADWORD_NOT_FOUND", argv[n]);
+                        continue;
+                }
+                reply("SSMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);
+                dict_remove(cInfo->badwords, argv[n]);
+        }
+        return 1;
 }
 
 static SPAMSERV_FUNC(cmd_setbad)
@@ -1582,21 +1601,29 @@ static SPAMSERV_FUNC(cmd_setbad)
                                   reply("SSMSG_BADWORD_SET_DONE");
                         }
                         else if(!strcmp("ACTION",setting)) {
-                                 if (!strcmp("1",value) || !strcmp("KICK",value)) {
-                                        badword->action = BADACTION_KICK;
-                                        reply("SSMSG_BADWORD_SET_DONE");
-                                 } else if (!strcmp("2",value) || !strcmp("BAN",value)) {
-                                        badword->action = BADACTION_BAN;
-                                        reply("SSMSG_BADWORD_SET_DONE");
-                                 } else if (!strcmp("3",value) || !strcmp("KILL",value)) {
-                                        badword->action = BADACTION_KILL;
-                                        reply("SSMSG_BADWORD_SET_DONE");
-                                 } else if (!strcmp("4",value) || !strcmp("GLINE",value)) {
-                                        badword->action = BADACTION_GLINE;
-                                        reply("SSMSG_BADWORD_SET_DONE");
-                                 } else {
-                                        reply("SSMSG_BADWORD_SET_INVALID", setting);
-                                 }
+                           if (!strcmp("1",value) || !strcmp("KICK",value)) {
+                                   badword->action = BADACTION_KICK;
+                                   reply("SSMSG_BADWORD_SET_DONE");
+                               } else if (!strcmp("2",value) || !strcmp("BAN",value)) {
+                                       badword->action = BADACTION_BAN;
+                                   reply("SSMSG_BADWORD_SET_DONE");
+                               } else if (!strcmp("3",value) || !strcmp("KILL",value)) {
+                    if(IsOper(user)) {
+                        badword->action = BADACTION_KILL;
+                        reply("SSMSG_BADWORD_SET_DONE");
+                    } else {
+                        reply("SSMSG_NO_ACCESS");
+                    }
+                               } else if (!strcmp("4",value) || !strcmp("GLINE",value)) {
+                    if(IsOper(user)) {
+                        badword->action = BADACTION_GLINE;
+                        reply("SSMSG_BADWORD_SET_DONE");
+                    } else {
+                        reply("SSMSG_NO_ACCESS");
+                    }
+                               } else {
+                                   reply("SSMSG_BADWORD_SET_INVALID", setting);
+                           }
                         } else {
                                  reply("SSMSG_BADWORD_SETTING_INVALID", setting);
                         }
@@ -1889,8 +1916,8 @@ spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct b
             hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
             sanitize_ircmask(hostmask);
             if(chan->channel_info) {
-                //registered channel
-                add_channel_ban(chan->channel_info, hostmask, spamserv->nick, now, now, now + spamserv_conf.long_ban_duration, reason);
+            //registered channel
+            add_channel_ban(chan->channel_info, hostmask, spamserv->nick, now, now, now + spamserv_conf.long_ban_duration, reason);
             }
             struct mod_chanmode change;
             mod_chanmode_init(&change);
@@ -1899,10 +1926,10 @@ spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct b
             change.args[0].u.hostmask = hostmask;
             mod_chanmode_announce(spamserv, chan, &change);
             free(hostmask);
-
+            break;
         case BADACTION_KICK:
             if(GetUserMode(chan, user))
-                KickChannelUser(user, chan, spamserv, reason);
+            KickChannelUser(user, chan, spamserv, reason);
             break;
         case BADACTION_KILL:
             DelUser(user, spamserv, 1, reason);
@@ -1914,7 +1941,7 @@ spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct b
         default:
             //error?
             break;
-        }
+    }
 }
 
 void
@@ -1931,6 +1958,11 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *
        if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick)))
                return;
 
+       if(IsOper(user))
+       {
+               return;
+       }
+
        if(!CHECK_CHANOPS(cInfo))
        {
                struct modeNode *mn = GetUserMode(channel, user);
@@ -2074,8 +2106,8 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *
                        }
                }
        }
-
-       dict_iterator_t it;
+    
+    dict_iterator_t it;
 
        for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
                struct badword *badword = iter_data(it);
@@ -2170,17 +2202,37 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *
        }
 }
 
+static int
+spamserv_saxdb_read_shitlist(const char *name, void *data, void *extra)
+{
+       struct record_data *rd = data;
+       struct chanInfo *chan = extra;
+       char *badword;
+       char *triggered, *action;
+
+       if (rd->type == RECDB_OBJECT) {
+               dict_t obj = GET_RECORD_OBJECT(rd);
+               /* new style structure */
+               badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);
+               triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);
+               action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);
+
+               add_badword(chan, badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);
+       }
+       return 0;
+}
+
 static int
 spamserv_saxdb_read(struct dict *database)
 {
-       dict_iterator_t it, itbad;
-       struct dict *object;
-       struct record_data *hir, hirbad;
+       dict_iterator_t it;
+       struct dict *badwords;
+       struct record_data *hir;
        struct chanNode *channel;
        struct chanInfo *cInfo;
        struct string_list *strlist;
-       unsigned int flags,exceptlevel,badwordid,action,triggered;
-       char *str, *info, *mask;
+       unsigned int flags,exceptlevel,badwordid;
+       char *str, *info;
        unsigned long expiry;    
 
        for(it = dict_first(database); it; it = iter_next(it))
@@ -2223,7 +2275,11 @@ spamserv_saxdb_read(struct dict *database)
                                        cInfo->suspend_expiry = expiry;                 
                 cInfo->exceptlevel=exceptlevel;
                 cInfo->badwords = dict_new();
-
+                str = database_get_data(hir->d.object, KEY_LASTBADWORDID, RECDB_QSTRING);
+                badwordid = str ? atoi(str) : 0;
+                cInfo->last_badword_id = badwordid;
+                if ((badwords = database_get_data(hir->d.object, KEY_BADWORDS, RECDB_OBJECT)))
+                       dict_foreach(badwords, spamserv_saxdb_read_shitlist, cInfo);
                        }
                }
                else
@@ -2256,8 +2312,8 @@ spamserv_saxdb_write(struct saxdb_context *ctx)
                if(cInfo->suspend_expiry)
                        saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);
 
-               saxdb_start_record(ctx, KEY_BADWORDS, 1);
                saxdb_write_int(ctx, KEY_LASTBADWORDID, cInfo->last_badword_id);
+               saxdb_start_record(ctx, KEY_BADWORDS, 1);
                dict_iterator_t itbad;
                for (itbad = dict_first(cInfo->badwords); itbad; itbad = iter_next(itbad)) {
                        struct badword *badword = iter_data(itbad);
@@ -2287,7 +2343,6 @@ spamserv_conf_read(void)
        }
 
        str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
-
        if(str)
        {
                spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);
@@ -2300,6 +2355,16 @@ spamserv_conf_read(void)
                spamserv_conf.debug_channel = NULL;
        }
 
+       str = database_get_data(conf_node, KEY_OPER_CHANNEL, RECDB_QSTRING);
+       if(str)
+       {
+               spamserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
+       }
+       else
+       {
+               spamserv_conf.oper_channel = NULL;
+       }
+
        spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);
 
        str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);