]> jfr.im git - irc/quakenet/newserv.git/blob - glines/glines_commands.c
whowas: Use a ring buffer for the whowas records.
[irc/quakenet/newserv.git] / glines / glines_commands.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 #include "../lib/version.h"
5 #include "../control/control.h"
6 #include "../lib/irc_string.h"
7 #include "../lib/splitline.h"
8 #include "../lib/strlfunc.h"
9 #include "../core/nsmalloc.h"
10 #include "../irc/irc.h"
11 #include "../localuser/localuser.h"
12 #include "../localuser/localuserchannel.h"
13 #include "glines.h"
14 #include "../trusts/trusts.h"
15
16 MODULE_VERSION("");
17
18 static void registercommands(int, void *);
19 static void deregistercommands(int, void *);
20
21 static int parse_gline_flags(nick *sender, const char *flagparam, int *overridesanity, int *overridelimit, int *simulate, int *chase, int *coff) {
22 const char *pos;
23
24 *coff = 0;
25 *overridesanity = 0;
26 *overridelimit = 0;
27 *simulate = 0;
28
29 if (chase)
30 *chase = 0;
31
32 if (flagparam[0] == '-') {
33 *coff = 1;
34
35 for (pos = flagparam + 1; *pos; pos++) {
36 switch (*pos) {
37 case 'f':
38 *overridesanity = 1;
39 break;
40 case 'l':
41 *overridelimit = 1;
42 break;
43 case 'S':
44 *simulate = 1;
45 break;
46 case 'c':
47 if (!chase)
48 goto invalid;
49
50 *chase = 1;
51 break;
52 default:
53 goto invalid;
54 }
55 }
56 }
57
58 return 1;
59
60 invalid:
61 controlreply(sender, "Invalid flag specified: %c", *pos);
62 return 0;
63 }
64
65 static int glines_cmdblock(void *source, int cargc, char **cargv) {
66 nick *sender = source;
67 nick *target, *wnp;
68 whowas *ww;
69 int hits, duration, id;
70 int coff, overridesanity, overridelimit, simulate, chase;
71 char *reason;
72 char creator[128];
73 glinebuf gbuf;
74 int ownww;
75
76 if (cargc < 1)
77 return CMD_USAGE;
78
79 if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, &chase, &coff))
80 return CMD_ERROR;
81
82 if (cargc < 3 + coff)
83 return CMD_USAGE;
84
85 duration = durationtolong(cargv[coff + 1]);
86
87 if (duration <= 0) {
88 controlreply(sender, "Invalid duration specified.");
89 return CMD_ERROR;
90 }
91
92 target = getnickbynick(cargv[coff]);
93
94 if (!target) {
95 ww = whowas_chase(cargv[coff], 1800);
96
97 if (!ww) {
98 controlreply(sender, "Sorry, couldn't find that user.");
99 return CMD_ERROR;
100 }
101
102 ownww = 0;
103
104 controlreply(sender, "Found matching whowas record:");
105 controlreply(sender, "%s", whowas_format(ww));
106 } else {
107 ww = whowas_fromnick(target, 1);
108 ownww = 1;
109 }
110
111 rejoinline(cargv[coff + 2], cargc - coff - 2);
112 reason = cargv[coff + 2];
113
114 if (sender->auth)
115 snprintf(creator, sizeof(creator), "#%s", sender->authname);
116 else
117 strncpy(creator, controlid(sender), sizeof(creator));
118
119 glinebufinit(&gbuf, 0);
120 glinebufcommentv(&gbuf, "BLOCK", cargc + coff - 1, cargv);
121 glinebufaddbywhowas(&gbuf, ww, 0, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
122
123 glinebufspew(&gbuf, sender);
124
125 if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
126 glinebufabort(&gbuf);
127 if (ownww)
128 whowas_free(ww);
129 controlreply(sender, "G-Lines failed sanity checks. Not setting G-Lines.");
130 return CMD_ERROR;
131 }
132
133 if (simulate) {
134 glinebufabort(&gbuf);
135 if (ownww)
136 whowas_free(ww);
137 controlreply(sender, "Simulation complete. Not setting G-Lines.");
138 return CMD_ERROR;
139 }
140
141 glinebufcounthits(&gbuf, &hits, NULL);
142 id = glinebufcommit(&gbuf, 1);
143
144 wnp = &ww->nick;
145 controlwall(NO_OPER, NL_GLINES, "%s BLOCK'ed user '%s!%s@%s' for %s with reason '%s' (%d hits)", controlid(sender),
146 wnp->nick, wnp->ident, wnp->host->name->content,
147 longtoduration(duration, 0), reason, hits);
148
149 if (ownww)
150 whowas_free(ww);
151
152 controlreply(sender, "Done. G-Line transaction ID: %d", id);
153
154 return CMD_OK;
155 }
156
157 static int glines_cmdgline(void *source, int cargc, char **cargv) {
158 nick *sender = source;
159 int duration, users, channels, id;
160 char *mask, *reason;
161 char creator[128];
162 int coff, overridesanity, overridelimit, simulate;
163 glinebuf gbuf;
164 #if SNIRCD_VERSION < 140
165 gline *gl;
166 #endif /* SNIRCD_VERSION */
167
168 if (cargc < 1)
169 return CMD_USAGE;
170
171 if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
172 return CMD_ERROR;
173
174 if (cargc < 3 + coff)
175 return CMD_USAGE;
176
177 mask = cargv[coff];
178
179 duration = durationtolong(cargv[coff + 1]);
180
181 if (duration <= 0) {
182 controlreply(sender, "Invalid duration specified.");
183 return CMD_ERROR;
184 }
185
186 rejoinline(cargv[coff + 2], cargc - coff - 2);
187 reason = cargv[coff + 2];
188
189 #if SNIRCD_VERSION < 140
190 gl = findgline(mask);
191
192 if (gl) {
193 /* warn opers that they can't modify this gline */
194 if (gl->flags & GLINE_ACTIVE) {
195 controlreply(sender, "Active G-Line already exists on %s - unable to modify", mask);
196 return CMD_ERROR;
197 }
198
199 controlreply(sender, "Reactivating existing gline on %s", mask);
200 }
201 #endif /* SNIRCD_VERSION */
202
203 if (sender->auth)
204 snprintf(creator, sizeof(creator), "#%s", sender->authname);
205 else
206 strncpy(creator, controlid(sender), sizeof(creator));
207
208 glinebufinit(&gbuf, 0);
209 glinebufcommentv(&gbuf, "GLINE", cargc + coff - 1, cargv);
210
211 if (!glinebufadd(&gbuf, mask, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration)) {
212 controlreply(sender, "Invalid G-Line mask.");
213 return CMD_ERROR;
214 }
215
216 glinebufspew(&gbuf, sender);
217
218 if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
219 glinebufabort(&gbuf);
220 controlreply(sender, "G-Lines failed sanity checks. Not setting G-Lines.");
221 return CMD_ERROR;
222 }
223
224 if (simulate) {
225 glinebufabort(&gbuf);
226 controlreply(sender, "Simulation complete. Not setting G-Lines.");
227 return CMD_ERROR;
228 }
229
230 glinebufcounthits(&gbuf, &users, &channels);
231 id = glinebufcommit(&gbuf, 1);
232
233 controlwall(NO_OPER, NL_GLINES, "%s GLINE'd mask '%s' for %s with reason '%s' (hits %d users/%d channels)",
234 controlid(sender), mask, longtoduration(duration, 0), reason, users, channels);
235
236 controlreply(sender, "Done. G-Line transaction ID: %d", id);
237
238 return CMD_OK;
239 }
240
241 static int glines_cmdsmartgline(void *source, int cargc, char **cargv) {
242 nick *sender = source;
243 char *origmask;
244 char mask[512];
245 char *p, *user, *host;
246 struct irc_in_addr ip;
247 unsigned char bits;
248 int hits, duration;
249 int coff, overridesanity, overridelimit, simulate, id;
250 char *reason;
251 char creator[128];
252 glinebuf gbuf;
253
254 if (cargc < 1)
255 return CMD_USAGE;
256
257 if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
258 return CMD_ERROR;
259
260 if (cargc < 3 + coff)
261 return CMD_USAGE;
262
263 origmask = cargv[coff];
264
265 if (origmask[0] == '#' || origmask[0] == '&' || origmask[0] == '$') {
266 controlreply(sender, "Please use \"gline\" for badchan or realname glines.");
267 return CMD_ERROR;
268 }
269
270 duration = durationtolong(cargv[coff + 1]);
271
272 if (duration <= 0) {
273 controlreply(sender, "Invalid duration specified.");
274 return CMD_ERROR;
275 }
276
277 rejoinline(cargv[coff + 2], cargc - coff - 2);
278 reason = cargv[coff + 2];
279
280 strncpy(mask, origmask, sizeof(mask));
281
282 if (strchr(mask, '!')) {
283 controlreply(sender, "Use \"gline\" to place nick glines.");
284 return CMD_ERROR;
285 }
286
287 p = strchr(mask, '@');
288
289 if (!p) {
290 controlreply(sender, "Mask must contain a username (e.g. user@ip).");
291 return CMD_ERROR;
292 }
293
294 user = mask;
295 host = p + 1;
296 *p = '\0';
297
298 if (strchr(user, '*') || strchr(user, '?')) {
299 controlreply(sender, "Usernames may not contain wildcards.");
300 return CMD_ERROR;
301 }
302
303 if (!ipmask_parse(host, &ip, &bits)) {
304 controlreply(sender, "Invalid CIDR mask.");
305 return CMD_ERROR;
306 }
307
308 if (sender->auth)
309 snprintf(creator, sizeof(creator), "#%s", sender->authname);
310 else
311 strncpy(creator, controlid(sender), sizeof(creator));
312
313 glinebufinit(&gbuf, 0);
314 glinebufcommentv(&gbuf, "SMARTGLINE", cargc + coff - 1, cargv);
315 glinebufaddbyip(&gbuf, user, &ip, 128, 0, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
316
317 glinebufspew(&gbuf, sender);
318
319 if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
320 glinebufabort(&gbuf);
321 controlreply(sender, "G-Lines failed sanity checks. Not setting G-Lines.");
322 return CMD_ERROR;
323 }
324
325 if (simulate) {
326 glinebufabort(&gbuf);
327 controlreply(sender, "Simulation complete. Not setting G-Lines.");
328 return CMD_ERROR;
329 }
330
331 glinebufcounthits(&gbuf, &hits, NULL);
332 id = glinebufcommit(&gbuf, 1);
333
334 controlwall(NO_OPER, NL_GLINES, "%s SMARTGLINE'd mask '%s' for %s with reason '%s' (%d hits)",
335 controlid(sender), cargv[0], longtoduration(duration, 0), reason, hits);
336
337 controlreply(sender, "Done. G-Line transaction ID: %d", id);
338
339 return CMD_OK;
340 }
341
342 static int glines_cmdungline(void *source, int cargc, char **cargv) {
343 nick *sender = source;
344 gline *gl;
345
346 if (cargc < 1)
347 return CMD_USAGE;
348
349 gl = findgline(cargv[0]);
350
351 if (!gl) {
352 controlreply(sender, "No such G-Line.");
353 return CMD_ERROR;
354 }
355
356 if (!(gl->flags & GLINE_ACTIVE)) {
357 controlreply(sender, "G-Line was already deactivated.");
358 return CMD_ERROR;
359 }
360
361 gline_deactivate(gl, 0, 1);
362
363 controlwall(NO_OPER, NL_GLINES, "%s UNGLINE'd mask '%s'", controlid(sender), cargv[0]);
364
365 controlreply(sender, "G-Line deactivated.");
366
367 return CMD_OK;
368 }
369
370 static int glines_cmddestroygline(void *source, int cargc, char **cargv) {
371 nick *sender = source;
372 gline *gl;
373
374 if (cargc < 1)
375 return CMD_USAGE;
376
377 gl = findgline(cargv[0]);
378
379 if (!gl) {
380 controlreply(sender, "No such G-Line.");
381 return CMD_ERROR;
382 }
383
384 gline_destroy(gl, 0, 1);
385
386 controlwall(NO_OPER, NL_GLINES, "%s DESTROYGLINE'd mask '%s'", controlid(sender), cargv[0]);
387
388 controlreply(sender, "G-Line destroyed.");
389
390 return CMD_OK;
391 }
392
393 static int glines_cmdclearchan(void *source, int cargc, char **cargv) {
394 nick *sender = source;
395 channel *cp;
396 nick *np;
397 char *reason = "Clearing channel.";
398 int mode, duration, i, slot, hits, id;
399 int coff, overridesanity, overridelimit, simulate;
400 array victims;
401 char creator[128];
402 glinebuf gbuf;
403
404 if (cargc < 1)
405 return CMD_USAGE;
406
407 if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
408 return CMD_ERROR;
409
410 if (cargc < 2 + coff)
411 return CMD_USAGE;
412
413 cp = findchannel(cargv[coff]);
414
415 if (!cp) {
416 controlreply(sender, "Couldn't find that channel.");
417 return CMD_ERROR;
418 }
419
420 if (strcmp(cargv[coff + 1], "kick") == 0)
421 mode = 0;
422 else if (strcmp(cargv[coff + 1], "kill") == 0)
423 mode = 1;
424 else if (strcmp(cargv[coff + 1], "gline") == 0)
425 mode = 2;
426 else if (strcmp(cargv[coff + 1], "glineall") == 0)
427 mode = 3;
428 else
429 return CMD_USAGE;
430
431 if (mode == 0 || mode == 1) {
432 if (cargc >= 3) {
433 rejoinline(cargv[coff + 2], cargc - coff - 2);
434 reason = cargv[coff + 2];
435 }
436 } else {
437 if (cargc < 3 + coff)
438 return CMD_USAGE;
439
440 duration = durationtolong(cargv[coff + 2]);
441
442 if (duration <= 0) {
443 controlreply(sender, "Invalid duration specified.");
444 return CMD_ERROR;
445 }
446
447 if (cargc >= 4 + coff) {
448 rejoinline(cargv[coff + 3], cargc - coff - 3);
449 reason = cargv[coff + 3];
450 }
451 }
452
453 array_init(&victims, sizeof(nick *));
454
455 /* we need to make a list of the channel users here because
456 * kicking/killing them will affect their position in the channel's
457 * user list. */
458 for (i = 0; i < cp->users->hashsize; i++) {
459 if (cp->users->content[i] == nouser)
460 continue;
461
462 np = getnickbynumeric(cp->users->content[i]);
463
464 if (!np)
465 continue;
466
467 if (IsService(np) || IsOper(np) || NickOnServiceServer(np))
468 continue;
469
470 slot = array_getfreeslot(&victims);
471 (((nick **)victims.content)[slot]) = np;
472 }
473
474 if (sender->auth)
475 snprintf(creator, sizeof(creator), "#%s", sender->authname);
476 else
477 strncpy(creator, controlid(sender), sizeof(creator));
478
479 glinebufinit(&gbuf, 0);
480 glinebufcommentv(&gbuf, "CLEARCHAN", cargc + coff - 1, cargv);
481
482 for (i = 0; i < victims.cursi; i++) {
483 np = ((nick **)victims.content)[i];
484
485 switch (mode) {
486 case 0:
487 if (simulate)
488 controlreply(sender, "user: %s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
489 else
490 localkickuser(NULL, cp, np, reason);
491
492 break;
493 case 1:
494 if (simulate)
495 controlreply(sender, "user: %s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
496 else
497 killuser(NULL, np, "%s", reason);
498
499 break;
500 case 2:
501 if (IsAccount(np))
502 break;
503 /* fall through */
504 case 3:
505 glinebufaddbynick(&gbuf, np, 0, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
506 break;
507 default:
508 assert(0);
509 }
510 }
511
512 if (mode != 0 && mode != 1) {
513 glinebufspew(&gbuf, sender);
514
515 if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
516 glinebufabort(&gbuf);
517 controlreply(sender, "G-Line failed sanity checks. Not setting G-Line.");
518 return CMD_ERROR;
519 }
520 }
521
522 if (simulate) {
523 glinebufabort(&gbuf);
524 controlreply(sender, "Simulation complete. Not clearing channel.");
525 return CMD_ERROR;
526 }
527
528 glinebufmerge(&gbuf);
529 glinebufcounthits(&gbuf, &hits, NULL);
530 id = glinebufcommit(&gbuf, 1);
531
532 array_free(&victims);
533
534 if (mode == 0 || mode == 1) {
535 controlwall(NO_OPER, NL_GLINES, "%s CLEARCHAN'd channel '%s' with mode '%s' and reason '%s'",
536 controlid(sender), cp->index->name->content, cargv[1], reason);
537 controlreply(sender, "Done.");
538 } else {
539 controlwall(NO_OPER, NL_GLINES, "%s CLEARCHAN'd channel '%s' with mode '%s', duration %s and reason '%s' (%d hits)",
540 controlid(sender), cp->index->name->content, cargv[1], longtoduration(duration, 0), reason, hits);
541 controlreply(sender, "Done. G-Line transaction ID: %d", id);
542 }
543
544 return CMD_OK;
545 }
546
547 static int glines_cmdtrustgline(void *source, int cargc, char **cargv) {
548 nick *sender = source;
549 trustgroup *tg;
550 trusthost *th;
551 int duration, hits;
552 int coff, overridesanity, overridelimit, simulate, id;
553 char *reason;
554 char mask[512];
555 char creator[128];
556 glinebuf gbuf;
557
558 if (cargc < 1)
559 return CMD_USAGE;
560
561 if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
562 return CMD_ERROR;
563
564 if (cargc < 4 + coff)
565 return CMD_USAGE;
566
567 tg = tg_strtotg(cargv[coff]);
568
569 if (!(tg->flags & TRUST_RELIABLE_USERNAME)) {
570 controlreply(sender, "Sorry, that trust group does not have the \"reliable username\" flag.");
571 return CMD_ERROR;
572 }
573
574 duration = durationtolong(cargv[coff + 2]);
575
576 if (duration <= 0) {
577 controlreply(sender, "Invalid duration specified.");
578 return CMD_ERROR;
579 }
580
581 rejoinline(cargv[coff + 3], cargc - coff - 3);
582 reason = cargv[coff + 3];
583
584 if (sender->auth)
585 snprintf(creator, sizeof(creator), "#%s", sender->authname);
586 else
587 strncpy(creator, controlid(sender), sizeof(creator));
588
589 glinebufinit(&gbuf, 0);
590 glinebufcommentv(&gbuf, "TRUSTGLINE", cargc + coff - 1, cargv);
591
592 for(th = tg->hosts; th; th = th->next) {
593 snprintf(mask, sizeof(mask), "*!%s@%s", cargv[1], CIDRtostr(th->ip, th->bits));
594 glinebufadd(&gbuf, mask, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
595 }
596
597 glinebufspew(&gbuf, sender);
598
599 if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
600 glinebufabort(&gbuf);
601 controlreply(sender, "G-Line failed sanity checks. Not setting G-Line.");
602 return CMD_ERROR;
603 }
604
605 if (simulate) {
606 glinebufabort(&gbuf);
607 controlreply(sender, "Simulation complete. Not setting G-Lines.");
608 return CMD_ERROR;
609 }
610
611 glinebufcounthits(&gbuf, &hits, NULL);
612 id = glinebufcommit(&gbuf, 1);
613
614 controlwall(NO_OPER, NL_GLINES, "%s TRUSTGLINE'd user '%s' on trust group '%s' for %s with reason '%s' (%d hits)",
615 controlid(sender), cargv[1], tg->name->content, longtoduration(duration, 0), reason, hits);
616
617 controlreply(sender, "Done. G-Line transaction ID: %d", id);
618
619 return CMD_OK;
620 }
621
622 static int glines_cmdtrustungline(void *source, int cargc, char **cargv) {
623 nick *sender = source;
624 trustgroup *tg;
625 trusthost *th;
626 char mask[512];
627 gline *gl;
628 int count;
629
630 if (cargc < 2)
631 return CMD_USAGE;
632
633 tg = tg_strtotg(cargv[0]);
634
635 if (!(tg->flags & TRUST_RELIABLE_USERNAME)) {
636 controlreply(sender, "Sorry, that trust group does not have the \"reliable username\" flag.");
637 return CMD_ERROR;
638 }
639
640 count = 0;
641
642 for (th = tg->hosts; th; th = th->next) {
643 snprintf(mask, sizeof(mask), "*!%s@%s", cargv[1], CIDRtostr(th->ip, th->bits));
644
645 gl = findgline(mask);
646
647 if (gl && (gl->flags & GLINE_ACTIVE)) {
648 gline_deactivate(gl, 0, 1);
649 count++;
650 }
651 }
652
653 controlwall(NO_OPER, NL_GLINES, "%s TRUSTUNGLINE'd user '%s' on trust group '%s' (%d G-Lines deactivated)",
654 controlid(sender), cargv[1], tg->name->content, count);
655
656 controlreply(sender, "Done.");
657
658 return CMD_OK;
659 }
660
661 static int glines_cmdglstats(void *source, int cargc, char **cargv) {
662 nick *sender = (nick*)source;
663 gline *gl, *next;
664 time_t curtime = getnettime();
665 int glinecount = 0, hostglinecount = 0, ipglinecount = 0, badchancount = 0, rnglinecount = 0;
666 int deactivecount = 0, activecount = 0;
667
668 for (gl = glinelist; gl; gl = next) {
669 next = gl->next;
670
671 if (gl->lifetime <= curtime) {
672 removegline(gl);
673 continue;
674 } else if (gl->expire <= curtime) {
675 gl->flags &= ~GLINE_ACTIVE;
676 }
677
678 if (gl->flags & GLINE_ACTIVE) {
679 activecount++;
680 } else {
681 deactivecount++;
682 }
683
684 if (gl->flags & GLINE_IPMASK)
685 ipglinecount++;
686 else if (gl->flags & GLINE_HOSTMASK)
687 hostglinecount++;
688 else if (gl->flags & GLINE_REALNAME)
689 rnglinecount++;
690 else if (gl->flags & GLINE_BADCHAN)
691 badchancount++;
692 glinecount++;
693 }
694
695 controlreply(sender, "Total G-Lines set: %d", glinecount);
696 controlreply(sender, "Hostmask G-Lines: %d", hostglinecount);
697 controlreply(sender, "IPMask G-Lines: %d", ipglinecount);
698 controlreply(sender, "Channel G-Lines: %d", badchancount);
699 controlreply(sender, "Realname G-Lines: %d", rnglinecount);
700
701 controlreply(sender, "Active G-Lines: %d", activecount);
702 controlreply(sender, "Inactive G-Lines: %d", deactivecount);
703
704 /* TODO show top 10 creators here */
705 /* TODO show unique creators count */
706 /* TODO show glines per create %8.1f", ccount?((float)gcount/(float)ccount):0 */
707 return CMD_OK;
708 }
709
710 static int glines_cmdglist(void *source, int cargc, char **cargv) {
711 nick *sender = (nick *)source;
712 gline *gl, *next;
713 time_t curtime = time(NULL);
714 int flags = 0;
715 char *mask;
716 int count = 0;
717 int limit = 500;
718 char expirestr[250], idstr[250];
719
720 if (cargc < 1 || (cargc == 1 && cargv[0][0] == '-')) {
721 controlreply(sender, "Syntax: glist [-flags] <mask>");
722 controlreply(sender, "Valid flags are:");
723 controlreply(sender, "-c: Count G-Lines.");
724 controlreply(sender, "-f: Find G-Lines active on <mask>.");
725 controlreply(sender, "-x: Find G-Lines matching <mask> exactly.");
726 controlreply(sender, "-R: Find G-lines on realnames.");
727 controlreply(sender, "-o: Search for glines by owner.");
728 controlreply(sender, "-r: Search for glines by reason.");
729 controlreply(sender, "-i: Include inactive glines.");
730 return CMD_ERROR;
731 }
732
733 if (cargc > 1) {
734 char* ch = cargv[0];
735
736 for (; *ch; ch++)
737 switch (*ch) {
738 case '-':
739 break;
740
741 case 'c':
742 flags |= GLIST_COUNT;
743 break;
744
745 case 'f':
746 flags |= GLIST_FIND;
747 break;
748
749 case 'x':
750 flags |= GLIST_EXACT;
751 break;
752
753 case 'r':
754 flags |= GLIST_REASON;
755 break;
756 case 'o':
757 flags |= GLIST_OWNER;
758 break;
759
760 case 'R':
761 flags |= GLIST_REALNAME;
762 break;
763
764 case 'i':
765 flags |= GLIST_INACTIVE;
766 break;
767
768 default:
769 controlreply(sender, "Invalid flag '%c'.", *ch);
770 return CMD_ERROR;
771 }
772
773 mask = cargv[1];
774 } else {
775 mask = cargv[0];
776 }
777
778 if ((flags & (GLIST_EXACT|GLIST_FIND)) == (GLIST_EXACT|GLIST_FIND)) {
779 controlreply(sender, "You cannot use -x and -f flags together.");
780 return CMD_ERROR;
781 }
782
783 if (!(flags & GLIST_COUNT))
784 controlreply(sender, "%-50s %-19s %-15s %-25s %s", "Mask:", "Expires in:", "Transaction ID:", "Creator:", "Reason:");
785
786 gline *searchgl = makegline(mask);
787
788 for (gl = glinelist; gl; gl = next) {
789 next = gl->next;
790
791 if (gl->lifetime <= curtime) {
792 removegline(gl);
793 continue;
794 } else if (gl->expire <= curtime) {
795 gl->flags &= ~GLINE_ACTIVE;
796 }
797
798 if (!(gl->flags & GLINE_ACTIVE)) {
799 if (!(flags & GLIST_INACTIVE)) {
800 continue;
801 }
802 }
803
804 if (flags & GLIST_REALNAME) {
805 if (!(gl->flags & GLINE_REALNAME))
806 continue;
807 if (flags & GLIST_EXACT) {
808 if (!glineequal(searchgl, gl))
809 continue;
810 } else if (flags & GLIST_FIND) {
811 if (!gline_match_mask(gl, searchgl))
812 continue;
813 } else {
814 if (!match2strings(mask, glinetostring(gl)))
815 continue;
816 }
817 } else {
818 if (gl->flags & GLINE_REALNAME)
819 continue;
820
821 if (flags & GLIST_REASON) {
822 if (flags & GLIST_EXACT) {
823 if (!gl->reason || ircd_strcmp(mask, gl->reason->content) != 0)
824 continue;
825 } else if (flags & GLIST_FIND) {
826 if (!gl->reason || !match2strings(gl->reason->content, mask))
827 continue;
828 } else if (!gl->reason || !match2strings(mask, gl->reason->content))
829 continue;
830 } else if (flags & GLIST_OWNER) {
831 if (flags & GLIST_EXACT) {
832 if (!gl->creator || ircd_strcmp(mask, gl->creator->content) != 0)
833 continue;
834 } else if (flags & GLIST_FIND) {
835 if (!gl->creator || !match2strings(gl->creator->content, mask))
836 continue;
837 } else if (!gl->creator || !match2strings(mask, gl->creator->content))
838 continue;
839 } else {
840 if (flags & GLIST_EXACT) {
841 if (!glineequal(searchgl, gl))
842 continue;
843 } else if (flags & GLIST_FIND) {
844 if (!gline_match_mask(gl, searchgl))
845 continue;
846 } else {
847 if (!match2strings(mask, glinetostring(gl)))
848 continue;
849 }
850 }
851 }
852
853 if (count == limit && !(flags & GLIST_COUNT))
854 controlreply(sender, "More than %d matches, list truncated.", limit);
855
856 count++;
857
858 if (!(flags & GLIST_COUNT) && count < limit) {
859 snprintf(expirestr, sizeof(expirestr), "%s", glinetostring(gl));
860 snprintf(idstr, sizeof(idstr), "%d", gl->glinebufid);
861 controlreply(sender, "%s%-49s %-19s %-15s %-25s %s",
862 (gl->flags & GLINE_ACTIVE) ? "+" : "-",
863 expirestr,
864 (gl->flags & GLINE_ACTIVE) ? (char*)longtoduration(gl->expire - curtime, 0) : "<inactive>",
865 gl->glinebufid ? idstr : "",
866 gl->creator ? gl->creator->content : "",
867 gl->reason ? gl->reason->content : "");
868 }
869 }
870
871 controlreply(sender, "%s%d G-Line%s found.", (flags & GLIST_COUNT) ? "" : "End of list - ", count, count == 1 ? "" : "s");
872
873 return CMD_OK;
874 }
875
876 static int glines_cmdglinelog(void *source, int cargc, char **cargv) {
877 nick *sender = source;
878 glinebuf *gbl;
879 gline *gl;
880 int i, id, count;
881 char timebuf[30];
882
883 id = 0;
884
885 if (cargc > 0) {
886 id = atoi(cargv[0]);
887
888 if (id == 0) {
889 controlreply(sender, "Invalid log ID.");
890 return CMD_ERROR;
891 }
892 }
893
894 controlreply(sender, "Time ID G-Lines User Hits Channel Hits Comment");
895
896 for (i = 0; i < MAXGLINELOG; i++) {
897 gbl = glinebuflog[(i + glinebuflogoffset + 1) % MAXGLINELOG];
898
899 if (!gbl)
900 continue;
901
902 if (id == 0 || gbl->id == id) {
903 count = 0;
904
905 for (gl = gbl->glines; gl; gl = gl->next)
906 count++;
907
908 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime((gbl->amend) ? &gbl->amend : &gbl->commit));
909 strncat(timebuf, (gbl->amend) ? "*" : " ", sizeof(timebuf));
910 controlreply(sender, "%-20s %-10d %-10d %-15d %-15d %s", timebuf, gbl->id, count, gbl->userhits, gbl->channelhits, gbl->comment ? gbl->comment->content : "(no comment)");
911 }
912
913 if (id != 0 && gbl->id == id) {
914 glinebufspew(gbl, sender);
915 controlreply(sender, "Done.");
916 return CMD_OK;
917 }
918 }
919
920 if (id == 0) {
921 controlreply(sender, "Done.");
922 } else {
923 controlreply(sender, "Log entry for ID %d not found.", id);
924 }
925
926 return CMD_OK;
927 }
928
929 static int glines_cmdglineundo(void *source, int cargc, char **cargv) {
930 nick *sender = source;
931 int id;
932
933 if (cargc < 1)
934 return CMD_USAGE;
935
936 id = atoi(cargv[0]);
937
938 if (id == 0 || !glinebufundo(id)) {
939 controlreply(sender, "Invalid log ID.");
940 return CMD_ERROR;
941 }
942
943 controlreply(sender, "Done.");
944
945 return CMD_OK;
946 }
947
948 static int glines_cmdsyncglines(void *source, int cargc, char **cargv) {
949 nick *sender = source;
950 gline *gl;
951 int count;
952
953 count = 0;
954
955 for (gl = glinelist; gl; gl = gl->next) {
956 gline_propagate(gl);
957 count++;
958 }
959
960 controlwall(NO_OPER, NL_GLINES, "%s SYNCGLINE'd %d G-Lines.",
961 controlid(sender), count);
962
963 controlreply(sender, "Done.");
964
965 return CMD_OK;
966 }
967
968 static int glines_cmdcleanupglines(void *source, int cargc, char **cargv) {
969 nick *sender = source;
970 gline **pnext, *gl;
971 int count;
972 time_t now;
973
974 count = 0;
975 time(&now);
976
977 for (pnext = &glinelist; *pnext;) {
978 gl = *pnext;
979
980 /* Remove inactivate glines that have been last changed more than a week ago */
981 if (!(gl->flags & GLINE_ACTIVE) && gl->lastmod < now - 7 * 24 * 60 * 60) {
982 gline_destroy(gl, 0, 1);
983 count++;
984 } else {
985 pnext = &((*pnext)->next);
986 }
987
988 if (!*pnext)
989 break;
990 }
991
992 controlwall(NO_OPER, NL_GLINES, "%s CLEANUPGLINES'd %d G-Lines.",
993 controlid(sender), count);
994
995 controlreply(sender, "Done.");
996
997 return CMD_OK;
998 }
999
1000 static int commandsregistered;
1001
1002 static void registercommands(int hooknum, void *arg) {
1003 if (commandsregistered)
1004 return;
1005 commandsregistered = 1;
1006
1007 registercontrolhelpcmd("block", NO_OPER, 4, glines_cmdblock, "Usage: block ?flags? <nick> <duration> <reason>\nSets a gline using an appropriate mask given the user's nickname.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit\n-c - chase nick across quits/kills/nick changes");
1008 registercontrolhelpcmd("gline", NO_OPER, 4, glines_cmdgline, "Usage: gline ?flags? <mask> <duration> <reason>\nSets a gline.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
1009 registercontrolhelpcmd("smartgline", NO_OPER, 4, glines_cmdsmartgline, "Usage: smartgline ?flags? <user@host> <duration> <reason>\nSets a gline. Automatically adjusts the mask depending on whether the specified mask is trusted.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
1010 registercontrolhelpcmd("ungline", NO_OPER, 1, glines_cmdungline, "Usage: ungline <mask>\nDeactivates a gline.");
1011 registercontrolhelpcmd("destroygline", NO_DEVELOPER, 1, glines_cmddestroygline, "Usage: destroygline <mask>\nDestroys a gline.");
1012 registercontrolhelpcmd("clearchan", NO_OPER, 5, glines_cmdclearchan, "Usage: clearchan ?flags? <#channel> <how> <duration> ?reason?\nClears a channel.\nhow can be one of:\nkick - Kicks users.\nkill - Kills users.\ngline - Glines non-authed users (using an appropriate mask).\nglineall - Glines users.\nDuration is only valid when glining users. Reason defaults to \"Clearing channel.\".\nFlags (for glines) can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
1013 registercontrolhelpcmd("trustgline", NO_OPER, 5, glines_cmdtrustgline, "Usage: trustgline ?flags? <#id|name> <user> <duration> <reason>\nSets a gline on the specified username for each host in the specified trust group. The username may contain wildcards.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
1014 registercontrolhelpcmd("trustungline", NO_OPER, 2, glines_cmdtrustungline, "Usage: trustungline <#id|name> <user>\nRemoves a gline that was previously set with trustgline.");
1015 registercontrolhelpcmd("glstats", NO_OPER, 0, glines_cmdglstats, "Usage: glstat\nShows statistics about G-Lines.");
1016 registercontrolhelpcmd("glist", NO_OPER, 2, glines_cmdglist, "Usage: glist [-flags] <mask>\nLists matching G-Lines.\nValid flags are:\n-c: Count G-Lines.\n-f: Find G-Lines active on <mask>.\n-x: Find G-Lines matching <mask> exactly.\n-R: Find G-lines on realnames.\n-o: Search for glines by owner.\n-r: Search for glines by reason.\n-i: Include inactive glines.");
1017 registercontrolhelpcmd("glinelog", NO_OPER, 1, glines_cmdglinelog, "Usage: glinelog ?id?\nShows information about previous gline transactions.");
1018 registercontrolhelpcmd("glineundo", NO_OPER, 1, glines_cmdglineundo, "Usage: glineundo ?id?\nUndoes a gline transaction.");
1019 registercontrolhelpcmd("syncglines", NO_DEVELOPER, 0, glines_cmdsyncglines, "Usage: syncglines\nSends all G-Lines to all other servers.");
1020 registercontrolhelpcmd("cleanupglines", NO_DEVELOPER, 0, glines_cmdcleanupglines, "Usage: cleanupglines\nDestroys all deactivated G-Lines.");
1021 }
1022
1023 static void deregistercommands(int hooknum, void *arg) {
1024 if (!commandsregistered)
1025 return;
1026 commandsregistered = 0;
1027
1028 deregistercontrolcmd("block", glines_cmdblock);
1029 deregistercontrolcmd("gline", glines_cmdgline);
1030 deregistercontrolcmd("smartgline", glines_cmdsmartgline);
1031 deregistercontrolcmd("ungline", glines_cmdungline);
1032 deregistercontrolcmd("destroygline", glines_cmddestroygline);
1033 deregistercontrolcmd("clearchan", glines_cmdclearchan);
1034 deregistercontrolcmd("trustgline", glines_cmdtrustgline);
1035 deregistercontrolcmd("trustungline", glines_cmdtrustungline);
1036 deregistercontrolcmd("glstats", glines_cmdglstats);
1037 deregistercontrolcmd("glist", glines_cmdglist);
1038 deregistercontrolcmd("glinelog", glines_cmdglinelog);
1039 deregistercontrolcmd("glineundo", glines_cmdglineundo);
1040 deregistercontrolcmd("syncglines", glines_cmdsyncglines);
1041 deregistercontrolcmd("cleanupglines", glines_cmdcleanupglines);
1042 }
1043
1044 void _init(void) {
1045 registerhook(HOOK_TRUSTS_DB_LOADED, registercommands);
1046 registerhook(HOOK_TRUSTS_DB_CLOSED, deregistercommands);
1047
1048 if (trustsdbloaded)
1049 registercommands(0, NULL);
1050 }
1051
1052 void _fini(void) {
1053 deregisterhook(HOOK_TRUSTS_DB_LOADED, registercommands);
1054 deregisterhook(HOOK_TRUSTS_DB_CLOSED, deregistercommands);
1055
1056 deregistercommands(0, NULL);
1057 }