added cmd_delbad
authorNurPech <nurpech@nurpech.de>
Mon, 20 May 2013 10:14:59 +0000 (12:14 +0200)
committerNurPech <nurpech@nurpech.de>
Mon, 20 May 2013 10:17:58 +0000 (12:17 +0200)
src/spamserv.c

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