X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/63c95a478af1b51ad021abef19f630de189f0229..b66855e2e4a9f73738a819eae10450a087978d1a:/src/spamserv.c diff --git a/src/spamserv.c b/src/spamserv.c index ad4acd4..a452614 100644 --- a/src/spamserv.c +++ b/src/spamserv.c @@ -3,7 +3,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. Important limitations are * listed in the COPYING file that accompanies this software. * @@ -28,6 +28,8 @@ #include "timeq.h" #include "gline.h" +#include + #define SPAMSERV_CONF_NAME "services/spamserv" #define KEY_EXCEPTIONS "exceptions" @@ -35,8 +37,13 @@ #define KEY_FLAGS "flags" #define KEY_INFO "info" #define KEY_EXPIRY "expiry" - +#define KEY_TRUSTED_HOSTS "trusted" +#define KEY_CHANNELS "channel" +#define KEY_ISSUER "issuer" +#define KEY_ISSUED "issued" +#define KEY_TRUSTED_ACCOUNTS "trusted" #define KEY_DEBUG_CHANNEL "debug_channel" +#define KEY_DEBUG_CHANNEL_MODES "debug_channel_modes" #define KEY_GLOBAL_EXCEPTIONS "global_exceptions" #define KEY_GLOBAL_BADWORDS "global_badwords" #define KEY_NETWORK_RULES "network_rules" @@ -53,6 +60,14 @@ #define KEY_ADV_CHAN_MUST_EXIST "adv_chan_must_exist" #define KEY_STRIP_MIRC_CODES "strip_mirc_codes" #define KEY_ALLOW_MOVE_MERGE "allow_move_merge" +#define KEY_CAPSMIN "capsmin" +#define KEY_CAPSPERCENT "capspercent" +#define KEY_EXCEPTLEVEL "exceptlevel" +#define KEY_EXCEPTSPAMLEVEL "exceptspamlevel" +#define KEY_EXCEPTFLOODLEVEL "exceptfloodlevel" +#define KEY_EXCEPTADVLEVEL "exceptadvlevel" +#define KEY_EXCEPTBADWORDLEVEL "exceptbadwordlevel" +#define KEY_EXCEPTCAPSLEVEL "exceptcapslevel" #define SPAMSERV_FUNC(NAME) MODCMD_FUNC(NAME) #define SPAMSERV_SYNTAX() svccmd_send_help(user, spamserv, cmd) @@ -73,13 +88,15 @@ dict_t registered_channels_dict; dict_t connected_users_dict; dict_t killed_users_dict; +#define SSFUNC_ARGS user, channel, argc, argv, cmd + #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 ss_reply(format...) send_message(user , spamserv , ## format) -#define SET_SUBCMDS_SIZE 13 +#define SET_SUBCMDS_SIZE 20 -const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "BADREACTION", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "BADWORDSCAN", "CHANFLOODSCAN", "JOINFLOODSCAN", "SCANCHANOPS", "SCANHALFOPS", "SCANVOICED"}; +const char *set_subcommands[SET_SUBCMDS_SIZE] = {"EXCEPTLEVEL", "EXCEPTADVLEVEL", "EXCEPTBADWORDLEVEL", "EXCEPTCAPSLEVEL", "EXCEPTFLOODLEVEL", "EXCEPTSPAMLEVEL", "SPAMLIMIT", "BADREACTION", "CAPSREACTION", "ADVREACTION", "WARNREACTION", "ADVSCAN", "CAPSSCAN", "SPAMSCAN", "BADWORDSCAN", "CHANFLOODSCAN", "JOINFLOODSCAN", "CAPSMIN", "CAPSPERCENT"}; extern struct string_list *autojoin_channels; static void spamserv_clear_spamNodes(struct chanNode *channel); @@ -142,6 +159,30 @@ static const struct message_entry msgtab[] = { { "SSMSG_WARNING_RULES_T", "%s is against the network rules. Read the network rules at %s" }, { "SSMSG_WARNING_RULES_2_T", "You are violating the network rules. Read the network rules at %s" }, + { "SSMSG_ALREADY_TRUSTED", "Account $b%s$b is already trusted." }, + { "SSMSG_NOT_TRUSTED", "Account $b%s$b is not trusted." }, + { "SSMSG_ADDED_TRUSTED", "Added %s to the global trusted-accounts list" }, + { "SSMSG_ADDED_TRUSTED_CHANNEL", "Added %s to the trusted-accounts list for channel %s." }, + { "SSMSG_REMOVED_TRUSTED", "Removed %s from the global trusted-accounts list." }, + { "SSMSG_REMOVED_TRUSTED_CHANNEL", "Removed %s from channel %s trusted-account list." }, + { "SSMSG_TRUSTED_LIST", "$bTrusted Accounts$b" }, + { "SSMSG_TRUSTED_LIST_HEADER", "Account Added By Time" }, + { "SSMSG_HOST_IS_TRUSTED", "%-15s %-10s set %s ago" }, + { "SSMSG_TRUSTED_LIST_BAR", "----------------------------------------" }, + { "SSMSG_TRUSTED_LIST_END", "---------End of Trusted Accounts--------" }, + { "SSMSG_HOST_NOT_TRUSTED", "%s does not have a special trust." }, + + { "SSMSG_MUST_BE_HELPING", "You must have security override (helping mode) on to use this command." }, + + { "SSMSG_SET_CAPSMIN", "$bCapsMin$b %d - atleast this min caps and atleast CapsPercent of the total line." }, + { "SSMSG_SET_CAPSPERCENT", "$bCapsPercent$b %d - atleast CapsPercent of the total line." }, + { "SSMSG_SET_EXCEPTLEVEL" , "$bExceptLevel$b %d - level and above will be excepted from all checks." }, + { "SSMSG_SET_EXCEPTADVLEVEL", "$bExceptAdvLevel$b %d - and above will be excepted from advertising checks." }, + { "SSMSG_SET_EXCEPTBADWORDLEVEL", "$bExceptBadWordLevel$b %d - and above will be excepted from badword checks." }, + { "SSMSG_SET_EXCEPTCAPSLEVEL" , "$bExceptCapsLevel$b %d - and above will be excepted from caps checks." }, + { "SSMSG_SET_EXCEPTFLOODLEVEL", "$bExceptFloodLevel$b %d - and above will be excepted from flood checks." }, + { "SSMSG_SET_EXCEPTSPAMLEVEL", "$bExceptSpamLevel$b %d - and above will be excepted from spam checks." }, + { NULL, NULL } }; @@ -155,6 +196,7 @@ static const struct message_entry msgtab[] = { #define SSMSG_FLOOD "Flooding the channel/network" #define SSMSG_ADV "Advertising" #define SSMSG_BAD "Badwords" +#define SSMSG_CAPS "Caps" #define SSMSG_JOINFLOOD "Join flooding the channel" #define SSMSG_WARNING "%s is against the network rules" @@ -169,6 +211,8 @@ static const struct message_entry msgtab[] = { #define SSMSG_WARNING_RULES_2 "SSMSG_WARNING_RULES_2_T" */ +static dict_t spamserv_trusted_accounts; + static struct { struct chanNode *debug_channel; @@ -188,8 +232,17 @@ static struct unsigned int adv_chan_must_exist : 1; unsigned int strip_mirc_codes : 1; unsigned int allow_move_merge : 1; + unsigned long untrusted_max; } spamserv_conf; +struct trusted_account { + char *account; + struct string_list *channel; + char *issuer; + unsigned long limit; + time_t issued; +}; + /***********************************************/ /* Channel */ /***********************************************/ @@ -234,9 +287,24 @@ spamserv_register_channel(struct chanNode *channel, struct string_list *exceptio cInfo->exceptions = exceptions ? string_list_copy(exceptions) : alloc_string_list(1); cInfo->badwords = badwords ? string_list_copy(badwords) : alloc_string_list(1); cInfo->flags = flags; + cInfo->exceptlevel = 300; + cInfo->exceptspamlevel = 100; + cInfo->exceptadvlevel = 100; + cInfo->exceptbadwordlevel = 100; + cInfo->exceptcapslevel = 100; + cInfo->exceptfloodlevel = 100; + cInfo->capsmin = 10; + cInfo->capspercent = 25; + + /* XXX Rewrite the flag system */ + if (strlen(info) < 5) + strcat(info, "s"); + if (strlen(info) < 6) + strcat(info, "s"); + safestrncpy(cInfo->info, info, sizeof(cInfo->info)); cInfo->suspend_expiry = 0; - dict_insert(registered_channels_dict, cInfo->channel->name, cInfo); + dict_insert(registered_channels_dict, strdup(cInfo->channel->name), cInfo); return cInfo; } @@ -247,9 +315,9 @@ spamserv_unregister_channel(struct chanInfo *cInfo) if(!cInfo) return; - dict_remove(registered_channels_dict, cInfo->channel->name); free_string_list(cInfo->exceptions); free_string_list(cInfo->badwords); + dict_remove(registered_channels_dict, cInfo->channel->name); free(cInfo); } @@ -300,7 +368,7 @@ spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct c cInfo->channel = target; dict_remove(registered_channels_dict, channel->name); - dict_insert(registered_channels_dict, target->name, cInfo); + dict_insert(registered_channels_dict, strdup(target->name), cInfo); if(move) { @@ -316,11 +384,14 @@ spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct c spamserv_part_channel(channel, reason); if(move) - snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, + "SSMSG_CHANNEL_MOVED", channel->name, target->name, + user->handle_info->handle); else - snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, + "SSMSG_CHANNEL_MERGED", channel->name, target->name, + user->handle_info->handle); - global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason); return 1; } @@ -334,20 +405,23 @@ spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_ if(cInfo) { - char global[MAXLEN], partmsg[MAXLEN]; + char partmsg[MAXLEN]; switch (type) { case manually: - snprintf(global, sizeof(global), "$X (channel %s) %s by %s.", channel->name, reason, user->handle_info->handle); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "SSMSG_UNREG_MANUAL", + channel->name, reason, user->handle_info->handle); snprintf(partmsg, sizeof(partmsg), "%s %s by %s.", channel->name, reason, user->handle_info->handle); break; case expire: - snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "SSMSG_REG_EXPIRED", + channel->name); snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name); break; case lost_all_users: - snprintf(global, sizeof(global), "$X (channel %s) lost all users.", channel->name); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "SSMSG_LOST_ALL_USERS", + channel->name); snprintf(partmsg, sizeof(partmsg), "%s lost all users.", channel->name); break; } @@ -356,7 +430,6 @@ 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); } } @@ -518,13 +591,14 @@ spamserv_create_user(struct userNode *user) uInfo->warnlevel = kNode ? kNode->warnlevel : 0; uInfo->lastadv = 0; uInfo->lastbad = 0; + uInfo->lastcaps = 0; - dict_insert(connected_users_dict, user->nick, uInfo); + dict_insert(connected_users_dict, strdup(user->nick), uInfo); if(kNode) { + /* free(kNode); dict_remove does this */ dict_remove(killed_users_dict, irc_ntoa(&user->ip)); - free(kNode); } } @@ -551,7 +625,7 @@ spamserv_delete_user(struct userInfo *uInfo) } static int -spamserv_new_user_func(struct userNode *user) +spamserv_new_user_func(struct userNode *user, UNUSED_ARG(void *extra)) { if(!IsLocal(user)) spamserv_create_user(user); @@ -560,7 +634,7 @@ spamserv_new_user_func(struct userNode *user) } static void -spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why)) +spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why), UNUSED_ARG(void *extra)) { struct userInfo *uInfo = get_userInfo(user->nick); struct killNode *kNode; @@ -583,23 +657,25 @@ spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_AR kNode->time = now; - dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode); + dict_insert(killed_users_dict, strdup(irc_ntoa(&user->ip)), kNode); } spamserv_delete_user(uInfo); } static void -spamserv_nick_change_func(struct userNode *user, const char *old_nick) +spamserv_nick_change_func(struct userNode *user, const char *old_nick, UNUSED_ARG(void *extra)) { struct userInfo *uInfo = get_userInfo(old_nick); - dict_remove(connected_users_dict, old_nick); - dict_insert(connected_users_dict, user->nick, uInfo); + if(uInfo) { + dict_remove(connected_users_dict, old_nick); + dict_insert(connected_users_dict, strdup(user->nick), uInfo); + } } static int -spamserv_user_join(struct modeNode *mNode) +spamserv_user_join(struct modeNode *mNode, UNUSED_ARG(void *extra)) { struct chanNode *channel = mNode->channel; struct userNode *user = mNode->user; @@ -644,7 +720,7 @@ spamserv_user_join(struct modeNode *mNode) } static void -spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) +spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason), UNUSED_ARG(void *extra)) { struct userNode *user = mn->user; struct chanNode *channel = mn->channel; @@ -729,6 +805,9 @@ timeq_flood(UNUSED_ARG(void *data)) { uInfo = iter_data(it); + if (!uInfo) + continue; + if(!(fNode = uInfo->flood)) continue; @@ -750,7 +829,7 @@ timeq_joinflood(UNUSED_ARG(void *data)) { dict_iterator_t it; struct userInfo *uInfo; - struct floodNode *fNode; + struct floodNode *fNode, *nextnode; for(it = dict_first(connected_users_dict); it; it = iter_next(it)) { @@ -759,8 +838,9 @@ timeq_joinflood(UNUSED_ARG(void *data)) if(!(fNode = uInfo->joinflood)) continue; - for(; fNode; fNode = fNode->next) + for(; fNode; fNode = nextnode) { + nextnode = fNode->next; if(now - fNode->time > JOINFLOOD_EXPIRE) { if(!(--fNode->count)) @@ -792,6 +872,26 @@ timeq_bad(UNUSED_ARG(void *data)) timeq_add(now + BAD_TIMEQ_FREQ, timeq_bad, NULL); } +static void +timeq_caps(UNUSED_ARG(void *data)) +{ + dict_iterator_t it; + struct userInfo *uInfo; + + for(it = dict_first(connected_users_dict); it; it = iter_next(it)) + { + uInfo = iter_data(it); + + if(uInfo->lastcaps && uInfo->lastcaps - now > CAPS_EXPIRE) + { + uInfo->lastcaps = 0; + uInfo->flags &= ~USER_CAPS_WARNED; + } + } + + timeq_add(now + CAPS_TIMEQ_FREQ, timeq_caps, NULL); +} + static void timeq_adv(UNUSED_ARG(void *data)) { @@ -835,13 +935,21 @@ timeq_kill(UNUSED_ARG(void *data)) dict_iterator_t it; struct killNode *kNode; - for(it = dict_first(killed_users_dict); it; it = iter_next(it)) - { - kNode = iter_data(it); - - if(kNode->time - now > KILL_EXPIRE) - free(kNode); - } + while(1) { + for(it = dict_first(killed_users_dict); it; it = iter_next(it)) + { + kNode = iter_data(it); + + if(now - kNode->time > KILL_EXPIRE) { + dict_remove(killed_users_dict, iter_key(it)); + /* have to restart the loop because next is + * now invalid. FIXME: how could we do this better? */ + break; /* out of for() loop */ + } + } + /* no more killed_users to delete, so stop while loop */ + break; /* out of while() loop */ + } timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL); } @@ -1125,7 +1233,6 @@ static SPAMSERV_FUNC(cmd_register) { struct chanInfo *cInfo; - char reason[MAXLEN]; if(!channel || !channel->channel_info) { @@ -1159,8 +1266,8 @@ 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); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "SSMSG_REGISTERED_BY", + channel->name, user->handle_info->handle); ss_reply("SSMSG_REG_SUCCESS", channel->name); return 1; @@ -1209,8 +1316,8 @@ 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); + global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "SSMSG_UNREGISTERED_BY", + channel->name, user->handle_info->handle); ss_reply("SSMSG_UNREG_SUCCESS", channel->name); return 1; @@ -1508,6 +1615,327 @@ SPAMSERV_FUNC(cmd_set) return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd); } +int ss_check_user_level(struct chanNode *channel, struct userNode *user, unsigned int minimum, int allow_override, int exempt_owner) +{ + struct userData *uData; + struct chanData *cData = channel->channel_info; + if(!minimum) + return 1; + uData = _GetChannelUser(cData, user->handle_info, allow_override, 0); + if(!uData) + return 0; + if(minimum <= uData->access) + return 1; + if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner) + return 1; + return 0; +} + + +static int +channel_except_level(struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) +{ + struct chanData *cData = channel->channel_info; + struct chanInfo *cInfo; + struct userData *uData; + unsigned short value; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + if(!ss_check_user_level(channel, user, cInfo->exceptlevel, 1, 1)) + { + reply("SSMSG_CANNOT_SET"); + return 0; + } + value = user_level_from_name(argv[1], UL_OWNER+1); + if(!value && strcmp(argv[1], "0")) + { + reply("SSMSG_INVALID_ACCESS", argv[1]); + return 0; + } + uData = GetChannelUser(cData, user->handle_info); + if(!uData || ((uData->access < UL_OWNER) && (value > uData->access))) + { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + cInfo->exceptlevel = value; + } + reply("SSMSG_SET_EXCEPTLEVEL", cInfo->exceptlevel); + return 0; +} + +static int +channel_except_adv_level(struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) +{ + struct chanData *cData = channel->channel_info; + struct chanInfo *cInfo; + struct userData *uData; + unsigned short value; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + if(!ss_check_user_level(channel, user, cInfo->exceptadvlevel, 1, 1)) + { + reply("SSMSG_CANNOT_SET"); + return 0; + } + value = user_level_from_name(argv[1], UL_OWNER+1); + if(!value && strcmp(argv[1], "0")) + { + reply("SSMSG_INVALID_ACCESS", argv[1]); + return 0; + } + uData = GetChannelUser(cData, user->handle_info); + if(!uData || ((uData->access < UL_OWNER) && (value > uData->access))) + { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + cInfo->exceptadvlevel = value; + } + reply("SSMSG_SET_EXCEPTADVLEVEL", cInfo->exceptadvlevel); + return 0; +} + +static int +channel_except_badword_level(struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) +{ + struct chanData *cData = channel->channel_info; + struct chanInfo *cInfo; + struct userData *uData; + unsigned short value; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + if(!ss_check_user_level(channel, user, cInfo->exceptbadwordlevel, 1, 1)) + { + reply("SSMSG_CANNOT_SET"); + return 0; + } + value = user_level_from_name(argv[1], UL_OWNER+1); + if(!value && strcmp(argv[1], "0")) + { + reply("SSMSG_INVALID_ACCESS", argv[1]); + return 0; + } + uData = GetChannelUser(cData, user->handle_info); + if(!uData || ((uData->access < UL_OWNER) && (value > uData->access))) + { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + cInfo->exceptbadwordlevel = value; + } + reply("SSMSG_SET_EXCEPTBADWORDLEVEL", cInfo->exceptbadwordlevel); + return 0; +} + +static int +channel_except_caps_level(struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) +{ + struct chanData *cData = channel->channel_info; + struct chanInfo *cInfo; + struct userData *uData; + unsigned short value; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + if(!ss_check_user_level(channel, user, cInfo->exceptcapslevel, 1, 1)) + { + reply("SSMSG_CANNOT_SET"); + return 0; + } + value = user_level_from_name(argv[1], UL_OWNER+1); + if(!value && strcmp(argv[1], "0")) + { + reply("SSMSG_INVALID_ACCESS", argv[1]); + return 0; + } + uData = GetChannelUser(cData, user->handle_info); + if(!uData || ((uData->access < UL_OWNER) && (value > uData->access))) + { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + cInfo->exceptcapslevel = value; + } + reply("SSMSG_SET_EXCEPTCAPSLEVEL", cInfo->exceptcapslevel); + return 0; +} + +static int +channel_except_flood_level(struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) +{ + struct chanData *cData = channel->channel_info; + struct chanInfo *cInfo; + struct userData *uData; + unsigned short value; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + if(!ss_check_user_level(channel, user, cInfo->exceptfloodlevel, 1, 1)) + { + reply("SSMSG_CANNOT_SET"); + return 0; + } + value = user_level_from_name(argv[1], UL_OWNER+1); + if(!value && strcmp(argv[1], "0")) + { + reply("SSMSG_INVALID_ACCESS", argv[1]); + return 0; + } + uData = GetChannelUser(cData, user->handle_info); + if(!uData || ((uData->access < UL_OWNER) && (value > uData->access))) + { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + cInfo->exceptfloodlevel = value; + } + reply("SSMSG_SET_EXCEPTFLOODLEVEL", cInfo->exceptfloodlevel); + return 0; +} + +static int +channel_except_spam_level(struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) +{ + struct chanData *cData = channel->channel_info; + struct chanInfo *cInfo; + struct userData *uData; + unsigned short value; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + if(!ss_check_user_level(channel, user, cInfo->exceptspamlevel, 1, 1)) + { + reply("SSMSG_CANNOT_SET"); + return 0; + } + value = user_level_from_name(argv[1], UL_OWNER+1); + if(!value && strcmp(argv[1], "0")) + { + reply("SSMSG_INVALID_ACCESS", argv[1]); + return 0; + } + uData = GetChannelUser(cData, user->handle_info); + if(!uData || ((uData->access < UL_OWNER) && (value > uData->access))) + { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + cInfo->exceptspamlevel = value; + } + reply("SSMSG_SET_EXCEPTSPAMLEVEL", cInfo->exceptspamlevel); + return 0; +} + +static +SPAMSERV_FUNC(opt_capsmin) +{ + struct chanInfo *cInfo; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + char *mask = strdup(argv[1]); + unsigned int old = cInfo->capsmin; + + if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) { + cInfo->capsmin = mask ? strtoul(mask, NULL, 0) : 10; + + if (cInfo->capsmin < 0) { + cInfo->capsmin = old; + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + } else { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + } + reply("SSMSG_SET_CAPSMIN", cInfo->capsmin); + return 0; +} + +static +SPAMSERV_FUNC(opt_capspercent) +{ + struct chanInfo *cInfo; + + cInfo = get_chanInfo(channel->name); + + if(argc > 1) + { + char *mask = strdup(argv[1]); + unsigned int old = cInfo->capspercent; + + if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) { + cInfo->capspercent = mask ? strtoul(mask, NULL, 0) : 10; + + if ((cInfo->capspercent < 0) || (cInfo->capspercent > 100)) { + cInfo->capspercent = old; + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + } else { + reply("SSMSG_BAD_SETLEVEL"); + return 0; + } + } + reply("SSMSG_SET_CAPSPERCENT", cInfo->capspercent); + return 0; +} + +static +SPAMSERV_FUNC(opt_exceptlevel) +{ + return channel_except_level(SSFUNC_ARGS); +} + +static +SPAMSERV_FUNC(opt_exceptadvlevel) +{ + return channel_except_adv_level(SSFUNC_ARGS); +} + +static +SPAMSERV_FUNC(opt_exceptbadwordlevel) +{ + return channel_except_badword_level(SSFUNC_ARGS); +} + +static +SPAMSERV_FUNC(opt_exceptcapslevel) +{ + return channel_except_caps_level(SSFUNC_ARGS); +} + +static +SPAMSERV_FUNC(opt_exceptfloodlevel) +{ + return channel_except_flood_level(SSFUNC_ARGS); +} + +static +SPAMSERV_FUNC(opt_exceptspamlevel) +{ + return channel_except_spam_level(SSFUNC_ARGS); +} + static SPAMSERV_FUNC(opt_spamlimit) { @@ -1568,12 +1996,33 @@ SPAMSERV_FUNC(opt_badreaction) MULTIPLE_OPTION("BadReaction ", "BadReaction", ci_BadReaction); } +static +SPAMSERV_FUNC(opt_capsreaction) +{ + struct valueData values[] = + { + {"Kick on disallowed caps.", 'k', 0}, + {"Kickban on disallowed caps.", 'b', 0}, + {"Short timed ban on disallowed caps.", 's', 0}, + {"Long timed ban on disallowed caps.", 'l', 0}, + {"Kill on disallowed caps.", 'd', 1} + }; + + MULTIPLE_OPTION("CapsReaction ", "CapsReaction", ci_CapsReaction); +} + static SPAMSERV_FUNC(opt_advscan) { BINARY_OPTION("AdvScan ", CHAN_ADV_SCAN); } +static +SPAMSERV_FUNC(opt_capsscan) +{ + BINARY_OPTION("CapsScan ", CHAN_CAPSSCAN); +} + static SPAMSERV_FUNC(opt_spamscan) { @@ -1598,22 +2047,349 @@ SPAMSERV_FUNC(opt_joinflood) BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD); } -static -SPAMSERV_FUNC(opt_scanops) +static void +spamserv_add_trusted_account(const char *account, struct string_list *channel, const char *issuer, time_t issued) { - BINARY_OPTION("ScanChanOps ", CHAN_SCAN_CHANOPS); + struct trusted_account *ta; + ta = calloc(1, sizeof(*ta)); + if (!ta) + return; + ta->account = strdup(account); + ta->channel = channel ? string_list_copy(channel) : alloc_string_list(1); + ta->issuer = strdup(issuer); + ta->issued = issued; + dict_insert(spamserv_trusted_accounts, strdup(ta->account), ta); } -static -SPAMSERV_FUNC(opt_scanhalfops) +/* +static void +free_trusted_account(void *data) +{ + struct trusted_account *ta = data; + free(ta->account); + free_string_list(ta->channel); + free(ta->issuer); + free(ta); +} +*/ + +static SPAMSERV_FUNC(cmd_addtrust) { - BINARY_OPTION("ScanHalfOps ", CHAN_SCAN_HALFOPS); + unsigned int i; + struct userData *uData; + struct chanData *cData; + struct chanInfo *cInfo; + struct trusted_account *ta; + struct string_list *templist; + struct handle_info *hi; + + if (!(channel = GetChannel(argv[2]))) { + ss_reply("SSMSG_NOT_REGISTERED", argv[2]); + return 0; + } + + cInfo = get_chanInfo(channel->name); + cData = channel->channel_info; + uData = GetChannelUser(cData, user->handle_info); + + if (!cInfo || !channel->channel_info) { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + + if (CHECK_SUSPENDED(cInfo)) { + ss_reply("SSMSG_SUSPENDED", channel->name); + return 0; + } + + if (!uData || (uData->access < UL_MANAGER)) { + ss_reply("SSMSG_NO_ACCESS"); + return 0; + } + + if (!(hi = modcmd_get_handle_info(user, argv[1]))) { + return 0; + } + + if ((ta = dict_find(spamserv_trusted_accounts, argv[1], NULL))) { + if (ta->channel->used && (argc > 1)) { + for (i=0; i < ta->channel->used; i++) { + if (!strcmp(ta->channel->list[i], argv[2])) { + ss_reply("SSMSG_ALREADY_TRUSTED", hi->handle); + return 0; + } + } + } + + string_list_append(ta->channel, argv[2]); + ss_reply("SSMSG_ADDED_TRUSTED_CHANNEL", hi->handle, argv[2]); + return 1; + } + + templist = alloc_string_list(sizeof(argv[2])+1); +// templist = alloc_string_list(1); + string_list_append(templist, argv[2]); + + spamserv_add_trusted_account(hi->handle, templist, user->handle_info->handle, now); + ss_reply("SSMSG_ADDED_TRUSTED_CHANNEL", hi->handle, argv[2]); + return 1; } -static -SPAMSERV_FUNC(opt_scanvoiced) +static SPAMSERV_FUNC(cmd_oaddtrust) { - BINARY_OPTION("ScanVoiced ", CHAN_SCAN_VOICED); + unsigned int i, global = 0; + struct chanInfo *cInfo; + struct chanData *cData; + struct trusted_account *ta; + struct string_list *templist; + struct handle_info *hi; + + if (!strcmp(argv[2], "global")) + global = 1; + + if (!(channel = GetChannel(argv[2])) && (global == 0)) { + ss_reply("SSMSG_NOT_REGISTERED", channel ? channel->name : (global ? "global" : "")); + return 0; + } + + if (channel) { + cInfo = get_chanInfo(channel->name); + cData = channel->channel_info; + + if (!cInfo || !channel->channel_info) { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + } + + if (!(hi = modcmd_get_handle_info(user, argv[1]))) { + return 0; + } + + if ((ta = dict_find(spamserv_trusted_accounts, argv[1], NULL))) { + if (ta->channel->used && (argc > 1)) { + for (i=0; i < ta->channel->used; i++) { + if (!strcmp(ta->channel->list[i], argv[2])) { + ss_reply("SSMSG_ALREADY_TRUSTED", argv[1]); + return 0; + } + } + } + + string_list_append(ta->channel, argv[2]); + + if (global == 1) + ss_reply("SSMSG_ADDED_TRUSTED", argv[1]); + else + ss_reply("SSMSG_ADDED_TRUSTED_CHANNEL", argv[1], argv[2]); + + return 1; + } + + templist = alloc_string_list(sizeof(argv[2])+1); +// templist = alloc_string_list(1); + string_list_append(templist, argv[2]); + + spamserv_add_trusted_account(hi->handle, templist, user->handle_info->handle, now); + + if (global == 1) + ss_reply("SSMSG_ADDED_TRUSTED", hi->handle); + else + ss_reply("SSMSG_ADDED_TRUSTED_CHANNEL", hi->handle, argv[2]); + + return 1; +} + +static SPAMSERV_FUNC(cmd_deltrust) +{ + unsigned int i; + int rem = 0; + struct trusted_account *ta; + struct userData *uData; + struct chanData *cData; + struct chanInfo *cInfo; + struct handle_info *hi; + + if (!(channel = GetChannel(argv[2]))) { + ss_reply("SSMSG_NOT_REGISTERED", argv[2]); + return 0; + } + + cInfo = get_chanInfo(channel->name); + cData = channel->channel_info; + uData = GetChannelUser(cData, user->handle_info); + + if (!cInfo || !channel->channel_info) { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + + if (CHECK_SUSPENDED(cInfo)) { + ss_reply("SSMSG_SUSPENDED", channel->name); + return 0; + } + + if (!uData || (uData->access < UL_MANAGER)) { + ss_reply("SSMSG_NO_ACCESS"); + return 0; + } + + if (!(hi = modcmd_get_handle_info(user, argv[1]))) { + return 0; + } + + ta = dict_find(spamserv_trusted_accounts, hi->handle, NULL); + + if (!ta) { + ss_reply("SSMSG_NOT_TRUSTED", argv[2]); + return 0; + } + + if (argc > 1) { + if (ta->channel->used) { + for (i=0; i < ta->channel->used; i++) { + if (!strcmp(ta->channel->list[i], argv[2])) { + string_list_delete(ta->channel, i); + rem = 1; + } + } + } + + if (rem == 1) + ss_reply("SSMSG_REMOVED_TRUSTED_CHANNEL", hi->handle, argv[2]); + else { + ss_reply("SSMSG_NOT_TRUSTED", hi->handle, argv[2]); + return 0; + } + } else { + dict_remove(spamserv_trusted_accounts, hi->handle); + ss_reply("SSMSG_REMOVED_TRUSTED", hi->handle); + } + + return 1; +} + +static SPAMSERV_FUNC(cmd_odeltrust) +{ + unsigned int i; + int rem = 0, global = 0; + struct trusted_account *ta; + struct chanInfo *cInfo; + struct chanData *cData; + struct handle_info *hi; + + if (!strcmp(argv[2], "global")) + global = 1; + + if (!(channel = GetChannel(argv[2])) && (global == 0)) { + ss_reply("SSMSG_NOT_REGISTERED", channel ? channel->name : (global ? "global" : "")); + return 0; + } + + if (channel) { + cInfo = get_chanInfo(channel->name); + cData = channel->channel_info; + + if (!cInfo || !channel->channel_info) { + ss_reply("SSMSG_NOT_REGISTERED", channel->name); + return 0; + } + } + + if (!(hi = modcmd_get_handle_info(user, argv[1]))) { + return 0; + } + + ta = dict_find(spamserv_trusted_accounts, hi->handle, NULL); + + if (!ta) { + ss_reply("SSMSG_NOT_TRUSTED", argv[2]); + return 0; + } + + if (argc > 1) { + if (ta->channel->used) { + for (i=0; i < ta->channel->used; i++) { + if (!strcmp(ta->channel->list[i], argv[2])) { + string_list_delete(ta->channel, i); + rem = 1; + } + } + } + + if (rem == 1) + ss_reply("SSMSG_REMOVED_TRUSTED_CHANNEL", hi->handle, argv[2]); + else { + ss_reply("SSMSG_NOT_TRUSTED", argv[2]); + return 0; + } + } else { + dict_remove(spamserv_trusted_accounts, hi->handle); + ss_reply("SSMSG_REMOVED_TRUSTED", hi->handle); + } + + return 1; +} + +static SPAMSERV_FUNC(cmd_listtrust) { + dict_iterator_t it; + struct trusted_account *ta; + char issued[INTERVALLEN]; + char *chan; + unsigned int i; + + if (argc > 0) { + if (!strcmp(argv[1], "global")) { + if (!IsHelping(user)) { + reply("SSMSG_MUST_BE_HELPING"); + return 0; + } else + chan = "global"; + } else { + channel = GetChannel(argv[1]); + if (channel) + chan = strdup(channel->name); + else { + ss_reply("SSMSG_NOT_REGISTERED", argv[1]); + return 0; + } + } + } else { + reply("MSG_INVALID_CHANNEL"); + return 0; + } + + reply("SSMSG_TRUSTED_LIST"); + reply("SSMSG_TRUSTED_LIST_BAR"); + reply("SSMSG_TRUSTED_LIST_HEADER"); + reply("SSMSG_TRUSTED_LIST_BAR"); + for (it = dict_first(spamserv_trusted_accounts); it; it = iter_next(it)) { + ta = iter_data(it); + + if (ta->channel->used) { + for (i=0; i < ta->channel->used; i++) { + + if (!strcmp(ta->channel->list[i], chan)) { + if (ta->issued) + intervalString(issued, now - ta->issued, user->handle_info); + + ss_reply("SSMSG_HOST_IS_TRUSTED", iter_key(it), + (ta->issuer ? ta->issuer : ""), + (ta->issued ? issued : "some time")); + + } else if (!strcmp(ta->channel->list[i], "global") && (!strcmp(chan, "global"))) { + if (ta->issued) + intervalString(issued, now - ta->issued, user->handle_info); + + ss_reply("SSMSG_HOST_IS_TRUSTED", iter_key(it), + (ta->issuer ? ta->issuer : ""), + (ta->issued ? issued : 0)); + } + } + } + } + ss_reply("SSMSG_TRUSTED_LIST_END"); + return 1; } static void @@ -1700,6 +2476,27 @@ is_in_badword_list(struct chanInfo *cInfo, char *message) return 0; } +static int +check_caps(struct chanInfo *cInfo, char *message) +{ + int c; + + if ( (c = strlen(message)) >= cInfo->capsmin) { + int i = 0; + char *s = strdup(message); + + do { + if (isupper(*s)) + i++; + } while (*s++); + + if (i >= cInfo->capsmin && i * 100 / c >= cInfo->capspercent) + return 1; + } + + return 0; +} + static int check_badwords(struct chanInfo *cInfo, char *message) { @@ -1796,42 +2593,83 @@ spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, void spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text) { + struct chanData *cData; struct chanInfo *cInfo; struct userInfo *uInfo; + struct userData *uData; struct spamNode *sNode; struct floodNode *fNode; + struct trusted_account *ta; unsigned int violation = 0; char reason[MAXLEN]; /* make sure: spamserv is not disabled; x3 is running; spamserv is in the chan; chan is regged, user does exist */ - if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick))) + if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || IsOper(user) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick))) return; - if(!CHECK_CHANOPS(cInfo)) - { - struct modeNode *mn = GetUserMode(channel, user); - if(mn->modes & MODE_CHANOP) - return; - } + cData = channel->channel_info; + uData = GetChannelUser(cData, user->handle_info); + + if (user->handle_info) { + ta = dict_find(spamserv_trusted_accounts, user->handle_info->handle, NULL); + if (ta) { + unsigned int i = 0; + for (i=0; i < ta->channel->used; i++) { + if (!strcmp(ta->channel->list[i], channel->name)) + return; + + if (!strcmp(ta->channel->list[i], "global")) + return; + } + } + } - if(!CHECK_HALFOPS(cInfo)) - { - struct modeNode *mn = GetUserMode(channel, user); - if(mn->modes & MODE_HALFOP) - return; - } - - if(!CHECK_VOICED(cInfo)) + + if(uData && (uData->access >= cInfo->exceptlevel)) + return; + + if(CHECK_CAPSSCAN(cInfo) && check_caps(cInfo, text)) { - struct modeNode *mn = GetUserMode(channel, user); - if((mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP) && !(mn->modes & MODE_HALFOP)) - return; - } + if(uData && (uData->access >= cInfo->exceptcapslevel)) + return; + + if(CHECK_CAPS_WARNED(uInfo)) + { + switch(cInfo->info[ci_CapsReaction]) + { + case 'k': uInfo->flags |= USER_KICK; break; + case 'b': uInfo->flags |= USER_KICKBAN; break; + case 's': uInfo->flags |= USER_SHORT_TBAN; break; + case 'l': uInfo->flags |= USER_LONG_TBAN; break; + case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break; + } + + uInfo->warnlevel += CAPS_WARNLEVEL; + violation = 5; + } + else + { + uInfo->flags |= USER_CAPS_WARNED; + uInfo->lastcaps = now; + uInfo->warnlevel += CAPS_WARNLEVEL; + + if(uInfo->warnlevel < MAX_WARNLEVEL) { + if (spamserv_conf.network_rules) + spamserv_notice(user, "SSMSG_WARNING_RULES_T", SSMSG_CAPS, spamserv_conf.network_rules); + else + spamserv_notice(user, "SSMSG_WARNING_T", SSMSG_CAPS, spamserv_conf.network_rules); + } + } + + } to_lower(text); if(CHECK_SPAM(cInfo)) { + if(uData && (uData->access >= cInfo->exceptspamlevel)) + return; + if(!(sNode = uInfo->spam)) { spamserv_create_spamNode(channel, uInfo, text); @@ -1902,6 +2740,9 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * if(CHECK_FLOOD(cInfo)) { + if(uData && (uData->access >= cInfo->exceptfloodlevel)) + return; + if(!(fNode = uInfo->flood)) { spamserv_create_floodNode(channel, user, &uInfo->flood); @@ -1912,31 +2753,25 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * if(fNode->channel == channel) break; - if(!fNode) - { + if(!fNode) { spamserv_create_floodNode(channel, user, &uInfo->flood); - } - else - { - if(((now - fNode->time) < FLOOD_EXPIRE)) - { + } else { + if(((now - fNode->time) < FLOOD_EXPIRE)) { fNode->count++; - if(fNode->count == FLOOD_MAX_LINES - 1) - { - uInfo->warnlevel += FLOOD_WARNLEVEL; - - if(uInfo->warnlevel < MAX_WARNLEVEL) { - if (spamserv_conf.network_rules) - spamserv_notice(user, "SSMSG_WARNING_RULES_T", SSMSG_FLOOD, spamserv_conf.network_rules); - else - spamserv_notice(user, "SSMSG_WARNING_T", SSMSG_FLOOD, spamserv_conf.network_rules); - } + if(fNode->count == FLOOD_MAX_LINES - 1) { + uInfo->warnlevel += FLOOD_WARNLEVEL; + + if(uInfo->warnlevel < MAX_WARNLEVEL) { + if (spamserv_conf.network_rules) + spamserv_notice(user, "SSMSG_WARNING_RULES_T", SSMSG_FLOOD, spamserv_conf.network_rules); + else + spamserv_notice(user, "SSMSG_WARNING_T", SSMSG_FLOOD, spamserv_conf.network_rules); + } + fNode->time = now; } - else if(fNode->count > FLOOD_MAX_LINES) - { - switch(cInfo->info[ci_WarnReaction]) - { + else if(fNode->count > FLOOD_MAX_LINES) { + switch(cInfo->info[ci_WarnReaction]) { case 'k': uInfo->flags |= USER_KICK; break; case 'b': uInfo->flags |= USER_KICKBAN; break; case 's': uInfo->flags |= USER_SHORT_TBAN; break; @@ -1948,15 +2783,18 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * uInfo->warnlevel += FLOOD_WARNLEVEL; violation = 2; } - } - - fNode->time = now; + } else { + fNode->time = now; + } } } } if(CHECK_BADWORDSCAN(cInfo) && check_badwords(cInfo, text)) { + if(uData && (uData->access >= cInfo->exceptbadwordlevel)) + return; + if(CHECK_BAD_WARNED(uInfo)) { switch(cInfo->info[ci_BadReaction]) @@ -1988,6 +2826,9 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * if(CHECK_ADV(cInfo) && check_advertising(cInfo, text)) { + if(uData && (uData->access >= cInfo->exceptspamlevel)) + return; + if(CHECK_ADV_WARNED(uInfo)) { switch(cInfo->info[ci_AdvReaction]) @@ -2034,7 +2875,7 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * else uInfo->flags |= USER_KILL; - violation = 5; + violation = 6; } if(!violation) @@ -2047,6 +2888,7 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * case 2: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules); break; case 3: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules); break; case 4: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_BAD, spamserv_conf.network_rules); break; + case 5: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_CAPS, spamserv_conf.network_rules); break; default: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules); break; } @@ -2081,6 +2923,28 @@ spamserv_channel_message(struct chanNode *channel, struct userNode *user, char * } } +static int +trusted_account_read(const char *account, void *data, UNUSED_ARG(void *extra)) +{ + struct record_data *rd = data; + const char *str, *issuer; + struct string_list *strlist; + time_t issued; + + if (rd->type == RECDB_OBJECT) { + dict_t obj = GET_RECORD_OBJECT(rd); + /* new style structure */ + strlist = database_get_data(obj, KEY_CHANNELS, RECDB_STRING_LIST); + issuer = database_get_data(obj, KEY_ISSUER, RECDB_QSTRING); + str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING); + issued = str ? ParseInterval(str) : 0; + } else + return 0; + + spamserv_add_trusted_account(account, strlist, issuer, issued); + return 0; +} + static int spamserv_saxdb_read(struct dict *database) { @@ -2090,8 +2954,15 @@ spamserv_saxdb_read(struct dict *database) struct chanInfo *cInfo; struct string_list *strlist, *strlist2; unsigned int flags; + unsigned int exceptlevel, exceptadvlevel, exceptbadwordlevel; + unsigned int exceptfloodlevel, exceptspamlevel, exceptcapslevel; + unsigned int capsmin, capspercent; char *str, *info; time_t expiry; + dict_t object; + + if ((object = database_get_data(database, KEY_TRUSTED_HOSTS, RECDB_OBJECT))) + dict_foreach(object, trusted_account_read, spamserv_trusted_accounts); for(it = dict_first(database); it; it = iter_next(it)) { @@ -2104,6 +2975,9 @@ spamserv_saxdb_read(struct dict *database) } channel = GetChannel(iter_key(it)); + if (!strcmp("trusted", iter_key(it))) + continue; + strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST); strlist2 = database_get_data(hir->d.object, KEY_BADWORDS, RECDB_STRING_LIST); @@ -2115,6 +2989,30 @@ spamserv_saxdb_read(struct dict *database) str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING); expiry = str ? strtoul(str, NULL, 0) : 0; + str = database_get_data(hir->d.object, KEY_CAPSMIN, RECDB_QSTRING); + capsmin = str ? strtoul(str, NULL, 0) : 10; + + str = database_get_data(hir->d.object, KEY_CAPSPERCENT, RECDB_QSTRING); + capspercent = str ? strtoul(str, NULL, 0) : 25; + + str = database_get_data(hir->d.object, KEY_EXCEPTLEVEL, RECDB_QSTRING); + exceptlevel = str ? strtoul(str, NULL, 0) : UL_MANAGER; + + str = database_get_data(hir->d.object, KEY_EXCEPTADVLEVEL, RECDB_QSTRING); + exceptadvlevel = str ? strtoul(str, NULL, 0) : UL_OP; + + str = database_get_data(hir->d.object, KEY_EXCEPTBADWORDLEVEL, RECDB_QSTRING); + exceptbadwordlevel = str ? strtoul(str, NULL, 0) : UL_OP; + + str = database_get_data(hir->d.object, KEY_EXCEPTCAPSLEVEL, RECDB_QSTRING); + exceptcapslevel = str ? strtoul(str, NULL, 0) : UL_OP; + + str = database_get_data(hir->d.object, KEY_EXCEPTFLOODLEVEL, RECDB_QSTRING); + exceptfloodlevel = str ? strtoul(str, NULL, 0) : UL_OP; + + str = database_get_data(hir->d.object, KEY_EXCEPTSPAMLEVEL, RECDB_QSTRING); + exceptspamlevel = str ? strtoul(str, NULL, 0) : UL_OP; + if(channel && info) { if((cInfo = spamserv_register_channel(channel, strlist, strlist2, flags, info))) @@ -2129,7 +3027,16 @@ spamserv_saxdb_read(struct dict *database) else if(!CHECK_SUSPENDED(cInfo)) spamserv_join_channel(cInfo->channel); else - cInfo->suspend_expiry = expiry; + cInfo->suspend_expiry = expiry; + + cInfo->capsmin = capsmin; + cInfo->capspercent = capspercent; + cInfo->exceptlevel = exceptlevel; + cInfo->exceptadvlevel = exceptadvlevel; + cInfo->exceptbadwordlevel = exceptbadwordlevel; + cInfo->exceptcapslevel = exceptcapslevel; + cInfo->exceptfloodlevel = exceptfloodlevel; + cInfo->exceptspamlevel = exceptspamlevel; } } else @@ -2144,6 +3051,19 @@ spamserv_saxdb_write(struct saxdb_context *ctx) { dict_iterator_t it; + if (dict_size(spamserv_trusted_accounts)) { + saxdb_start_record(ctx, KEY_TRUSTED_ACCOUNTS, 1); + for (it = dict_first(spamserv_trusted_accounts); it; it = iter_next(it)) { + struct trusted_account *ta = iter_data(it); + saxdb_start_record(ctx, iter_key(it), 0); + if (ta->channel) saxdb_write_string_list(ctx, KEY_CHANNELS, ta->channel); + if (ta->issued) saxdb_write_int(ctx, KEY_ISSUED, ta->issued); + if (ta->issuer) saxdb_write_string(ctx, KEY_ISSUER, ta->issuer); + saxdb_end_record(ctx); + } + saxdb_end_record(ctx); + } + for(it = dict_first(registered_channels_dict); it; it = iter_next(it)) { struct chanInfo *cInfo = iter_data(it); @@ -2157,7 +3077,31 @@ spamserv_saxdb_write(struct saxdb_context *ctx) saxdb_write_string_list(ctx, KEY_BADWORDS, cInfo->badwords); if(cInfo->flags) - saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags); + saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags); + + if(cInfo->capsmin) + saxdb_write_int(ctx, KEY_CAPSMIN, cInfo->capsmin); + + if(cInfo->capspercent) + saxdb_write_int(ctx, KEY_CAPSPERCENT, cInfo->capspercent); + + if(cInfo->exceptlevel) + saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel); + + if(cInfo->exceptadvlevel) + saxdb_write_int(ctx, KEY_EXCEPTADVLEVEL, cInfo->exceptadvlevel); + + if(cInfo->exceptbadwordlevel) + saxdb_write_int(ctx, KEY_EXCEPTBADWORDLEVEL, cInfo->exceptbadwordlevel); + + if(cInfo->exceptcapslevel) + saxdb_write_int(ctx, KEY_EXCEPTCAPSLEVEL, cInfo->exceptcapslevel); + + if(cInfo->exceptfloodlevel) + saxdb_write_int(ctx, KEY_EXCEPTFLOODLEVEL, cInfo->exceptfloodlevel); + + if(cInfo->exceptspamlevel) + saxdb_write_int(ctx, KEY_EXCEPTSPAMLEVEL, cInfo->exceptspamlevel); saxdb_write_string(ctx, KEY_INFO, cInfo->info); @@ -2173,7 +3117,7 @@ static void spamserv_conf_read(void) { dict_t conf_node; - const char *str; + const char *str, *modes; if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT))) { @@ -2185,7 +3129,9 @@ spamserv_conf_read(void) if(str) { - spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL, NULL); + modes = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING); + + spamserv_conf.debug_channel = AddChannel(str, now, (modes ? modes : "+tinms"), NULL, NULL); if(spamserv_conf.debug_channel) spamserv_join_channel(spamserv_conf.debug_channel); @@ -2243,7 +3189,7 @@ spamserv_conf_read(void) } static void -spamserv_db_cleanup(void) +spamserv_db_cleanup(UNUSED_ARG(void* extra)) { dict_iterator_t it; @@ -2252,14 +3198,17 @@ spamserv_db_cleanup(void) spamserv_unregister_channel(iter_data(it)); } - while((it = dict_first(killed_users_dict))) +/* now handled automatically + * while((it = dict_first(killed_users_dict))) { free(iter_data(it)); } +*/ dict_delete(registered_channels_dict); dict_delete(connected_users_dict); dict_delete(killed_users_dict); + dict_delete(spamserv_trusted_accounts); } void @@ -2272,31 +3221,50 @@ init_spamserv(const char *nick) return; const char *modes = conf_get_data("services/spamserv/modes", RECDB_QSTRING); - spamserv = AddService(nick, modes ? modes : NULL, "Anti Spam Services", NULL); + spamserv = AddLocalUser(nick, nick, NULL, "Anti Spam Services", modes); spamserv_service = service_register(spamserv); conf_register_reload(spamserv_conf_read); SS_LOG = log_register_type("SpamServ", "file:spamserv.log"); + /* auto-free the keys for these dicts, + * and auto-free the keys AND data for killed_users_dict. + * other data need free'd manually. */ registered_channels_dict = dict_new(); + dict_set_free_keys(registered_channels_dict, free); connected_users_dict = dict_new(); + dict_set_free_keys(connected_users_dict, free); killed_users_dict = dict_new(); + dict_set_free_keys(killed_users_dict, free); + dict_set_free_data(killed_users_dict, free); + spamserv_trusted_accounts = dict_new(); + dict_set_free_keys(spamserv_trusted_accounts, free); + dict_set_free_data(spamserv_trusted_accounts, free); + + saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write); - reg_new_user_func(spamserv_new_user_func); - reg_del_user_func(spamserv_del_user_func); - reg_nick_change_func(spamserv_nick_change_func); - reg_join_func(spamserv_user_join); - reg_part_func(spamserv_user_part); + reg_new_user_func(spamserv_new_user_func, NULL); + reg_del_user_func(spamserv_del_user_func, NULL); + reg_nick_change_func(spamserv_nick_change_func, NULL); + reg_join_func(spamserv_user_join, NULL); + reg_part_func(spamserv_user_part, NULL); timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL); timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL); timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL); timeq_add(now + BAD_TIMEQ_FREQ, timeq_bad, NULL); + timeq_add(now + CAPS_TIMEQ_FREQ, timeq_caps, NULL); timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL); timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL); spamserv_module = module_register("SpamServ", SS_LOG, "spamserv.help", NULL); + + modcmd_register(spamserv_module, "ADDTRUST", cmd_addtrust, 3, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan", NULL); + modcmd_register(spamserv_module, "DELTRUST", cmd_deltrust, 3, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan", NULL); + modcmd_register(spamserv_module, "OADDTRUST", cmd_oaddtrust, 3, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL); + modcmd_register(spamserv_module, "ODELTRUST", cmd_odeltrust, 3, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL); + modcmd_register(spamserv_module, "LISTTRUST", cmd_listtrust, 2, MODCMD_REQUIRE_AUTHED, NULL); modcmd_register(spamserv_module, "REGISTER", cmd_register, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+acceptchan,+helping", NULL); modcmd_register(spamserv_module, "UNREGISTER", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+loghostmask", NULL); modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); @@ -2305,18 +3273,25 @@ init_spamserv(const char *nick) modcmd_register(spamserv_module, "DELBADWORD", cmd_delbadword, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL); modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET EXCEPTLEVEL", opt_exceptlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET EXCEPTADVLEVEL", opt_exceptadvlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET EXCEPTBADWORDLEVEL", opt_exceptbadwordlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET EXCEPTCAPSLEVEL", opt_exceptcapslevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET EXCEPTFLOODLEVEL", opt_exceptfloodlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET EXCEPTSPAMLEVEL", opt_exceptspamlevel, 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 BADREACTION", opt_badreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET CAPSREACTION", opt_capsreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET WARNREACTION", opt_warnreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET ADVSCAN", opt_advscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET CAPSSCAN", opt_capsscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET BADWORDSCAN", opt_badwordscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET SPAMSCAN", opt_spamscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET CHANFLOODSCAN", opt_chanfloodscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); modcmd_register(spamserv_module, "SET JOINFLOODSCAN", opt_joinflood, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); - modcmd_register(spamserv_module, "SET SCANCHANOPS", opt_scanops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); - modcmd_register(spamserv_module, "SET SCANHALFOPS", opt_scanhalfops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); - modcmd_register(spamserv_module, "SET SCANVOICED", opt_scanvoiced, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET CAPSMIN", opt_capsmin, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); + modcmd_register(spamserv_module, "SET CAPSPERCENT", opt_capspercent, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL); spamserv_service->trigger = spamserv_conf.trigger; @@ -2328,8 +3303,7 @@ init_spamserv(const char *nick) } } - saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write); - reg_exit_func(spamserv_db_cleanup); + reg_exit_func(spamserv_db_cleanup, NULL); message_register_table(msgtab); crc32_init(); }