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