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