restricted kill and gline command from SpamServ
[srvx.git] / src / spamserv.c
1 /* spamserv.c - anti spam service
2  * Copyright 2004 feigling
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.  Important limitations are
8  * listed in the COPYING file that accompanies this software.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, email srvx-maintainers@srvx.net.
17  *
18  * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $
19  */
20
21 #include "conf.h"
22 #include "spamserv.h"
23 #include "chanserv.h"
24 #include "global.h"
25 #include "modcmd.h"
26 #include "saxdb.h"
27 #include "timeq.h"
28 #include "gline.h"
29
30 #define SPAMSERV_CONF_NAME           "services/spamserv"
31
32 #define KEY_EXCEPTIONS               "exceptions"
33 #define KEY_FLAGS                    "flags"
34 #define KEY_INFO                     "info"
35 #define KEY_EXCEPTLEVEL              "exceptlevel"
36 #define KEY_EXPIRY                   "expiry"
37 #define KEY_LASTBADWORDID            "last_badword_id"
38 #define KEY_BADWORDS                 "badwords"
39
40 #define KEY_BADWORD_MASK             "mask"
41 #define KEY_BADWORD_TRIGGERED        "count"
42 #define KEY_BADWORD_ACTION           "action"
43 #define KEY_BADWORDID                "badwordid"
44
45 #define KEY_DEBUG_CHANNEL            "debug_channel"
46 #define KEY_OPER_CHANNEL            "oper_channel"
47 #define KEY_GLOBAL_EXCEPTIONS        "global_exceptions"
48 #define KEY_NETWORK_RULES            "network_rules"
49 #define KEY_TRIGGER                  "trigger"
50 #define KEY_SHORT_BAN_DURATION       "short_ban_duration"
51 #define KEY_LONG_BAN_DURATION        "long_ban_duration"
52 #define KEY_GLINE_DURATION           "gline_duration"
53 #define KEY_EXCEPTION_MAX            "exception_max"
54 #define KEY_EXCEPTION_MIN_LEN        "exception_min_len"
55 #define KEY_EXCEPTION_MAX_LEN        "exception_max_len"
56 #define KEY_ADV_CHAN_MUST_EXIST      "adv_chan_must_exist"
57 #define KEY_STRIP_MIRC_CODES         "strip_mirc_codes"
58 #define KEY_ALLOW_MOVE_MERGE         "allow_move_merge"
59
60 #define SPAMSERV_FUNC(NAME)     MODCMD_FUNC(NAME)
61 #define SPAMSERV_SYNTAX()       svccmd_send_help(user, spamserv, cmd)
62 #define SPAMSERV_MIN_PARMS(N) do { \
63 (void)argv; \
64   if(argc < N) { \
65     ss_reply(MSG_MISSING_PARAMS, argv[0]); \
66     SPAMSERV_SYNTAX(); \
67     return 0; } } while(0)
68
69 struct userNode                 *spamserv;
70 static struct module    *spamserv_module;
71 static struct service   *spamserv_service;
72 static struct log_type  *SS_LOG;
73 static unsigned long    crc_table[256];
74
75 dict_t registered_channels_dict;
76 dict_t connected_users_dict;
77 dict_t killed_users_dict;
78
79 #define spamserv_notice(target, format...) send_message(target , spamserv , ## format)
80 #define spamserv_debug(format...) do { if(spamserv_conf.debug_channel) send_channel_message(spamserv_conf.debug_channel , spamserv , ## format); } while(0)
81 #define spamserv_oper_message(format...) do { if(spamserv_conf.oper_channel) send_channel_message(spamserv_conf.oper_channel , spamserv , ## format); } while(0)
82 #define ss_reply(format...)     send_message(user , spamserv , ## format)
83
84 #define SET_SUBCMDS_SIZE 10
85
86 const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};
87
88 static void spamserv_clear_spamNodes(struct chanNode *channel);
89 static void spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban);
90 static unsigned long crc32(const char *text);
91
92 #define BINARY_OPTION(arguments...)     return binary_option(arguments, user, channel, argc, argv);
93 #define MULTIPLE_OPTION(arguments...)   return multiple_option(arguments, values, ArrayLength(values), user, channel, argc, argv);
94
95 static const struct message_entry msgtab[] = {
96     { "SSMSG_CHANNEL_OPTIONS",         "Channel Options:" },
97     { "SSMSG_STRING_VALUE",            "$b%s$b%s" },
98     { "SSMSG_NUMERIC_VALUE",           "$b%s$b%d - %s" },
99     { "SSMSG_EASYNUMERIC_VALUE",       "$b%s$b%d" },
100     { "SSMSG_INVALID_NUM_SET",         "$b'%d'$b is an invalid %s setting." },
101     { "SSMSG_INVALID_OPTION",          "$b%s$b is not a valid %s option." },
102     { "SSMSG_INVALID_BINARY",          "$b%s$b is an invalid binary value." },
103
104     { "SSMSG_NOT_REGISTERED",          "$b%s$b has not been registered with $b$X$b." },
105     { "SSMSG_NOT_REGISTERED_CS",       "$b%s$b has not been registered with $b$C$b." },
106     { "SSMSG_ALREADY_REGISTERED",      "$b%s$b is already registered." },
107     { "SSMSG_DEBUG_CHAN",              "You may not register the debug channel." },
108     { "SSMSG_SUSPENDED_CS",            "$b$C$b access to $b%s$b has been temporarily suspended, thus you can't %s it." },
109     { "SSMSG_SUSPENDED",               "$b$X$b access to $b%s$b has been temporarily suspended." },
110     { "SSMSG_NO_REGISTER",             "Due to an error it was not possible to register $b%s$b." },
111     { "SSMSG_REG_SUCCESS",             "Channel $b%s$b registered." },
112     { "SSMSG_UNREG_SUCCESS",           "$b%s$b has been unregistered." },
113     { "SSMSG_NO_ACCESS",               "You lack sufficient access to use this command." },
114     { "SSMSG_MUST_BE_OPER",            "You must be an irc operator to set this option." },
115     { "SSMSG_CONFIRM_UNREG",           "To confirm this unregistration, you must append 'CONFIRM' to the end of your command. For example, 'unregister CONFIRM'." },
116
117     { "SSMSG_NO_EXCEPTIONS",           "No words found in the exception list." },
118     { "SSMSG_NO_SUCH_EXCEPTION",       "Word $b%s$b not found in the exception list." },
119     { "SSMSG_EXCEPTION_LIST",          "The following words are in the exception list:" },
120     { "SSMSG_EXCEPTION_ADDED",         "Word $b%s$b added to the exception list." },
121     { "SSMSG_EXCEPTION_DELETED",       "Word $b%s$b deleted from the exception list." },
122     { "SSMSG_EXCEPTION_IN_LIST",       "The word $b%s$b is already in the exception list." },
123     { "SSMSG_EXCEPTION_MAX",           "The exception list has reached the maximum exceptions (max %lu). Delete a word to add another one." },
124     { "SSMSG_EXCEPTION_TOO_SHORT",     "The word must be at least %lu characters long." },
125     { "SSMSG_EXCEPTION_TOO_LONG",      "The word may not be longer than %lu characters." },
126
127     { "SSMSG_STATUS",                  "$bStatus:$b" },
128     { "SSMSG_STATUS_USERS",            "Total Users Online:  %u" },
129     { "SSMSG_STATUS_CHANNELS",         "Registered Channels: %u" },
130     { "SSMSG_STATUS_MEMORY",           "$bMemory Information:$b" },
131     { "SSMSG_STATUS_CHANNEL_LIST",     "$bRegistered Channels:$b" },
132     { "SSMSG_STATUS_NO_CHANNEL",       "No channels registered." },
133     
134     { "SSMSG_BADWORD_ALREADY_ADDED",   "$b%s$b is already added. (ID: %s)" },
135     { "SSMSG_BADWORD_ADDED",           "added '$b%s$b' to the badword list with ID %s." },
136     { "SSMSG_BADWORD_SET_DONE",        "Done." },
137     { "SSMSG_BADWORD_SET_INVALID",     "Invalid Option for setting %s" },
138     { "SSMSG_BADWORD_SET",             "Settings for BadWord entry $b%s$b" },
139     { "SSMSG_BADWORD_SET_MASK",        "$bMASK$b:   %s" },
140     { "SSMSG_BADWORD_SET_ACTION",      "$bACTION$b: %s" },
141     { "SSMSG_BADWORD_NOT_FOUND",       "badword with ID %s does not exist." },
142     { "SSMSG_BADWORD_REMOVED",         "badword ID $b%s$b has been removed (mask: '%s')" },
143         { NULL, NULL }
144 };
145
146 #define SSMSG_DEBUG_KICK              "Kicked user $b%s$b from $b%s$b, reason: %s"
147 #define SSMSG_DEBUG_BAN               "Banned user $b%s$b from $b%s$b, reason: %s"
148 #define SSMSG_DEBUG_KILL              "Killed user $b%s$b, last violation in $b%s$b"
149 #define SSMSG_DEBUG_GLINE             "Glined user $b%s$b, host $b%s$b, last violation in $b%s$b"
150 #define SSMSG_DEBUG_RECONNECT         "Killed user $b%s$b reconnected to the network"
151 #define SSMSG_SPAM                    "Spamming"
152 #define SSMSG_FLOOD                   "Flooding the channel/network"
153 #define SSMSG_ADV                     "Advertising"
154 #define SSMSG_JOINFLOOD               "Join flooding the channel"
155 #define SSMSG_WARNING                 "%s is against the network rules"
156 #define SSMSG_WARNING_2               "You are violating the network rules"
157 #define SSMSG_WARNING_RULES           "%s is against the network rules. Read the network rules at %s"
158 #define SSMSG_WARNING_RULES_2         "You are violating the network rules. Read the network rules at %s"
159 #define SSMSG_BADWORD_DETECTED            "Your message contained a forbidden word."
160 #define SSMSG_CHANNEL_REGISTERED      "%s (channel %s) registered by %s."
161 #define SSMSG_CHANNEL_UNREGISTERED    "%s (channel %s) unregistered by %s."
162
163 static struct
164 {
165         struct chanNode *debug_channel;
166         struct chanNode *oper_channel;
167         struct string_list *global_exceptions;
168         const char *network_rules;
169         unsigned char trigger;
170         unsigned long short_ban_duration;
171         unsigned long long_ban_duration;
172         unsigned long gline_duration;
173         unsigned long exception_max;
174         unsigned long exception_min_len;
175         unsigned long exception_max_len;
176         unsigned int adv_chan_must_exist : 1;
177         unsigned int strip_mirc_codes : 1;
178         unsigned int allow_move_merge : 1;
179 } spamserv_conf;
180
181 /***********************************************/
182 /*                   Channel                   */
183 /***********************************************/
184
185 struct chanInfo*
186 get_chanInfo(const char *channelname)
187 {
188         return dict_find(registered_channels_dict, channelname, 0);
189 }
190
191 static void
192 spamserv_join_channel(struct chanNode *channel)
193 {
194         struct mod_chanmode change;
195         mod_chanmode_init(&change);
196         change.argc = 1;
197         change.args[0].mode = MODE_CHANOP;
198         change.args[0].u.member = AddChannelUser(spamserv, channel);
199         mod_chanmode_announce(spamserv, channel, &change);
200 }
201
202 static void
203 spamserv_part_channel(struct chanNode *channel, char *reason)
204 {
205         /* we only have to clear the spamNodes because every other node expires on it's own */
206         spamserv_clear_spamNodes(channel);
207         DelChannelUser(spamserv, channel, reason, 0);
208 }
209
210 static struct chanInfo*
211 spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)
212 {
213         struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));
214         
215         if(!cInfo)
216         {
217                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);
218                 return NULL;
219         }
220
221         cInfo->channel = channel;
222         cInfo->exceptions = exceptions ? string_list_copy(exceptions) : alloc_string_list(1);
223         cInfo->flags = flags;
224     cInfo->exceptlevel = 400;
225         safestrncpy(cInfo->info, info, sizeof(cInfo->info));
226         cInfo->suspend_expiry = 0;
227         dict_insert(registered_channels_dict, cInfo->channel->name, cInfo);
228
229         return cInfo;
230 }
231
232 static void
233 spamserv_unregister_channel(struct chanInfo *cInfo)
234 {
235         if(!cInfo)
236                 return;
237
238         dict_remove(registered_channels_dict, cInfo->channel->name);
239         free_string_list(cInfo->exceptions);
240         free(cInfo);
241 }
242
243 void
244 spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)
245 {
246         struct chanInfo *cInfo = get_chanInfo(channel->name);
247
248         if(cInfo)
249         {
250                 if(suspend)
251                 {
252                         cInfo->flags |= CHAN_SUSPENDED;
253                         cInfo->suspend_expiry = expiry;
254                         spamserv_part_channel(channel, reason);
255                 }
256                 else
257                 {
258                         if(CHECK_SUSPENDED(cInfo))
259                         {
260                                 cInfo->flags &= ~CHAN_SUSPENDED;
261                                 cInfo->suspend_expiry = 0;
262                         }
263                 }
264         }
265 }
266
267 int
268 spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)
269 {
270         struct chanInfo *cInfo = get_chanInfo(channel->name);
271
272         if(cInfo)
273         {
274                 char reason[MAXLEN];
275
276                 if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))
277                 {
278                         if(move)
279                                 snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);
280                         else
281                                 snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);
282
283                         spamserv_cs_unregister(user, channel, manually, reason);
284                         return 0;
285                 }
286
287                 cInfo->channel = target;
288
289                 dict_remove(registered_channels_dict, channel->name);
290                 dict_insert(registered_channels_dict, target->name, cInfo);
291
292                 if(move)
293                 {
294                         snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);
295                 }
296                 else
297                 {
298                         spamserv_join_channel(target);
299                         snprintf(reason, sizeof(reason), "%s merged into %s by %s.", channel->name, target->name, user->handle_info->handle);   
300                 }
301
302                 if(!CHECK_SUSPENDED(cInfo))
303                         spamserv_part_channel(channel, reason);
304
305                 if(move)
306                         snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
307                 else
308                         snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
309
310                 /*global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);*/
311                 spamserv_oper_message("%s", reason);
312                 return 1;
313         }
314
315         return 0;
316 }
317
318 void
319 spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)
320 {
321         struct chanInfo *cInfo = get_chanInfo(channel->name);
322
323         if(cInfo)
324         {
325                 char global[MAXLEN], partmsg[MAXLEN];
326
327                 switch (type)
328                 {
329                 case manually:
330                         snprintf(global, sizeof(global), "$X (channel %s) %s by %s.", channel->name, reason, user->handle_info->handle);
331                         snprintf(partmsg, sizeof(partmsg), "%s %s by %s.", channel->name, reason, user->handle_info->handle);                   
332                         break;
333                 case expire:
334                         snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name);
335                         snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name);                  
336                         break;
337                 case lost_all_users:
338                         snprintf(global, sizeof(global), "$X (channel %s) lost all users.", channel->name);
339                         snprintf(partmsg, sizeof(partmsg), "%s lost all users.", channel->name);                        
340                         break;
341                 }
342
343                 if(!CHECK_SUSPENDED(cInfo))
344                         spamserv_part_channel(channel, partmsg);
345                 
346                 spamserv_unregister_channel(cInfo);
347                 spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
348         }
349 }
350
351 /***********************************************/
352 /*                    User                     */
353 /***********************************************/
354
355 static struct userInfo*
356 get_userInfo(const char *nickname)
357 {
358         return dict_find(connected_users_dict, nickname, 0);
359 }
360
361 static void
362 spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)
363 {
364         struct spamNode *sNode = malloc(sizeof(struct spamNode));
365
366         if(!sNode)
367         {
368                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);
369                 return;
370         }
371
372         sNode->channel = channel;       
373         sNode->crc32 = crc32(text);
374         sNode->count = 1;
375         sNode->next = NULL;
376
377         if(uInfo->spam)
378         {
379                 struct spamNode *temp = uInfo->spam;
380                 
381                 while(temp->next)
382                         temp = temp->next;
383
384                 sNode->prev = temp;
385                 temp->next = sNode;
386         }
387         else
388         {
389                 sNode->prev = NULL;
390                 uInfo->spam = sNode;
391         }
392 }
393
394 static void
395 spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)
396 {
397         if(!sNode)
398                 return;
399
400         if(sNode == uInfo->spam)
401                 uInfo->spam = sNode->next;
402         
403         if(sNode->next)
404                  sNode->next->prev = sNode->prev;
405         if(sNode->prev)
406                  sNode->prev->next = sNode->next;
407
408         free(sNode);
409 }
410
411 static void
412 spamserv_clear_spamNodes(struct chanNode *channel)
413 {
414         struct userInfo *uInfo;
415         struct spamNode *sNode;
416         unsigned int i;
417
418         for(i = 0; i < channel->members.used; i++)
419         {
420                 if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))
421                 {
422                         if((sNode = uInfo->spam))
423                         {
424                                 for(; sNode; sNode = sNode->next)
425                                         if(sNode->channel == channel)
426                                                 break;
427                                         
428                                 if(sNode)
429                                         spamserv_delete_spamNode(uInfo, sNode);
430                         }
431                 }
432         }
433 }
434
435 static void
436 spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)
437 {
438         struct floodNode *fNode = malloc(sizeof(struct floodNode));
439
440         if(!fNode)
441         {
442                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);
443                 return;
444         }
445
446         fNode->channel = channel;
447         fNode->owner = user;
448         fNode->count = 1;
449         fNode->time = now;      
450         fNode->next = NULL;
451
452         if(*uI_fNode)
453         {
454                 struct floodNode *temp = *uI_fNode;
455                 
456                 while(temp->next)
457                         temp = temp->next;
458                 
459                 fNode->prev = temp;
460                 temp->next = fNode;
461         }
462         else
463         {
464                 fNode->prev = NULL;
465                 *uI_fNode = fNode;
466         }
467 }
468
469 static void
470 spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)
471 {
472         if(!fNode)
473                 return;
474
475         if(fNode == *uI_fNode)
476                 *uI_fNode = fNode->next;
477         
478         if(fNode->next)
479                  fNode->next->prev = fNode->prev;
480         if(fNode->prev)
481                  fNode->prev->next = fNode->next;
482
483         free(fNode);
484 }
485
486 static void
487 spamserv_create_user(struct userNode *user)
488 {
489         struct userInfo *uInfo = malloc(sizeof(struct userInfo));
490         struct killNode *kNode = dict_find(killed_users_dict, irc_ntoa(&user->ip), 0);
491
492         if(!uInfo)
493         {
494                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);
495                 return;
496         }
497
498         if(kNode)
499                 spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);
500
501         uInfo->user = user;
502         uInfo->spam = NULL;
503         uInfo->flood = NULL;
504         uInfo->joinflood = NULL;
505         uInfo->flags = kNode ? USER_KILLED : 0;
506         uInfo->warnlevel = kNode ? kNode->warnlevel : 0;
507         uInfo->lastadv = 0;
508
509         dict_insert(connected_users_dict, user->nick, uInfo);
510
511         if(kNode)
512         {
513                 dict_remove(killed_users_dict, irc_ntoa(&user->ip));
514                 free(kNode);
515         }
516 }
517
518 static void
519 spamserv_delete_user(struct userInfo *uInfo)
520 {
521         if(!uInfo)
522                 return;
523
524         if(uInfo->spam)
525                 while(uInfo->spam)
526                         spamserv_delete_spamNode(uInfo, uInfo->spam);   
527
528         if(uInfo->flood)
529                 while(uInfo->flood)
530                         spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);
531
532         if(uInfo->joinflood)
533                 while(uInfo->joinflood)
534                         spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);
535
536         dict_remove(connected_users_dict, uInfo->user->nick);
537         free(uInfo);
538 }
539
540 static void
541 spamserv_new_user_func(struct userNode *user)
542 {
543         if(!IsLocal(user))
544                 spamserv_create_user(user);
545 }
546
547 static void
548 spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))
549 {
550         struct userInfo *uInfo = get_userInfo(user->nick);
551         struct killNode *kNode;
552
553         if(killer == spamserv)
554         {
555                 kNode = malloc(sizeof(struct killNode));
556
557                 if(!kNode)
558                 {
559                         log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for killNode - nickname %s", user->nick);
560                         spamserv_delete_user(uInfo);                    
561                         return;
562                 }
563
564                 if(uInfo->warnlevel > KILL_WARNLEVEL)
565                         kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;
566                 else
567                         kNode->warnlevel = 0;
568
569                 kNode->time = now;
570
571                 dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);
572         }
573
574         spamserv_delete_user(uInfo);    
575 }
576
577 static void
578 spamserv_nick_change_func(struct userNode *user, const char *old_nick)
579 {
580         struct userInfo *uInfo = get_userInfo(old_nick);
581
582         dict_remove(connected_users_dict, old_nick);
583         dict_insert(connected_users_dict, user->nick, uInfo);
584 }
585
586 static int
587 spamserv_user_join(struct modeNode *mNode)
588 {
589         struct chanNode *channel = mNode->channel;
590         struct userNode *user = mNode->user;    
591         struct chanInfo *cInfo;
592         struct userInfo *uInfo;
593         struct floodNode *jfNode;
594
595         if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))
596                 return 0;
597
598         if(IsOper(user))
599         {
600                 return 0;
601         }
602         
603     if(!CHECK_CHANOPS(cInfo))
604         {
605                 //struct modeNode *mn = GetUserMode(channel, user);
606                 //if(mn->modes & MODE_CHANOP)
607                 //      return;
608         if(check_user_level(channel, user, lvlGiveOps, 1, 0)) 
609             return 0;
610         }
611     
612     if(!CHECK_VOICED(cInfo))
613         {
614         if(check_user_level(channel, user, lvlGiveVoice, 1, 0)) 
615             return 0;
616     }
617     
618     if(cInfo->exceptlevel == 0)
619         return 0;
620     if(cInfo->exceptlevel < 501) {
621       struct userData *uData;
622        if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
623         return 0;
624        }
625     }
626
627         if(!(jfNode = uInfo->joinflood))
628         {
629                 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
630         }
631         else
632         {
633                 for(; jfNode; jfNode = jfNode->next)
634                         if(jfNode->channel == channel)
635                                 break;
636
637                 if(!jfNode)
638                 {
639                         spamserv_create_floodNode(channel, user, &uInfo->joinflood);
640                 }
641                 else
642                 {
643                         jfNode->count++;
644                         jfNode->time = now;             
645
646                         if(jfNode->count > JOINFLOOD_MAX)
647                         {
648                                 char reason[MAXLEN];
649
650                                 spamserv_delete_floodNode(&uInfo->joinflood, jfNode);
651                                 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_JOINFLOOD, spamserv_conf.network_rules);
652                                 spamserv_punish(channel, user, JOINFLOOD_B_DURATION, reason, 1);
653                         }
654                 }
655         }
656
657         return 0;
658 }
659
660 static void
661 spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
662 {
663         struct userNode *user = mn->user;
664         struct chanNode *channel = mn->channel;
665         struct userInfo *uInfo;
666         struct spamNode *sNode;
667         struct floodNode *fNode;
668
669         if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))
670                 return;
671
672         if((sNode = uInfo->spam))
673         {
674                 for(; sNode; sNode = sNode->next)
675                         if(sNode->channel == channel)
676                                 break;
677
678                 if(sNode)
679                         spamserv_delete_spamNode(uInfo, sNode);
680         }
681
682         if((fNode = uInfo->flood))
683         {
684                 for(; fNode; fNode = fNode->next)
685                         if(fNode->channel == channel)
686                                 break;
687
688                 if(fNode)
689                         spamserv_delete_floodNode(&uInfo->flood, fNode);
690         }
691 }
692
693 static struct badword*
694 add_badword(struct chanInfo *cInfo, const char *badword_mask, unsigned int triggered, unsigned int action, const char *id)
695 {
696     struct badword *badword;
697
698     badword = calloc(1, sizeof(*badword));
699     if (!badword)
700         return NULL;
701
702     if(!id) {
703         cInfo->last_badword_id++;
704         badword->id = strtab(cInfo->last_badword_id);
705     } else
706         badword->id = strdup(id);
707     badword->badword_mask = strdup(badword_mask);
708     badword->triggered = triggered;
709     badword->action = action;
710     dict_insert(cInfo->badwords, badword->id, badword);
711     return badword;
712 }
713
714 /***********************************************/
715 /*                 Other Stuff                 */
716 /***********************************************/
717
718 static void
719 crc32_init(void)
720 {
721         unsigned long crc;
722         int i, j;
723
724         for(i = 0; i < 256; i++)
725         {
726                 crc = i;
727
728                 for(j = 8; j > 0; j--)
729                 {
730                         if(crc & 1)
731                         {
732                                 crc = (crc >> 1) ^ 0xEDB88320L;
733                         }
734                         else
735                         {
736                                 crc >>= 1;
737                         }
738                 }
739
740                 crc_table[i] = crc;
741         }
742 }
743
744 static unsigned long
745 crc32(const char *text)
746 {
747         register unsigned long crc = 0xFFFFFFFF;
748         unsigned int c, i = 0;
749         
750         while((c = (unsigned int)text[i++]) != 0)
751                 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];
752  
753         return (crc^0xFFFFFFFF);
754 }
755
756 static void
757 timeq_flood(UNUSED_ARG(void *data))
758 {
759         dict_iterator_t         it;
760         struct userInfo         *uInfo;
761         struct floodNode        *fNode;
762
763         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
764         {
765                 uInfo = iter_data(it);
766
767                 if(!(fNode = uInfo->flood))
768                         continue;
769
770                 for(; fNode; fNode = fNode->next)
771                 {
772                         if(now - fNode->time > FLOOD_EXPIRE)
773                         {
774                                 if(!(--fNode->count))
775                                         spamserv_delete_floodNode(&uInfo->flood, fNode);
776                         }
777                 }
778         }
779         
780         timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
781 }
782
783 static void
784 timeq_joinflood(UNUSED_ARG(void *data))
785 {
786         dict_iterator_t it;
787         struct userInfo *uInfo;
788         struct floodNode *fNode;
789
790         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
791         {
792                 uInfo = iter_data(it);
793
794                 if(!(fNode = uInfo->joinflood))
795                         continue;
796
797                 for(; fNode; fNode = fNode->next)
798                 {
799                         if(now - fNode->time > JOINFLOOD_EXPIRE)
800                         {
801                                 if(!(--fNode->count))
802                                         spamserv_delete_floodNode(&uInfo->joinflood, fNode);                            
803                         }
804                 }
805         }
806
807         timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
808 }
809
810 static void
811 timeq_adv(UNUSED_ARG(void *data))
812 {
813         dict_iterator_t it;
814         struct userInfo *uInfo;
815
816         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
817         {
818                 uInfo = iter_data(it);
819
820                 if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)
821                 {
822                         uInfo->lastadv = 0;
823                         uInfo->flags &= ~USER_ADV_WARNED;
824                 }
825         }
826
827         timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
828 }
829
830 static void
831 timeq_warnlevel(UNUSED_ARG(void *data))
832 {
833         dict_iterator_t it;
834         struct userInfo *uInfo;
835
836         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
837         {
838                 uInfo = iter_data(it);
839
840                 if(uInfo->warnlevel > 0)
841                         uInfo->warnlevel--;
842         }
843
844         timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
845 }
846
847 static void
848 timeq_kill(UNUSED_ARG(void *data))
849 {
850         dict_iterator_t it;
851         struct killNode *kNode;
852
853         for(it = dict_first(killed_users_dict); it; it = iter_next(it))
854         {
855                 kNode = iter_data(it);
856
857                 if(kNode->time - now > KILL_EXPIRE)
858                         free(kNode);
859         }
860
861         timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
862 }
863
864 static int
865 binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
866 {
867         struct chanInfo *cInfo = get_chanInfo(channel->name);
868         int value;
869
870         if(argc > 1)
871         {
872                 if(enabled_string(argv[1]))
873                 {
874                         cInfo->flags |= mask;
875                         value = 1;
876                 }
877                 else if(disabled_string(argv[1]))
878                 {
879                     cInfo->flags &= ~mask;
880                     value = 0;
881                 }
882                 else
883                 {
884                    spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);
885                    return 0;
886                 }
887         }
888         else
889         {
890                 value = (cInfo->flags & mask) ? 1 : 0;
891         }
892
893         spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");
894         return 1;
895 }
896
897 struct valueData
898 {
899         char *description;
900         char value;
901         int  oper_only : 1;
902 };
903
904 static int
905 multiple_option(char *name, char *description, enum channelinfo info, struct valueData *values, int count, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
906 {
907         struct chanInfo *cInfo = get_chanInfo(channel->name);
908         int index;
909
910         if(argc > 1)
911         {
912                 index = atoi(argv[1]);
913                 
914                 if(index < 0 || index >= count)
915                 {
916                         spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);
917
918             for(index = 0; index < count; index++)
919                 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
920
921                         return 0;
922                 }
923
924                 if(values[index].oper_only && !IsOper(user))
925                 {
926                         spamserv_notice(user, "SSMSG_MUST_BE_OPER");
927                         return 0;
928                 }
929                 
930                 cInfo->info[info] = values[index].value;
931         }
932         else
933         {
934                 for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);
935         }
936
937         spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
938         return 1;
939 }
940
941 static int
942 show_exceptions(struct userNode *user, struct chanInfo *cInfo)
943 {
944         struct helpfile_table table;
945         unsigned int i;
946
947         if(!cInfo->exceptions->used)
948         {
949                 spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");
950                 return 0;
951         }
952
953         spamserv_notice(user, "SSMSG_EXCEPTION_LIST");
954
955         table.length = 0;
956         table.width = 1;
957         table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
958         table.contents = alloca(cInfo->exceptions->used * sizeof(*table.contents));
959
960         for(i = 0; i < cInfo->exceptions->used; i++)
961         {
962                 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
963                 table.contents[table.length][0] = cInfo->exceptions->list[i];
964                 table.length++;
965         }
966         
967         table_send(spamserv, user->nick, 0, NULL, table);
968
969         return 1;
970 }
971
972 static void
973 show_memory_usage(struct userNode *user)
974 {
975         dict_iterator_t it;
976         struct helpfile_table table;
977         struct chanInfo *cInfo;
978         struct userInfo *uInfo;
979         struct spamNode *sNode;
980         struct floodNode *fNode;
981         double channel_size = 0, user_size, size;
982         unsigned int spamcount = 0, floodcount = 0, i, j;
983         char buffer[64];
984
985         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
986         {
987                 cInfo = iter_data(it);
988
989                 if(!cInfo->exceptions->used)
990                         continue;
991
992                 for(i = 0; i < cInfo->exceptions->used; i++)
993                         channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char);              
994         }
995
996         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
997         {
998                 uInfo = iter_data(it);
999
1000                 for(sNode = uInfo->spam; sNode; sNode = sNode->next, spamcount++);
1001                 for(fNode = uInfo->flood; fNode; fNode = fNode->next, floodcount++);
1002                 for(fNode = uInfo->joinflood; fNode; fNode = fNode->next, floodcount++);
1003         }
1004
1005         channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);
1006         
1007         user_size = dict_size(connected_users_dict) * sizeof(struct userInfo) +
1008                                 dict_size(killed_users_dict) * sizeof(struct killNode) +
1009                                 spamcount * sizeof(struct spamNode)     +
1010                                 floodcount *  sizeof(struct floodNode);
1011
1012         size = channel_size + user_size;
1013         
1014         ss_reply("SSMSG_STATUS_MEMORY");
1015         
1016         table.length = 3;
1017         table.width = 4;
1018         table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;
1019         table.contents = calloc(table.length, sizeof(char**));
1020
1021         // chanInfo
1022         table.contents[0] = calloc(table.width, sizeof(char*));
1023         snprintf(buffer, sizeof(buffer), "Channel Memory Usage:");
1024         table.contents[0][0] = strdup(buffer);
1025         snprintf(buffer, sizeof(buffer), " %g Byte; ", channel_size);
1026         table.contents[0][1] = strdup(buffer);
1027         snprintf(buffer, sizeof(buffer), "%g KiloByte; ", channel_size / 1024);
1028         table.contents[0][2] = strdup(buffer);
1029         snprintf(buffer, sizeof(buffer), "%g MegaByte", channel_size / 1024 / 1024);
1030         table.contents[0][3] = strdup(buffer);
1031
1032         // userInfo
1033         table.contents[1] = calloc(table.width, sizeof(char*));
1034         snprintf(buffer, sizeof(buffer), "User Memory Usage   :");
1035         table.contents[1][0] = strdup(buffer);
1036         snprintf(buffer, sizeof(buffer), " %g Byte; ", user_size);
1037         table.contents[1][1] = strdup(buffer);
1038         snprintf(buffer, sizeof(buffer), "%g KiloByte; ", user_size / 1024);
1039         table.contents[1][2] = strdup(buffer);
1040         snprintf(buffer, sizeof(buffer), "%g MegaByte", user_size / 1024 / 1024);
1041         table.contents[1][3] = strdup(buffer);
1042
1043         // total memory usage
1044         table.contents[2] = calloc(table.width, sizeof(char*));
1045         snprintf(buffer, sizeof(buffer), "Total Memory Usage  :");
1046         table.contents[2][0] = strdup(buffer);
1047         snprintf(buffer, sizeof(buffer), " %g Byte; ", size);
1048         table.contents[2][1] = strdup(buffer);
1049         snprintf(buffer, sizeof(buffer), "%g KiloByte; ", size / 1024);
1050         table.contents[2][2] = strdup(buffer);
1051         snprintf(buffer, sizeof(buffer), "%g MegaByte", size / 1024 / 1024);
1052         table.contents[2][3] = strdup(buffer);
1053
1054         table_send(spamserv, user->nick, 0, NULL, table);
1055         
1056         for(i = 0; i < table.length; i++)
1057         {
1058                 for(j = 0; j < table.width; j++)
1059                         free((char*)table.contents[i][j]);
1060
1061         free(table.contents[i]);
1062         }
1063
1064         free(table.contents);
1065 }
1066
1067 static void
1068 show_registered_channels(struct userNode *user)
1069 {
1070         struct helpfile_table table;
1071         dict_iterator_t it;
1072
1073         spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");
1074
1075         if(!dict_size(registered_channels_dict))
1076         {
1077                 spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");
1078                 return;
1079         }
1080
1081         table.length = 0;
1082         table.width = 1;
1083         table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
1084         table.contents = alloca(dict_size(registered_channels_dict) * sizeof(*table.contents));
1085
1086         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
1087         {
1088                 struct chanInfo *cInfo = iter_data(it);
1089
1090                 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
1091                 table.contents[table.length][0] = cInfo->channel->name;
1092                 table.length++;
1093         }
1094         
1095         table_send(spamserv, user->nick, 0, NULL, table);
1096 }
1097
1098 /***********************************************/
1099 /*                SpamServ_Func                */
1100 /***********************************************/
1101
1102 static 
1103 SPAMSERV_FUNC(cmd_register)
1104 {
1105         struct chanInfo *cInfo;
1106
1107         if(!channel || !channel->channel_info)
1108         {
1109                 ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);
1110                 return 0;
1111         }
1112
1113         if(get_chanInfo(channel->name))
1114         {
1115                 ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);
1116                 return 0;
1117         }
1118
1119         if(IsSuspended(channel->channel_info))
1120         {
1121                 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");
1122                 return 0;
1123         }
1124
1125         if(channel == spamserv_conf.debug_channel)
1126         {
1127                 ss_reply("SSMSG_DEBUG_CHAN");
1128                 return 0;
1129         }
1130
1131         if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))
1132         {
1133                 ss_reply("SSMSG_NO_REGISTER", channel->name);
1134                 return 0;
1135         }
1136
1137         spamserv_join_channel(cInfo->channel);
1138         
1139         spamserv_oper_message(SSMSG_CHANNEL_REGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
1140         ss_reply("SSMSG_REG_SUCCESS", channel->name);
1141
1142         return 1;
1143 }
1144
1145 static 
1146 SPAMSERV_FUNC(cmd_unregister)
1147 {
1148         struct chanInfo *cInfo;
1149         struct chanData *cData;
1150         struct userData *uData;
1151         char reason[MAXLEN];
1152
1153         if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))
1154         {
1155                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1156                 return 0;
1157         }
1158
1159         if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))
1160         {
1161         ss_reply("SSMSG_NO_ACCESS");
1162         return 0;
1163         }
1164
1165         if(!IsHelping(user))
1166         {
1167         if(IsSuspended(cData))
1168         {
1169             ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");
1170             return 0;
1171         }
1172
1173                 if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))
1174                 {
1175                         ss_reply("SSMSG_CONFIRM_UNREG");
1176                         return 0;
1177                 }
1178         }
1179
1180         if(!CHECK_SUSPENDED(cInfo))
1181         {
1182                 snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle);          
1183                 spamserv_part_channel(channel, reason);
1184         }
1185         
1186         spamserv_unregister_channel(cInfo);     
1187
1188         spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
1189         ss_reply("SSMSG_UNREG_SUCCESS", channel->name);
1190
1191         return 1;
1192 }
1193
1194 static 
1195 SPAMSERV_FUNC(cmd_status)
1196 {
1197         ss_reply("SSMSG_STATUS");
1198         ss_reply("SSMSG_STATUS_USERS", dict_size(connected_users_dict));
1199         ss_reply("SSMSG_STATUS_CHANNELS", dict_size(registered_channels_dict));
1200
1201         if(IsOper(user) && argc > 1)
1202         {
1203                 if(!irccasecmp(argv[1], "memory"))
1204                         show_memory_usage(user);
1205                 else if(!irccasecmp(argv[1], "channels"))
1206                         show_registered_channels(user);         
1207         }
1208         
1209         return 1;
1210 }
1211
1212 static 
1213 SPAMSERV_FUNC(cmd_addexception)
1214 {
1215         struct chanInfo *cInfo = get_chanInfo(channel->name);
1216         struct userData *uData;
1217         unsigned int i;
1218
1219         if(!cInfo || !channel->channel_info)
1220         {
1221                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1222                 return 0;
1223         }
1224
1225         if(CHECK_SUSPENDED(cInfo))
1226         {
1227                 ss_reply("SSMSG_SUSPENDED", channel->name);
1228                 return 0;
1229         }
1230
1231         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
1232         {
1233                 ss_reply("SSMSG_NO_ACCESS");
1234                 return 0;
1235         }
1236
1237         if(argc < 2)
1238                 return show_exceptions(user, cInfo);
1239
1240         if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))
1241         {
1242                 ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);
1243                 return 0;
1244         }
1245
1246         if(strlen(argv[1]) < spamserv_conf.exception_min_len)
1247         {
1248                 ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);
1249                 return 0;
1250         }
1251         else if(strlen(argv[1]) > spamserv_conf.exception_max_len)
1252         {
1253                 ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);
1254                 return 0;
1255         }
1256
1257         for(i = 0; i < cInfo->exceptions->used; i++)
1258         {
1259                 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
1260                 {
1261                         ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);
1262                         return 0;
1263                 }
1264         }
1265
1266         string_list_append(cInfo->exceptions, strdup(argv[1]));
1267         ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);
1268
1269         return 1;
1270 }
1271
1272 static 
1273 SPAMSERV_FUNC(cmd_delexception)
1274 {
1275         struct chanInfo *cInfo = get_chanInfo(channel->name);
1276         struct userData *uData;
1277         unsigned int i;
1278         int found = -1;
1279
1280         if(!cInfo || !channel->channel_info)
1281         {
1282                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1283                 return 0;
1284         }
1285
1286         if(CHECK_SUSPENDED(cInfo))
1287         {
1288                 ss_reply("SSMSG_SUSPENDED", channel->name);
1289                 return 0;
1290         }
1291
1292         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
1293         {
1294                 ss_reply("SSMSG_NO_ACCESS");
1295                 return 0;
1296         }
1297
1298         if(argc < 2)
1299                 return show_exceptions(user, cInfo);
1300
1301         for(i = 0; i < cInfo->exceptions->used; i++)
1302         {
1303                 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
1304                 {
1305                         found = i;
1306                         break;
1307                 }
1308         }
1309         
1310         if(found == -1)
1311         {
1312                 ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);
1313                 return 0;
1314         }
1315
1316         string_list_delete(cInfo->exceptions, i);
1317         ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);
1318
1319         return 1;
1320 }
1321
1322 static 
1323 SPAMSERV_FUNC(cmd_set)
1324 {
1325         struct chanInfo *cInfo = get_chanInfo(channel->name);
1326         struct svccmd   *subcmd;        
1327         char cmd_name[MAXLEN];
1328         unsigned int i;
1329
1330         if(!cInfo)
1331         {
1332                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1333                 return 0;
1334         }
1335
1336         if(CHECK_SUSPENDED(cInfo))
1337         {
1338                 ss_reply("SSMSG_SUSPENDED", channel->name);
1339                 return 0;
1340         }
1341
1342     if(!check_user_level(channel,user,lvlSetters,1,0))
1343         {
1344                 ss_reply("SSMSG_NO_ACCESS");
1345                 return 0;
1346         }
1347         
1348         if(argc < 2)
1349         {
1350                 ss_reply("SSMSG_CHANNEL_OPTIONS");
1351
1352                 for(i = 0; i < SET_SUBCMDS_SIZE; i++)
1353                 {
1354                         sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);
1355
1356                         if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))
1357                                 subcmd->command->func(user, channel, 1, argv + 1, subcmd);
1358                 }
1359
1360                 return 1;
1361         }
1362
1363         sprintf(cmd_name, "%s %s", cmd->name, argv[1]);
1364         subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);
1365
1366         if(!subcmd)
1367         {
1368                 reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);
1369                 return 0;
1370         }
1371
1372         return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
1373 }
1374
1375 static 
1376 SPAMSERV_FUNC(opt_spamlimit)
1377 {
1378         struct valueData values[] =
1379         {
1380                 {"Users may send the same message $b2$b times.", 'a', 0},
1381                 {"Users may send the same message $b3$b times.", 'b', 0},
1382                 {"Users may send the same message $b4$b times.", 'c', 0},
1383                 {"Users may send the same message $b5$b times.", 'd', 0},
1384                 {"Users may send the same message $b6$b times.", 'e', 0}
1385         };
1386
1387         MULTIPLE_OPTION("SpamLimit     ", "SpamLimit", ci_SpamLimit);
1388 }
1389
1390 static 
1391 SPAMSERV_FUNC(opt_advreaction)
1392 {
1393         struct valueData values[] =
1394         {
1395                 {"Kick on disallowed advertising.", 'k', 0},
1396                 {"Kickban on disallowed advertising.", 'b', 0},
1397                 {"Short timed ban on disallowed advertising.", 's', 0},
1398                 {"Long timed ban on disallowed advertising.", 'l', 0},
1399                 {"Kill on disallowed advertising.", 'd', 1}
1400         };
1401
1402         MULTIPLE_OPTION("AdvReaction   ", "AdvReaction", ci_AdvReaction);
1403 }
1404
1405 static 
1406 SPAMSERV_FUNC(opt_warnreaction)
1407 {
1408         struct valueData values[] =
1409         {
1410                 {"Kick after warning.", 'k', 0},
1411                 {"Kickban after warning.", 'b', 0},
1412                 {"Short timed ban after warning.", 's', 0},
1413                 {"Long timed ban after warning.", 'l', 0},
1414                 {"Kill after warning.", 'd', 1}
1415         };
1416
1417         MULTIPLE_OPTION("WarnReaction  ", "WarnReaction", ci_WarnReaction);
1418 }
1419
1420 static 
1421 SPAMSERV_FUNC(opt_advscan)
1422 {
1423         BINARY_OPTION("AdvScan       ", CHAN_ADV_SCAN);
1424 }
1425
1426 static 
1427 SPAMSERV_FUNC(opt_spamscan)
1428 {
1429         BINARY_OPTION("SpamScan      ", CHAN_SPAMSCAN);
1430 }
1431
1432 static 
1433 SPAMSERV_FUNC(opt_floodscan)
1434 {
1435         BINARY_OPTION("FloodScan     ", CHAN_FLOODSCAN);
1436 }
1437
1438 static 
1439 SPAMSERV_FUNC(opt_joinflood)
1440 {
1441         BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);
1442 }
1443
1444 static 
1445 SPAMSERV_FUNC(opt_scanops)
1446 {
1447         BINARY_OPTION("ScanChanOps   ", CHAN_SCAN_CHANOPS);
1448 }
1449
1450 static 
1451 SPAMSERV_FUNC(opt_scanvoiced)
1452 {
1453         BINARY_OPTION("ScanVoiced    ", CHAN_SCAN_VOICED);
1454 }
1455
1456 static 
1457 SPAMSERV_FUNC(opt_exceptlevel)
1458 {
1459  struct chanInfo *cInfo = get_chanInfo(channel->name);
1460  struct userData *uData;
1461  int index;
1462  if(argc > 1)
1463         {
1464                 index = atoi(argv[1]);
1465                 if(index < 1 || index > 501)
1466                 {
1467                         spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");
1468                         return 0;
1469                 }
1470         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))
1471         {
1472             ss_reply("SSMSG_NO_ACCESS");
1473             return 0;
1474         }
1475         cInfo->exceptlevel=index;
1476     } else {
1477      index=cInfo->exceptlevel;
1478     }
1479     spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel   ", index);
1480     return 1;
1481 }
1482
1483 static SPAMSERV_FUNC(cmd_addbad)
1484 {
1485          struct chanInfo *cInfo = get_chanInfo(channel->name);
1486          struct userData *uData;
1487
1488          if(!cInfo)
1489          {
1490                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1491                 return 0;
1492          }
1493
1494          if(CHECK_SUSPENDED(cInfo))
1495          {
1496                  ss_reply("SSMSG_SUSPENDED", channel->name);
1497              return 0;
1498          }
1499
1500          if(!check_user_level(channel,user,lvlSetters,1,0))
1501          {
1502                 ss_reply("SSMSG_NO_ACCESS");
1503                 return 0;
1504          }
1505
1506          dict_iterator_t it;
1507          char *mask = unsplit_string(argv + 1, argc - 1, NULL);
1508          for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1509                  struct badword *badword = iter_data(it);
1510                  if(match_ircglob(mask,badword->badword_mask)) {
1511                          reply("SSMSG_BADWORD_ALREADY_ADDED", mask, badword->id);
1512                          return 1;
1513                  }
1514          }
1515
1516          struct badword *new_badword = add_badword(cInfo, mask, 0, BADACTION_KICK, NULL);
1517          for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1518                  struct badword *badword = iter_data(it);
1519                  if(match_ircglob(badword->badword_mask, new_badword->badword_mask) && badword != new_badword) {
1520                          dict_remove(cInfo->badwords, badword->id);
1521                  }
1522          }
1523
1524          reply("SSMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id);
1525          return 1;
1526 }
1527
1528 static SPAMSERV_FUNC(cmd_delbad)
1529 {
1530          struct chanInfo *cInfo = get_chanInfo(channel->name);
1531          struct userData *uData;
1532          unsigned int n;
1533
1534          if(!cInfo)
1535          {
1536                  ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1537                  return 0;
1538          }
1539
1540          if(CHECK_SUSPENDED(cInfo))
1541          {
1542                  ss_reply("SSMSG_SUSPENDED", channel->name);
1543              return 0;
1544          }
1545
1546          if(!check_user_level(channel,user,lvlSetters,1,0))
1547          {
1548                  ss_reply("SSMSG_NO_ACCESS");
1549                  return 0;
1550          }
1551
1552          for (n=1; n<argc; n++) {
1553                  struct badword *badword = dict_find(cInfo->badwords, argv[n], NULL);
1554                  if (!badword) {
1555                          reply("SSMSG_BADWORD_NOT_FOUND", argv[n]);
1556                          continue;
1557                  }
1558                  reply("SSMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);
1559                  dict_remove(cInfo->badwords, argv[n]);
1560          }
1561          return 1;
1562 }
1563
1564 static SPAMSERV_FUNC(cmd_setbad)
1565 {
1566          struct chanInfo *cInfo = get_chanInfo(channel->name);
1567          struct userData *uData;
1568          struct badword *badword;
1569
1570          if(!cInfo)
1571          {
1572                  ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1573                  return 0;
1574          }
1575
1576          if(CHECK_SUSPENDED(cInfo))
1577          {
1578                  ss_reply("SSMSG_SUSPENDED", channel->name);
1579                  return 0;
1580          }
1581
1582          if(!check_user_level(channel,user,lvlSetters,1,0))
1583          {
1584                  ss_reply("SSMSG_NO_ACCESS");
1585                  return 0;
1586          }
1587
1588          if ((badword = dict_find(cInfo->badwords, argv[1], NULL))) {
1589                  if (argc > 3) {
1590                          unsigned int ii;
1591                          char *setting = argv[2];
1592                          char *value = argv[3];
1593                          for( ii = 0; setting[ ii ]; ii++)
1594                                  setting[ ii ] = toupper( setting[ ii ] );
1595                          for( ii = 0; value[ ii ]; ii++)
1596                                  value[ ii ] = toupper( value[ ii ] );
1597                          if(!strcmp("MASK",setting)) {
1598                                    free(badword->badword_mask);
1599                                    badword->badword_mask = strdup(argv[3]);
1600                                    badword->triggered = 0;
1601                                    reply("SSMSG_BADWORD_SET_DONE");
1602                          }
1603                          else if(!strcmp("ACTION",setting)) {
1604                             if (!strcmp("1",value) || !strcmp("KICK",value)) {
1605                                     badword->action = BADACTION_KICK;
1606                                     reply("SSMSG_BADWORD_SET_DONE");
1607                                 } else if (!strcmp("2",value) || !strcmp("BAN",value)) {
1608                                         badword->action = BADACTION_BAN;
1609                                     reply("SSMSG_BADWORD_SET_DONE");
1610                                 } else if (!strcmp("3",value) || !strcmp("KILL",value)) {
1611                     if(IsOper(user)) {
1612                         badword->action = BADACTION_KILL;
1613                         reply("SSMSG_BADWORD_SET_DONE");
1614                     } else {
1615                         reply("SSMSG_NO_ACCESS");
1616                     }
1617                                 } else if (!strcmp("4",value) || !strcmp("GLINE",value)) {
1618                     if(IsOper(user)) {
1619                         badword->action = BADACTION_GLINE;
1620                         reply("SSMSG_BADWORD_SET_DONE");
1621                     } else {
1622                         reply("SSMSG_NO_ACCESS");
1623                     }
1624                                 } else {
1625                                     reply("SSMSG_BADWORD_SET_INVALID", setting);
1626                             }
1627                          } else {
1628                                   reply("SSMSG_BADWORD_SETTING_INVALID", setting);
1629                          }
1630
1631                  } else {
1632                          reply("SSMSG_BADWORD_SET", badword->id);
1633                          reply("SSMSG_BADWORD_SET_MASK", badword->badword_mask);
1634                          switch(badword->action) {
1635                                  case BADACTION_KICK:
1636                                    reply("SSMSG_BADWORD_SET_ACTION", "KICK");
1637                                    break;
1638                                  case BADACTION_BAN:
1639                                    reply("SSMSG_BADWORD_SET_ACTION", "BAN");
1640                                    break;
1641                                  case BADACTION_KILL:
1642                                    reply("SSMSG_BADWORD_SET_ACTION", "KILL");
1643                                    break;
1644                                  case BADACTION_GLINE:
1645                                    reply("SSMSG_BADWORD_SET_ACTION", "GLINE");
1646                                    break;
1647                                  default:
1648                                    reply("SSMSG_BADWORD_SET_ACTION", "*undef*");
1649                          }
1650                  }
1651          } else {
1652                  reply("SSMSG_BADWORD_NOT_FOUND", argv[1]);
1653                  return 0;
1654          }
1655          return 1;
1656 }
1657
1658 int
1659 ss_badwords_sort(const void *pa, const void *pb)
1660 {
1661         struct badword *a = *(struct badword**)pa;
1662         struct badword *b = *(struct badword**)pb;
1663
1664         return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0);
1665 }
1666
1667 static SPAMSERV_FUNC(cmd_listbad)
1668 {
1669          struct helpfile_table tbl;
1670          struct chanInfo *cInfo = get_chanInfo(channel->name);
1671          struct userData *uData;
1672          struct badword **badwords;
1673          unsigned int count = 0, ii = 0;
1674
1675          if(!cInfo)
1676          {
1677              ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1678                  return 0;
1679          }
1680
1681      if(CHECK_SUSPENDED(cInfo))
1682          {
1683          ss_reply("SSMSG_SUSPENDED", channel->name);
1684                  return 0;
1685          }
1686
1687          if(!check_user_level(channel,user,lvlSetters,1,0))
1688          {
1689                  ss_reply("SSMSG_NO_ACCESS");
1690                  return 0;
1691          }
1692
1693          dict_iterator_t it;
1694          for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1695                  count++;
1696          }
1697
1698          tbl.length = count+1;
1699          tbl.width = 4;
1700          tbl.flags = 0;
1701          tbl.flags = TABLE_NO_FREE;
1702          tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
1703          tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
1704          tbl.contents[0][0] = "#";
1705          tbl.contents[0][1] = "Badword";
1706          tbl.contents[0][2] = "Action";
1707          tbl.contents[0][3] = "(Triggered)";
1708          if(!count)
1709          {
1710                  table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
1711              reply("MSG_NONE");
1712              free(tbl.contents[0]);
1713              free(tbl.contents);
1714              return 0;
1715          }
1716          badwords = alloca(count * sizeof(badwords[0]));
1717          for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1718              struct badword *bw = iter_data(it);
1719              badwords[ii++] = bw;
1720           }
1721
1722          qsort(badwords, count, sizeof(badwords[0]), ss_badwords_sort);
1723          for (ii = 1; ii <= count; ii++) {
1724                  struct badword *bw = badwords[ii-1];
1725                  tbl.contents[ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
1726                  tbl.contents[ii][0] = strdup(bw->id);
1727                  tbl.contents[ii][1] = strdup(bw->badword_mask);
1728                  switch(bw->action) {
1729                          case BADACTION_KICK:
1730                            tbl.contents[ii][2] = "KICK";
1731                            break;
1732                          case BADACTION_BAN:
1733                            tbl.contents[ii][2] = "BAN";
1734                            break;
1735                          case BADACTION_KILL:
1736                            tbl.contents[ii][2] = "KILL";
1737                            break;
1738                          case BADACTION_GLINE:
1739                            tbl.contents[ii][2] = "GLINE";
1740                            break;
1741                          default:
1742                            tbl.contents[ii][2] = "*undef*";
1743                  }
1744                  tbl.contents[ii][3] = strtab(bw->triggered);
1745          }
1746
1747          table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
1748          for(ii = 1; ii < tbl.length; ++ii)
1749          {
1750                  free(tbl.contents[ii]);
1751          }
1752          free(tbl.contents[0]);
1753          free(tbl.contents);
1754          return 1;
1755 }
1756
1757 static void 
1758 to_lower(char *message)
1759 {
1760         unsigned int i, diff = 'a' - 'A';
1761
1762         for(i = 0; i < strlen(message); i++)
1763         {
1764                 if((message[i] >= 'A') && (message[i] <= 'Z'))
1765                         message[i] = message[i] + diff;
1766         }
1767 }
1768
1769 static char *
1770 strip_mirc_codes(char *text)
1771 {
1772         // taken from xchat and modified
1773         int nc = 0, i = 0, col = 0, len = strlen(text);
1774         static char new_str[MAXLEN];
1775
1776         while(len > 0)
1777         {
1778                 if((col && isdigit(*text) && nc < 2) ||
1779                         (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))
1780                 {
1781                         nc++;
1782
1783                         if(*text == ',')
1784                                 nc = 0;
1785                 }
1786                 else
1787                 {
1788                         col = 0;
1789
1790                         switch(*text)
1791                         {
1792                         case '\003':
1793                                 col = 1;
1794                                 nc = 0;
1795                                 break;
1796                         case '\002':
1797                         case '\022':
1798                         case '\026':                    
1799                         case '\031':
1800                         case '\037':
1801                                 break;
1802                         default:
1803                                 new_str[i] = *text;
1804                                 i++;
1805                         }
1806                 }
1807
1808                 text++;
1809                 len--;
1810         }
1811
1812         new_str[i] = '\0';
1813
1814         return new_str;
1815 }
1816
1817 static int
1818 is_in_exception_list(struct chanInfo *cInfo, char *message)
1819 {
1820         unsigned int i;
1821
1822         for(i = 0; i < cInfo->exceptions->used; i++)
1823                 if(strstr(message, cInfo->exceptions->list[i]))
1824                         return 1;
1825
1826         return 0;
1827 }
1828
1829 static int
1830 check_advertising(struct chanInfo *cInfo, char *message)
1831 {
1832         unsigned int i = 0;
1833
1834         if(spamserv_conf.strip_mirc_codes)
1835                 message = strip_mirc_codes(message);
1836
1837         if(is_in_exception_list(cInfo, message))
1838                 return 0;
1839
1840         while(message[i] != 0)
1841         {
1842                 if(message[i] == '#')
1843                 {
1844                         char channelname[CHANNELLEN];
1845                         unsigned int j = 0;
1846
1847                         if(!spamserv_conf.adv_chan_must_exist)
1848                                 return 1;
1849
1850                         /* only return 1, if the channel does exist */  
1851
1852                         while((message[i] != 0) && (message[i] != ' '))
1853                         {
1854                                 channelname[j] = message[i];
1855                                 i++;
1856                                 j++;                            
1857                         }
1858
1859                         channelname[j] = '\0';
1860
1861                         if(GetChannel(channelname))
1862                                 return 1;
1863                 }
1864                 else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))
1865                         return 1;
1866                 else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))
1867                         return 1;
1868                 else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))
1869                         return 1;
1870
1871                 i++;
1872         }
1873
1874         return 0;
1875 }
1876
1877 struct banData *add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason);
1878
1879 static void
1880 spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)
1881 {
1882         if(ban)
1883         {
1884                 struct mod_chanmode change;
1885                 char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
1886
1887                 sanitize_ircmask(hostmask);
1888
1889                 if(expires)
1890                         add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);
1891
1892                 mod_chanmode_init(&change);
1893                 change.argc = 1;
1894                 change.args[0].mode = MODE_BAN;
1895       change.args[0].u.hostmask = hostmask;
1896                 mod_chanmode_announce(spamserv, channel, &change);        
1897
1898                 free(hostmask);
1899
1900                 spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);
1901         }
1902         else
1903                 spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);
1904
1905         KickChannelUser(user, channel, spamserv, reason);       
1906 }
1907
1908 static void
1909 spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct badword *badword)
1910 {
1911     char *hostmask;
1912     char *reason = SSMSG_BADWORD_DETECTED;
1913     char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };
1914     switch(badword->action) {
1915         case BADACTION_BAN:
1916             hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
1917             sanitize_ircmask(hostmask);
1918             if(chan->channel_info) {
1919             //registered channel
1920             add_channel_ban(chan->channel_info, hostmask, spamserv->nick, now, now, now + spamserv_conf.long_ban_duration, reason);
1921             }
1922             struct mod_chanmode change;
1923             mod_chanmode_init(&change);
1924             change.argc = 1;
1925             change.args[0].mode = MODE_BAN;
1926             change.args[0].u.hostmask = hostmask;
1927             mod_chanmode_announce(spamserv, chan, &change);
1928             free(hostmask);
1929             break;
1930         case BADACTION_KICK:
1931             if(GetUserMode(chan, user))
1932             KickChannelUser(user, chan, spamserv, reason);
1933             break;
1934         case BADACTION_KILL:
1935             DelUser(user, spamserv, 1, reason);
1936             break;
1937         case BADACTION_GLINE:
1938             irc_ntop(mask + 2, sizeof(mask) - 2, &user->ip);
1939             gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
1940             break;
1941         default:
1942             //error?
1943             break;
1944     }
1945 }
1946
1947 void
1948 spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)
1949 {
1950         struct chanInfo *cInfo;
1951         struct userInfo *uInfo;
1952         struct spamNode *sNode;
1953         struct floodNode *fNode;
1954         unsigned int violation = 0;
1955         char reason[MAXLEN];
1956
1957         /* make sure: spamserv is not disabled; srvx is running; spamserv is in the chan; chan is regged, user does exist */
1958         if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick)))
1959                 return;
1960
1961         if(IsOper(user))
1962         {
1963                 return;
1964         }
1965
1966         if(!CHECK_CHANOPS(cInfo))
1967         {
1968                 struct modeNode *mn = GetUserMode(channel, user);
1969                 if(mn && mn->modes & MODE_CHANOP)
1970                         return;
1971         //if(check_user_level(channel, user, lvlGiveOps, 1, 0)) 
1972         //    return;
1973         }
1974         
1975         if(!CHECK_VOICED(cInfo))
1976         {
1977                 struct modeNode *mn = GetUserMode(channel, user);
1978                 if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))
1979                         return;
1980         }
1981     
1982     if(cInfo->exceptlevel == 0)
1983         return;
1984     if(cInfo->exceptlevel < 501) {
1985       struct userData *uData;
1986        if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
1987         return;
1988        }
1989     }
1990
1991         to_lower(text);
1992
1993         if(CHECK_SPAM(cInfo))
1994         {
1995                 if(!(sNode = uInfo->spam))
1996                 {
1997                         spamserv_create_spamNode(channel, uInfo, text);
1998                 }
1999                 else
2000                 {
2001                         for(; sNode; sNode = sNode->next)
2002                                 if(sNode->channel == channel)
2003                                         break;
2004
2005                         if(!sNode)
2006                         {
2007                                 spamserv_create_spamNode(channel, uInfo, text);
2008                         }
2009                         else
2010                         {
2011                                 unsigned long crc = crc32(text);
2012
2013                                 if(crc == sNode->crc32)
2014                                 {
2015                                         unsigned int spamlimit = 2;
2016                                         sNode->count++;
2017
2018                                         switch(cInfo->info[ci_SpamLimit])
2019                                         {
2020                                                 case 'a': spamlimit = 2; break;
2021                                                 case 'b': spamlimit = 3; break;
2022                                                 case 'c': spamlimit = 4; break;
2023                                                 case 'd': spamlimit = 5; break;
2024                                                 case 'e': spamlimit = 6; break;
2025                                         }
2026
2027                                         if(sNode->count == spamlimit)
2028                                         {
2029                                                 uInfo->warnlevel += SPAM_WARNLEVEL;
2030
2031                                                 if(uInfo->warnlevel < MAX_WARNLEVEL)
2032                                                         spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules);
2033                                         }
2034                                         else if(sNode->count > spamlimit)
2035                                         {
2036                                                 switch(cInfo->info[ci_WarnReaction])
2037                                                 {
2038                                                         case 'k': uInfo->flags |= USER_KICK; break;
2039                                                         case 'b': uInfo->flags |= USER_KICKBAN; break;
2040                                                         case 's': uInfo->flags |= USER_SHORT_TBAN; break;
2041                                                         case 'l': uInfo->flags |= USER_LONG_TBAN; break;
2042                                                         case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
2043                                                 }
2044
2045                                                 spamserv_delete_spamNode(uInfo, sNode);
2046                                                 uInfo->warnlevel += SPAM_WARNLEVEL;
2047                                                 violation = 1;
2048                                         }
2049                                 }
2050                                 else
2051                                 {
2052                                         sNode->crc32 = crc;                                     
2053                                         sNode->count = 1;
2054                                 }
2055                         }
2056                 }
2057         }
2058
2059         if(CHECK_FLOOD(cInfo))
2060         {
2061                 if(!(fNode = uInfo->flood))
2062                 {
2063                         spamserv_create_floodNode(channel, user, &uInfo->flood);
2064                 }
2065                 else
2066                 {
2067                         for(; fNode; fNode = fNode->next)
2068                                 if(fNode->channel == channel)
2069                                         break;
2070                                 
2071                         if(!fNode)
2072                         {
2073                                 spamserv_create_floodNode(channel, user, &uInfo->flood);
2074                         }
2075                         else
2076                         {
2077                                 if(((now - fNode->time) < FLOOD_EXPIRE))
2078                                 {
2079                                         fNode->count++;
2080                                         
2081                                         if(fNode->count == FLOOD_MAX_LINES - 1)
2082                                         {
2083                                                 uInfo->warnlevel += FLOOD_WARNLEVEL;
2084
2085                                                 if(uInfo->warnlevel < MAX_WARNLEVEL)
2086                                                         spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules);
2087                                         }
2088                                         else if(fNode->count > FLOOD_MAX_LINES)
2089                                         {
2090                                                 switch(cInfo->info[ci_WarnReaction])
2091                                                 {
2092                                                         case 'k': uInfo->flags |= USER_KICK; break;
2093                                                         case 'b': uInfo->flags |= USER_KICKBAN; break;
2094                                                         case 's': uInfo->flags |= USER_SHORT_TBAN; break;
2095                                                         case 'l': uInfo->flags |= USER_LONG_TBAN; break;
2096                                                         case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
2097                                                 }
2098
2099                                                 spamserv_delete_floodNode(&uInfo->flood, fNode);
2100                                                 uInfo->warnlevel += FLOOD_WARNLEVEL;
2101                                                 violation = 2;                                          
2102                                         }
2103                                 }
2104
2105                                 fNode->time = now;
2106                         }
2107                 }
2108         }
2109     
2110     dict_iterator_t it;
2111
2112         for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
2113                 struct badword *badword = iter_data(it);
2114                 if(match_ircglob(text, badword->badword_mask)) {
2115                         spamserv_detected_badword(user, channel, badword);
2116                 }
2117         }
2118
2119         if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))
2120         {
2121                 if(CHECK_ADV_WARNED(uInfo))
2122                 {
2123                         switch(cInfo->info[ci_AdvReaction])
2124                         {
2125                                 case 'k': uInfo->flags |= USER_KICK; break;
2126                                 case 'b': uInfo->flags |= USER_KICKBAN; break;
2127                                 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
2128                                 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
2129                                 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
2130                         }
2131
2132                         uInfo->warnlevel += ADV_WARNLEVEL;
2133                         violation = 3;
2134                 }
2135                 else
2136                 {               
2137                         uInfo->flags |= USER_ADV_WARNED;
2138                         uInfo->lastadv = now;
2139                         uInfo->warnlevel += ADV_WARNLEVEL;
2140
2141                         if(uInfo->warnlevel < MAX_WARNLEVEL)
2142                                 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules);
2143                 }               
2144         }
2145
2146         if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)
2147         {
2148                 uInfo->flags |= USER_WARNED;
2149                 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules);
2150                 irc_notice(spamserv, user->numeric, reason);
2151                 irc_privmsg(spamserv, user->numeric, reason);
2152         }
2153         else if(uInfo->warnlevel > MAX_WARNLEVEL)
2154         {
2155                 if(CHECK_KILLED(uInfo))
2156                         uInfo->flags |= USER_GLINE;
2157                 else
2158                         uInfo->flags |= USER_KILL;
2159
2160                 violation = 5;
2161         }
2162
2163         if(!violation)
2164                 return;
2165
2166         switch(violation)
2167         {
2168                 case 1: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules); break;
2169                 case 2: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules); break;
2170                 case 3: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules); break;
2171                 default: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules); break;
2172         }
2173
2174         if(CHECK_GLINE(uInfo))
2175         {
2176                 int size = strlen(user->hostname) + 3;
2177                 char *mask = alloca(size);
2178                 snprintf(mask, size, "*@%s", user->hostname);
2179                 gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
2180                 spamserv_debug(SSMSG_DEBUG_GLINE, user->nick, user->hostname, channel->name);
2181         }
2182         else if(CHECK_KILL(uInfo))
2183         {
2184                 DelUser(user, spamserv, 1, reason);
2185                 spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);
2186         }
2187         else if(CHECK_LONG_TBAN(uInfo))
2188         {
2189                 spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);
2190         }
2191         else if(CHECK_SHORT_TBAN(uInfo))
2192         {
2193                 spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);
2194         }
2195         else if(CHECK_KICKBAN(uInfo))
2196         {
2197                 spamserv_punish(channel, user, 0, reason, 1);
2198         }
2199         else if(CHECK_KICK(uInfo))
2200         {
2201                 spamserv_punish(channel, user, 0, reason, 0);
2202         }
2203 }
2204
2205 static int
2206 spamserv_saxdb_read_shitlist(const char *name, void *data, void *extra)
2207 {
2208         struct record_data *rd = data;
2209         struct chanInfo *chan = extra;
2210         char *badword;
2211         char *triggered, *action;
2212
2213         if (rd->type == RECDB_OBJECT) {
2214                 dict_t obj = GET_RECORD_OBJECT(rd);
2215                 /* new style structure */
2216                 badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);
2217                 triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);
2218                 action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);
2219
2220                 add_badword(chan, badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);
2221         }
2222         return 0;
2223 }
2224
2225 static int
2226 spamserv_saxdb_read(struct dict *database)
2227 {
2228         dict_iterator_t it;
2229         struct dict *badwords;
2230         struct record_data *hir;
2231         struct chanNode *channel;
2232         struct chanInfo *cInfo;
2233         struct string_list *strlist;
2234         unsigned int flags,exceptlevel,badwordid;
2235         char *str, *info;
2236         unsigned long expiry;    
2237
2238         for(it = dict_first(database); it; it = iter_next(it))
2239         {
2240                 hir = iter_data(it);
2241
2242                 if(hir->type != RECDB_OBJECT)
2243                 {
2244                         log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
2245                         continue;
2246                 }
2247
2248                 channel = GetChannel(iter_key(it));
2249                 strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);
2250
2251                 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
2252                 flags = str ? atoi(str) : 0;
2253
2254                 info = database_get_data(hir->d.object, KEY_INFO, RECDB_QSTRING);
2255         str = database_get_data(hir->d.object, KEY_EXCEPTLEVEL, RECDB_QSTRING);
2256         exceptlevel = str ? atoi(str) : 400;
2257         
2258                 str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);
2259                 expiry = str ? strtoul(str, NULL, 0) : 0;
2260
2261                 if(channel && info)
2262                 {
2263                         if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))
2264                         {
2265                                 /* if the channel is suspended and expiry = 0 it means: channel will
2266                                    never expire ! it does NOT mean, the channel is not suspended */
2267                                 if(CHECK_SUSPENDED(cInfo) && expiry && (expiry < now))
2268                                 {
2269                                         cInfo->flags &= ~CHAN_SUSPENDED;
2270                                         spamserv_join_channel(cInfo->channel);
2271                                 }
2272                                 else if(!CHECK_SUSPENDED(cInfo))
2273                                         spamserv_join_channel(cInfo->channel);
2274                                 else
2275                                         cInfo->suspend_expiry = expiry;                 
2276                 cInfo->exceptlevel=exceptlevel;
2277                 cInfo->badwords = dict_new();
2278                 str = database_get_data(hir->d.object, KEY_LASTBADWORDID, RECDB_QSTRING);
2279                 badwordid = str ? atoi(str) : 0;
2280                 cInfo->last_badword_id = badwordid;
2281                 if ((badwords = database_get_data(hir->d.object, KEY_BADWORDS, RECDB_OBJECT)))
2282                         dict_foreach(badwords, spamserv_saxdb_read_shitlist, cInfo);
2283                         }
2284                 }
2285                 else
2286                         log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it));  
2287         }
2288
2289         return 0;
2290 }
2291
2292 static int
2293 spamserv_saxdb_write(struct saxdb_context *ctx)
2294 {
2295         dict_iterator_t it;
2296
2297         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
2298         {
2299                 struct chanInfo *cInfo = iter_data(it);
2300
2301                 saxdb_start_record(ctx, cInfo->channel->name, 1);
2302
2303                 if(cInfo->exceptions->used)
2304                         saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);
2305
2306                 if(cInfo->flags)
2307                         saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags);  
2308
2309                 saxdb_write_string(ctx, KEY_INFO, cInfo->info);         
2310         saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel);              
2311
2312                 if(cInfo->suspend_expiry)
2313                         saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);
2314
2315                 saxdb_write_int(ctx, KEY_LASTBADWORDID, cInfo->last_badword_id);
2316                 saxdb_start_record(ctx, KEY_BADWORDS, 1);
2317                 dict_iterator_t itbad;
2318                 for (itbad = dict_first(cInfo->badwords); itbad; itbad = iter_next(itbad)) {
2319                         struct badword *badword = iter_data(itbad);
2320                         saxdb_start_record(ctx, badword->id, 1);
2321                         saxdb_write_string(ctx, KEY_BADWORDID, badword->id);
2322                         saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask);
2323                         saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action);
2324                         saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered);
2325                         saxdb_end_record(ctx);
2326                 }
2327                 saxdb_end_record(ctx);
2328                 saxdb_end_record(ctx);          
2329         }
2330         return 0;
2331 }
2332
2333 static void
2334 spamserv_conf_read(void)
2335 {
2336         dict_t conf_node;
2337         const char *str; 
2338
2339         if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))
2340         {
2341                 log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);
2342                 return;
2343         }
2344
2345         str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
2346         if(str)
2347         {
2348                 spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);
2349
2350                 if(spamserv_conf.debug_channel)
2351                         spamserv_join_channel(spamserv_conf.debug_channel);
2352         }
2353         else
2354         {
2355                 spamserv_conf.debug_channel = NULL;
2356         }
2357
2358         str = database_get_data(conf_node, KEY_OPER_CHANNEL, RECDB_QSTRING);
2359         if(str)
2360         {
2361                 spamserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
2362         }
2363         else
2364         {
2365                 spamserv_conf.oper_channel = NULL;
2366         }
2367
2368         spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);
2369
2370         str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);
2371         spamserv_conf.network_rules = str ? str : NULL;
2372
2373         str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);
2374         spamserv_conf.trigger = str ? str[0] : 0;
2375
2376         str = database_get_data(conf_node, KEY_SHORT_BAN_DURATION, RECDB_QSTRING);
2377         spamserv_conf.short_ban_duration = str ? ParseInterval(str) : ParseInterval("15m");
2378
2379         str = database_get_data(conf_node, KEY_LONG_BAN_DURATION, RECDB_QSTRING);
2380         spamserv_conf.long_ban_duration = str ? ParseInterval(str) : ParseInterval("1h");
2381
2382         str = database_get_data(conf_node, KEY_GLINE_DURATION, RECDB_QSTRING);
2383         spamserv_conf.gline_duration = str ? ParseInterval(str) : ParseInterval("1h");
2384
2385         str = database_get_data(conf_node, KEY_EXCEPTION_MAX, RECDB_QSTRING);
2386         spamserv_conf.exception_max = str ? strtoul(str, NULL, 0) : 10;
2387
2388         str = database_get_data(conf_node, KEY_EXCEPTION_MIN_LEN, RECDB_QSTRING);
2389         spamserv_conf.exception_min_len = str ? strtoul(str, NULL, 0) : 4;
2390
2391         str = database_get_data(conf_node, KEY_EXCEPTION_MAX_LEN, RECDB_QSTRING);
2392         spamserv_conf.exception_max_len = str ? strtoul(str, NULL, 0) : 15;
2393
2394         str = database_get_data(conf_node, KEY_ADV_CHAN_MUST_EXIST, RECDB_QSTRING);
2395         spamserv_conf.adv_chan_must_exist = str ? enabled_string(str) : 1;
2396
2397         str = database_get_data(conf_node, KEY_STRIP_MIRC_CODES, RECDB_QSTRING);
2398         spamserv_conf.strip_mirc_codes = str ? enabled_string(str) : 0;
2399
2400         str = database_get_data(conf_node, KEY_ALLOW_MOVE_MERGE, RECDB_QSTRING);
2401         spamserv_conf.allow_move_merge = str ? enabled_string(str) : 0;
2402 }
2403
2404 static void
2405 spamserv_db_cleanup(void)
2406 {
2407         dict_iterator_t it;
2408
2409         while((it = dict_first(registered_channels_dict)))
2410         {
2411                 spamserv_unregister_channel(iter_data(it));
2412         }
2413
2414         while((it = dict_first(killed_users_dict)))
2415         {
2416                 free(iter_data(it));
2417         }
2418         
2419         dict_delete(registered_channels_dict);
2420         dict_delete(connected_users_dict);
2421         dict_delete(killed_users_dict);
2422 }
2423
2424 void
2425 init_spamserv(const char *nick)
2426 {
2427         if(!nick)
2428                 return;
2429
2430         const char *modes = conf_get_data("services/spamserv/modes", RECDB_QSTRING);    
2431         spamserv = AddLocalUser(nick, nick, NULL, "Anti Spam Services", modes);
2432         spamserv_service = service_register(spamserv);
2433         service_register(spamserv)->trigger = spamserv_conf.trigger;
2434
2435         conf_register_reload(spamserv_conf_read);
2436
2437         SS_LOG = log_register_type("SpamServ", "file:spamserv.log");    
2438
2439         registered_channels_dict = dict_new();
2440         connected_users_dict = dict_new();
2441         killed_users_dict = dict_new();
2442
2443         reg_new_user_func(spamserv_new_user_func);
2444         reg_del_user_func(spamserv_del_user_func);
2445         reg_nick_change_func(spamserv_nick_change_func);
2446         reg_join_func(spamserv_user_join);
2447         reg_part_func(spamserv_user_part);
2448
2449         timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
2450         timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
2451         timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
2452         timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
2453         timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
2454
2455         spamserv_module = module_register("SpamServ", SS_LOG, "spamserv.help", NULL);
2456         modcmd_register(spamserv_module, "REGISTER", cmd_register, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+acceptchan,+helping", NULL);
2457         modcmd_register(spamserv_module, "UNREGISTER", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+loghostmask", NULL);
2458         modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2459         modcmd_register(spamserv_module, "DELEXCEPTION", cmd_delexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2460         modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL);
2461     modcmd_register(spamserv_module, "ADDBAD", cmd_addbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2462     modcmd_register(spamserv_module, "DELBAD", cmd_delbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2463     modcmd_register(spamserv_module, "SETBAD", cmd_setbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2464     modcmd_register(spamserv_module, "LISTBAD", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2465         modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2466         modcmd_register(spamserv_module, "SET SPAMLIMIT", opt_spamlimit, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2467         modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2468         modcmd_register(spamserv_module, "SET WARNREACTION", opt_warnreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2469         modcmd_register(spamserv_module, "SET ADVSCAN", opt_advscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2470         modcmd_register(spamserv_module, "SET SPAMSCAN", opt_spamscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2471         modcmd_register(spamserv_module, "SET FLOODSCAN", opt_floodscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2472         modcmd_register(spamserv_module, "SET JOINFLOODSCAN", opt_joinflood, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2473         modcmd_register(spamserv_module, "SET SCANCHANOPS", opt_scanops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2474         modcmd_register(spamserv_module, "SET SCANVOICED", opt_scanvoiced, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2475     modcmd_register(spamserv_module, "SET EXCEPTLEVEL", opt_exceptlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2476
2477         saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write);
2478         reg_exit_func(spamserv_db_cleanup);
2479         message_register_table(msgtab);
2480         crc32_init();
2481 }