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