]>
Commit | Line | Data |
---|---|---|
ec311f39 | 1 | /* mod-track.c - User surveillance module |
2 | * Copyright 2002-2004 srvx Development Team | |
3 | * | |
4 | * This file is part of x3. | |
5 | * | |
d0f04f71 | 6 | * x3 is free software; you can redistribute it and/or modify |
ec311f39 | 7 | * it under the terms of the GNU General Public License as published by |
348683aa | 8 | * the Free Software Foundation; either version 3 of the License, or |
ec311f39 | 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 | * "track" { | |
24 | * // What data to show. | |
25 | * "snomask" "nick,join,part,kick,new,del,auth,chanmode,umode"; | |
26 | * // Where to send track messages? | |
27 | * "channel" "#wherever"; | |
28 | * // Which bot? | |
29 | * "bot" "OpServ"; | |
30 | * // Show new users and joins from net joins? (off by default) | |
31 | * "show_bursts" "0"; | |
32 | * }; | |
33 | * }; | |
34 | */ | |
35 | ||
36 | #include "conf.h" | |
37 | #include "chanserv.h" | |
38 | #include "helpfile.h" | |
39 | #include "nickserv.h" | |
40 | #include "modcmd.h" | |
41 | #include "proto.h" | |
42 | #include "dict.h" | |
43 | #include "hash.h" | |
44 | ||
45 | #ifdef HAVE_NETINET_IN_H | |
46 | #include <netinet/in.h> | |
47 | #endif | |
48 | #ifdef HAVE_ARPA_INET_H | |
49 | #include <arpa/inet.h> | |
50 | #endif | |
51 | ||
52 | /* track snomask definitions */ | |
53 | #define TRACK_NICK 0x0001 /* report nickchanges */ | |
54 | #define TRACK_JOIN 0x0002 /* report join/part */ | |
55 | #define TRACK_PART 0x0004 /* report parts */ | |
56 | #define TRACK_KICK 0x0008 /* report kicks */ | |
57 | #define TRACK_NEW 0x0010 /* report new users */ | |
58 | #define TRACK_DEL 0x0020 /* report quits */ | |
59 | #define TRACK_AUTH 0x0040 /* report auths */ | |
60 | #define TRACK_CHANMODE 0x0080 /* report channel modes */ | |
61 | #define TRACK_UMODE 0x0100 /* report user modes */ | |
62 | ||
63 | /* check track status */ | |
64 | #define check_track_nick(x) ((x).snomask & TRACK_NICK) | |
65 | #define check_track_join(x) ((x).snomask & TRACK_JOIN) | |
66 | #define check_track_part(x) ((x).snomask & TRACK_PART) | |
67 | #define check_track_kick(x) ((x).snomask & TRACK_KICK) | |
68 | #define check_track_new(x) ((x).snomask & TRACK_NEW) | |
69 | #define check_track_del(x) ((x).snomask & TRACK_DEL) | |
70 | #define check_track_auth(x) ((x).snomask & TRACK_AUTH) | |
71 | #define check_track_chanmode(x) ((x).snomask & TRACK_CHANMODE) | |
72 | #define check_track_umode(x) ((x).snomask & TRACK_UMODE) | |
73 | ||
74 | /* set track status */ | |
75 | #define set_track_nick(x) ((x).snomask |= TRACK_NICK) | |
6cf5f880 | 76 | #define set_track_join(x) ((x).snomask |= TRACK_JOIN) |
ec311f39 | 77 | #define set_track_part(x) ((x).snomask |= TRACK_PART) |
78 | #define set_track_kick(x) ((x).snomask |= TRACK_KICK) | |
6cf5f880 | 79 | #define set_track_new(x) ((x).snomask |= TRACK_NEW) |
ec311f39 | 80 | #define set_track_del(x) ((x).snomask |= TRACK_DEL) |
81 | #define set_track_auth(x) ((x).snomask |= TRACK_AUTH) | |
82 | #define set_track_chanmode(x) ((x).snomask |= TRACK_CHANMODE) | |
83 | #define set_track_umode(x) ((x).snomask |= TRACK_UMODE) | |
84 | #define set_track_all(x) ((x).snomask |= TRACK_NICK|TRACK_JOIN|TRACK_PART|TRACK_KICK|TRACK_NEW|TRACK_DEL|TRACK_AUTH|TRACK_CHANMODE|TRACK_UMODE) | |
85 | ||
86 | /* clear track status */ | |
87 | #define clear_track_nick(x) ((x).snomask &= ~TRACK_NICK) | |
6cf5f880 | 88 | #define clear_track_join(x) ((x).snomask &= ~TRACK_JOIN) |
ec311f39 | 89 | #define clear_track_part(x) ((x).snomask &= ~TRACK_PART) |
90 | #define clear_track_kick(x) ((x).snomask &= ~TRACK_KICK) | |
6cf5f880 | 91 | #define clear_track_new(x) ((x).snomask &= ~TRACK_NEW) |
ec311f39 | 92 | #define clear_track_del(x) ((x).snomask &= ~TRACK_DEL) |
93 | #define clear_track_auth(x) ((x).snomask &= ~TRACK_AUTH) | |
94 | #define clear_track_chanmode(x) ((x).snomask &= ~TRACK_CHANMODE) | |
95 | #define clear_track_umode(x) ((x).snomask &= ~TRACK_UMODE) | |
96 | #define clear_track_all(x) ((x).snomask &= ~(TRACK_NICK|TRACK_JOIN|TRACK_PART|TRACK_KICK|TRACK_NEW|TRACK_DEL|TRACK_AUTH|TRACK_CHANMODE|TRACK_UMODE)) | |
97 | ||
98 | extern struct modcmd *opserv_define_func(const char *name, modcmd_func_t *func, int min_level, int reqchan, int min_argc); | |
99 | ||
100 | extern time_t now; | |
101 | static struct { | |
102 | struct chanNode *channel; | |
103 | struct userNode *bot; | |
104 | unsigned int snomask; | |
105 | unsigned int show_bursts : 1; | |
106 | unsigned int enabled : 1; | |
107 | } track_cfg; | |
108 | static char timestamp[16]; | |
109 | static dict_t track_db = NULL; | |
110 | ||
111 | const char *track_module_deps[] = { NULL }; | |
112 | ||
113 | static int finalized; | |
114 | int track_finalize(void); | |
115 | ||
116 | #define TRACK(FORMAT, ARGS...) send_channel_message(track_cfg.channel, track_cfg.bot, "%s "FORMAT, timestamp , ## ARGS) | |
117 | #define UPDATE_TIMESTAMP() strftime(timestamp, sizeof(timestamp), "[%H:%M:%S]", localtime(&now)) | |
118 | ||
6cf5f880 AS |
119 | void |
120 | add_track_user(struct userNode *user) | |
121 | { | |
122 | dict_insert(track_db, strdup(user->nick), user); | |
123 | } | |
124 | ||
125 | static void | |
126 | del_track_user(const char *nick) | |
127 | { | |
128 | dict_remove(track_db, nick); | |
129 | } | |
130 | ||
ec311f39 | 131 | static int |
6cf5f880 | 132 | check_track_user(const char *nick) |
ec311f39 | 133 | { |
134 | int found; | |
6cf5f880 | 135 | if(!nick) |
7637f48f | 136 | return 0; |
6cf5f880 | 137 | dict_find(track_db, nick, &found); |
ec311f39 | 138 | return found; |
139 | } | |
140 | ||
141 | static void | |
142 | parse_track_conf(char *line) | |
143 | { | |
144 | char *t = NULL, *s = line; | |
145 | ||
146 | while(s) | |
147 | { | |
148 | if ((t = strchr(s, ','))) | |
149 | *t++ = 0; | |
150 | ||
151 | switch (tolower(s[0])) | |
152 | { | |
153 | case 'a': | |
154 | if(!strcasecmp(s, "auth")) | |
155 | set_track_auth(track_cfg); | |
156 | break; | |
157 | case 'c': | |
158 | if(!strcasecmp(s, "chanmode")) | |
159 | set_track_chanmode(track_cfg); | |
160 | break; | |
161 | case 'd': | |
162 | if(!strcasecmp(s, "del")) | |
163 | set_track_del(track_cfg); | |
164 | break; | |
165 | case 'j': | |
166 | if(!strcasecmp(s, "join")) | |
167 | set_track_join(track_cfg); | |
168 | break; | |
169 | case 'k': | |
170 | if(!strcasecmp(s, "kick")) | |
171 | set_track_kick(track_cfg); | |
172 | break; | |
173 | case 'n': | |
174 | if(!strcasecmp(s, "new")) | |
175 | set_track_new(track_cfg); | |
176 | if(!strcasecmp(s, "nick")) | |
177 | set_track_nick(track_cfg); | |
178 | break; | |
179 | case 'p': | |
180 | if(!strcasecmp(s, "part")) | |
181 | set_track_nick(track_cfg); | |
182 | break; | |
183 | case 'u': | |
184 | if(!strcasecmp(s, "umode")) | |
185 | set_track_umode(track_cfg); | |
186 | break; | |
187 | } | |
188 | s = t; | |
189 | } | |
190 | } | |
191 | ||
192 | static void | |
193 | track_nick_change(struct userNode *user, const char *old_nick) { | |
194 | if (!track_cfg.enabled) return; | |
6cf5f880 AS |
195 | |
196 | if(check_track_user(old_nick)) { | |
197 | del_track_user(old_nick); | |
198 | add_track_user(user); | |
199 | if (check_track_nick(track_cfg)) | |
200 | { | |
201 | UPDATE_TIMESTAMP(); | |
202 | TRACK("$bNICK$b change %s -> %s", old_nick, user->nick); | |
203 | } | |
ec311f39 | 204 | } |
205 | } | |
206 | ||
207 | static int | |
208 | track_join(struct modeNode *mNode) { | |
209 | struct userNode *user = mNode->user; | |
210 | struct chanNode *chan = mNode->channel; | |
211 | if (!track_cfg.enabled) return 0; | |
212 | if (user->uplink->burst && !track_cfg.show_bursts) return 0; | |
6cf5f880 | 213 | if (check_track_join(track_cfg) && check_track_user(user->nick)) |
ec311f39 | 214 | { |
215 | UPDATE_TIMESTAMP(); | |
216 | if (chan->members.used == 1) { | |
217 | TRACK("$bCREATE$b %s by %s", chan->name, user->nick); | |
218 | } else { | |
219 | TRACK("$bJOIN$b %s by %s", chan->name, user->nick); | |
220 | } | |
221 | } | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static void | |
226 | track_part(struct modeNode *mn, const char *reason) { | |
227 | if (!track_cfg.enabled) return; | |
228 | if (mn->user->dead) return; | |
6cf5f880 | 229 | if (check_track_part(track_cfg) && check_track_user(mn->user->nick)) |
ec311f39 | 230 | { |
231 | UPDATE_TIMESTAMP(); | |
232 | TRACK("$bPART$b %s by %s (%s)", mn->channel->name, mn->user->nick, reason ? reason : ""); | |
233 | } | |
234 | } | |
235 | ||
236 | static void | |
237 | track_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *chan) { | |
238 | if (!track_cfg.enabled) return; | |
6cf5f880 | 239 | if (check_track_kick(track_cfg) && ((check_track_user(kicker->nick) || check_track_user(victim->nick)))) |
ec311f39 | 240 | { |
241 | UPDATE_TIMESTAMP(); | |
242 | TRACK("$bKICK$b %s from %s by %s", victim->nick, chan->name, (kicker ? kicker->nick : "some server")); | |
243 | } | |
244 | } | |
245 | ||
246 | static int | |
247 | track_new_user(struct userNode *user) { | |
f1d4a7db | 248 | |
ec311f39 | 249 | if (!track_cfg.enabled) return 0; |
250 | if (user->uplink->burst && !track_cfg.show_bursts) return 0; | |
6cf5f880 | 251 | if (check_track_new(track_cfg) && check_track_user(user->nick)) |
ec311f39 | 252 | { |
253 | UPDATE_TIMESTAMP(); | |
254 | TRACK("$bNICK$b %s %s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name); | |
255 | } | |
256 | return 0; | |
257 | } | |
258 | ||
259 | static void | |
260 | track_del_user(struct userNode *user, struct userNode *killer, const char *why) { | |
261 | if (!track_cfg.enabled) return; | |
6cf5f880 | 262 | if (check_track_del(track_cfg) && (check_track_user(user->nick) || (killer && check_track_user(killer->nick)))) |
ec311f39 | 263 | { |
264 | UPDATE_TIMESTAMP(); | |
265 | if (killer) { | |
266 | TRACK("$bKILL$b %s (%s@%s, on %s) by %s (%s)", user->nick, user->ident, user->hostname, user->uplink->name, killer->nick, why); | |
267 | } else { | |
268 | TRACK("$bQUIT$b %s (%s@%s, on %s) (%s)", user->nick, user->ident, user->hostname, user->uplink->name, why); | |
269 | } | |
6cf5f880 | 270 | del_track_user(user->nick); |
ec311f39 | 271 | } |
272 | } | |
273 | ||
274 | static void | |
275 | track_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) { | |
276 | if (!track_cfg.enabled) return; | |
277 | if (user->uplink->burst && !track_cfg.show_bursts) return; | |
6cf5f880 | 278 | if (user->handle_info && (check_track_auth(track_cfg) && check_track_user(user->nick))) { |
ec311f39 | 279 | UPDATE_TIMESTAMP(); |
280 | TRACK("$bAUTH$b %s!%s@%s [%s] on %s as %s", user->nick, user->ident, user->hostname, | |
281 | irc_ntoa(&user->ip), user->uplink->name, user->handle_info->handle); | |
282 | } | |
283 | } | |
284 | ||
285 | static void | |
286 | track_user_mode(struct userNode *user, const char *mode_change) { | |
287 | if (!track_cfg.enabled) return; | |
288 | if (user->uplink->burst && !track_cfg.show_bursts) return; | |
289 | if (!mode_change[1]) return; /* warning there has to be atleast one char in the buffer */ | |
6cf5f880 | 290 | if(check_track_umode(track_cfg) && check_track_user(user->nick)) |
ec311f39 | 291 | { |
292 | UPDATE_TIMESTAMP(); | |
293 | TRACK("$bUMODE$b %s %s", user->nick, mode_change); | |
294 | } | |
295 | } | |
296 | ||
297 | static void | |
298 | track_oper(struct userNode *user) { | |
299 | if (!track_cfg.enabled) return; | |
300 | if (user->uplink->burst && !track_cfg.show_bursts) return; | |
301 | UPDATE_TIMESTAMP(); | |
302 | TRACK("$bOPER$b %s!%s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name); | |
303 | } | |
304 | ||
305 | static void | |
306 | track_channel_mode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc) | |
307 | { | |
308 | if (!track_cfg.enabled) return; | |
309 | if(who) | |
310 | { | |
311 | if (who->uplink->burst && !track_cfg.show_bursts) return; | |
6cf5f880 | 312 | if (!check_track_chanmode(track_cfg) || !check_track_user(who->nick)) return; |
ec311f39 | 313 | } else |
314 | return; | |
315 | ||
316 | static char targets[MAXLEN], string[MAXLEN]; | |
317 | struct userNode *un = NULL; | |
318 | char *tmp = NULL, *tg = NULL, *md = NULL; | |
319 | int add = 0; | |
320 | ||
321 | string[0] = 0; | |
322 | targets[0] = 0; | |
323 | ||
324 | if (argc > 0) | |
325 | unsplit_string(modes, argc, string); | |
326 | else | |
327 | strcpy(string, *modes); | |
328 | ||
329 | if((tg = strchr(string, ' '))) | |
330 | { | |
331 | *tg++ = 0; | |
332 | for(md = string; *md; md++) | |
333 | { | |
334 | if (*md == '+') | |
335 | { | |
336 | add = 1; | |
337 | md++; | |
338 | } | |
339 | if (*md == '-') | |
340 | { | |
341 | add = 0; | |
342 | md++; | |
343 | } | |
344 | switch(*md) | |
345 | { | |
346 | case 'k': | |
347 | { | |
348 | strcat(targets, " "); | |
349 | if ((tmp = strchr(tg, ' '))) | |
350 | *tmp++ = 0; | |
351 | strcat(targets, tg); | |
352 | if(tmp) | |
353 | tg = tmp; | |
354 | break; | |
355 | } | |
356 | case 'l': | |
357 | { | |
358 | if(add) | |
359 | { | |
360 | strcat(targets, " "); | |
361 | if ((tmp = strchr(tg, ' '))) | |
362 | *tmp++ = 0; | |
363 | strcat(targets, tg); | |
364 | if(tmp) | |
365 | tg = tmp; | |
366 | break; | |
367 | } | |
368 | } | |
369 | case 'b': | |
370 | { | |
371 | strcat(targets, " "); | |
372 | if ((tmp = strchr(tg, ' '))) | |
373 | *tmp++ = 0; | |
374 | strcat(targets, tg); | |
375 | if(tmp) | |
376 | tg = tmp; | |
377 | break; | |
378 | } | |
379 | case 'e': | |
380 | { | |
381 | strcat(targets, " "); | |
382 | if ((tmp = strchr(tg, ' '))) | |
383 | *tmp++ = 0; | |
384 | strcat(targets, tg); | |
385 | if(tmp) | |
386 | tg = tmp; | |
387 | break; | |
388 | } | |
389 | case 'o': | |
390 | { | |
391 | strcat(targets, " "); | |
392 | if ((tmp = strchr(tg, ' '))) | |
393 | *tmp++ = 0; | |
394 | if((un = GetUserN(tg))) | |
395 | strcat(targets, un->nick); | |
396 | else | |
397 | strcat(targets, tg); | |
398 | if(tmp) | |
399 | tg = tmp; | |
400 | break; | |
401 | } | |
402 | case 'v': | |
403 | { | |
404 | strcat(targets, " "); | |
405 | if ((tmp = strchr(tg, ' '))) | |
406 | *tmp++ = 0; | |
407 | if((un = GetUserN(tg))) | |
408 | strcat(targets, un->nick); | |
409 | else | |
410 | strcat(targets, tg); | |
411 | if(tmp) | |
412 | tg = tmp; | |
413 | break; | |
414 | } | |
415 | } | |
416 | } | |
417 | } | |
418 | UPDATE_TIMESTAMP(); | |
419 | if (who) | |
420 | TRACK("$bMODE$b %s %s%s by %s", channel->name, string, targets, who->nick); | |
421 | else | |
422 | TRACK("$bMODE$b %s %s%s", channel->name, string, targets); | |
423 | } | |
424 | ||
425 | static void | |
426 | check_track_state(struct userNode *user) | |
427 | { | |
12673a59 | 428 | send_message_type(4, user, track_cfg.bot, "TRACK is tracking: %s%s%s%s%s%s%s%s%s", |
ec311f39 | 429 | check_track_nick(track_cfg) ? " nick":"", check_track_join(track_cfg) ? " join":"", |
430 | check_track_part(track_cfg) ? " part":"", check_track_kick(track_cfg) ? " kick":"", | |
431 | check_track_new(track_cfg) ? " new":"", check_track_del(track_cfg) ? " del":"", | |
432 | check_track_auth(track_cfg) ? " auth":"", check_track_chanmode(track_cfg) ? " chanmode":"", | |
433 | check_track_umode(track_cfg) ? " umode":""); | |
434 | } | |
435 | ||
436 | MODCMD_FUNC(cmd_track) | |
437 | { | |
438 | unsigned int i, add; | |
439 | const char *data; | |
440 | char changed = false; | |
441 | ||
442 | if(argc == 1) | |
443 | { | |
12673a59 | 444 | svccmd_send_help_brief(user, track_cfg.bot, cmd); |
ec311f39 | 445 | check_track_state(user); |
446 | return 0; | |
447 | } | |
448 | ||
449 | for(i = 1; i < argc; i++) | |
450 | { | |
451 | data = argv[i]; | |
452 | add = 2; | |
453 | changed = true; | |
454 | ||
6cf5f880 | 455 | if(*data == '+') |
ec311f39 | 456 | add = 1; |
6cf5f880 | 457 | if(*data == '-') |
ec311f39 | 458 | add = 0; |
459 | ||
460 | if(add == 2) | |
461 | { | |
e5c85987 | 462 | if ((!strcasecmp(data, "all"))) |
ec311f39 | 463 | { |
464 | set_track_all(track_cfg); | |
465 | check_track_state(user); | |
466 | TRACK("$bALERT$b TRACK fully enabled by %s", user->nick); | |
467 | } | |
e5c85987 | 468 | else if (!strcasecmp(data, "none")) |
ec311f39 | 469 | { |
470 | clear_track_all(track_cfg); | |
471 | check_track_state(user); | |
472 | TRACK("$bALERT$b TRACK disabled by %s", user->nick); | |
473 | } | |
474 | else | |
b34a7505 AS |
475 | { |
476 | send_message_type(4, user, track_cfg.bot, "Unrecognised parameter: %s", data); | |
477 | svccmd_send_help_brief(user, track_cfg.bot, cmd); | |
478 | } | |
ec311f39 | 479 | return 0; |
480 | } | |
481 | ||
6cf5f880 AS |
482 | data++; |
483 | ||
484 | if(!strcasecmp(data, "auth")) { | |
485 | if (add) | |
486 | set_track_auth(track_cfg); | |
487 | else | |
488 | clear_track_auth(track_cfg); | |
489 | } else if(!strcasecmp(data, "chanmode")) { | |
490 | if (add) | |
491 | set_track_chanmode(track_cfg); | |
492 | else | |
493 | clear_track_chanmode(track_cfg); | |
494 | } else if(!strcasecmp(data, "del")) { | |
495 | if (add) | |
496 | set_track_del(track_cfg); | |
497 | else | |
498 | clear_track_del(track_cfg); | |
499 | } else if(!strcasecmp(data, "join")) { | |
500 | if(add) | |
501 | set_track_join(track_cfg); | |
502 | else | |
503 | clear_track_join(track_cfg); | |
504 | } else if(!strcasecmp(data, "kick")) { | |
505 | if(add) | |
506 | set_track_kick(track_cfg); | |
507 | else | |
508 | clear_track_kick(track_cfg); | |
509 | } else if(!strcasecmp(data, "new")) { | |
510 | if(add) | |
511 | set_track_new(track_cfg); | |
512 | else | |
513 | clear_track_new(track_cfg); | |
514 | } else if(!strcasecmp(data, "nick")) { | |
515 | if(add) | |
516 | set_track_nick(track_cfg); | |
517 | else | |
518 | clear_track_nick(track_cfg); | |
519 | } else if(!strcasecmp(data, "part")) { | |
520 | if(add) | |
521 | set_track_part(track_cfg); | |
522 | else | |
523 | clear_track_part(track_cfg); | |
524 | } else if(!strcasecmp(data, "umode")) { | |
525 | if(add) | |
526 | set_track_umode(track_cfg); | |
527 | else | |
528 | clear_track_umode(track_cfg); | |
529 | } else { | |
530 | TRACK("Error, Unknown value %s", data); | |
ec311f39 | 531 | } |
532 | } | |
533 | check_track_state(user); | |
534 | ||
535 | if(changed) | |
536 | { | |
537 | char buf[256]; | |
538 | unsigned int pos = 0; | |
539 | memset(buf, 0, sizeof(buf)); | |
540 | for(i = 1; i < argc; i++) | |
541 | { | |
542 | unsigned int len; | |
543 | data = argv[i]; | |
544 | len = strlen(data); | |
545 | if(pos + len > sizeof(buf)) | |
546 | break; | |
547 | strcpy(&buf[pos], data); | |
548 | pos += len; | |
549 | } | |
550 | ||
551 | UPDATE_TIMESTAMP(); | |
552 | TRACK("$bALERT$b TRACK command called with parameters '%s' by %s", | |
553 | buf, user->nick); | |
554 | } | |
555 | return 0; | |
556 | } | |
557 | ||
558 | MODCMD_FUNC(cmd_deltrack) | |
559 | { | |
560 | struct userNode *un = NULL; | |
561 | ||
562 | if((argc > 1) && (un = dict_find(clients, argv[1], NULL))) | |
563 | { | |
6cf5f880 | 564 | if(check_track_user(un->nick)) |
ec311f39 | 565 | { |
6cf5f880 | 566 | del_track_user(un->nick); |
ec311f39 | 567 | UPDATE_TIMESTAMP(); |
568 | TRACK("$bALERT$b No longer monitoring %s!%s@%s on %s requested by %s", | |
569 | un->nick, un->ident, un->hostname, un->uplink->name, user->nick); | |
570 | } | |
571 | else | |
572 | send_message_type(4, user, track_cfg.bot, "This nick isn't monitored."); | |
573 | } | |
574 | else | |
b34a7505 | 575 | { |
ec311f39 | 576 | send_message_type(4, user, track_cfg.bot, "No nick or invalid nick specified."); |
12673a59 | 577 | svccmd_send_help_brief(user, track_cfg.bot, cmd); |
b34a7505 | 578 | } |
ec311f39 | 579 | return 0; |
580 | } | |
581 | ||
582 | MODCMD_FUNC(cmd_addtrack) | |
583 | { | |
6cf5f880 | 584 | struct userNode *un = NULL; |
ec311f39 | 585 | |
6cf5f880 AS |
586 | if((argc > 1) && (un = dict_find(clients, argv[1], NULL))) |
587 | { | |
588 | add_track_user(un); | |
589 | UPDATE_TIMESTAMP(); | |
590 | TRACK("$bALERT$b Manually enabled monitoring of %s!%s@%s on %s requested by %s", | |
591 | un->nick, un->ident, un->hostname, un->uplink->name, user->nick); | |
b34a7505 | 592 | send_message_type(4, user, track_cfg.bot, "Now tracking %s!%s@%s on %s", un->nick,un->ident,un->hostname, un->uplink->name); |
6cf5f880 AS |
593 | } |
594 | else | |
b34a7505 | 595 | { |
6cf5f880 | 596 | send_message_type(4, user, track_cfg.bot, "No nick or invalid nick specified."); |
12673a59 | 597 | svccmd_send_help_brief(user, track_cfg.bot, cmd); |
b34a7505 | 598 | } |
6cf5f880 | 599 | return 0; |
ec311f39 | 600 | } |
601 | ||
602 | MODCMD_FUNC(cmd_listtrack) | |
603 | { | |
604 | dict_iterator_t it, next; | |
605 | if (track_db == NULL) return 0; | |
606 | struct userNode *un = NULL; | |
607 | send_message_type(4, user, track_cfg.bot, "Currently tracking:"); | |
608 | for (it=dict_first(track_db); it; it=next) { | |
609 | next = iter_next(it); | |
610 | un = it->data; | |
611 | send_message_type(4, user, track_cfg.bot, "%s!%s@%s [%s] on %s", | |
612 | un->nick, un->ident, un->hostname, irc_ntoa(&un->ip), un->uplink->name); | |
613 | } | |
614 | send_message_type(4, user, track_cfg.bot, "End of track list."); | |
615 | return 0; | |
616 | } | |
617 | ||
618 | static void | |
619 | track_conf_read(void) { | |
620 | dict_t node; | |
621 | char *str; | |
622 | ||
623 | node = conf_get_data("modules/track", RECDB_OBJECT); | |
624 | if (!node) | |
625 | return; | |
626 | str = database_get_data(node, "snomask", RECDB_QSTRING); | |
627 | if (!str) | |
628 | track_cfg.snomask = TRACK_NICK|TRACK_KICK|TRACK_JOIN|TRACK_PART|TRACK_CHANMODE|TRACK_NEW|TRACK_DEL|TRACK_AUTH; | |
629 | else | |
630 | parse_track_conf(str); | |
631 | str = database_get_data(node, "channel", RECDB_QSTRING); | |
632 | if (!str) | |
633 | return; | |
b34a7505 AS |
634 | // XXX - dont do addchannel if the channel is being shared with |
635 | // another module: | |
636 | track_cfg.channel = AddChannel(str, now, "+sntOm", NULL, NULL); | |
ec311f39 | 637 | if (!track_cfg.channel) |
638 | return; | |
639 | str = database_get_data(node, "show_bursts", RECDB_QSTRING); | |
640 | track_cfg.show_bursts = str ? enabled_string(str) : 0; | |
641 | track_cfg.enabled = 1; | |
642 | if (finalized) | |
643 | track_finalize(); | |
644 | } | |
645 | ||
646 | void | |
647 | track_cleanup(void) { | |
648 | track_cfg.enabled = 0; | |
649 | unreg_del_user_func(track_del_user); | |
6cf5f880 | 650 | dict_delete(track_db); |
ec311f39 | 651 | } |
652 | ||
653 | int | |
654 | track_init(void) { | |
655 | track_db = dict_new(); | |
6cf5f880 | 656 | dict_set_free_keys(track_db, free); |
ec311f39 | 657 | |
658 | reg_exit_func(track_cleanup); | |
659 | conf_register_reload(track_conf_read); | |
660 | reg_nick_change_func(track_nick_change); | |
661 | reg_join_func(track_join); | |
662 | reg_part_func(track_part); | |
663 | reg_kick_func(track_kick); | |
664 | reg_new_user_func(track_new_user); | |
665 | reg_del_user_func(track_del_user); | |
666 | reg_auth_func(track_auth); | |
667 | reg_channel_mode_func(track_channel_mode); | |
668 | reg_user_mode_func(track_user_mode); | |
669 | reg_oper_func(track_oper); | |
670 | opserv_define_func("TRACK", cmd_track, 800, 0, 0); | |
671 | opserv_define_func("DELTRACK", cmd_deltrack, 800, 0, 0); | |
672 | opserv_define_func("ADDTRACK", cmd_addtrack, 800, 0, 0); | |
673 | opserv_define_func("LISTTRACK", cmd_listtrack, 800, 0, 0); | |
674 | return 1; | |
675 | } | |
676 | ||
677 | int | |
678 | track_finalize(void) { | |
679 | struct mod_chanmode change; | |
680 | dict_t node; | |
681 | char *str; | |
682 | ||
683 | finalized = 1; | |
684 | node = conf_get_data("modules/track", RECDB_OBJECT); | |
685 | if (!node) | |
686 | return 0; | |
687 | str = database_get_data(node, "snomask", RECDB_QSTRING); | |
688 | if (!str) | |
689 | track_cfg.snomask = TRACK_NICK|TRACK_KICK|TRACK_JOIN|TRACK_PART|TRACK_CHANMODE|TRACK_NEW|TRACK_DEL|TRACK_AUTH; | |
690 | else | |
691 | parse_track_conf(str); | |
692 | str = database_get_data(node, "bot", RECDB_QSTRING); | |
693 | if (!str) | |
694 | return 0; | |
695 | track_cfg.bot = GetUserH(str); | |
696 | if (!track_cfg.bot) | |
697 | return 0; | |
698 | mod_chanmode_init(&change); | |
699 | change.argc = 1; | |
700 | change.args[0].mode = MODE_CHANOP; | |
701 | change.args[0].u.member = AddChannelUser(track_cfg.bot, track_cfg.channel); | |
702 | mod_chanmode_announce(track_cfg.bot, track_cfg.channel, &change); | |
703 | return 1; | |
704 | } | |
705 | ||
706 |