Merge branch 'master' into SpamServ
[srvx.git] / src / spamserv.c
1 <<<<<<< HEAD
2 /* spamserv.c - anti spam service\r
3  * Copyright 2004 feigling\r
4  *\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
10  *\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
15  *\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
18  *\r
19  * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $\r
20  */\r
21 \r
22 #include "conf.h"\r
23 #include "spamserv.h"\r
24 #include "chanserv.h"\r
25 #include "global.h"\r
26 #include "modcmd.h"\r
27 #include "saxdb.h"\r
28 #include "timeq.h"\r
29 #include "gline.h"\r
30 \r
31 #define SPAMSERV_CONF_NAME           "services/spamserv"\r
32 \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
40 \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
45 \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
59 \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
63 (void)argv; \\r
64   if(argc < N) { \\r
65     ss_reply(MSG_MISSING_PARAMS, argv[0]); \\r
66     SPAMSERV_SYNTAX(); \\r
67     return 0; } } while(0)\r
68 \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
74 \r
75 dict_t registered_channels_dict;\r
76 dict_t connected_users_dict;\r
77 dict_t killed_users_dict;\r
78 \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
82 \r
83 #define SET_SUBCMDS_SIZE 10\r
84 \r
85 const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};\r
86 \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
90 \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
93 \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
102 \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
115 \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
125 \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
132 \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
142         { NULL, NULL }\r
143 };\r
144 \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
159 \r
160 static struct\r
161 {\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
175 } spamserv_conf;\r
176 \r
177 /***********************************************/\r
178 /*                   Channel                   */\r
179 /***********************************************/\r
180 \r
181 struct chanInfo*\r
182 get_chanInfo(const char *channelname)\r
183 {\r
184         return dict_find(registered_channels_dict, channelname, 0);\r
185 }\r
186 \r
187 static void\r
188 spamserv_join_channel(struct chanNode *channel)\r
189 {\r
190         struct mod_chanmode change;\r
191         mod_chanmode_init(&change);\r
192         change.argc = 1;\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
196 }\r
197 \r
198 static void\r
199 spamserv_part_channel(struct chanNode *channel, char *reason)\r
200 {\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
204 }\r
205 \r
206 static struct chanInfo*\r
207 spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)\r
208 {\r
209         struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));\r
210         \r
211         if(!cInfo)\r
212         {\r
213                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);\r
214                 return NULL;\r
215         }\r
216 \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
226 \r
227         return cInfo;\r
228 }\r
229 \r
230 static void\r
231 spamserv_unregister_channel(struct chanInfo *cInfo)\r
232 {\r
233         if(!cInfo)\r
234                 return;\r
235 \r
236         dict_remove(registered_channels_dict, cInfo->channel->name);\r
237         free_string_list(cInfo->exceptions);\r
238         free(cInfo);\r
239 }\r
240 \r
241 void\r
242 spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)\r
243 {\r
244         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
245 \r
246         if(cInfo)\r
247         {\r
248                 if(suspend)\r
249                 {\r
250                         cInfo->flags |= CHAN_SUSPENDED;\r
251                         cInfo->suspend_expiry = expiry;\r
252                         spamserv_part_channel(channel, reason);\r
253                 }\r
254                 else\r
255                 {\r
256                         if(CHECK_SUSPENDED(cInfo))\r
257                         {\r
258                                 cInfo->flags &= ~CHAN_SUSPENDED;\r
259                                 cInfo->suspend_expiry = 0;\r
260                         }\r
261                 }\r
262         }\r
263 }\r
264 \r
265 int\r
266 spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)\r
267 {\r
268         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
269 \r
270         if(cInfo)\r
271         {\r
272                 char reason[MAXLEN];\r
273 \r
274                 if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))\r
275                 {\r
276                         if(move)\r
277                                 snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);\r
278                         else\r
279                                 snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);\r
280 \r
281                         spamserv_cs_unregister(user, channel, manually, reason);\r
282                         return 0;\r
283                 }\r
284 \r
285                 cInfo->channel = target;\r
286 \r
287                 dict_remove(registered_channels_dict, channel->name);\r
288                 dict_insert(registered_channels_dict, target->name, cInfo);\r
289 \r
290                 if(move)\r
291                 {\r
292                         snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);\r
293                 }\r
294                 else\r
295                 {\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
298                 }\r
299 \r
300                 if(!CHECK_SUSPENDED(cInfo))\r
301                         spamserv_part_channel(channel, reason);\r
302 \r
303                 if(move)\r
304                         snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);\r
305                 else\r
306                         snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);\r
307 \r
308                 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);\r
309                 return 1;\r
310         }\r
311 \r
312         return 0;\r
313 }\r
314 \r
315 void\r
316 spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)\r
317 {\r
318         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
319 \r
320         if(cInfo)\r
321         {\r
322                 char global[MAXLEN], partmsg[MAXLEN];\r
323 \r
324                 switch (type)\r
325                 {\r
326                 case manually:\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
329                         break;\r
330                 case expire:\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
333                         break;\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
337                         break;\r
338                 }\r
339 \r
340                 if(!CHECK_SUSPENDED(cInfo))\r
341                         spamserv_part_channel(channel, partmsg);\r
342                 \r
343                 spamserv_unregister_channel(cInfo);\r
344                 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, global);\r
345         }\r
346 }\r
347 \r
348 /***********************************************/\r
349 /*                    User                     */\r
350 /***********************************************/\r
351 \r
352 static struct userInfo*\r
353 get_userInfo(const char *nickname)\r
354 {\r
355         return dict_find(connected_users_dict, nickname, 0);\r
356 }\r
357 \r
358 static void\r
359 spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)\r
360 {\r
361         struct spamNode *sNode = malloc(sizeof(struct spamNode));\r
362 \r
363         if(!sNode)\r
364         {\r
365                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);\r
366                 return;\r
367         }\r
368 \r
369         sNode->channel = channel;       \r
370         sNode->crc32 = crc32(text);\r
371         sNode->count = 1;\r
372         sNode->next = NULL;\r
373 \r
374         if(uInfo->spam)\r
375         {\r
376                 struct spamNode *temp = uInfo->spam;\r
377                 \r
378                 while(temp->next)\r
379                         temp = temp->next;\r
380 \r
381                 sNode->prev = temp;\r
382                 temp->next = sNode;\r
383         }\r
384         else\r
385         {\r
386                 sNode->prev = NULL;\r
387                 uInfo->spam = sNode;\r
388         }\r
389 }\r
390 \r
391 static void\r
392 spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)\r
393 {\r
394         if(!sNode)\r
395                 return;\r
396 \r
397         if(sNode == uInfo->spam)\r
398                 uInfo->spam = sNode->next;\r
399         \r
400         if(sNode->next)\r
401                  sNode->next->prev = sNode->prev;\r
402         if(sNode->prev)\r
403                  sNode->prev->next = sNode->next;\r
404 \r
405         free(sNode);\r
406 }\r
407 \r
408 static void\r
409 spamserv_clear_spamNodes(struct chanNode *channel)\r
410 {\r
411         struct userInfo *uInfo;\r
412         struct spamNode *sNode;\r
413         unsigned int i;\r
414 \r
415         for(i = 0; i < channel->members.used; i++)\r
416         {\r
417                 if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))\r
418                 {\r
419                         if((sNode = uInfo->spam))\r
420                         {\r
421                                 for(; sNode; sNode = sNode->next)\r
422                                         if(sNode->channel == channel)\r
423                                                 break;\r
424                                         \r
425                                 if(sNode)\r
426                                         spamserv_delete_spamNode(uInfo, sNode);\r
427                         }\r
428                 }\r
429         }\r
430 }\r
431 \r
432 static void\r
433 spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)\r
434 {\r
435         struct floodNode *fNode = malloc(sizeof(struct floodNode));\r
436 \r
437         if(!fNode)\r
438         {\r
439                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);\r
440                 return;\r
441         }\r
442 \r
443         fNode->channel = channel;\r
444         fNode->owner = user;\r
445         fNode->count = 1;\r
446         fNode->time = now;      \r
447         fNode->next = NULL;\r
448 \r
449         if(*uI_fNode)\r
450         {\r
451                 struct floodNode *temp = *uI_fNode;\r
452                 \r
453                 while(temp->next)\r
454                         temp = temp->next;\r
455                 \r
456                 fNode->prev = temp;\r
457                 temp->next = fNode;\r
458         }\r
459         else\r
460         {\r
461                 fNode->prev = NULL;\r
462                 *uI_fNode = fNode;\r
463         }\r
464 }\r
465 \r
466 static void\r
467 spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)\r
468 {\r
469         if(!fNode)\r
470                 return;\r
471 \r
472         if(fNode == *uI_fNode)\r
473                 *uI_fNode = fNode->next;\r
474         \r
475         if(fNode->next)\r
476                  fNode->next->prev = fNode->prev;\r
477         if(fNode->prev)\r
478                  fNode->prev->next = fNode->next;\r
479 \r
480         free(fNode);\r
481 }\r
482 \r
483 static void\r
484 spamserv_create_user(struct userNode *user)\r
485 {\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
488 \r
489         if(!uInfo)\r
490         {\r
491                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);\r
492                 return;\r
493         }\r
494 \r
495         if(kNode)\r
496                 spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);\r
497 \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
505 \r
506         dict_insert(connected_users_dict, user->nick, uInfo);\r
507 \r
508         if(kNode)\r
509         {\r
510                 dict_remove(killed_users_dict, irc_ntoa(&user->ip));\r
511                 free(kNode);\r
512         }\r
513 }\r
514 \r
515 static void\r
516 spamserv_delete_user(struct userInfo *uInfo)\r
517 {\r
518         if(!uInfo)\r
519                 return;\r
520 \r
521         if(uInfo->spam)\r
522                 while(uInfo->spam)\r
523                         spamserv_delete_spamNode(uInfo, uInfo->spam);   \r
524 \r
525         if(uInfo->flood)\r
526                 while(uInfo->flood)\r
527                         spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);\r
528 \r
529         if(uInfo->joinflood)\r
530                 while(uInfo->joinflood)\r
531                         spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);\r
532 \r
533         dict_remove(connected_users_dict, uInfo->user->nick);\r
534         free(uInfo);\r
535 }\r
536 \r
537 static void\r
538 spamserv_new_user_func(struct userNode *user)\r
539 {\r
540         if(!IsLocal(user))\r
541                 spamserv_create_user(user);\r
542 }\r
543 \r
544 static void\r
545 spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))\r
546 {\r
547         struct userInfo *uInfo = get_userInfo(user->nick);\r
548         struct killNode *kNode;\r
549 \r
550         if(killer == spamserv)\r
551         {\r
552                 kNode = malloc(sizeof(struct killNode));\r
553 \r
554                 if(!kNode)\r
555                 {\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
558                         return;\r
559                 }\r
560 \r
561                 if(uInfo->warnlevel > KILL_WARNLEVEL)\r
562                         kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;\r
563                 else\r
564                         kNode->warnlevel = 0;\r
565 \r
566                 kNode->time = now;\r
567 \r
568                 dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);\r
569         }\r
570 \r
571         spamserv_delete_user(uInfo);    \r
572 }\r
573 \r
574 static void\r
575 spamserv_nick_change_func(struct userNode *user, const char *old_nick)\r
576 {\r
577         struct userInfo *uInfo = get_userInfo(old_nick);\r
578 \r
579         dict_remove(connected_users_dict, old_nick);\r
580         dict_insert(connected_users_dict, user->nick, uInfo);\r
581 }\r
582 \r
583 static int\r
584 spamserv_user_join(struct modeNode *mNode)\r
585 {\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
591 \r
592         if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))\r
593                 return 0;\r
594         \r
595         \r
596     if(!CHECK_CHANOPS(cInfo))\r
597         {\r
598                 //struct modeNode *mn = GetUserMode(channel, user);\r
599                 //if(mn->modes & MODE_CHANOP)\r
600                 //      return;\r
601         if(check_user_level(channel, user, lvlGiveOps, 1, 0)) \r
602             return 0;\r
603         }\r
604     \r
605     if(!CHECK_VOICED(cInfo))\r
606         {\r
607         if(check_user_level(channel, user, lvlGiveVoice, 1, 0)) \r
608             return 0;\r
609     }\r
610     \r
611     if(cInfo->exceptlevel == 0)\r
612         return 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
616         return 0;\r
617        }\r
618     }\r
619 \r
620         if(!(jfNode = uInfo->joinflood))\r
621         {\r
622                 spamserv_create_floodNode(channel, user, &uInfo->joinflood);\r
623         }\r
624         else\r
625         {\r
626                 for(; jfNode; jfNode = jfNode->next)\r
627                         if(jfNode->channel == channel)\r
628                                 break;\r
629 \r
630                 if(!jfNode)\r
631                 {\r
632                         spamserv_create_floodNode(channel, user, &uInfo->joinflood);\r
633                 }\r
634                 else\r
635                 {\r
636                         jfNode->count++;\r
637                         jfNode->time = now;             \r
638 \r
639                         if(jfNode->count > JOINFLOOD_MAX)\r
640                         {\r
641                                 char reason[MAXLEN];\r
642 \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
646                         }\r
647                 }\r
648         }\r
649 \r
650         return 0;\r
651 }\r
652 \r
653 static void\r
654 spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))\r
655 {\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
661 \r
662         if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))\r
663                 return;\r
664 \r
665         if((sNode = uInfo->spam))\r
666         {\r
667                 for(; sNode; sNode = sNode->next)\r
668                         if(sNode->channel == channel)\r
669                                 break;\r
670 \r
671                 if(sNode)\r
672                         spamserv_delete_spamNode(uInfo, sNode);\r
673         }\r
674 \r
675         if((fNode = uInfo->flood))\r
676         {\r
677                 for(; fNode; fNode = fNode->next)\r
678                         if(fNode->channel == channel)\r
679                                 break;\r
680 \r
681                 if(fNode)\r
682                         spamserv_delete_floodNode(&uInfo->flood, fNode);\r
683         }\r
684 }\r
685 \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
688 {\r
689     struct badword *badword;\r
690 \r
691     badword = calloc(1, sizeof(*badword));\r
692     if (!badword)\r
693         return NULL;\r
694 \r
695     if(!id) {\r
696         cInfo->last_badword_id++;\r
697         badword->id = strtab(cInfo->last_badword_id);\r
698     } else\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
704     return badword;\r
705 }\r
706 \r
707 /***********************************************/\r
708 /*                 Other Stuff                 */\r
709 /***********************************************/\r
710 \r
711 static void\r
712 crc32_init(void)\r
713 {\r
714         unsigned long crc;\r
715         int i, j;\r
716 \r
717         for(i = 0; i < 256; i++)\r
718         {\r
719                 crc = i;\r
720 \r
721                 for(j = 8; j > 0; j--)\r
722                 {\r
723                         if(crc & 1)\r
724                         {\r
725                                 crc = (crc >> 1) ^ 0xEDB88320L;\r
726                         }\r
727                         else\r
728                         {\r
729                                 crc >>= 1;\r
730                         }\r
731                 }\r
732 \r
733                 crc_table[i] = crc;\r
734         }\r
735 }\r
736 \r
737 static unsigned long\r
738 crc32(const char *text)\r
739 {\r
740         register unsigned long crc = 0xFFFFFFFF;\r
741         unsigned int c, i = 0;\r
742         \r
743         while((c = (unsigned int)text[i++]) != 0)\r
744                 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];\r
745  \r
746         return (crc^0xFFFFFFFF);\r
747 }\r
748 \r
749 static void\r
750 timeq_flood(UNUSED_ARG(void *data))\r
751 {\r
752         dict_iterator_t         it;\r
753         struct userInfo         *uInfo;\r
754         struct floodNode        *fNode;\r
755 \r
756         for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
757         {\r
758                 uInfo = iter_data(it);\r
759 \r
760                 if(!(fNode = uInfo->flood))\r
761                         continue;\r
762 \r
763                 for(; fNode; fNode = fNode->next)\r
764                 {\r
765                         if(now - fNode->time > FLOOD_EXPIRE)\r
766                         {\r
767                                 if(!(--fNode->count))\r
768                                         spamserv_delete_floodNode(&uInfo->flood, fNode);\r
769                         }\r
770                 }\r
771         }\r
772         \r
773         timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);\r
774 }\r
775 \r
776 static void\r
777 timeq_joinflood(UNUSED_ARG(void *data))\r
778 {\r
779         dict_iterator_t it;\r
780         struct userInfo *uInfo;\r
781         struct floodNode *fNode;\r
782 \r
783         for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
784         {\r
785                 uInfo = iter_data(it);\r
786 \r
787                 if(!(fNode = uInfo->joinflood))\r
788                         continue;\r
789 \r
790                 for(; fNode; fNode = fNode->next)\r
791                 {\r
792                         if(now - fNode->time > JOINFLOOD_EXPIRE)\r
793                         {\r
794                                 if(!(--fNode->count))\r
795                                         spamserv_delete_floodNode(&uInfo->joinflood, fNode);                            \r
796                         }\r
797                 }\r
798         }\r
799 \r
800         timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);\r
801 }\r
802 \r
803 static void\r
804 timeq_adv(UNUSED_ARG(void *data))\r
805 {\r
806         dict_iterator_t it;\r
807         struct userInfo *uInfo;\r
808 \r
809         for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
810         {\r
811                 uInfo = iter_data(it);\r
812 \r
813                 if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)\r
814                 {\r
815                         uInfo->lastadv = 0;\r
816                         uInfo->flags &= ~USER_ADV_WARNED;\r
817                 }\r
818         }\r
819 \r
820         timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);\r
821 }\r
822 \r
823 static void\r
824 timeq_warnlevel(UNUSED_ARG(void *data))\r
825 {\r
826         dict_iterator_t it;\r
827         struct userInfo *uInfo;\r
828 \r
829         for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
830         {\r
831                 uInfo = iter_data(it);\r
832 \r
833                 if(uInfo->warnlevel > 0)\r
834                         uInfo->warnlevel--;\r
835         }\r
836 \r
837         timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);\r
838 }\r
839 \r
840 static void\r
841 timeq_kill(UNUSED_ARG(void *data))\r
842 {\r
843         dict_iterator_t it;\r
844         struct killNode *kNode;\r
845 \r
846         for(it = dict_first(killed_users_dict); it; it = iter_next(it))\r
847         {\r
848                 kNode = iter_data(it);\r
849 \r
850                 if(kNode->time - now > KILL_EXPIRE)\r
851                         free(kNode);\r
852         }\r
853 \r
854         timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);\r
855 }\r
856 \r
857 static int\r
858 binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])\r
859 {\r
860         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
861         int value;\r
862 \r
863         if(argc > 1)\r
864         {\r
865                 if(enabled_string(argv[1]))\r
866                 {\r
867                         cInfo->flags |= mask;\r
868                         value = 1;\r
869                 }\r
870                 else if(disabled_string(argv[1]))\r
871                 {\r
872                     cInfo->flags &= ~mask;\r
873                     value = 0;\r
874                 }\r
875                 else\r
876                 {\r
877                    spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);\r
878                    return 0;\r
879                 }\r
880         }\r
881         else\r
882         {\r
883                 value = (cInfo->flags & mask) ? 1 : 0;\r
884         }\r
885 \r
886         spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");\r
887         return 1;\r
888 }\r
889 \r
890 struct valueData\r
891 {\r
892         char *description;\r
893         char value;\r
894         int  oper_only : 1;\r
895 };\r
896 \r
897 static int\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
899 {\r
900         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
901         int index;\r
902 \r
903         if(argc > 1)\r
904         {\r
905                 index = atoi(argv[1]);\r
906                 \r
907                 if(index < 0 || index >= count)\r
908                 {\r
909                         spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);\r
910 \r
911             for(index = 0; index < count; index++)\r
912                 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);\r
913 \r
914                         return 0;\r
915                 }\r
916 \r
917                 if(values[index].oper_only && !IsOper(user))\r
918                 {\r
919                         spamserv_notice(user, "SSMSG_MUST_BE_OPER");\r
920                         return 0;\r
921                 }\r
922                 \r
923                 cInfo->info[info] = values[index].value;\r
924         }\r
925         else\r
926         {\r
927                 for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);\r
928         }\r
929 \r
930         spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);\r
931         return 1;\r
932 }\r
933 \r
934 static int\r
935 show_exceptions(struct userNode *user, struct chanInfo *cInfo)\r
936 {\r
937         struct helpfile_table table;\r
938         unsigned int i;\r
939 \r
940         if(!cInfo->exceptions->used)\r
941         {\r
942                 spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");\r
943                 return 0;\r
944         }\r
945 \r
946         spamserv_notice(user, "SSMSG_EXCEPTION_LIST");\r
947 \r
948         table.length = 0;\r
949         table.width = 1;\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
952 \r
953         for(i = 0; i < cInfo->exceptions->used; i++)\r
954         {\r
955                 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));\r
956                 table.contents[table.length][0] = cInfo->exceptions->list[i];\r
957                 table.length++;\r
958         }\r
959         \r
960         table_send(spamserv, user->nick, 0, NULL, table);\r
961 \r
962         return 1;\r
963 }\r
964 \r
965 static void\r
966 show_memory_usage(struct userNode *user)\r
967 {\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
976         char buffer[64];\r
977 \r
978         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))\r
979         {\r
980                 cInfo = iter_data(it);\r
981 \r
982                 if(!cInfo->exceptions->used)\r
983                         continue;\r
984 \r
985                 for(i = 0; i < cInfo->exceptions->used; i++)\r
986                         channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char);              \r
987         }\r
988 \r
989         for(it = dict_first(connected_users_dict); it; it = iter_next(it))\r
990         {\r
991                 uInfo = iter_data(it);\r
992 \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
996         }\r
997 \r
998         channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);\r
999         \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
1004 \r
1005         size = channel_size + user_size;\r
1006         \r
1007         ss_reply("SSMSG_STATUS_MEMORY");\r
1008         \r
1009         table.length = 3;\r
1010         table.width = 4;\r
1011         table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;\r
1012         table.contents = calloc(table.length, sizeof(char**));\r
1013 \r
1014         // chanInfo\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
1024 \r
1025         // userInfo\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
1035 \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
1046 \r
1047         table_send(spamserv, user->nick, 0, NULL, table);\r
1048         \r
1049         for(i = 0; i < table.length; i++)\r
1050         {\r
1051                 for(j = 0; j < table.width; j++)\r
1052                         free((char*)table.contents[i][j]);\r
1053 \r
1054         free(table.contents[i]);\r
1055         }\r
1056 \r
1057         free(table.contents);\r
1058 }\r
1059 \r
1060 static void\r
1061 show_registered_channels(struct userNode *user)\r
1062 {\r
1063         struct helpfile_table table;\r
1064         dict_iterator_t it;\r
1065 \r
1066         spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");\r
1067 \r
1068         if(!dict_size(registered_channels_dict))\r
1069         {\r
1070                 spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");\r
1071                 return;\r
1072         }\r
1073 \r
1074         table.length = 0;\r
1075         table.width = 1;\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
1078 \r
1079         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))\r
1080         {\r
1081                 struct chanInfo *cInfo = iter_data(it);\r
1082 \r
1083                 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));\r
1084                 table.contents[table.length][0] = cInfo->channel->name;\r
1085                 table.length++;\r
1086         }\r
1087         \r
1088         table_send(spamserv, user->nick, 0, NULL, table);\r
1089 }\r
1090 \r
1091 /***********************************************/\r
1092 /*                SpamServ_Func                */\r
1093 /***********************************************/\r
1094 \r
1095 static \r
1096 SPAMSERV_FUNC(cmd_register)\r
1097 {\r
1098         struct chanInfo *cInfo;\r
1099         char reason[MAXLEN];\r
1100 \r
1101         if(!channel || !channel->channel_info)\r
1102         {\r
1103                 ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);\r
1104                 return 0;\r
1105         }\r
1106 \r
1107         if(get_chanInfo(channel->name))\r
1108         {\r
1109                 ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);\r
1110                 return 0;\r
1111         }\r
1112 \r
1113         if(IsSuspended(channel->channel_info))\r
1114         {\r
1115                 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");\r
1116                 return 0;\r
1117         }\r
1118 \r
1119         if(channel == spamserv_conf.debug_channel)\r
1120         {\r
1121                 ss_reply("SSMSG_DEBUG_CHAN");\r
1122                 return 0;\r
1123         }\r
1124 \r
1125         if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))\r
1126         {\r
1127                 ss_reply("SSMSG_NO_REGISTER", channel->name);\r
1128                 return 0;\r
1129         }\r
1130 \r
1131         spamserv_join_channel(cInfo->channel);\r
1132         \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
1136 \r
1137         return 1;\r
1138 }\r
1139 \r
1140 static \r
1141 SPAMSERV_FUNC(cmd_unregister)\r
1142 {\r
1143         struct chanInfo *cInfo;\r
1144         struct chanData *cData;\r
1145         struct userData *uData;\r
1146         char reason[MAXLEN];\r
1147 \r
1148         if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))\r
1149         {\r
1150                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1151                 return 0;\r
1152         }\r
1153 \r
1154         if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))\r
1155         {\r
1156         ss_reply("SSMSG_NO_ACCESS");\r
1157         return 0;\r
1158         }\r
1159 \r
1160         if(!IsHelping(user))\r
1161         {\r
1162         if(IsSuspended(cData))\r
1163         {\r
1164             ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");\r
1165             return 0;\r
1166         }\r
1167 \r
1168                 if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))\r
1169                 {\r
1170                         ss_reply("SSMSG_CONFIRM_UNREG");\r
1171                         return 0;\r
1172                 }\r
1173         }\r
1174 \r
1175         if(!CHECK_SUSPENDED(cInfo))\r
1176         {\r
1177                 snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle);          \r
1178                 spamserv_part_channel(channel, reason);\r
1179         }\r
1180         \r
1181         spamserv_unregister_channel(cInfo);     \r
1182 \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
1186 \r
1187         return 1;\r
1188 }\r
1189 \r
1190 static \r
1191 SPAMSERV_FUNC(cmd_status)\r
1192 {\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
1196 \r
1197         if(IsOper(user) && argc > 1)\r
1198         {\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
1203         }\r
1204         \r
1205         return 1;\r
1206 }\r
1207 \r
1208 static \r
1209 SPAMSERV_FUNC(cmd_addexception)\r
1210 {\r
1211         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1212         struct userData *uData;\r
1213         unsigned int i;\r
1214 \r
1215         if(!cInfo || !channel->channel_info)\r
1216         {\r
1217                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1218                 return 0;\r
1219         }\r
1220 \r
1221         if(CHECK_SUSPENDED(cInfo))\r
1222         {\r
1223                 ss_reply("SSMSG_SUSPENDED", channel->name);\r
1224                 return 0;\r
1225         }\r
1226 \r
1227         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))\r
1228         {\r
1229                 ss_reply("SSMSG_NO_ACCESS");\r
1230                 return 0;\r
1231         }\r
1232 \r
1233         if(argc < 2)\r
1234                 return show_exceptions(user, cInfo);\r
1235 \r
1236         if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))\r
1237         {\r
1238                 ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);\r
1239                 return 0;\r
1240         }\r
1241 \r
1242         if(strlen(argv[1]) < spamserv_conf.exception_min_len)\r
1243         {\r
1244                 ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);\r
1245                 return 0;\r
1246         }\r
1247         else if(strlen(argv[1]) > spamserv_conf.exception_max_len)\r
1248         {\r
1249                 ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);\r
1250                 return 0;\r
1251         }\r
1252 \r
1253         for(i = 0; i < cInfo->exceptions->used; i++)\r
1254         {\r
1255                 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))\r
1256                 {\r
1257                         ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);\r
1258                         return 0;\r
1259                 }\r
1260         }\r
1261 \r
1262         string_list_append(cInfo->exceptions, strdup(argv[1]));\r
1263         ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);\r
1264 \r
1265         return 1;\r
1266 }\r
1267 \r
1268 static \r
1269 SPAMSERV_FUNC(cmd_delexception)\r
1270 {\r
1271         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1272         struct userData *uData;\r
1273         unsigned int i;\r
1274         int found = -1;\r
1275 \r
1276         if(!cInfo || !channel->channel_info)\r
1277         {\r
1278                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1279                 return 0;\r
1280         }\r
1281 \r
1282         if(CHECK_SUSPENDED(cInfo))\r
1283         {\r
1284                 ss_reply("SSMSG_SUSPENDED", channel->name);\r
1285                 return 0;\r
1286         }\r
1287 \r
1288         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))\r
1289         {\r
1290                 ss_reply("SSMSG_NO_ACCESS");\r
1291                 return 0;\r
1292         }\r
1293 \r
1294         if(argc < 2)\r
1295                 return show_exceptions(user, cInfo);\r
1296 \r
1297         for(i = 0; i < cInfo->exceptions->used; i++)\r
1298         {\r
1299                 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))\r
1300                 {\r
1301                         found = i;\r
1302                         break;\r
1303                 }\r
1304         }\r
1305         \r
1306         if(found == -1)\r
1307         {\r
1308                 ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);\r
1309                 return 0;\r
1310         }\r
1311 \r
1312         string_list_delete(cInfo->exceptions, i);\r
1313         ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);\r
1314 \r
1315         return 1;\r
1316 }\r
1317 \r
1318 static \r
1319 SPAMSERV_FUNC(cmd_set)\r
1320 {\r
1321         struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1322         struct svccmd   *subcmd;        \r
1323         char cmd_name[MAXLEN];\r
1324         unsigned int i;\r
1325 \r
1326         if(!cInfo)\r
1327         {\r
1328                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1329                 return 0;\r
1330         }\r
1331 \r
1332         if(CHECK_SUSPENDED(cInfo))\r
1333         {\r
1334                 ss_reply("SSMSG_SUSPENDED", channel->name);\r
1335                 return 0;\r
1336         }\r
1337 \r
1338     if(!check_user_level(channel,user,lvlSetters,1,0))\r
1339         {\r
1340                 ss_reply("SSMSG_NO_ACCESS");\r
1341                 return 0;\r
1342         }\r
1343         \r
1344         if(argc < 2)\r
1345         {\r
1346                 ss_reply("SSMSG_CHANNEL_OPTIONS");\r
1347 \r
1348                 for(i = 0; i < SET_SUBCMDS_SIZE; i++)\r
1349                 {\r
1350                         sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);\r
1351 \r
1352                         if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))\r
1353                                 subcmd->command->func(user, channel, 1, argv + 1, subcmd);\r
1354                 }\r
1355 \r
1356                 return 1;\r
1357         }\r
1358 \r
1359         sprintf(cmd_name, "%s %s", cmd->name, argv[1]);\r
1360         subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);\r
1361 \r
1362         if(!subcmd)\r
1363         {\r
1364                 reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);\r
1365                 return 0;\r
1366         }\r
1367 \r
1368         return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);\r
1369 }\r
1370 \r
1371 static \r
1372 SPAMSERV_FUNC(opt_spamlimit)\r
1373 {\r
1374         struct valueData values[] =\r
1375         {\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
1381         };\r
1382 \r
1383         MULTIPLE_OPTION("SpamLimit     ", "SpamLimit", ci_SpamLimit);\r
1384 }\r
1385 \r
1386 static \r
1387 SPAMSERV_FUNC(opt_advreaction)\r
1388 {\r
1389         struct valueData values[] =\r
1390         {\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
1396         };\r
1397 \r
1398         MULTIPLE_OPTION("AdvReaction   ", "AdvReaction", ci_AdvReaction);\r
1399 }\r
1400 \r
1401 static \r
1402 SPAMSERV_FUNC(opt_warnreaction)\r
1403 {\r
1404         struct valueData values[] =\r
1405         {\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
1411         };\r
1412 \r
1413         MULTIPLE_OPTION("WarnReaction  ", "WarnReaction", ci_WarnReaction);\r
1414 }\r
1415 \r
1416 static \r
1417 SPAMSERV_FUNC(opt_advscan)\r
1418 {\r
1419         BINARY_OPTION("AdvScan       ", CHAN_ADV_SCAN);\r
1420 }\r
1421 \r
1422 static \r
1423 SPAMSERV_FUNC(opt_spamscan)\r
1424 {\r
1425         BINARY_OPTION("SpamScan      ", CHAN_SPAMSCAN);\r
1426 }\r
1427 \r
1428 static \r
1429 SPAMSERV_FUNC(opt_floodscan)\r
1430 {\r
1431         BINARY_OPTION("FloodScan     ", CHAN_FLOODSCAN);\r
1432 }\r
1433 \r
1434 static \r
1435 SPAMSERV_FUNC(opt_joinflood)\r
1436 {\r
1437         BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);\r
1438 }\r
1439 \r
1440 static \r
1441 SPAMSERV_FUNC(opt_scanops)\r
1442 {\r
1443         BINARY_OPTION("ScanChanOps   ", CHAN_SCAN_CHANOPS);\r
1444 }\r
1445 \r
1446 static \r
1447 SPAMSERV_FUNC(opt_scanvoiced)\r
1448 {\r
1449         BINARY_OPTION("ScanVoiced    ", CHAN_SCAN_VOICED);\r
1450 }\r
1451 \r
1452 static \r
1453 SPAMSERV_FUNC(opt_exceptlevel)\r
1454 {\r
1455  struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1456  struct userData *uData;\r
1457  int index;\r
1458  if(argc > 1)\r
1459         {\r
1460                 index = atoi(argv[1]);\r
1461                 if(index < 1 || index > 501)\r
1462                 {\r
1463                         spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");\r
1464                         return 0;\r
1465                 }\r
1466         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))\r
1467         {\r
1468             ss_reply("SSMSG_NO_ACCESS");\r
1469             return 0;\r
1470         }\r
1471         cInfo->exceptlevel=index;\r
1472     } else {\r
1473      index=cInfo->exceptlevel;\r
1474     }\r
1475     spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel   ", index);\r
1476     return 1;\r
1477 }\r
1478 \r
1479 static SPAMSERV_FUNC(cmd_addbad)\r
1480 {\r
1481          struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1482          struct userData *uData;\r
1483 \r
1484          if(!cInfo)\r
1485          {\r
1486                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1487                 return 0;\r
1488          }\r
1489 \r
1490          if(CHECK_SUSPENDED(cInfo))\r
1491          {\r
1492                  ss_reply("SSMSG_SUSPENDED", channel->name);\r
1493              return 0;\r
1494          }\r
1495 \r
1496          if(!check_user_level(channel,user,lvlSetters,1,0))\r
1497          {\r
1498                 ss_reply("SSMSG_NO_ACCESS");\r
1499                 return 0;\r
1500          }\r
1501 \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
1508                          return 1;\r
1509                  }\r
1510          }\r
1511 \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
1517                  }\r
1518          }\r
1519 \r
1520          reply("SSMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id);\r
1521          return 1;\r
1522 }\r
1523 \r
1524 static SPAMSERV_FUNC(cmd_delbad)\r
1525 {\r
1526          struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1527          struct userData *uData;\r
1528          unsigned int n;\r
1529 \r
1530          if(!cInfo)\r
1531          {\r
1532                  ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1533                  return 0;\r
1534          }\r
1535 \r
1536          if(CHECK_SUSPENDED(cInfo))\r
1537          {\r
1538                  ss_reply("SSMSG_SUSPENDED", channel->name);\r
1539              return 0;\r
1540          }\r
1541 \r
1542          if(!check_user_level(channel,user,lvlSetters,1,0))\r
1543          {\r
1544                  ss_reply("SSMSG_NO_ACCESS");\r
1545                  return 0;\r
1546          }\r
1547 \r
1548          for (n=1; n<argc; n++) {\r
1549                  struct badword *badword = dict_find(cInfo->badwords, argv[n], NULL);\r
1550                  if (!badword) {\r
1551                          reply("SSMSG_BADWORD_NOT_FOUND", argv[n]);\r
1552                          continue;\r
1553                  }\r
1554                  reply("SSMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);\r
1555                  dict_remove(cInfo->badwords, argv[n]);\r
1556          }\r
1557          return 1;\r
1558 }\r
1559 \r
1560 static SPAMSERV_FUNC(cmd_setbad)\r
1561 {\r
1562          struct chanInfo *cInfo = get_chanInfo(channel->name);\r
1563          struct userData *uData;\r
1564          struct badword *badword;\r
1565 \r
1566          if(!cInfo)\r
1567          {\r
1568                  ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1569                  return 0;\r
1570          }\r
1571 \r
1572          if(CHECK_SUSPENDED(cInfo))\r
1573          {\r
1574                  ss_reply("SSMSG_SUSPENDED", channel->name);\r
1575                  return 0;\r
1576          }\r
1577 \r
1578          if(!check_user_level(channel,user,lvlSetters,1,0))\r
1579          {\r
1580                  ss_reply("SSMSG_NO_ACCESS");\r
1581                  return 0;\r
1582          }\r
1583 \r
1584          if ((badword = dict_find(cInfo->badwords, argv[1], NULL))) {\r
1585                  if (argc > 3) {\r
1586                          unsigned int ii;\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
1598                          }\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
1612                                   } else {\r
1613                                          reply("SSMSG_BADWORD_SET_INVALID", setting);\r
1614                                   }\r
1615                          } else {\r
1616                                   reply("SSMSG_BADWORD_SETTING_INVALID", setting);\r
1617                          }\r
1618 \r
1619                  } else {\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
1625                                    break;\r
1626                                  case BADACTION_BAN:\r
1627                                    reply("SSMSG_BADWORD_SET_ACTION", "BAN");\r
1628                                    break;\r
1629                                  case BADACTION_KILL:\r
1630                                    reply("SSMSG_BADWORD_SET_ACTION", "KILL");\r
1631                                    break;\r
1632                                  case BADACTION_GLINE:\r
1633                                    reply("SSMSG_BADWORD_SET_ACTION", "GLINE");\r
1634                                    break;\r
1635                                  default:\r
1636                                    reply("SSMSG_BADWORD_SET_ACTION", "*undef*");\r
1637                          }\r
1638                  }\r
1639          } else {\r
1640                  reply("SSMSG_BADWORD_NOT_FOUND", argv[1]);\r
1641                  return 0;\r
1642          }\r
1643          return 1;\r
1644 }\r
1645 \r
1646 int\r
1647 ss_badwords_sort(const void *pa, const void *pb)\r
1648 {\r
1649         struct badword *a = *(struct badword**)pa;\r
1650         struct badword *b = *(struct badword**)pb;\r
1651 \r
1652         return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0);\r
1653 }\r
1654 \r
1655 static SPAMSERV_FUNC(cmd_listbad)\r
1656 {\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
1662 \r
1663          if(!cInfo)\r
1664          {\r
1665              ss_reply("SSMSG_NOT_REGISTERED", channel->name);\r
1666                  return 0;\r
1667          }\r
1668 \r
1669      if(CHECK_SUSPENDED(cInfo))\r
1670          {\r
1671          ss_reply("SSMSG_SUSPENDED", channel->name);\r
1672                  return 0;\r
1673          }\r
1674 \r
1675          if(!check_user_level(channel,user,lvlSetters,1,0))\r
1676          {\r
1677                  ss_reply("SSMSG_NO_ACCESS");\r
1678                  return 0;\r
1679          }\r
1680 \r
1681          dict_iterator_t it;\r
1682          for (it = dict_first(cInfo->badwords); it; it = iter_next(it)) {\r
1683                  count++;\r
1684          }\r
1685 \r
1686          tbl.length = count+1;\r
1687          tbl.width = 4;\r
1688          tbl.flags = 0;\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
1696          if(!count)\r
1697          {\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
1702              return 0;\r
1703          }\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
1708           }\r
1709 \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
1719                            break;\r
1720                          case BADACTION_BAN:\r
1721                            tbl.contents[ii][2] = "BAN";\r
1722                            break;\r
1723                          case BADACTION_KILL:\r
1724                            tbl.contents[ii][2] = "KILL";\r
1725                            break;\r
1726                          case BADACTION_GLINE:\r
1727                            tbl.contents[ii][2] = "GLINE";\r
1728                            break;\r
1729                          default:\r
1730                            tbl.contents[ii][2] = "*undef*";\r
1731                  }\r
1732                  tbl.contents[ii][3] = strtab(bw->triggered);\r
1733          }\r
1734 \r
1735          table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);\r
1736          for(ii = 1; ii < tbl.length; ++ii)\r
1737          {\r
1738                  free(tbl.contents[ii]);\r
1739          }\r
1740          free(tbl.contents[0]);\r
1741          free(tbl.contents);\r
1742          return 1;\r
1743 }\r
1744 \r
1745 static void \r
1746 to_lower(char *message)\r
1747 {\r
1748         unsigned int i, diff = 'a' - 'A';\r
1749 \r
1750         for(i = 0; i < strlen(message); i++)\r
1751         {\r
1752                 if((message[i] >= 'A') && (message[i] <= 'Z'))\r
1753                         message[i] = message[i] + diff;\r
1754         }\r
1755 }\r
1756 \r
1757 static char *\r
1758 strip_mirc_codes(char *text)\r
1759 {\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
1763 \r
1764         while(len > 0)\r
1765         {\r
1766                 if((col && isdigit(*text) && nc < 2) ||\r
1767                         (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))\r
1768                 {\r
1769                         nc++;\r
1770 \r
1771                         if(*text == ',')\r
1772                                 nc = 0;\r
1773                 }\r
1774                 else\r
1775                 {\r
1776                         col = 0;\r
1777 \r
1778                         switch(*text)\r
1779                         {\r
1780                         case '\003':\r
1781                                 col = 1;\r
1782                                 nc = 0;\r
1783                                 break;\r
1784                         case '\002':\r
1785                         case '\022':\r
1786                         case '\026':                    \r
1787                         case '\031':\r
1788                         case '\037':\r
1789                                 break;\r
1790                         default:\r
1791                                 new_str[i] = *text;\r
1792                                 i++;\r
1793                         }\r
1794                 }\r
1795 \r
1796                 text++;\r
1797                 len--;\r
1798         }\r
1799 \r
1800         new_str[i] = '\0';\r
1801 \r
1802         return new_str;\r
1803 }\r
1804 \r
1805 static int\r
1806 is_in_exception_list(struct chanInfo *cInfo, char *message)\r
1807 {\r
1808         unsigned int i;\r
1809 \r
1810         for(i = 0; i < cInfo->exceptions->used; i++)\r
1811                 if(strstr(message, cInfo->exceptions->list[i]))\r
1812                         return 1;\r
1813 \r
1814         return 0;\r
1815 }\r
1816 \r
1817 static int\r
1818 check_advertising(struct chanInfo *cInfo, char *message)\r
1819 {\r
1820         unsigned int i = 0;\r
1821 \r
1822         if(spamserv_conf.strip_mirc_codes)\r
1823                 message = strip_mirc_codes(message);\r
1824 \r
1825         if(is_in_exception_list(cInfo, message))\r
1826                 return 0;\r
1827 \r
1828         while(message[i] != 0)\r
1829         {\r
1830                 if(message[i] == '#')\r
1831                 {\r
1832                         char channelname[CHANNELLEN];\r
1833                         unsigned int j = 0;\r
1834 \r
1835                         if(!spamserv_conf.adv_chan_must_exist)\r
1836                                 return 1;\r
1837 \r
1838                         /* only return 1, if the channel does exist */  \r
1839 \r
1840                         while((message[i] != 0) && (message[i] != ' '))\r
1841                         {\r
1842                                 channelname[j] = message[i];\r
1843                                 i++;\r
1844                                 j++;                            \r
1845                         }\r
1846 \r
1847                         channelname[j] = '\0';\r
1848 \r
1849                         if(GetChannel(channelname))\r
1850                                 return 1;\r
1851                 }\r
1852                 else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))\r
1853                         return 1;\r
1854                 else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))\r
1855                         return 1;\r
1856                 else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))\r
1857                         return 1;\r
1858 \r
1859                 i++;\r
1860         }\r
1861 \r
1862         return 0;\r
1863 }\r
1864 \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
1866 \r
1867 static void\r
1868 spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)\r
1869 {\r
1870         if(ban)\r
1871         {\r
1872                 struct mod_chanmode change;\r
1873                 char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);\r
1874 \r
1875                 sanitize_ircmask(hostmask);\r
1876 \r
1877                 if(expires)\r
1878                         add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);\r
1879 \r
1880                 mod_chanmode_init(&change);\r
1881                 change.argc = 1;\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
1885 \r
1886                 free(hostmask);\r
1887 \r
1888                 spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);\r
1889         }\r
1890         else\r
1891                 spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);\r
1892 \r
1893         KickChannelUser(user, channel, spamserv, reason);       \r
1894 }\r
1895 \r
1896 static void\r
1897 spamserv_detected_badword(struct userNode *user, struct chanNode *chan, struct badword *badword)\r
1898 {\r
1899     char *hostmask;\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
1909             }\r
1910             struct mod_chanmode change;\r
1911             mod_chanmode_init(&change);\r
1912             change.argc = 1;\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
1916             free(hostmask);\r
1917 \r
1918         case BADACTION_KICK:\r
1919             if(GetUserMode(chan, user))\r
1920                 KickChannelUser(user, chan, spamserv, reason);\r
1921             break;\r
1922         case BADACTION_KILL:\r
1923             DelUser(user, spamserv, 1, reason);\r
1924             break;\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
1928             break;\r
1929         default:\r
1930             //error?\r
1931             break;\r
1932         }\r
1933 }\r
1934 \r
1935 void\r
1936 spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)\r
1937 {\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
1944 \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
1947                 return;\r
1948 \r
1949         if(!CHECK_CHANOPS(cInfo))\r
1950         {\r
1951                 struct modeNode *mn = GetUserMode(channel, user);\r
1952                 if(mn && mn->modes & MODE_CHANOP)\r
1953                         return;\r
1954         //if(check_user_level(channel, user, lvlGiveOps, 1, 0)) \r
1955         //    return;\r
1956         }\r
1957         \r
1958         if(!CHECK_VOICED(cInfo))\r
1959         {\r
1960                 struct modeNode *mn = GetUserMode(channel, user);\r
1961                 if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))\r
1962                         return;\r
1963         }\r
1964     \r
1965     if(cInfo->exceptlevel == 0)\r
1966         return;\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
1970         return;\r
1971        }\r
1972     }\r
1973 \r
1974         to_lower(text);\r
1975 \r
1976         if(CHECK_SPAM(cInfo))\r
1977         {\r
1978                 if(!(sNode = uInfo->spam))\r
1979                 {\r
1980                         spamserv_create_spamNode(channel, uInfo, text);\r
1981                 }\r
1982                 else\r
1983                 {\r
1984                         for(; sNode; sNode = sNode->next)\r
1985                                 if(sNode->channel == channel)\r
1986                                         break;\r
1987 \r
1988                         if(!sNode)\r
1989                         {\r
1990                                 spamserv_create_spamNode(channel, uInfo, text);\r
1991                         }\r
1992                         else\r
1993                         {\r
1994                                 unsigned long crc = crc32(text);\r
1995 \r
1996                                 if(crc == sNode->crc32)\r
1997                                 {\r
1998                                         unsigned int spamlimit = 2;\r
1999                                         sNode->count++;\r
2000 \r
2001                                         switch(cInfo->info[ci_SpamLimit])\r
2002                                         {\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
2008                                         }\r
2009 \r
2010                                         if(sNode->count == spamlimit)\r
2011                                         {\r
2012                                                 uInfo->warnlevel += SPAM_WARNLEVEL;\r
2013 \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
2016                                         }\r
2017                                         else if(sNode->count > spamlimit)\r
2018                                         {\r
2019                                                 switch(cInfo->info[ci_WarnReaction])\r
2020                                                 {\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
2026                                                 }\r
2027 \r
2028                                                 spamserv_delete_spamNode(uInfo, sNode);\r
2029                                                 uInfo->warnlevel += SPAM_WARNLEVEL;\r
2030                                                 violation = 1;\r
2031                                         }\r
2032                                 }\r
2033                                 else\r
2034                                 {\r
2035                                         sNode->crc32 = crc;                                     \r
2036                                         sNode->count = 1;\r
2037                                 }\r
2038                         }\r
2039                 }\r
2040         }\r
2041 \r
2042         if(CHECK_FLOOD(cInfo))\r
2043         {\r
2044                 if(!(fNode = uInfo->flood))\r
2045                 {\r
2046                         spamserv_create_floodNode(channel, user, &uInfo->flood);\r
2047                 }\r
2048                 else\r
2049                 {\r
2050                         for(; fNode; fNode = fNode->next)\r
2051                                 if(fNode->channel == channel)\r
2052                                         break;\r
2053                                 \r
2054                         if(!fNode)\r
2055                         {\r
2056                                 spamserv_create_floodNode(channel, user, &uInfo->flood);\r
2057                         }\r
2058                         else\r
2059                         {\r
2060                                 if(((now - fNode->time) < FLOOD_EXPIRE))\r
2061                                 {\r
2062                                         fNode->count++;\r
2063                                         \r
2064                                         if(fNode->count == FLOOD_MAX_LINES - 1)\r
2065                                         {\r
2066                                                 uInfo->warnlevel += FLOOD_WARNLEVEL;\r
2067 \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
2070                                         }\r
2071                                         else if(fNode->count > FLOOD_MAX_LINES)\r
2072                                         {\r
2073                                                 switch(cInfo->info[ci_WarnReaction])\r
2074                                                 {\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
2080                                                 }\r
2081 \r
2082                                                 spamserv_delete_floodNode(&uInfo->flood, fNode);\r
2083                                                 uInfo->warnlevel += FLOOD_WARNLEVEL;\r
2084                                                 violation = 2;                                          \r
2085                                         }\r
2086                                 }\r
2087 \r
2088                                 fNode->time = now;\r
2089                         }\r
2090                 }\r
2091         }\r
2092 \r
2093         dict_iterator_t it;\r
2094 \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
2099                 }\r
2100         }\r
2101 \r
2102         if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))\r
2103         {\r
2104                 if(CHECK_ADV_WARNED(uInfo))\r
2105                 {\r
2106                         switch(cInfo->info[ci_AdvReaction])\r
2107                         {\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
2113                         }\r
2114 \r
2115                         uInfo->warnlevel += ADV_WARNLEVEL;\r
2116                         violation = 3;\r
2117                 }\r
2118                 else\r
2119                 {               \r
2120                         uInfo->flags |= USER_ADV_WARNED;\r
2121                         uInfo->lastadv = now;\r
2122                         uInfo->warnlevel += ADV_WARNLEVEL;\r
2123 \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
2126                 }               \r
2127         }\r
2128 \r
2129         if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)\r
2130         {\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
2135         }\r
2136         else if(uInfo->warnlevel > MAX_WARNLEVEL)\r
2137         {\r
2138                 if(CHECK_KILLED(uInfo))\r
2139                         uInfo->flags |= USER_GLINE;\r
2140                 else\r
2141                         uInfo->flags |= USER_KILL;\r
2142 \r
2143                 violation = 5;\r
2144         }\r
2145 \r
2146         if(!violation)\r
2147                 return;\r
2148 \r
2149         switch(violation)\r
2150         {\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
2155         }\r
2156 \r
2157         if(CHECK_GLINE(uInfo))\r
2158         {\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
2164         }\r
2165         else if(CHECK_KILL(uInfo))\r
2166         {\r
2167                 DelUser(user, spamserv, 1, reason);\r
2168                 spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);\r
2169         }\r
2170         else if(CHECK_LONG_TBAN(uInfo))\r
2171         {\r
2172                 spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);\r
2173         }\r
2174         else if(CHECK_SHORT_TBAN(uInfo))\r
2175         {\r
2176                 spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);\r
2177         }\r
2178         else if(CHECK_KICKBAN(uInfo))\r
2179         {\r
2180                 spamserv_punish(channel, user, 0, reason, 1);\r
2181         }\r
2182         else if(CHECK_KICK(uInfo))\r
2183         {\r
2184                 spamserv_punish(channel, user, 0, reason, 0);\r
2185         }\r
2186 }\r
2187 \r
2188 static int\r
2189 spamserv_saxdb_read_shitlist(const char *name, void *data, void *extra)\r
2190 {\r
2191         struct record_data *rd = data;\r
2192         struct chanInfo *chan = extra;\r
2193         char *badword;\r
2194         char *triggered, *action;\r
2195 \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
2202 \r
2203                 add_badword(chan, badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);\r
2204         }\r
2205         return 0;\r
2206 }\r
2207 \r
2208 static int\r
2209 spamserv_saxdb_read(struct dict *database)\r
2210 {\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
2218         char *str, *info;\r
2219         unsigned long expiry;    \r
2220 \r
2221         for(it = dict_first(database); it; it = iter_next(it))\r
2222         {\r
2223                 hir = iter_data(it);\r
2224 \r
2225                 if(hir->type != RECDB_OBJECT)\r
2226                 {\r
2227                         log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));\r
2228                         continue;\r
2229                 }\r
2230 \r
2231                 channel = GetChannel(iter_key(it));\r
2232                 strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);\r
2233 \r
2234                 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);\r
2235                 flags = str ? atoi(str) : 0;\r
2236 \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
2240         \r
2241                 str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);\r
2242                 expiry = str ? strtoul(str, NULL, 0) : 0;\r
2243 \r
2244                 if(channel && info)\r
2245                 {\r
2246                         if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))\r
2247                         {\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
2251                                 {\r
2252                                         cInfo->flags &= ~CHAN_SUSPENDED;\r
2253                                         spamserv_join_channel(cInfo->channel);\r
2254                                 }\r
2255                                 else if(!CHECK_SUSPENDED(cInfo))\r
2256                                         spamserv_join_channel(cInfo->channel);\r
2257                                 else\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
2266                         }\r
2267                 }\r
2268                 else\r
2269                         log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it));  \r
2270         }\r
2271 \r
2272         return 0;\r
2273 }\r
2274 \r
2275 static int\r
2276 spamserv_saxdb_write(struct saxdb_context *ctx)\r
2277 {\r
2278         dict_iterator_t it;\r
2279 \r
2280         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))\r
2281         {\r
2282                 struct chanInfo *cInfo = iter_data(it);\r
2283 \r
2284                 saxdb_start_record(ctx, cInfo->channel->name, 1);\r
2285 \r
2286                 if(cInfo->exceptions->used)\r
2287                         saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);\r
2288 \r
2289                 if(cInfo->flags)\r
2290                         saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags);  \r
2291 \r
2292                 saxdb_write_string(ctx, KEY_INFO, cInfo->info);         \r
2293         saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel);              \r
2294 \r
2295                 if(cInfo->suspend_expiry)\r
2296                         saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);\r
2297 \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
2309                 }\r
2310                 saxdb_end_record(ctx);\r
2311                 saxdb_end_record(ctx);          \r
2312         }\r
2313         return 0;\r
2314 }\r
2315 \r
2316 static void\r
2317 spamserv_conf_read(void)\r
2318 {\r
2319         dict_t conf_node;\r
2320         const char *str; \r
2321 \r
2322         if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))\r
2323         {\r
2324                 log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);\r
2325                 return;\r
2326         }\r
2327 \r
2328         str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);\r
2329 \r
2330         if(str)\r
2331         {\r
2332                 spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);\r
2333 \r
2334                 if(spamserv_conf.debug_channel)\r
2335                         spamserv_join_channel(spamserv_conf.debug_channel);\r
2336         }\r
2337         else\r
2338         {\r
2339                 spamserv_conf.debug_channel = NULL;\r
2340         }\r
2341 \r
2342         spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);\r
2343 \r
2344         str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);\r
2345         spamserv_conf.network_rules = str ? str : NULL;\r
2346 \r
2347         str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);\r
2348         spamserv_conf.trigger = str ? str[0] : 0;\r
2349 \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
2352 \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
2355 \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
2358 \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
2361 \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
2364 \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
2367 \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
2370 \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
2373 \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
2376 }\r
2377 \r
2378 static void\r
2379 spamserv_db_cleanup(void)\r
2380 {\r
2381         dict_iterator_t it;\r
2382 \r
2383         while((it = dict_first(registered_channels_dict)))\r
2384         {\r
2385                 spamserv_unregister_channel(iter_data(it));\r
2386         }\r
2387 \r
2388         while((it = dict_first(killed_users_dict)))\r
2389         {\r
2390                 free(iter_data(it));\r
2391         }\r
2392         \r
2393         dict_delete(registered_channels_dict);\r
2394         dict_delete(connected_users_dict);\r
2395         dict_delete(killed_users_dict);\r
2396 }\r
2397 \r
2398 void\r
2399 init_spamserv(const char *nick)\r
2400 {\r
2401         if(!nick)\r
2402                 return;\r
2403 \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
2408 \r
2409         conf_register_reload(spamserv_conf_read);\r
2410 \r
2411         SS_LOG = log_register_type("SpamServ", "file:spamserv.log");    \r
2412 \r
2413         registered_channels_dict = dict_new();\r
2414         connected_users_dict = dict_new();\r
2415         killed_users_dict = dict_new();\r
2416 \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
2422 \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
2428 \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
2450 \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
2454         crc32_init();\r
2455 }\r
2456 =======
2457 /* spamserv.c - anti spam service
2458  * Copyright 2004 feigling
2459  *
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.
2465  *
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.
2470  *
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.
2473  *
2474  * $Id: spamserv.c,v 1.08 2004/06/27 22:21:00 feigling Exp $
2475  */
2476
2477 #include "conf.h"
2478 #include "spamserv.h"
2479 #include "chanserv.h"
2480 #include "global.h"
2481 #include "modcmd.h"
2482 #include "saxdb.h"
2483 #include "timeq.h"
2484 #include "gline.h"
2485
2486 #define SPAMSERV_CONF_NAME           "services/spamserv"
2487
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"
2493
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"
2508
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 { \
2512 (void)argv; \
2513   if(argc < N) { \
2514     ss_reply(MSG_MISSING_PARAMS, argv[0]); \
2515     SPAMSERV_SYNTAX(); \
2516     return 0; } } while(0)
2517
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];
2523
2524 dict_t registered_channels_dict;
2525 dict_t connected_users_dict;
2526 dict_t killed_users_dict;
2527
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)
2532
2533 #define SET_SUBCMDS_SIZE 10
2534
2535 const char *set_subcommands[SET_SUBCMDS_SIZE] = {"SPAMLIMIT", "ADVREACTION", "WARNREACTION", "ADVSCAN", "SPAMSCAN", "FLOODSCAN", "JOINFLOODSCAN", "EXCEPTLEVEL","SCANCHANOPS", "SCANVOICED"};
2536
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);
2540
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);
2543
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." },
2552
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'." },
2565
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." },
2575
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." },
2582         { NULL, NULL }
2583 };
2584
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."
2600
2601 static struct
2602 {
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;
2617 } spamserv_conf;
2618
2619 /***********************************************/
2620 /*                   Channel                   */
2621 /***********************************************/
2622
2623 struct chanInfo*
2624 get_chanInfo(const char *channelname)
2625 {
2626         return dict_find(registered_channels_dict, channelname, 0);
2627 }
2628
2629 static void
2630 spamserv_join_channel(struct chanNode *channel)
2631 {
2632         struct mod_chanmode change;
2633         mod_chanmode_init(&change);
2634         change.argc = 1;
2635         change.args[0].mode = MODE_CHANOP;
2636         change.args[0].u.member = AddChannelUser(spamserv, channel);
2637         mod_chanmode_announce(spamserv, channel, &change);
2638 }
2639
2640 static void
2641 spamserv_part_channel(struct chanNode *channel, char *reason)
2642 {
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);
2646 }
2647
2648 static struct chanInfo*
2649 spamserv_register_channel(struct chanNode *channel, struct string_list *exceptions, unsigned int flags, char *info)
2650 {
2651         struct chanInfo *cInfo = malloc(sizeof(struct chanInfo));
2652         
2653         if(!cInfo)
2654         {
2655                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for cInfo; channel: %s", channel->name);
2656                 return NULL;
2657         }
2658
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);
2666
2667         return cInfo;
2668 }
2669
2670 static void
2671 spamserv_unregister_channel(struct chanInfo *cInfo)
2672 {
2673         if(!cInfo)
2674                 return;
2675
2676         dict_remove(registered_channels_dict, cInfo->channel->name);
2677         free_string_list(cInfo->exceptions);
2678         free(cInfo);
2679 }
2680
2681 void
2682 spamserv_cs_suspend(struct chanNode *channel, time_t expiry, int suspend, char *reason)
2683 {
2684         struct chanInfo *cInfo = get_chanInfo(channel->name);
2685
2686         if(cInfo)
2687         {
2688                 if(suspend)
2689                 {
2690                         cInfo->flags |= CHAN_SUSPENDED;
2691                         cInfo->suspend_expiry = expiry;
2692                         spamserv_part_channel(channel, reason);
2693                 }
2694                 else
2695                 {
2696                         if(CHECK_SUSPENDED(cInfo))
2697                         {
2698                                 cInfo->flags &= ~CHAN_SUSPENDED;
2699                                 cInfo->suspend_expiry = 0;
2700                         }
2701                 }
2702         }
2703 }
2704
2705 int
2706 spamserv_cs_move_merge(struct userNode *user, struct chanNode *channel, struct chanNode *target, int move)
2707 {
2708         struct chanInfo *cInfo = get_chanInfo(channel->name);
2709
2710         if(cInfo)
2711         {
2712                 char reason[MAXLEN];
2713
2714                 if(!spamserv_conf.allow_move_merge || get_chanInfo(target->name))
2715                 {
2716                         if(move)
2717                                 snprintf(reason, sizeof(reason), "unregistered due to a channel move to %s", target->name);
2718                         else
2719                                 snprintf(reason, sizeof(reason), "unregistered due to a channel merge into %s", target->name);
2720
2721                         spamserv_cs_unregister(user, channel, manually, reason);
2722                         return 0;
2723                 }
2724
2725                 cInfo->channel = target;
2726
2727                 dict_remove(registered_channels_dict, channel->name);
2728                 dict_insert(registered_channels_dict, target->name, cInfo);
2729
2730                 if(move)
2731                 {
2732                         snprintf(reason, sizeof(reason), "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2733                 }
2734                 else
2735                 {
2736                         spamserv_join_channel(target);
2737                         snprintf(reason, sizeof(reason), "%s merged into %s by %s.", channel->name, target->name, user->handle_info->handle);   
2738                 }
2739
2740                 if(!CHECK_SUSPENDED(cInfo))
2741                         spamserv_part_channel(channel, reason);
2742
2743                 if(move)
2744                         snprintf(reason, sizeof(reason), "$X (channel %s) moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2745                 else
2746                         snprintf(reason, sizeof(reason), "$X (channel %s) merged into %s by %s.", channel->name, target->name, user->handle_info->handle);
2747
2748                 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2749                 return 1;
2750         }
2751
2752         return 0;
2753 }
2754
2755 void
2756 spamserv_cs_unregister(struct userNode *user, struct chanNode *channel, enum cs_unreg type, char *reason)
2757 {
2758         struct chanInfo *cInfo = get_chanInfo(channel->name);
2759
2760         if(cInfo)
2761         {
2762                 char global[MAXLEN], partmsg[MAXLEN];
2763
2764                 switch (type)
2765                 {
2766                 case manually:
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);                   
2769                         break;
2770                 case expire:
2771                         snprintf(global, sizeof(global), "$X (channel %s) registration expired.", channel->name);
2772                         snprintf(partmsg, sizeof(partmsg), "%s registration expired.", channel->name);                  
2773                         break;
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);                        
2777                         break;
2778                 }
2779
2780                 if(!CHECK_SUSPENDED(cInfo))
2781                         spamserv_part_channel(channel, partmsg);
2782                 
2783                 spamserv_unregister_channel(cInfo);
2784                 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, global);
2785         }
2786 }
2787
2788 /***********************************************/
2789 /*                    User                     */
2790 /***********************************************/
2791
2792 static struct userInfo*
2793 get_userInfo(const char *nickname)
2794 {
2795         return dict_find(connected_users_dict, nickname, 0);
2796 }
2797
2798 static void
2799 spamserv_create_spamNode(struct chanNode *channel, struct userInfo *uInfo, char *text)
2800 {
2801         struct spamNode *sNode = malloc(sizeof(struct spamNode));
2802
2803         if(!sNode)
2804         {
2805                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for sNode; channel: %s; user: %s", channel->name, uInfo->user->nick);
2806                 return;
2807         }
2808
2809         sNode->channel = channel;       
2810         sNode->crc32 = crc32(text);
2811         sNode->count = 1;
2812         sNode->next = NULL;
2813
2814         if(uInfo->spam)
2815         {
2816                 struct spamNode *temp = uInfo->spam;
2817                 
2818                 while(temp->next)
2819                         temp = temp->next;
2820
2821                 sNode->prev = temp;
2822                 temp->next = sNode;
2823         }
2824         else
2825         {
2826                 sNode->prev = NULL;
2827                 uInfo->spam = sNode;
2828         }
2829 }
2830
2831 static void
2832 spamserv_delete_spamNode(struct userInfo *uInfo, struct spamNode *sNode)
2833 {
2834         if(!sNode)
2835                 return;
2836
2837         if(sNode == uInfo->spam)
2838                 uInfo->spam = sNode->next;
2839         
2840         if(sNode->next)
2841                  sNode->next->prev = sNode->prev;
2842         if(sNode->prev)
2843                  sNode->prev->next = sNode->next;
2844
2845         free(sNode);
2846 }
2847
2848 static void
2849 spamserv_clear_spamNodes(struct chanNode *channel)
2850 {
2851         struct userInfo *uInfo;
2852         struct spamNode *sNode;
2853         unsigned int i;
2854
2855         for(i = 0; i < channel->members.used; i++)
2856         {
2857                 if((uInfo = get_userInfo(channel->members.list[i]->user->nick)))
2858                 {
2859                         if((sNode = uInfo->spam))
2860                         {
2861                                 for(; sNode; sNode = sNode->next)
2862                                         if(sNode->channel == channel)
2863                                                 break;
2864                                         
2865                                 if(sNode)
2866                                         spamserv_delete_spamNode(uInfo, sNode);
2867                         }
2868                 }
2869         }
2870 }
2871
2872 static void
2873 spamserv_create_floodNode(struct chanNode *channel, struct userNode *user, struct floodNode **uI_fNode)
2874 {
2875         struct floodNode *fNode = malloc(sizeof(struct floodNode));
2876
2877         if(!fNode)
2878         {
2879                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for fNode; channel: %s; user: %s", channel->name, user->nick);
2880                 return;
2881         }
2882
2883         fNode->channel = channel;
2884         fNode->owner = user;
2885         fNode->count = 1;
2886         fNode->time = now;      
2887         fNode->next = NULL;
2888
2889         if(*uI_fNode)
2890         {
2891                 struct floodNode *temp = *uI_fNode;
2892                 
2893                 while(temp->next)
2894                         temp = temp->next;
2895                 
2896                 fNode->prev = temp;
2897                 temp->next = fNode;
2898         }
2899         else
2900         {
2901                 fNode->prev = NULL;
2902                 *uI_fNode = fNode;
2903         }
2904 }
2905
2906 static void
2907 spamserv_delete_floodNode(struct floodNode **uI_fNode, struct floodNode *fNode)
2908 {
2909         if(!fNode)
2910                 return;
2911
2912         if(fNode == *uI_fNode)
2913                 *uI_fNode = fNode->next;
2914         
2915         if(fNode->next)
2916                  fNode->next->prev = fNode->prev;
2917         if(fNode->prev)
2918                  fNode->prev->next = fNode->next;
2919
2920         free(fNode);
2921 }
2922
2923 static void
2924 spamserv_create_user(struct userNode *user)
2925 {
2926         struct userInfo *uInfo = malloc(sizeof(struct userInfo));
2927         struct killNode *kNode = dict_find(killed_users_dict, irc_ntoa(&user->ip), 0);
2928
2929         if(!uInfo)
2930         {
2931                 log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for uInfo; nick: %s", user->nick);
2932                 return;
2933         }
2934
2935         if(kNode)
2936                 spamserv_debug(SSMSG_DEBUG_RECONNECT, user->nick);
2937
2938         uInfo->user = user;
2939         uInfo->spam = NULL;
2940         uInfo->flood = NULL;
2941         uInfo->joinflood = NULL;
2942         uInfo->flags = kNode ? USER_KILLED : 0;
2943         uInfo->warnlevel = kNode ? kNode->warnlevel : 0;
2944         uInfo->lastadv = 0;
2945
2946         dict_insert(connected_users_dict, user->nick, uInfo);
2947
2948         if(kNode)
2949         {
2950                 dict_remove(killed_users_dict, irc_ntoa(&user->ip));
2951                 free(kNode);
2952         }
2953 }
2954
2955 static void
2956 spamserv_delete_user(struct userInfo *uInfo)
2957 {
2958         if(!uInfo)
2959                 return;
2960
2961         if(uInfo->spam)
2962                 while(uInfo->spam)
2963                         spamserv_delete_spamNode(uInfo, uInfo->spam);   
2964
2965         if(uInfo->flood)
2966                 while(uInfo->flood)
2967                         spamserv_delete_floodNode(&uInfo->flood, uInfo->flood);
2968
2969         if(uInfo->joinflood)
2970                 while(uInfo->joinflood)
2971                         spamserv_delete_floodNode(&uInfo->joinflood, uInfo->joinflood);
2972
2973         dict_remove(connected_users_dict, uInfo->user->nick);
2974         free(uInfo);
2975 }
2976
2977 static void
2978 spamserv_new_user_func(struct userNode *user)
2979 {
2980         if(!IsLocal(user))
2981                 spamserv_create_user(user);
2982 }
2983
2984 static void
2985 spamserv_del_user_func(struct userNode *user, struct userNode *killer, UNUSED_ARG(const char *why))
2986 {
2987         struct userInfo *uInfo = get_userInfo(user->nick);
2988         struct killNode *kNode;
2989
2990         if(killer == spamserv)
2991         {
2992                 kNode = malloc(sizeof(struct killNode));
2993
2994                 if(!kNode)
2995                 {
2996                         log_module(SS_LOG, LOG_ERROR, "Couldn't allocate memory for killNode - nickname %s", user->nick);
2997                         spamserv_delete_user(uInfo);                    
2998                         return;
2999                 }
3000
3001                 if(uInfo->warnlevel > KILL_WARNLEVEL)
3002                         kNode->warnlevel = uInfo->warnlevel - KILL_WARNLEVEL;
3003                 else
3004                         kNode->warnlevel = 0;
3005
3006                 kNode->time = now;
3007
3008                 dict_insert(killed_users_dict, irc_ntoa(&user->ip), kNode);
3009         }
3010
3011         spamserv_delete_user(uInfo);    
3012 }
3013
3014 static void
3015 spamserv_nick_change_func(struct userNode *user, const char *old_nick)
3016 {
3017         struct userInfo *uInfo = get_userInfo(old_nick);
3018
3019         dict_remove(connected_users_dict, old_nick);
3020         dict_insert(connected_users_dict, user->nick, uInfo);
3021 }
3022
3023 static int
3024 spamserv_user_join(struct modeNode *mNode)
3025 {
3026         struct chanNode *channel = mNode->channel;
3027         struct userNode *user = mNode->user;    
3028         struct chanInfo *cInfo;
3029         struct userInfo *uInfo;
3030         struct floodNode *jfNode;
3031
3032         if(user->uplink->burst || !(cInfo = get_chanInfo(channel->name)) || !CHECK_JOINFLOOD(cInfo) || !(uInfo = get_userInfo(user->nick)))
3033                 return 0;
3034         
3035         
3036     if(!CHECK_CHANOPS(cInfo))
3037         {
3038                 //struct modeNode *mn = GetUserMode(channel, user);
3039                 //if(mn->modes & MODE_CHANOP)
3040                 //      return;
3041         if(check_user_level(channel, user, lvlGiveOps, 1, 0)) 
3042             return 0;
3043         }
3044     
3045     if(!CHECK_VOICED(cInfo))
3046         {
3047         if(check_user_level(channel, user, lvlGiveVoice, 1, 0)) 
3048             return 0;
3049     }
3050     
3051     if(cInfo->exceptlevel == 0)
3052         return 0;
3053     if(cInfo->exceptlevel < 501) {
3054       struct userData *uData;
3055        if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
3056         return 0;
3057        }
3058     }
3059
3060         if(!(jfNode = uInfo->joinflood))
3061         {
3062                 spamserv_create_floodNode(channel, user, &uInfo->joinflood);
3063         }
3064         else
3065         {
3066                 for(; jfNode; jfNode = jfNode->next)
3067                         if(jfNode->channel == channel)
3068                                 break;
3069
3070                 if(!jfNode)
3071                 {
3072                         spamserv_create_floodNode(channel, user, &uInfo->joinflood);
3073                 }
3074                 else
3075                 {
3076                         jfNode->count++;
3077                         jfNode->time = now;             
3078
3079                         if(jfNode->count > JOINFLOOD_MAX)
3080                         {
3081                                 char reason[MAXLEN];
3082
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);
3086                         }
3087                 }
3088         }
3089
3090         return 0;
3091 }
3092
3093 static void
3094 spamserv_user_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
3095 {
3096         struct userNode *user = mn->user;
3097         struct chanNode *channel = mn->channel;
3098         struct userInfo *uInfo;
3099         struct spamNode *sNode;
3100         struct floodNode *fNode;
3101
3102         if(user->dead || !get_chanInfo(channel->name) || !(uInfo = get_userInfo(user->nick)))
3103                 return;
3104
3105         if((sNode = uInfo->spam))
3106         {
3107                 for(; sNode; sNode = sNode->next)
3108                         if(sNode->channel == channel)
3109                                 break;
3110
3111                 if(sNode)
3112                         spamserv_delete_spamNode(uInfo, sNode);
3113         }
3114
3115         if((fNode = uInfo->flood))
3116         {
3117                 for(; fNode; fNode = fNode->next)
3118                         if(fNode->channel == channel)
3119                                 break;
3120
3121                 if(fNode)
3122                         spamserv_delete_floodNode(&uInfo->flood, fNode);
3123         }
3124 }
3125
3126 /***********************************************/
3127 /*                 Other Stuff                 */
3128 /***********************************************/
3129
3130 static void
3131 crc32_init(void)
3132 {
3133         unsigned long crc;
3134         int i, j;
3135
3136         for(i = 0; i < 256; i++)
3137         {
3138                 crc = i;
3139
3140                 for(j = 8; j > 0; j--)
3141                 {
3142                         if(crc & 1)
3143                         {
3144                                 crc = (crc >> 1) ^ 0xEDB88320L;
3145                         }
3146                         else
3147                         {
3148                                 crc >>= 1;
3149                         }
3150                 }
3151
3152                 crc_table[i] = crc;
3153         }
3154 }
3155
3156 static unsigned long
3157 crc32(const char *text)
3158 {
3159         register unsigned long crc = 0xFFFFFFFF;
3160         unsigned int c, i = 0;
3161         
3162         while((c = (unsigned int)text[i++]) != 0)
3163                 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];
3164  
3165         return (crc^0xFFFFFFFF);
3166 }
3167
3168 static void
3169 timeq_flood(UNUSED_ARG(void *data))
3170 {
3171         dict_iterator_t         it;
3172         struct userInfo         *uInfo;
3173         struct floodNode        *fNode;
3174
3175         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3176         {
3177                 uInfo = iter_data(it);
3178
3179                 if(!(fNode = uInfo->flood))
3180                         continue;
3181
3182                 for(; fNode; fNode = fNode->next)
3183                 {
3184                         if(now - fNode->time > FLOOD_EXPIRE)
3185                         {
3186                                 if(!(--fNode->count))
3187                                         spamserv_delete_floodNode(&uInfo->flood, fNode);
3188                         }
3189                 }
3190         }
3191         
3192         timeq_add(now + FLOOD_TIMEQ_FREQ, timeq_flood, NULL);
3193 }
3194
3195 static void
3196 timeq_joinflood(UNUSED_ARG(void *data))
3197 {
3198         dict_iterator_t it;
3199         struct userInfo *uInfo;
3200         struct floodNode *fNode;
3201
3202         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3203         {
3204                 uInfo = iter_data(it);
3205
3206                 if(!(fNode = uInfo->joinflood))
3207                         continue;
3208
3209                 for(; fNode; fNode = fNode->next)
3210                 {
3211                         if(now - fNode->time > JOINFLOOD_EXPIRE)
3212                         {
3213                                 if(!(--fNode->count))
3214                                         spamserv_delete_floodNode(&uInfo->joinflood, fNode);                            
3215                         }
3216                 }
3217         }
3218
3219         timeq_add(now + JOINFLOOD_TIMEQ_FREQ, timeq_joinflood, NULL);
3220 }
3221
3222 static void
3223 timeq_adv(UNUSED_ARG(void *data))
3224 {
3225         dict_iterator_t it;
3226         struct userInfo *uInfo;
3227
3228         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3229         {
3230                 uInfo = iter_data(it);
3231
3232                 if(uInfo->lastadv && uInfo->lastadv - now > ADV_EXPIRE)
3233                 {
3234                         uInfo->lastadv = 0;
3235                         uInfo->flags &= ~USER_ADV_WARNED;
3236                 }
3237         }
3238
3239         timeq_add(now + ADV_TIMEQ_FREQ, timeq_adv, NULL);
3240 }
3241
3242 static void
3243 timeq_warnlevel(UNUSED_ARG(void *data))
3244 {
3245         dict_iterator_t it;
3246         struct userInfo *uInfo;
3247
3248         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3249         {
3250                 uInfo = iter_data(it);
3251
3252                 if(uInfo->warnlevel > 0)
3253                         uInfo->warnlevel--;
3254         }
3255
3256         timeq_add(now + WARNLEVEL_TIMEQ_FREQ, timeq_warnlevel, NULL);
3257 }
3258
3259 static void
3260 timeq_kill(UNUSED_ARG(void *data))
3261 {
3262         dict_iterator_t it;
3263         struct killNode *kNode;
3264
3265         for(it = dict_first(killed_users_dict); it; it = iter_next(it))
3266         {
3267                 kNode = iter_data(it);
3268
3269                 if(kNode->time - now > KILL_EXPIRE)
3270                         free(kNode);
3271         }
3272
3273         timeq_add(now + KILL_TIMEQ_FREQ, timeq_kill, NULL);
3274 }
3275
3276 static int
3277 binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[])
3278 {
3279         struct chanInfo *cInfo = get_chanInfo(channel->name);
3280         int value;
3281
3282         if(argc > 1)
3283         {
3284                 if(enabled_string(argv[1]))
3285                 {
3286                         cInfo->flags |= mask;
3287                         value = 1;
3288                 }
3289                 else if(disabled_string(argv[1]))
3290                 {
3291                     cInfo->flags &= ~mask;
3292                     value = 0;
3293                 }
3294                 else
3295                 {
3296                    spamserv_notice(user, "SSMSG_INVALID_BINARY", argv[1]);
3297                    return 0;
3298                 }
3299         }
3300         else
3301         {
3302                 value = (cInfo->flags & mask) ? 1 : 0;
3303         }
3304
3305         spamserv_notice(user, "SSMSG_STRING_VALUE", name, value ? "Enabled." : "Disabled.");
3306         return 1;
3307 }
3308
3309 struct valueData
3310 {
3311         char *description;
3312         char value;
3313         int  oper_only : 1;
3314 };
3315
3316 static int
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[])
3318 {
3319         struct chanInfo *cInfo = get_chanInfo(channel->name);
3320         int index;
3321
3322         if(argc > 1)
3323         {
3324                 index = atoi(argv[1]);
3325                 
3326                 if(index < 0 || index >= count)
3327                 {
3328                         spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, description);
3329
3330             for(index = 0; index < count; index++)
3331                 spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
3332
3333                         return 0;
3334                 }
3335
3336                 if(values[index].oper_only && !IsOper(user))
3337                 {
3338                         spamserv_notice(user, "SSMSG_MUST_BE_OPER");
3339                         return 0;
3340                 }
3341                 
3342                 cInfo->info[info] = values[index].value;
3343         }
3344         else
3345         {
3346                 for(index = 0; index < count && cInfo->info[info] != values[index].value; index++);
3347         }
3348
3349         spamserv_notice(user, "SSMSG_NUMERIC_VALUE", name, index, values[index].description);
3350         return 1;
3351 }
3352
3353 static int
3354 show_exceptions(struct userNode *user, struct chanInfo *cInfo)
3355 {
3356         struct helpfile_table table;
3357         unsigned int i;
3358
3359         if(!cInfo->exceptions->used)
3360         {
3361                 spamserv_notice(user, "SSMSG_NO_EXCEPTIONS");
3362                 return 0;
3363         }
3364
3365         spamserv_notice(user, "SSMSG_EXCEPTION_LIST");
3366
3367         table.length = 0;
3368         table.width = 1;
3369         table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3370         table.contents = alloca(cInfo->exceptions->used * sizeof(*table.contents));
3371
3372         for(i = 0; i < cInfo->exceptions->used; i++)
3373         {
3374                 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
3375                 table.contents[table.length][0] = cInfo->exceptions->list[i];
3376                 table.length++;
3377         }
3378         
3379         table_send(spamserv, user->nick, 0, NULL, table);
3380
3381         return 1;
3382 }
3383
3384 static void
3385 show_memory_usage(struct userNode *user)
3386 {
3387         dict_iterator_t it;
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;
3395         char buffer[64];
3396
3397         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
3398         {
3399                 cInfo = iter_data(it);
3400
3401                 if(!cInfo->exceptions->used)
3402                         continue;
3403
3404                 for(i = 0; i < cInfo->exceptions->used; i++)
3405                         channel_size += strlen(cInfo->exceptions->list[i]) * sizeof(char);              
3406         }
3407
3408         for(it = dict_first(connected_users_dict); it; it = iter_next(it))
3409         {
3410                 uInfo = iter_data(it);
3411
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++);
3415         }
3416
3417         channel_size += dict_size(registered_channels_dict) * sizeof(struct chanInfo);
3418         
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);
3423
3424         size = channel_size + user_size;
3425         
3426         ss_reply("SSMSG_STATUS_MEMORY");
3427         
3428         table.length = 3;
3429         table.width = 4;
3430         table.flags = TABLE_NO_FREE | TABLE_NO_HEADERS | TABLE_PAD_LEFT;
3431         table.contents = calloc(table.length, sizeof(char**));
3432
3433         // chanInfo
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);
3443
3444         // userInfo
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);
3454
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);
3465
3466         table_send(spamserv, user->nick, 0, NULL, table);
3467         
3468         for(i = 0; i < table.length; i++)
3469         {
3470                 for(j = 0; j < table.width; j++)
3471                         free((char*)table.contents[i][j]);
3472
3473         free(table.contents[i]);
3474         }
3475
3476         free(table.contents);
3477 }
3478
3479 static void
3480 show_registered_channels(struct userNode *user)
3481 {
3482         struct helpfile_table table;
3483         dict_iterator_t it;
3484
3485         spamserv_notice(user, "SSMSG_STATUS_CHANNEL_LIST");
3486
3487         if(!dict_size(registered_channels_dict))
3488         {
3489                 spamserv_notice(user, "SSMSG_STATUS_NO_CHANNEL");
3490                 return;
3491         }
3492
3493         table.length = 0;
3494         table.width = 1;
3495         table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3496         table.contents = alloca(dict_size(registered_channels_dict) * sizeof(*table.contents));
3497
3498         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
3499         {
3500                 struct chanInfo *cInfo = iter_data(it);
3501
3502                 table.contents[table.length] = alloca(table.width * sizeof(**table.contents));
3503                 table.contents[table.length][0] = cInfo->channel->name;
3504                 table.length++;
3505         }
3506         
3507         table_send(spamserv, user->nick, 0, NULL, table);
3508 }
3509
3510 /***********************************************/
3511 /*                SpamServ_Func                */
3512 /***********************************************/
3513
3514 static 
3515 SPAMSERV_FUNC(cmd_register)
3516 {
3517         struct chanInfo *cInfo;
3518         char reason[MAXLEN];
3519
3520         if(!channel || !channel->channel_info)
3521         {
3522                 ss_reply("SSMSG_NOT_REGISTERED_CS", channel->name);
3523                 return 0;
3524         }
3525
3526         if(get_chanInfo(channel->name))
3527         {
3528                 ss_reply("SSMSG_ALREADY_REGISTERED", channel->name);
3529                 return 0;
3530         }
3531
3532         if(IsSuspended(channel->channel_info))
3533         {
3534                 ss_reply("SSMSG_SUSPENDED_CS", channel->name, "register");
3535                 return 0;
3536         }
3537
3538         if(channel == spamserv_conf.debug_channel)
3539         {
3540                 ss_reply("SSMSG_DEBUG_CHAN");
3541                 return 0;
3542         }
3543
3544         if(!(cInfo = spamserv_register_channel(channel, spamserv_conf.global_exceptions, CHAN_FLAGS_DEFAULT , CHAN_INFO_DEFAULT)))
3545         {
3546                 ss_reply("SSMSG_NO_REGISTER", channel->name);
3547                 return 0;
3548         }
3549
3550         spamserv_join_channel(cInfo->channel);
3551         
3552         spamserv_oper_message(SSMSG_CHANNEL_REGISTERED, spamserv->nick, channel->name, user->handle_info->handle);
3553         ss_reply("SSMSG_REG_SUCCESS", channel->name);
3554
3555         return 1;
3556 }
3557
3558 static 
3559 SPAMSERV_FUNC(cmd_unregister)
3560 {
3561         struct chanInfo *cInfo;
3562         struct chanData *cData;
3563         struct userData *uData;
3564         char reason[MAXLEN];
3565
3566         if(!channel || !(cData = channel->channel_info) || !(cInfo = get_chanInfo(channel->name)))
3567         {
3568                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3569                 return 0;
3570         }
3571
3572         if(!(uData = GetChannelUser(cData, user->handle_info)) || (uData->access < UL_OWNER))
3573         {
3574         ss_reply("SSMSG_NO_ACCESS");
3575         return 0;
3576         }
3577
3578         if(!IsHelping(user))
3579         {
3580         if(IsSuspended(cData))
3581         {
3582             ss_reply("SSMSG_SUSPENDED_CS", channel->name, "unregister");
3583             return 0;
3584         }
3585
3586                 if(argc < 2 || strcasecmp(argv[1], "CONFIRM"))
3587                 {
3588                         ss_reply("SSMSG_CONFIRM_UNREG");
3589                         return 0;
3590                 }
3591         }
3592
3593         if(!CHECK_SUSPENDED(cInfo))
3594         {
3595                 snprintf(reason, sizeof(reason), "%s unregistered by %s.", spamserv->nick, user->handle_info->handle);          
3596                 spamserv_part_channel(channel, reason);
3597         }
3598         
3599         spamserv_unregister_channel(cInfo);     
3600
3601         spamserv_oper_message(SSMSG_CHANNEL_UNREGISTERED, channel->name, user->handle_info->handle);
3602         ss_reply("SSMSG_UNREG_SUCCESS", channel->name);
3603
3604         return 1;
3605 }
3606
3607 static 
3608 SPAMSERV_FUNC(cmd_status)
3609 {
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));
3613
3614         if(IsOper(user) && argc > 1)
3615         {
3616                 if(!irccasecmp(argv[1], "memory"))
3617                         show_memory_usage(user);
3618                 else if(!irccasecmp(argv[1], "channels"))
3619                         show_registered_channels(user);         
3620         }
3621         
3622         return 1;
3623 }
3624
3625 static 
3626 SPAMSERV_FUNC(cmd_addexception)
3627 {
3628         struct chanInfo *cInfo = get_chanInfo(channel->name);
3629         struct userData *uData;
3630         unsigned int i;
3631
3632         if(!cInfo || !channel->channel_info)
3633         {
3634                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3635                 return 0;
3636         }
3637
3638         if(CHECK_SUSPENDED(cInfo))
3639         {
3640                 ss_reply("SSMSG_SUSPENDED", channel->name);
3641                 return 0;
3642         }
3643
3644         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
3645         {
3646                 ss_reply("SSMSG_NO_ACCESS");
3647                 return 0;
3648         }
3649
3650         if(argc < 2)
3651                 return show_exceptions(user, cInfo);
3652
3653         if(cInfo->exceptions->used == spamserv_conf.exception_max && !IsOper(user))
3654         {
3655                 ss_reply("SSMSG_EXCEPTION_MAX", spamserv_conf.exception_max);
3656                 return 0;
3657         }
3658
3659         if(strlen(argv[1]) < spamserv_conf.exception_min_len)
3660         {
3661                 ss_reply("SSMSG_EXCEPTION_TOO_SHORT", spamserv_conf.exception_min_len);
3662                 return 0;
3663         }
3664         else if(strlen(argv[1]) > spamserv_conf.exception_max_len)
3665         {
3666                 ss_reply("SSMSG_EXCEPTION_TOO_LONG", spamserv_conf.exception_max_len);
3667                 return 0;
3668         }
3669
3670         for(i = 0; i < cInfo->exceptions->used; i++)
3671         {
3672                 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
3673                 {
3674                         ss_reply("SSMSG_EXCEPTION_IN_LIST", argv[1]);
3675                         return 0;
3676                 }
3677         }
3678
3679         string_list_append(cInfo->exceptions, strdup(argv[1]));
3680         ss_reply("SSMSG_EXCEPTION_ADDED", argv[1]);
3681
3682         return 1;
3683 }
3684
3685 static 
3686 SPAMSERV_FUNC(cmd_delexception)
3687 {
3688         struct chanInfo *cInfo = get_chanInfo(channel->name);
3689         struct userData *uData;
3690         unsigned int i;
3691         int found = -1;
3692
3693         if(!cInfo || !channel->channel_info)
3694         {
3695                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3696                 return 0;
3697         }
3698
3699         if(CHECK_SUSPENDED(cInfo))
3700         {
3701                 ss_reply("SSMSG_SUSPENDED", channel->name);
3702                 return 0;
3703         }
3704
3705         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < 400))
3706         {
3707                 ss_reply("SSMSG_NO_ACCESS");
3708                 return 0;
3709         }
3710
3711         if(argc < 2)
3712                 return show_exceptions(user, cInfo);
3713
3714         for(i = 0; i < cInfo->exceptions->used; i++)
3715         {
3716                 if(!irccasecmp(argv[1], cInfo->exceptions->list[i]))
3717                 {
3718                         found = i;
3719                         break;
3720                 }
3721         }
3722         
3723         if(found == -1)
3724         {
3725                 ss_reply("SSMSG_NO_SUCH_EXCEPTION", argv[1]);
3726                 return 0;
3727         }
3728
3729         string_list_delete(cInfo->exceptions, i);
3730         ss_reply("SSMSG_EXCEPTION_DELETED", argv[1]);
3731
3732         return 1;
3733 }
3734
3735 static 
3736 SPAMSERV_FUNC(cmd_set)
3737 {
3738         struct chanInfo *cInfo = get_chanInfo(channel->name);
3739         struct svccmd   *subcmd;        
3740         char cmd_name[MAXLEN];
3741         unsigned int i;
3742
3743         if(!cInfo)
3744         {
3745                 ss_reply("SSMSG_NOT_REGISTERED", channel->name);
3746                 return 0;
3747         }
3748
3749         if(CHECK_SUSPENDED(cInfo))
3750         {
3751                 ss_reply("SSMSG_SUSPENDED", channel->name);
3752                 return 0;
3753         }
3754
3755     if(!check_user_level(channel,user,lvlSetters,1,0))
3756         {
3757                 ss_reply("SSMSG_NO_ACCESS");
3758                 return 0;
3759         }
3760         
3761         if(argc < 2)
3762         {
3763                 ss_reply("SSMSG_CHANNEL_OPTIONS");
3764
3765                 for(i = 0; i < SET_SUBCMDS_SIZE; i++)
3766                 {
3767                         sprintf(cmd_name, "%s %s", cmd->name, set_subcommands[i]);
3768
3769                         if((subcmd = dict_find(cmd->parent->commands, cmd_name, NULL)))
3770                                 subcmd->command->func(user, channel, 1, argv + 1, subcmd);
3771                 }
3772
3773                 return 1;
3774         }
3775
3776         sprintf(cmd_name, "%s %s", cmd->name, argv[1]);
3777         subcmd = dict_find(cmd->parent->commands, cmd_name, NULL);
3778
3779         if(!subcmd)
3780         {
3781                 reply("SSMSG_INVALID_OPTION", argv[1], argv[0]);
3782                 return 0;
3783         }
3784
3785         return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
3786 }
3787
3788 static 
3789 SPAMSERV_FUNC(opt_spamlimit)
3790 {
3791         struct valueData values[] =
3792         {
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}
3798         };
3799
3800         MULTIPLE_OPTION("SpamLimit     ", "SpamLimit", ci_SpamLimit);
3801 }
3802
3803 static 
3804 SPAMSERV_FUNC(opt_advreaction)
3805 {
3806         struct valueData values[] =
3807         {
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}
3813         };
3814
3815         MULTIPLE_OPTION("AdvReaction   ", "AdvReaction", ci_AdvReaction);
3816 }
3817
3818 static 
3819 SPAMSERV_FUNC(opt_warnreaction)
3820 {
3821         struct valueData values[] =
3822         {
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}
3828         };
3829
3830         MULTIPLE_OPTION("WarnReaction  ", "WarnReaction", ci_WarnReaction);
3831 }
3832
3833 static 
3834 SPAMSERV_FUNC(opt_advscan)
3835 {
3836         BINARY_OPTION("AdvScan       ", CHAN_ADV_SCAN);
3837 }
3838
3839 static 
3840 SPAMSERV_FUNC(opt_spamscan)
3841 {
3842         BINARY_OPTION("SpamScan      ", CHAN_SPAMSCAN);
3843 }
3844
3845 static 
3846 SPAMSERV_FUNC(opt_floodscan)
3847 {
3848         BINARY_OPTION("FloodScan     ", CHAN_FLOODSCAN);
3849 }
3850
3851 static 
3852 SPAMSERV_FUNC(opt_joinflood)
3853 {
3854         BINARY_OPTION("JoinFloodScan ", CHAN_JOINFLOOD);
3855 }
3856
3857 static 
3858 SPAMSERV_FUNC(opt_scanops)
3859 {
3860         BINARY_OPTION("ScanChanOps   ", CHAN_SCAN_CHANOPS);
3861 }
3862
3863 static 
3864 SPAMSERV_FUNC(opt_scanvoiced)
3865 {
3866         BINARY_OPTION("ScanVoiced    ", CHAN_SCAN_VOICED);
3867 }
3868
3869 static 
3870 SPAMSERV_FUNC(opt_exceptlevel)
3871 {
3872  struct chanInfo *cInfo = get_chanInfo(channel->name);
3873  struct userData *uData;
3874  int index;
3875  if(argc > 1)
3876         {
3877                 index = atoi(argv[1]);
3878                 if(index < 1 || index > 501)
3879                 {
3880                         spamserv_notice(user, "SSMSG_INVALID_NUM_SET", index, "ExceptLevel");
3881                         return 0;
3882                 }
3883         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)) || (uData->access < cInfo->exceptlevel || index > uData->access))
3884         {
3885             ss_reply("SSMSG_NO_ACCESS");
3886             return 0;
3887         }
3888         cInfo->exceptlevel=index;
3889     } else {
3890      index=cInfo->exceptlevel;
3891     }
3892     spamserv_notice(user, "SSMSG_EASYNUMERIC_VALUE", "ExceptLevel   ", index);
3893     return 1;
3894 }
3895
3896 static void 
3897 to_lower(char *message)
3898 {
3899         unsigned int i, diff = 'a' - 'A';
3900
3901         for(i = 0; i < strlen(message); i++)
3902         {
3903                 if((message[i] >= 'A') && (message[i] <= 'Z'))
3904                         message[i] = message[i] + diff;
3905         }
3906 }
3907
3908 static char *
3909 strip_mirc_codes(char *text)
3910 {
3911         // taken from xchat and modified
3912         int nc = 0, i = 0, col = 0, len = strlen(text);
3913         static char new_str[MAXLEN];
3914
3915         while(len > 0)
3916         {
3917                 if((col && isdigit(*text) && nc < 2) ||
3918                         (col && *text == ',' && isdigit(*(text + 1)) && nc < 3))
3919                 {
3920                         nc++;
3921
3922                         if(*text == ',')
3923                                 nc = 0;
3924                 }
3925                 else
3926                 {
3927                         col = 0;
3928
3929                         switch(*text)
3930                         {
3931                         case '\003':
3932                                 col = 1;
3933                                 nc = 0;
3934                                 break;
3935                         case '\002':
3936                         case '\022':
3937                         case '\026':                    
3938                         case '\031':
3939                         case '\037':
3940                                 break;
3941                         default:
3942                                 new_str[i] = *text;
3943                                 i++;
3944                         }
3945                 }
3946
3947                 text++;
3948                 len--;
3949         }
3950
3951         new_str[i] = '\0';
3952
3953         return new_str;
3954 }
3955
3956 static int
3957 is_in_exception_list(struct chanInfo *cInfo, char *message)
3958 {
3959         unsigned int i;
3960
3961         for(i = 0; i < cInfo->exceptions->used; i++)
3962                 if(strstr(message, cInfo->exceptions->list[i]))
3963                         return 1;
3964
3965         return 0;
3966 }
3967
3968 static int
3969 check_advertising(struct chanInfo *cInfo, char *message)
3970 {
3971         unsigned int i = 0;
3972
3973         if(spamserv_conf.strip_mirc_codes)
3974                 message = strip_mirc_codes(message);
3975
3976         if(is_in_exception_list(cInfo, message))
3977                 return 0;
3978
3979         while(message[i] != 0)
3980         {
3981                 if(message[i] == '#')
3982                 {
3983                         char channelname[CHANNELLEN];
3984                         unsigned int j = 0;
3985
3986                         if(!spamserv_conf.adv_chan_must_exist)
3987                                 return 1;
3988
3989                         /* only return 1, if the channel does exist */  
3990
3991                         while((message[i] != 0) && (message[i] != ' '))
3992                         {
3993                                 channelname[j] = message[i];
3994                                 i++;
3995                                 j++;                            
3996                         }
3997
3998                         channelname[j] = '\0';
3999
4000                         if(GetChannel(channelname))
4001                                 return 1;
4002                 }
4003                 else if((message[i] == 'w') && (message[i+1] == 'w') && (message[i+2] == 'w') && (message[i+3] == '.'))
4004                         return 1;
4005                 else if((message[i] == 'h') && (message[i+1] == 't') && (message[i+2] == 't') && (message[i+3] == 'p') && (message[i+4] == ':'))
4006                         return 1;
4007                 else if((message[i] == 'f') && (message[i+1] == 't') && (message[i+2] == 'p') && ((message[i+3] == '.') || (message[i+3] == ':')))
4008                         return 1;
4009
4010                 i++;
4011         }
4012
4013         return 0;
4014 }
4015
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);
4017
4018 static void
4019 spamserv_punish(struct chanNode *channel, struct userNode *user, time_t expires, char *reason, int ban)
4020 {
4021         if(ban)
4022         {
4023                 struct mod_chanmode change;
4024                 char *hostmask = generate_hostmask(user, GENMASK_STRICT_HOST | GENMASK_ANY_IDENT);
4025
4026                 sanitize_ircmask(hostmask);
4027
4028                 if(expires)
4029                         add_channel_ban(channel->channel_info, hostmask, spamserv->nick, now, now, now + expires, reason);
4030
4031                 mod_chanmode_init(&change);
4032                 change.argc = 1;
4033                 change.args[0].mode = MODE_BAN;
4034       change.args[0].u.hostmask = hostmask;
4035                 mod_chanmode_announce(spamserv, channel, &change);        
4036
4037                 free(hostmask);
4038
4039                 spamserv_debug(SSMSG_DEBUG_BAN, user->nick, channel->name, reason);
4040         }
4041         else
4042                 spamserv_debug(SSMSG_DEBUG_KICK, user->nick, channel->name, reason);
4043
4044         KickChannelUser(user, channel, spamserv, reason);       
4045 }
4046
4047 void
4048 spamserv_channel_message(struct chanNode *channel, struct userNode *user, char *text)
4049 {
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];
4056
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)))
4059                 return;
4060
4061         if(!CHECK_CHANOPS(cInfo))
4062         {
4063                 struct modeNode *mn = GetUserMode(channel, user);
4064                 if(mn && mn->modes & MODE_CHANOP)
4065                         return;
4066         //if(check_user_level(channel, user, lvlGiveOps, 1, 0)) 
4067         //    return;
4068         }
4069         
4070         if(!CHECK_VOICED(cInfo))
4071         {
4072                 struct modeNode *mn = GetUserMode(channel, user);
4073                 if(mn && (mn->modes & MODE_VOICE) && !(mn->modes & MODE_CHANOP))
4074                         return;
4075         }
4076     
4077     if(cInfo->exceptlevel == 0)
4078         return;
4079     if(cInfo->exceptlevel < 501) {
4080       struct userData *uData;
4081        if((uData = GetChannelUser(channel->channel_info, user->handle_info)) && (uData->access >= cInfo->exceptlevel)) {
4082         return;
4083        }
4084     }
4085
4086         to_lower(text);
4087
4088         if(CHECK_SPAM(cInfo))
4089         {
4090                 if(!(sNode = uInfo->spam))
4091                 {
4092                         spamserv_create_spamNode(channel, uInfo, text);
4093                 }
4094                 else
4095                 {
4096                         for(; sNode; sNode = sNode->next)
4097                                 if(sNode->channel == channel)
4098                                         break;
4099
4100                         if(!sNode)
4101                         {
4102                                 spamserv_create_spamNode(channel, uInfo, text);
4103                         }
4104                         else
4105                         {
4106                                 unsigned long crc = crc32(text);
4107
4108                                 if(crc == sNode->crc32)
4109                                 {
4110                                         unsigned int spamlimit = 2;
4111                                         sNode->count++;
4112
4113                                         switch(cInfo->info[ci_SpamLimit])
4114                                         {
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;
4120                                         }
4121
4122                                         if(sNode->count == spamlimit)
4123                                         {
4124                                                 uInfo->warnlevel += SPAM_WARNLEVEL;
4125
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);
4128                                         }
4129                                         else if(sNode->count > spamlimit)
4130                                         {
4131                                                 switch(cInfo->info[ci_WarnReaction])
4132                                                 {
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;
4138                                                 }
4139
4140                                                 spamserv_delete_spamNode(uInfo, sNode);
4141                                                 uInfo->warnlevel += SPAM_WARNLEVEL;
4142                                                 violation = 1;
4143                                         }
4144                                 }
4145                                 else
4146                                 {
4147                                         sNode->crc32 = crc;                                     
4148                                         sNode->count = 1;
4149                                 }
4150                         }
4151                 }
4152         }
4153
4154         if(CHECK_FLOOD(cInfo))
4155         {
4156                 if(!(fNode = uInfo->flood))
4157                 {
4158                         spamserv_create_floodNode(channel, user, &uInfo->flood);
4159                 }
4160                 else
4161                 {
4162                         for(; fNode; fNode = fNode->next)
4163                                 if(fNode->channel == channel)
4164                                         break;
4165                                 
4166                         if(!fNode)
4167                         {
4168                                 spamserv_create_floodNode(channel, user, &uInfo->flood);
4169                         }
4170                         else
4171                         {
4172                                 if(((now - fNode->time) < FLOOD_EXPIRE))
4173                                 {
4174                                         fNode->count++;
4175                                         
4176                                         if(fNode->count == FLOOD_MAX_LINES - 1)
4177                                         {
4178                                                 uInfo->warnlevel += FLOOD_WARNLEVEL;
4179
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);
4182                                         }
4183                                         else if(fNode->count > FLOOD_MAX_LINES)
4184                                         {
4185                                                 switch(cInfo->info[ci_WarnReaction])
4186                                                 {
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;
4192                                                 }
4193
4194                                                 spamserv_delete_floodNode(&uInfo->flood, fNode);
4195                                                 uInfo->warnlevel += FLOOD_WARNLEVEL;
4196                                                 violation = 2;                                          
4197                                         }
4198                                 }
4199
4200                                 fNode->time = now;
4201                         }
4202                 }
4203         }
4204
4205         if(CHECK_ADV(cInfo) && check_advertising(cInfo, text))
4206         {
4207                 if(CHECK_ADV_WARNED(uInfo))
4208                 {
4209                         switch(cInfo->info[ci_AdvReaction])
4210                         {
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;
4216                         }
4217
4218                         uInfo->warnlevel += ADV_WARNLEVEL;
4219                         violation = 3;
4220                 }
4221                 else
4222                 {               
4223                         uInfo->flags |= USER_ADV_WARNED;
4224                         uInfo->lastadv = now;
4225                         uInfo->warnlevel += ADV_WARNLEVEL;
4226
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);
4229                 }               
4230         }
4231
4232         if(!CHECK_WARNED(uInfo) && !CHECK_KILL(uInfo) && !CHECK_GLINE(uInfo) && uInfo->warnlevel == MAX_WARNLEVEL)
4233         {
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);
4238         }
4239         else if(uInfo->warnlevel > MAX_WARNLEVEL)
4240         {
4241                 if(CHECK_KILLED(uInfo))
4242                         uInfo->flags |= USER_GLINE;
4243                 else
4244                         uInfo->flags |= USER_KILL;
4245
4246                 violation = 5;
4247         }
4248
4249         if(!violation)
4250                 return;
4251
4252         switch(violation)
4253         {
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;
4258         }
4259
4260         if(CHECK_GLINE(uInfo))
4261         {
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);
4267         }
4268         else if(CHECK_KILL(uInfo))
4269         {
4270                 DelUser(user, spamserv, 1, reason);
4271                 spamserv_debug(SSMSG_DEBUG_KILL, user->nick, channel->name);
4272         }
4273         else if(CHECK_LONG_TBAN(uInfo))
4274         {
4275                 spamserv_punish(channel, user, spamserv_conf.long_ban_duration, reason, 1);
4276         }
4277         else if(CHECK_SHORT_TBAN(uInfo))
4278         {
4279                 spamserv_punish(channel, user, spamserv_conf.short_ban_duration, reason, 1);
4280         }
4281         else if(CHECK_KICKBAN(uInfo))
4282         {
4283                 spamserv_punish(channel, user, 0, reason, 1);
4284         }
4285         else if(CHECK_KICK(uInfo))
4286         {
4287                 spamserv_punish(channel, user, 0, reason, 0);
4288         }
4289 }
4290
4291 static int
4292 spamserv_saxdb_read(struct dict *database)
4293 {
4294         dict_iterator_t it;
4295         struct record_data *hir;
4296         struct chanNode *channel;
4297         struct chanInfo *cInfo;
4298         struct string_list *strlist;
4299         unsigned int flags,exceptlevel;
4300         char *str, *info;       
4301         unsigned long expiry;    
4302
4303         for(it = dict_first(database); it; it = iter_next(it))
4304         {
4305                 hir = iter_data(it);
4306
4307                 if(hir->type != RECDB_OBJECT)
4308                 {
4309                         log_module(SS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
4310                         continue;
4311                 }
4312
4313                 channel = GetChannel(iter_key(it));
4314                 strlist = database_get_data(hir->d.object, KEY_EXCEPTIONS, RECDB_STRING_LIST);
4315
4316                 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
4317                 flags = str ? atoi(str) : 0;
4318
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;
4322         
4323                 str = database_get_data(hir->d.object, KEY_EXPIRY, RECDB_QSTRING);
4324                 expiry = str ? strtoul(str, NULL, 0) : 0;
4325
4326                 if(channel && info)
4327                 {
4328                         if((cInfo = spamserv_register_channel(channel, strlist, flags, info)))
4329                         {
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))
4333                                 {
4334                                         cInfo->flags &= ~CHAN_SUSPENDED;
4335                                         spamserv_join_channel(cInfo->channel);
4336                                 }
4337                                 else if(!CHECK_SUSPENDED(cInfo))
4338                                         spamserv_join_channel(cInfo->channel);
4339                                 else
4340                                         cInfo->suspend_expiry = expiry;                 
4341                 cInfo->exceptlevel=exceptlevel;
4342                         }
4343                 }
4344                 else
4345                         log_module(SS_LOG, LOG_ERROR, "Couldn't register channel %s. Channel or info invalid.", iter_key(it));  
4346         }
4347
4348         return 0;
4349 }
4350
4351 static int
4352 spamserv_saxdb_write(struct saxdb_context *ctx)
4353 {
4354         dict_iterator_t it;
4355
4356         for(it = dict_first(registered_channels_dict); it; it = iter_next(it))
4357         {
4358                 struct chanInfo *cInfo = iter_data(it);
4359
4360                 saxdb_start_record(ctx, cInfo->channel->name, 1);
4361
4362                 if(cInfo->exceptions->used)
4363                         saxdb_write_string_list(ctx, KEY_EXCEPTIONS, cInfo->exceptions);
4364
4365                 if(cInfo->flags)
4366                         saxdb_write_int(ctx, KEY_FLAGS, cInfo->flags);  
4367
4368                 saxdb_write_string(ctx, KEY_INFO, cInfo->info);         
4369         saxdb_write_int(ctx, KEY_EXCEPTLEVEL, cInfo->exceptlevel);              
4370
4371                 if(cInfo->suspend_expiry)
4372                         saxdb_write_int(ctx, KEY_EXPIRY, cInfo->suspend_expiry);                
4373
4374                 saxdb_end_record(ctx);          
4375         }
4376         return 0;
4377 }
4378
4379 static void
4380 spamserv_conf_read(void)
4381 {
4382         dict_t conf_node;
4383         const char *str; 
4384
4385         if(!(conf_node = conf_get_data(SPAMSERV_CONF_NAME, RECDB_OBJECT)))
4386         {
4387                 log_module(SS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", SPAMSERV_CONF_NAME);
4388                 return;
4389         }
4390
4391         str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
4392         if(str)
4393         {
4394                 spamserv_conf.debug_channel = AddChannel(str, now, "+tinms", NULL);
4395
4396                 if(spamserv_conf.debug_channel)
4397                         spamserv_join_channel(spamserv_conf.debug_channel);
4398         }
4399         else
4400         {
4401                 spamserv_conf.debug_channel = NULL;
4402         }
4403
4404         str = database_get_data(conf_node, KEY_OPER_CHANNEL, RECDB_QSTRING);
4405         if(str)
4406         {
4407                 spamserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
4408         }
4409         else
4410         {
4411                 spamserv_conf.oper_channel = NULL;
4412         }
4413
4414         spamserv_conf.global_exceptions = database_get_data(conf_node, KEY_GLOBAL_EXCEPTIONS, RECDB_STRING_LIST);
4415
4416         str = database_get_data(conf_node, KEY_NETWORK_RULES, RECDB_QSTRING);
4417         spamserv_conf.network_rules = str ? str : NULL;
4418
4419         str = database_get_data(conf_node, KEY_TRIGGER, RECDB_QSTRING);
4420         spamserv_conf.trigger = str ? str[0] : 0;
4421
4422         str = database_get_data(conf_node, KEY_SHORT_BAN_DURATION, RECDB_QSTRING);
4423         spamserv_conf.short_ban_duration = str ? ParseInterval(str) : ParseInterval("15m");
4424
4425         str = database_get_data(conf_node, KEY_LONG_BAN_DURATION, RECDB_QSTRING);
4426         spamserv_conf.long_ban_duration = str ? ParseInterval(str) : ParseInterval("1h");
4427
4428         str = database_get_data(conf_node, KEY_GLINE_DURATION, RECDB_QSTRING);
4429         spamserv_conf.gline_duration = str ? ParseInterval(str) : ParseInterval("1h");
4430
4431         str = database_get_data(conf_node, KEY_EXCEPTION_MAX, RECDB_QSTRING);
4432         spamserv_conf.exception_max = str ? strtoul(str, NULL, 0) : 10;
4433
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;
4436
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;
4439
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;
4442
4443         str = database_get_data(conf_node, KEY_STRIP_MIRC_CODES, RECDB_QSTRING);
4444         spamserv_conf.strip_mirc_codes = str ? enabled_string(str) : 0;
4445
4446         str = database_get_data(conf_node, KEY_ALLOW_MOVE_MERGE, RECDB_QSTRING);
4447         spamserv_conf.allow_move_merge = str ? enabled_string(str) : 0;
4448 }
4449
4450 static void
4451 spamserv_db_cleanup(void)
4452 {
4453         dict_iterator_t it;
4454
4455         while((it = dict_first(registered_channels_dict)))
4456         {
4457                 spamserv_unregister_channel(iter_data(it));
4458         }
4459
4460         while((it = dict_first(killed_users_dict)))
4461         {
4462                 free(iter_data(it));
4463         }
4464         
4465         dict_delete(registered_channels_dict);
4466         dict_delete(connected_users_dict);
4467         dict_delete(killed_users_dict);
4468 }
4469
4470 void
4471 init_spamserv(const char *nick)
4472 {
4473         if(!nick)
4474                 return;
4475
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;
4480
4481         conf_register_reload(spamserv_conf_read);
4482
4483         SS_LOG = log_register_type("SpamServ", "file:spamserv.log");    
4484
4485         registered_channels_dict = dict_new();
4486         connected_users_dict = dict_new();
4487         killed_users_dict = dict_new();
4488
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);
4494
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);
4500
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);
4518
4519         saxdb_register("SpamServ", spamserv_saxdb_read, spamserv_saxdb_write);
4520         reg_exit_func(spamserv_db_cleanup);
4521         message_register_table(msgtab);
4522         crc32_init();
4523 }
4524 >>>>>>> master