From: NurPech Date: Mon, 20 May 2013 03:02:32 +0000 (+0200) Subject: Merge branch 'master' into SpamServ X-Git-Url: http://git.pk910.de/?a=commitdiff_plain;h=360a03c99e897a8ee119827be098669d7cea40b8;hp=18a22dac8302182719e030976fa1c045bde8a461;p=srvx.git Merge branch 'master' into SpamServ --- diff --git a/src/spamserv.c b/src/spamserv.c index aaf17b7..2cbd3e8 100644 --- a/src/spamserv.c +++ b/src/spamserv.c @@ -34,6 +34,13 @@ #define KEY_INFO "info" #define KEY_EXCEPTLEVEL "exceptlevel" #define KEY_EXPIRY "expiry" +#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_DEBUG_CHANNEL "debug_channel" #define KEY_GLOBAL_EXCEPTIONS "global_exceptions" @@ -121,6 +128,14 @@ 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" }, { NULL, NULL } }; @@ -137,6 +152,7 @@ static const struct message_entry msgtab[] = { #define SSMSG_WARNING_2 "You are violating the network rules" #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." static struct { @@ -201,6 +217,8 @@ 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; @@ -662,6 +680,27 @@ spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) } } +static struct badword* +add_badword(struct chanInfo *cInfo, const char *badword_mask, unsigned int triggered, unsigned int action, const char *id) +{ + struct badword *badword; + + badword = calloc(1, sizeof(*badword)); + if (!badword) + return NULL; + + if(!id) { + cInfo->last_badword_id++; + badword->id = strtab(cInfo->last_badword_id); + } else + badword->id = strdup(id); + badword->badword_mask = strdup(badword_mask); + badword->triggered = triggered; + badword->action = action; + dict_insert(cInfo->badwords, badword->id, badword); + return badword; +} + /***********************************************/ /* Other Stuff */ /***********************************************/ @@ -1434,6 +1473,260 @@ SPAMSERV_FUNC(opt_exceptlevel) return 1; } +static SPAMSERV_FUNC(cmd_addbad) +{ + struct chanInfo *cInfo = get_chanInfo(channel->name); + struct userData *uData; + + if(!cInfo) + { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + + if(CHECK_SUSPENDED(cInfo)) + { + ss_reply("SSMSG_SUSPENDED", channel->name); + return 0; + } + + if(!check_user_level(channel,user,lvlSetters,1,0)) + { + ss_reply("SSMSG_NO_ACCESS"); + return 0; + } + + dict_iterator_t it; + char *mask = unsplit_string(argv + 1, argc - 1, NULL); + for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) { + struct badword *badword = iter_data(it); + if(match_ircglob(mask,badword->badword_mask)) { + reply("SSMSG_BADWORD_ALREADY_ADDED", mask, badword->id); + return 1; + } + } + + struct badword *new_badword = add_badword(cInfo, mask, 0, BADACTION_KICK, NULL); + for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) { + struct badword *badword = iter_data(it); + if(match_ircglob(badword->badword_mask, new_badword->badword_mask) && badword != new_badword) { + dict_remove(cInfo->badwords, badword->id); + } + } + + reply("SSMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id); + return 1; +} + +static SPAMSERV_FUNC(cmd_delbad) +{ + struct chanInfo *cInfo = get_chanInfo(channel->name); + struct userData *uData; + + if(!cInfo) + { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + + if(CHECK_SUSPENDED(cInfo)) + { + ss_reply("SSMSG_SUSPENDED", channel->name); + return 0; + } + + if(!check_user_level(channel,user,lvlSetters,1,0)) + { + ss_reply("SSMSG_NO_ACCESS"); + return 0; + } +} + +static SPAMSERV_FUNC(cmd_setbad) +{ + struct chanInfo *cInfo = get_chanInfo(channel->name); + struct userData *uData; + struct badword *badword; + + if(!cInfo) + { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + + if(CHECK_SUSPENDED(cInfo)) + { + ss_reply("SSMSG_SUSPENDED", channel->name); + return 0; + } + + if(!check_user_level(channel,user,lvlSetters,1,0)) + { + ss_reply("SSMSG_NO_ACCESS"); + return 0; + } + + if ((badword = dict_find(cInfo->badwords, argv[1], NULL))) { + if (argc > 3) { + unsigned int ii; + char *setting = argv[2]; + char *value = argv[3]; + for( ii = 0; setting[ ii ]; ii++) + setting[ ii ] = toupper( setting[ ii ] ); + for( ii = 0; value[ ii ]; ii++) + value[ ii ] = toupper( value[ ii ] ); + if(!strcmp("MASK",setting)) { + free(badword->badword_mask); + badword->badword_mask = strdup(argv[3]); + badword->triggered = 0; + 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); + } + } else { + reply("SSMSG_BADWORD_SETTING_INVALID", setting); + } + + } else { + reply("SSMSG_BADWORD_SET", badword->id); + reply("SSMSG_BADWORD_SET_MASK", badword->badword_mask); + switch(badword->action) { + case BADACTION_KICK: + reply("SSMSG_BADWORD_SET_ACTION", "KICK"); + break; + case BADACTION_BAN: + reply("SSMSG_BADWORD_SET_ACTION", "BAN"); + break; + case BADACTION_KILL: + reply("SSMSG_BADWORD_SET_ACTION", "KILL"); + break; + case BADACTION_GLINE: + reply("SSMSG_BADWORD_SET_ACTION", "GLINE"); + break; + default: + reply("SSMSG_BADWORD_SET_ACTION", "*undef*"); + } + } + } else { + reply("SSMSG_BADWORD_NOT_FOUND", argv[1]); + return 0; + } + return 1; +} + +int +ss_badwords_sort(const void *pa, const void *pb) +{ + struct badword *a = *(struct badword**)pa; + struct badword *b = *(struct badword**)pb; + + return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0); +} + +static SPAMSERV_FUNC(cmd_listbad) +{ + struct helpfile_table tbl; + struct chanInfo *cInfo = get_chanInfo(channel->name); + struct userData *uData; + struct badword **badwords; + unsigned int count = 0, ii = 0; + + if(!cInfo) + { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + + if(CHECK_SUSPENDED(cInfo)) + { + ss_reply("SSMSG_SUSPENDED", channel->name); + return 0; + } + + if(!check_user_level(channel,user,lvlSetters,1,0)) + { + ss_reply("SSMSG_NO_ACCESS"); + return 0; + } + + dict_iterator_t it; + for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) { + count++; + } + + tbl.length = count+1; + tbl.width = 4; + tbl.flags = 0; + tbl.flags = TABLE_NO_FREE; + tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0])); + tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0])); + tbl.contents[0][0] = "#"; + tbl.contents[0][1] = "Badword"; + tbl.contents[0][2] = "Action"; + tbl.contents[0][3] = "(Triggered)"; + if(!count) + { + table_send(cmd->parent->bot, user->nick, 0, NULL, tbl); + reply("MSG_NONE"); + free(tbl.contents[0]); + free(tbl.contents); + return 0; + } + badwords = alloca(count * sizeof(badwords[0])); + for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) { + struct badword *bw = iter_data(it); + badwords[ii++] = bw; + } + + qsort(badwords, count, sizeof(badwords[0]), ss_badwords_sort); + for (ii = 1; ii <= count; ii++) { + struct badword *bw = badwords[ii-1]; + tbl.contents[ii] = malloc(tbl.width * sizeof(tbl.contents[0][0])); + tbl.contents[ii][0] = strdup(bw->id); + tbl.contents[ii][1] = strdup(bw->badword_mask); + switch(bw->action) { + case BADACTION_KICK: + tbl.contents[ii][2] = "KICK"; + break; + case BADACTION_BAN: + tbl.contents[ii][2] = "BAN"; + break; + case BADACTION_KILL: + tbl.contents[ii][2] = "KILL"; + break; + case BADACTION_GLINE: + tbl.contents[ii][2] = "GLINE"; + break; + default: + tbl.contents[ii][2] = "*undef*"; + } + tbl.contents[ii][3] = strtab(bw->triggered); + } + + table_send(cmd->parent->bot, user->nick, 0, NULL, tbl); + for(ii = 1; ii < tbl.length; ++ii) + { + free(tbl.contents[ii]); + } + free(tbl.contents[0]); + free(tbl.contents); + return 1; +} + static void to_lower(char *message) { @@ -1585,6 +1878,45 @@ spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, KickChannelUser(user, channel, spamserv, reason); } +static void +spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct badword *badword) +{ + char *hostmask; + char *reason = SSMSG_BADWORD_DETECTED; + char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' }; + switch(badword->action) { + case BADACTION_BAN: + 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); + } + struct mod_chanmode change; + mod_chanmode_init(&change); + change.argc = 1; + change.args[0].mode = MODE_BAN; + change.args[0].u.hostmask = hostmask; + mod_chanmode_announce(spamserv, chan, &change); + free(hostmask); + + case BADACTION_KICK: + if(GetUserMode(chan, user)) + KickChannelUser(user, chan, spamserv, reason); + break; + case BADACTION_KILL: + DelUser(user, spamserv, 1, reason); + break; + case BADACTION_GLINE: + irc_ntop(mask + 2, sizeof(mask) - 2, &user->ip); + gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1); + break; + default: + //error? + break; + } +} + void spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text) { @@ -1743,6 +2075,15 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * } } + dict_iterator_t it; + + for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) { + struct badword *badword = iter_data(it); + if(match_ircglob(text, badword->badword_mask)) { + spamserv_detected_badword(user, channel, badword); + } + } + if(CHECK_ADV(cInfo) && check_advertising(cInfo, text)) { if(CHECK_ADV_WARNED(uInfo)) @@ -1829,16 +2170,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; - struct record_data *hir; + dict_iterator_t it, itbad; + struct dict *object, *badwords; + struct record_data *hir, hirbad; struct chanNode *channel; struct chanInfo *cInfo; struct string_list *strlist; - unsigned int flags,exceptlevel; - char *str, *info; + unsigned int flags,exceptlevel,badwordid,action,triggered; + char *str, *info, *mask; unsigned long expiry; for(it = dict_first(database); it; it = iter_next(it)) @@ -1880,6 +2242,12 @@ spamserv_saxdb_read(struct dict *database) else 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 @@ -1910,8 +2278,21 @@ spamserv_saxdb_write(struct saxdb_context *ctx) saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel); if(cInfo->suspend_expiry) - saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry); - + saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry); + + 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); + saxdb_start_record(ctx, badword->id, 1); + saxdb_write_string(ctx, KEY_BADWORDID, badword->id); + saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask); + saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action); + saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered); + saxdb_end_record(ctx); + } + saxdb_end_record(ctx); saxdb_end_record(ctx); } return 0; @@ -2036,6 +2417,10 @@ init_spamserv(const char *nick) modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "DELEXCEPTION", cmd_delexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL); + modcmd_register(spamserv_module, "ADDBAD", cmd_addbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "DELBAD", cmd_delbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SETBAD", cmd_setbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "LISTBAD", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET SPAMLIMIT", opt_spamlimit, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); diff --git a/src/spamserv.h b/src/spamserv.h index b6b6ac6..d74c85d 100644 --- a/src/spamserv.h +++ b/src/spamserv.h @@ -58,14 +58,21 @@ enum channelinfo #define CHECK_VOICED(x) ((x)->flags & CHAN_SCAN_VOICED) #define CHECK_SUSPENDED(x) ((x)->flags & CHAN_SUSPENDED) +#define BADACTION_KICK 0 +#define BADACTION_BAN 1 +#define BADACTION_KILL 2 +#define BADACTION_GLINE 3 + struct chanInfo { struct chanNode *channel; struct string_list *exceptions; + dict_t badwords; unsigned int flags : 30; unsigned int exceptlevel; char info[CHAN_INFO_SIZE]; unsigned long suspend_expiry; + unsigned int last_badword_id; }; /***********************************************/ @@ -151,6 +158,13 @@ struct userInfo time_t lastadv; }; +struct badword { + char *id; + char *badword_mask; + unsigned int triggered : 29; + unsigned int action : 3; +}; + /***********************************************/ /* Other Stuff */ /***********************************************/ @@ -168,5 +182,6 @@ void spamserv_channel_message(struct chanNode *channel, struct userNode *user, c void spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason); int spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move); void spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason); +static struct badword *add_badword(struct chanInfo *cInfo, const char *badword_mask, unsigned int triggered, unsigned int action, const char *id); -#endif \ No newline at end of file +#endif