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