2 /* spamserv.c - anti spam service
\r
3 * Copyright 2004 feigling
\r
5 * This program is free software; you can redistribute it and/or modify
\r
6 * it under the terms of the GNU General Public License as published by
\r
7 * the Free Software Foundation; either version 2 of the License, or
\r
8 * (at your option) any later version. Important limitations are
\r
9 * listed in the COPYING file that accompanies this software.
\r
11 * This program is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program; if not, email srvx-maintainers@srvx.net.
\r
19 * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $
\r
23 #include "spamserv.h"
\r
24 #include "chanserv.h"
\r
31 #define SPAMSERV_CONF_NAME "services/spamserv"
\r
33 #define KEY_EXCEPTIONS "exceptions"
\r
34 #define KEY_FLAGS "flags"
\r
35 #define KEY_INFO "info"
\r
36 #define KEY_EXCEPTLEVEL "exceptlevel"
\r
37 #define KEY_EXPIRY "expiry"
\r
38 #define KEY_LASTBADWORDID "last_badword_id"
\r
39 #define KEY_BADWORDS "badwords"
\r
41 #define KEY_BADWORD_MASK "mask"
\r
42 #define KEY_BADWORD_TRIGGERED "count"
\r
43 #define KEY_BADWORD_ACTION "action"
\r
44 #define KEY_BADWORDID "badwordid"
\r
46 #define KEY_DEBUG_CHANNEL "debug_channel"
\r
47 #define KEY_GLOBAL_EXCEPTIONS "global_exceptions"
\r
48 #define KEY_NETWORK_RULES "network_rules"
\r
49 #define KEY_TRIGGER "trigger"
\r
50 #define KEY_SHORT_BAN_DURATION "short_ban_duration"
\r
51 #define KEY_LONG_BAN_DURATION "long_ban_duration"
\r
52 #define KEY_GLINE_DURATION "gline_duration"
\r
53 #define KEY_EXCEPTION_MAX "exception_max"
\r
54 #define KEY_EXCEPTION_MIN_LEN "exception_min_len"
\r
55 #define KEY_EXCEPTION_MAX_LEN "exception_max_len"
\r
56 #define KEY_ADV_CHAN_MUST_EXIST "adv_chan_must_exist"
\r
57 #define KEY_STRIP_MIRC_CODES "strip_mirc_codes"
\r
58 #define KEY_ALLOW_MOVE_MERGE "allow_move_merge"
\r
60 #define SPAMSERV_FUNC(NAME) MODCMD_FUNC(NAME)
\r
61 #define SPAMSERV_SYNTAX() svccmd_send_help(user, spamserv, cmd)
\r
62 #define SPAMSERV_MIN_PARMS(N) do { \
\r
65 ss_reply(MSG_MISSING_PARAMS, argv[0]); \
\r
66 SPAMSERV_SYNTAX(); \
\r
67 return 0; } } while(0)
\r
69 struct userNode *spamserv;
\r
70 static struct module *spamserv_module;
\r
71 static struct service *spamserv_service;
\r
72 static struct log_type *SS_LOG;
\r
73 static unsigned long crc_table[256];
\r
75 dict_t registered_channels_dict;
\r
76 dict_t connected_users_dict;
\r
77 dict_t killed_users_dict;
\r
79 #define spamserv_notice(target, format...) send_message(target , spamserv , ## format)
\r
80 #define spamserv_debug(format...) do { if(spamserv_conf.debug_channel) send_channel_message(spamserv_conf.debug_channel , spamserv , ## format); } while(0)
\r
81 #define ss_reply(format...) send_message(user , spamserv , ## format)
\r
83 #define SET_SUBCMDS_SIZE 10
\r
85 const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};
\r
87 static void spamserv_clear_spamNodes(struct chanNode *channel);
\r
88 static void spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban);
\r
89 static unsigned long crc32(const char *text);
\r
91 #define BINARY_OPTION(arguments...) return binary_option(arguments, user, channel, argc, argv);
\r
92 #define MULTIPLE_OPTION(arguments...) return multiple_option(arguments, values, ArrayLength(values), user, channel, argc, argv);
\r
94 static const struct message_entry msgtab[] = {
\r
95 { "SSMSG_CHANNEL_OPTIONS", "Channel Options:" },
\r
96 { "SSMSG_STRING_VALUE", "$b%s$b%s" },
\r
97 { "SSMSG_NUMERIC_VALUE", "$b%s$b%d - %s" },
\r
98 { "SSMSG_EASYNUMERIC_VALUE", "$b%s$b%d" },
\r
99 { "SSMSG_INVALID_NUM_SET", "$b'%d'$b is an invalid %s setting." },
\r
100 { "SSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
\r
101 { "SSMSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },
\r
103 { "SSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$X$b." },
\r
104 { "SSMSG_NOT_REGISTERED_CS", "$b%s$b has not been registered with $b$C$b." },
\r
105 { "SSMSG_ALREADY_REGISTERED", "$b%s$b is already registered." },
\r
106 { "SSMSG_DEBUG_CHAN", "You may not register the debug channel." },
\r
107 { "SSMSG_SUSPENDED_CS", "$b$C$b access to $b%s$b has been temporarily suspended, thus you can't %s it." },
\r
108 { "SSMSG_SUSPENDED", "$b$X$b access to $b%s$b has been temporarily suspended." },
\r
109 { "SSMSG_NO_REGISTER", "Due to an error it was not possible to register $b%s$b." },
\r
110 { "SSMSG_REG_SUCCESS", "Channel $b%s$b registered." },
\r
111 { "SSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
\r
112 { "SSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
\r
113 { "SSMSG_MUST_BE_OPER", "You must be an irc operator to set this option." },
\r
114 { "SSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must append 'CONFIRM' to the end of your command. For example, 'unregister CONFIRM'." },
\r
116 { "SSMSG_NO_EXCEPTIONS", "No words found in the exception list." },
\r
117 { "SSMSG_NO_SUCH_EXCEPTION", "Word $b%s$b not found in the exception list." },
\r
118 { "SSMSG_EXCEPTION_LIST", "The following words are in the exception list:" },
\r
119 { "SSMSG_EXCEPTION_ADDED", "Word $b%s$b added to the exception list." },
\r
120 { "SSMSG_EXCEPTION_DELETED", "Word $b%s$b deleted from the exception list." },
\r
121 { "SSMSG_EXCEPTION_IN_LIST", "The word $b%s$b is already in the exception list." },
\r
122 { "SSMSG_EXCEPTION_MAX", "The exception list has reached the maximum exceptions (max %lu). Delete a word to add another one." },
\r
123 { "SSMSG_EXCEPTION_TOO_SHORT", "The word must be at least %lu characters long." },
\r
124 { "SSMSG_EXCEPTION_TOO_LONG", "The word may not be longer than %lu characters." },
\r
126 { "SSMSG_STATUS", "$bStatus:$b" },
\r
127 { "SSMSG_STATUS_USERS", "Total Users Online: %u" },
\r
128 { "SSMSG_STATUS_CHANNELS", "Registered Channels: %u" },
\r
129 { "SSMSG_STATUS_MEMORY", "$bMemory Information:$b" },
\r
130 { "SSMSG_STATUS_CHANNEL_LIST", "$bRegistered Channels:$b" },
\r
131 { "SSMSG_STATUS_NO_CHANNEL", "No channels registered." },
\r
133 { "SSMSG_BADWORD_ALREADY_ADDED", "$b%s$b is already added. (ID: %s)" },
\r
134 { "SSMSG_BADWORD_ADDED", "added '$b%s$b' to the badword list with ID %s." },
\r
135 { "SSMSG_BADWORD_SET_DONE", "Done." },
\r
136 { "SSMSG_BADWORD_SET_INVALID", "Invalid Option for setting %s" },
\r
137 { "SSMSG_BADWORD_SET", "Settings for BadWord entry $b%s$b" },
\r
138 { "SSMSG_BADWORD_SET_MASK", "$bMASK$b: %s" },
\r
139 { "SSMSG_BADWORD_SET_ACTION", "$bACTION$b: %s" },
\r
140 { "SSMSG_BADWORD_NOT_FOUND", "badword with ID %s does not exist." },
\r
141 { "SSMSG_BADWORD_REMOVED", "badword ID $b%s$b has been removed (mask: '%s')" },
\r
145 #define SSMSG_DEBUG_KICK "Kicked user $b%s$b from $b%s$b, reason: %s"
\r
146 #define SSMSG_DEBUG_BAN "Banned user $b%s$b from $b%s$b, reason: %s"
\r
147 #define SSMSG_DEBUG_KILL "Killed user $b%s$b, last violation in $b%s$b"
\r
148 #define SSMSG_DEBUG_GLINE "Glined user $b%s$b, host $b%s$b, last violation in $b%s$b"
\r
149 #define SSMSG_DEBUG_RECONNECT "Killed user $b%s$b reconnected to the network"
\r
150 #define SSMSG_SPAM "Spamming"
\r
151 #define SSMSG_FLOOD "Flooding the channel/network"
\r
152 #define SSMSG_ADV "Advertising"
\r
153 #define SSMSG_JOINFLOOD "Join flooding the channel"
\r
154 #define SSMSG_WARNING "%s is against the network rules"
\r
155 #define SSMSG_WARNING_2 "You are violating the network rules"
\r
156 #define SSMSG_WARNING_RULES "%s is against the network rules. Read the network rules at %s"
\r
157 #define SSMSG_WARNING_RULES_2 "You are violating the network rules. Read the network rules at %s"
\r
158 #define SSMSG_BADWORD_DETECTED "Your message contained a forbidden word."
\r
162 struct chanNode *debug_channel;
\r
163 struct string_list *global_exceptions;
\r
164 const char *network_rules;
\r
165 unsigned char trigger;
\r
166 unsigned long short_ban_duration;
\r
167 unsigned long long_ban_duration;
\r
168 unsigned long gline_duration;
\r
169 unsigned long exception_max;
\r
170 unsigned long exception_min_len;
\r
171 unsigned long exception_max_len;
\r
172 unsigned int adv_chan_must_exist : 1;
\r
173 unsigned int strip_mirc_codes : 1;
\r
174 unsigned int allow_move_merge : 1;
\r
177 /***********************************************/
\r
179 /***********************************************/
\r
182 get_chanInfo(const char *channelname)
\r
184 return dict_find(registered_channels_dict, channelname, 0);
\r
188 spamserv_join_channel(struct chanNode *channel)
\r
190 struct mod_chanmode change;
\r
191 mod_chanmode_init(&change);
\r
193 change.args[0].mode = MODE_CHANOP;
\r
194 change.args[0].u.member = AddChannelUser(spamserv, channel);
\r
195 mod_chanmode_announce(spamserv, channel, &change);
\r
199 spamserv_part_channel(struct chanNode *channel, char *reason)
\r
201 /* we only have to clear the spamNodes because every other node expires on it's own */
\r
202 spamserv_clear_spamNodes(channel);
\r
203 DelChannelUser(spamserv, channel, reason, 0);
\r
206 static struct chanInfo*
\r
207 spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)
\r
209 struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));
\r
213 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);
\r
217 cInfo->channel = channel;
\r
218 cInfo->exceptions = exceptions ? string_list_copy(exceptions) : alloc_string_list(1);
\r
219 cInfo->flags = flags;
\r
220 cInfo->exceptlevel = 400;
\r
221 safestrncpy(cInfo->info, info, sizeof(cInfo->info));
\r
222 cInfo->suspend_expiry = 0;
\r
223 cInfo->badwords = dict_new();
\r
224 cInfo->last_badword_id = 0;
\r
225 dict_insert(registered_channels_dict, cInfo->channel->name, cInfo);
\r
231 spamserv_unregister_channel(struct chanInfo *cInfo)
\r
236 dict_remove(registered_channels_dict, cInfo->channel->name);
\r
237 free_string_list(cInfo->exceptions);
\r
242 spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)
\r
244 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
250 cInfo->flags |= CHAN_SUSPENDED;
\r
251 cInfo->suspend_expiry = expiry;
\r
252 spamserv_part_channel(channel, reason);
\r
256 if(CHECK_SUSPENDED(cInfo))
\r
258 cInfo->flags &= ~CHAN_SUSPENDED;
\r
259 cInfo->suspend_expiry = 0;
\r
266 spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)
\r
268 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
272 char reason[MAXLEN];
\r
274 if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))
\r
277 snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);
\r
279 snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);
\r
281 spamserv_cs_unregister(user, channel, manually, reason);
\r
285 cInfo->channel = target;
\r
287 dict_remove(registered_channels_dict, channel->name);
\r
288 dict_insert(registered_channels_dict, target->name, cInfo);
\r
292 snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);
\r
296 spamserv_join_channel(target);
\r
297 snprintf(reason, sizeof(reason), "%s merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
\r
300 if(!CHECK_SUSPENDED(cInfo))
\r
301 spamserv_part_channel(channel, reason);
\r
304 snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
\r
306 snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
\r
308 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
\r
316 spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)
\r
318 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
322 char global[MAXLEN], partmsg[MAXLEN];
\r
327 snprintf(global, sizeof(global), "$X (channel %s) %s by %s.", channel->name, reason, user->handle_info->handle);
\r
328 snprintf(partmsg, sizeof(partmsg), "%s %s by %s.", channel->name, reason, user->handle_info->handle);
\r
331 snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name);
\r
332 snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name);
\r
334 case lost_all_users:
\r
335 snprintf(global, sizeof(global), "$X (channel %s) lost all users.", channel->name);
\r
336 snprintf(partmsg, sizeof(partmsg), "%s lost all users.", channel->name);
\r
340 if(!CHECK_SUSPENDED(cInfo))
\r
341 spamserv_part_channel(channel, partmsg);
\r
343 spamserv_unregister_channel(cInfo);
\r
344 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, global);
\r
348 /***********************************************/
\r
350 /***********************************************/
\r
352 static struct userInfo*
\r
353 get_userInfo(const char *nickname)
\r
355 return dict_find(connected_users_dict, nickname, 0);
\r
359 spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)
\r
361 struct spamNode *sNode = malloc(sizeof(struct spamNode));
\r
365 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);
\r
369 sNode->channel = channel;
\r
370 sNode->crc32 = crc32(text);
\r
372 sNode->next = NULL;
\r
376 struct spamNode *temp = uInfo->spam;
\r
381 sNode->prev = temp;
\r
382 temp->next = sNode;
\r
386 sNode->prev = NULL;
\r
387 uInfo->spam = sNode;
\r
392 spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)
\r
397 if(sNode == uInfo->spam)
\r
398 uInfo->spam = sNode->next;
\r
401 sNode->next->prev = sNode->prev;
\r
403 sNode->prev->next = sNode->next;
\r
409 spamserv_clear_spamNodes(struct chanNode *channel)
\r
411 struct userInfo *uInfo;
\r
412 struct spamNode *sNode;
\r
415 for(i = 0; i < channel->members.used; i++)
\r
417 if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))
\r
419 if((sNode = uInfo->spam))
\r
421 for(; sNode; sNode = sNode->next)
\r
422 if(sNode->channel == channel)
\r
426 spamserv_delete_spamNode(uInfo, sNode);
\r
433 spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)
\r
435 struct floodNode *fNode = malloc(sizeof(struct floodNode));
\r
439 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);
\r
443 fNode->channel = channel;
\r
444 fNode->owner = user;
\r
446 fNode->time = now;
\r
447 fNode->next = NULL;
\r
451 struct floodNode *temp = *uI_fNode;
\r
456 fNode->prev = temp;
\r
457 temp->next = fNode;
\r
461 fNode->prev = NULL;
\r
467 spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)
\r
472 if(fNode == *uI_fNode)
\r
473 *uI_fNode = fNode->next;
\r
476 fNode->next->prev = fNode->prev;
\r
478 fNode->prev->next = fNode->next;
\r
484 spamserv_create_user(struct userNode *user)
\r
486 struct userInfo *uInfo = malloc(sizeof(struct userInfo));
\r
487 struct killNode *kNode = dict_find(killed_users_dict, irc_ntoa(&user->ip), 0);
\r
491 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);
\r
496 spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);
\r
498 uInfo->user = user;
\r
499 uInfo->spam = NULL;
\r
500 uInfo->flood = NULL;
\r
501 uInfo->joinflood = NULL;
\r
502 uInfo->flags = kNode ? USER_KILLED : 0;
\r
503 uInfo->warnlevel = kNode ? kNode->warnlevel : 0;
\r
504 uInfo->lastadv = 0;
\r
506 dict_insert(connected_users_dict, user->nick, uInfo);
\r
510 dict_remove(killed_users_dict, irc_ntoa(&user->ip));
\r
516 spamserv_delete_user(struct userInfo *uInfo)
\r
523 spamserv_delete_spamNode(uInfo, uInfo->spam);
\r
526 while(uInfo->flood)
\r
527 spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);
\r
529 if(uInfo->joinflood)
\r
530 while(uInfo->joinflood)
\r
531 spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);
\r
533 dict_remove(connected_users_dict, uInfo->user->nick);
\r
538 spamserv_new_user_func(struct userNode *user)
\r
541 spamserv_create_user(user);
\r
545 spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))
\r
547 struct userInfo *uInfo = get_userInfo(user->nick);
\r
548 struct killNode *kNode;
\r
550 if(killer == spamserv)
\r
552 kNode = malloc(sizeof(struct killNode));
\r
556 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for killNode - nickname %s", user->nick);
\r
557 spamserv_delete_user(uInfo);
\r
561 if(uInfo->warnlevel > KILL_WARNLEVEL)
\r
562 kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;
\r
564 kNode->warnlevel = 0;
\r
568 dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);
\r
571 spamserv_delete_user(uInfo);
\r
575 spamserv_nick_change_func(struct userNode *user, const char *old_nick)
\r
577 struct userInfo *uInfo = get_userInfo(old_nick);
\r
579 dict_remove(connected_users_dict, old_nick);
\r
580 dict_insert(connected_users_dict, user->nick, uInfo);
\r
584 spamserv_user_join(struct modeNode *mNode)
\r
586 struct chanNode *channel = mNode->channel;
\r
587 struct userNode *user = mNode->user;
\r
588 struct chanInfo *cInfo;
\r
589 struct userInfo *uInfo;
\r
590 struct floodNode *jfNode;
\r
592 if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))
\r
596 if(!CHECK_CHANOPS(cInfo))
\r
598 //struct modeNode *mn = GetUserMode(channel, user);
\r
599 //if(mn->modes & MODE_CHANOP)
\r
601 if(check_user_level(channel, user, lvlGiveOps, 1, 0))
\r
605 if(!CHECK_VOICED(cInfo))
\r
607 if(check_user_level(channel, user, lvlGiveVoice, 1, 0))
\r
611 if(cInfo->exceptlevel == 0)
\r
613 if(cInfo->exceptlevel < 501) {
\r
614 struct userData *uData;
\r
615 if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
\r
620 if(!(jfNode = uInfo->joinflood))
\r
622 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
\r
626 for(; jfNode; jfNode = jfNode->next)
\r
627 if(jfNode->channel == channel)
\r
632 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
\r
637 jfNode->time = now;
\r
639 if(jfNode->count > JOINFLOOD_MAX)
\r
641 char reason[MAXLEN];
\r
643 spamserv_delete_floodNode(&uInfo->joinflood, jfNode);
\r
644 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_JOINFLOOD, spamserv_conf.network_rules);
\r
645 spamserv_punish(channel, user, JOINFLOOD_B_DURATION, reason, 1);
\r
654 spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
\r
656 struct userNode *user = mn->user;
\r
657 struct chanNode *channel = mn->channel;
\r
658 struct userInfo *uInfo;
\r
659 struct spamNode *sNode;
\r
660 struct floodNode *fNode;
\r
662 if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))
\r
665 if((sNode = uInfo->spam))
\r
667 for(; sNode; sNode = sNode->next)
\r
668 if(sNode->channel == channel)
\r
672 spamserv_delete_spamNode(uInfo, sNode);
\r
675 if((fNode = uInfo->flood))
\r
677 for(; fNode; fNode = fNode->next)
\r
678 if(fNode->channel == channel)
\r
682 spamserv_delete_floodNode(&uInfo->flood, fNode);
\r
686 static struct badword*
\r
687 add_badword(struct chanInfo *cInfo, const char *badword_mask, unsigned int triggered, unsigned int action, const char *id)
\r
689 struct badword *badword;
\r
691 badword = calloc(1, sizeof(*badword));
\r
696 cInfo->last_badword_id++;
\r
697 badword->id = strtab(cInfo->last_badword_id);
\r
699 badword->id = strdup(id);
\r
700 badword->badword_mask = strdup(badword_mask);
\r
701 badword->triggered = triggered;
\r
702 badword->action = action;
\r
703 dict_insert(cInfo->badwords, badword->id, badword);
\r
707 /***********************************************/
\r
709 /***********************************************/
\r
717 for(i = 0; i < 256; i++)
\r
721 for(j = 8; j > 0; j--)
\r
725 crc = (crc >> 1) ^ 0xEDB88320L;
\r
733 crc_table[i] = crc;
\r
737 static unsigned long
\r
738 crc32(const char *text)
\r
740 register unsigned long crc = 0xFFFFFFFF;
\r
741 unsigned int c, i = 0;
\r
743 while((c = (unsigned int)text[i++]) != 0)
\r
744 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];
\r
746 return (crc^0xFFFFFFFF);
\r
750 timeq_flood(UNUSED_ARG(void *data))
\r
752 dict_iterator_t it;
\r
753 struct userInfo *uInfo;
\r
754 struct floodNode *fNode;
\r
756 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
\r
758 uInfo = iter_data(it);
\r
760 if(!(fNode = uInfo->flood))
\r
763 for(; fNode; fNode = fNode->next)
\r
765 if(now - fNode->time > FLOOD_EXPIRE)
\r
767 if(!(--fNode->count))
\r
768 spamserv_delete_floodNode(&uInfo->flood, fNode);
\r
773 timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
\r
777 timeq_joinflood(UNUSED_ARG(void *data))
\r
779 dict_iterator_t it;
\r
780 struct userInfo *uInfo;
\r
781 struct floodNode *fNode;
\r
783 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
\r
785 uInfo = iter_data(it);
\r
787 if(!(fNode = uInfo->joinflood))
\r
790 for(; fNode; fNode = fNode->next)
\r
792 if(now - fNode->time > JOINFLOOD_EXPIRE)
\r
794 if(!(--fNode->count))
\r
795 spamserv_delete_floodNode(&uInfo->joinflood, fNode);
\r
800 timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
\r
804 timeq_adv(UNUSED_ARG(void *data))
\r
806 dict_iterator_t it;
\r
807 struct userInfo *uInfo;
\r
809 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
\r
811 uInfo = iter_data(it);
\r
813 if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)
\r
815 uInfo->lastadv = 0;
\r
816 uInfo->flags &= ~USER_ADV_WARNED;
\r
820 timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
\r
824 timeq_warnlevel(UNUSED_ARG(void *data))
\r
826 dict_iterator_t it;
\r
827 struct userInfo *uInfo;
\r
829 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
\r
831 uInfo = iter_data(it);
\r
833 if(uInfo->warnlevel > 0)
\r
834 uInfo->warnlevel--;
\r
837 timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
\r
841 timeq_kill(UNUSED_ARG(void *data))
\r
843 dict_iterator_t it;
\r
844 struct killNode *kNode;
\r
846 for(it = dict_first(killed_users_dict); it; it = iter_next(it))
\r
848 kNode = iter_data(it);
\r
850 if(kNode->time - now > KILL_EXPIRE)
\r
854 timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
\r
858 binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
\r
860 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
865 if(enabled_string(argv[1]))
\r
867 cInfo->flags |= mask;
\r
870 else if(disabled_string(argv[1]))
\r
872 cInfo->flags &= ~mask;
\r
877 spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);
\r
883 value = (cInfo->flags & mask) ? 1 : 0;
\r
886 spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");
\r
898 multiple_option(char *name, char *description, enum channelinfo info, struct valueData *values, int count, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
\r
900 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
905 index = atoi(argv[1]);
\r
907 if(index < 0 || index >= count)
\r
909 spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);
\r
911 for(index = 0; index < count; index++)
\r
912 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
\r
917 if(values[index].oper_only && !IsOper(user))
\r
919 spamserv_notice(user, "SSMSG_MUST_BE_OPER");
\r
923 cInfo->info[info] = values[index].value;
\r
927 for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);
\r
930 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
\r
935 show_exceptions(struct userNode *user, struct chanInfo *cInfo)
\r
937 struct helpfile_table table;
\r
940 if(!cInfo->exceptions->used)
\r
942 spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");
\r
946 spamserv_notice(user, "SSMSG_EXCEPTION_LIST");
\r
950 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
\r
951 table.contents = alloca(cInfo->exceptions->used * sizeof(*table.contents));
\r
953 for(i = 0; i < cInfo->exceptions->used; i++)
\r
955 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
\r
956 table.contents[table.length][0] = cInfo->exceptions->list[i];
\r
960 table_send(spamserv, user->nick, 0, NULL, table);
\r
966 show_memory_usage(struct userNode *user)
\r
968 dict_iterator_t it;
\r
969 struct helpfile_table table;
\r
970 struct chanInfo *cInfo;
\r
971 struct userInfo *uInfo;
\r
972 struct spamNode *sNode;
\r
973 struct floodNode *fNode;
\r
974 double channel_size = 0, user_size, size;
\r
975 unsigned int spamcount = 0, floodcount = 0, i, j;
\r
978 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
\r
980 cInfo = iter_data(it);
\r
982 if(!cInfo->exceptions->used)
\r
985 for(i = 0; i < cInfo->exceptions->used; i++)
\r
986 channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char);
\r
989 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
\r
991 uInfo = iter_data(it);
\r
993 for(sNode = uInfo->spam; sNode; sNode = sNode->next, spamcount++);
\r
994 for(fNode = uInfo->flood; fNode; fNode = fNode->next, floodcount++);
\r
995 for(fNode = uInfo->joinflood; fNode; fNode = fNode->next, floodcount++);
\r
998 channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);
\r
1000 user_size = dict_size(connected_users_dict) * sizeof(struct userInfo) +
\r
1001 dict_size(killed_users_dict) * sizeof(struct killNode) +
\r
1002 spamcount * sizeof(struct spamNode) +
\r
1003 floodcount * sizeof(struct floodNode);
\r
1005 size = channel_size + user_size;
\r
1007 ss_reply("SSMSG_STATUS_MEMORY");
\r
1011 table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;
\r
1012 table.contents = calloc(table.length, sizeof(char**));
\r
1015 table.contents[0] = calloc(table.width, sizeof(char*));
\r
1016 snprintf(buffer, sizeof(buffer), "Channel Memory Usage:");
\r
1017 table.contents[0][0] = strdup(buffer);
\r
1018 snprintf(buffer, sizeof(buffer), " %g Byte; ", channel_size);
\r
1019 table.contents[0][1] = strdup(buffer);
\r
1020 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", channel_size / 1024);
\r
1021 table.contents[0][2] = strdup(buffer);
\r
1022 snprintf(buffer, sizeof(buffer), "%g MegaByte", channel_size / 1024 / 1024);
\r
1023 table.contents[0][3] = strdup(buffer);
\r
1026 table.contents[1] = calloc(table.width, sizeof(char*));
\r
1027 snprintf(buffer, sizeof(buffer), "User Memory Usage :");
\r
1028 table.contents[1][0] = strdup(buffer);
\r
1029 snprintf(buffer, sizeof(buffer), " %g Byte; ", user_size);
\r
1030 table.contents[1][1] = strdup(buffer);
\r
1031 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", user_size / 1024);
\r
1032 table.contents[1][2] = strdup(buffer);
\r
1033 snprintf(buffer, sizeof(buffer), "%g MegaByte", user_size / 1024 / 1024);
\r
1034 table.contents[1][3] = strdup(buffer);
\r
1036 // total memory usage
\r
1037 table.contents[2] = calloc(table.width, sizeof(char*));
\r
1038 snprintf(buffer, sizeof(buffer), "Total Memory Usage :");
\r
1039 table.contents[2][0] = strdup(buffer);
\r
1040 snprintf(buffer, sizeof(buffer), " %g Byte; ", size);
\r
1041 table.contents[2][1] = strdup(buffer);
\r
1042 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", size / 1024);
\r
1043 table.contents[2][2] = strdup(buffer);
\r
1044 snprintf(buffer, sizeof(buffer), "%g MegaByte", size / 1024 / 1024);
\r
1045 table.contents[2][3] = strdup(buffer);
\r
1047 table_send(spamserv, user->nick, 0, NULL, table);
\r
1049 for(i = 0; i < table.length; i++)
\r
1051 for(j = 0; j < table.width; j++)
\r
1052 free((char*)table.contents[i][j]);
\r
1054 free(table.contents[i]);
\r
1057 free(table.contents);
\r
1061 show_registered_channels(struct userNode *user)
\r
1063 struct helpfile_table table;
\r
1064 dict_iterator_t it;
\r
1066 spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");
\r
1068 if(!dict_size(registered_channels_dict))
\r
1070 spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");
\r
1076 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
\r
1077 table.contents = alloca(dict_size(registered_channels_dict) * sizeof(*table.contents));
\r
1079 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
\r
1081 struct chanInfo *cInfo = iter_data(it);
\r
1083 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
\r
1084 table.contents[table.length][0] = cInfo->channel->name;
\r
1088 table_send(spamserv, user->nick, 0, NULL, table);
\r
1091 /***********************************************/
\r
1092 /* SpamServ_Func */
\r
1093 /***********************************************/
\r
1096 SPAMSERV_FUNC(cmd_register)
\r
1098 struct chanInfo *cInfo;
\r
1099 char reason[MAXLEN];
\r
1101 if(!channel || !channel->channel_info)
\r
1103 ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);
\r
1107 if(get_chanInfo(channel->name))
\r
1109 ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);
\r
1113 if(IsSuspended(channel->channel_info))
\r
1115 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");
\r
1119 if(channel == spamserv_conf.debug_channel)
\r
1121 ss_reply("SSMSG_DEBUG_CHAN");
\r
1125 if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))
\r
1127 ss_reply("SSMSG_NO_REGISTER", channel->name);
\r
1131 spamserv_join_channel(cInfo->channel);
\r
1133 snprintf(reason, sizeof(reason), "%s (channel %s) registered by %s.", spamserv->nick, channel->name, user->handle_info->handle);
\r
1134 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
\r
1135 ss_reply("SSMSG_REG_SUCCESS", channel->name);
\r
1141 SPAMSERV_FUNC(cmd_unregister)
\r
1143 struct chanInfo *cInfo;
\r
1144 struct chanData *cData;
\r
1145 struct userData *uData;
\r
1146 char reason[MAXLEN];
\r
1148 if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))
\r
1150 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1154 if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))
\r
1156 ss_reply("SSMSG_NO_ACCESS");
\r
1160 if(!IsHelping(user))
\r
1162 if(IsSuspended(cData))
\r
1164 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");
\r
1168 if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))
\r
1170 ss_reply("SSMSG_CONFIRM_UNREG");
\r
1175 if(!CHECK_SUSPENDED(cInfo))
\r
1177 snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle);
\r
1178 spamserv_part_channel(channel, reason);
\r
1181 spamserv_unregister_channel(cInfo);
\r
1183 snprintf(reason, sizeof(reason), "%s (channel %s) unregistered by %s.", spamserv->nick, channel->name, user->handle_info->handle);
\r
1184 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
\r
1185 ss_reply("SSMSG_UNREG_SUCCESS", channel->name);
\r
1191 SPAMSERV_FUNC(cmd_status)
\r
1193 ss_reply("SSMSG_STATUS");
\r
1194 ss_reply("SSMSG_STATUS_USERS", dict_size(connected_users_dict));
\r
1195 ss_reply("SSMSG_STATUS_CHANNELS", dict_size(registered_channels_dict));
\r
1197 if(IsOper(user) && argc > 1)
\r
1199 if(!irccasecmp(argv[1], "memory"))
\r
1200 show_memory_usage(user);
\r
1201 else if(!irccasecmp(argv[1], "channels"))
\r
1202 show_registered_channels(user);
\r
1209 SPAMSERV_FUNC(cmd_addexception)
\r
1211 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1212 struct userData *uData;
\r
1215 if(!cInfo || !channel->channel_info)
\r
1217 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1221 if(CHECK_SUSPENDED(cInfo))
\r
1223 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1227 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
\r
1229 ss_reply("SSMSG_NO_ACCESS");
\r
1234 return show_exceptions(user, cInfo);
\r
1236 if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))
\r
1238 ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);
\r
1242 if(strlen(argv[1]) < spamserv_conf.exception_min_len)
\r
1244 ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);
\r
1247 else if(strlen(argv[1]) > spamserv_conf.exception_max_len)
\r
1249 ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);
\r
1253 for(i = 0; i < cInfo->exceptions->used; i++)
\r
1255 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
\r
1257 ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);
\r
1262 string_list_append(cInfo->exceptions, strdup(argv[1]));
\r
1263 ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);
\r
1269 SPAMSERV_FUNC(cmd_delexception)
\r
1271 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1272 struct userData *uData;
\r
1276 if(!cInfo || !channel->channel_info)
\r
1278 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1282 if(CHECK_SUSPENDED(cInfo))
\r
1284 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1288 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
\r
1290 ss_reply("SSMSG_NO_ACCESS");
\r
1295 return show_exceptions(user, cInfo);
\r
1297 for(i = 0; i < cInfo->exceptions->used; i++)
\r
1299 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
\r
1308 ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);
\r
1312 string_list_delete(cInfo->exceptions, i);
\r
1313 ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);
\r
1319 SPAMSERV_FUNC(cmd_set)
\r
1321 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1322 struct svccmd *subcmd;
\r
1323 char cmd_name[MAXLEN];
\r
1328 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1332 if(CHECK_SUSPENDED(cInfo))
\r
1334 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1338 if(!check_user_level(channel,user,lvlSetters,1,0))
\r
1340 ss_reply("SSMSG_NO_ACCESS");
\r
1346 ss_reply("SSMSG_CHANNEL_OPTIONS");
\r
1348 for(i = 0; i < SET_SUBCMDS_SIZE; i++)
\r
1350 sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);
\r
1352 if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))
\r
1353 subcmd->command->func(user, channel, 1, argv + 1, subcmd);
\r
1359 sprintf(cmd_name, "%s %s", cmd->name, argv[1]);
\r
1360 subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);
\r
1364 reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);
\r
1368 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
\r
1372 SPAMSERV_FUNC(opt_spamlimit)
\r
1374 struct valueData values[] =
\r
1376 {"Users may send the same message $b2$b times.", 'a', 0},
\r
1377 {"Users may send the same message $b3$b times.", 'b', 0},
\r
1378 {"Users may send the same message $b4$b times.", 'c', 0},
\r
1379 {"Users may send the same message $b5$b times.", 'd', 0},
\r
1380 {"Users may send the same message $b6$b times.", 'e', 0}
\r
1383 MULTIPLE_OPTION("SpamLimit ", "SpamLimit", ci_SpamLimit);
\r
1387 SPAMSERV_FUNC(opt_advreaction)
\r
1389 struct valueData values[] =
\r
1391 {"Kick on disallowed advertising.", 'k', 0},
\r
1392 {"Kickban on disallowed advertising.", 'b', 0},
\r
1393 {"Short timed ban on disallowed advertising.", 's', 0},
\r
1394 {"Long timed ban on disallowed advertising.", 'l', 0},
\r
1395 {"Kill on disallowed advertising.", 'd', 1}
\r
1398 MULTIPLE_OPTION("AdvReaction ", "AdvReaction", ci_AdvReaction);
\r
1402 SPAMSERV_FUNC(opt_warnreaction)
\r
1404 struct valueData values[] =
\r
1406 {"Kick after warning.", 'k', 0},
\r
1407 {"Kickban after warning.", 'b', 0},
\r
1408 {"Short timed ban after warning.", 's', 0},
\r
1409 {"Long timed ban after warning.", 'l', 0},
\r
1410 {"Kill after warning.", 'd', 1}
\r
1413 MULTIPLE_OPTION("WarnReaction ", "WarnReaction", ci_WarnReaction);
\r
1417 SPAMSERV_FUNC(opt_advscan)
\r
1419 BINARY_OPTION("AdvScan ", CHAN_ADV_SCAN);
\r
1423 SPAMSERV_FUNC(opt_spamscan)
\r
1425 BINARY_OPTION("SpamScan ", CHAN_SPAMSCAN);
\r
1429 SPAMSERV_FUNC(opt_floodscan)
\r
1431 BINARY_OPTION("FloodScan ", CHAN_FLOODSCAN);
\r
1435 SPAMSERV_FUNC(opt_joinflood)
\r
1437 BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);
\r
1441 SPAMSERV_FUNC(opt_scanops)
\r
1443 BINARY_OPTION("ScanChanOps ", CHAN_SCAN_CHANOPS);
\r
1447 SPAMSERV_FUNC(opt_scanvoiced)
\r
1449 BINARY_OPTION("ScanVoiced ", CHAN_SCAN_VOICED);
\r
1453 SPAMSERV_FUNC(opt_exceptlevel)
\r
1455 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1456 struct userData *uData;
\r
1460 index = atoi(argv[1]);
\r
1461 if(index < 1 || index > 501)
\r
1463 spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");
\r
1466 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))
\r
1468 ss_reply("SSMSG_NO_ACCESS");
\r
1471 cInfo->exceptlevel=index;
\r
1473 index=cInfo->exceptlevel;
\r
1475 spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel ", index);
\r
1479 static SPAMSERV_FUNC(cmd_addbad)
\r
1481 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1482 struct userData *uData;
\r
1486 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1490 if(CHECK_SUSPENDED(cInfo))
\r
1492 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1496 if(!check_user_level(channel,user,lvlSetters,1,0))
\r
1498 ss_reply("SSMSG_NO_ACCESS");
\r
1502 dict_iterator_t it;
\r
1503 char *mask = unsplit_string(argv + 1, argc - 1, NULL);
\r
1504 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
\r
1505 struct badword *badword = iter_data(it);
\r
1506 if(match_ircglob(mask,badword->badword_mask)) {
\r
1507 reply("SSMSG_BADWORD_ALREADY_ADDED", mask, badword->id);
\r
1512 struct badword *new_badword = add_badword(cInfo, mask, 0, BADACTION_KICK, NULL);
\r
1513 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
\r
1514 struct badword *badword = iter_data(it);
\r
1515 if(match_ircglob(badword->badword_mask, new_badword->badword_mask) && badword != new_badword) {
\r
1516 dict_remove(cInfo->badwords, badword->id);
\r
1520 reply("SSMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id);
\r
1524 static SPAMSERV_FUNC(cmd_delbad)
\r
1526 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1527 struct userData *uData;
\r
1532 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1536 if(CHECK_SUSPENDED(cInfo))
\r
1538 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1542 if(!check_user_level(channel,user,lvlSetters,1,0))
\r
1544 ss_reply("SSMSG_NO_ACCESS");
\r
1548 for (n=1; n<argc; n++) {
\r
1549 struct badword *badword = dict_find(cInfo->badwords, argv[n], NULL);
\r
1551 reply("SSMSG_BADWORD_NOT_FOUND", argv[n]);
\r
1554 reply("SSMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);
\r
1555 dict_remove(cInfo->badwords, argv[n]);
\r
1560 static SPAMSERV_FUNC(cmd_setbad)
\r
1562 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1563 struct userData *uData;
\r
1564 struct badword *badword;
\r
1568 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1572 if(CHECK_SUSPENDED(cInfo))
\r
1574 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1578 if(!check_user_level(channel,user,lvlSetters,1,0))
\r
1580 ss_reply("SSMSG_NO_ACCESS");
\r
1584 if ((badword = dict_find(cInfo->badwords, argv[1], NULL))) {
\r
1587 char *setting = argv[2];
\r
1588 char *value = argv[3];
\r
1589 for( ii = 0; setting[ ii ]; ii++)
\r
1590 setting[ ii ] = toupper( setting[ ii ] );
\r
1591 for( ii = 0; value[ ii ]; ii++)
\r
1592 value[ ii ] = toupper( value[ ii ] );
\r
1593 if(!strcmp("MASK",setting)) {
\r
1594 free(badword->badword_mask);
\r
1595 badword->badword_mask = strdup(argv[3]);
\r
1596 badword->triggered = 0;
\r
1597 reply("SSMSG_BADWORD_SET_DONE");
\r
1599 else if(!strcmp("ACTION",setting)) {
\r
1600 if (!strcmp("1",value) || !strcmp("KICK",value)) {
\r
1601 badword->action = BADACTION_KICK;
\r
1602 reply("SSMSG_BADWORD_SET_DONE");
\r
1603 } else if (!strcmp("2",value) || !strcmp("BAN",value)) {
\r
1604 badword->action = BADACTION_BAN;
\r
1605 reply("SSMSG_BADWORD_SET_DONE");
\r
1606 } else if (!strcmp("3",value) || !strcmp("KILL",value)) {
\r
1607 badword->action = BADACTION_KILL;
\r
1608 reply("SSMSG_BADWORD_SET_DONE");
\r
1609 } else if (!strcmp("4",value) || !strcmp("GLINE",value)) {
\r
1610 badword->action = BADACTION_GLINE;
\r
1611 reply("SSMSG_BADWORD_SET_DONE");
\r
1613 reply("SSMSG_BADWORD_SET_INVALID", setting);
\r
1616 reply("SSMSG_BADWORD_SETTING_INVALID", setting);
\r
1620 reply("SSMSG_BADWORD_SET", badword->id);
\r
1621 reply("SSMSG_BADWORD_SET_MASK", badword->badword_mask);
\r
1622 switch(badword->action) {
\r
1623 case BADACTION_KICK:
\r
1624 reply("SSMSG_BADWORD_SET_ACTION", "KICK");
\r
1626 case BADACTION_BAN:
\r
1627 reply("SSMSG_BADWORD_SET_ACTION", "BAN");
\r
1629 case BADACTION_KILL:
\r
1630 reply("SSMSG_BADWORD_SET_ACTION", "KILL");
\r
1632 case BADACTION_GLINE:
\r
1633 reply("SSMSG_BADWORD_SET_ACTION", "GLINE");
\r
1636 reply("SSMSG_BADWORD_SET_ACTION", "*undef*");
\r
1640 reply("SSMSG_BADWORD_NOT_FOUND", argv[1]);
\r
1647 ss_badwords_sort(const void *pa, const void *pb)
\r
1649 struct badword *a = *(struct badword**)pa;
\r
1650 struct badword *b = *(struct badword**)pb;
\r
1652 return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0);
\r
1655 static SPAMSERV_FUNC(cmd_listbad)
\r
1657 struct helpfile_table tbl;
\r
1658 struct chanInfo *cInfo = get_chanInfo(channel->name);
\r
1659 struct userData *uData;
\r
1660 struct badword **badwords;
\r
1661 unsigned int count = 0, ii = 0;
\r
1665 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
\r
1669 if(CHECK_SUSPENDED(cInfo))
\r
1671 ss_reply("SSMSG_SUSPENDED", channel->name);
\r
1675 if(!check_user_level(channel,user,lvlSetters,1,0))
\r
1677 ss_reply("SSMSG_NO_ACCESS");
\r
1681 dict_iterator_t it;
\r
1682 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
\r
1686 tbl.length = count+1;
\r
1689 tbl.flags = TABLE_NO_FREE;
\r
1690 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
\r
1691 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
\r
1692 tbl.contents[0][0] = "#";
\r
1693 tbl.contents[0][1] = "Badword";
\r
1694 tbl.contents[0][2] = "Action";
\r
1695 tbl.contents[0][3] = "(Triggered)";
\r
1698 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
\r
1699 reply("MSG_NONE");
\r
1700 free(tbl.contents[0]);
\r
1701 free(tbl.contents);
\r
1704 badwords = alloca(count * sizeof(badwords[0]));
\r
1705 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
\r
1706 struct badword *bw = iter_data(it);
\r
1707 badwords[ii++] = bw;
\r
1710 qsort(badwords, count, sizeof(badwords[0]), ss_badwords_sort);
\r
1711 for (ii = 1; ii <= count; ii++) {
\r
1712 struct badword *bw = badwords[ii-1];
\r
1713 tbl.contents[ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
\r
1714 tbl.contents[ii][0] = strdup(bw->id);
\r
1715 tbl.contents[ii][1] = strdup(bw->badword_mask);
\r
1716 switch(bw->action) {
\r
1717 case BADACTION_KICK:
\r
1718 tbl.contents[ii][2] = "KICK";
\r
1720 case BADACTION_BAN:
\r
1721 tbl.contents[ii][2] = "BAN";
\r
1723 case BADACTION_KILL:
\r
1724 tbl.contents[ii][2] = "KILL";
\r
1726 case BADACTION_GLINE:
\r
1727 tbl.contents[ii][2] = "GLINE";
\r
1730 tbl.contents[ii][2] = "*undef*";
\r
1732 tbl.contents[ii][3] = strtab(bw->triggered);
\r
1735 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
\r
1736 for(ii = 1; ii < tbl.length; ++ii)
\r
1738 free(tbl.contents[ii]);
\r
1740 free(tbl.contents[0]);
\r
1741 free(tbl.contents);
\r
1746 to_lower(char *message)
\r
1748 unsigned int i, diff = 'a' - 'A';
\r
1750 for(i = 0; i < strlen(message); i++)
\r
1752 if((message[i] >= 'A') && (message[i] <= 'Z'))
\r
1753 message[i] = message[i] + diff;
\r
1758 strip_mirc_codes(char *text)
\r
1760 // taken from xchat and modified
\r
1761 int nc = 0, i = 0, col = 0, len = strlen(text);
\r
1762 static char new_str[MAXLEN];
\r
1766 if((col && isdigit(*text) && nc < 2) ||
\r
1767 (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))
\r
1791 new_str[i] = *text;
\r
1800 new_str[i] = '\0';
\r
1806 is_in_exception_list(struct chanInfo *cInfo, char *message)
\r
1810 for(i = 0; i < cInfo->exceptions->used; i++)
\r
1811 if(strstr(message, cInfo->exceptions->list[i]))
\r
1818 check_advertising(struct chanInfo *cInfo, char *message)
\r
1820 unsigned int i = 0;
\r
1822 if(spamserv_conf.strip_mirc_codes)
\r
1823 message = strip_mirc_codes(message);
\r
1825 if(is_in_exception_list(cInfo, message))
\r
1828 while(message[i] != 0)
\r
1830 if(message[i] == '#')
\r
1832 char channelname[CHANNELLEN];
\r
1833 unsigned int j = 0;
\r
1835 if(!spamserv_conf.adv_chan_must_exist)
\r
1838 /* only return 1, if the channel does exist */
\r
1840 while((message[i] != 0) && (message[i] != ' '))
\r
1842 channelname[j] = message[i];
\r
1847 channelname[j] = '\0';
\r
1849 if(GetChannel(channelname))
\r
1852 else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))
\r
1854 else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))
\r
1856 else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))
\r
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);
\r
1868 spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)
\r
1872 struct mod_chanmode change;
\r
1873 char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
\r
1875 sanitize_ircmask(hostmask);
\r
1878 add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);
\r
1880 mod_chanmode_init(&change);
\r
1882 change.args[0].mode = MODE_BAN;
\r
1883 change.args[0].u.hostmask = hostmask;
\r
1884 mod_chanmode_announce(spamserv, channel, &change);
\r
1888 spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);
\r
1891 spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);
\r
1893 KickChannelUser(user, channel, spamserv, reason);
\r
1897 spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct badword *badword)
\r
1900 char *reason = SSMSG_BADWORD_DETECTED;
\r
1901 char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };
\r
1902 switch(badword->action) {
\r
1903 case BADACTION_BAN:
\r
1904 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
\r
1905 sanitize_ircmask(hostmask);
\r
1906 if(chan->channel_info) {
\r
1907 //registered channel
\r
1908 add_channel_ban(chan->channel_info, hostmask, spamserv->nick, now, now, now + spamserv_conf.long_ban_duration, reason);
\r
1910 struct mod_chanmode change;
\r
1911 mod_chanmode_init(&change);
\r
1913 change.args[0].mode = MODE_BAN;
\r
1914 change.args[0].u.hostmask = hostmask;
\r
1915 mod_chanmode_announce(spamserv, chan, &change);
\r
1918 case BADACTION_KICK:
\r
1919 if(GetUserMode(chan, user))
\r
1920 KickChannelUser(user, chan, spamserv, reason);
\r
1922 case BADACTION_KILL:
\r
1923 DelUser(user, spamserv, 1, reason);
\r
1925 case BADACTION_GLINE:
\r
1926 irc_ntop(mask + 2, sizeof(mask) - 2, &user->ip);
\r
1927 gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
\r
1936 spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)
\r
1938 struct chanInfo *cInfo;
\r
1939 struct userInfo *uInfo;
\r
1940 struct spamNode *sNode;
\r
1941 struct floodNode *fNode;
\r
1942 unsigned int violation = 0;
\r
1943 char reason[MAXLEN];
\r
1945 /* make sure: spamserv is not disabled; srvx is running; spamserv is in the chan; chan is regged, user does exist */
\r
1946 if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick)))
\r
1949 if(!CHECK_CHANOPS(cInfo))
\r
1951 struct modeNode *mn = GetUserMode(channel, user);
\r
1952 if(mn && mn->modes & MODE_CHANOP)
\r
1954 //if(check_user_level(channel, user, lvlGiveOps, 1, 0))
\r
1958 if(!CHECK_VOICED(cInfo))
\r
1960 struct modeNode *mn = GetUserMode(channel, user);
\r
1961 if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))
\r
1965 if(cInfo->exceptlevel == 0)
\r
1967 if(cInfo->exceptlevel < 501) {
\r
1968 struct userData *uData;
\r
1969 if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
\r
1976 if(CHECK_SPAM(cInfo))
\r
1978 if(!(sNode = uInfo->spam))
\r
1980 spamserv_create_spamNode(channel, uInfo, text);
\r
1984 for(; sNode; sNode = sNode->next)
\r
1985 if(sNode->channel == channel)
\r
1990 spamserv_create_spamNode(channel, uInfo, text);
\r
1994 unsigned long crc = crc32(text);
\r
1996 if(crc == sNode->crc32)
\r
1998 unsigned int spamlimit = 2;
\r
2001 switch(cInfo->info[ci_SpamLimit])
\r
2003 case 'a': spamlimit = 2; break;
\r
2004 case 'b': spamlimit = 3; break;
\r
2005 case 'c': spamlimit = 4; break;
\r
2006 case 'd': spamlimit = 5; break;
\r
2007 case 'e': spamlimit = 6; break;
\r
2010 if(sNode->count == spamlimit)
\r
2012 uInfo->warnlevel += SPAM_WARNLEVEL;
\r
2014 if(uInfo->warnlevel < MAX_WARNLEVEL)
\r
2015 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules);
\r
2017 else if(sNode->count > spamlimit)
\r
2019 switch(cInfo->info[ci_WarnReaction])
\r
2021 case 'k': uInfo->flags |= USER_KICK; break;
\r
2022 case 'b': uInfo->flags |= USER_KICKBAN; break;
\r
2023 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
\r
2024 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
\r
2025 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
\r
2028 spamserv_delete_spamNode(uInfo, sNode);
\r
2029 uInfo->warnlevel += SPAM_WARNLEVEL;
\r
2035 sNode->crc32 = crc;
\r
2042 if(CHECK_FLOOD(cInfo))
\r
2044 if(!(fNode = uInfo->flood))
\r
2046 spamserv_create_floodNode(channel, user, &uInfo->flood);
\r
2050 for(; fNode; fNode = fNode->next)
\r
2051 if(fNode->channel == channel)
\r
2056 spamserv_create_floodNode(channel, user, &uInfo->flood);
\r
2060 if(((now - fNode->time) < FLOOD_EXPIRE))
\r
2064 if(fNode->count == FLOOD_MAX_LINES - 1)
\r
2066 uInfo->warnlevel += FLOOD_WARNLEVEL;
\r
2068 if(uInfo->warnlevel < MAX_WARNLEVEL)
\r
2069 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules);
\r
2071 else if(fNode->count > FLOOD_MAX_LINES)
\r
2073 switch(cInfo->info[ci_WarnReaction])
\r
2075 case 'k': uInfo->flags |= USER_KICK; break;
\r
2076 case 'b': uInfo->flags |= USER_KICKBAN; break;
\r
2077 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
\r
2078 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
\r
2079 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
\r
2082 spamserv_delete_floodNode(&uInfo->flood, fNode);
\r
2083 uInfo->warnlevel += FLOOD_WARNLEVEL;
\r
2088 fNode->time = now;
\r
2093 dict_iterator_t it;
\r
2095 for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {
\r
2096 struct badword *badword = iter_data(it);
\r
2097 if(match_ircglob(text, badword->badword_mask)) {
\r
2098 spamserv_detected_badword(user, channel, badword);
\r
2102 if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))
\r
2104 if(CHECK_ADV_WARNED(uInfo))
\r
2106 switch(cInfo->info[ci_AdvReaction])
\r
2108 case 'k': uInfo->flags |= USER_KICK; break;
\r
2109 case 'b': uInfo->flags |= USER_KICKBAN; break;
\r
2110 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
\r
2111 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
\r
2112 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
\r
2115 uInfo->warnlevel += ADV_WARNLEVEL;
\r
2120 uInfo->flags |= USER_ADV_WARNED;
\r
2121 uInfo->lastadv = now;
\r
2122 uInfo->warnlevel += ADV_WARNLEVEL;
\r
2124 if(uInfo->warnlevel < MAX_WARNLEVEL)
\r
2125 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules);
\r
2129 if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)
\r
2131 uInfo->flags |= USER_WARNED;
\r
2132 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules);
\r
2133 irc_notice(spamserv, user->numeric, reason);
\r
2134 irc_privmsg(spamserv, user->numeric, reason);
\r
2136 else if(uInfo->warnlevel > MAX_WARNLEVEL)
\r
2138 if(CHECK_KILLED(uInfo))
\r
2139 uInfo->flags |= USER_GLINE;
\r
2141 uInfo->flags |= USER_KILL;
\r
2151 case 1: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules); break;
\r
2152 case 2: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules); break;
\r
2153 case 3: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules); break;
\r
2154 default: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules); break;
\r
2157 if(CHECK_GLINE(uInfo))
\r
2159 int size = strlen(user->hostname) + 3;
\r
2160 char *mask = alloca(size);
\r
2161 snprintf(mask, size, "*@%s", user->hostname);
\r
2162 gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
\r
2163 spamserv_debug(SSMSG_DEBUG_GLINE, user->nick, user->hostname, channel->name);
\r
2165 else if(CHECK_KILL(uInfo))
\r
2167 DelUser(user, spamserv, 1, reason);
\r
2168 spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);
\r
2170 else if(CHECK_LONG_TBAN(uInfo))
\r
2172 spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);
\r
2174 else if(CHECK_SHORT_TBAN(uInfo))
\r
2176 spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);
\r
2178 else if(CHECK_KICKBAN(uInfo))
\r
2180 spamserv_punish(channel, user, 0, reason, 1);
\r
2182 else if(CHECK_KICK(uInfo))
\r
2184 spamserv_punish(channel, user, 0, reason, 0);
\r
2189 spamserv_saxdb_read_shitlist(const char *name, void *data, void *extra)
\r
2191 struct record_data *rd = data;
\r
2192 struct chanInfo *chan = extra;
\r
2194 char *triggered, *action;
\r
2196 if (rd->type == RECDB_OBJECT) {
\r
2197 dict_t obj = GET_RECORD_OBJECT(rd);
\r
2198 /* new style structure */
\r
2199 badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);
\r
2200 triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);
\r
2201 action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);
\r
2203 add_badword(chan, badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);
\r
2209 spamserv_saxdb_read(struct dict *database)
\r
2211 dict_iterator_t it;
\r
2212 struct dict *badwords;
\r
2213 struct record_data *hir;
\r
2214 struct chanNode *channel;
\r
2215 struct chanInfo *cInfo;
\r
2216 struct string_list *strlist;
\r
2217 unsigned int flags,exceptlevel,badwordid;
\r
2219 unsigned long expiry;
\r
2221 for(it = dict_first(database); it; it = iter_next(it))
\r
2223 hir = iter_data(it);
\r
2225 if(hir->type != RECDB_OBJECT)
\r
2227 log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
\r
2231 channel = GetChannel(iter_key(it));
\r
2232 strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);
\r
2234 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
\r
2235 flags = str ? atoi(str) : 0;
\r
2237 info = database_get_data(hir->d.object, KEY_INFO, RECDB_QSTRING);
\r
2238 str = database_get_data(hir->d.object, KEY_EXCEPTLEVEL, RECDB_QSTRING);
\r
2239 exceptlevel = str ? atoi(str) : 400;
\r
2241 str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);
\r
2242 expiry = str ? strtoul(str, NULL, 0) : 0;
\r
2244 if(channel && info)
\r
2246 if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))
\r
2248 /* if the channel is suspended and expiry = 0 it means: channel will
\r
2249 never expire ! it does NOT mean, the channel is not suspended */
\r
2250 if(CHECK_SUSPENDED(cInfo) && expiry && (expiry < now))
\r
2252 cInfo->flags &= ~CHAN_SUSPENDED;
\r
2253 spamserv_join_channel(cInfo->channel);
\r
2255 else if(!CHECK_SUSPENDED(cInfo))
\r
2256 spamserv_join_channel(cInfo->channel);
\r
2258 cInfo->suspend_expiry = expiry;
\r
2259 cInfo->exceptlevel=exceptlevel;
\r
2260 cInfo->badwords = dict_new();
\r
2261 str = database_get_data(hir->d.object, KEY_LASTBADWORDID, RECDB_QSTRING);
\r
2262 badwordid = str ? atoi(str) : 0;
\r
2263 cInfo->last_badword_id = badwordid;
\r
2264 if ((badwords = database_get_data(hir->d.object, KEY_BADWORDS, RECDB_OBJECT)))
\r
2265 dict_foreach(badwords, spamserv_saxdb_read_shitlist, cInfo);
\r
2269 log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it));
\r
2276 spamserv_saxdb_write(struct saxdb_context *ctx)
\r
2278 dict_iterator_t it;
\r
2280 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
\r
2282 struct chanInfo *cInfo = iter_data(it);
\r
2284 saxdb_start_record(ctx, cInfo->channel->name, 1);
\r
2286 if(cInfo->exceptions->used)
\r
2287 saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);
\r
2290 saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags);
\r
2292 saxdb_write_string(ctx, KEY_INFO, cInfo->info);
\r
2293 saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel);
\r
2295 if(cInfo->suspend_expiry)
\r
2296 saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);
\r
2298 saxdb_write_int(ctx, KEY_LASTBADWORDID, cInfo->last_badword_id);
\r
2299 saxdb_start_record(ctx, KEY_BADWORDS, 1);
\r
2300 dict_iterator_t itbad;
\r
2301 for (itbad = dict_first(cInfo->badwords); itbad; itbad = iter_next(itbad)) {
\r
2302 struct badword *badword = iter_data(itbad);
\r
2303 saxdb_start_record(ctx, badword->id, 1);
\r
2304 saxdb_write_string(ctx, KEY_BADWORDID, badword->id);
\r
2305 saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask);
\r
2306 saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action);
\r
2307 saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered);
\r
2308 saxdb_end_record(ctx);
\r
2310 saxdb_end_record(ctx);
\r
2311 saxdb_end_record(ctx);
\r
2317 spamserv_conf_read(void)
\r
2322 if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))
\r
2324 log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);
\r
2328 str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
\r
2332 spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);
\r
2334 if(spamserv_conf.debug_channel)
\r
2335 spamserv_join_channel(spamserv_conf.debug_channel);
\r
2339 spamserv_conf.debug_channel = NULL;
\r
2342 spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);
\r
2344 str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);
\r
2345 spamserv_conf.network_rules = str ? str : NULL;
\r
2347 str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);
\r
2348 spamserv_conf.trigger = str ? str[0] : 0;
\r
2350 str = database_get_data(conf_node, KEY_SHORT_BAN_DURATION, RECDB_QSTRING);
\r
2351 spamserv_conf.short_ban_duration = str ? ParseInterval(str) : ParseInterval("15m");
\r
2353 str = database_get_data(conf_node, KEY_LONG_BAN_DURATION, RECDB_QSTRING);
\r
2354 spamserv_conf.long_ban_duration = str ? ParseInterval(str) : ParseInterval("1h");
\r
2356 str = database_get_data(conf_node, KEY_GLINE_DURATION, RECDB_QSTRING);
\r
2357 spamserv_conf.gline_duration = str ? ParseInterval(str) : ParseInterval("1h");
\r
2359 str = database_get_data(conf_node, KEY_EXCEPTION_MAX, RECDB_QSTRING);
\r
2360 spamserv_conf.exception_max = str ? strtoul(str, NULL, 0) : 10;
\r
2362 str = database_get_data(conf_node, KEY_EXCEPTION_MIN_LEN, RECDB_QSTRING);
\r
2363 spamserv_conf.exception_min_len = str ? strtoul(str, NULL, 0) : 4;
\r
2365 str = database_get_data(conf_node, KEY_EXCEPTION_MAX_LEN, RECDB_QSTRING);
\r
2366 spamserv_conf.exception_max_len = str ? strtoul(str, NULL, 0) : 15;
\r
2368 str = database_get_data(conf_node, KEY_ADV_CHAN_MUST_EXIST, RECDB_QSTRING);
\r
2369 spamserv_conf.adv_chan_must_exist = str ? enabled_string(str) : 1;
\r
2371 str = database_get_data(conf_node, KEY_STRIP_MIRC_CODES, RECDB_QSTRING);
\r
2372 spamserv_conf.strip_mirc_codes = str ? enabled_string(str) : 0;
\r
2374 str = database_get_data(conf_node, KEY_ALLOW_MOVE_MERGE, RECDB_QSTRING);
\r
2375 spamserv_conf.allow_move_merge = str ? enabled_string(str) : 0;
\r
2379 spamserv_db_cleanup(void)
\r
2381 dict_iterator_t it;
\r
2383 while((it = dict_first(registered_channels_dict)))
\r
2385 spamserv_unregister_channel(iter_data(it));
\r
2388 while((it = dict_first(killed_users_dict)))
\r
2390 free(iter_data(it));
\r
2393 dict_delete(registered_channels_dict);
\r
2394 dict_delete(connected_users_dict);
\r
2395 dict_delete(killed_users_dict);
\r
2399 init_spamserv(const char *nick)
\r
2404 const char *modes = conf_get_data("services/spamserv/modes", RECDB_QSTRING);
\r
2405 spamserv = AddLocalUser(nick, nick, NULL, "Anti Spam Services", modes);
\r
2406 spamserv_service = service_register(spamserv);
\r
2407 service_register(spamserv)->trigger = spamserv_conf.trigger;
\r
2409 conf_register_reload(spamserv_conf_read);
\r
2411 SS_LOG = log_register_type("SpamServ", "file:spamserv.log");
\r
2413 registered_channels_dict = dict_new();
\r
2414 connected_users_dict = dict_new();
\r
2415 killed_users_dict = dict_new();
\r
2417 reg_new_user_func(spamserv_new_user_func);
\r
2418 reg_del_user_func(spamserv_del_user_func);
\r
2419 reg_nick_change_func(spamserv_nick_change_func);
\r
2420 reg_join_func(spamserv_user_join);
\r
2421 reg_part_func(spamserv_user_part);
\r
2423 timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
\r
2424 timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
\r
2425 timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
\r
2426 timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
\r
2427 timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
\r
2429 spamserv_module = module_register("SpamServ", SS_LOG, "spamserv.help", NULL);
\r
2430 modcmd_register(spamserv_module, "REGISTER", cmd_register, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+acceptchan,+helping", NULL);
\r
2431 modcmd_register(spamserv_module, "UNREGISTER", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+loghostmask", NULL);
\r
2432 modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2433 modcmd_register(spamserv_module, "DELEXCEPTION", cmd_delexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2434 modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL);
\r
2435 modcmd_register(spamserv_module, "ADDBAD", cmd_addbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2436 modcmd_register(spamserv_module, "DELBAD", cmd_delbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2437 modcmd_register(spamserv_module, "SETBAD", cmd_setbad, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2438 modcmd_register(spamserv_module, "LISTBAD", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2439 modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2440 modcmd_register(spamserv_module, "SET SPAMLIMIT", opt_spamlimit, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2441 modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2442 modcmd_register(spamserv_module, "SET WARNREACTION", opt_warnreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2443 modcmd_register(spamserv_module, "SET ADVSCAN", opt_advscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2444 modcmd_register(spamserv_module, "SET SPAMSCAN", opt_spamscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2445 modcmd_register(spamserv_module, "SET FLOODSCAN", opt_floodscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2446 modcmd_register(spamserv_module, "SET JOINFLOODSCAN", opt_joinflood, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2447 modcmd_register(spamserv_module, "SET SCANCHANOPS", opt_scanops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2448 modcmd_register(spamserv_module, "SET SCANVOICED", opt_scanvoiced, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2449 modcmd_register(spamserv_module, "SET EXCEPTLEVEL", opt_exceptlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
\r
2451 saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write);
\r
2452 reg_exit_func(spamserv_db_cleanup);
\r
2453 message_register_table(msgtab);
\r
2457 /* spamserv.c - anti spam service
2458 * Copyright 2004 feigling
2460 * This program is free software; you can redistribute it and/or modify
2461 * it under the terms of the GNU General Public License as published by
2462 * the Free Software Foundation; either version 2 of the License, or
2463 * (at your option) any later version. Important limitations are
2464 * listed in the COPYING file that accompanies this software.
2466 * This program is distributed in the hope that it will be useful,
2467 * but WITHOUT ANY WARRANTY; without even the implied warranty of
2468 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2469 * GNU General Public License for more details.
2471 * You should have received a copy of the GNU General Public License
2472 * along with this program; if not, email srvx-maintainers@srvx.net.
2474 * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $
2478 #include "spamserv.h"
2479 #include "chanserv.h"
2486 #define SPAMSERV_CONF_NAME "services/spamserv"
2488 #define KEY_EXCEPTIONS "exceptions"
2489 #define KEY_FLAGS "flags"
2490 #define KEY_INFO "info"
2491 #define KEY_EXCEPTLEVEL "exceptlevel"
2492 #define KEY_EXPIRY "expiry"
2494 #define KEY_DEBUG_CHANNEL "debug_channel"
2495 #define KEY_OPER_CHANNEL "oper_channel"
2496 #define KEY_GLOBAL_EXCEPTIONS "global_exceptions"
2497 #define KEY_NETWORK_RULES "network_rules"
2498 #define KEY_TRIGGER "trigger"
2499 #define KEY_SHORT_BAN_DURATION "short_ban_duration"
2500 #define KEY_LONG_BAN_DURATION "long_ban_duration"
2501 #define KEY_GLINE_DURATION "gline_duration"
2502 #define KEY_EXCEPTION_MAX "exception_max"
2503 #define KEY_EXCEPTION_MIN_LEN "exception_min_len"
2504 #define KEY_EXCEPTION_MAX_LEN "exception_max_len"
2505 #define KEY_ADV_CHAN_MUST_EXIST "adv_chan_must_exist"
2506 #define KEY_STRIP_MIRC_CODES "strip_mirc_codes"
2507 #define KEY_ALLOW_MOVE_MERGE "allow_move_merge"
2509 #define SPAMSERV_FUNC(NAME) MODCMD_FUNC(NAME)
2510 #define SPAMSERV_SYNTAX() svccmd_send_help(user, spamserv, cmd)
2511 #define SPAMSERV_MIN_PARMS(N) do { \
2514 ss_reply(MSG_MISSING_PARAMS, argv[0]); \
2515 SPAMSERV_SYNTAX(); \
2516 return 0; } } while(0)
2518 struct userNode *spamserv;
2519 static struct module *spamserv_module;
2520 static struct service *spamserv_service;
2521 static struct log_type *SS_LOG;
2522 static unsigned long crc_table[256];
2524 dict_t registered_channels_dict;
2525 dict_t connected_users_dict;
2526 dict_t killed_users_dict;
2528 #define spamserv_notice(target, format...) send_message(target , spamserv , ## format)
2529 #define spamserv_debug(format...) do { if(spamserv_conf.debug_channel) send_channel_message(spamserv_conf.debug_channel , spamserv , ## format); } while(0)
2530 #define spamserv_oper_message(format...) do { if(spamserv_conf.oper_channel) send_channel_message(spamserv_conf.oper_channel , spamserv , ## format); } while(0)
2531 #define ss_reply(format...) send_message(user , spamserv , ## format)
2533 #define SET_SUBCMDS_SIZE 10
2535 const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};
2537 static void spamserv_clear_spamNodes(struct chanNode *channel);
2538 static void spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban);
2539 static unsigned long crc32(const char *text);
2541 #define BINARY_OPTION(arguments...) return binary_option(arguments, user, channel, argc, argv);
2542 #define MULTIPLE_OPTION(arguments...) return multiple_option(arguments, values, ArrayLength(values), user, channel, argc, argv);
2544 static const struct message_entry msgtab[] = {
2545 { "SSMSG_CHANNEL_OPTIONS", "Channel Options:" },
2546 { "SSMSG_STRING_VALUE", "$b%s$b%s" },
2547 { "SSMSG_NUMERIC_VALUE", "$b%s$b%d - %s" },
2548 { "SSMSG_EASYNUMERIC_VALUE", "$b%s$b%d" },
2549 { "SSMSG_INVALID_NUM_SET", "$b'%d'$b is an invalid %s setting." },
2550 { "SSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
2551 { "SSMSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },
2553 { "SSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$X$b." },
2554 { "SSMSG_NOT_REGISTERED_CS", "$b%s$b has not been registered with $b$C$b." },
2555 { "SSMSG_ALREADY_REGISTERED", "$b%s$b is already registered." },
2556 { "SSMSG_DEBUG_CHAN", "You may not register the debug channel." },
2557 { "SSMSG_SUSPENDED_CS", "$b$C$b access to $b%s$b has been temporarily suspended, thus you can't %s it." },
2558 { "SSMSG_SUSPENDED", "$b$X$b access to $b%s$b has been temporarily suspended." },
2559 { "SSMSG_NO_REGISTER", "Due to an error it was not possible to register $b%s$b." },
2560 { "SSMSG_REG_SUCCESS", "Channel $b%s$b registered." },
2561 { "SSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
2562 { "SSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
2563 { "SSMSG_MUST_BE_OPER", "You must be an irc operator to set this option." },
2564 { "SSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must append 'CONFIRM' to the end of your command. For example, 'unregister CONFIRM'." },
2566 { "SSMSG_NO_EXCEPTIONS", "No words found in the exception list." },
2567 { "SSMSG_NO_SUCH_EXCEPTION", "Word $b%s$b not found in the exception list." },
2568 { "SSMSG_EXCEPTION_LIST", "The following words are in the exception list:" },
2569 { "SSMSG_EXCEPTION_ADDED", "Word $b%s$b added to the exception list." },
2570 { "SSMSG_EXCEPTION_DELETED", "Word $b%s$b deleted from the exception list." },
2571 { "SSMSG_EXCEPTION_IN_LIST", "The word $b%s$b is already in the exception list." },
2572 { "SSMSG_EXCEPTION_MAX", "The exception list has reached the maximum exceptions (max %lu). Delete a word to add another one." },
2573 { "SSMSG_EXCEPTION_TOO_SHORT", "The word must be at least %lu characters long." },
2574 { "SSMSG_EXCEPTION_TOO_LONG", "The word may not be longer than %lu characters." },
2576 { "SSMSG_STATUS", "$bStatus:$b" },
2577 { "SSMSG_STATUS_USERS", "Total Users Online: %u" },
2578 { "SSMSG_STATUS_CHANNELS", "Registered Channels: %u" },
2579 { "SSMSG_STATUS_MEMORY", "$bMemory Information:$b" },
2580 { "SSMSG_STATUS_CHANNEL_LIST", "$bRegistered Channels:$b" },
2581 { "SSMSG_STATUS_NO_CHANNEL", "No channels registered." },
2585 #define SSMSG_DEBUG_KICK "Kicked user $b%s$b from $b%s$b, reason: %s"
2586 #define SSMSG_DEBUG_BAN "Banned user $b%s$b from $b%s$b, reason: %s"
2587 #define SSMSG_DEBUG_KILL "Killed user $b%s$b, last violation in $b%s$b"
2588 #define SSMSG_DEBUG_GLINE "Glined user $b%s$b, host $b%s$b, last violation in $b%s$b"
2589 #define SSMSG_DEBUG_RECONNECT "Killed user $b%s$b reconnected to the network"
2590 #define SSMSG_SPAM "Spamming"
2591 #define SSMSG_FLOOD "Flooding the channel/network"
2592 #define SSMSG_ADV "Advertising"
2593 #define SSMSG_JOINFLOOD "Join flooding the channel"
2594 #define SSMSG_WARNING "%s is against the network rules"
2595 #define SSMSG_WARNING_2 "You are violating the network rules"
2596 #define SSMSG_WARNING_RULES "%s is against the network rules. Read the network rules at %s"
2597 #define SSMSG_WARNING_RULES_2 "You are violating the network rules. Read the network rules at %s"
2598 #define SSMSG_CHANNEL_REGISTERED "%s (channel %s) registered by %s."
2599 #define SSMSG_CHANNEL_UNREGISTERED "%s (channel %s) unregistered by %s."
2603 struct chanNode *debug_channel;
2604 struct chanNode *oper_channel;
2605 struct string_list *global_exceptions;
2606 const char *network_rules;
2607 unsigned char trigger;
2608 unsigned long short_ban_duration;
2609 unsigned long long_ban_duration;
2610 unsigned long gline_duration;
2611 unsigned long exception_max;
2612 unsigned long exception_min_len;
2613 unsigned long exception_max_len;
2614 unsigned int adv_chan_must_exist : 1;
2615 unsigned int strip_mirc_codes : 1;
2616 unsigned int allow_move_merge : 1;
2619 /***********************************************/
2621 /***********************************************/
2624 get_chanInfo(const char *channelname)
2626 return dict_find(registered_channels_dict, channelname, 0);
2630 spamserv_join_channel(struct chanNode *channel)
2632 struct mod_chanmode change;
2633 mod_chanmode_init(&change);
2635 change.args[0].mode = MODE_CHANOP;
2636 change.args[0].u.member = AddChannelUser(spamserv, channel);
2637 mod_chanmode_announce(spamserv, channel, &change);
2641 spamserv_part_channel(struct chanNode *channel, char *reason)
2643 /* we only have to clear the spamNodes because every other node expires on it's own */
2644 spamserv_clear_spamNodes(channel);
2645 DelChannelUser(spamserv, channel, reason, 0);
2648 static struct chanInfo*
2649 spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)
2651 struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));
2655 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);
2659 cInfo->channel = channel;
2660 cInfo->exceptions = exceptions ? string_list_copy(exceptions) : alloc_string_list(1);
2661 cInfo->flags = flags;
2662 cInfo->exceptlevel = 400;
2663 safestrncpy(cInfo->info, info, sizeof(cInfo->info));
2664 cInfo->suspend_expiry = 0;
2665 dict_insert(registered_channels_dict, cInfo->channel->name, cInfo);
2671 spamserv_unregister_channel(struct chanInfo *cInfo)
2676 dict_remove(registered_channels_dict, cInfo->channel->name);
2677 free_string_list(cInfo->exceptions);
2682 spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)
2684 struct chanInfo *cInfo = get_chanInfo(channel->name);
2690 cInfo->flags |= CHAN_SUSPENDED;
2691 cInfo->suspend_expiry = expiry;
2692 spamserv_part_channel(channel, reason);
2696 if(CHECK_SUSPENDED(cInfo))
2698 cInfo->flags &= ~CHAN_SUSPENDED;
2699 cInfo->suspend_expiry = 0;
2706 spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)
2708 struct chanInfo *cInfo = get_chanInfo(channel->name);
2712 char reason[MAXLEN];
2714 if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))
2717 snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);
2719 snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);
2721 spamserv_cs_unregister(user, channel, manually, reason);
2725 cInfo->channel = target;
2727 dict_remove(registered_channels_dict, channel->name);
2728 dict_insert(registered_channels_dict, target->name, cInfo);
2732 snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2736 spamserv_join_channel(target);
2737 snprintf(reason, sizeof(reason), "%s merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
2740 if(!CHECK_SUSPENDED(cInfo))
2741 spamserv_part_channel(channel, reason);
2744 snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2746 snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
2748 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2756 spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)
2758 struct chanInfo *cInfo = get_chanInfo(channel->name);
2762 char global[MAXLEN], partmsg[MAXLEN];
2767 snprintf(global, sizeof(global), "$X (channel %s) %s by %s.", channel->name, reason, user->handle_info->handle);
2768 snprintf(partmsg, sizeof(partmsg), "%s %s by %s.", channel->name, reason, user->handle_info->handle);
2771 snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name);
2772 snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name);
2774 case lost_all_users:
2775 snprintf(global, sizeof(global), "$X (channel %s) lost all users.", channel->name);
2776 snprintf(partmsg, sizeof(partmsg), "%s lost all users.", channel->name);
2780 if(!CHECK_SUSPENDED(cInfo))
2781 spamserv_part_channel(channel, partmsg);
2783 spamserv_unregister_channel(cInfo);
2784 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, global);
2788 /***********************************************/
2790 /***********************************************/
2792 static struct userInfo*
2793 get_userInfo(const char *nickname)
2795 return dict_find(connected_users_dict, nickname, 0);
2799 spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)
2801 struct spamNode *sNode = malloc(sizeof(struct spamNode));
2805 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);
2809 sNode->channel = channel;
2810 sNode->crc32 = crc32(text);
2816 struct spamNode *temp = uInfo->spam;
2827 uInfo->spam = sNode;
2832 spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)
2837 if(sNode == uInfo->spam)
2838 uInfo->spam = sNode->next;
2841 sNode->next->prev = sNode->prev;
2843 sNode->prev->next = sNode->next;
2849 spamserv_clear_spamNodes(struct chanNode *channel)
2851 struct userInfo *uInfo;
2852 struct spamNode *sNode;
2855 for(i = 0; i < channel->members.used; i++)
2857 if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))
2859 if((sNode = uInfo->spam))
2861 for(; sNode; sNode = sNode->next)
2862 if(sNode->channel == channel)
2866 spamserv_delete_spamNode(uInfo, sNode);
2873 spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)
2875 struct floodNode *fNode = malloc(sizeof(struct floodNode));
2879 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);
2883 fNode->channel = channel;
2884 fNode->owner = user;
2891 struct floodNode *temp = *uI_fNode;
2907 spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)
2912 if(fNode == *uI_fNode)
2913 *uI_fNode = fNode->next;
2916 fNode->next->prev = fNode->prev;
2918 fNode->prev->next = fNode->next;
2924 spamserv_create_user(struct userNode *user)
2926 struct userInfo *uInfo = malloc(sizeof(struct userInfo));
2927 struct killNode *kNode = dict_find(killed_users_dict, irc_ntoa(&user->ip), 0);
2931 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);
2936 spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);
2940 uInfo->flood = NULL;
2941 uInfo->joinflood = NULL;
2942 uInfo->flags = kNode ? USER_KILLED : 0;
2943 uInfo->warnlevel = kNode ? kNode->warnlevel : 0;
2946 dict_insert(connected_users_dict, user->nick, uInfo);
2950 dict_remove(killed_users_dict, irc_ntoa(&user->ip));
2956 spamserv_delete_user(struct userInfo *uInfo)
2963 spamserv_delete_spamNode(uInfo, uInfo->spam);
2967 spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);
2969 if(uInfo->joinflood)
2970 while(uInfo->joinflood)
2971 spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);
2973 dict_remove(connected_users_dict, uInfo->user->nick);
2978 spamserv_new_user_func(struct userNode *user)
2981 spamserv_create_user(user);
2985 spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))
2987 struct userInfo *uInfo = get_userInfo(user->nick);
2988 struct killNode *kNode;
2990 if(killer == spamserv)
2992 kNode = malloc(sizeof(struct killNode));
2996 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for killNode - nickname %s", user->nick);
2997 spamserv_delete_user(uInfo);
3001 if(uInfo->warnlevel > KILL_WARNLEVEL)
3002 kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;
3004 kNode->warnlevel = 0;
3008 dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);
3011 spamserv_delete_user(uInfo);
3015 spamserv_nick_change_func(struct userNode *user, const char *old_nick)
3017 struct userInfo *uInfo = get_userInfo(old_nick);
3019 dict_remove(connected_users_dict, old_nick);
3020 dict_insert(connected_users_dict, user->nick, uInfo);
3024 spamserv_user_join(struct modeNode *mNode)
3026 struct chanNode *channel = mNode->channel;
3027 struct userNode *user = mNode->user;
3028 struct chanInfo *cInfo;
3029 struct userInfo *uInfo;
3030 struct floodNode *jfNode;
3032 if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))
3036 if(!CHECK_CHANOPS(cInfo))
3038 //struct modeNode *mn = GetUserMode(channel, user);
3039 //if(mn->modes & MODE_CHANOP)
3041 if(check_user_level(channel, user, lvlGiveOps, 1, 0))
3045 if(!CHECK_VOICED(cInfo))
3047 if(check_user_level(channel, user, lvlGiveVoice, 1, 0))
3051 if(cInfo->exceptlevel == 0)
3053 if(cInfo->exceptlevel < 501) {
3054 struct userData *uData;
3055 if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
3060 if(!(jfNode = uInfo->joinflood))
3062 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
3066 for(; jfNode; jfNode = jfNode->next)
3067 if(jfNode->channel == channel)
3072 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
3079 if(jfNode->count > JOINFLOOD_MAX)
3081 char reason[MAXLEN];
3083 spamserv_delete_floodNode(&uInfo->joinflood, jfNode);
3084 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_JOINFLOOD, spamserv_conf.network_rules);
3085 spamserv_punish(channel, user, JOINFLOOD_B_DURATION, reason, 1);
3094 spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
3096 struct userNode *user = mn->user;
3097 struct chanNode *channel = mn->channel;
3098 struct userInfo *uInfo;
3099 struct spamNode *sNode;
3100 struct floodNode *fNode;
3102 if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))
3105 if((sNode = uInfo->spam))
3107 for(; sNode; sNode = sNode->next)
3108 if(sNode->channel == channel)
3112 spamserv_delete_spamNode(uInfo, sNode);
3115 if((fNode = uInfo->flood))
3117 for(; fNode; fNode = fNode->next)
3118 if(fNode->channel == channel)
3122 spamserv_delete_floodNode(&uInfo->flood, fNode);
3126 /***********************************************/
3128 /***********************************************/
3136 for(i = 0; i < 256; i++)
3140 for(j = 8; j > 0; j--)
3144 crc = (crc >> 1) ^ 0xEDB88320L;
3156 static unsigned long
3157 crc32(const char *text)
3159 register unsigned long crc = 0xFFFFFFFF;
3160 unsigned int c, i = 0;
3162 while((c = (unsigned int)text[i++]) != 0)
3163 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];
3165 return (crc^0xFFFFFFFF);
3169 timeq_flood(UNUSED_ARG(void *data))
3172 struct userInfo *uInfo;
3173 struct floodNode *fNode;
3175 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3177 uInfo = iter_data(it);
3179 if(!(fNode = uInfo->flood))
3182 for(; fNode; fNode = fNode->next)
3184 if(now - fNode->time > FLOOD_EXPIRE)
3186 if(!(--fNode->count))
3187 spamserv_delete_floodNode(&uInfo->flood, fNode);
3192 timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
3196 timeq_joinflood(UNUSED_ARG(void *data))
3199 struct userInfo *uInfo;
3200 struct floodNode *fNode;
3202 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3204 uInfo = iter_data(it);
3206 if(!(fNode = uInfo->joinflood))
3209 for(; fNode; fNode = fNode->next)
3211 if(now - fNode->time > JOINFLOOD_EXPIRE)
3213 if(!(--fNode->count))
3214 spamserv_delete_floodNode(&uInfo->joinflood, fNode);
3219 timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
3223 timeq_adv(UNUSED_ARG(void *data))
3226 struct userInfo *uInfo;
3228 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3230 uInfo = iter_data(it);
3232 if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)
3235 uInfo->flags &= ~USER_ADV_WARNED;
3239 timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
3243 timeq_warnlevel(UNUSED_ARG(void *data))
3246 struct userInfo *uInfo;
3248 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3250 uInfo = iter_data(it);
3252 if(uInfo->warnlevel > 0)
3256 timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
3260 timeq_kill(UNUSED_ARG(void *data))
3263 struct killNode *kNode;
3265 for(it = dict_first(killed_users_dict); it; it = iter_next(it))
3267 kNode = iter_data(it);
3269 if(kNode->time - now > KILL_EXPIRE)
3273 timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
3277 binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
3279 struct chanInfo *cInfo = get_chanInfo(channel->name);
3284 if(enabled_string(argv[1]))
3286 cInfo->flags |= mask;
3289 else if(disabled_string(argv[1]))
3291 cInfo->flags &= ~mask;
3296 spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);
3302 value = (cInfo->flags & mask) ? 1 : 0;
3305 spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");
3317 multiple_option(char *name, char *description, enum channelinfo info, struct valueData *values, int count, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
3319 struct chanInfo *cInfo = get_chanInfo(channel->name);
3324 index = atoi(argv[1]);
3326 if(index < 0 || index >= count)
3328 spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);
3330 for(index = 0; index < count; index++)
3331 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
3336 if(values[index].oper_only && !IsOper(user))
3338 spamserv_notice(user, "SSMSG_MUST_BE_OPER");
3342 cInfo->info[info] = values[index].value;
3346 for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);
3349 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
3354 show_exceptions(struct userNode *user, struct chanInfo *cInfo)
3356 struct helpfile_table table;
3359 if(!cInfo->exceptions->used)
3361 spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");
3365 spamserv_notice(user, "SSMSG_EXCEPTION_LIST");
3369 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3370 table.contents = alloca(cInfo->exceptions->used * sizeof(*table.contents));
3372 for(i = 0; i < cInfo->exceptions->used; i++)
3374 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
3375 table.contents[table.length][0] = cInfo->exceptions->list[i];
3379 table_send(spamserv, user->nick, 0, NULL, table);
3385 show_memory_usage(struct userNode *user)
3388 struct helpfile_table table;
3389 struct chanInfo *cInfo;
3390 struct userInfo *uInfo;
3391 struct spamNode *sNode;
3392 struct floodNode *fNode;
3393 double channel_size = 0, user_size, size;
3394 unsigned int spamcount = 0, floodcount = 0, i, j;
3397 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
3399 cInfo = iter_data(it);
3401 if(!cInfo->exceptions->used)
3404 for(i = 0; i < cInfo->exceptions->used; i++)
3405 channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char);
3408 for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3410 uInfo = iter_data(it);
3412 for(sNode = uInfo->spam; sNode; sNode = sNode->next, spamcount++);
3413 for(fNode = uInfo->flood; fNode; fNode = fNode->next, floodcount++);
3414 for(fNode = uInfo->joinflood; fNode; fNode = fNode->next, floodcount++);
3417 channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);
3419 user_size = dict_size(connected_users_dict) * sizeof(struct userInfo) +
3420 dict_size(killed_users_dict) * sizeof(struct killNode) +
3421 spamcount * sizeof(struct spamNode) +
3422 floodcount * sizeof(struct floodNode);
3424 size = channel_size + user_size;
3426 ss_reply("SSMSG_STATUS_MEMORY");
3430 table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;
3431 table.contents = calloc(table.length, sizeof(char**));
3434 table.contents[0] = calloc(table.width, sizeof(char*));
3435 snprintf(buffer, sizeof(buffer), "Channel Memory Usage:");
3436 table.contents[0][0] = strdup(buffer);
3437 snprintf(buffer, sizeof(buffer), " %g Byte; ", channel_size);
3438 table.contents[0][1] = strdup(buffer);
3439 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", channel_size / 1024);
3440 table.contents[0][2] = strdup(buffer);
3441 snprintf(buffer, sizeof(buffer), "%g MegaByte", channel_size / 1024 / 1024);
3442 table.contents[0][3] = strdup(buffer);
3445 table.contents[1] = calloc(table.width, sizeof(char*));
3446 snprintf(buffer, sizeof(buffer), "User Memory Usage :");
3447 table.contents[1][0] = strdup(buffer);
3448 snprintf(buffer, sizeof(buffer), " %g Byte; ", user_size);
3449 table.contents[1][1] = strdup(buffer);
3450 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", user_size / 1024);
3451 table.contents[1][2] = strdup(buffer);
3452 snprintf(buffer, sizeof(buffer), "%g MegaByte", user_size / 1024 / 1024);
3453 table.contents[1][3] = strdup(buffer);
3455 // total memory usage
3456 table.contents[2] = calloc(table.width, sizeof(char*));
3457 snprintf(buffer, sizeof(buffer), "Total Memory Usage :");
3458 table.contents[2][0] = strdup(buffer);
3459 snprintf(buffer, sizeof(buffer), " %g Byte; ", size);
3460 table.contents[2][1] = strdup(buffer);
3461 snprintf(buffer, sizeof(buffer), "%g KiloByte; ", size / 1024);
3462 table.contents[2][2] = strdup(buffer);
3463 snprintf(buffer, sizeof(buffer), "%g MegaByte", size / 1024 / 1024);
3464 table.contents[2][3] = strdup(buffer);
3466 table_send(spamserv, user->nick, 0, NULL, table);
3468 for(i = 0; i < table.length; i++)
3470 for(j = 0; j < table.width; j++)
3471 free((char*)table.contents[i][j]);
3473 free(table.contents[i]);
3476 free(table.contents);
3480 show_registered_channels(struct userNode *user)
3482 struct helpfile_table table;
3485 spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");
3487 if(!dict_size(registered_channels_dict))
3489 spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");
3495 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3496 table.contents = alloca(dict_size(registered_channels_dict) * sizeof(*table.contents));
3498 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
3500 struct chanInfo *cInfo = iter_data(it);
3502 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
3503 table.contents[table.length][0] = cInfo->channel->name;
3507 table_send(spamserv, user->nick, 0, NULL, table);
3510 /***********************************************/
3512 /***********************************************/
3515 SPAMSERV_FUNC(cmd_register)
3517 struct chanInfo *cInfo;
3518 char reason[MAXLEN];
3520 if(!channel || !channel->channel_info)
3522 ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);
3526 if(get_chanInfo(channel->name))
3528 ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);
3532 if(IsSuspended(channel->channel_info))
3534 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");
3538 if(channel == spamserv_conf.debug_channel)
3540 ss_reply("SSMSG_DEBUG_CHAN");
3544 if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))
3546 ss_reply("SSMSG_NO_REGISTER", channel->name);
3550 spamserv_join_channel(cInfo->channel);
3552 spamserv_oper_message(SSMSG_CHANNEL_REGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
3553 ss_reply("SSMSG_REG_SUCCESS", channel->name);
3559 SPAMSERV_FUNC(cmd_unregister)
3561 struct chanInfo *cInfo;
3562 struct chanData *cData;
3563 struct userData *uData;
3564 char reason[MAXLEN];
3566 if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))
3568 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3572 if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))
3574 ss_reply("SSMSG_NO_ACCESS");
3578 if(!IsHelping(user))
3580 if(IsSuspended(cData))
3582 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");
3586 if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))
3588 ss_reply("SSMSG_CONFIRM_UNREG");
3593 if(!CHECK_SUSPENDED(cInfo))
3595 snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle);
3596 spamserv_part_channel(channel, reason);
3599 spamserv_unregister_channel(cInfo);
3601 spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, channel->name, user->handle_info->handle);
3602 ss_reply("SSMSG_UNREG_SUCCESS", channel->name);
3608 SPAMSERV_FUNC(cmd_status)
3610 ss_reply("SSMSG_STATUS");
3611 ss_reply("SSMSG_STATUS_USERS", dict_size(connected_users_dict));
3612 ss_reply("SSMSG_STATUS_CHANNELS", dict_size(registered_channels_dict));
3614 if(IsOper(user) && argc > 1)
3616 if(!irccasecmp(argv[1], "memory"))
3617 show_memory_usage(user);
3618 else if(!irccasecmp(argv[1], "channels"))
3619 show_registered_channels(user);
3626 SPAMSERV_FUNC(cmd_addexception)
3628 struct chanInfo *cInfo = get_chanInfo(channel->name);
3629 struct userData *uData;
3632 if(!cInfo || !channel->channel_info)
3634 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3638 if(CHECK_SUSPENDED(cInfo))
3640 ss_reply("SSMSG_SUSPENDED", channel->name);
3644 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
3646 ss_reply("SSMSG_NO_ACCESS");
3651 return show_exceptions(user, cInfo);
3653 if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))
3655 ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);
3659 if(strlen(argv[1]) < spamserv_conf.exception_min_len)
3661 ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);
3664 else if(strlen(argv[1]) > spamserv_conf.exception_max_len)
3666 ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);
3670 for(i = 0; i < cInfo->exceptions->used; i++)
3672 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
3674 ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);
3679 string_list_append(cInfo->exceptions, strdup(argv[1]));
3680 ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);
3686 SPAMSERV_FUNC(cmd_delexception)
3688 struct chanInfo *cInfo = get_chanInfo(channel->name);
3689 struct userData *uData;
3693 if(!cInfo || !channel->channel_info)
3695 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3699 if(CHECK_SUSPENDED(cInfo))
3701 ss_reply("SSMSG_SUSPENDED", channel->name);
3705 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
3707 ss_reply("SSMSG_NO_ACCESS");
3712 return show_exceptions(user, cInfo);
3714 for(i = 0; i < cInfo->exceptions->used; i++)
3716 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
3725 ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);
3729 string_list_delete(cInfo->exceptions, i);
3730 ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);
3736 SPAMSERV_FUNC(cmd_set)
3738 struct chanInfo *cInfo = get_chanInfo(channel->name);
3739 struct svccmd *subcmd;
3740 char cmd_name[MAXLEN];
3745 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3749 if(CHECK_SUSPENDED(cInfo))
3751 ss_reply("SSMSG_SUSPENDED", channel->name);
3755 if(!check_user_level(channel,user,lvlSetters,1,0))
3757 ss_reply("SSMSG_NO_ACCESS");
3763 ss_reply("SSMSG_CHANNEL_OPTIONS");
3765 for(i = 0; i < SET_SUBCMDS_SIZE; i++)
3767 sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);
3769 if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))
3770 subcmd->command->func(user, channel, 1, argv + 1, subcmd);
3776 sprintf(cmd_name, "%s %s", cmd->name, argv[1]);
3777 subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);
3781 reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);
3785 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
3789 SPAMSERV_FUNC(opt_spamlimit)
3791 struct valueData values[] =
3793 {"Users may send the same message $b2$b times.", 'a', 0},
3794 {"Users may send the same message $b3$b times.", 'b', 0},
3795 {"Users may send the same message $b4$b times.", 'c', 0},
3796 {"Users may send the same message $b5$b times.", 'd', 0},
3797 {"Users may send the same message $b6$b times.", 'e', 0}
3800 MULTIPLE_OPTION("SpamLimit ", "SpamLimit", ci_SpamLimit);
3804 SPAMSERV_FUNC(opt_advreaction)
3806 struct valueData values[] =
3808 {"Kick on disallowed advertising.", 'k', 0},
3809 {"Kickban on disallowed advertising.", 'b', 0},
3810 {"Short timed ban on disallowed advertising.", 's', 0},
3811 {"Long timed ban on disallowed advertising.", 'l', 0},
3812 {"Kill on disallowed advertising.", 'd', 1}
3815 MULTIPLE_OPTION("AdvReaction ", "AdvReaction", ci_AdvReaction);
3819 SPAMSERV_FUNC(opt_warnreaction)
3821 struct valueData values[] =
3823 {"Kick after warning.", 'k', 0},
3824 {"Kickban after warning.", 'b', 0},
3825 {"Short timed ban after warning.", 's', 0},
3826 {"Long timed ban after warning.", 'l', 0},
3827 {"Kill after warning.", 'd', 1}
3830 MULTIPLE_OPTION("WarnReaction ", "WarnReaction", ci_WarnReaction);
3834 SPAMSERV_FUNC(opt_advscan)
3836 BINARY_OPTION("AdvScan ", CHAN_ADV_SCAN);
3840 SPAMSERV_FUNC(opt_spamscan)
3842 BINARY_OPTION("SpamScan ", CHAN_SPAMSCAN);
3846 SPAMSERV_FUNC(opt_floodscan)
3848 BINARY_OPTION("FloodScan ", CHAN_FLOODSCAN);
3852 SPAMSERV_FUNC(opt_joinflood)
3854 BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);
3858 SPAMSERV_FUNC(opt_scanops)
3860 BINARY_OPTION("ScanChanOps ", CHAN_SCAN_CHANOPS);
3864 SPAMSERV_FUNC(opt_scanvoiced)
3866 BINARY_OPTION("ScanVoiced ", CHAN_SCAN_VOICED);
3870 SPAMSERV_FUNC(opt_exceptlevel)
3872 struct chanInfo *cInfo = get_chanInfo(channel->name);
3873 struct userData *uData;
3877 index = atoi(argv[1]);
3878 if(index < 1 || index > 501)
3880 spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");
3883 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))
3885 ss_reply("SSMSG_NO_ACCESS");
3888 cInfo->exceptlevel=index;
3890 index=cInfo->exceptlevel;
3892 spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel ", index);
3897 to_lower(char *message)
3899 unsigned int i, diff = 'a' - 'A';
3901 for(i = 0; i < strlen(message); i++)
3903 if((message[i] >= 'A') && (message[i] <= 'Z'))
3904 message[i] = message[i] + diff;
3909 strip_mirc_codes(char *text)
3911 // taken from xchat and modified
3912 int nc = 0, i = 0, col = 0, len = strlen(text);
3913 static char new_str[MAXLEN];
3917 if((col && isdigit(*text) && nc < 2) ||
3918 (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))
3957 is_in_exception_list(struct chanInfo *cInfo, char *message)
3961 for(i = 0; i < cInfo->exceptions->used; i++)
3962 if(strstr(message, cInfo->exceptions->list[i]))
3969 check_advertising(struct chanInfo *cInfo, char *message)
3973 if(spamserv_conf.strip_mirc_codes)
3974 message = strip_mirc_codes(message);
3976 if(is_in_exception_list(cInfo, message))
3979 while(message[i] != 0)
3981 if(message[i] == '#')
3983 char channelname[CHANNELLEN];
3986 if(!spamserv_conf.adv_chan_must_exist)
3989 /* only return 1, if the channel does exist */
3991 while((message[i] != 0) && (message[i] != ' '))
3993 channelname[j] = message[i];
3998 channelname[j] = '\0';
4000 if(GetChannel(channelname))
4003 else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))
4005 else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))
4007 else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))
4016 struct banData *add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason);
4019 spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)
4023 struct mod_chanmode change;
4024 char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
4026 sanitize_ircmask(hostmask);
4029 add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);
4031 mod_chanmode_init(&change);
4033 change.args[0].mode = MODE_BAN;
4034 change.args[0].u.hostmask = hostmask;
4035 mod_chanmode_announce(spamserv, channel, &change);
4039 spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);
4042 spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);
4044 KickChannelUser(user, channel, spamserv, reason);
4048 spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)
4050 struct chanInfo *cInfo;
4051 struct userInfo *uInfo;
4052 struct spamNode *sNode;
4053 struct floodNode *fNode;
4054 unsigned int violation = 0;
4055 char reason[MAXLEN];
4057 /* make sure: spamserv is not disabled; srvx is running; spamserv is in the chan; chan is regged, user does exist */
4058 if(!spamserv || quit_services || !GetUserMode(channel, spamserv) || !(cInfo = get_chanInfo(channel->name)) || !(uInfo = get_userInfo(user->nick)))
4061 if(!CHECK_CHANOPS(cInfo))
4063 struct modeNode *mn = GetUserMode(channel, user);
4064 if(mn && mn->modes & MODE_CHANOP)
4066 //if(check_user_level(channel, user, lvlGiveOps, 1, 0))
4070 if(!CHECK_VOICED(cInfo))
4072 struct modeNode *mn = GetUserMode(channel, user);
4073 if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))
4077 if(cInfo->exceptlevel == 0)
4079 if(cInfo->exceptlevel < 501) {
4080 struct userData *uData;
4081 if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
4088 if(CHECK_SPAM(cInfo))
4090 if(!(sNode = uInfo->spam))
4092 spamserv_create_spamNode(channel, uInfo, text);
4096 for(; sNode; sNode = sNode->next)
4097 if(sNode->channel == channel)
4102 spamserv_create_spamNode(channel, uInfo, text);
4106 unsigned long crc = crc32(text);
4108 if(crc == sNode->crc32)
4110 unsigned int spamlimit = 2;
4113 switch(cInfo->info[ci_SpamLimit])
4115 case 'a': spamlimit = 2; break;
4116 case 'b': spamlimit = 3; break;
4117 case 'c': spamlimit = 4; break;
4118 case 'd': spamlimit = 5; break;
4119 case 'e': spamlimit = 6; break;
4122 if(sNode->count == spamlimit)
4124 uInfo->warnlevel += SPAM_WARNLEVEL;
4126 if(uInfo->warnlevel < MAX_WARNLEVEL)
4127 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules);
4129 else if(sNode->count > spamlimit)
4131 switch(cInfo->info[ci_WarnReaction])
4133 case 'k': uInfo->flags |= USER_KICK; break;
4134 case 'b': uInfo->flags |= USER_KICKBAN; break;
4135 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
4136 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
4137 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
4140 spamserv_delete_spamNode(uInfo, sNode);
4141 uInfo->warnlevel += SPAM_WARNLEVEL;
4154 if(CHECK_FLOOD(cInfo))
4156 if(!(fNode = uInfo->flood))
4158 spamserv_create_floodNode(channel, user, &uInfo->flood);
4162 for(; fNode; fNode = fNode->next)
4163 if(fNode->channel == channel)
4168 spamserv_create_floodNode(channel, user, &uInfo->flood);
4172 if(((now - fNode->time) < FLOOD_EXPIRE))
4176 if(fNode->count == FLOOD_MAX_LINES - 1)
4178 uInfo->warnlevel += FLOOD_WARNLEVEL;
4180 if(uInfo->warnlevel < MAX_WARNLEVEL)
4181 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules);
4183 else if(fNode->count > FLOOD_MAX_LINES)
4185 switch(cInfo->info[ci_WarnReaction])
4187 case 'k': uInfo->flags |= USER_KICK; break;
4188 case 'b': uInfo->flags |= USER_KICKBAN; break;
4189 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
4190 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
4191 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
4194 spamserv_delete_floodNode(&uInfo->flood, fNode);
4195 uInfo->warnlevel += FLOOD_WARNLEVEL;
4205 if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))
4207 if(CHECK_ADV_WARNED(uInfo))
4209 switch(cInfo->info[ci_AdvReaction])
4211 case 'k': uInfo->flags |= USER_KICK; break;
4212 case 'b': uInfo->flags |= USER_KICKBAN; break;
4213 case 's': uInfo->flags |= USER_SHORT_TBAN; break;
4214 case 'l': uInfo->flags |= USER_LONG_TBAN; break;
4215 case 'd': uInfo->flags |= CHECK_KILLED(uInfo) ? USER_GLINE : USER_KILL; break;
4218 uInfo->warnlevel += ADV_WARNLEVEL;
4223 uInfo->flags |= USER_ADV_WARNED;
4224 uInfo->lastadv = now;
4225 uInfo->warnlevel += ADV_WARNLEVEL;
4227 if(uInfo->warnlevel < MAX_WARNLEVEL)
4228 spamserv_notice(user, spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules);
4232 if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)
4234 uInfo->flags |= USER_WARNED;
4235 snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules);
4236 irc_notice(spamserv, user->numeric, reason);
4237 irc_privmsg(spamserv, user->numeric, reason);
4239 else if(uInfo->warnlevel > MAX_WARNLEVEL)
4241 if(CHECK_KILLED(uInfo))
4242 uInfo->flags |= USER_GLINE;
4244 uInfo->flags |= USER_KILL;
4254 case 1: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_SPAM, spamserv_conf.network_rules); break;
4255 case 2: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_FLOOD, spamserv_conf.network_rules); break;
4256 case 3: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES : SSMSG_WARNING, SSMSG_ADV, spamserv_conf.network_rules); break;
4257 default: snprintf(reason, sizeof(reason), spamserv_conf.network_rules ? SSMSG_WARNING_RULES_2 : SSMSG_WARNING_2, spamserv_conf.network_rules); break;
4260 if(CHECK_GLINE(uInfo))
4262 int size = strlen(user->hostname) + 3;
4263 char *mask = alloca(size);
4264 snprintf(mask, size, "*@%s", user->hostname);
4265 gline_add(spamserv->nick, mask, spamserv_conf.gline_duration, reason, now, now, 0, 1);
4266 spamserv_debug(SSMSG_DEBUG_GLINE, user->nick, user->hostname, channel->name);
4268 else if(CHECK_KILL(uInfo))
4270 DelUser(user, spamserv, 1, reason);
4271 spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);
4273 else if(CHECK_LONG_TBAN(uInfo))
4275 spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);
4277 else if(CHECK_SHORT_TBAN(uInfo))
4279 spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);
4281 else if(CHECK_KICKBAN(uInfo))
4283 spamserv_punish(channel, user, 0, reason, 1);
4285 else if(CHECK_KICK(uInfo))
4287 spamserv_punish(channel, user, 0, reason, 0);
4292 spamserv_saxdb_read(struct dict *database)
4295 struct record_data *hir;
4296 struct chanNode *channel;
4297 struct chanInfo *cInfo;
4298 struct string_list *strlist;
4299 unsigned int flags,exceptlevel;
4301 unsigned long expiry;
4303 for(it = dict_first(database); it; it = iter_next(it))
4305 hir = iter_data(it);
4307 if(hir->type != RECDB_OBJECT)
4309 log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
4313 channel = GetChannel(iter_key(it));
4314 strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);
4316 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
4317 flags = str ? atoi(str) : 0;
4319 info = database_get_data(hir->d.object, KEY_INFO, RECDB_QSTRING);
4320 str = database_get_data(hir->d.object, KEY_EXCEPTLEVEL, RECDB_QSTRING);
4321 exceptlevel = str ? atoi(str) : 400;
4323 str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);
4324 expiry = str ? strtoul(str, NULL, 0) : 0;
4328 if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))
4330 /* if the channel is suspended and expiry = 0 it means: channel will
4331 never expire ! it does NOT mean, the channel is not suspended */
4332 if(CHECK_SUSPENDED(cInfo) && expiry && (expiry < now))
4334 cInfo->flags &= ~CHAN_SUSPENDED;
4335 spamserv_join_channel(cInfo->channel);
4337 else if(!CHECK_SUSPENDED(cInfo))
4338 spamserv_join_channel(cInfo->channel);
4340 cInfo->suspend_expiry = expiry;
4341 cInfo->exceptlevel=exceptlevel;
4345 log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it));
4352 spamserv_saxdb_write(struct saxdb_context *ctx)
4356 for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
4358 struct chanInfo *cInfo = iter_data(it);
4360 saxdb_start_record(ctx, cInfo->channel->name, 1);
4362 if(cInfo->exceptions->used)
4363 saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);
4366 saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags);
4368 saxdb_write_string(ctx, KEY_INFO, cInfo->info);
4369 saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel);
4371 if(cInfo->suspend_expiry)
4372 saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);
4374 saxdb_end_record(ctx);
4380 spamserv_conf_read(void)
4385 if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))
4387 log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);
4391 str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
4394 spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);
4396 if(spamserv_conf.debug_channel)
4397 spamserv_join_channel(spamserv_conf.debug_channel);
4401 spamserv_conf.debug_channel = NULL;
4404 str = database_get_data(conf_node, KEY_OPER_CHANNEL, RECDB_QSTRING);
4407 spamserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
4411 spamserv_conf.oper_channel = NULL;
4414 spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);
4416 str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);
4417 spamserv_conf.network_rules = str ? str : NULL;
4419 str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);
4420 spamserv_conf.trigger = str ? str[0] : 0;
4422 str = database_get_data(conf_node, KEY_SHORT_BAN_DURATION, RECDB_QSTRING);
4423 spamserv_conf.short_ban_duration = str ? ParseInterval(str) : ParseInterval("15m");
4425 str = database_get_data(conf_node, KEY_LONG_BAN_DURATION, RECDB_QSTRING);
4426 spamserv_conf.long_ban_duration = str ? ParseInterval(str) : ParseInterval("1h");
4428 str = database_get_data(conf_node, KEY_GLINE_DURATION, RECDB_QSTRING);
4429 spamserv_conf.gline_duration = str ? ParseInterval(str) : ParseInterval("1h");
4431 str = database_get_data(conf_node, KEY_EXCEPTION_MAX, RECDB_QSTRING);
4432 spamserv_conf.exception_max = str ? strtoul(str, NULL, 0) : 10;
4434 str = database_get_data(conf_node, KEY_EXCEPTION_MIN_LEN, RECDB_QSTRING);
4435 spamserv_conf.exception_min_len = str ? strtoul(str, NULL, 0) : 4;
4437 str = database_get_data(conf_node, KEY_EXCEPTION_MAX_LEN, RECDB_QSTRING);
4438 spamserv_conf.exception_max_len = str ? strtoul(str, NULL, 0) : 15;
4440 str = database_get_data(conf_node, KEY_ADV_CHAN_MUST_EXIST, RECDB_QSTRING);
4441 spamserv_conf.adv_chan_must_exist = str ? enabled_string(str) : 1;
4443 str = database_get_data(conf_node, KEY_STRIP_MIRC_CODES, RECDB_QSTRING);
4444 spamserv_conf.strip_mirc_codes = str ? enabled_string(str) : 0;
4446 str = database_get_data(conf_node, KEY_ALLOW_MOVE_MERGE, RECDB_QSTRING);
4447 spamserv_conf.allow_move_merge = str ? enabled_string(str) : 0;
4451 spamserv_db_cleanup(void)
4455 while((it = dict_first(registered_channels_dict)))
4457 spamserv_unregister_channel(iter_data(it));
4460 while((it = dict_first(killed_users_dict)))
4462 free(iter_data(it));
4465 dict_delete(registered_channels_dict);
4466 dict_delete(connected_users_dict);
4467 dict_delete(killed_users_dict);
4471 init_spamserv(const char *nick)
4476 const char *modes = conf_get_data("services/spamserv/modes", RECDB_QSTRING);
4477 spamserv = AddLocalUser(nick, nick, NULL, "Anti Spam Services", modes);
4478 spamserv_service = service_register(spamserv);
4479 service_register(spamserv)->trigger = spamserv_conf.trigger;
4481 conf_register_reload(spamserv_conf_read);
4483 SS_LOG = log_register_type("SpamServ", "file:spamserv.log");
4485 registered_channels_dict = dict_new();
4486 connected_users_dict = dict_new();
4487 killed_users_dict = dict_new();
4489 reg_new_user_func(spamserv_new_user_func);
4490 reg_del_user_func(spamserv_del_user_func);
4491 reg_nick_change_func(spamserv_nick_change_func);
4492 reg_join_func(spamserv_user_join);
4493 reg_part_func(spamserv_user_part);
4495 timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
4496 timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
4497 timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
4498 timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
4499 timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
4501 spamserv_module = module_register("SpamServ", SS_LOG, "spamserv.help", NULL);
4502 modcmd_register(spamserv_module, "REGISTER", cmd_register, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+acceptchan,+helping", NULL);
4503 modcmd_register(spamserv_module, "UNREGISTER", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, "flags", "+loghostmask", NULL);
4504 modcmd_register(spamserv_module, "ADDEXCEPTION", cmd_addexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4505 modcmd_register(spamserv_module, "DELEXCEPTION", cmd_delexception, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4506 modcmd_register(spamserv_module, "STATUS", cmd_status, 1, 0, NULL);
4507 modcmd_register(spamserv_module, "SET", cmd_set, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4508 modcmd_register(spamserv_module, "SET SPAMLIMIT", opt_spamlimit, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4509 modcmd_register(spamserv_module, "SET ADVREACTION", opt_advreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4510 modcmd_register(spamserv_module, "SET WARNREACTION", opt_warnreaction, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4511 modcmd_register(spamserv_module, "SET ADVSCAN", opt_advscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4512 modcmd_register(spamserv_module, "SET SPAMSCAN", opt_spamscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4513 modcmd_register(spamserv_module, "SET FLOODSCAN", opt_floodscan, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4514 modcmd_register(spamserv_module, "SET JOINFLOODSCAN", opt_joinflood, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4515 modcmd_register(spamserv_module, "SET SCANCHANOPS", opt_scanops, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4516 modcmd_register(spamserv_module, "SET SCANVOICED", opt_scanvoiced, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4517 modcmd_register(spamserv_module, "SET EXCEPTLEVEL", opt_exceptlevel, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
4519 saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write);
4520 reg_exit_func(spamserv_db_cleanup);
4521 message_register_table(msgtab);