1 /* mod-watchdog.c - Watchdog module for srvx
2 * Copyright 2003-2004 Martijn Smit and srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 /* Adds new section to srvx.conf:
38 #define KEY_BADWORDS "badwords"
39 #define KEY_BADWORD_MASK "mask"
40 #define KEY_BADWORD_TRIGGERED "count"
41 #define KEY_BADWORD_ACTION "action"
42 #define KEY_CHANNELS "channel"
43 #define KEY_BADWORDID "badwordid"
45 static const struct message_entry msgtab[] = {
46 { "WDMSG_REGISTER_SUCCESS", "$b%s$b is now registered with %s." },
47 { "WDMSG_NOT_REGISTERED", "$b%s$b is not registered with %s." },
48 { "WDMSG_ALREADY_ADDED", "$b%s$b is already added. (ID: %s)" },
49 { "WDMSG_BADWORD_ADDED", "added '$b%s$b' to the badword list with ID %s." },
50 { "WDMSG_BADWORD_NOT_FOUND", "badword with ID %s does not exist." },
51 { "WDMSG_BADWORD_REMOVED", "badword ID $b%s$b has been removed (mask: '%s')" },
52 { "ID_DEBUG", "%s: %s" },
59 unsigned int triggered : 29;
60 unsigned int action : 3;
63 struct watchdog_channel {
64 struct chanNode *channel;
65 //struct shitList *shitlist;
68 /* badword.action fields */
69 #define BADACTION_KICK 1
70 #define BADACTION_KILL 2
71 #define BADACTION_GLINE 3
78 const char *watchdog_module_deps[] = { NULL };
79 struct userNode *watchdog;
80 static struct module *watchdog_module;
81 static struct service *watchdog_service;
82 static dict_t shitlist;
83 static dict_t chanlist;
84 static struct log_type *MS_LOG;
85 static unsigned int last_badword_id = 0;
87 static struct watchdog_channel *add_channel(const char *name);
88 static struct badword *add_badword(const char *badword_mask, unsigned int triggered, unsigned int action, const char *id);
89 #define watchdog_notice(target, format...) send_message(target , watchdog , ## format)
91 static MODCMD_FUNC(cmd_addbad)
94 char *mask = unsplit_string(argv + 1, argc - 1, NULL);
95 for (it = dict_first(shitlist); it; it = iter_next(it)) {
96 struct badword *badword = iter_data(it);
97 if(match_ircglob(mask,badword->badword_mask)) {
98 reply("WDMSG_ALREADY_ADDED", mask, badword->id);
103 struct badword *new_badword = add_badword(mask, 0, BADACTION_KICK, NULL);
104 for (it = dict_first(shitlist); it; it = iter_next(it)) {
105 struct badword *badword = iter_data(it);
106 if(match_ircglob(badword->badword_mask, new_badword->badword_mask) && badword != new_badword) {
107 dict_remove(shitlist, badword->id);
111 reply("WDMSG_BADWORD_ADDED", new_badword->badword_mask, new_badword->id);
115 static MODCMD_FUNC(cmd_delbad)
119 for (n=1; n<argc; n++) {
120 struct badword *badword = dict_find(shitlist, argv[n], NULL);
122 reply("WDMSG_BADWORD_NOT_FOUND", argv[n]);
125 reply("WDMSG_BADWORD_REMOVED", argv[n], badword->badword_mask);
126 dict_remove(shitlist, argv[n]);
131 static MODCMD_FUNC(cmd_editbad)
138 badwords_sort(const void *pa, const void *pb)
140 struct badword *a = *(struct badword**)pa;
141 struct badword *b = *(struct badword**)pb;
143 return strtoul(a->id, NULL, 0) - strtoul(b->id, NULL, 0);
146 static MODCMD_FUNC(cmd_listbad)
148 struct helpfile_table tbl;
149 unsigned int count = 0, ii = 0;
150 struct badword **badwords;
153 for (it = dict_first(shitlist); it; it = iter_next(it)) {
156 tbl.length = count+1;
159 tbl.flags = TABLE_NO_FREE;
160 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
161 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
162 tbl.contents[0][0] = "#";
163 tbl.contents[0][1] = "Badword";
164 tbl.contents[0][2] = "Action";
165 tbl.contents[0][3] = "(Triggered)";
168 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
170 free(tbl.contents[0]);
174 badwords = alloca(count * sizeof(badwords[0]));
175 for (it = dict_first(shitlist); it; it = iter_next(it)) {
176 struct badword *bw = iter_data(it);
179 qsort(badwords, count, sizeof(badwords[0]), badwords_sort);
180 for (ii = 1; ii <= count; ii++) {
181 struct badword *bw = badwords[ii-1];
182 tbl.contents[ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
183 tbl.contents[ii][0] = strdup(bw->id);
184 tbl.contents[ii][1] = strdup(bw->badword_mask);
187 tbl.contents[ii][2] = "KICK";
190 tbl.contents[ii][2] = "KILL";
192 case BADACTION_GLINE:
193 tbl.contents[ii][2] = "GLINE";
196 tbl.contents[ii][2] = "*undef*";
198 tbl.contents[ii][3] = strtab(bw->triggered);
200 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
201 for(ii = 1; ii < tbl.length; ++ii)
203 free(tbl.contents[ii]);
205 free(tbl.contents[0]);
210 static MODCMD_FUNC(cmd_register)
214 if((argc < 2) || !IsChannelName(argv[1]))
216 reply("MSG_NOT_CHANNEL_NAME");
220 if(opserv_bad_channel(argv[1]))
222 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
226 channel = AddChannel(argv[1], now, NULL, NULL);
228 for (it = dict_first(chanlist); it; it = iter_next(it)) {
229 struct watchdog_channel *chan = iter_data(it);
230 if(chan->channel == channel) {
231 reply("CSMSG_ALREADY_REGGED", channel->name);
236 add_channel(channel->name);
237 reply("WDMSG_REGISTER_SUCCESS", channel->name, watchdog->nick);
241 static MODCMD_FUNC(cmd_unregister)
243 struct watchdog_channel *chan = NULL;
246 for (it = dict_first(chanlist); it; it = iter_next(it)) {
247 chan = iter_data(it);
248 if(chan->channel == channel)
252 if(chan && chan->channel == channel) {
253 //found, unregister it!
254 DelChannelUser(watchdog, channel, "unregistered.", 0);
255 dict_remove(chanlist, channel->name);
256 reply("CSMSG_UNREG_SUCCESS", channel->name);
259 reply("WDMSG_NOT_REGISTERED", channel->name, watchdog->nick);
266 watchdog_channel_message(struct userNode *user, struct chanNode *chan, const char *text, struct userNode *bot, unsigned int is_notice)
271 static struct badword*
272 add_badword(const char *badword_mask, unsigned int triggered, unsigned int action, const char *id)
274 struct badword *badword;
276 badword = calloc(1, sizeof(*badword));
282 badword->id = strtab(last_badword_id);
284 badword->id = strdup(id);
285 badword->badword_mask = strdup(badword_mask);
286 badword->triggered = triggered;
287 badword->action = action;
288 dict_insert(shitlist, badword->id, badword);
293 free_shitlist_entry(void *data)
295 struct badword *badword = data;
297 free(badword->badword_mask);
301 static struct watchdog_channel*
302 add_channel(const char *name)
304 struct watchdog_channel *wc;
305 struct mod_chanmode *change;
307 if(!watchdog) //module disabled
310 wc = calloc(1, sizeof(*wc));
314 wc->channel = AddChannel(name, now, NULL, NULL);
315 change = mod_chanmode_alloc(1);
317 change->args[0].mode = MODE_CHANOP;
318 change->args[0].u.member = AddChannelUser(watchdog, wc->channel);
319 mod_chanmode_announce(watchdog, wc->channel, change);
320 mod_chanmode_free(change);
321 dict_insert(chanlist, wc->channel->name, wc);
326 free_chanlist_entry(void *data)
328 struct watchdog_channel *wc = data;
334 watchdog_conf_read(void)
339 str = "modules/watchdog";
340 if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
341 log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
345 str = database_get_data(conf_node, "nick", RECDB_QSTRING);
346 if(watchdog_conf.nick && strcmp(watchdog_conf.nick, str)) {
349 watchdog_conf.nick = str;
351 str = database_get_data(conf_node, "modes", RECDB_QSTRING);
352 watchdog_conf.modes = (str ? str : NULL);
356 watchdog_saxdb_read_shitlist(const char *name, void *data, UNUSED_ARG(void *extra))
358 struct record_data *rd = data;
360 char *triggered, *action;
362 if (rd->type == RECDB_OBJECT) {
363 dict_t obj = GET_RECORD_OBJECT(rd);
364 /* new style structure */
365 badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);
366 triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);
367 action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);
369 add_badword(badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0), name);
375 watchdog_saxdb_read_chanlist(const char *name, void *data, UNUSED_ARG(void *extra))
377 struct record_data *rd = data;
379 if (rd->type == RECDB_OBJECT) {
380 dict_t obj = GET_RECORD_OBJECT(rd);
381 /* nothing in here, yet */
389 watchdog_saxdb_read(struct dict *db)
393 str = database_get_data(db, KEY_BADWORDID, RECDB_QSTRING);
394 last_badword_id = str ? strtoul(str, NULL, 0) : 0;
396 if ((object = database_get_data(db, KEY_BADWORDS, RECDB_OBJECT)))
397 dict_foreach(object, watchdog_saxdb_read_shitlist, NULL);
399 if ((object = database_get_data(db, KEY_CHANNELS, RECDB_OBJECT)))
400 dict_foreach(object, watchdog_saxdb_read_chanlist, NULL);
406 watchdog_saxdb_write(struct saxdb_context *ctx)
411 saxdb_write_int(ctx, KEY_BADWORDID, last_badword_id);
413 if (dict_size(shitlist)) {
414 saxdb_start_record(ctx, KEY_BADWORDS, 1);
415 for (it = dict_first(shitlist); it; it = iter_next(it)) {
416 struct badword *badword = iter_data(it);
417 saxdb_start_record(ctx, iter_key(it), 0);
419 saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask);
420 saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered);
421 saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action);
423 saxdb_end_record(ctx);
425 saxdb_end_record(ctx);
428 if (dict_size(chanlist)) {
429 saxdb_start_record(ctx, KEY_CHANNELS, 1);
430 for (it = dict_first(chanlist); it; it = iter_next(it)) {
431 struct watchdog_channel *wc = iter_data(it);
432 saxdb_start_record(ctx, wc->channel->name, 0);
434 saxdb_end_record(ctx);
436 saxdb_end_record(ctx);
443 watchdog_cleanup(void)
445 dict_delete(shitlist);
446 dict_delete(chanlist);
452 MS_LOG = log_register_type("Watchdog", "file:watchdog.log");
454 /* set up shitlist dict */
455 dict_delete(shitlist);
456 shitlist = dict_new();
457 dict_set_free_data(shitlist, free_shitlist_entry);
458 /* set up chanlist dict */
459 dict_delete(chanlist);
460 chanlist = dict_new();
461 dict_set_free_data(chanlist, free_chanlist_entry);
463 const char *nick, *modes;
464 if((nick = conf_get_data("modules/watchdog/nick", RECDB_QSTRING))) {
465 modes = conf_get_data("modules/watchdog/modes", RECDB_QSTRING);
466 watchdog = AddLocalUser(nick, nick, NULL, "Watchdog Service", modes);
467 watchdog_service = service_register(watchdog);
468 watchdog_service->trigger = ',';
469 reg_allchanmsg_func(watchdog, watchdog_channel_message);
472 conf_register_reload(watchdog_conf_read);
473 reg_exit_func(watchdog_cleanup);
474 saxdb_register("Watchdog", watchdog_saxdb_read, watchdog_saxdb_write);
476 watchdog_module = module_register("Watchdog", MS_LOG, "mod-watchdog.help", NULL);
477 modcmd_register(watchdog_module, "addbad", cmd_addbad, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
478 modcmd_register(watchdog_module, "delbad", cmd_delbad, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
479 modcmd_register(watchdog_module, "editbad", cmd_editbad, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
480 modcmd_register(watchdog_module, "listbad", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
481 modcmd_register(watchdog_module, "register", cmd_register, 2, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
482 modcmd_register(watchdog_module, "unregister", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_CHANNEL, "flags", "+helping", NULL);
483 message_register_table(msgtab);
489 watchdog_finalize(void) {
493 str = "modules/watchdog";
494 if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
495 log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
499 str = database_get_data(conf_node, "nick", RECDB_QSTRING);
500 if (str) watchdog_conf.nick = str;
502 str = database_get_data(conf_node, "modes", RECDB_QSTRING);
503 if (str) watchdog_conf.modes = str;