connect Watchdog before loading saved data (channels)
[srvx.git] / src / mod-watchdog.c
1 /* mod-watchdog.c - Watchdog module for srvx
2  * Copyright 2003-2004 Martijn Smit and srvx Development Team
3  *
4  * This file is part of srvx.
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 /* Adds new section to srvx.conf:
22  * "modules" {
23  *     "watchdog" {
24  *         "nick" "Watchdog";
25  *         "modes" "+iok";
26  *     };
27  *  };
28  *
29  */
30
31 #include "chanserv.h"
32 #include "conf.h"
33 #include "modcmd.h"
34 #include "saxdb.h"
35 #include "timeq.h"
36
37 #define KEY_BADWORDS "badwords"
38 #define KEY_BADWORD_MASK "mask"
39 #define KEY_BADWORD_TRIGGERED "count"
40 #define KEY_BADWORD_ACTION "action"
41 #define KEY_CHANNELS "channel"
42
43 static const struct message_entry msgtab[] = {
44     { "WD_REGISTER_SUCCESS", "$b%s$b is now registered with %s." },
45     { "WD_NOT_REGISTERED", "$b%s$b is not registered with %s." },
46     { NULL, NULL }
47 };
48
49 DECLARE_LIST(shitList, struct badword*);
50 DEFINE_LIST(shitList, struct badword*)
51
52 struct badword {
53     char *badword_mask;
54     unsigned int triggered : 29;
55     unsigned int action : 3;
56 };
57
58 struct watchdog_channel {
59     struct chanNode *channel;
60     //struct shitList *shitlist;
61 };
62
63 /* badword.action fields */
64 #define BADACTION_KICK   1
65 #define BADACTION_KILL   2
66 #define BADACTION_GLINE  3
67
68 static struct {
69     const char *nick;
70     const char *modes;
71 } watchdog_conf;
72
73 const char *watchdog_module_deps[] = { NULL };
74 struct userNode *watchdog;
75 static struct module *watchdog_module;
76 static struct service *watchdog_service;
77 static struct shitList shitlist;
78 static dict_t chanlist;
79 static struct log_type *MS_LOG;
80
81
82 static MODCMD_FUNC(cmd_addbad)
83 {
84     //to be continued...
85     return 1;
86 }
87
88 static MODCMD_FUNC(cmd_delbad)
89 {
90     //to be continued...
91     return 1;
92 }
93
94 static MODCMD_FUNC(cmd_editbad)
95 {
96     //to be continued...
97     return 1;
98 }
99
100 static MODCMD_FUNC(cmd_listbad)
101 {
102     //to be continued...
103     return 1;
104 }
105
106 static struct watchdog_channel *add_channel(const char *name);
107
108 static MODCMD_FUNC(cmd_register)
109 {
110     dict_iterator_t it;
111
112     if((argc < 2) || !IsChannelName(argv[1]))
113     {
114         reply("MSG_NOT_CHANNEL_NAME");
115         return 0;
116     }
117
118     if(opserv_bad_channel(argv[1]))
119     {
120         reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
121         return 0;
122     }
123
124     channel = AddChannel(argv[1], now, NULL, NULL);
125
126     for (it = dict_first(chanlist); it; it = iter_next(it)) {
127         struct watchdog_channel *chan = iter_data(it);
128         if(chan->channel == channel) {
129             reply("CSMSG_ALREADY_REGGED", channel->name);
130             return 0;
131         }
132     }
133
134     add_channel(channel->name);
135     reply("WD_REGISTER_SUCCESS", channel->name, watchdog->nick);
136     return 1;
137 }
138
139 static MODCMD_FUNC(cmd_unregister)
140 {
141     struct watchdog_channel *chan = NULL;
142     dict_iterator_t it;
143     
144     for (it = dict_first(chanlist); it; it = iter_next(it)) {
145         chan = iter_data(it);
146         if(chan->channel == channel)
147             break;
148     }
149     
150     if(chan && chan->channel == channel) {
151         //found, unregister it!
152         DelChannelUser(watchdog, channel, "unregistered.", 0);
153         dict_remove(chanlist, channel->name);
154         reply("CSMSG_UNREG_SUCCESS", channel->name);
155         return 1;
156     } else {
157         reply("WD_NOT_REGISTERED", channel->name, watchdog->nick);
158         return 0;
159     }
160
161 }
162
163 static void
164 watchdog_channel_message(struct userNode *user, struct chanNode *chan, const char *text, struct userNode *bot, unsigned int is_notice)
165 {
166     //to be continued...
167 }
168
169 static struct badword*
170 add_badword(char *badword_mask, unsigned int triggered, unsigned int action)
171 {
172     struct badword *badword;
173
174     badword = calloc(1, sizeof(*badword));
175     if (!badword)
176         return NULL;
177
178     badword->badword_mask = strdup(badword_mask);
179     badword->triggered = triggered;
180     badword->action = action;
181     shitList_append(&shitlist, badword);
182     return badword;
183 }
184
185 static void
186 delete_badword(struct badword *badword)
187 {
188     shitList_remove(&shitlist, badword);
189     free(badword->badword_mask);
190     free(badword);
191 }
192
193 static struct watchdog_channel*
194 add_channel(const char *name)
195 {
196     struct watchdog_channel *wc;
197     struct mod_chanmode *change;
198
199     if(!watchdog) //module disabled
200         return NULL;
201
202     wc = calloc(1, sizeof(*wc));
203     if (!wc)
204         return NULL;
205
206     wc->channel = AddChannel(name, now, NULL, NULL);
207     change = mod_chanmode_alloc(1);
208     change->argc = 1;
209     change->args[0].mode = MODE_CHANOP;
210     change->args[0].u.member = AddChannelUser(watchdog, wc->channel);
211     mod_chanmode_announce(watchdog, wc->channel, change);
212         mod_chanmode_free(change);
213     dict_insert(chanlist, wc->channel->name, wc);
214     return wc;
215 }
216
217 static void
218 free_chanlist_entry(void *data)
219 {
220     struct watchdog_channel *wc = data;
221     
222     free(wc);
223 }
224
225 static void
226 watchdog_conf_read(void)
227 {
228     dict_t conf_node;
229     const char *str;
230
231     str = "modules/watchdog";
232     if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
233         log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
234         return;
235     }
236
237     str = database_get_data(conf_node, "nick", RECDB_QSTRING);
238     if(watchdog_conf.nick && strcmp(watchdog_conf.nick, str)) {
239         //nick changed
240     }
241     watchdog_conf.nick = str;
242     
243     str = database_get_data(conf_node, "modes", RECDB_QSTRING);
244     watchdog_conf.modes = (str ? str : NULL);
245 }
246
247 static int
248 watchdog_saxdb_read_shitlist(const char *name, void *data, UNUSED_ARG(void *extra))
249 {
250     struct record_data *rd = data;
251     char *badword;
252     char *triggered, *action;
253
254      if (rd->type == RECDB_OBJECT) {
255         dict_t obj = GET_RECORD_OBJECT(rd);
256         /* new style structure */
257         badword = database_get_data(obj, KEY_BADWORD_MASK, RECDB_QSTRING);
258         triggered = database_get_data(obj, KEY_BADWORD_TRIGGERED, RECDB_QSTRING);
259         action = database_get_data(obj, KEY_BADWORD_ACTION, RECDB_QSTRING);
260
261         add_badword(badword, strtoul(triggered, NULL, 0), strtoul(action, NULL, 0));
262         return 1;
263     } else
264         return 0;
265 }
266
267 static int
268 watchdog_saxdb_read_chanlist(const char *name, void *data, UNUSED_ARG(void *extra))
269 {
270     struct record_data *rd = data;
271
272      if (rd->type == RECDB_OBJECT) {
273         dict_t obj = GET_RECORD_OBJECT(rd);
274         /* nothing in here, yet */
275
276         add_channel(name);
277         return 1;
278     } else
279         return 0;
280 }
281
282 static int
283 watchdog_saxdb_read(struct dict *db)
284 {
285     struct dict *object;
286     if ((object = database_get_data(db, KEY_BADWORDS, RECDB_OBJECT)))
287         dict_foreach(object, watchdog_saxdb_read_shitlist, NULL);
288     if ((object = database_get_data(db, KEY_CHANNELS, RECDB_OBJECT)))
289         dict_foreach(object, watchdog_saxdb_read_chanlist, NULL);
290     return 1;
291 }
292
293 static int
294 watchdog_saxdb_write(struct saxdb_context *ctx)
295 {
296     struct badword *badword;
297     char str[17];
298     unsigned int id = 0, ii;
299     dict_iterator_t it;
300
301     saxdb_start_record(ctx, KEY_BADWORDS, 1);
302     for (ii = 0; ii < shitlist.used; ++ii) {
303         badword = shitlist.list[ii];
304         snprintf(str, sizeof(str), "%x", id++);
305         saxdb_start_record(ctx, str, 0);
306         saxdb_write_string(ctx, KEY_BADWORD_MASK, badword->badword_mask);
307         saxdb_write_int(ctx, KEY_BADWORD_TRIGGERED, badword->triggered);
308         saxdb_write_int(ctx, KEY_BADWORD_ACTION, badword->action);
309         saxdb_end_record(ctx);
310     }
311     saxdb_end_record(ctx);
312
313     if (dict_size(chanlist)) {
314         saxdb_start_record(ctx, KEY_CHANNELS, 1);
315         for (it = dict_first(chanlist); it; it = iter_next(it)) {
316             struct watchdog_channel *wc = iter_data(it);
317             saxdb_start_record(ctx, wc->channel->name, 0);
318             //anything else?
319             saxdb_end_record(ctx);
320         }
321         saxdb_end_record(ctx);
322     }
323     
324     return 0;
325 }
326
327 static void
328 watchdog_cleanup(void)
329 {
330     while (shitlist.used)
331         delete_badword(shitlist.list[0]);
332     shitList_clean(&shitlist);
333     dict_delete(chanlist);
334 }
335
336 int
337 watchdog_init(void)
338 {
339     MS_LOG = log_register_type("Watchdog", "file:watchdog.log");
340     
341     shitList_init(&shitlist);
342     /* set up chanlist dict */
343     dict_delete(chanlist);
344     chanlist = dict_new();
345     dict_set_free_data(chanlist, free_chanlist_entry);
346
347     const char *nick, *modes;
348     if((nick = conf_get_data("modules/watchdog/nick", RECDB_QSTRING))) {
349         modes = conf_get_data("modules/watchdog/modes", RECDB_QSTRING);
350         watchdog = AddLocalUser(nick, nick, NULL, "Watchdog Service", modes);
351         watchdog_service = service_register(watchdog);
352         watchdog_service->trigger = ',';
353         reg_allchanmsg_func(watchdog, watchdog_channel_message);
354     }
355
356     conf_register_reload(watchdog_conf_read);
357     reg_exit_func(watchdog_cleanup);
358     saxdb_register("Watchdog", watchdog_saxdb_read, watchdog_saxdb_write);
359
360     watchdog_module = module_register("Watchdog", MS_LOG, "mod-watchdog.help", NULL);
361     modcmd_register(watchdog_module, "addbad", cmd_addbad, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
362     modcmd_register(watchdog_module, "delbad", cmd_delbad, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
363     modcmd_register(watchdog_module, "editbad", cmd_editbad, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
364     modcmd_register(watchdog_module, "listbad", cmd_listbad, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
365     modcmd_register(watchdog_module, "register", cmd_register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
366     modcmd_register(watchdog_module, "unregister", cmd_unregister, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_CHANNEL, "flags", "+helping", NULL);
367     message_register_table(msgtab);
368
369     return 1;
370 }
371
372 int
373 watchdog_finalize(void) {
374     dict_t conf_node;
375     const char *str;
376
377     str = "modules/watchdog";
378     if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
379         log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
380         return 0;
381     }
382
383     str = database_get_data(conf_node, "nick", RECDB_QSTRING);
384     if (str) watchdog_conf.nick = str;
385     
386     str = database_get_data(conf_node, "modes", RECDB_QSTRING);
387     if (str) watchdog_conf.modes = str;
388     return 1;
389 }