]> jfr.im git - irc/quakenet/newserv.git/blame - glines/glines_buf.c
BUILD: add require-all build mode
[irc/quakenet/newserv.git] / glines / glines_buf.c
CommitLineData
ce11a200 1#include <stdarg.h>
cb581522
GB
2#include <stdio.h>
3#include <string.h>
31c690b7 4#include <assert.h>
a887af59 5#include "../lib/array.h"
a86fc0c4
GB
6#include "../lib/irc_string.h"
7#include "../irc/irc.h"
cb581522
GB
8#include "../control/control.h"
9#include "../trusts/trusts.h"
10#include "glines.h"
11
ce11a200
GB
12static int nextglinebufid = 1;
13glinebuf *glinebuflog[MAXGLINELOG] = {};
14int glinebuflogoffset = 0;
15
324b4e11
GB
16void glinebufinit(glinebuf *gbuf, int id) {
17 gbuf->id = id;
ce11a200 18 gbuf->comment = NULL;
33e09c2c 19 gbuf->commit = 0;
9f47116c 20 gbuf->amend = 0;
ce11a200 21 gbuf->glines = NULL;
fe262e64 22 gbuf->hitsvalid = 1;
bbb80250
GB
23 gbuf->userhits = 0;
24 gbuf->channelhits = 0;
a887af59 25 array_init(&gbuf->hits, sizeof(sstring *));
cb581522
GB
26}
27
28gline *glinebufadd(glinebuf *gbuf, const char *mask, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
324b4e11 29 gline *gl;
cb581522
GB
30
31 gl = makegline(mask); /* sets up nick,user,host,node and flags */
32
33 if (!gl) {
34 /* couldn't process gline mask */
35 Error("gline", ERR_WARNING, "Tried to add malformed G-Line %s!", mask);
36 return 0;
37 }
38
cb581522
GB
39 gl->creator = getsstring(creator, 255);
40
41 /* it's not unreasonable to assume gline is active, if we're adding a deactivated gline, we can remove this later */
42 gl->flags |= GLINE_ACTIVE;
43
44 gl->reason = getsstring(reason, 255);
45 gl->expire = expire;
46 gl->lastmod = lastmod;
47 gl->lifetime = lifetime;
48
ce11a200
GB
49 gl->next = gbuf->glines;
50 gbuf->glines = gl;
cb581522 51
33e09c2c
GB
52 gbuf->hitsvalid = 0;
53
cb581522
GB
54 return gl;
55}
56
0e1e87e6 57char *glinebufaddbyip(glinebuf *gbuf, const char *user, struct irc_in_addr *ip, unsigned char bits, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
cb581522 58 trusthost *th, *oth;
0e1e87e6 59 static char mask[512];
cb581522
GB
60 unsigned char nodebits;
61
62 nodebits = getnodebits(ip);
63
64 if (!(flags & GLINE_IGNORE_TRUST)) {
65 th = th_getbyhost(ip);
66
67 if (th && (th->group->flags & TRUST_RELIABLE_USERNAME)) { /* Trust with reliable usernames */
a86fc0c4 68 for (oth = th->group->hosts; oth; oth = oth->next)
cb581522 69 glinebufaddbyip(gbuf, user, &oth->ip, oth->bits, flags | GLINE_ALWAYS_USER | GLINE_IGNORE_TRUST, creator, reason, expire, lastmod, lifetime);
a86fc0c4 70
0e1e87e6
TS
71 snprintf(mask, sizeof(mask), "%s@%s/tg%u", user, (bits == 128) ? IPtostr(*ip) : CIDRtostr(*ip, bits), th->group->id);
72 return mask;
cb581522
GB
73 }
74 }
75
76 if (!(flags & GLINE_ALWAYS_USER))
77 user = "*";
78
79 /* Widen gline to match the node mask. */
80 if (nodebits < bits)
81 bits = nodebits;
82
440cd7e6 83 snprintf(mask, sizeof(mask), "%s@%s", user, (bits == 128) ? IPtostr(*ip) : CIDRtostr(*ip, bits));
cb581522
GB
84
85 glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
0e1e87e6 86 return mask;
cb581522
GB
87}
88
0e1e87e6 89char *glinebufaddbynick(glinebuf *gbuf, nick *np, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
cb581522 90 if (flags & GLINE_ALWAYS_NICK) {
0e1e87e6 91 static char mask[512];
cb581522
GB
92 snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
93 glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
0e1e87e6 94 return mask;
cb581522 95 } else {
0e1e87e6 96 return glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
cb581522
GB
97 }
98}
99
accce086 100void glinebufaddbywhowas(glinebuf *gbuf, whowas *ww, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
0495c1d1 101 nick *np = &ww->nick;
0eb4cbd3 102
accce086
GB
103 if (flags & GLINE_ALWAYS_NICK) {
104 char mask[512];
0eb4cbd3 105 snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
accce086
GB
106 glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
107 } else {
fdcb5d66 108 glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
accce086
GB
109 }
110}
111
33e09c2c 112void glinebufcounthits(glinebuf *gbuf, int *users, int *channels) {
cb581522 113 gline *gl;
a887af59 114 int i, hit, slot;
cb581522
GB
115 chanindex *cip;
116 channel *cp;
117 nick *np;
a887af59 118 char uhmask[512];
cb581522 119
33e09c2c
GB
120#if 0 /* Let's just do a new hit check anyway. */
121 if (gbuf->hitsvalid)
122 return;
123#endif
124
bbb80250
GB
125 gbuf->userhits = 0;
126 gbuf->channelhits = 0;
cb581522 127
a887af59
GB
128 for (i = 0; i < gbuf->hits.cursi; i++)
129 freesstring(((sstring **)gbuf->hits.content)[i]);
130
131 array_free(&gbuf->hits);
132 array_init(&gbuf->hits, sizeof(sstring *));
133
bbb80250
GB
134 for (i = 0; i<CHANNELHASHSIZE; i++) {
135 for (cip = chantable[i]; cip; cip = cip->next) {
136 cp = cip->channel;
cb581522 137
bbb80250
GB
138 if (!cp)
139 continue;
cb581522 140
bbb80250 141 hit = 0;
cb581522 142
bbb80250
GB
143 for (gl = gbuf->glines; gl; gl = gl->next) {
144 if (gline_match_channel(gl, cp)) {
145 hit = 1;
146 break;
cb581522 147 }
bbb80250 148 }
cb581522 149
bbb80250 150 if (hit) {
a887af59
GB
151 snprintf(uhmask, sizeof(uhmask), "channel: %s", cip->name->content);
152
bbb80250 153 gbuf->channelhits++;
a887af59
GB
154
155 slot = array_getfreeslot(&gbuf->hits);
156 ((sstring **)gbuf->hits.content)[slot] = getsstring(uhmask, 512);
cb581522
GB
157 }
158 }
159 }
160
bbb80250
GB
161 for (i = 0; i < NICKHASHSIZE; i++) {
162 for (np = nicktable[i]; np; np = np->next) {
163 hit = 0;
cb581522 164
bbb80250
GB
165 for (gl = gbuf->glines; gl; gl = gl->next) {
166 if (gline_match_nick(gl, np)) {
167 hit = 1;
168 break;
cb581522 169 }
bbb80250 170 }
cb581522 171
bbb80250 172 if (hit) {
33e09c2c
GB
173 snprintf(uhmask, sizeof(uhmask), "user: %s!%s@%s%s%s r(%s)", np->nick, np->ident, np->host->name->content,
174 (np->auth) ? "/" : "", (np->auth) ? np->authname : "", np->realname->name->content);
18845894 175
bbb80250 176 gbuf->userhits++;
a887af59
GB
177
178 slot = array_getfreeslot(&gbuf->hits);
179 ((sstring **)gbuf->hits.content)[slot] = getsstring(uhmask, 512);
cb581522
GB
180 }
181 }
182 }
33e09c2c
GB
183
184 gbuf->hitsvalid = 1;
185
bbb80250
GB
186 if (users)
187 *users = gbuf->userhits;
188
189 if (channels)
190 *channels = gbuf->channelhits;
cb581522
GB
191}
192
33e09c2c 193int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int overridelimit) {
18845894
GB
194 gline *gl;
195 int users, channels, good;
a86fc0c4
GB
196 const char *hint;
197
33e09c2c 198 glinebufcounthits(gbuf, &users, &channels);
a86fc0c4 199
18845894
GB
200 if (!overridelimit) {
201 if (channels > MAXUSERGLINECHANNELHITS) {
202 controlreply(spewto, "G-Lines would hit %d channels. Limit is %d. Not setting G-Lines.", channels, MAXUSERGLINECHANNELHITS);
203 return 0;
204 } else if (users > MAXUSERGLINEUSERHITS) {
205 controlreply(spewto, "G-Lines would hit %d users. Limit is %d. Not setting G-Lines.", users, MAXUSERGLINEUSERHITS);
206 return 0;
207 }
208 }
209
210 good = 1;
a86fc0c4 211
18845894
GB
212 if (!overridesanity) {
213 /* Remove glines that fail the sanity check */
ce11a200 214 for (gl = gbuf->glines; gl; gl = gl->next) {
18845894 215 if (!isglinesane(gl, &hint)) {
56af56be
GB
216 controlreply(spewto, "Sanity check failed for G-Line on '%s' - Skipping: %s",
217 glinetostring(gl), hint);
18845894 218 good = 0;
56af56be 219 }
a86fc0c4
GB
220 }
221 }
222
18845894 223 return good;
a86fc0c4
GB
224}
225
56af56be 226void glinebufspew(glinebuf *gbuf, nick *spewto) {
a86fc0c4 227 gline *gl;
a887af59 228 int i;
dda7fd90 229 char timebuf[30], lastmod[30];
33e09c2c
GB
230
231 if (!gbuf->hitsvalid)
232 glinebufcounthits(gbuf, NULL, NULL);
233
234 if (gbuf->id == 0)
235 controlreply(spewto, "Uncommitted G-Line transaction.");
236 else
237 controlreply(spewto, "G-Line transaction ID %d", gbuf->id);
238
239 controlreply(spewto, "Comment: %s", (gbuf->comment) ? gbuf->comment->content : "(no comment)");
240
241 if (gbuf->commit) {
242 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->commit));
243 controlreply(spewto, "Committed at: %s", timebuf);
244 }
245
9f47116c
GB
246 if (gbuf->amend) {
247 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->amend));
eb8f36ca 248 controlreply(spewto, "Amended at: %s", timebuf);
33e09c2c
GB
249 }
250
dda7fd90 251 controlreply(spewto, "Mask Expiry Last modified Creator Reason");
ce11a200 252
324b4e11 253 for (gl = gbuf->glines; gl; gl = gl->next) {
dda7fd90
GB
254 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gl->expire));
255
256 if (gl->lastmod == 0)
257 strncpy(lastmod, "<ulined>", sizeof(lastmod));
258 else
259 strftime(lastmod, sizeof(lastmod), "%d/%m/%y %H:%M:%S", localtime(&gl->lastmod));
260
d7191799 261 controlreply(spewto, "%-40s %-20s %-20s %-25s %s", glinetostring(gl), timebuf, lastmod, gl->creator->content, gl->reason ? gl->reason->content : "");
324b4e11 262 }
a887af59 263
33e09c2c 264 controlreply(spewto, "Hits");
a887af59 265
33e09c2c 266 for (i = 0; i < gbuf->hits.cursi; i++) {
a887af59 267 controlreply(spewto, "%s", ((sstring **)gbuf->hits.content)[i]->content);
33e09c2c
GB
268
269 if (i >= 500) {
270 controlreply(spewto, "More than 500 hits, list truncated.");
271 break;
272 }
273 }
31c690b7
GB
274
275 if (i == 0)
276 controlreply(spewto, "(no hits)");
a86fc0c4
GB
277}
278
324b4e11
GB
279void glinebufmerge(glinebuf *gbuf) {
280 /* TODO: reimplement */
281
282/*
283 if (gbuf->merge) {
284 /-* Check if an existing gline supercedes this mask *-/
285 for (sgl = gbuf->glines; sgl; sgl = sgl->next) {
286 if (gline_match_mask(sgl, gl)) {
287 freegline(gl);
288 return sgl;
289 }
290 }
291
292 /-* Remove older glines this gline matches *-/
293 for (pnext = &gbuf->glines; *pnext; pnext = &((*pnext)->next)) {
294 sgl = *pnext;
295
296 if (gline_match_mask(gl, sgl)) {
297 *pnext = sgl->next;
298 freegline(sgl);
299
300 if (!*pnext)
301 break;
302 }
303 }
304 }
305*/
306}
307
0c925dc0 308int glinebufcommit(glinebuf *gbuf, int propagate) {
cb581522 309 gline *gl, *next, *sgl;
fe262e64 310 int users, channels, id;
cb581522
GB
311
312 /* Sanity check */
33e09c2c 313 glinebufcounthits(gbuf, &users, &channels);
cb581522
GB
314
315 if (propagate && (users > MAXGLINEUSERHITS || channels > MAXGLINECHANNELHITS)) {
4e52c61d 316 controlwall(NO_OPER, NL_GLINES_AUTO, "G-Line buffer would hit %d users/%d channels. Not setting G-Lines.");
0b2e8a55 317 glinebufabort(gbuf);
0c925dc0 318 return 0;
cb581522
GB
319 }
320
31c690b7 321 /* Record the commit time */
324b4e11 322 time(&gbuf->commit);
ce11a200 323
fe262e64 324 id = glinebufwritelog(gbuf, propagate);
ce11a200 325
0c925dc0 326 /* Move glines to the global gline list */
ce11a200 327 for (gl = gbuf->glines; gl; gl = next) {
cb581522
GB
328 next = gl->next;
329
330 sgl = findgline(glinetostring(gl));
331
332 if (sgl) {
333 /* existing gline
334 * in 1.4, can update expire, reason etc
335 * in 1.3 can only activate/deactivate an existing gline */
336
337 if (gl->flags & GLINE_ACTIVE && !(sgl->flags & GLINE_ACTIVE))
338 gline_activate(sgl, 0, 0);
339 else if (!(gl->flags & GLINE_ACTIVE) && sgl->flags & GLINE_ACTIVE)
340 gline_deactivate(sgl, 0, 0);
341
ea0acfb3
GB
342#if SNIRCD_VERSION >= 140
343 sgl->expire = gl->expire;
344
345 if (gl->lifetime > sgl->lifetime)
346 sgl->lifetime = gl->lifetime;
347
348 freesstring(sgl->reason);
349 sgl->reason = getsstring(gl->reason, 512);
350#endif
351
cb581522
GB
352 freegline(gl);
353 gl = sgl;
354 } else {
355 gl->next = glinelist;
356 glinelist = gl;
357 }
358
31c690b7
GB
359 gl->glinebufid = id;
360
fe262e64 361 if (propagate)
cb581522 362 gline_propagate(gl);
ce11a200
GB
363 }
364
324b4e11 365 /* We've moved all glines to the global gline list. Clear glines link in the glinebuf. */
6eba1a37
GB
366 gbuf->glines = NULL;
367
0b2e8a55 368 glinebufabort(gbuf);
0c925dc0
GB
369
370 return id;
cb581522
GB
371}
372
0b2e8a55 373void glinebufabort(glinebuf *gbuf) {
cb581522 374 gline *gl, *next;
a887af59 375 int i;
cb581522 376
ce11a200 377 for (gl = gbuf->glines; gl; gl = next) {
cb581522
GB
378 next = gl->next;
379
380 freegline(gl);
381 }
0b2e8a55
GB
382
383 freesstring(gbuf->comment);
a887af59
GB
384
385 for (i = 0; i < gbuf->hits.cursi; i++)
386 freesstring(((sstring **)gbuf->hits.content)[i]);
387
388 array_free(&gbuf->hits);
0b2e8a55
GB
389}
390
391int glinebufundo(int id) {
392 glinebuf *gbl;
393 gline *gl, *sgl;
394 int i;
395
396 for (i = 0; i < MAXGLINELOG; i++) {
397 gbl = glinebuflog[i];
398
399 if (!gbl || gbl->id != id)
400 continue;
401
402 for (gl = gbl->glines; gl; gl = gl->next) {
403 sgl = findgline(glinetostring(gl));
404
405 if (!sgl)
406 continue;
31c690b7
GB
407
408 sgl->glinebufid = 0;
409
0b2e8a55
GB
410 gline_deactivate(sgl, 0, 1);
411 }
412
413 glinebufabort(gbl);
414 glinebuflog[i] = NULL;
415
416 return 1;
417 }
418
419 return 0;
cb581522 420}
ce11a200
GB
421
422void glinebufcommentf(glinebuf *gbuf, const char *format, ...) {
423 char comment[512];
424 va_list va;
425
426 va_start(va, format);
427 vsnprintf(comment, 511, format, va);
428 comment[511] = '\0';
429 va_end(va);
430
431 gbuf->comment = getsstring(comment, 512);
432}
433
434void glinebufcommentv(glinebuf *gbuf, const char *prefix, int cargc, char **cargv) {
435 char comment[512];
436 int i;
437
438 if (prefix)
439 strncpy(comment, prefix, sizeof(comment));
440 else
441 comment[0] = '\0';
442
443 for (i = 0; i < cargc; i++) {
444 if (comment[0])
445 strncat(comment, " ", sizeof(comment));
446
447 strncat(comment, cargv[i], sizeof(comment));
448 }
449
450 comment[511] = '\0';
451
452 gbuf->comment = getsstring(comment, 512);
453}
fe262e64
GB
454
455int glinebufwritelog(glinebuf *gbuf, int propagating) {
456 int i, slot;
457 gline *gl, *sgl;
458 glinebuf *gbl;
459
460 if (!gbuf->glines)
461 return 0; /* Don't waste log IDs on empty gline buffers */
462
463 gbl = NULL;
464
465 /* Find an existing log buffer with the same id */
466 if (gbuf->id != 0) {
467 for (i = 0; i < MAXGLINELOG; i++) {
468 if (!glinebuflog[i])
469 continue;
470
471 if (glinebuflog[i]->id == gbuf->id) {
472 gbl = glinebuflog[i];
473 gbl->amend = gbuf->commit;
474
475 /* We're going to re-insert this glinebuf, so remove it for now */
476 glinebuflog[i] = NULL;
477
478 break;
479 }
480 }
481 }
482
483 /* Find a recent glinebuf that's a close match */
484 if (!gbl && !propagating) {
485 for (i = 0; i < MAXGLINELOG; i++) {
486 if (!glinebuflog[i])
487 continue;
488
489 if (glinebuflog[i]->commit < getnettime() - 5 && glinebuflog[i]->amend < getnettime() - 5)
490 continue;
491
492 assert(glinebuflog[i]->glines);
493
494 if (strcmp(glinebuflog[i]->glines->creator->content, gbuf->glines->creator->content) != 0)
495 continue;
496
497 gbl = glinebuflog[i];
498 gbl->amend = gbuf->commit;
499
500 /* We're going to re-insert this glinebuf, so remove it for now */
501 glinebuflog[i] = NULL;
502
503 break;
504 }
505 }
506
507 /* Make a new glinebuf for the log */
0bc2d49e 508 if (!gbl) {
fe262e64
GB
509 gbl = malloc(sizeof(glinebuf));
510 glinebufinit(gbl, (gbuf->id == 0) ? nextglinebufid++ : gbuf->id);
511
512 assert(gbl->hitsvalid);
513
514 if (gbuf->comment)
515 glinebufcommentf(gbl, "%s", gbuf->comment->content);
516 else if (!propagating)
ac034e04 517 glinebufcommentf(gbl, "G-Lines set by %s", gbuf->glines->creator->content);
fe262e64
GB
518
519 gbl->commit = gbuf->commit;
520 }
521
522 /* Save a duplicate of the glines in the log buffer */
523 for (gl = gbuf->glines; gl; gl = gl->next) {
524 sgl = glinedup(gl);
525 sgl->next = gbl->glines;
526 gbl->glines = sgl;
527 }
528
529 gbl->userhits += gbuf->userhits;
530 gbl->channelhits += gbuf->channelhits;
531
532 assert(gbuf->userhits + gbuf->channelhits == gbuf->hits.cursi);
533
534 for (i = 0; i < gbuf->hits.cursi; i++) {
535 slot = array_getfreeslot(&gbl->hits);
536 ((sstring **)gbl->hits.content)[slot] = getsstring(((sstring **)gbuf->hits.content)[i]->content, 512);
537 }
538
539 /* Log the transaction */
540 glinebuflogoffset++;
541
542 if (glinebuflogoffset >= MAXGLINELOG)
543 glinebuflogoffset = 0;
544
545 if (glinebuflog[glinebuflogoffset])
546 glinebufabort(glinebuflog[glinebuflogoffset]);
547
ac034e04 548 glinebuflog[glinebuflogoffset] = gbl;
fe262e64
GB
549
550 return gbl->id;
eb8f36ca 551}