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