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