]> jfr.im git - irc/quakenet/newserv.git/blame - nickwatch/nickwatch.c
CHANSERV: remove accidental sendemail from SETEMAIL command.
[irc/quakenet/newserv.git] / nickwatch / nickwatch.c
CommitLineData
4860501e
GB
1#include <stdarg.h>
2#include <stdio.h>
3#include <string.h>
4#include "../core/schedule.h"
55b3d820
TS
5#include "../lib/irc_string.h"
6#include "../lib/splitline.h"
4860501e
GB
7#include "../control/control.h"
8#include "../newsearch/newsearch.h"
9#include "../newsearch/parser.h"
10
21704023 11#define NW_FORMAT_TIME "%d/%m/%y %H:%M GMT"
55b3d820 12#define NW_DURATION_MAX (60*60*24*7) // 7 days
21704023 13
4860501e
GB
14typedef struct nickwatch {
15 int id;
16
6cd0364c
GB
17 char createdby[64];
18 int hits;
21704023 19 time_t lastactive;
55b3d820 20 time_t expiry;
4860501e
GB
21 char term[512];
22 parsertree *tree;
23
24 struct nickwatch *next;
25} nickwatch;
26
27typedef struct nickwatchevent {
28 char description[128];
59694aa9 29 struct nickwatchevent *next;
4860501e
GB
30} nickwatchevent;
31
32static nickwatch *nickwatches;
33static int nextnickwatch = 1;
59694aa9 34static int nickwatchext;
4860501e
GB
35
36static void nw_dummyreply(nick *np, char *format, ...) { }
4860501e
GB
37static void nw_dummywall(int level, char *format, ...) { }
38
6cd0364c 39static nickwatch *nw_currentwatch;
59694aa9 40static array nw_pendingnicks;
4860501e 41
55b3d820
TS
42static int nw_nickunwatch(int id) {
43 nickwatch **pnext, *nw;
44
45 for (pnext = &nickwatches; *pnext; pnext = &((*pnext)->next)) {
46 nw = *pnext;
47
48 if (nw->id == id) {
49 parse_free(nw->tree);
50 *pnext = nw->next;
51 free(nw);
52 return 0;
53 }
54 }
55
56 return 1;
57}
58
4860501e 59static void nw_printnick(searchCtx *ctx, nick *sender, nick *np) {
b32b3eb1 60 char hostbuf[HOSTLEN+NICKLEN+USERLEN+4], modebuf[34];
59694aa9
GB
61 char events[512];
62 nickwatchevent *nwe = np->exts[nickwatchext];
63 int len;
4860501e 64
6cd0364c 65 nw_currentwatch->hits++;
21704023 66 nw_currentwatch->lastactive = time(NULL);
6cd0364c 67
59694aa9
GB
68 events[0] = '\0';
69 len = 0;
70
71 for (nwe = np->exts[nickwatchext]; nwe; nwe = nwe->next) {
72 if (len > 0)
73 len += snprintf(events + len, sizeof(events) - len, ", ");
74
75 len += snprintf(events + len, sizeof(events) - len, "%s", nwe->description);
76 }
77
b32b3eb1
GB
78 strncpy(modebuf, printflags(np->umodes, umodeflags), sizeof(modebuf));
79
59694aa9 80 controlwall(NO_OPER, NL_HITS, "nickwatch(#%d, %s): %s [%s] (%s) (%s)", nw_currentwatch->id, events, visiblehostmask(np,hostbuf),
b32b3eb1 81 IPtostr(np->ipaddress), modebuf, np->realname->name->content);
4860501e
GB
82}
83
59694aa9 84static void nwe_enqueue(nick *np, const char *format, ...) {
4860501e
GB
85 nickwatchevent *nwe;
86 va_list va;
59694aa9 87 int slot;
4860501e
GB
88
89 nwe = malloc(sizeof(nickwatchevent));
4860501e
GB
90
91 va_start(va, format);
92 vsnprintf(nwe->description, sizeof(nwe->description), format, va);
93 va_end(va);
94
59694aa9
GB
95 nwe->next = np->exts[nickwatchext];
96 np->exts[nickwatchext] = nwe;
4860501e 97
59694aa9
GB
98 slot = array_getfreeslot(&nw_pendingnicks);
99 ((nick **)nw_pendingnicks.content)[slot] = np;
4860501e
GB
100}
101
59694aa9
GB
102static void nwe_clear(nick *np) {
103 nickwatchevent *nwe, *next;
4860501e 104
59694aa9
GB
105 for (nwe = np->exts[nickwatchext]; nwe; nwe = next) {
106 next = nwe->next;
107 free(nwe);
4860501e 108 }
59694aa9
GB
109
110 np->exts[nickwatchext] = NULL;
111}
112
113static void nw_sched_processevents(void *arg) {
55b3d820 114 nickwatch *nw, *next;
45eb43cb
GB
115 int i, slot;
116 unsigned int marker;
59694aa9 117 nick *np;
45eb43cb 118 array nicks;
55b3d820 119 time_t now = time(NULL);
4860501e 120
45eb43cb
GB
121 array_init(&nicks, sizeof(nick *));
122 marker = nextnickmarker();
4860501e 123
59694aa9
GB
124 for (i = 0; i < nw_pendingnicks.cursi; i++) {
125 np = ((nick **)nw_pendingnicks.content)[i];
45eb43cb
GB
126
127 if (!np)
128 continue;
129
130 if (np->marker != marker) {
131 np->marker = marker;
132 slot = array_getfreeslot(&nicks);
133 ((nick **)nicks.content)[slot] = np;
134 }
59694aa9
GB
135 }
136
137 array_free(&nw_pendingnicks);
138 array_init(&nw_pendingnicks, sizeof(nick *));
45eb43cb 139
55b3d820 140 for (nw = nickwatches; nw; nw = next) {
45eb43cb 141 nw_currentwatch = nw;
55b3d820 142 next = nw->next;
45eb43cb 143 ast_nicksearch(nw->tree->root, &nw_dummyreply, mynick, &nw_dummywall, &nw_printnick, NULL, NULL, 10, &nicks);
55b3d820
TS
144 if (nw->expiry && nw->expiry <= now) {
145 controlwall(NO_OPER, NL_HITS, "nickwatch(#%d) by %s expired (%d hits): %s", nw->id, nw->createdby, nw->hits, nw->term);
146 nw_nickunwatch(nw->id);
147 }
45eb43cb
GB
148 }
149
150 for (i = 0; i < nicks.cursi; i++) {
151 np = ((nick **)nicks.content)[i];
152 nwe_clear(np);
153 }
154
155 array_free(&nicks);
4860501e
GB
156}
157
158static void nw_hook_newnick(int hooknum, void *arg) {
159 nick *np = arg;
59694aa9
GB
160 nwe_enqueue(np, "new user");
161}
162
18afb5fb
GB
163static void nw_hook_account(int hooknum, void *arg) {
164 nick *np = arg;
165 nwe_enqueue(np, "logged in with account %s", np->authname);
166}
167
59694aa9
GB
168static void nw_hook_lostnick(int hooknum, void *arg) {
169 nick *np = arg;
170 int i;
171
172 nwe_clear(np);
173
9d938546 174 for (i = 0; i < nw_pendingnicks.cursi; i++)
59694aa9 175 if (((nick **)nw_pendingnicks.content)[i] == np)
9d938546 176 ((nick **)nw_pendingnicks.content)[i] = NULL;
4860501e
GB
177}
178
9a9336db
GB
179static void nw_hook_rename(int hooknum, void *arg) {
180 void **args = arg;
181 nick *np = args[0];
182 char *oldnick = args[1];
59694aa9 183 nwe_enqueue(np, "renamed from %s", oldnick);
9a9336db
GB
184}
185
474d3d62
GB
186static void nw_hook_umodechange(int hooknum, void *arg) {
187 void **args = arg;
188 nick *np = args[0];
189 flag_t oldmodes = (uintptr_t)args[1];
190 char buf[64];
191 strncpy(buf, printflags(np->umodes, umodeflags), sizeof(buf));
59694aa9 192 nwe_enqueue(np, "umodes %s -> %s", printflags(oldmodes, umodeflags), buf);
474d3d62
GB
193}
194
80e32fcd
GB
195static void nw_hook_message(int hooknum, void *arg) {
196 void **args = arg;
197 nick *np = args[0];
198 int isnotice = (uintptr_t)args[2];
59694aa9 199 nwe_enqueue(np, isnotice ? "notice" : "message");
80e32fcd
GB
200}
201
4860501e
GB
202static void nw_hook_joinchannel(int hooknum, void *arg) {
203 void **args = arg;
204 channel *cp = args[0];
205 nick *np = args[1];
59694aa9 206 nwe_enqueue(np, "join channel %s", cp->index->name->content);
4860501e
GB
207}
208
209static int nw_cmd_nickwatch(void *source, int cargc, char **cargv) {
210 nick *sender = source;
211 nickwatch *nw;
212 parsertree *tree;
55b3d820
TS
213 time_t duration = NW_DURATION_MAX;
214 size_t i;
215
216 for (i = 0; i < cargc && cargv[i][0] == '-'; i++) {
217 switch (cargv[i][1]) {
218 case 'd':
219 if (++i == cargc)
220 return CMD_USAGE;
221 duration = durationtolong(cargv[i]);
222 if (!duration || duration > NW_DURATION_MAX) {
223 controlreply(sender, "Invalid duration. Maximum: %s.", longtoduration(NW_DURATION_MAX, 1));
224 return CMD_ERROR;
225 }
226 break;
227 default:
228 return CMD_USAGE;
229 }
230 }
4860501e 231
55b3d820 232 if (i == cargc)
4860501e
GB
233 return CMD_USAGE;
234
55b3d820
TS
235 if (i < (cargc - 1))
236 rejoinline(cargv[i],cargc-i);
237
238 tree = parse_string(reg_nicksearch, cargv[i]);
4860501e 239 if (!tree) {
55b3d820 240 displaystrerror(controlreply, sender, cargv[i]);
4860501e
GB
241 return CMD_ERROR;
242 }
243
244 nw = malloc(sizeof(nickwatch));
245 nw->id = nextnickwatch++;
6cd0364c
GB
246 snprintf(nw->createdby, sizeof(nw->createdby), "#%s", sender->authname);
247 nw->hits = 0;
21704023 248 nw->lastactive = 0;
55b3d820
TS
249 nw->expiry = duration + time(NULL);
250 strncpy(nw->term, cargv[i], sizeof(nw->term));
251 nw->tree = tree;
4860501e
GB
252 nw->next = nickwatches;
253 nickwatches = nw;
254
255 controlreply(sender, "Done.");
256
257 return CMD_OK;
258}
259
260static int nw_cmd_nickunwatch(void *source, int cargc, char **cargv) {
261 nick *sender = source;
4860501e
GB
262 int id;
263
264 if (cargc < 1)
265 return CMD_USAGE;
266
267 id = atoi(cargv[0]);
268
55b3d820
TS
269 if (nw_nickunwatch(id)) {
270 controlreply(sender, "Nickwatch #%d not found.", id);
271 return CMD_ERROR;
4860501e
GB
272 }
273
55b3d820
TS
274 controlreply(sender, "Done.");
275 return CMD_OK;
276}
4860501e 277
55b3d820
TS
278static void nw_formattime(time_t time, char *buf, size_t bufsize) {
279 if (time == 0)
280 strncpy(buf, "(never)", bufsize);
281 else
282 strftime(buf, bufsize, NW_FORMAT_TIME, gmtime(&time));
4860501e
GB
283}
284
55b3d820 285
4860501e
GB
286static int nw_cmd_nickwatches(void *source, int cargc, char **cargv) {
287 nick *sender = source;
288 nickwatch *nw;
55b3d820 289 char timebuf1[20], timebuf2[20];
4860501e 290
55b3d820 291 controlreply(sender, "ID Created By Hits Expires Last active Term");
4860501e 292
21704023 293 for (nw = nickwatches; nw; nw = nw->next) {
55b3d820
TS
294 nw_formattime(nw->expiry, timebuf1, sizeof(timebuf1));
295 nw_formattime(nw->lastactive, timebuf2, sizeof(timebuf2));
296 controlreply(sender, "%-5d %-15s %-7d %-18s %-18s %s", nw->id, nw->createdby, nw->hits, timebuf1, timebuf2, nw->term);
21704023 297 }
4860501e
GB
298
299 controlreply(sender, "--- End of nickwatches.");
300
301 return CMD_OK;
302}
303
304void _init(void) {
59694aa9
GB
305 nickwatchext = registernickext("nickwatch");
306
307 array_init(&nw_pendingnicks, sizeof(nick *));
308
55b3d820 309 registercontrolhelpcmd("nickwatch", NO_OPER, 3, &nw_cmd_nickwatch, "Usage: nickwatch ?-d <duration (e.g. 12h5m)>? <nicksearch term>\nAdds a nickwatch entry.");
4860501e
GB
310 registercontrolhelpcmd("nickunwatch", NO_OPER, 1, &nw_cmd_nickunwatch, "Usage: nickunwatch <#id>\nRemoves a nickwatch entry.");
311 registercontrolhelpcmd("nickwatches", NO_OPER, 0, &nw_cmd_nickwatches, "Usage: nickwatches\nLists nickwatches.");
312
313 registerhook(HOOK_NICK_NEWNICK, &nw_hook_newnick);
18afb5fb 314 registerhook(HOOK_NICK_ACCOUNT, &nw_hook_account);
59694aa9 315 registerhook(HOOK_NICK_LOSTNICK, &nw_hook_lostnick);
9a9336db 316 registerhook(HOOK_NICK_RENAME, &nw_hook_rename);
474d3d62 317 registerhook(HOOK_NICK_MODECHANGE, &nw_hook_umodechange);
80e32fcd 318 registerhook(HOOK_NICK_MESSAGE, &nw_hook_message);
4860501e
GB
319 registerhook(HOOK_CHANNEL_CREATE, &nw_hook_joinchannel);
320 registerhook(HOOK_CHANNEL_JOIN, &nw_hook_joinchannel);
59694aa9
GB
321
322 schedulerecurring(time(NULL) + 5, 0, 1, &nw_sched_processevents, NULL);
4860501e
GB
323}
324
325void _fini(void) {
326 nickwatch *nw, *next;
327
328 deregistercontrolcmd("nickwatch", &nw_cmd_nickwatch);
329 deregistercontrolcmd("nickunwatch", &nw_cmd_nickunwatch);
330 deregistercontrolcmd("nickwatches", &nw_cmd_nickwatches);
331
332 deregisterhook(HOOK_NICK_NEWNICK, &nw_hook_newnick);
18afb5fb 333 deregisterhook(HOOK_NICK_ACCOUNT, &nw_hook_account);
59694aa9 334 deregisterhook(HOOK_NICK_LOSTNICK, &nw_hook_lostnick);
9a9336db 335 deregisterhook(HOOK_NICK_RENAME, &nw_hook_rename);
474d3d62 336 deregisterhook(HOOK_NICK_MODECHANGE, &nw_hook_umodechange);
80e32fcd 337 deregisterhook(HOOK_NICK_MESSAGE, &nw_hook_message);
4860501e
GB
338 deregisterhook(HOOK_CHANNEL_CREATE, &nw_hook_joinchannel);
339 deregisterhook(HOOK_CHANNEL_JOIN, &nw_hook_joinchannel);
340
59694aa9
GB
341 deleteallschedules(&nw_sched_processevents);
342
343 /* Process all pending events */
344 nw_sched_processevents(NULL);
345
346 array_free(&nw_pendingnicks);
347
348 releasenickext(nickwatchext);
349
4860501e
GB
350 for (nw = nickwatches; nw; nw = next) {
351 next = nw->next;
352
353 parse_free(nw->tree);
354 free(nw);
355 }
356}