X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fspamserv.c;h=178818395a46ae011fcb9b0c48aac44e4080ce40;hb=5bbd945af96418ab4ced2e04cd98b07d6e3c9e71;hp=e4bcf4bb76161800364b5c785ece033286c91af0;hpb=189ce3434b260169ae7f5af2f3f203e995f7c5de;p=srvx.git diff --git a/src/spamserv.c b/src/spamserv.c index e4bcf4b..1788183 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_OPER_CHANNEL "oper_channel" @@ -123,6 +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_NOT_FOUND", "badword with ID %s does not exist." }, + { "SSMSG_BADWORD_REMOVED", "badword ID $b%s$b has been removed (mask: '%s')" }, { NULL, NULL } }; @@ -139,6 +156,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." #define SSMSG_CHANNEL_REGISTERED "%s (channel %s) registered by %s." #define SSMSG_CHANNEL_UNREGISTERED "%s (channel %s) unregistered by %s." @@ -672,6 +690,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 */ /***********************************************/ @@ -1146,7 +1185,7 @@ SPAMSERV_FUNC(cmd_unregister) spamserv_unregister_channel(cInfo); - spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, channel->name, user->handle_info->handle); + spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle); ss_reply("SSMSG_UNREG_SUCCESS", channel->name); return 1; @@ -1441,6 +1480,272 @@ 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; + unsigned int n; + + 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; + } + + for (n=1; nbadwords, 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) +{ + 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) { @@ -1592,6 +1897,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); + break; + 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) { @@ -1754,6 +2098,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)) { @@ -1841,16 +2194,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 dict *badwords; struct record_data *hir; struct chanNode *channel; struct chanInfo *cInfo; struct string_list *strlist; - unsigned int flags,exceptlevel; - char *str, *info; + unsigned int flags,exceptlevel,badwordid; + char *str, *info; unsigned long expiry; for(it = dict_first(database); it; it = iter_next(it)) @@ -1892,6 +2266,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 @@ -1922,8 +2302,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; @@ -2057,6 +2450,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);