]>
Commit | Line | Data |
---|---|---|
1 | /* mod-track.c - User surveillance module | |
2 | * Copyright 2002-2004 srvx Development Team | |
3 | * | |
4 | * This file is part of x3. | |
5 | * | |
6 | * x3 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 3 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 | * "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) | |
76 | #define set_track_join(x) ((x).snomask |= TRACK_JOIN) | |
77 | #define set_track_part(x) ((x).snomask |= TRACK_PART) | |
78 | #define set_track_kick(x) ((x).snomask |= TRACK_KICK) | |
79 | #define set_track_new(x) ((x).snomask |= TRACK_NEW) | |
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) | |
88 | #define clear_track_join(x) ((x).snomask &= ~TRACK_JOIN) | |
89 | #define clear_track_part(x) ((x).snomask &= ~TRACK_PART) | |
90 | #define clear_track_kick(x) ((x).snomask &= ~TRACK_KICK) | |
91 | #define clear_track_new(x) ((x).snomask &= ~TRACK_NEW) | |
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 | ||
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 | ||
131 | static int | |
132 | check_track_user(const char *nick) | |
133 | { | |
134 | int found; | |
135 | if(!nick) | |
136 | return 0; | |
137 | dict_find(track_db, nick, &found); | |
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, UNUSED_ARG(void *extra)) { | |
194 | if (!track_cfg.enabled) return; | |
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 | } | |
204 | } | |
205 | } | |
206 | ||
207 | static int | |
208 | track_join(struct modeNode *mNode, UNUSED_ARG(void *extra)) { | |
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; | |
213 | if (check_track_join(track_cfg) && check_track_user(user->nick)) | |
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, UNUSED_ARG(void *extra)) { | |
227 | if (!track_cfg.enabled) return; | |
228 | if (mn->user->dead) return; | |
229 | if (check_track_part(track_cfg) && check_track_user(mn->user->nick)) | |
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, UNUSED_ARG(void *extra)) { | |
238 | if (!track_cfg.enabled) return; | |
239 | if (check_track_kick(track_cfg) && check_track_user(victim->nick)) | |
240 | { | |
241 | if (kicker) /* net rider kicks dont have a kicker set */ | |
242 | { | |
243 | if (!check_track_user(kicker->nick)) | |
244 | return; | |
245 | } | |
246 | ||
247 | UPDATE_TIMESTAMP(); | |
248 | TRACK("$bKICK$b %s from %s by %s", victim->nick, chan->name, (kicker ? kicker->nick : "some server")); | |
249 | } | |
250 | } | |
251 | ||
252 | static int | |
253 | track_new_user(struct userNode *user, UNUSED_ARG(void *extra)) { | |
254 | ||
255 | if (!track_cfg.enabled) return 0; | |
256 | if (user->uplink->burst && !track_cfg.show_bursts) return 0; | |
257 | if (check_track_new(track_cfg) && check_track_user(user->nick)) | |
258 | { | |
259 | UPDATE_TIMESTAMP(); | |
260 | TRACK("$bNICK$b %s %s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name); | |
261 | } | |
262 | return 0; | |
263 | } | |
264 | ||
265 | static void | |
266 | track_del_user(struct userNode *user, struct userNode *killer, const char *why, UNUSED_ARG(void *extra)) { | |
267 | if (!track_cfg.enabled) return; | |
268 | if (check_track_del(track_cfg) && (check_track_user(user->nick) || (killer && check_track_user(killer->nick)))) | |
269 | { | |
270 | UPDATE_TIMESTAMP(); | |
271 | if (killer) { | |
272 | TRACK("$bKILL$b %s (%s@%s, on %s) by %s (%s)", user->nick, user->ident, user->hostname, user->uplink->name, killer->nick, why); | |
273 | } else { | |
274 | TRACK("$bQUIT$b %s (%s@%s, on %s) (%s)", user->nick, user->ident, user->hostname, user->uplink->name, why); | |
275 | } | |
276 | del_track_user(user->nick); | |
277 | } | |
278 | } | |
279 | ||
280 | static void | |
281 | track_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra)) { | |
282 | if (!track_cfg.enabled) return; | |
283 | if (user->uplink->burst && !track_cfg.show_bursts) return; | |
284 | if (user->handle_info && (check_track_auth(track_cfg) && check_track_user(user->nick))) { | |
285 | UPDATE_TIMESTAMP(); | |
286 | TRACK("$bAUTH$b %s!%s@%s [%s] on %s as %s", user->nick, user->ident, user->hostname, | |
287 | irc_ntoa(&user->ip), user->uplink->name, user->handle_info->handle); | |
288 | } | |
289 | } | |
290 | ||
291 | static void | |
292 | track_user_mode(struct userNode *user, const char *mode_change, UNUSED_ARG(void *extra)) { | |
293 | if (!track_cfg.enabled) return; | |
294 | if (user->uplink->burst && !track_cfg.show_bursts) return; | |
295 | if (!mode_change[1]) return; /* warning there has to be atleast one char in the buffer */ | |
296 | if(check_track_umode(track_cfg) && check_track_user(user->nick)) | |
297 | { | |
298 | UPDATE_TIMESTAMP(); | |
299 | TRACK("$bUMODE$b %s %s", user->nick, mode_change); | |
300 | } | |
301 | } | |
302 | ||
303 | static void | |
304 | track_oper(struct userNode *user, UNUSED_ARG(void *extra)) { | |
305 | if (!track_cfg.enabled) return; | |
306 | if (user->uplink->burst && !track_cfg.show_bursts) return; | |
307 | UPDATE_TIMESTAMP(); | |
308 | TRACK("$bOPER$b %s!%s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name); | |
309 | } | |
310 | ||
311 | static void | |
312 | track_channel_mode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, UNUSED_ARG(void *extra)) | |
313 | { | |
314 | if (!track_cfg.enabled) return; | |
315 | if(who) | |
316 | { | |
317 | if (who->uplink->burst && !track_cfg.show_bursts) return; | |
318 | if (!check_track_chanmode(track_cfg) || !check_track_user(who->nick)) return; | |
319 | } else | |
320 | return; | |
321 | ||
322 | static char targets[MAXLEN], string[MAXLEN]; | |
323 | struct userNode *un = NULL; | |
324 | char *tmp = NULL, *tg = NULL, *md = NULL; | |
325 | int add = 0; | |
326 | ||
327 | string[0] = 0; | |
328 | targets[0] = 0; | |
329 | ||
330 | if (argc > 0) | |
331 | unsplit_string(modes, argc, string); | |
332 | else | |
333 | strcpy(string, *modes); | |
334 | ||
335 | if((tg = strchr(string, ' '))) | |
336 | { | |
337 | *tg++ = 0; | |
338 | for(md = string; *md; md++) | |
339 | { | |
340 | if (*md == '+') | |
341 | { | |
342 | add = 1; | |
343 | md++; | |
344 | } | |
345 | if (*md == '-') | |
346 | { | |
347 | add = 0; | |
348 | md++; | |
349 | } | |
350 | switch(*md) | |
351 | { | |
352 | case 'k': | |
353 | { | |
354 | strcat(targets, " "); | |
355 | if ((tmp = strchr(tg, ' '))) | |
356 | *tmp++ = 0; | |
357 | strcat(targets, tg); | |
358 | if(tmp) | |
359 | tg = tmp; | |
360 | break; | |
361 | } | |
362 | case 'l': | |
363 | { | |
364 | if(add) | |
365 | { | |
366 | strcat(targets, " "); | |
367 | if ((tmp = strchr(tg, ' '))) | |
368 | *tmp++ = 0; | |
369 | strcat(targets, tg); | |
370 | if(tmp) | |
371 | tg = tmp; | |
372 | break; | |
373 | } | |
374 | } | |
375 | case 'b': | |
376 | { | |
377 | strcat(targets, " "); | |
378 | if ((tmp = strchr(tg, ' '))) | |
379 | *tmp++ = 0; | |
380 | strcat(targets, tg); | |
381 | if(tmp) | |
382 | tg = tmp; | |
383 | break; | |
384 | } | |
385 | case 'e': | |
386 | { | |
387 | strcat(targets, " "); | |
388 | if ((tmp = strchr(tg, ' '))) | |
389 | *tmp++ = 0; | |
390 | strcat(targets, tg); | |
391 | if(tmp) | |
392 | tg = tmp; | |
393 | break; | |
394 | } | |
395 | case 'o': | |
396 | { | |
397 | strcat(targets, " "); | |
398 | if ((tmp = strchr(tg, ' '))) | |
399 | *tmp++ = 0; | |
400 | if((un = GetUserN(tg))) | |
401 | strcat(targets, un->nick); | |
402 | else | |
403 | strcat(targets, tg); | |
404 | if(tmp) | |
405 | tg = tmp; | |
406 | break; | |
407 | } | |
408 | case 'v': | |
409 | { | |
410 | strcat(targets, " "); | |
411 | if ((tmp = strchr(tg, ' '))) | |
412 | *tmp++ = 0; | |
413 | if((un = GetUserN(tg))) | |
414 | strcat(targets, un->nick); | |
415 | else | |
416 | strcat(targets, tg); | |
417 | if(tmp) | |
418 | tg = tmp; | |
419 | break; | |
420 | } | |
421 | } | |
422 | } | |
423 | } | |
424 | UPDATE_TIMESTAMP(); | |
425 | if (who) | |
426 | TRACK("$bMODE$b %s %s%s by %s", channel->name, string, targets, who->nick); | |
427 | else | |
428 | TRACK("$bMODE$b %s %s%s", channel->name, string, targets); | |
429 | } | |
430 | ||
431 | static void | |
432 | check_track_state(struct userNode *user) | |
433 | { | |
434 | send_message_type(4, user, track_cfg.bot, "TRACK is tracking: %s%s%s%s%s%s%s%s%s", | |
435 | check_track_nick(track_cfg) ? " nick":"", check_track_join(track_cfg) ? " join":"", | |
436 | check_track_part(track_cfg) ? " part":"", check_track_kick(track_cfg) ? " kick":"", | |
437 | check_track_new(track_cfg) ? " new":"", check_track_del(track_cfg) ? " del":"", | |
438 | check_track_auth(track_cfg) ? " auth":"", check_track_chanmode(track_cfg) ? " chanmode":"", | |
439 | check_track_umode(track_cfg) ? " umode":""); | |
440 | } | |
441 | ||
442 | MODCMD_FUNC(cmd_track) | |
443 | { | |
444 | unsigned int i, add; | |
445 | const char *data; | |
446 | char changed = false; | |
447 | ||
448 | if(argc == 1) | |
449 | { | |
450 | svccmd_send_help_brief(user, track_cfg.bot, cmd); | |
451 | check_track_state(user); | |
452 | return 0; | |
453 | } | |
454 | ||
455 | for(i = 1; i < argc; i++) | |
456 | { | |
457 | data = argv[i]; | |
458 | add = 2; | |
459 | changed = true; | |
460 | ||
461 | if(*data == '+') | |
462 | add = 1; | |
463 | if(*data == '-') | |
464 | add = 0; | |
465 | ||
466 | if(add == 2) | |
467 | { | |
468 | if ((!strcasecmp(data, "all"))) | |
469 | { | |
470 | set_track_all(track_cfg); | |
471 | check_track_state(user); | |
472 | TRACK("$bALERT$b TRACK fully enabled by %s", user->nick); | |
473 | } | |
474 | else if (!strcasecmp(data, "none")) | |
475 | { | |
476 | clear_track_all(track_cfg); | |
477 | check_track_state(user); | |
478 | TRACK("$bALERT$b TRACK disabled by %s", user->nick); | |
479 | } | |
480 | else | |
481 | { | |
482 | send_message_type(4, user, track_cfg.bot, "Unrecognised parameter: %s", data); | |
483 | svccmd_send_help_brief(user, track_cfg.bot, cmd); | |
484 | } | |
485 | return 0; | |
486 | } | |
487 | ||
488 | data++; | |
489 | ||
490 | if(!strcasecmp(data, "auth")) { | |
491 | if (add) | |
492 | set_track_auth(track_cfg); | |
493 | else | |
494 | clear_track_auth(track_cfg); | |
495 | } else if(!strcasecmp(data, "chanmode")) { | |
496 | if (add) | |
497 | set_track_chanmode(track_cfg); | |
498 | else | |
499 | clear_track_chanmode(track_cfg); | |
500 | } else if(!strcasecmp(data, "del")) { | |
501 | if (add) | |
502 | set_track_del(track_cfg); | |
503 | else | |
504 | clear_track_del(track_cfg); | |
505 | } else if(!strcasecmp(data, "join")) { | |
506 | if(add) | |
507 | set_track_join(track_cfg); | |
508 | else | |
509 | clear_track_join(track_cfg); | |
510 | } else if(!strcasecmp(data, "kick")) { | |
511 | if(add) | |
512 | set_track_kick(track_cfg); | |
513 | else | |
514 | clear_track_kick(track_cfg); | |
515 | } else if(!strcasecmp(data, "new")) { | |
516 | if(add) | |
517 | set_track_new(track_cfg); | |
518 | else | |
519 | clear_track_new(track_cfg); | |
520 | } else if(!strcasecmp(data, "nick")) { | |
521 | if(add) | |
522 | set_track_nick(track_cfg); | |
523 | else | |
524 | clear_track_nick(track_cfg); | |
525 | } else if(!strcasecmp(data, "part")) { | |
526 | if(add) | |
527 | set_track_part(track_cfg); | |
528 | else | |
529 | clear_track_part(track_cfg); | |
530 | } else if(!strcasecmp(data, "umode")) { | |
531 | if(add) | |
532 | set_track_umode(track_cfg); | |
533 | else | |
534 | clear_track_umode(track_cfg); | |
535 | } else { | |
536 | TRACK("Error, Unknown value %s", data); | |
537 | } | |
538 | } | |
539 | check_track_state(user); | |
540 | ||
541 | if(changed) | |
542 | { | |
543 | char buf[256]; | |
544 | unsigned int pos = 0; | |
545 | memset(buf, 0, sizeof(buf)); | |
546 | for(i = 1; i < argc; i++) | |
547 | { | |
548 | unsigned int len; | |
549 | data = argv[i]; | |
550 | len = strlen(data); | |
551 | if(pos + len > sizeof(buf)) | |
552 | break; | |
553 | strcpy(&buf[pos], data); | |
554 | pos += len; | |
555 | } | |
556 | ||
557 | UPDATE_TIMESTAMP(); | |
558 | TRACK("$bALERT$b TRACK command called with parameters '%s' by %s", | |
559 | buf, user->nick); | |
560 | } | |
561 | return 0; | |
562 | } | |
563 | ||
564 | MODCMD_FUNC(cmd_deltrack) | |
565 | { | |
566 | struct userNode *un = NULL; | |
567 | ||
568 | if((argc > 1) && (un = dict_find(clients, argv[1], NULL))) | |
569 | { | |
570 | if(check_track_user(un->nick)) | |
571 | { | |
572 | del_track_user(un->nick); | |
573 | UPDATE_TIMESTAMP(); | |
574 | TRACK("$bALERT$b No longer monitoring %s!%s@%s on %s requested by %s", | |
575 | un->nick, un->ident, un->hostname, un->uplink->name, user->nick); | |
576 | } | |
577 | else | |
578 | send_message_type(4, user, track_cfg.bot, "This nick isn't monitored."); | |
579 | } | |
580 | else | |
581 | { | |
582 | send_message_type(4, user, track_cfg.bot, "No nick or invalid nick specified."); | |
583 | svccmd_send_help_brief(user, track_cfg.bot, cmd); | |
584 | } | |
585 | return 0; | |
586 | } | |
587 | ||
588 | MODCMD_FUNC(cmd_addtrack) | |
589 | { | |
590 | struct userNode *un = NULL; | |
591 | ||
592 | if((argc > 1) && (un = dict_find(clients, argv[1], NULL))) | |
593 | { | |
594 | add_track_user(un); | |
595 | UPDATE_TIMESTAMP(); | |
596 | TRACK("$bALERT$b Manually enabled monitoring of %s!%s@%s on %s requested by %s", | |
597 | un->nick, un->ident, un->hostname, un->uplink->name, user->nick); | |
598 | send_message_type(4, user, track_cfg.bot, "Now tracking %s!%s@%s on %s", un->nick,un->ident,un->hostname, un->uplink->name); | |
599 | } | |
600 | else | |
601 | { | |
602 | send_message_type(4, user, track_cfg.bot, "No nick or invalid nick specified."); | |
603 | svccmd_send_help_brief(user, track_cfg.bot, cmd); | |
604 | } | |
605 | return 0; | |
606 | } | |
607 | ||
608 | MODCMD_FUNC(cmd_listtrack) | |
609 | { | |
610 | dict_iterator_t it, next; | |
611 | if (track_db == NULL) return 0; | |
612 | struct userNode *un = NULL; | |
613 | send_message_type(4, user, track_cfg.bot, "Currently tracking:"); | |
614 | for (it=dict_first(track_db); it; it=next) { | |
615 | next = iter_next(it); | |
616 | un = it->data; | |
617 | send_message_type(4, user, track_cfg.bot, "%s!%s@%s [%s] on %s", | |
618 | un->nick, un->ident, un->hostname, irc_ntoa(&un->ip), un->uplink->name); | |
619 | } | |
620 | send_message_type(4, user, track_cfg.bot, "End of track list."); | |
621 | return 0; | |
622 | } | |
623 | ||
624 | static void | |
625 | track_conf_read(void) { | |
626 | dict_t node; | |
627 | char *str, *modes; | |
628 | ||
629 | node = conf_get_data("modules/track", RECDB_OBJECT); | |
630 | if (!node) | |
631 | return; | |
632 | str = database_get_data(node, "snomask", RECDB_QSTRING); | |
633 | if (!str) | |
634 | track_cfg.snomask = TRACK_NICK|TRACK_KICK|TRACK_JOIN|TRACK_PART|TRACK_CHANMODE|TRACK_NEW|TRACK_DEL|TRACK_AUTH; | |
635 | else | |
636 | parse_track_conf(str); | |
637 | str = database_get_data(node, "channel", RECDB_QSTRING); | |
638 | modes = database_get_data(node, "channel_modes", RECDB_QSTRING); | |
639 | if (!str) | |
640 | return; | |
641 | // XXX - dont do addchannel if the channel is being shared with | |
642 | // another module: | |
643 | track_cfg.channel = AddChannel(str, now, (modes ? modes : "+sntOm"), NULL, NULL); | |
644 | if (!track_cfg.channel) | |
645 | return; | |
646 | str = database_get_data(node, "show_bursts", RECDB_QSTRING); | |
647 | track_cfg.show_bursts = str ? enabled_string(str) : 0; | |
648 | track_cfg.enabled = 1; | |
649 | if (finalized) | |
650 | track_finalize(); | |
651 | } | |
652 | ||
653 | void | |
654 | track_cleanup(UNUSED_ARG(void *extra)) { | |
655 | track_cfg.enabled = 0; | |
656 | unreg_del_user_func(track_del_user, NULL); | |
657 | dict_delete(track_db); | |
658 | } | |
659 | ||
660 | int | |
661 | track_init(void) { | |
662 | track_db = dict_new(); | |
663 | dict_set_free_keys(track_db, free); | |
664 | ||
665 | reg_exit_func(track_cleanup, NULL); | |
666 | conf_register_reload(track_conf_read); | |
667 | reg_nick_change_func(track_nick_change, NULL); | |
668 | reg_join_func(track_join, NULL); | |
669 | reg_part_func(track_part, NULL); | |
670 | reg_kick_func(track_kick, NULL); | |
671 | reg_new_user_func(track_new_user, NULL); | |
672 | reg_del_user_func(track_del_user, NULL); | |
673 | reg_auth_func(track_auth, NULL); | |
674 | reg_channel_mode_func(track_channel_mode, NULL); | |
675 | reg_user_mode_func(track_user_mode, NULL); | |
676 | reg_oper_func(track_oper, NULL); | |
677 | opserv_define_func("TRACK", cmd_track, 800, 0, 0); | |
678 | opserv_define_func("DELTRACK", cmd_deltrack, 800, 0, 0); | |
679 | opserv_define_func("ADDTRACK", cmd_addtrack, 800, 0, 0); | |
680 | opserv_define_func("LISTTRACK", cmd_listtrack, 800, 0, 0); | |
681 | return 1; | |
682 | } | |
683 | ||
684 | int | |
685 | track_finalize(void) { | |
686 | struct mod_chanmode change; | |
687 | dict_t node; | |
688 | char *str; | |
689 | ||
690 | finalized = 1; | |
691 | node = conf_get_data("modules/track", RECDB_OBJECT); | |
692 | if (!node) | |
693 | return 0; | |
694 | str = database_get_data(node, "snomask", RECDB_QSTRING); | |
695 | if (!str) | |
696 | track_cfg.snomask = TRACK_NICK|TRACK_KICK|TRACK_JOIN|TRACK_PART|TRACK_CHANMODE|TRACK_NEW|TRACK_DEL|TRACK_AUTH; | |
697 | else | |
698 | parse_track_conf(str); | |
699 | str = database_get_data(node, "bot", RECDB_QSTRING); | |
700 | if (!str) | |
701 | return 0; | |
702 | track_cfg.bot = GetUserH(str); | |
703 | if (!track_cfg.bot) | |
704 | return 0; | |
705 | mod_chanmode_init(&change); | |
706 | change.argc = 1; | |
707 | change.args[0].mode = MODE_CHANOP; | |
708 | change.args[0].u.member = AddChannelUser(track_cfg.bot, track_cfg.channel); | |
709 | mod_chanmode_announce(track_cfg.bot, track_cfg.channel, &change); | |
710 | return 1; | |
711 | } | |
712 | ||
713 |