-<<<<<<< HEAD
-/* spamserv.c - anti spam service\r
- * Copyright 2004 feigling\r
- *\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version. Important limitations are\r
- * listed in the COPYING file that accompanies this software.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, email srvx-maintainers@srvx.net.\r
- *\r
- * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $\r
- */\r
-\r
-#include "conf.h"\r
-#include "spamserv.h"\r
-#include "chanserv.h"\r
-#include "global.h"\r
-#include "modcmd.h"\r
-#include "saxdb.h"\r
-#include "timeq.h"\r
-#include "gline.h"\r
-\r
-#define SPAMSERV_CONF_NAME "services/spamserv"\r
-\r
-#define KEY_EXCEPTIONS "exceptions"\r
-#define KEY_FLAGS "flags"\r
-#define KEY_INFO "info"\r
-#define KEY_EXCEPTLEVEL "exceptlevel"\r
-#define KEY_EXPIRY "expiry"\r
-#define KEY_LASTBADWORDID "last_badword_id"\r
-#define KEY_BADWORDS "badwords"\r
-\r
-#define KEY_BADWORD_MASK "mask"\r
-#define KEY_BADWORD_TRIGGERED "count"\r
-#define KEY_BADWORD_ACTION "action"\r
-#define KEY_BADWORDID "badwordid"\r
-\r
-#define KEY_DEBUG_CHANNEL "debug_channel"\r
-#define KEY_GLOBAL_EXCEPTIONS "global_exceptions"\r
-#define KEY_NETWORK_RULES "network_rules"\r
-#define KEY_TRIGGER "trigger"\r
-#define KEY_SHORT_BAN_DURATION "short_ban_duration"\r
-#define KEY_LONG_BAN_DURATION "long_ban_duration"\r
-#define KEY_GLINE_DURATION "gline_duration"\r
-#define KEY_EXCEPTION_MAX "exception_max"\r
-#define KEY_EXCEPTION_MIN_LEN "exception_min_len"\r
-#define KEY_EXCEPTION_MAX_LEN "exception_max_len"\r
-#define KEY_ADV_CHAN_MUST_EXIST "adv_chan_must_exist"\r
-#define KEY_STRIP_MIRC_CODES "strip_mirc_codes"\r
-#define KEY_ALLOW_MOVE_MERGE "allow_move_merge"\r
-\r
-#define SPAMSERV_FUNC(NAME) MODCMD_FUNC(NAME)\r
-#define SPAMSERV_SYNTAX() svccmd_send_help(user, spamserv, cmd)\r
-#define SPAMSERV_MIN_PARMS(N) do { \\r
-(void)argv; \\r
- if(argc < N) { \\r
- ss_reply(MSG_MISSING_PARAMS, argv[0]); \\r
- SPAMSERV_SYNTAX(); \\r
- return 0; } } while(0)\r
-\r
-struct userNode *spamserv;\r
-static struct module *spamserv_module;\r
-static struct service *spamserv_service;\r
-static struct log_type *SS_LOG;\r
-static unsigned long crc_table[256];\r
-\r
-dict_t registered_channels_dict;\r
-dict_t connected_users_dict;\r
-dict_t killed_users_dict;\r
-\r
-#define spamserv_notice(target, format...) send_message(target , spamserv , ## format)\r
-#define spamserv_debug(format...) do { if(spamserv_conf.debug_channel) send_channel_message(spamserv_conf.debug_channel , spamserv , ## format); } while(0)\r
-#define ss_reply(format...) send_message(user , spamserv , ## format)\r
-\r
-#define SET_SUBCMDS_SIZE 10\r
-\r
-const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};\r
-\r
-static void spamserv_clear_spamNodes(struct chanNode *channel);\r
-static void spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban);\r
-static unsigned long crc32(const char *text);\r
-\r
-#define BINARY_OPTION(arguments...) return binary_option(arguments, user, channel, argc, argv);\r
-#define MULTIPLE_OPTION(arguments...) return multiple_option(arguments, values, ArrayLength(values), user, channel, argc, argv);\r
-\r
-static const struct message_entry msgtab[] = {\r
- { "SSMSG_CHANNEL_OPTIONS", "Channel Options:" },\r
- { "SSMSG_STRING_VALUE", "$b%s$b%s" },\r
- { "SSMSG_NUMERIC_VALUE", "$b%s$b%d - %s" },\r
- { "SSMSG_EASYNUMERIC_VALUE", "$b%s$b%d" },\r
- { "SSMSG_INVALID_NUM_SET", "$b'%d'$b is an invalid %s setting." },\r
- { "SSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },\r
- { "SSMSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },\r
-\r
- { "SSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$X$b." },\r
- { "SSMSG_NOT_REGISTERED_CS", "$b%s$b has not been registered with $b$C$b." },\r
- { "SSMSG_ALREADY_REGISTERED", "$b%s$b is already registered." },\r
- { "SSMSG_DEBUG_CHAN", "You may not register the debug channel." },\r
- { "SSMSG_SUSPENDED_CS", "$b$C$b access to $b%s$b has been temporarily suspended, thus you can't %s it." },\r
- { "SSMSG_SUSPENDED", "$b$X$b access to $b%s$b has been temporarily suspended." },\r
- { "SSMSG_NO_REGISTER", "Due to an error it was not possible to register $b%s$b." },\r
- { "SSMSG_REG_SUCCESS", "Channel $b%s$b registered." },\r
- { "SSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },\r
- { "SSMSG_NO_ACCESS", "You lack sufficient access to use this command." },\r
- { "SSMSG_MUST_BE_OPER", "You must be an irc operator to set this option." },\r
- { "SSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must append 'CONFIRM' to the end of your command. For example, 'unregister CONFIRM'." },\r
-\r
- { "SSMSG_NO_EXCEPTIONS", "No words found in the exception list." },\r
- { "SSMSG_NO_SUCH_EXCEPTION", "Word $b%s$b not found in the exception list." },\r
- { "SSMSG_EXCEPTION_LIST", "The following words are in the exception list:" },\r
- { "SSMSG_EXCEPTION_ADDED", "Word $b%s$b added to the exception list." },\r
- { "SSMSG_EXCEPTION_DELETED", "Word $b%s$b deleted from the exception list." },\r
- { "SSMSG_EXCEPTION_IN_LIST", "The word $b%s$b is already in the exception list." },\r
- { "SSMSG_EXCEPTION_MAX", "The exception list has reached the maximum exceptions (max %lu). Delete a word to add another one." },\r
- { "SSMSG_EXCEPTION_TOO_SHORT", "The word must be at least %lu characters long." },\r
- { "SSMSG_EXCEPTION_TOO_LONG", "The word may not be longer than %lu characters." },\r
-\r
- { "SSMSG_STATUS", "$bStatus:$b" },\r
- { "SSMSG_STATUS_USERS", "Total Users Online: %u" },\r
- { "SSMSG_STATUS_CHANNELS", "Registered Channels: %u" },\r
- { "SSMSG_STATUS_MEMORY", "$bMemory Information:$b" },\r
- { "SSMSG_STATUS_CHANNEL_LIST", "$bRegistered Channels:$b" },\r
- { "SSMSG_STATUS_NO_CHANNEL", "No channels registered." },\r
-\r
- { "SSMSG_BADWORD_ALREADY_ADDED", "$b%s$b is already added. (ID: %s)" },\r
- { "SSMSG_BADWORD_ADDED", "added '$b%s$b' to the badword list with ID %s." },\r
- { "SSMSG_BADWORD_SET_DONE", "Done." },\r
- { "SSMSG_BADWORD_SET_INVALID", "Invalid Option for setting %s" },\r
- { "SSMSG_BADWORD_SET", "Settings for BadWord entry $b%s$b" },\r
- { "SSMSG_BADWORD_SET_MASK", "$bMASK$b: %s" },\r
- { "SSMSG_BADWORD_SET_ACTION", "$bACTION$b: %s" },\r
- { "SSMSG_BADWORD_NOT_FOUND", "badword with ID %s does not exist." },\r
- { "SSMSG_BADWORD_REMOVED", "badword ID $b%s$b has been removed (mask: '%s')" },\r
- { NULL, NULL }\r
-};\r
-\r
-#define SSMSG_DEBUG_KICK "Kicked user $b%s$b from $b%s$b, reason: %s"\r
-#define SSMSG_DEBUG_BAN "Banned user $b%s$b from $b%s$b, reason: %s"\r
-#define SSMSG_DEBUG_KILL "Killed user $b%s$b, last violation in $b%s$b"\r
-#define SSMSG_DEBUG_GLINE "Glined user $b%s$b, host $b%s$b, last violation in $b%s$b"\r
-#define SSMSG_DEBUG_RECONNECT "Killed user $b%s$b reconnected to the network"\r
-#define SSMSG_SPAM "Spamming"\r
-#define SSMSG_FLOOD "Flooding the channel/network"\r
-#define SSMSG_ADV "Advertising"\r
-#define SSMSG_JOINFLOOD "Join flooding the channel"\r
-#define SSMSG_WARNING "%s is against the network rules"\r
-#define SSMSG_WARNING_2 "You are violating the network rules"\r
-#define SSMSG_WARNING_RULES "%s is against the network rules. Read the network rules at %s"\r
-#define SSMSG_WARNING_RULES_2 "You are violating the network rules. Read the network rules at %s"\r
-#define SSMSG_BADWORD_DETECTED "Your message contained a forbidden word."\r
-\r
-static struct\r
-{\r
- struct chanNode *debug_channel;\r
- struct string_list *global_exceptions;\r
- const char *network_rules;\r
- unsigned char trigger;\r
- unsigned long short_ban_duration;\r
- unsigned long long_ban_duration;\r
- unsigned long gline_duration;\r
- unsigned long exception_max;\r
- unsigned long exception_min_len;\r
- unsigned long exception_max_len;\r
- unsigned int adv_chan_must_exist : 1;\r
- unsigned int strip_mirc_codes : 1;\r
- unsigned int allow_move_merge : 1;\r
-} spamserv_conf;\r
-\r
-/***********************************************/\r
-/* Channel */\r
-/***********************************************/\r
-\r
-struct chanInfo*\r
-get_chanInfo(const char *channelname)\r
-{\r
- return dict_find(registered_channels_dict, channelname, 0);\r
-}\r
-\r
-static void\r
-spamserv_join_channel(struct chanNode *channel)\r
-{\r
- struct mod_chanmode change;\r
- mod_chanmode_init(&change);\r
- change.argc = 1;\r
- change.args[0].mode = MODE_CHANOP;\r
- change.args[0].u.member = AddChannelUser(spamserv, channel);\r
- mod_chanmode_announce(spamserv, channel, &change);\r
-}\r
-\r
-static void\r
-spamserv_part_channel(struct chanNode *channel, char *reason)\r
-{\r
- /* we only have to clear the spamNodes because every other node expires on it's own */\r
- spamserv_clear_spamNodes(channel);\r
- DelChannelUser(spamserv, channel, reason, 0);\r
-}\r
-\r
-static struct chanInfo*\r
-spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)\r
-{\r
- struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));\r
- \r
- if(!cInfo)\r
- {\r
- log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);\r
- return NULL;\r
- }\r
-\r
- cInfo->channel = channel;\r
- cInfo->exceptions = exceptions ? string_list_copy(exceptions) : alloc_string_list(1);\r
- cInfo->flags = flags;\r
- cInfo->exceptlevel = 400;\r
- safestrncpy(cInfo->info, info, sizeof(cInfo->info));\r
- cInfo->suspend_expiry = 0;\r
- cInfo->badwords = dict_new();\r
- cInfo->last_badword_id = 0;\r
- dict_insert(registered_channels_dict, cInfo->channel->name, cInfo);\r
-\r
- return cInfo;\r
-}\r
-\r
-static void\r
-spamserv_unregister_channel(struct chanInfo *cInfo)\r
-{\r
- if(!cInfo)\r
- return;\r
-\r
- dict_remove(registered_channels_dict, cInfo->channel->name);\r
- free_string_list(cInfo->exceptions);\r
- free(cInfo);\r
-}\r
-\r
-void\r
-spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
-\r
- if(cInfo)\r
- {\r
- if(suspend)\r
- {\r
- cInfo->flags |= CHAN_SUSPENDED;\r
- cInfo->suspend_expiry = expiry;\r
- spamserv_part_channel(channel, reason);\r
- }\r
- else\r
- {\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- cInfo->flags &= ~CHAN_SUSPENDED;\r
- cInfo->suspend_expiry = 0;\r
- }\r
- }\r
- }\r
-}\r
-\r
-int\r
-spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
-\r
- if(cInfo)\r
- {\r
- char reason[MAXLEN];\r
-\r
- if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))\r
- {\r
- if(move)\r
- snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);\r
- else\r
- snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);\r
-\r
- spamserv_cs_unregister(user, channel, manually, reason);\r
- return 0;\r
- }\r
-\r
- cInfo->channel = target;\r
-\r
- dict_remove(registered_channels_dict, channel->name);\r
- dict_insert(registered_channels_dict, target->name, cInfo);\r
-\r
- if(move)\r
- {\r
- snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);\r
- }\r
- else\r
- {\r
- spamserv_join_channel(target);\r
- snprintf(reason, sizeof(reason), "%s merged into %s by %s.", channel->name, target->name, user->handle_info->handle); \r
- }\r
-\r
- if(!CHECK_SUSPENDED(cInfo))\r
- spamserv_part_channel(channel, reason);\r
-\r
- if(move)\r
- snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);\r
- else\r
- snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);\r
-\r
- global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);\r
- return 1;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-void\r
-spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
-\r
- if(cInfo)\r
- {\r
- char global[MAXLEN], partmsg[MAXLEN];\r
-\r
- switch (type)\r
- {\r
- case manually:\r
- snprintf(global, sizeof(global), "$X (channel %s) %s by %s.", channel->name, reason, user->handle_info->handle);\r
- snprintf(partmsg, sizeof(partmsg), "%s %s by %s.", channel->name, reason, user->handle_info->handle); \r
- break;\r
- case expire:\r
- snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name);\r
- snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name); \r
- break;\r
- case lost_all_users:\r
- snprintf(global, sizeof(global), "$X (channel %s) lost all users.", channel->name);\r
- snprintf(partmsg, sizeof(partmsg), "%s lost all users.", channel->name); \r
- break;\r
- }\r
-\r
- if(!CHECK_SUSPENDED(cInfo))\r
- spamserv_part_channel(channel, partmsg);\r
- \r
- spamserv_unregister_channel(cInfo);\r
- global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, global);\r
- }\r
-}\r
-\r
-/***********************************************/\r
-/* User */\r
-/***********************************************/\r
-\r
-static struct userInfo*\r
-get_userInfo(const char *nickname)\r
-{\r
- return dict_find(connected_users_dict, nickname, 0);\r
-}\r
-\r
-static void\r
-spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)\r
-{\r
- struct spamNode *sNode = malloc(sizeof(struct spamNode));\r
-\r
- if(!sNode)\r
- {\r
- log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);\r
- return;\r
- }\r
-\r
- sNode->channel = channel; \r
- sNode->crc32 = crc32(text);\r
- sNode->count = 1;\r
- sNode->next = NULL;\r
-\r
- if(uInfo->spam)\r
- {\r
- struct spamNode *temp = uInfo->spam;\r
- \r
- while(temp->next)\r
- temp = temp->next;\r
-\r
- sNode->prev = temp;\r
- temp->next = sNode;\r
- }\r
- else\r
- {\r
- sNode->prev = NULL;\r
- uInfo->spam = sNode;\r
- }\r
-}\r
-\r
-static void\r
-spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)\r
-{\r
- if(!sNode)\r
- return;\r
-\r
- if(sNode == uInfo->spam)\r
- uInfo->spam = sNode->next;\r
- \r
- if(sNode->next)\r
- sNode->next->prev = sNode->prev;\r
- if(sNode->prev)\r
- sNode->prev->next = sNode->next;\r
-\r
- free(sNode);\r
-}\r
-\r
-static void\r
-spamserv_clear_spamNodes(struct chanNode *channel)\r
-{\r
- struct userInfo *uInfo;\r
- struct spamNode *sNode;\r
- unsigned int i;\r
-\r
- for(i = 0; i < channel->members.used; i++)\r
- {\r
- if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))\r
- {\r
- if((sNode = uInfo->spam))\r
- {\r
- for(; sNode; sNode = sNode->next)\r
- if(sNode->channel == channel)\r
- break;\r
- \r
- if(sNode)\r
- spamserv_delete_spamNode(uInfo, sNode);\r
- }\r
- }\r
- }\r
-}\r
-\r
-static void\r
-spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)\r
-{\r
- struct floodNode *fNode = malloc(sizeof(struct floodNode));\r
-\r
- if(!fNode)\r
- {\r
- log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);\r
- return;\r
- }\r
-\r
- fNode->channel = channel;\r
- fNode->owner = user;\r
- fNode->count = 1;\r
- fNode->time = now; \r
- fNode->next = NULL;\r
-\r
- if(*uI_fNode)\r
- {\r
- struct floodNode *temp = *uI_fNode;\r
- \r
- while(temp->next)\r
- temp = temp->next;\r
- \r
- fNode->prev = temp;\r
- temp->next = fNode;\r
- }\r
- else\r
- {\r
- fNode->prev = NULL;\r
- *uI_fNode = fNode;\r
- }\r
-}\r
-\r
-static void\r
-spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)\r
-{\r
- if(!fNode)\r
- return;\r
-\r
- if(fNode == *uI_fNode)\r
- *uI_fNode = fNode->next;\r
- \r
- if(fNode->next)\r
- fNode->next->prev = fNode->prev;\r
- if(fNode->prev)\r
- fNode->prev->next = fNode->next;\r
-\r
- free(fNode);\r
-}\r
-\r
-static void\r
-spamserv_create_user(struct userNode *user)\r
-{\r
- struct userInfo *uInfo = malloc(sizeof(struct userInfo));\r
- struct killNode *kNode = dict_find(killed_users_dict, irc_ntoa(&user->ip), 0);\r
-\r
- if(!uInfo)\r
- {\r
- log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);\r
- return;\r
- }\r
-\r
- if(kNode)\r
- spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);\r
-\r
- uInfo->user = user;\r
- uInfo->spam = NULL;\r
- uInfo->flood = NULL;\r
- uInfo->joinflood = NULL;\r
- uInfo->flags = kNode ? USER_KILLED : 0;\r
- uInfo->warnlevel = kNode ? kNode->warnlevel : 0;\r
- uInfo->lastadv = 0;\r
-\r
- dict_insert(connected_users_dict, user->nick, uInfo);\r
-\r
- if(kNode)\r
- {\r
- dict_remove(killed_users_dict, irc_ntoa(&user->ip));\r
- free(kNode);\r
- }\r
-}\r
-\r
-static void\r
-spamserv_delete_user(struct userInfo *uInfo)\r
-{\r
- if(!uInfo)\r
- return;\r
-\r
- if(uInfo->spam)\r
- while(uInfo->spam)\r
- spamserv_delete_spamNode(uInfo, uInfo->spam); \r
-\r
- if(uInfo->flood)\r
- while(uInfo->flood)\r
- spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);\r
-\r
- if(uInfo->joinflood)\r
- while(uInfo->joinflood)\r
- spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);\r
-\r
- dict_remove(connected_users_dict, uInfo->user->nick);\r
- free(uInfo);\r
-}\r
-\r
-static void\r
-spamserv_new_user_func(struct userNode *user)\r
-{\r
- if(!IsLocal(user))\r
- spamserv_create_user(user);\r
-}\r
-\r
-static void\r
-spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))\r
-{\r
- struct userInfo *uInfo = get_userInfo(user->nick);\r
- struct killNode *kNode;\r
-\r
- if(killer == spamserv)\r
- {\r
- kNode = malloc(sizeof(struct killNode));\r
-\r
- if(!kNode)\r
- {\r
- log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for killNode - nickname %s", user->nick);\r
- spamserv_delete_user(uInfo); \r
- return;\r
- }\r
-\r
- if(uInfo->warnlevel > KILL_WARNLEVEL)\r
- kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;\r
- else\r
- kNode->warnlevel = 0;\r
-\r
- kNode->time = now;\r
-\r
- dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);\r
- }\r
-\r
- spamserv_delete_user(uInfo); \r
-}\r
-\r
-static void\r
-spamserv_nick_change_func(struct userNode *user, const char *old_nick)\r
-{\r
- struct userInfo *uInfo = get_userInfo(old_nick);\r
-\r
- dict_remove(connected_users_dict, old_nick);\r
- dict_insert(connected_users_dict, user->nick, uInfo);\r
-}\r
-\r
-static int\r
-spamserv_user_join(struct modeNode *mNode)\r
-{\r
- struct chanNode *channel = mNode->channel;\r
- struct userNode *user = mNode->user; \r
- struct chanInfo *cInfo;\r
- struct userInfo *uInfo;\r
- struct floodNode *jfNode;\r
-\r
- if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))\r
- return 0;\r
- \r
- \r
- if(!CHECK_CHANOPS(cInfo))\r
- {\r
- //struct modeNode *mn = GetUserMode(channel, user);\r
- //if(mn->modes & MODE_CHANOP)\r
- // return;\r
- if(check_user_level(channel, user, lvlGiveOps, 1, 0)) \r
- return 0;\r
- }\r
- \r
- if(!CHECK_VOICED(cInfo))\r
- {\r
- if(check_user_level(channel, user, lvlGiveVoice, 1, 0)) \r
- return 0;\r
- }\r
- \r
- if(cInfo->exceptlevel == 0)\r
- return 0;\r
- if(cInfo->exceptlevel < 501) {\r
- struct userData *uData;\r
- if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {\r
- return 0;\r
- }\r
- }\r
-\r
- if(!(jfNode = uInfo->joinflood))\r
- {\r
- spamserv_create_floodNode(channel, user, &uInfo->joinflood);\r
- }\r
- else\r
- {\r
- for(; jfNode; jfNode = jfNode->next)\r
- if(jfNode->channel == channel)\r
- break;\r
-\r
- if(!jfNode)\r
- {\r
- spamserv_create_floodNode(channel, user, &uInfo->joinflood);\r
- }\r
- else\r
- {\r
- jfNode->count++;\r
- jfNode->time = now; \r
-\r
- if(jfNode->count > JOINFLOOD_MAX)\r
- {\r
- char reason[MAXLEN];\r
-\r
- spamserv_delete_floodNode(&uInfo->joinflood, jfNode);\r
- snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_JOINFLOOD, spamserv_conf.network_rules);\r
- spamserv_punish(channel, user, JOINFLOOD_B_DURATION, reason, 1);\r
- }\r
- }\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-static void\r
-spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))\r
-{\r
- struct userNode *user = mn->user;\r
- struct chanNode *channel = mn->channel;\r
- struct userInfo *uInfo;\r
- struct spamNode *sNode;\r
- struct floodNode *fNode;\r
-\r
- if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))\r
- return;\r
-\r
- if((sNode = uInfo->spam))\r
- {\r
- for(; sNode; sNode = sNode->next)\r
- if(sNode->channel == channel)\r
- break;\r
-\r
- if(sNode)\r
- spamserv_delete_spamNode(uInfo, sNode);\r
- }\r
-\r
- if((fNode = uInfo->flood))\r
- {\r
- for(; fNode; fNode = fNode->next)\r
- if(fNode->channel == channel)\r
- break;\r
-\r
- if(fNode)\r
- spamserv_delete_floodNode(&uInfo->flood, fNode);\r
- }\r
-}\r
-\r
-static struct badword*\r
-add_badword(struct chanInfo *cInfo, const char *badword_mask, unsigned int triggered, unsigned int action, const char *id)\r
-{\r
- struct badword *badword;\r
-\r
- badword = calloc(1, sizeof(*badword));\r
- if (!badword)\r
- return NULL;\r
-\r
- if(!id) {\r
- cInfo->last_badword_id++;\r
- badword->id = strtab(cInfo->last_badword_id);\r
- } else\r
- badword->id = strdup(id);\r
- badword->badword_mask = strdup(badword_mask);\r
- badword->triggered = triggered;\r
- badword->action = action;\r
- dict_insert(cInfo->badwords, badword->id, badword);\r
- return badword;\r
-}\r
-\r
-/***********************************************/\r
-/* Other Stuff */\r
-/***********************************************/\r
-\r
-static void\r
-crc32_init(void)\r
-{\r
- unsigned long crc;\r
- int i, j;\r
-\r
- for(i = 0; i < 256; i++)\r
- {\r
- crc = i;\r
-\r
- for(j = 8; j > 0; j--)\r
- {\r
- if(crc & 1)\r
- {\r
- crc = (crc >> 1) ^ 0xEDB88320L;\r
- }\r
- else\r
- {\r
- crc >>= 1;\r
- }\r
- }\r
-\r
- crc_table[i] = crc;\r
- }\r
-}\r
-\r
-static unsigned long\r
-crc32(const char *text)\r
-{\r
- register unsigned long crc = 0xFFFFFFFF;\r
- unsigned int c, i = 0;\r
- \r
- while((c = (unsigned int)text[i++]) != 0)\r
- crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];\r
- \r
- return (crc^0xFFFFFFFF);\r
-}\r
-\r
-static void\r
-timeq_flood(UNUSED_ARG(void *data))\r
-{\r
- dict_iterator_t it;\r
- struct userInfo *uInfo;\r
- struct floodNode *fNode;\r
-\r
- for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
- {\r
- uInfo = iter_data(it);\r
-\r
- if(!(fNode = uInfo->flood))\r
- continue;\r
-\r
- for(; fNode; fNode = fNode->next)\r
- {\r
- if(now - fNode->time > FLOOD_EXPIRE)\r
- {\r
- if(!(--fNode->count))\r
- spamserv_delete_floodNode(&uInfo->flood, fNode);\r
- }\r
- }\r
- }\r
- \r
- timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);\r
-}\r
-\r
-static void\r
-timeq_joinflood(UNUSED_ARG(void *data))\r
-{\r
- dict_iterator_t it;\r
- struct userInfo *uInfo;\r
- struct floodNode *fNode;\r
-\r
- for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
- {\r
- uInfo = iter_data(it);\r
-\r
- if(!(fNode = uInfo->joinflood))\r
- continue;\r
-\r
- for(; fNode; fNode = fNode->next)\r
- {\r
- if(now - fNode->time > JOINFLOOD_EXPIRE)\r
- {\r
- if(!(--fNode->count))\r
- spamserv_delete_floodNode(&uInfo->joinflood, fNode); \r
- }\r
- }\r
- }\r
-\r
- timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);\r
-}\r
-\r
-static void\r
-timeq_adv(UNUSED_ARG(void *data))\r
-{\r
- dict_iterator_t it;\r
- struct userInfo *uInfo;\r
-\r
- for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
- {\r
- uInfo = iter_data(it);\r
-\r
- if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)\r
- {\r
- uInfo->lastadv = 0;\r
- uInfo->flags &= ~USER_ADV_WARNED;\r
- }\r
- }\r
-\r
- timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);\r
-}\r
-\r
-static void\r
-timeq_warnlevel(UNUSED_ARG(void *data))\r
-{\r
- dict_iterator_t it;\r
- struct userInfo *uInfo;\r
-\r
- for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
- {\r
- uInfo = iter_data(it);\r
-\r
- if(uInfo->warnlevel > 0)\r
- uInfo->warnlevel--;\r
- }\r
-\r
- timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);\r
-}\r
-\r
-static void\r
-timeq_kill(UNUSED_ARG(void *data))\r
-{\r
- dict_iterator_t it;\r
- struct killNode *kNode;\r
-\r
- for(it = dict_first(killed_users_dict); it; it = iter_next(it))\r
- {\r
- kNode = iter_data(it);\r
-\r
- if(kNode->time - now > KILL_EXPIRE)\r
- free(kNode);\r
- }\r
-\r
- timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);\r
-}\r
-\r
-static int\r
-binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- int value;\r
-\r
- if(argc > 1)\r
- {\r
- if(enabled_string(argv[1]))\r
- {\r
- cInfo->flags |= mask;\r
- value = 1;\r
- }\r
- else if(disabled_string(argv[1]))\r
- {\r
- cInfo->flags &= ~mask;\r
- value = 0;\r
- }\r
- else\r
- {\r
- spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);\r
- return 0;\r
- }\r
- }\r
- else\r
- {\r
- value = (cInfo->flags & mask) ? 1 : 0;\r
- }\r
-\r
- spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");\r
- return 1;\r
-}\r
-\r
-struct valueData\r
-{\r
- char *description;\r
- char value;\r
- int oper_only : 1;\r
-};\r
-\r
-static int\r
-multiple_option(char *name, char *description, enum channelinfo info, struct valueData *values, int count, struct userNode *user, struct chanNode *channel, int argc, char *argv[])\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- int index;\r
-\r
- if(argc > 1)\r
- {\r
- index = atoi(argv[1]);\r
- \r
- if(index < 0 || index >= count)\r
- {\r
- spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);\r
-\r
- for(index = 0; index < count; index++)\r
- spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);\r
-\r
- return 0;\r
- }\r
-\r
- if(values[index].oper_only && !IsOper(user))\r
- {\r
- spamserv_notice(user, "SSMSG_MUST_BE_OPER");\r
- return 0;\r
- }\r
- \r
- cInfo->info[info] = values[index].value;\r
- }\r
- else\r
- {\r
- for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);\r
- }\r
-\r
- spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);\r
- return 1;\r
-}\r
-\r
-static int\r
-show_exceptions(struct userNode *user, struct chanInfo *cInfo)\r
-{\r
- struct helpfile_table table;\r
- unsigned int i;\r
-\r
- if(!cInfo->exceptions->used)\r
- {\r
- spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");\r
- return 0;\r
- }\r
-\r
- spamserv_notice(user, "SSMSG_EXCEPTION_LIST");\r
-\r
- table.length = 0;\r
- table.width = 1;\r
- table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;\r
- table.contents = alloca(cInfo->exceptions->used * sizeof(*table.contents));\r
-\r
- for(i = 0; i < cInfo->exceptions->used; i++)\r
- {\r
- table.contents[table.length] = alloca(table.width * sizeof(**table.contents));\r
- table.contents[table.length][0] = cInfo->exceptions->list[i];\r
- table.length++;\r
- }\r
- \r
- table_send(spamserv, user->nick, 0, NULL, table);\r
-\r
- return 1;\r
-}\r
-\r
-static void\r
-show_memory_usage(struct userNode *user)\r
-{\r
- dict_iterator_t it;\r
- struct helpfile_table table;\r
- struct chanInfo *cInfo;\r
- struct userInfo *uInfo;\r
- struct spamNode *sNode;\r
- struct floodNode *fNode;\r
- double channel_size = 0, user_size, size;\r
- unsigned int spamcount = 0, floodcount = 0, i, j;\r
- char buffer[64];\r
-\r
- for(it = dict_first(registered_channels_dict); it; it = iter_next(it))\r
- {\r
- cInfo = iter_data(it);\r
-\r
- if(!cInfo->exceptions->used)\r
- continue;\r
-\r
- for(i = 0; i < cInfo->exceptions->used; i++)\r
- channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char); \r
- }\r
-\r
- for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
- {\r
- uInfo = iter_data(it);\r
-\r
- for(sNode = uInfo->spam; sNode; sNode = sNode->next, spamcount++);\r
- for(fNode = uInfo->flood; fNode; fNode = fNode->next, floodcount++);\r
- for(fNode = uInfo->joinflood; fNode; fNode = fNode->next, floodcount++);\r
- }\r
-\r
- channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);\r
- \r
- user_size = dict_size(connected_users_dict) * sizeof(struct userInfo) +\r
- dict_size(killed_users_dict) * sizeof(struct killNode) +\r
- spamcount * sizeof(struct spamNode) +\r
- floodcount * sizeof(struct floodNode);\r
-\r
- size = channel_size + user_size;\r
- \r
- ss_reply("SSMSG_STATUS_MEMORY");\r
- \r
- table.length = 3;\r
- table.width = 4;\r
- table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;\r
- table.contents = calloc(table.length, sizeof(char**));\r
-\r
- // chanInfo\r
- table.contents[0] = calloc(table.width, sizeof(char*));\r
- snprintf(buffer, sizeof(buffer), "Channel Memory Usage:");\r
- table.contents[0][0] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), " %g Byte; ", channel_size);\r
- table.contents[0][1] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), "%g KiloByte; ", channel_size / 1024);\r
- table.contents[0][2] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), "%g MegaByte", channel_size / 1024 / 1024);\r
- table.contents[0][3] = strdup(buffer);\r
-\r
- // userInfo\r
- table.contents[1] = calloc(table.width, sizeof(char*));\r
- snprintf(buffer, sizeof(buffer), "User Memory Usage :");\r
- table.contents[1][0] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), " %g Byte; ", user_size);\r
- table.contents[1][1] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), "%g KiloByte; ", user_size / 1024);\r
- table.contents[1][2] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), "%g MegaByte", user_size / 1024 / 1024);\r
- table.contents[1][3] = strdup(buffer);\r
-\r
- // total memory usage\r
- table.contents[2] = calloc(table.width, sizeof(char*));\r
- snprintf(buffer, sizeof(buffer), "Total Memory Usage :");\r
- table.contents[2][0] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), " %g Byte; ", size);\r
- table.contents[2][1] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), "%g KiloByte; ", size / 1024);\r
- table.contents[2][2] = strdup(buffer);\r
- snprintf(buffer, sizeof(buffer), "%g MegaByte", size / 1024 / 1024);\r
- table.contents[2][3] = strdup(buffer);\r
-\r
- table_send(spamserv, user->nick, 0, NULL, table);\r
- \r
- for(i = 0; i < table.length; i++)\r
- {\r
- for(j = 0; j < table.width; j++)\r
- free((char*)table.contents[i][j]);\r
-\r
- free(table.contents[i]);\r
- }\r
-\r
- free(table.contents);\r
-}\r
-\r
-static void\r
-show_registered_channels(struct userNode *user)\r
-{\r
- struct helpfile_table table;\r
- dict_iterator_t it;\r
-\r
- spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");\r
-\r
- if(!dict_size(registered_channels_dict))\r
- {\r
- spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");\r
- return;\r
- }\r
-\r
- table.length = 0;\r
- table.width = 1;\r
- table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;\r
- table.contents = alloca(dict_size(registered_channels_dict) * sizeof(*table.contents));\r
-\r
- for(it = dict_first(registered_channels_dict); it; it = iter_next(it))\r
- {\r
- struct chanInfo *cInfo = iter_data(it);\r
-\r
- table.contents[table.length] = alloca(table.width * sizeof(**table.contents));\r
- table.contents[table.length][0] = cInfo->channel->name;\r
- table.length++;\r
- }\r
- \r
- table_send(spamserv, user->nick, 0, NULL, table);\r
-}\r
-\r
-/***********************************************/\r
-/* SpamServ_Func */\r
-/***********************************************/\r
-\r
-static \r
-SPAMSERV_FUNC(cmd_register)\r
-{\r
- struct chanInfo *cInfo;\r
- char reason[MAXLEN];\r
-\r
- if(!channel || !channel->channel_info)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);\r
- return 0;\r
- }\r
-\r
- if(get_chanInfo(channel->name))\r
- {\r
- ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(IsSuspended(channel->channel_info))\r
- {\r
- ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");\r
- return 0;\r
- }\r
-\r
- if(channel == spamserv_conf.debug_channel)\r
- {\r
- ss_reply("SSMSG_DEBUG_CHAN");\r
- return 0;\r
- }\r
-\r
- if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))\r
- {\r
- ss_reply("SSMSG_NO_REGISTER", channel->name);\r
- return 0;\r
- }\r
-\r
- spamserv_join_channel(cInfo->channel);\r
- \r
- snprintf(reason, sizeof(reason), "%s (channel %s) registered by %s.", spamserv->nick, channel->name, user->handle_info->handle);\r
- global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);\r
- ss_reply("SSMSG_REG_SUCCESS", channel->name);\r
-\r
- return 1;\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(cmd_unregister)\r
-{\r
- struct chanInfo *cInfo;\r
- struct chanData *cData;\r
- struct userData *uData;\r
- char reason[MAXLEN];\r
-\r
- if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- if(!IsHelping(user))\r
- {\r
- if(IsSuspended(cData))\r
- {\r
- ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");\r
- return 0;\r
- }\r
-\r
- if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))\r
- {\r
- ss_reply("SSMSG_CONFIRM_UNREG");\r
- return 0;\r
- }\r
- }\r
-\r
- if(!CHECK_SUSPENDED(cInfo))\r
- {\r
- snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle); \r
- spamserv_part_channel(channel, reason);\r
- }\r
- \r
- spamserv_unregister_channel(cInfo); \r
-\r
- snprintf(reason, sizeof(reason), "%s (channel %s) unregistered by %s.", spamserv->nick, channel->name, user->handle_info->handle);\r
- global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);\r
- ss_reply("SSMSG_UNREG_SUCCESS", channel->name);\r
-\r
- return 1;\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(cmd_status)\r
-{\r
- ss_reply("SSMSG_STATUS");\r
- ss_reply("SSMSG_STATUS_USERS", dict_size(connected_users_dict));\r
- ss_reply("SSMSG_STATUS_CHANNELS", dict_size(registered_channels_dict));\r
-\r
- if(IsOper(user) && argc > 1)\r
- {\r
- if(!irccasecmp(argv[1], "memory"))\r
- show_memory_usage(user);\r
- else if(!irccasecmp(argv[1], "channels"))\r
- show_registered_channels(user); \r
- }\r
- \r
- return 1;\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(cmd_addexception)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
- unsigned int i;\r
-\r
- if(!cInfo || !channel->channel_info)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- if(argc < 2)\r
- return show_exceptions(user, cInfo);\r
-\r
- if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))\r
- {\r
- ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);\r
- return 0;\r
- }\r
-\r
- if(strlen(argv[1]) < spamserv_conf.exception_min_len)\r
- {\r
- ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);\r
- return 0;\r
- }\r
- else if(strlen(argv[1]) > spamserv_conf.exception_max_len)\r
- {\r
- ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);\r
- return 0;\r
- }\r
-\r
- for(i = 0; i < cInfo->exceptions->used; i++)\r
- {\r
- if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))\r
- {\r
- ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);\r
- return 0;\r
- }\r
- }\r
-\r
- string_list_append(cInfo->exceptions, strdup(argv[1]));\r
- ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);\r
-\r
- return 1;\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(cmd_delexception)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
- unsigned int i;\r
- int found = -1;\r
-\r
- if(!cInfo || !channel->channel_info)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- if(argc < 2)\r
- return show_exceptions(user, cInfo);\r
-\r
- for(i = 0; i < cInfo->exceptions->used; i++)\r
- {\r
- if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))\r
- {\r
- found = i;\r
- break;\r
- }\r
- }\r
- \r
- if(found == -1)\r
- {\r
- ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);\r
- return 0;\r
- }\r
-\r
- string_list_delete(cInfo->exceptions, i);\r
- ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);\r
-\r
- return 1;\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(cmd_set)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct svccmd *subcmd; \r
- char cmd_name[MAXLEN];\r
- unsigned int i;\r
-\r
- if(!cInfo)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!check_user_level(channel,user,lvlSetters,1,0))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
- \r
- if(argc < 2)\r
- {\r
- ss_reply("SSMSG_CHANNEL_OPTIONS");\r
-\r
- for(i = 0; i < SET_SUBCMDS_SIZE; i++)\r
- {\r
- sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);\r
-\r
- if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))\r
- subcmd->command->func(user, channel, 1, argv + 1, subcmd);\r
- }\r
-\r
- return 1;\r
- }\r
-\r
- sprintf(cmd_name, "%s %s", cmd->name, argv[1]);\r
- subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);\r
-\r
- if(!subcmd)\r
- {\r
- reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);\r
- return 0;\r
- }\r
-\r
- return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_spamlimit)\r
-{\r
- struct valueData values[] =\r
- {\r
- {"Users may send the same message $b2$b times.", 'a', 0},\r
- {"Users may send the same message $b3$b times.", 'b', 0},\r
- {"Users may send the same message $b4$b times.", 'c', 0},\r
- {"Users may send the same message $b5$b times.", 'd', 0},\r
- {"Users may send the same message $b6$b times.", 'e', 0}\r
- };\r
-\r
- MULTIPLE_OPTION("SpamLimit ", "SpamLimit", ci_SpamLimit);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_advreaction)\r
-{\r
- struct valueData values[] =\r
- {\r
- {"Kick on disallowed advertising.", 'k', 0},\r
- {"Kickban on disallowed advertising.", 'b', 0},\r
- {"Short timed ban on disallowed advertising.", 's', 0},\r
- {"Long timed ban on disallowed advertising.", 'l', 0},\r
- {"Kill on disallowed advertising.", 'd', 1}\r
- };\r
-\r
- MULTIPLE_OPTION("AdvReaction ", "AdvReaction", ci_AdvReaction);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_warnreaction)\r
-{\r
- struct valueData values[] =\r
- {\r
- {"Kick after warning.", 'k', 0},\r
- {"Kickban after warning.", 'b', 0},\r
- {"Short timed ban after warning.", 's', 0},\r
- {"Long timed ban after warning.", 'l', 0},\r
- {"Kill after warning.", 'd', 1}\r
- };\r
-\r
- MULTIPLE_OPTION("WarnReaction ", "WarnReaction", ci_WarnReaction);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_advscan)\r
-{\r
- BINARY_OPTION("AdvScan ", CHAN_ADV_SCAN);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_spamscan)\r
-{\r
- BINARY_OPTION("SpamScan ", CHAN_SPAMSCAN);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_floodscan)\r
-{\r
- BINARY_OPTION("FloodScan ", CHAN_FLOODSCAN);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_joinflood)\r
-{\r
- BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_scanops)\r
-{\r
- BINARY_OPTION("ScanChanOps ", CHAN_SCAN_CHANOPS);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_scanvoiced)\r
-{\r
- BINARY_OPTION("ScanVoiced ", CHAN_SCAN_VOICED);\r
-}\r
-\r
-static \r
-SPAMSERV_FUNC(opt_exceptlevel)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
- int index;\r
- if(argc > 1)\r
- {\r
- index = atoi(argv[1]);\r
- if(index < 1 || index > 501)\r
- {\r
- spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");\r
- return 0;\r
- }\r
- if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
- cInfo->exceptlevel=index;\r
- } else {\r
- index=cInfo->exceptlevel;\r
- }\r
- spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel ", index);\r
- return 1;\r
-}\r
-\r
-static SPAMSERV_FUNC(cmd_addbad)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
-\r
- if(!cInfo)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!check_user_level(channel,user,lvlSetters,1,0))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- dict_iterator_t it;\r
- char *mask = unsplit_string(argv + 1, argc - 1, NULL);\r
- for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {\r
- struct badword *badword = iter_data(it);\r
- if(match_ircglob(mask,badword->badword_mask)) {\r
- reply("SSMSG_BADWORD_ALREADY_ADDED", mask, badword->id);\r
- return 1;\r
- }\r
- }\r
-\r
- struct badword *new_badword = add_badword(cInfo, mask, 0, BADACTION_KICK, NULL);\r
- for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {\r
- struct badword *badword = iter_data(it);\r
- if(match_ircglob(badword->badword_mask, new_badword->badword_mask) && badword != new_badword) {\r
- dict_remove(cInfo->badwords, badword->id);\r
- }\r
- }\r
-\r
- reply("SSMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id);\r
- return 1;\r
-}\r
-\r
-static SPAMSERV_FUNC(cmd_delbad)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
- unsigned int n;\r
-\r
- if(!cInfo)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!check_user_level(channel,user,lvlSetters,1,0))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- for (n=1; n<argc; n++) {\r
- struct badword *badword = dict_find(cInfo->badwords, argv[n], NULL);\r
- if (!badword) {\r
- reply("SSMSG_BADWORD_NOT_FOUND", argv[n]);\r
- continue;\r
- }\r
- reply("SSMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);\r
- dict_remove(cInfo->badwords, argv[n]);\r
- }\r
- return 1;\r
-}\r
-\r
-static SPAMSERV_FUNC(cmd_setbad)\r
-{\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
- struct badword *badword;\r
-\r
- if(!cInfo)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!check_user_level(channel,user,lvlSetters,1,0))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- if ((badword = dict_find(cInfo->badwords, argv[1], NULL))) {\r
- if (argc > 3) {\r
- unsigned int ii;\r
- char *setting = argv[2];\r
- char *value = argv[3];\r
- for( ii = 0; setting[ ii ]; ii++)\r
- setting[ ii ] = toupper( setting[ ii ] );\r
- for( ii = 0; value[ ii ]; ii++)\r
- value[ ii ] = toupper( value[ ii ] );\r
- if(!strcmp("MASK",setting)) {\r
- free(badword->badword_mask);\r
- badword->badword_mask = strdup(argv[3]);\r
- badword->triggered = 0;\r
- reply("SSMSG_BADWORD_SET_DONE");\r
- }\r
- else if(!strcmp("ACTION",setting)) {\r
- if (!strcmp("1",value) || !strcmp("KICK",value)) {\r
- badword->action = BADACTION_KICK;\r
- reply("SSMSG_BADWORD_SET_DONE");\r
- } else if (!strcmp("2",value) || !strcmp("BAN",value)) {\r
- badword->action = BADACTION_BAN;\r
- reply("SSMSG_BADWORD_SET_DONE");\r
- } else if (!strcmp("3",value) || !strcmp("KILL",value)) {\r
- badword->action = BADACTION_KILL;\r
- reply("SSMSG_BADWORD_SET_DONE");\r
- } else if (!strcmp("4",value) || !strcmp("GLINE",value)) {\r
- badword->action = BADACTION_GLINE;\r
- reply("SSMSG_BADWORD_SET_DONE");\r
- } else {\r
- reply("SSMSG_BADWORD_SET_INVALID", setting);\r
- }\r
- } else {\r
- reply("SSMSG_BADWORD_SETTING_INVALID", setting);\r
- }\r
-\r
- } else {\r
- reply("SSMSG_BADWORD_SET", badword->id);\r
- reply("SSMSG_BADWORD_SET_MASK", badword->badword_mask);\r
- switch(badword->action) {\r
- case BADACTION_KICK:\r
- reply("SSMSG_BADWORD_SET_ACTION", "KICK");\r
- break;\r
- case BADACTION_BAN:\r
- reply("SSMSG_BADWORD_SET_ACTION", "BAN");\r
- break;\r
- case BADACTION_KILL:\r
- reply("SSMSG_BADWORD_SET_ACTION", "KILL");\r
- break;\r
- case BADACTION_GLINE:\r
- reply("SSMSG_BADWORD_SET_ACTION", "GLINE");\r
- break;\r
- default:\r
- reply("SSMSG_BADWORD_SET_ACTION", "*undef*");\r
- }\r
- }\r
- } else {\r
- reply("SSMSG_BADWORD_NOT_FOUND", argv[1]);\r
- return 0;\r
- }\r
- return 1;\r
-}\r
-\r
-int\r
-ss_badwords_sort(const void *pa, const void *pb)\r
-{\r
- struct badword *a = *(struct badword**)pa;\r
- struct badword *b = *(struct badword**)pb;\r
-\r
- return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0);\r
-}\r
-\r
-static SPAMSERV_FUNC(cmd_listbad)\r
-{\r
- struct helpfile_table tbl;\r
- struct chanInfo *cInfo = get_chanInfo(channel->name);\r
- struct userData *uData;\r
- struct badword **badwords;\r
- unsigned int count = 0, ii = 0;\r
-\r
- if(!cInfo)\r
- {\r
- ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(CHECK_SUSPENDED(cInfo))\r
- {\r
- ss_reply("SSMSG_SUSPENDED", channel->name);\r
- return 0;\r
- }\r
-\r
- if(!check_user_level(channel,user,lvlSetters,1,0))\r
- {\r
- ss_reply("SSMSG_NO_ACCESS");\r
- return 0;\r
- }\r
-\r
- dict_iterator_t it;\r
- for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {\r
- count++;\r
- }\r
-\r
- tbl.length = count+1;\r
- tbl.width = 4;\r
- tbl.flags = 0;\r
- tbl.flags = TABLE_NO_FREE;\r
- tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));\r
- tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));\r
- tbl.contents[0][0] = "#";\r
- tbl.contents[0][1] = "Badword";\r
- tbl.contents[0][2] = "Action";\r
- tbl.contents[0][3] = "(Triggered)";\r
- if(!count)\r
- {\r
- table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);\r
- reply("MSG_NONE");\r
- free(tbl.contents[0]);\r
- free(tbl.contents);\r
- return 0;\r
- }\r
- badwords = alloca(count * sizeof(badwords[0]));\r
- for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {\r
- struct badword *bw = iter_data(it);\r
- badwords[ii++] = bw;\r
- }\r
-\r
- qsort(badwords, count, sizeof(badwords[0]), ss_badwords_sort);\r
- for (ii = 1; ii <= count; ii++) {\r
- struct badword *bw = badwords[ii-1];\r
- tbl.contents[ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));\r
- tbl.contents[ii][0] = strdup(bw->id);\r
- tbl.contents[ii][1] = strdup(bw->badword_mask);\r
- switch(bw->action) {\r
- case BADACTION_KICK:\r
- tbl.contents[ii][2] = "KICK";\r
- break;\r
- case BADACTION_BAN:\r
- tbl.contents[ii][2] = "BAN";\r
- break;\r
- case BADACTION_KILL:\r
- tbl.contents[ii][2] = "KILL";\r
- break;\r
- case BADACTION_GLINE:\r
- tbl.contents[ii][2] = "GLINE";\r
- break;\r
- default:\r
- tbl.contents[ii][2] = "*undef*";\r
- }\r
- tbl.contents[ii][3] = strtab(bw->triggered);\r
- }\r
-\r
- table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);\r
- for(ii = 1; ii < tbl.length; ++ii)\r
- {\r
- free(tbl.contents[ii]);\r
- }\r
- free(tbl.contents[0]);\r
- free(tbl.contents);\r
- return 1;\r
-}\r
-\r
-static void \r
-to_lower(char *message)\r
-{\r
- unsigned int i, diff = 'a' - 'A';\r
-\r
- for(i = 0; i < strlen(message); i++)\r
- {\r
- if((message[i] >= 'A') && (message[i] <= 'Z'))\r
- message[i] = message[i] + diff;\r
- }\r
-}\r
-\r
-static char *\r
-strip_mirc_codes(char *text)\r
-{\r
- // taken from xchat and modified\r
- int nc = 0, i = 0, col = 0, len = strlen(text);\r
- static char new_str[MAXLEN];\r
-\r
- while(len > 0)\r
- {\r
- if((col && isdigit(*text) && nc < 2) ||\r
- (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))\r
- {\r
- nc++;\r
-\r
- if(*text == ',')\r
- nc = 0;\r
- }\r
- else\r
- {\r
- col = 0;\r
-\r
- switch(*text)\r
- {\r
- case '\003':\r
- col = 1;\r
- nc = 0;\r
- break;\r
- case '\002':\r
- case '\022':\r
- case '\026': \r
- case '\031':\r
- case '\037':\r
- break;\r
- default:\r
- new_str[i] = *text;\r
- i++;\r
- }\r
- }\r
-\r
- text++;\r
- len--;\r
- }\r
-\r
- new_str[i] = '\0';\r
-\r
- return new_str;\r
-}\r
-\r
-static int\r
-is_in_exception_list(struct chanInfo *cInfo, char *message)\r
-{\r
- unsigned int i;\r
-\r
- for(i = 0; i < cInfo->exceptions->used; i++)\r
- if(strstr(message, cInfo->exceptions->list[i]))\r
- return 1;\r
-\r
- return 0;\r
-}\r
-\r
-static int\r
-check_advertising(struct chanInfo *cInfo, char *message)\r
-{\r
- unsigned int i = 0;\r
-\r
- if(spamserv_conf.strip_mirc_codes)\r
- message = strip_mirc_codes(message);\r
-\r
- if(is_in_exception_list(cInfo, message))\r
- return 0;\r
-\r
- while(message[i] != 0)\r
- {\r
- if(message[i] == '#')\r
- {\r
- char channelname[CHANNELLEN];\r
- unsigned int j = 0;\r
-\r
- if(!spamserv_conf.adv_chan_must_exist)\r
- return 1;\r
-\r
- /* only return 1, if the channel does exist */ \r
-\r
- while((message[i] != 0) && (message[i] != ' '))\r
- {\r
- channelname[j] = message[i];\r
- i++;\r
- j++; \r
- }\r
-\r
- channelname[j] = '\0';\r
-\r
- if(GetChannel(channelname))\r
- return 1;\r
- }\r
- else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))\r
- return 1;\r
- else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))\r
- return 1;\r
- else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))\r
- return 1;\r
-\r
- i++;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-struct banData *add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason);\r
-\r
-static void\r
-spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)\r
-{\r
- if(ban)\r
- {\r
- struct mod_chanmode change;\r
- char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);\r
-\r
- sanitize_ircmask(hostmask);\r
-\r
- if(expires)\r
- add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);\r
-\r
- mod_chanmode_init(&change);\r
- change.argc = 1;\r
- change.args[0].mode = MODE_BAN;\r
- change.args[0].u.hostmask = hostmask;\r
- mod_chanmode_announce(spamserv, channel, &change); \r
-\r
- free(hostmask);\r
-\r
- spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);\r
- }\r
- else\r
- spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);\r
-\r
- KickChannelUser(user, channel, spamserv, reason); \r
-}\r
-\r
-static void\r
-spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct badword *badword)\r
-{\r
- char *hostmask;\r
- char *reason = SSMSG_BADWORD_DETECTED;\r
- char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };\r
- switch(badword->action) {\r
- case BADACTION_BAN:\r
- hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);\r
- sanitize_ircmask(hostmask);\r
- if(chan->channel_info) {\r
- //registered channel\r
- add_channel_ban(chan->channel_info, hostmask, spamserv->nick, now, now, now + spamserv_conf.long_ban_duration, reason);\r
- }\r
- struct mod_chanmode change;\r
- mod_chanmode_init(&change);\r
- change.argc = 1;\r
- change.args[0].mode = MODE_BAN;\r
- change.args[0].u.hostmask = hostmask;\r
- mod_chanmode_announce(spamserv, chan, &change);\r
- free(hostmask);\r
-\r
- case BADACTION_KICK:\r
- if(GetUserMode(chan, user))\r
- KickChannelUser(user, chan, spamserv, reason);\r
- break;\r
- case BADACTION_KILL:\r
- DelUser(user, spamserv, 1, reason);\r
- break;\r
- case BADACTION_GLINE:\r
- irc_ntop(mask + 2, sizeof(mask) - 2, &user->ip);\r
- gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);\r
- break;\r
- default:\r
- //error?\r
- break;\r
- }\r
-}\r
-\r
-void\r
-spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)\r
-{\r
- struct chanInfo *cInfo;\r
- struct userInfo *uInfo;\r
- struct spamNode *sNode;\r
- struct floodNode *fNode;\r
- unsigned int violation = 0;\r
- char reason[MAXLEN];\r
-\r
- /* make sure: spamserv is not disabled; srvx is running; spamserv is in the chan; chan is regged, user does exist */\r
- if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick)))\r
- return;\r
-\r
- if(!CHECK_CHANOPS(cInfo))\r
- {\r
- struct modeNode *mn = GetUserMode(channel, user);\r
- if(mn && mn->modes & MODE_CHANOP)\r
- return;\r
- //if(check_user_level(channel, user, lvlGiveOps, 1, 0)) \r
- // return;\r
- }\r
- \r
- if(!CHECK_VOICED(cInfo))\r
- {\r
- struct modeNode *mn = GetUserMode(channel, user);\r
- if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))\r
- return;\r
- }\r
- \r
- if(cInfo->exceptlevel == 0)\r
- return;\r
- if(cInfo->exceptlevel < 501) {\r
- struct userData *uData;\r
- if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {\r
- return;\r
- }\r
- }\r
-\r
- to_lower(text);\r
-\r
- if(CHECK_SPAM(cInfo))\r
- {\r
- if(!(sNode = uInfo->spam))\r
- {\r
- spamserv_create_spamNode(channel, uInfo, text);\r
- }\r
- else\r
- {\r
- for(; sNode; sNode = sNode->next)\r
- if(sNode->channel == channel)\r
- break;\r
-\r
- if(!sNode)\r
- {\r
- spamserv_create_spamNode(channel, uInfo, text);\r
- }\r
- else\r
- {\r
- unsigned long crc = crc32(text);\r
-\r
- if(crc == sNode->crc32)\r
- {\r
- unsigned int spamlimit = 2;\r
- sNode->count++;\r
-\r
- switch(cInfo->info[ci_SpamLimit])\r
- {\r
- case 'a': spamlimit = 2; break;\r
- case 'b': spamlimit = 3; break;\r
- case 'c': spamlimit = 4; break;\r
- case 'd': spamlimit = 5; break;\r
- case 'e': spamlimit = 6; break;\r
- }\r
-\r
- if(sNode->count == spamlimit)\r
- {\r
- uInfo->warnlevel += SPAM_WARNLEVEL;\r
-\r
- if(uInfo->warnlevel < MAX_WARNLEVEL)\r
- spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules);\r
- }\r
- else if(sNode->count > spamlimit)\r
- {\r
- switch(cInfo->info[ci_WarnReaction])\r
- {\r
- case 'k': uInfo->flags |= USER_KICK; break;\r
- case 'b': uInfo->flags |= USER_KICKBAN; break;\r
- case 's': uInfo->flags |= USER_SHORT_TBAN; break;\r
- case 'l': uInfo->flags |= USER_LONG_TBAN; break;\r
- case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;\r
- }\r
-\r
- spamserv_delete_spamNode(uInfo, sNode);\r
- uInfo->warnlevel += SPAM_WARNLEVEL;\r
- violation = 1;\r
- }\r
- }\r
- else\r
- {\r
- sNode->crc32 = crc; \r
- sNode->count = 1;\r
- }\r
- }\r
- }\r
- }\r
-\r
- if(CHECK_FLOOD(cInfo))\r
- {\r
- if(!(fNode = uInfo->flood))\r
- {\r
- spamserv_create_floodNode(channel, user, &uInfo->flood);\r
- }\r
- else\r
- {\r
- for(; fNode; fNode = fNode->next)\r
- if(fNode->channel == channel)\r
- break;\r
- \r
- if(!fNode)\r
- {\r
- spamserv_create_floodNode(channel, user, &uInfo->flood);\r
- }\r
- else\r
- {\r
- if(((now - fNode->time) < FLOOD_EXPIRE))\r
- {\r
- fNode->count++;\r
- \r
- if(fNode->count == FLOOD_MAX_LINES - 1)\r
- {\r
- uInfo->warnlevel += FLOOD_WARNLEVEL;\r
-\r
- if(uInfo->warnlevel < MAX_WARNLEVEL)\r
- spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules);\r
- }\r
- else if(fNode->count > FLOOD_MAX_LINES)\r
- {\r
- switch(cInfo->info[ci_WarnReaction])\r
- {\r
- case 'k': uInfo->flags |= USER_KICK; break;\r
- case 'b': uInfo->flags |= USER_KICKBAN; break;\r
- case 's': uInfo->flags |= USER_SHORT_TBAN; break;\r
- case 'l': uInfo->flags |= USER_LONG_TBAN; break;\r
- case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;\r
- }\r
-\r
- spamserv_delete_floodNode(&uInfo->flood, fNode);\r
- uInfo->warnlevel += FLOOD_WARNLEVEL;\r
- violation = 2; \r
- }\r
- }\r
-\r
- fNode->time = now;\r
- }\r
- }\r
- }\r
-\r
- dict_iterator_t it;\r
-\r
- for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {\r
- struct badword *badword = iter_data(it);\r
- if(match_ircglob(text, badword->badword_mask)) {\r
- spamserv_detected_badword(user, channel, badword);\r
- }\r
- }\r
-\r
- if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))\r
- {\r
- if(CHECK_ADV_WARNED(uInfo))\r
- {\r
- switch(cInfo->info[ci_AdvReaction])\r
- {\r
- case 'k': uInfo->flags |= USER_KICK; break;\r
- case 'b': uInfo->flags |= USER_KICKBAN; break;\r
- case 's': uInfo->flags |= USER_SHORT_TBAN; break;\r
- case 'l': uInfo->flags |= USER_LONG_TBAN; break;\r
- case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;\r
- }\r
-\r
- uInfo->warnlevel += ADV_WARNLEVEL;\r
- violation = 3;\r
- }\r
- else\r
- { \r
- uInfo->flags |= USER_ADV_WARNED;\r
- uInfo->lastadv = now;\r
- uInfo->warnlevel += ADV_WARNLEVEL;\r
-\r
- if(uInfo->warnlevel < MAX_WARNLEVEL)\r
- spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules);\r
- } \r
- }\r
-\r
- if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)\r
- {\r
- uInfo->flags |= USER_WARNED;\r
- snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules);\r
- irc_notice(spamserv, user->numeric, reason);\r
- irc_privmsg(spamserv, user->numeric, reason);\r
- }\r
- else if(uInfo->warnlevel > MAX_WARNLEVEL)\r
- {\r
- if(CHECK_KILLED(uInfo))\r
- uInfo->flags |= USER_GLINE;\r
- else\r
- uInfo->flags |= USER_KILL;\r
-\r
- violation = 5;\r
- }\r
-\r
- if(!violation)\r
- return;\r
-\r
- switch(violation)\r
- {\r
- case 1: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules); break;\r
- case 2: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules); break;\r
- case 3: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules); break;\r
- default: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules); break;\r
- }\r
-\r
- if(CHECK_GLINE(uInfo))\r
- {\r
- int size = strlen(user->hostname) + 3;\r
- char *mask = alloca(size);\r
- snprintf(mask, size, "*@%s", user->hostname);\r
- gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);\r
- spamserv_debug(SSMSG_DEBUG_GLINE, user->nick, user->hostname, channel->name);\r
- }\r
- else if(CHECK_KILL(uInfo))\r
- {\r
- DelUser(user, spamserv, 1, reason);\r
- spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);\r
- }\r
- else if(CHECK_LONG_TBAN(uInfo))\r
- {\r
- spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);\r
- }\r
- else if(CHECK_SHORT_TBAN(uInfo))\r
- {\r
- spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);\r
- }\r
- else if(CHECK_KICKBAN(uInfo))\r
- {\r
- spamserv_punish(channel, user, 0, reason, 1);\r
- }\r
- else if(CHECK_KICK(uInfo))\r
- {\r
- spamserv_punish(channel, user, 0, reason, 0);\r
- }\r
-}\r
-\r
-static int\r
-spamserv_saxdb_read_shitlist(const char *name, void *data, void *extra)\r
-{\r
- struct record_data *rd = data;\r
- struct chanInfo *chan = extra;\r
- char *badword;\r
- char *triggered, *action;\r
-\r
- if (rd->type == RECDB_OBJECT) {\r
- dict_t obj = GET_RECORD_OBJECT(rd);\r
- /* new style structure */\r
- badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);\r
- triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);\r
- action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);\r
-\r
- add_badword(chan, badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);\r
- }\r
- return 0;\r
-}\r
-\r
-static int\r
-spamserv_saxdb_read(struct dict *database)\r
-{\r
- dict_iterator_t it;\r
- struct dict *badwords;\r
- struct record_data *hir;\r
- struct chanNode *channel;\r
- struct chanInfo *cInfo;\r
- struct string_list *strlist;\r
- unsigned int flags,exceptlevel,badwordid;\r
- char *str, *info;\r
- unsigned long expiry; \r
-\r
- for(it = dict_first(database); it; it = iter_next(it))\r
- {\r
- hir = iter_data(it);\r
-\r
- if(hir->type != RECDB_OBJECT)\r
- {\r
- log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));\r
- continue;\r
- }\r
-\r
- channel = GetChannel(iter_key(it));\r
- strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);\r
-\r
- str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);\r
- flags = str ? atoi(str) : 0;\r
-\r
- info = database_get_data(hir->d.object, KEY_INFO, RECDB_QSTRING);\r
- str = database_get_data(hir->d.object, KEY_EXCEPTLEVEL, RECDB_QSTRING);\r
- exceptlevel = str ? atoi(str) : 400;\r
- \r
- str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);\r
- expiry = str ? strtoul(str, NULL, 0) : 0;\r
-\r
- if(channel && info)\r
- {\r
- if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))\r
- {\r
- /* if the channel is suspended and expiry = 0 it means: channel will\r
- never expire ! it does NOT mean, the channel is not suspended */\r
- if(CHECK_SUSPENDED(cInfo) && expiry && (expiry < now))\r
- {\r
- cInfo->flags &= ~CHAN_SUSPENDED;\r
- spamserv_join_channel(cInfo->channel);\r
- }\r
- else if(!CHECK_SUSPENDED(cInfo))\r
- spamserv_join_channel(cInfo->channel);\r
- else\r
- cInfo->suspend_expiry = expiry; \r
- cInfo->exceptlevel=exceptlevel;\r
- cInfo->badwords = dict_new();\r
- str = database_get_data(hir->d.object, KEY_LASTBADWORDID, RECDB_QSTRING);\r
- badwordid = str ? atoi(str) : 0;\r
- cInfo->last_badword_id = badwordid;\r
- if ((badwords = database_get_data(hir->d.object, KEY_BADWORDS, RECDB_OBJECT)))\r
- dict_foreach(badwords, spamserv_saxdb_read_shitlist, cInfo);\r
- }\r
- }\r
- else\r
- log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it)); \r
- }\r
-\r
- return 0;\r
-}\r
-\r
-static int\r
-spamserv_saxdb_write(struct saxdb_context *ctx)\r
-{\r
- dict_iterator_t it;\r
-\r
- for(it = dict_first(registered_channels_dict); it; it = iter_next(it))\r
- {\r
- struct chanInfo *cInfo = iter_data(it);\r
-\r
- saxdb_start_record(ctx, cInfo->channel->name, 1);\r
-\r
- if(cInfo->exceptions->used)\r
- saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);\r
-\r
- if(cInfo->flags)\r
- saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags); \r
-\r
- saxdb_write_string(ctx, KEY_INFO, cInfo->info); \r
- saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel); \r
-\r
- if(cInfo->suspend_expiry)\r
- saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);\r
-\r
- saxdb_write_int(ctx, KEY_LASTBADWORDID, cInfo->last_badword_id);\r
- saxdb_start_record(ctx, KEY_BADWORDS, 1);\r
- dict_iterator_t itbad;\r
- for (itbad = dict_first(cInfo->badwords); itbad; itbad = iter_next(itbad)) {\r
- struct badword *badword = iter_data(itbad);\r
- saxdb_start_record(ctx, badword->id, 1);\r
- saxdb_write_string(ctx, KEY_BADWORDID, badword->id);\r
- saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask);\r
- saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action);\r
- saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered);\r
- saxdb_end_record(ctx);\r
- }\r
- saxdb_end_record(ctx);\r
- saxdb_end_record(ctx); \r
- }\r
- return 0;\r
-}\r
-\r
-static void\r
-spamserv_conf_read(void)\r
-{\r
- dict_t conf_node;\r
- const char *str; \r
-\r
- if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))\r
- {\r
- log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);\r
- return;\r
- }\r
-\r
- str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);\r
-\r
- if(str)\r
- {\r
- spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);\r
-\r
- if(spamserv_conf.debug_channel)\r
- spamserv_join_channel(spamserv_conf.debug_channel);\r
- }\r
- else\r
- {\r
- spamserv_conf.debug_channel = NULL;\r
- }\r
-\r
- spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);\r
-\r
- str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);\r
- spamserv_conf.network_rules = str ? str : NULL;\r
-\r
- str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);\r
- spamserv_conf.trigger = str ? str[0] : 0;\r
-\r
- str = database_get_data(conf_node, KEY_SHORT_BAN_DURATION, RECDB_QSTRING);\r
- spamserv_conf.short_ban_duration = str ? ParseInterval(str) : ParseInterval("15m");\r
-\r
- str = database_get_data(conf_node, KEY_LONG_BAN_DURATION, RECDB_QSTRING);\r
- spamserv_conf.long_ban_duration = str ? ParseInterval(str) : ParseInterval("1h");\r
-\r
- str = database_get_data(conf_node, KEY_GLINE_DURATION, RECDB_QSTRING);\r
- spamserv_conf.gline_duration = str ? ParseInterval(str) : ParseInterval("1h");\r
-\r
- str = database_get_data(conf_node, KEY_EXCEPTION_MAX, RECDB_QSTRING);\r
- spamserv_conf.exception_max = str ? strtoul(str, NULL, 0) : 10;\r
-\r
- str = database_get_data(conf_node, KEY_EXCEPTION_MIN_LEN, RECDB_QSTRING);\r
- spamserv_conf.exception_min_len = str ? strtoul(str, NULL, 0) : 4;\r
-\r
- str = database_get_data(conf_node, KEY_EXCEPTION_MAX_LEN, RECDB_QSTRING);\r
- spamserv_conf.exception_max_len = str ? strtoul(str, NULL, 0) : 15;\r
-\r
- str = database_get_data(conf_node, KEY_ADV_CHAN_MUST_EXIST, RECDB_QSTRING);\r
- spamserv_conf.adv_chan_must_exist = str ? enabled_string(str) : 1;\r
-\r
- str = database_get_data(conf_node, KEY_STRIP_MIRC_CODES, RECDB_QSTRING);\r
- spamserv_conf.strip_mirc_codes = str ? enabled_string(str) : 0;\r
-\r
- str = database_get_data(conf_node, KEY_ALLOW_MOVE_MERGE, RECDB_QSTRING);\r
- spamserv_conf.allow_move_merge = str ? enabled_string(str) : 0;\r
-}\r
-\r
-static void\r
-spamserv_db_cleanup(void)\r
-{\r
- dict_iterator_t it;\r
-\r
- while((it = dict_first(registered_channels_dict)))\r
- {\r
- spamserv_unregister_channel(iter_data(it));\r
- }\r
-\r
- while((it = dict_first(killed_users_dict)))\r
- {\r
- free(iter_data(it));\r
- }\r
- \r
- dict_delete(registered_channels_dict);\r
- dict_delete(connected_users_dict);\r
- dict_delete(killed_users_dict);\r
-}\r
-\r
-void\r
-init_spamserv(const char *nick)\r
-{\r
- if(!nick)\r
- return;\r
-\r
- const char *modes = conf_get_data("services/spamserv/modes", RECDB_QSTRING); \r
- spamserv = AddLocalUser(nick, nick, NULL, "Anti Spam Services", modes);\r
- spamserv_service = service_register(spamserv);\r
- service_register(spamserv)->trigger = spamserv_conf.trigger;\r
-\r
- conf_register_reload(spamserv_conf_read);\r
-\r
- SS_LOG = log_register_type("SpamServ", "file:spamserv.log"); \r
-\r
- registered_channels_dict = dict_new();\r
- connected_users_dict = dict_new();\r
- killed_users_dict = dict_new();\r
-\r
- reg_new_user_func(spamserv_new_user_func);\r
- reg_del_user_func(spamserv_del_user_func);\r
- reg_nick_change_func(spamserv_nick_change_func);\r
- reg_join_func(spamserv_user_join);\r
- reg_part_func(spamserv_user_part);\r
-\r
- timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);\r
- timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);\r
- timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);\r
- timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);\r
- timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);\r
-\r
- spamserv_module = module_register("SpamServ", SS_LOG, "spamserv.help", NULL);\r
- modcmd_register(spamserv_module, "REGISTER", cmd_register, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+acceptchan,+helping", NULL);\r
- modcmd_register(spamserv_module, "UNREGISTER", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+loghostmask", NULL);\r
- modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "DELEXCEPTION", cmd_delexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL);\r
- modcmd_register(spamserv_module, "ADDBAD", cmd_addbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "DELBAD", cmd_delbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SETBAD", cmd_setbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "LISTBAD", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET SPAMLIMIT", opt_spamlimit, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET WARNREACTION", opt_warnreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET ADVSCAN", opt_advscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET SPAMSCAN", opt_spamscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET FLOODSCAN", opt_floodscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET JOINFLOODSCAN", opt_joinflood, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET SCANCHANOPS", opt_scanops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET SCANVOICED", opt_scanvoiced, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
- modcmd_register(spamserv_module, "SET EXCEPTLEVEL", opt_exceptlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);\r
-\r
- saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write);\r
- reg_exit_func(spamserv_db_cleanup);\r
- message_register_table(msgtab);\r
- crc32_init();\r
-}\r
-=======
/* spamserv.c - anti spam service
* Copyright 2004 feigling
*
{ "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 }
};
#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."
}
}
+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 */
/***********************************************/
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; 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)
+{
+ 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)
{
}
}
}
+
+ 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))
{
}
}
+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))
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
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;
message_register_table(msgtab);
crc32_init();
}
->>>>>>> master