1 /* spamserv.c - anti spam service
2 * Copyright 2004 feigling
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.
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.
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.
18 * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $
30 #define SPAMSERV_CONF_NAME "services/spamserv"
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"
40 #define KEY_BADWORD_MASK "mask"
41 #define KEY_BADWORD_TRIGGERED "count"
42 #define KEY_BADWORD_ACTION "action"
43 #define KEY_BADWORDID "badwordid"
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"
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 { \
65 ss_reply(MSG_MISSING_PARAMS, argv[0]); \
67 return 0; } } while(0)
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];
75 dict_t registered_channels_dict;
76 dict_t connected_users_dict;
77 dict_t killed_users_dict;
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)
84 #define SET_SUBCMDS_SIZE 10
86 const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};
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);
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);
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." },
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'." },
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." },
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." },
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')" },
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."
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;
181 /***********************************************/
183 /***********************************************/
186 get_chanInfo(const char *channelname)
188 return dict_find(registered_channels_dict, channelname, 0);
192 spamserv_join_channel(struct chanNode *channel)
194 struct mod_chanmode change;
195 mod_chanmode_init(&change);
197 change.args[0].mode = MODE_CHANOP;
198 change.args[0].u.member = AddChannelUser(spamserv, channel);
199 mod_chanmode_announce(spamserv, channel, &change);
203 spamserv_part_channel(struct chanNode *channel, char *reason)
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);
210 static struct chanInfo*
211 spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)
213 struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));
217 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);
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);
233 spamserv_unregister_channel(struct chanInfo *cInfo)
238 dict_remove(registered_channels_dict, cInfo->channel->name);
239 free_string_list(cInfo->exceptions);
244 spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)
246 struct chanInfo *cInfo = get_chanInfo(channel->name);
252 cInfo->flags |= CHAN_SUSPENDED;
253 cInfo->suspend_expiry = expiry;
254 spamserv_part_channel(channel, reason);
258 if(CHECK_SUSPENDED(cInfo))
260 cInfo->flags &= ~CHAN_SUSPENDED;
261 cInfo->suspend_expiry = 0;
268 spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)
270 struct chanInfo *cInfo = get_chanInfo(channel->name);
276 if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))
279 snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);
281 snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);
283 spamserv_cs_unregister(user, channel, manually, reason);
287 cInfo->channel = target;
289 dict_remove(registered_channels_dict, channel->name);
290 dict_insert(registered_channels_dict, target->name, cInfo);
294 snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);
298 spamserv_join_channel(target);
299 snprintf(reason, sizeof(reason), "%s merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
302 if(!CHECK_SUSPENDED(cInfo))
303 spamserv_part_channel(channel, reason);
306 snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
308 snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
310 /*global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);*/
311 spamserv_oper_message("%s", reason);
319 spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)
321 struct chanInfo *cInfo = get_chanInfo(channel->name);
325 char global[MAXLEN], partmsg[MAXLEN];
330 snprintf(global, sizeof(global), "$X (channel %s) %s by %s.", channel->name, reason, user->handle_info->handle);
331 snprintf(partmsg, sizeof(partmsg), "%s %s by %s.", channel->name, reason, user->handle_info->handle);
334 snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name);
335 snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name);
338 snprintf(global, sizeof(global), "$X (channel %s) lost all users.", channel->name);
339 snprintf(partmsg, sizeof(partmsg), "%s lost all users.", channel->name);
343 if(!CHECK_SUSPENDED(cInfo))
344 spamserv_part_channel(channel, partmsg);
346 spamserv_unregister_channel(cInfo);
347 spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
351 /***********************************************/
353 /***********************************************/
355 static struct userInfo*
356 get_userInfo(const char *nickname)
358 return dict_find(connected_users_dict, nickname, 0);
362 spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)
364 struct spamNode *sNode = malloc(sizeof(struct spamNode));
368 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);
372 sNode->channel = channel;
373 sNode->crc32 = crc32(text);
379 struct spamNode *temp = uInfo->spam;
395 spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)
400 if(sNode == uInfo->spam)
401 uInfo->spam = sNode->next;
404 sNode->next->prev = sNode->prev;
406 sNode->prev->next = sNode->next;
412 spamserv_clear_spamNodes(struct chanNode *channel)
414 struct userInfo *uInfo;
415 struct spamNode *sNode;
418 for(i = 0; i < channel->members.used; i++)
420 if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))
422 if((sNode = uInfo->spam))
424 for(; sNode; sNode = sNode->next)
425 if(sNode->channel == channel)
429 spamserv_delete_spamNode(uInfo, sNode);
436 spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)
438 struct floodNode *fNode = malloc(sizeof(struct floodNode));
442 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);
446 fNode->channel = channel;
454 struct floodNode *temp = *uI_fNode;
470 spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)
475 if(fNode == *uI_fNode)
476 *uI_fNode = fNode->next;
479 fNode->next->prev = fNode->prev;
481 fNode->prev->next = fNode->next;
487 spamserv_create_user(struct userNode *user)
489 struct userInfo *uInfo = malloc(sizeof(struct userInfo));
490 struct killNode *kNode = dict_find(killed_users_dict, irc_ntoa(&user->ip), 0);
494 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);
499 spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);
504 uInfo->joinflood = NULL;
505 uInfo->flags = kNode ? USER_KILLED : 0;
506 uInfo->warnlevel = kNode ? kNode->warnlevel : 0;
509 dict_insert(connected_users_dict, user->nick, uInfo);
513 dict_remove(killed_users_dict, irc_ntoa(&user->ip));
519 spamserv_delete_user(struct userInfo *uInfo)
526 spamserv_delete_spamNode(uInfo, uInfo->spam);
530 spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);
533 while(uInfo->joinflood)
534 spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);
536 dict_remove(connected_users_dict, uInfo->user->nick);
541 spamserv_new_user_func(struct userNode *user)
544 spamserv_create_user(user);
548 spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))
550 struct userInfo *uInfo = get_userInfo(user->nick);
551 struct killNode *kNode;
553 if(killer == spamserv)
555 kNode = malloc(sizeof(struct killNode));
559 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for killNode - nickname %s", user->nick);
560 spamserv_delete_user(uInfo);
564 if(uInfo->warnlevel > KILL_WARNLEVEL)
565 kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;
567 kNode->warnlevel = 0;
571 dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);
574 spamserv_delete_user(uInfo);
578 spamserv_nick_change_func(struct userNode *user, const char *old_nick)
580 struct userInfo *uInfo = get_userInfo(old_nick);
582 dict_remove(connected_users_dict, old_nick);
583 dict_insert(connected_users_dict, user->nick, uInfo);
587 spamserv_user_join(struct modeNode *mNode)
589 struct chanNode *channel = mNode->channel;
590 struct userNode *user = mNode->user;
591 struct chanInfo *cInfo;
592 struct userInfo *uInfo;
593 struct floodNode *jfNode;
595 if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))
603 if(!CHECK_CHANOPS(cInfo))
605 //struct modeNode *mn = GetUserMode(channel, user);
606 //if(mn->modes & MODE_CHANOP)
608 if(check_user_level(channel, user, lvlGiveOps, 1, 0))
612 if(!CHECK_VOICED(cInfo))
614 if(check_user_level(channel, user, lvlGiveVoice, 1, 0))
618 if(cInfo->exceptlevel == 0)
620 if(cInfo->exceptlevel < 501) {
621 struct userData *uData;
622 if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
627 if(!(jfNode = uInfo->joinflood))
629 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
633 for(; jfNode; jfNode = jfNode->next)
634 if(jfNode->channel == channel)
639 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
646 if(jfNode->count > JOINFLOOD_MAX)
650 spamserv_delete_floodNode(&uInfo->joinflood, jfNode);
651 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_JOINFLOOD, spamserv_conf.network_rules);
652 spamserv_punish(channel, user, JOINFLOOD_B_DURATION, reason, 1);
661 spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
663 struct userNode *user = mn->user;
664 struct chanNode *channel = mn->channel;
665 struct userInfo *uInfo;
666 struct spamNode *sNode;
667 struct floodNode *fNode;
669 if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))
672 if((sNode = uInfo->spam))
674 for(; sNode; sNode = sNode->next)
675 if(sNode->channel == channel)
679 spamserv_delete_spamNode(uInfo, sNode);
682 if((fNode = uInfo->flood))
684 for(; fNode; fNode = fNode->next)
685 if(fNode->channel == channel)
689 spamserv_delete_floodNode(&uInfo->flood, fNode);
693 static struct badword*
694 add_badword(struct chanInfo *cInfo, const char *badword_mask, unsigned int triggered, unsigned int action, const char *id)
696 struct badword *badword;
698 badword = calloc(1, sizeof(*badword));
703 cInfo->last_badword_id++;
704 badword->id = strtab(cInfo->last_badword_id);
706 badword->id = strdup(id);
707 badword->badword_mask = strdup(badword_mask);
708 badword->triggered = triggered;
709 badword->action = action;
710 dict_insert(cInfo->badwords, badword->id, badword);
714 /***********************************************/
716 /***********************************************/
724 for(i = 0; i < 256; i++)
728 for(j = 8; j > 0; j--)
732 crc = (crc >> 1) ^ 0xEDB88320L;
745 crc32(const char *text)
747 register unsigned long crc = 0xFFFFFFFF;
748 unsigned int c, i = 0;
750 while((c = (unsigned int)text[i++]) != 0)
751 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];
753 return (crc^0xFFFFFFFF);
757 timeq_flood(UNUSED_ARG(void *data))
760 struct userInfo *uInfo;
761 struct floodNode *fNode;
763 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
765 uInfo = iter_data(it);
767 if(!(fNode = uInfo->flood))
770 for(; fNode; fNode = fNode->next)
772 if(now - fNode->time > FLOOD_EXPIRE)
774 if(!(--fNode->count))
775 spamserv_delete_floodNode(&uInfo->flood, fNode);
780 timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
784 timeq_joinflood(UNUSED_ARG(void *data))
787 struct userInfo *uInfo;
788 struct floodNode *fNode;
790 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
792 uInfo = iter_data(it);
794 if(!(fNode = uInfo->joinflood))
797 for(; fNode; fNode = fNode->next)
799 if(now - fNode->time > JOINFLOOD_EXPIRE)
801 if(!(--fNode->count))
802 spamserv_delete_floodNode(&uInfo->joinflood, fNode);
807 timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
811 timeq_adv(UNUSED_ARG(void *data))
814 struct userInfo *uInfo;
816 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
818 uInfo = iter_data(it);
820 if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)
823 uInfo->flags &= ~USER_ADV_WARNED;
827 timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
831 timeq_warnlevel(UNUSED_ARG(void *data))
834 struct userInfo *uInfo;
836 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
838 uInfo = iter_data(it);
840 if(uInfo->warnlevel > 0)
844 timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
848 timeq_kill(UNUSED_ARG(void *data))
851 struct killNode *kNode;
853 for(it = dict_first(killed_users_dict); it; it = iter_next(it))
855 kNode = iter_data(it);
857 if(kNode->time - now > KILL_EXPIRE)
861 timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
865 binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
867 struct chanInfo *cInfo = get_chanInfo(channel->name);
872 if(enabled_string(argv[1]))
874 cInfo->flags |= mask;
877 else if(disabled_string(argv[1]))
879 cInfo->flags &= ~mask;
884 spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);
890 value = (cInfo->flags & mask) ? 1 : 0;
893 spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");
905 multiple_option(char *name, char *description, enum channelinfo info, struct valueData *values, int count, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
907 struct chanInfo *cInfo = get_chanInfo(channel->name);
912 index = atoi(argv[1]);
914 if(index < 0 || index >= count)
916 spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);
918 for(index = 0; index < count; index++)
919 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
924 if(values[index].oper_only && !IsOper(user))
926 spamserv_notice(user, "SSMSG_MUST_BE_OPER");
930 cInfo->info[info] = values[index].value;
934 for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);
937 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
942 show_exceptions(struct userNode *user, struct chanInfo *cInfo)
944 struct helpfile_table table;
947 if(!cInfo->exceptions->used)
949 spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");
953 spamserv_notice(user, "SSMSG_EXCEPTION_LIST");
957 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
958 table.contents = alloca(cInfo->exceptions->used * sizeof(*table.contents));
960 for(i = 0; i < cInfo->exceptions->used; i++)
962 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
963 table.contents[table.length][0] = cInfo->exceptions->list[i];
967 table_send(spamserv, user->nick, 0, NULL, table);
973 show_memory_usage(struct userNode *user)
976 struct helpfile_table table;
977 struct chanInfo *cInfo;
978 struct userInfo *uInfo;
979 struct spamNode *sNode;
980 struct floodNode *fNode;
981 double channel_size = 0, user_size, size;
982 unsigned int spamcount = 0, floodcount = 0, i, j;
985 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
987 cInfo = iter_data(it);
989 if(!cInfo->exceptions->used)
992 for(i = 0; i < cInfo->exceptions->used; i++)
993 channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char);
996 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
998 uInfo = iter_data(it);
1000 for(sNode = uInfo->spam; sNode; sNode = sNode->next, spamcount++);
1001 for(fNode = uInfo->flood; fNode; fNode = fNode->next, floodcount++);
1002 for(fNode = uInfo->joinflood; fNode; fNode = fNode->next, floodcount++);
1005 channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);
1007 user_size = dict_size(connected_users_dict) * sizeof(struct userInfo) +
1008 dict_size(killed_users_dict) * sizeof(struct killNode) +
1009 spamcount * sizeof(struct spamNode) +
1010 floodcount * sizeof(struct floodNode);
1012 size = channel_size + user_size;
1014 ss_reply("SSMSG_STATUS_MEMORY");
1018 table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;
1019 table.contents = calloc(table.length, sizeof(char**));
1022 table.contents[0] = calloc(table.width, sizeof(char*));
1023 snprintf(buffer, sizeof(buffer), "Channel Memory Usage:");
1024 table.contents[0][0] = strdup(buffer);
1025 snprintf(buffer, sizeof(buffer), " %g Byte; ", channel_size);
1026 table.contents[0][1] = strdup(buffer);
1027 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", channel_size / 1024);
1028 table.contents[0][2] = strdup(buffer);
1029 snprintf(buffer, sizeof(buffer), "%g MegaByte", channel_size / 1024 / 1024);
1030 table.contents[0][3] = strdup(buffer);
1033 table.contents[1] = calloc(table.width, sizeof(char*));
1034 snprintf(buffer, sizeof(buffer), "User Memory Usage :");
1035 table.contents[1][0] = strdup(buffer);
1036 snprintf(buffer, sizeof(buffer), " %g Byte; ", user_size);
1037 table.contents[1][1] = strdup(buffer);
1038 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", user_size / 1024);
1039 table.contents[1][2] = strdup(buffer);
1040 snprintf(buffer, sizeof(buffer), "%g MegaByte", user_size / 1024 / 1024);
1041 table.contents[1][3] = strdup(buffer);
1043 // total memory usage
1044 table.contents[2] = calloc(table.width, sizeof(char*));
1045 snprintf(buffer, sizeof(buffer), "Total Memory Usage :");
1046 table.contents[2][0] = strdup(buffer);
1047 snprintf(buffer, sizeof(buffer), " %g Byte; ", size);
1048 table.contents[2][1] = strdup(buffer);
1049 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", size / 1024);
1050 table.contents[2][2] = strdup(buffer);
1051 snprintf(buffer, sizeof(buffer), "%g MegaByte", size / 1024 / 1024);
1052 table.contents[2][3] = strdup(buffer);
1054 table_send(spamserv, user->nick, 0, NULL, table);
1056 for(i = 0; i < table.length; i++)
1058 for(j = 0; j < table.width; j++)
1059 free((char*)table.contents[i][j]);
1061 free(table.contents[i]);
1064 free(table.contents);
1068 show_registered_channels(struct userNode *user)
1070 struct helpfile_table table;
1073 spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");
1075 if(!dict_size(registered_channels_dict))
1077 spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");
1083 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
1084 table.contents = alloca(dict_size(registered_channels_dict) * sizeof(*table.contents));
1086 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
1088 struct chanInfo *cInfo = iter_data(it);
1090 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
1091 table.contents[table.length][0] = cInfo->channel->name;
1095 table_send(spamserv, user->nick, 0, NULL, table);
1098 /***********************************************/
1100 /***********************************************/
1103 SPAMSERV_FUNC(cmd_register)
1105 struct chanInfo *cInfo;
1107 if(!channel || !channel->channel_info)
1109 ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);
1113 if(get_chanInfo(channel->name))
1115 ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);
1119 if(IsSuspended(channel->channel_info))
1121 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");
1125 if(channel == spamserv_conf.debug_channel)
1127 ss_reply("SSMSG_DEBUG_CHAN");
1131 if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))
1133 ss_reply("SSMSG_NO_REGISTER", channel->name);
1137 spamserv_join_channel(cInfo->channel);
1139 spamserv_oper_message(SSMSG_CHANNEL_REGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
1140 ss_reply("SSMSG_REG_SUCCESS", channel->name);
1146 SPAMSERV_FUNC(cmd_unregister)
1148 struct chanInfo *cInfo;
1149 struct chanData *cData;
1150 struct userData *uData;
1151 char reason[MAXLEN];
1153 if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))
1155 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1159 if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))
1161 ss_reply("SSMSG_NO_ACCESS");
1165 if(!IsHelping(user))
1167 if(IsSuspended(cData))
1169 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");
1173 if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))
1175 ss_reply("SSMSG_CONFIRM_UNREG");
1180 if(!CHECK_SUSPENDED(cInfo))
1182 snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle);
1183 spamserv_part_channel(channel, reason);
1186 spamserv_unregister_channel(cInfo);
1188 spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
1189 ss_reply("SSMSG_UNREG_SUCCESS", channel->name);
1195 SPAMSERV_FUNC(cmd_status)
1197 ss_reply("SSMSG_STATUS");
1198 ss_reply("SSMSG_STATUS_USERS", dict_size(connected_users_dict));
1199 ss_reply("SSMSG_STATUS_CHANNELS", dict_size(registered_channels_dict));
1201 if(IsOper(user) && argc > 1)
1203 if(!irccasecmp(argv[1], "memory"))
1204 show_memory_usage(user);
1205 else if(!irccasecmp(argv[1], "channels"))
1206 show_registered_channels(user);
1213 SPAMSERV_FUNC(cmd_addexception)
1215 struct chanInfo *cInfo = get_chanInfo(channel->name);
1216 struct userData *uData;
1219 if(!cInfo || !channel->channel_info)
1221 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1225 if(CHECK_SUSPENDED(cInfo))
1227 ss_reply("SSMSG_SUSPENDED", channel->name);
1231 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
1233 ss_reply("SSMSG_NO_ACCESS");
1238 return show_exceptions(user, cInfo);
1240 if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))
1242 ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);
1246 if(strlen(argv[1]) < spamserv_conf.exception_min_len)
1248 ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);
1251 else if(strlen(argv[1]) > spamserv_conf.exception_max_len)
1253 ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);
1257 for(i = 0; i < cInfo->exceptions->used; i++)
1259 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
1261 ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);
1266 string_list_append(cInfo->exceptions, strdup(argv[1]));
1267 ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);
1273 SPAMSERV_FUNC(cmd_delexception)
1275 struct chanInfo *cInfo = get_chanInfo(channel->name);
1276 struct userData *uData;
1280 if(!cInfo || !channel->channel_info)
1282 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1286 if(CHECK_SUSPENDED(cInfo))
1288 ss_reply("SSMSG_SUSPENDED", channel->name);
1292 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
1294 ss_reply("SSMSG_NO_ACCESS");
1299 return show_exceptions(user, cInfo);
1301 for(i = 0; i < cInfo->exceptions->used; i++)
1303 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
1312 ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);
1316 string_list_delete(cInfo->exceptions, i);
1317 ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);
1323 SPAMSERV_FUNC(cmd_set)
1325 struct chanInfo *cInfo = get_chanInfo(channel->name);
1326 struct svccmd *subcmd;
1327 char cmd_name[MAXLEN];
1332 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1336 if(CHECK_SUSPENDED(cInfo))
1338 ss_reply("SSMSG_SUSPENDED", channel->name);
1342 if(!check_user_level(channel,user,lvlSetters,1,0))
1344 ss_reply("SSMSG_NO_ACCESS");
1350 ss_reply("SSMSG_CHANNEL_OPTIONS");
1352 for(i = 0; i < SET_SUBCMDS_SIZE; i++)
1354 sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);
1356 if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))
1357 subcmd->command->func(user, channel, 1, argv + 1, subcmd);
1363 sprintf(cmd_name, "%s %s", cmd->name, argv[1]);
1364 subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);
1368 reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);
1372 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
1376 SPAMSERV_FUNC(opt_spamlimit)
1378 struct valueData values[] =
1380 {"Users may send the same message $b2$b times.", 'a', 0},
1381 {"Users may send the same message $b3$b times.", 'b', 0},
1382 {"Users may send the same message $b4$b times.", 'c', 0},
1383 {"Users may send the same message $b5$b times.", 'd', 0},
1384 {"Users may send the same message $b6$b times.", 'e', 0}
1387 MULTIPLE_OPTION("SpamLimit ", "SpamLimit", ci_SpamLimit);
1391 SPAMSERV_FUNC(opt_advreaction)
1393 struct valueData values[] =
1395 {"Kick on disallowed advertising.", 'k', 0},
1396 {"Kickban on disallowed advertising.", 'b', 0},
1397 {"Short timed ban on disallowed advertising.", 's', 0},
1398 {"Long timed ban on disallowed advertising.", 'l', 0},
1399 {"Kill on disallowed advertising.", 'd', 1}
1402 MULTIPLE_OPTION("AdvReaction ", "AdvReaction", ci_AdvReaction);
1406 SPAMSERV_FUNC(opt_warnreaction)
1408 struct valueData values[] =
1410 {"Kick after warning.", 'k', 0},
1411 {"Kickban after warning.", 'b', 0},
1412 {"Short timed ban after warning.", 's', 0},
1413 {"Long timed ban after warning.", 'l', 0},
1414 {"Kill after warning.", 'd', 1}
1417 MULTIPLE_OPTION("WarnReaction ", "WarnReaction", ci_WarnReaction);
1421 SPAMSERV_FUNC(opt_advscan)
1423 BINARY_OPTION("AdvScan ", CHAN_ADV_SCAN);
1427 SPAMSERV_FUNC(opt_spamscan)
1429 BINARY_OPTION("SpamScan ", CHAN_SPAMSCAN);
1433 SPAMSERV_FUNC(opt_floodscan)
1435 BINARY_OPTION("FloodScan ", CHAN_FLOODSCAN);
1439 SPAMSERV_FUNC(opt_joinflood)
1441 BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);
1445 SPAMSERV_FUNC(opt_scanops)
1447 BINARY_OPTION("ScanChanOps ", CHAN_SCAN_CHANOPS);
1451 SPAMSERV_FUNC(opt_scanvoiced)
1453 BINARY_OPTION("ScanVoiced ", CHAN_SCAN_VOICED);
1457 SPAMSERV_FUNC(opt_exceptlevel)
1459 struct chanInfo *cInfo = get_chanInfo(channel->name);
1460 struct userData *uData;
1464 index = atoi(argv[1]);
1465 if(index < 1 || index > 501)
1467 spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");
1470 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))
1472 ss_reply("SSMSG_NO_ACCESS");
1475 cInfo->exceptlevel=index;
1477 index=cInfo->exceptlevel;
1479 spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel ", index);
1483 static SPAMSERV_FUNC(cmd_addbad)
1485 struct chanInfo *cInfo = get_chanInfo(channel->name);
1486 struct userData *uData;
1490 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1494 if(CHECK_SUSPENDED(cInfo))
1496 ss_reply("SSMSG_SUSPENDED", channel->name);
1500 if(!check_user_level(channel,user,lvlSetters,1,0))
1502 ss_reply("SSMSG_NO_ACCESS");
1507 char *mask = unsplit_string(argv + 1, argc - 1, NULL);
1508 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1509 struct badword *badword = iter_data(it);
1510 if(match_ircglob(mask,badword->badword_mask)) {
1511 reply("SSMSG_BADWORD_ALREADY_ADDED", mask, badword->id);
1516 struct badword *new_badword = add_badword(cInfo, mask, 0, BADACTION_KICK, NULL);
1517 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1518 struct badword *badword = iter_data(it);
1519 if(match_ircglob(badword->badword_mask, new_badword->badword_mask) && badword != new_badword) {
1520 dict_remove(cInfo->badwords, badword->id);
1524 reply("SSMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id);
1528 static SPAMSERV_FUNC(cmd_delbad)
1530 struct chanInfo *cInfo = get_chanInfo(channel->name);
1531 struct userData *uData;
1536 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1540 if(CHECK_SUSPENDED(cInfo))
1542 ss_reply("SSMSG_SUSPENDED", channel->name);
1546 if(!check_user_level(channel,user,lvlSetters,1,0))
1548 ss_reply("SSMSG_NO_ACCESS");
1552 for (n=1; n<argc; n++) {
1553 struct badword *badword = dict_find(cInfo->badwords, argv[n], NULL);
1555 reply("SSMSG_BADWORD_NOT_FOUND", argv[n]);
1558 reply("SSMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);
1559 dict_remove(cInfo->badwords, argv[n]);
1564 static SPAMSERV_FUNC(cmd_setbad)
1566 struct chanInfo *cInfo = get_chanInfo(channel->name);
1567 struct userData *uData;
1568 struct badword *badword;
1572 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1576 if(CHECK_SUSPENDED(cInfo))
1578 ss_reply("SSMSG_SUSPENDED", channel->name);
1582 if(!check_user_level(channel,user,lvlSetters,1,0))
1584 ss_reply("SSMSG_NO_ACCESS");
1588 if ((badword = dict_find(cInfo->badwords, argv[1], NULL))) {
1591 char *setting = argv[2];
1592 char *value = argv[3];
1593 for( ii = 0; setting[ ii ]; ii++)
1594 setting[ ii ] = toupper( setting[ ii ] );
1595 for( ii = 0; value[ ii ]; ii++)
1596 value[ ii ] = toupper( value[ ii ] );
1597 if(!strcmp("MASK",setting)) {
1598 free(badword->badword_mask);
1599 badword->badword_mask = strdup(argv[3]);
1600 badword->triggered = 0;
1601 reply("SSMSG_BADWORD_SET_DONE");
1603 else if(!strcmp("ACTION",setting)) {
1604 if (!strcmp("1",value) || !strcmp("KICK",value)) {
1605 badword->action = BADACTION_KICK;
1606 reply("SSMSG_BADWORD_SET_DONE");
1607 } else if (!strcmp("2",value) || !strcmp("BAN",value)) {
1608 badword->action = BADACTION_BAN;
1609 reply("SSMSG_BADWORD_SET_DONE");
1610 } else if (!strcmp("3",value) || !strcmp("KILL",value)) {
1612 badword->action = BADACTION_KILL;
1613 reply("SSMSG_BADWORD_SET_DONE");
1615 reply("SSMSG_NO_ACCESS");
1617 } else if (!strcmp("4",value) || !strcmp("GLINE",value)) {
1619 badword->action = BADACTION_GLINE;
1620 reply("SSMSG_BADWORD_SET_DONE");
1622 reply("SSMSG_NO_ACCESS");
1625 reply("SSMSG_BADWORD_SET_INVALID", setting);
1628 reply("SSMSG_BADWORD_SETTING_INVALID", setting);
1632 reply("SSMSG_BADWORD_SET", badword->id);
1633 reply("SSMSG_BADWORD_SET_MASK", badword->badword_mask);
1634 switch(badword->action) {
1635 case BADACTION_KICK:
1636 reply("SSMSG_BADWORD_SET_ACTION", "KICK");
1639 reply("SSMSG_BADWORD_SET_ACTION", "BAN");
1641 case BADACTION_KILL:
1642 reply("SSMSG_BADWORD_SET_ACTION", "KILL");
1644 case BADACTION_GLINE:
1645 reply("SSMSG_BADWORD_SET_ACTION", "GLINE");
1648 reply("SSMSG_BADWORD_SET_ACTION", "*undef*");
1652 reply("SSMSG_BADWORD_NOT_FOUND", argv[1]);
1659 ss_badwords_sort(const void *pa, const void *pb)
1661 struct badword *a = *(struct badword**)pa;
1662 struct badword *b = *(struct badword**)pb;
1664 return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0);
1667 static SPAMSERV_FUNC(cmd_listbad)
1669 struct helpfile_table tbl;
1670 struct chanInfo *cInfo = get_chanInfo(channel->name);
1671 struct userData *uData;
1672 struct badword **badwords;
1673 unsigned int count = 0, ii = 0;
1677 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
1681 if(CHECK_SUSPENDED(cInfo))
1683 ss_reply("SSMSG_SUSPENDED", channel->name);
1687 if(!check_user_level(channel,user,lvlSetters,1,0))
1689 ss_reply("SSMSG_NO_ACCESS");
1694 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1698 tbl.length = count+1;
1701 tbl.flags = TABLE_NO_FREE;
1702 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
1703 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
1704 tbl.contents[0][0] = "#";
1705 tbl.contents[0][1] = "Badword";
1706 tbl.contents[0][2] = "Action";
1707 tbl.contents[0][3] = "(Triggered)";
1710 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
1712 free(tbl.contents[0]);
1716 badwords = alloca(count * sizeof(badwords[0]));
1717 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
1718 struct badword *bw = iter_data(it);
1719 badwords[ii++] = bw;
1722 qsort(badwords, count, sizeof(badwords[0]), ss_badwords_sort);
1723 for (ii = 1; ii <= count; ii++) {
1724 struct badword *bw = badwords[ii-1];
1725 tbl.contents[ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
1726 tbl.contents[ii][0] = strdup(bw->id);
1727 tbl.contents[ii][1] = strdup(bw->badword_mask);
1728 switch(bw->action) {
1729 case BADACTION_KICK:
1730 tbl.contents[ii][2] = "KICK";
1733 tbl.contents[ii][2] = "BAN";
1735 case BADACTION_KILL:
1736 tbl.contents[ii][2] = "KILL";
1738 case BADACTION_GLINE:
1739 tbl.contents[ii][2] = "GLINE";
1742 tbl.contents[ii][2] = "*undef*";
1744 tbl.contents[ii][3] = strtab(bw->triggered);
1747 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
1748 for(ii = 1; ii < tbl.length; ++ii)
1750 free(tbl.contents[ii]);
1752 free(tbl.contents[0]);
1758 to_lower(char *message)
1760 unsigned int i, diff = 'a' - 'A';
1762 for(i = 0; i < strlen(message); i++)
1764 if((message[i] >= 'A') && (message[i] <= 'Z'))
1765 message[i] = message[i] + diff;
1770 strip_mirc_codes(char *text)
1772 // taken from xchat and modified
1773 int nc = 0, i = 0, col = 0, len = strlen(text);
1774 static char new_str[MAXLEN];
1778 if((col && isdigit(*text) && nc < 2) ||
1779 (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))
1818 is_in_exception_list(struct chanInfo *cInfo, char *message)
1822 for(i = 0; i < cInfo->exceptions->used; i++)
1823 if(strstr(message, cInfo->exceptions->list[i]))
1830 check_advertising(struct chanInfo *cInfo, char *message)
1834 if(spamserv_conf.strip_mirc_codes)
1835 message = strip_mirc_codes(message);
1837 if(is_in_exception_list(cInfo, message))
1840 while(message[i] != 0)
1842 if(message[i] == '#')
1844 char channelname[CHANNELLEN];
1847 if(!spamserv_conf.adv_chan_must_exist)
1850 /* only return 1, if the channel does exist */
1852 while((message[i] != 0) && (message[i] != ' '))
1854 channelname[j] = message[i];
1859 channelname[j] = '\0';
1861 if(GetChannel(channelname))
1864 else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))
1866 else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))
1868 else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))
1877 struct banData *add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason);
1880 spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)
1884 struct mod_chanmode change;
1885 char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
1887 sanitize_ircmask(hostmask);
1890 add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);
1892 mod_chanmode_init(&change);
1894 change.args[0].mode = MODE_BAN;
1895 change.args[0].u.hostmask = hostmask;
1896 mod_chanmode_announce(spamserv, channel, &change);
1900 spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);
1903 spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);
1905 KickChannelUser(user, channel, spamserv, reason);
1909 spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct badword *badword)
1912 char *reason = SSMSG_BADWORD_DETECTED;
1913 char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };
1914 switch(badword->action) {
1916 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
1917 sanitize_ircmask(hostmask);
1918 if(chan->channel_info) {
1919 //registered channel
1920 add_channel_ban(chan->channel_info, hostmask, spamserv->nick, now, now, now + spamserv_conf.long_ban_duration, reason);
1922 struct mod_chanmode change;
1923 mod_chanmode_init(&change);
1925 change.args[0].mode = MODE_BAN;
1926 change.args[0].u.hostmask = hostmask;
1927 mod_chanmode_announce(spamserv, chan, &change);
1930 case BADACTION_KICK:
1931 if(GetUserMode(chan, user))
1932 KickChannelUser(user, chan, spamserv, reason);
1934 case BADACTION_KILL:
1935 DelUser(user, spamserv, 1, reason);
1937 case BADACTION_GLINE:
1938 irc_ntop(mask + 2, sizeof(mask) - 2, &user->ip);
1939 gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
1948 spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)
1950 struct chanInfo *cInfo;
1951 struct userInfo *uInfo;
1952 struct spamNode *sNode;
1953 struct floodNode *fNode;
1954 unsigned int violation = 0;
1955 char reason[MAXLEN];
1957 /* make sure: spamserv is not disabled; srvx is running; spamserv is in the chan; chan is regged, user does exist */
1958 if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick)))
1966 if(!CHECK_CHANOPS(cInfo))
1968 struct modeNode *mn = GetUserMode(channel, user);
1969 if(mn && mn->modes & MODE_CHANOP)
1971 //if(check_user_level(channel, user, lvlGiveOps, 1, 0))
1975 if(!CHECK_VOICED(cInfo))
1977 struct modeNode *mn = GetUserMode(channel, user);
1978 if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))
1982 if(cInfo->exceptlevel == 0)
1984 if(cInfo->exceptlevel < 501) {
1985 struct userData *uData;
1986 if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
1993 if(CHECK_SPAM(cInfo))
1995 if(!(sNode = uInfo->spam))
1997 spamserv_create_spamNode(channel, uInfo, text);
2001 for(; sNode; sNode = sNode->next)
2002 if(sNode->channel == channel)
2007 spamserv_create_spamNode(channel, uInfo, text);
2011 unsigned long crc = crc32(text);
2013 if(crc == sNode->crc32)
2015 unsigned int spamlimit = 2;
2018 switch(cInfo->info[ci_SpamLimit])
2020 case 'a': spamlimit = 2; break;
2021 case 'b': spamlimit = 3; break;
2022 case 'c': spamlimit = 4; break;
2023 case 'd': spamlimit = 5; break;
2024 case 'e': spamlimit = 6; break;
2027 if(sNode->count == spamlimit)
2029 uInfo->warnlevel += SPAM_WARNLEVEL;
2031 if(uInfo->warnlevel < MAX_WARNLEVEL)
2032 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules);
2034 else if(sNode->count > spamlimit)
2036 switch(cInfo->info[ci_WarnReaction])
2038 case 'k': uInfo->flags |= USER_KICK; break;
2039 case 'b': uInfo->flags |= USER_KICKBAN; break;
2040 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
2041 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
2042 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
2045 spamserv_delete_spamNode(uInfo, sNode);
2046 uInfo->warnlevel += SPAM_WARNLEVEL;
2059 if(CHECK_FLOOD(cInfo))
2061 if(!(fNode = uInfo->flood))
2063 spamserv_create_floodNode(channel, user, &uInfo->flood);
2067 for(; fNode; fNode = fNode->next)
2068 if(fNode->channel == channel)
2073 spamserv_create_floodNode(channel, user, &uInfo->flood);
2077 if(((now - fNode->time) < FLOOD_EXPIRE))
2081 if(fNode->count == FLOOD_MAX_LINES - 1)
2083 uInfo->warnlevel += FLOOD_WARNLEVEL;
2085 if(uInfo->warnlevel < MAX_WARNLEVEL)
2086 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules);
2088 else if(fNode->count > FLOOD_MAX_LINES)
2090 switch(cInfo->info[ci_WarnReaction])
2092 case 'k': uInfo->flags |= USER_KICK; break;
2093 case 'b': uInfo->flags |= USER_KICKBAN; break;
2094 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
2095 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
2096 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
2099 spamserv_delete_floodNode(&uInfo->flood, fNode);
2100 uInfo->warnlevel += FLOOD_WARNLEVEL;
2112 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
2113 struct badword *badword = iter_data(it);
2114 if(match_ircglob(text, badword->badword_mask)) {
2115 spamserv_detected_badword(user, channel, badword);
2119 if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))
2121 if(CHECK_ADV_WARNED(uInfo))
2123 switch(cInfo->info[ci_AdvReaction])
2125 case 'k': uInfo->flags |= USER_KICK; break;
2126 case 'b': uInfo->flags |= USER_KICKBAN; break;
2127 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
2128 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
2129 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
2132 uInfo->warnlevel += ADV_WARNLEVEL;
2137 uInfo->flags |= USER_ADV_WARNED;
2138 uInfo->lastadv = now;
2139 uInfo->warnlevel += ADV_WARNLEVEL;
2141 if(uInfo->warnlevel < MAX_WARNLEVEL)
2142 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules);
2146 if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)
2148 uInfo->flags |= USER_WARNED;
2149 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules);
2150 irc_notice(spamserv, user->numeric, reason);
2151 irc_privmsg(spamserv, user->numeric, reason);
2153 else if(uInfo->warnlevel > MAX_WARNLEVEL)
2155 if(CHECK_KILLED(uInfo))
2156 uInfo->flags |= USER_GLINE;
2158 uInfo->flags |= USER_KILL;
2168 case 1: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules); break;
2169 case 2: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules); break;
2170 case 3: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules); break;
2171 default: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules); break;
2174 if(CHECK_GLINE(uInfo))
2176 int size = strlen(user->hostname) + 3;
2177 char *mask = alloca(size);
2178 snprintf(mask, size, "*@%s", user->hostname);
2179 gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
2180 spamserv_debug(SSMSG_DEBUG_GLINE, user->nick, user->hostname, channel->name);
2182 else if(CHECK_KILL(uInfo))
2184 DelUser(user, spamserv, 1, reason);
2185 spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);
2187 else if(CHECK_LONG_TBAN(uInfo))
2189 spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);
2191 else if(CHECK_SHORT_TBAN(uInfo))
2193 spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);
2195 else if(CHECK_KICKBAN(uInfo))
2197 spamserv_punish(channel, user, 0, reason, 1);
2199 else if(CHECK_KICK(uInfo))
2201 spamserv_punish(channel, user, 0, reason, 0);
2206 spamserv_saxdb_read_shitlist(const char *name, void *data, void *extra)
2208 struct record_data *rd = data;
2209 struct chanInfo *chan = extra;
2211 char *triggered, *action;
2213 if (rd->type == RECDB_OBJECT) {
2214 dict_t obj = GET_RECORD_OBJECT(rd);
2215 /* new style structure */
2216 badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);
2217 triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);
2218 action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);
2220 add_badword(chan, badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);
2226 spamserv_saxdb_read(struct dict *database)
2229 struct dict *badwords;
2230 struct record_data *hir;
2231 struct chanNode *channel;
2232 struct chanInfo *cInfo;
2233 struct string_list *strlist;
2234 unsigned int flags,exceptlevel,badwordid;
2236 unsigned long expiry;
2238 for(it = dict_first(database); it; it = iter_next(it))
2240 hir = iter_data(it);
2242 if(hir->type != RECDB_OBJECT)
2244 log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
2248 channel = GetChannel(iter_key(it));
2249 strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);
2251 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
2252 flags = str ? atoi(str) : 0;
2254 info = database_get_data(hir->d.object, KEY_INFO, RECDB_QSTRING);
2255 str = database_get_data(hir->d.object, KEY_EXCEPTLEVEL, RECDB_QSTRING);
2256 exceptlevel = str ? atoi(str) : 400;
2258 str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);
2259 expiry = str ? strtoul(str, NULL, 0) : 0;
2263 if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))
2265 /* if the channel is suspended and expiry = 0 it means: channel will
2266 never expire ! it does NOT mean, the channel is not suspended */
2267 if(CHECK_SUSPENDED(cInfo) && expiry && (expiry < now))
2269 cInfo->flags &= ~CHAN_SUSPENDED;
2270 spamserv_join_channel(cInfo->channel);
2272 else if(!CHECK_SUSPENDED(cInfo))
2273 spamserv_join_channel(cInfo->channel);
2275 cInfo->suspend_expiry = expiry;
2276 cInfo->exceptlevel=exceptlevel;
2277 cInfo->badwords = dict_new();
2278 str = database_get_data(hir->d.object, KEY_LASTBADWORDID, RECDB_QSTRING);
2279 badwordid = str ? atoi(str) : 0;
2280 cInfo->last_badword_id = badwordid;
2281 if ((badwords = database_get_data(hir->d.object, KEY_BADWORDS, RECDB_OBJECT)))
2282 dict_foreach(badwords, spamserv_saxdb_read_shitlist, cInfo);
2286 log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it));
2293 spamserv_saxdb_write(struct saxdb_context *ctx)
2297 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
2299 struct chanInfo *cInfo = iter_data(it);
2301 saxdb_start_record(ctx, cInfo->channel->name, 1);
2303 if(cInfo->exceptions->used)
2304 saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);
2307 saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags);
2309 saxdb_write_string(ctx, KEY_INFO, cInfo->info);
2310 saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel);
2312 if(cInfo->suspend_expiry)
2313 saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);
2315 saxdb_write_int(ctx, KEY_LASTBADWORDID, cInfo->last_badword_id);
2316 saxdb_start_record(ctx, KEY_BADWORDS, 1);
2317 dict_iterator_t itbad;
2318 for (itbad = dict_first(cInfo->badwords); itbad; itbad = iter_next(itbad)) {
2319 struct badword *badword = iter_data(itbad);
2320 saxdb_start_record(ctx, badword->id, 1);
2321 saxdb_write_string(ctx, KEY_BADWORDID, badword->id);
2322 saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask);
2323 saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action);
2324 saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered);
2325 saxdb_end_record(ctx);
2327 saxdb_end_record(ctx);
2328 saxdb_end_record(ctx);
2334 spamserv_conf_read(void)
2339 if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))
2341 log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);
2345 str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
2348 spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);
2350 if(spamserv_conf.debug_channel)
2351 spamserv_join_channel(spamserv_conf.debug_channel);
2355 spamserv_conf.debug_channel = NULL;
2358 str = database_get_data(conf_node, KEY_OPER_CHANNEL, RECDB_QSTRING);
2361 spamserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
2365 spamserv_conf.oper_channel = NULL;
2368 spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);
2370 str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);
2371 spamserv_conf.network_rules = str ? str : NULL;
2373 str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);
2374 spamserv_conf.trigger = str ? str[0] : 0;
2376 str = database_get_data(conf_node, KEY_SHORT_BAN_DURATION, RECDB_QSTRING);
2377 spamserv_conf.short_ban_duration = str ? ParseInterval(str) : ParseInterval("15m");
2379 str = database_get_data(conf_node, KEY_LONG_BAN_DURATION, RECDB_QSTRING);
2380 spamserv_conf.long_ban_duration = str ? ParseInterval(str) : ParseInterval("1h");
2382 str = database_get_data(conf_node, KEY_GLINE_DURATION, RECDB_QSTRING);
2383 spamserv_conf.gline_duration = str ? ParseInterval(str) : ParseInterval("1h");
2385 str = database_get_data(conf_node, KEY_EXCEPTION_MAX, RECDB_QSTRING);
2386 spamserv_conf.exception_max = str ? strtoul(str, NULL, 0) : 10;
2388 str = database_get_data(conf_node, KEY_EXCEPTION_MIN_LEN, RECDB_QSTRING);
2389 spamserv_conf.exception_min_len = str ? strtoul(str, NULL, 0) : 4;
2391 str = database_get_data(conf_node, KEY_EXCEPTION_MAX_LEN, RECDB_QSTRING);
2392 spamserv_conf.exception_max_len = str ? strtoul(str, NULL, 0) : 15;
2394 str = database_get_data(conf_node, KEY_ADV_CHAN_MUST_EXIST, RECDB_QSTRING);
2395 spamserv_conf.adv_chan_must_exist = str ? enabled_string(str) : 1;
2397 str = database_get_data(conf_node, KEY_STRIP_MIRC_CODES, RECDB_QSTRING);
2398 spamserv_conf.strip_mirc_codes = str ? enabled_string(str) : 0;
2400 str = database_get_data(conf_node, KEY_ALLOW_MOVE_MERGE, RECDB_QSTRING);
2401 spamserv_conf.allow_move_merge = str ? enabled_string(str) : 0;
2405 spamserv_db_cleanup(void)
2409 while((it = dict_first(registered_channels_dict)))
2411 spamserv_unregister_channel(iter_data(it));
2414 while((it = dict_first(killed_users_dict)))
2416 free(iter_data(it));
2419 dict_delete(registered_channels_dict);
2420 dict_delete(connected_users_dict);
2421 dict_delete(killed_users_dict);
2425 init_spamserv(const char *nick)
2430 const char *modes = conf_get_data("services/spamserv/modes", RECDB_QSTRING);
2431 spamserv = AddLocalUser(nick, nick, NULL, "Anti Spam Services", modes);
2432 spamserv_service = service_register(spamserv);
2433 service_register(spamserv)->trigger = spamserv_conf.trigger;
2435 conf_register_reload(spamserv_conf_read);
2437 SS_LOG = log_register_type("SpamServ", "file:spamserv.log");
2439 registered_channels_dict = dict_new();
2440 connected_users_dict = dict_new();
2441 killed_users_dict = dict_new();
2443 reg_new_user_func(spamserv_new_user_func);
2444 reg_del_user_func(spamserv_del_user_func);
2445 reg_nick_change_func(spamserv_nick_change_func);
2446 reg_join_func(spamserv_user_join);
2447 reg_part_func(spamserv_user_part);
2449 timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
2450 timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
2451 timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
2452 timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
2453 timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
2455 spamserv_module = module_register("SpamServ", SS_LOG, "spamserv.help", NULL);
2456 modcmd_register(spamserv_module, "REGISTER", cmd_register, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+acceptchan,+helping", NULL);
2457 modcmd_register(spamserv_module, "UNREGISTER", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+loghostmask", NULL);
2458 modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2459 modcmd_register(spamserv_module, "DELEXCEPTION", cmd_delexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2460 modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL);
2461 modcmd_register(spamserv_module, "ADDBAD", cmd_addbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2462 modcmd_register(spamserv_module, "DELBAD", cmd_delbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2463 modcmd_register(spamserv_module, "SETBAD", cmd_setbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2464 modcmd_register(spamserv_module, "LISTBAD", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2465 modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2466 modcmd_register(spamserv_module, "SET SPAMLIMIT", opt_spamlimit, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2467 modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2468 modcmd_register(spamserv_module, "SET WARNREACTION", opt_warnreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2469 modcmd_register(spamserv_module, "SET ADVSCAN", opt_advscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2470 modcmd_register(spamserv_module, "SET SPAMSCAN", opt_spamscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2471 modcmd_register(spamserv_module, "SET FLOODSCAN", opt_floodscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2472 modcmd_register(spamserv_module, "SET JOINFLOODSCAN", opt_joinflood, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2473 modcmd_register(spamserv_module, "SET SCANCHANOPS", opt_scanops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2474 modcmd_register(spamserv_module, "SET SCANVOICED", opt_scanvoiced, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2475 modcmd_register(spamserv_module, "SET EXCEPTLEVEL", opt_exceptlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
2477 saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write);
2478 reg_exit_func(spamserv_db_cleanup);
2479 message_register_table(msgtab);