]> jfr.im git - irc/quakenet/newserv.git/blame - regexgline/regexgline.c
HELPMOD2: make anti-idle kick message more informative
[irc/quakenet/newserv.git] / regexgline / regexgline.c
CommitLineData
1e0c4014
CP
1/* regexgline.c */
2
3/* TODO:
4
5 FUTURE: natural (sort of) language parsing
6 ^^ CIDR
7
8 PPA: if multiple users match the same user@host or *@host it'll send multiple glines?!
9*/
10
11#include "regexgline.h"
87698d77 12#include "../lib/version.h"
eba15ac4 13#include "../dbapi/dbapi.h"
86178656 14#include "../lib/stringbuf.h"
36c1eaf1
CP
15#include "../core/hooks.h"
16#include "../server/server.h"
c4d6944b 17#include "../lib/strlfunc.h"
8bab42e7 18#include <stdint.h>
87698d77 19
444d2433
CP
20#define INSTANT_IDENT_GLINE 1
21#define INSTANT_HOST_GLINE 2
22#define INSTANT_KILL 3
23#define DELAYED_IDENT_GLINE 4
24#define DELAYED_HOST_GLINE 5
25#define DELAYED_KILL 6
26
e2fa40b2
CP
27#define RESERVED_NICK_GLINE_DURATION 3600 /* 1h */
28
29MODULE_VERSION("1.44");
1e0c4014
CP
30
31typedef struct rg_glinenode {
32 nick *np;
33 struct rg_struct *reason;
20c491ca 34 short punish;
1e0c4014
CP
35 struct rg_glinenode *next;
36} rg_glinenode;
37
38typedef struct rg_glinelist {
39 struct rg_glinenode *start;
40 struct rg_glinenode *end;
41} rg_glinelist;
42
5f3eb6fa
CP
43typedef struct rg_delay {
44 schedule *sch;
45 nick *np;
46 struct rg_struct *reason;
47 short punish;
48 struct rg_delay *next;
49} rg_delay;
50
c4d6944b
CP
51#define GLINE_HEADER " ID Expires Set by Class Type Last seen (ago) Hits(p) Hits Reason"
52
5f3eb6fa
CP
53rg_delay *rg_delays;
54
55void rg_setdelay(nick *np, struct rg_struct *reason, short punish);
56void rg_deletedelay(rg_delay *delay);
57void rg_dodelay(void *arg);
58
1e0c4014 59void rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched);
c4d6944b 60void rg_flush_schedule(void *arg);
1e0c4014 61
fad97385 62static char *gvhost(nick *np);
24072adc
CP
63typedef void (scannick_fn)(struct rg_struct *, nick *, char *, void *);
64static void rg_scannick(nick *np, scannick_fn *fn, void *arg);
fcec2be0 65static void rg_gline_match(struct rg_struct *rp, nick *np, char *hostname, void *arg);
fad97385 66
eba15ac4
CP
67static DBModuleIdentifier dbid;
68static unsigned long highestid = 0;
69static int attached = 0, started = 0;
70
c4d6944b
CP
71static unsigned int getrgmarker(void);
72
e2fa40b2 73#define RESERVED_NICK_CLASS "reservednick"
d73fc7e5 74/* shadowserver only reports classes[0] */
e2fa40b2 75static const char *classes[] = { "drone", "proxy", "spam", "other", RESERVED_NICK_CLASS, (char *)0 };
86178656 76
24072adc
CP
77void rg_initglinelist(struct rg_glinelist *gll);
78void rg_flushglines(struct rg_glinelist *gll);
79
1e0c4014
CP
80void _init(void) {
81 sstring *max_casualties, *max_spew, *expiry_time, *max_per_gline;
82
83 max_casualties = getcopyconfigitem("regexgline", "maxcasualties", RGStringise(RG_MAX_CASUALTIES_DEFAULT), 8);
84 if(!protectedatoi(max_casualties->content, &rg_max_casualties))
85 rg_max_casualties = RG_MAX_CASUALTIES_DEFAULT;
86
87 freesstring(max_casualties);
88
89 max_spew = getcopyconfigitem("regexgline", "maxspew", RGStringise(RG_MAX_SPEW_DEFAULT), 8);
90 if(!protectedatoi(max_spew->content, &rg_max_spew))
91 rg_max_spew = RG_MAX_SPEW_DEFAULT;
92
93 freesstring(max_spew);
94
95 expiry_time = getcopyconfigitem("regexgline", "expirytime", RGStringise(RG_EXPIRY_TIME_DEFAULT), 8);
96 if(!protectedatoi(expiry_time->content, &rg_expiry_time))
97 rg_expiry_time = RG_EXPIRY_TIME_DEFAULT;
98
99 freesstring(expiry_time);
100
101 max_per_gline = getcopyconfigitem("regexgline", "maxpergline", RGStringise(RG_MAX_PER_GLINE_DEFAULT), 8);
102 if(!protectedatoi(max_per_gline->content, &rg_max_per_gline))
103 rg_max_per_gline = RG_MAX_PER_GLINE_DEFAULT;
104
105 freesstring(max_per_gline);
5f3eb6fa
CP
106
107 rg_delays = NULL;
1e0c4014 108
eba15ac4
CP
109 if(dbconnected()) {
110 attached = 1;
111 dbid = dbgetid();
1e0c4014 112 rg_dbload();
eba15ac4
CP
113 } else {
114 Error("regexgline", ERR_STOP, "Could not connect to database.");
1e0c4014
CP
115 }
116}
24072adc
CP
117
118static void rg_count_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
119 void **varg = (void **)arg;
120 int *count = (int *)varg[0];
121
122 (*count)++;
123}
124
125static void rg_gline_reply_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
126 void **varg = (void **)arg;
127
128 rg_count_match(rp, np, hostname, arg);
129 rg_gline_match(rp, np, hostname, varg[1]);
130}
131
132int rg_rescan(void *source, int cargc, char **cargv) {
133 int gline = 0;
134 int j;
135 nick *np = (nick *)source, *tnp;
136 void *arg[2];
137 int count = 0;
138 struct rg_glinelist gll;
139 scannick_fn *fn;
140
141 if(cargc > 0)
142 gline = !strcmp(cargv[0], "-g");
143
144 arg[0] = &count;
145
146 if(gline == 0) {
147 fn = rg_count_match;
148 } else {
149 controlreply(np, "G-line mode activated.");
150
151 rg_initglinelist(&gll);
152 arg[1] = &gll;
153
154 fn = rg_gline_reply_match;
155 }
156
157 controlreply(np, "Beginning scan, this may take a while...");
158
159 for(j=0;j<NICKHASHSIZE;j++)
160 for(tnp=nicktable[j];tnp;tnp=tnp->next)
161 rg_scannick(tnp, fn, arg);
162
163 controlreply(np, "Scan completed, %d hits.", count);
164
165 if(gline)
166 rg_flushglines(&gll);
167
168 return CMD_OK;
169}
170
1e0c4014
CP
171void _fini(void) {
172 struct rg_struct *gp = rg_list, *oldgp;
5f3eb6fa 173 rg_delay *delay, *delaynext;
1e0c4014 174
eba15ac4
CP
175 if(started) {
176 deregisterhook(HOOK_NICK_NEWNICK, &rg_nick);
2fdef282 177 deregisterhook(HOOK_NICK_RENAME, &rg_rename);
eba15ac4
CP
178 deregisterhook(HOOK_NICK_LOSTNICK, &rg_lostnick);
179 deregistercontrolcmd("regexspew", rg_spew);
180 deregistercontrolcmd("regexglist", rg_glist);
181 deregistercontrolcmd("regexdelgline", rg_delgline);
182 deregistercontrolcmd("regexgline", rg_gline);
183 deregistercontrolcmd("regexidlookup", rg_idlist);
24072adc 184 deregistercontrolcmd("regexrescan", rg_rescan);
eba15ac4
CP
185 }
186
5f3eb6fa
CP
187 if(rg_delays) {
188 for(delay=rg_delays;delay;delay=delaynext) {
189 delaynext=delay->next;
190 deleteschedule(delay->sch, rg_dodelay, delay);
191 free(delay);
192 }
193 }
1e0c4014
CP
194
195 if(rg_schedule) {
196 deleteschedule(rg_schedule, &rg_checkexpiry, NULL);
197 rg_schedule = NULL;
198 }
c4d6944b
CP
199
200 deleteallschedules(rg_flush_schedule);
201 rg_flush_schedule(NULL);
202
1e0c4014
CP
203 for(gp=rg_list;gp;) {
204 oldgp = gp;
205 gp = gp->next;
206 rg_freestruct(oldgp);
207 }
eba15ac4
CP
208
209 if(attached) {
210 dbdetach("regexgline");
211 dbfreeid(dbid);
212 }
1e0c4014
CP
213}
214
1b02955c
CP
215static int ignorable_nick(nick *np) {
216 if(IsOper(np) || IsService(np) || IsXOper(np) || SIsService(&serverlist[homeserver(np->numeric)]))
217 return 1;
218 return 0;
219}
220
1e0c4014
CP
221void rg_checkexpiry(void *arg) {
222 struct rg_struct *rp = rg_list, *lp = NULL;
223 time_t current = time(NULL);
224
225 while(rp) {
226 if (current >= rp->expires) {
f2a9a545 227 dbquery("DELETE FROM regexglines WHERE id = %d", rp->id);
1e0c4014
CP
228 if (lp) {
229 lp->next = rp->next;
230 rg_freestruct(rp);
231 rp = lp->next;
232 } else {
233 rg_list = rp->next;
234 rg_freestruct(rp);
235 rp = rg_list;
236 }
237 } else {
238 lp = rp;
239 rp = rp->next;
240 }
241 }
242}
243
5f3eb6fa
CP
244void rg_setdelay(nick *np, rg_struct *reason, short punish) {
245 rg_delay *delay;
246 delay = (rg_delay *)malloc(sizeof(rg_delay));
247
248 /* Just incase */
249 if(!delay) {
250 killuser(NULL, np, "%s (ID: %08lx)", reason->reason->content, reason->glineid);
251 return;
252 }
253
254 delay->np = np;
255 delay->reason = reason;
256 delay->punish = punish;
257 delay->next = rg_delays;
258 rg_delays = delay;
259
260 delay->sch = scheduleoneshot(time(NULL) + (RG_MINIMUM_DELAY_TIME + (rand() % RG_MAXIMUM_RAND_TIME)), rg_dodelay, delay);
261}
262
36c1eaf1
CP
263static void rg_shadowserver(nick *np, struct rg_struct *reason, int type) {
264 char buf[1024];
d73fc7e5
CP
265
266 if(reason->class != classes[0]) /* drone */
267 return;
268
36c1eaf1
CP
269 snprintf(buf, sizeof(buf), "regex-ban %lu %s!%s@%s %s %s", time(NULL), np->nick, np->ident, np->host->name->content, reason->mask->content, serverlist[homeserver(np->numeric)].name->content);
270
271 triggerhook(HOOK_SHADOW_SERVER, (void *)buf);
272}
273
5f3eb6fa
CP
274void rg_deletedelay(rg_delay *delay) {
275 rg_delay *temp, *prev;
276 prev = NULL;
277 for (temp=rg_delays;temp;temp=temp->next) {
278 if (temp==delay) {
279 if (temp==rg_delays)
280 rg_delays = temp->next;
281 else
282 prev->next = temp->next;
283
284 free(temp);
285 return;
286 }
287
288 prev = temp;
289 }
290}
291
292void rg_dodelay(void *arg) {
293 rg_delay *delay = (rg_delay *)arg;
294 char hostname[RG_MASKLEN];
295 int hostlen, usercount = 0;
296
297 /* User or regex gline no longer exists */
298 if((!delay->np) || (!delay->reason)) {
299 rg_deletedelay(delay);
300 return;
301 }
302
303 hostlen = RGBuildHostname(hostname, delay->np);
304
305 /* User has wisely changed nicknames */
306 if(pcre_exec(delay->reason->regex, delay->reason->hint, hostname, hostlen, 0, 0, NULL, 0) < 0) {
307 rg_deletedelay(delay);
308 return;
309 }
310
444d2433 311 if (delay->reason->type == DELAYED_HOST_GLINE) {
5f3eb6fa 312 usercount = delay->np->host->clonecount;
526e7c1d 313 snprintf(hostname, sizeof(hostname), "*@%s", IPtostr(delay->np->p_ipaddr));
5f3eb6fa
CP
314 }
315
444d2433 316 if((delay->reason->type == DELAYED_IDENT_GLINE) || (usercount > rg_max_per_gline)) {
5f3eb6fa
CP
317 nick *tnp;
318
319 for(usercount=0,tnp=delay->np->host->nicks;tnp;tnp=tnp->nextbyhost)
320 if(!ircd_strcmp(delay->np->ident, tnp->ident))
321 usercount++;
322
526e7c1d 323 snprintf(hostname, sizeof(hostname), "%s@%s", delay->np->ident, IPtostr(delay->np->p_ipaddr));
5f3eb6fa
CP
324 }
325
444d2433 326 if ((delay->reason->type == DELAYED_KILL) || (usercount > rg_max_per_gline)) {
fad97385 327 controlwall(NO_OPER, NL_HITS, "%s matched delayed kill regex %08lx (class: %s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class);
36c1eaf1
CP
328
329 rg_shadowserver(delay->np, delay->reason, DELAYED_KILL);
5f3eb6fa
CP
330 killuser(NULL, delay->np, "%s (ID: %08lx)", delay->reason->reason->content, delay->reason->glineid);
331 return;
332 }
333
444d2433 334 if (delay->reason->type == DELAYED_IDENT_GLINE) {
fad97385 335 controlwall(NO_OPER, NL_HITS, "%s matched delayed user@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
444d2433 336 } else if (delay->reason->type == DELAYED_HOST_GLINE) {
fad97385 337 controlwall(NO_OPER, NL_HITS, "%s matched delayed *@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
444d2433
CP
338 } else {
339 return;
5f3eb6fa
CP
340 }
341
36c1eaf1 342 rg_shadowserver(delay->np, delay->reason, delay->reason->type);
58a4da4a 343 irc_send("%s GL * +%s %d %jd :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, (intmax_t)time(NULL), delay->reason->reason->content, delay->reason->glineid);
5f3eb6fa
CP
344 rg_deletedelay(delay);
345}
346
1e0c4014
CP
347void rg_initglinelist(struct rg_glinelist *gll) {
348 gll->start = NULL;
349 gll->end = NULL;
350}
351
352void rg_flushglines(struct rg_glinelist *gll) {
353 struct rg_glinenode *nn, *pn;
354 for(nn=gll->start;nn;nn=pn) {
355 pn = nn->next;
36c1eaf1 356 if(nn->punish == INSTANT_KILL) {
fad97385 357 controlwall(NO_OPER, NL_HITS, "%s matched kill regex %08lx (class: %s)", gvhost(nn->np), nn->reason->glineid, nn->reason->class);
36c1eaf1
CP
358
359 rg_shadowserver(nn->np, nn->reason, nn->punish);
1e0c4014 360 killuser(NULL, nn->np, "%s (ID: %08lx)", nn->reason->reason->content, nn->reason->glineid);
36c1eaf1 361 } else if ((nn->punish == DELAYED_IDENT_GLINE) || (nn->punish == DELAYED_HOST_GLINE) || (nn->punish == DELAYED_KILL)) {
5f3eb6fa
CP
362 rg_setdelay(nn->np, nn->reason, nn->punish);
363 }
1e0c4014
CP
364 free(nn);
365 }
366
367 rg_initglinelist(gll);
368}
369
eba15ac4
CP
370static void dbloaddata(DBConn *dbconn, void *arg) {
371 DBResult *dbres = dbgetresult(dbconn);
372
373 if(!dbquerysuccessful(dbres)) {
374 Error("chanserv", ERR_ERROR, "Error loading DB");
375 return;
1e0c4014 376 }
1e0c4014 377
c4d6944b 378 if (dbnumfields(dbres) != 9) {
eba15ac4
CP
379 Error("regexgline", ERR_ERROR, "DB format error");
380 return;
381 }
382
383 while(dbfetchrow(dbres)) {
c4d6944b
CP
384 unsigned long id, hitssaved;
385 time_t lastseen;
86178656 386 char *gline, *setby, *reason, *expires, *type, *class;
eba15ac4
CP
387
388 id = strtoul(dbgetvalue(dbres, 0), NULL, 10);
389 if(id > highestid)
390 highestid = id;
391
392 gline = dbgetvalue(dbres, 1);
393 setby = dbgetvalue(dbres, 2);
394 reason = dbgetvalue(dbres, 3);
395 expires = dbgetvalue(dbres, 4);
396 type = dbgetvalue(dbres, 5);
86178656 397 class = dbgetvalue(dbres, 6);
eba15ac4 398
c4d6944b
CP
399 lastseen = strtoul(dbgetvalue(dbres, 7), NULL, 10);
400 hitssaved = strtoul(dbgetvalue(dbres, 8), NULL, 10);
401
402 if (!rg_newsstruct(id, gline, setby, reason, expires, type, 0, class, lastseen, hitssaved))
16739dbe 403 dbquery("DELETE FROM regexgline.glines WHERE id = %lu", id);
eba15ac4
CP
404 }
405
406 dbclear(dbres);
407}
408
409static void dbloadfini(DBConn *dbconn, void *arg) {
410 started = 1;
86178656
CP
411 StringBuf b;
412 const char **p;
413 char helpbuf[8192 * 2], allclasses[8192];
414
415 sbinit(&b, (char *)allclasses, sizeof(allclasses));
416 for(p=classes;*p;p++) {
417 sbaddstr(&b, (char *)*p);
418 sbaddchar(&b, ' ');
419 }
420 sbterminate(&b);
232eadea 421
86178656
CP
422 snprintf(helpbuf, sizeof(helpbuf),
423 "Usage: regexgline <regex> <duration> <type> <class> <reason>\n"
232eadea
CP
424 "Adds a new regular expression pattern.\n"
425 "Duration is represented as 3d, 3M etc.\n"
86178656 426 "Class is one of the following: %s\n"
232eadea 427 "Type is an integer which represents the following:\n"
01d1f71c
CP
428 "1 - Instant USER@IP GLINE (igu)\n"
429 "2 - Instant *@IP GLINE (igh)\n"
430 "3 - Instant KILL (ik)\n"
431 "4 - Delayed USER@IP GLINE (dgu)\n"
432 "5 - Delayed *@IP GLINE (dgh)\n"
e2fa40b2
CP
433 "6 - Delayed KILL (dk)\n"
434 "Note that some classes may have additional side effects (e.g. 'reservednick' also sets nick style glines).",
86178656
CP
435 allclasses);
436
437 registercontrolhelpcmd("regexgline", NO_OPER, 5, &rg_gline, helpbuf);
eba15ac4
CP
438 registercontrolhelpcmd("regexdelgline", NO_OPER, 1, &rg_delgline, "Usage: regexdelgline <pattern>\nDeletes a regular expression pattern.");
439 registercontrolhelpcmd("regexglist", NO_OPER, 1, &rg_glist, "Usage: regexglist <pattern>\nLists regular expression patterns.");
440 registercontrolhelpcmd("regexspew", NO_OPER, 1, &rg_spew, "Usage: regexspew <pattern>\nLists users currently on the network which match the given pattern.");
441 registercontrolhelpcmd("regexidlookup", NO_OPER, 1, &rg_idlist, "Usage: regexidlookup <id>\nFinds a regular expression pattern by it's ID number.");
24072adc 442 registercontrolhelpcmd("regexrescan", NO_OPER, 1, &rg_rescan, "Usage: regexrescan ?-g?\nRescans the net for missed clients, optionally glining matches (used for debugging).");
232eadea 443
eba15ac4 444 registerhook(HOOK_NICK_NEWNICK, &rg_nick);
2fdef282 445 registerhook(HOOK_NICK_RENAME, &rg_rename);
eba15ac4
CP
446 registerhook(HOOK_NICK_LOSTNICK, &rg_lostnick);
447 rg_startup();
232eadea 448
eba15ac4 449 rg_schedule = schedulerecurring(time(NULL) + 1, 0, 1, rg_checkexpiry, NULL);
c4d6944b 450 schedulerecurring(time(NULL) + 60, 0, 60, rg_flush_schedule, NULL);
1e0c4014
CP
451}
452
eba15ac4
CP
453void rg_dbload(void) {
454 dbattach("regexgline");
c4d6944b 455 dbcreatequery("CREATE TABLE regexgline.glines (id INT NOT NULL PRIMARY KEY, gline TEXT NOT NULL, setby VARCHAR(%d) NOT NULL, reason VARCHAR(%d) NOT NULL, expires INT NOT NULL, type INT NOT NULL DEFAULT 1, class TEXT NOT NULL, lastseen INT DEFAULT 0, hits INT DEFAULT 0)", ACCOUNTLEN, RG_REASON_MAX);
63c6ac26
CP
456 dbcreatequery("CREATE TABLE regexgline.clog (host VARCHAR(%d) NOT NULL, account VARCHAR(%d) NOT NULL, event TEXT NOT NULL, arg TEXT NOT NULL, ts TIMESTAMP)", RG_MASKLEN - 1, ACCOUNTLEN);
457 dbcreatequery("CREATE TABLE regexgline.glog (glineid INT NOT NULL, ts TIMESTAMP, nickname VARCHAR(%d) NOT NULL, username VARCHAR(%d) NOT NULL, hostname VARCHAR(%d) NOT NULL, realname VARCHAR(%d))", NICKLEN, USERLEN, HOSTLEN, REALLEN);
eba15ac4 458
63c6ac26 459 dbloadtable("regexgline.glines", NULL, dbloaddata, dbloadfini);
1e0c4014
CP
460}
461
24072adc 462static void rg_scannick(nick *np, scannick_fn *fn, void *arg) {
1e0c4014
CP
463 struct rg_struct *rp;
464 char hostname[RG_MASKLEN];
465 int hostlen;
c1c1cc5b 466
1b02955c 467 if(ignorable_nick(np))
c1c1cc5b
CP
468 return;
469
fcec2be0
CP
470 hostlen = RGBuildHostname(hostname, np);
471
1e0c4014
CP
472 for(rp=rg_list;rp;rp=rp->next) {
473 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
fcec2be0 474 fn(rp, np, hostname, arg);
1e0c4014
CP
475 break;
476 }
477 }
fcec2be0
CP
478}
479
480static void rg_gline_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
481 struct rg_glinelist *gll = (struct rg_glinelist *)arg;
482
483 rg_dogline(gll, np, rp, hostname);
484}
485
2fdef282
CP
486void rg_rename(int hooknum, void *arg) {
487 void **harg = (void **)arg;
488 rg_nick(hooknum, harg[0]);
489}
490
fcec2be0
CP
491void rg_nick(int hooknum, void *arg) {
492 nick *np = (nick *)arg;
493 struct rg_glinelist gll;
494
495 rg_initglinelist(&gll);
496
497 rg_scannick(np, rg_gline_match, &gll);
1e0c4014
CP
498
499 rg_flushglines(&gll);
500}
501
5f3eb6fa
CP
502void rg_lostnick(int hooknum, void *arg) {
503 nick *np = (nick *)arg;
504 rg_delay *delay;
505
506 /* Cleanup the delays */
507 for(delay=rg_delays;delay;delay=delay->next)
508 if(delay->np==np)
509 delay->np = NULL;
510}
511
1e0c4014
CP
512int rg_gline(void *source, int cargc, char **cargv) {
513 nick *np = (nick *)source, *tnp;
514 time_t realexpiry;
515 const char *expirybuf;
516 int expiry, count, j, hostlen;
517 struct rg_struct *rp;
518 struct rg_glinelist gll;
86178656 519 const char **p;
1e0c4014 520
86178656
CP
521 char eemask[RG_QUERY_BUF_SIZE], eesetby[RG_QUERY_BUF_SIZE], eereason[RG_QUERY_BUF_SIZE], eeclass[RG_QUERY_BUF_SIZE];
522 char hostname[RG_MASKLEN], *class, *reason, *regex, type;
1e0c4014 523
86178656 524 if(cargc < 5)
5f3eb6fa 525 return CMD_USAGE;
86178656
CP
526
527 type = cargv[2][0];
528 if ((strlen(cargv[2]) != 1) || ((type != '1') && (type != '2') && (type != '3') && (type != '4') && (type != '5') && (type != '6'))) {
1e0c4014 529 controlreply(np, "Invalid type specified!");
5f3eb6fa 530 return CMD_USAGE;
1e0c4014 531 }
5f3eb6fa 532
86178656
CP
533 regex = cargv[0];
534 class = cargv[3];
535 reason = cargv[4];
536
537 for(p=classes;*p;p++)
538 if(!strcasecmp(class, *p))
539 break;
540
541 if(!*p) {
542 controlreply(np, "Bad class supplied.");
543 return CMD_USAGE;
544 }
545
1e0c4014
CP
546 if (!(expiry = durationtolong(cargv[1]))) {
547 controlreply(np, "Invalid duration specified!");
5f3eb6fa 548 return CMD_USAGE;
1e0c4014
CP
549 }
550
551 for(rp=rg_list;rp;rp=rp->next) {
86178656
CP
552 if (RGMasksEqual(rp->mask->content, regex)) {
553 controlreply(np, "That regexgline already exists!");
1e0c4014
CP
554 return CMD_ERROR;
555 }
556 }
557
86178656 558 if (rg_sanitycheck(regex, &count)) {
1e0c4014
CP
559 controlreply(np, "Error in expression.");
560 return CMD_ERROR;
561 } else if (count < 0) {
562 controlreply(np, "That expression would hit too many users (%d)!", -count);
563 return CMD_ERROR;
564 }
565
566 realexpiry = expiry + time(NULL);
567
86178656 568 dbescapestring(eemask, regex, strlen(regex));
eba15ac4 569 dbescapestring(eesetby, np->nick, strlen(np->nick));
86178656
CP
570 dbescapestring(eeclass, class, strlen(class));
571 dbescapestring(eereason, reason, strlen(reason));
1e0c4014 572
eba15ac4 573 highestid = highestid + 1;
16739dbe 574 dbquery("INSERT INTO regexgline.glines (id, gline, setby, reason, expires, type, class, lastseen, hits) VALUES (%lu, '%s', '%s', '%s', %lu, %c, '%s', 0, 0)", highestid, eemask, eesetby, eereason, realexpiry, type, eeclass);
c4d6944b 575 rp = rg_newsstruct(highestid, regex, np->nick, reason, "", cargv[2], realexpiry, class, 0, 0);
1e0c4014
CP
576
577 rg_initglinelist(&gll);
578
579 for(j=0;j<NICKHASHSIZE;j++) {
580 for(tnp=nicktable[j];tnp;tnp=tnp->next) {
1b02955c 581 if(ignorable_nick(tnp))
c1c1cc5b
CP
582 continue;
583
d28e0741 584 hostlen = RGBuildHostname(hostname, tnp);
1e0c4014
CP
585 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0)
586 rg_dogline(&gll, tnp, rp, hostname);
587 }
588 }
589
590 rg_flushglines(&gll);
591
592 expirybuf = longtoduration(expiry, 0);
593
86178656
CP
594 rg_logevent(np, "regexgline", "%s %d %d %s %s", regex, expiry, count, class, reason);
595 controlreply(np, "Added regexgline: %s (class: %s, expires in: %s, hit %d user%s): %s", regex, class, expirybuf, count, (count!=1)?"s":"", reason);
5f3eb6fa 596 /* If we are using NO, can we safely assume the user is authed here and use ->authname? */
86178656 597 controlwall(NO_OPER, NL_GLINES, "%s!%s@%s/%s added regexgline: %s (class: %s, expires in: %s, hit %d user%s): %s", np->nick, np->ident, np->host->name->content, np->authname, regex, class, expirybuf, count, (count!=1)?"s":"", reason);
1e0c4014
CP
598
599 return CMD_OK;
600}
601
602int rg_sanitycheck(char *mask, int *count) {
603 const char *error;
604 char hostname[RG_MASKLEN];
605 int erroroffset, hostlen, j, masklen = strlen(mask);
606 pcre *regex;
607 pcre_extra *hint;
608 nick *np;
609
610 if((masklen < RG_MIN_MASK_LEN) || (masklen > RG_REGEXGLINE_MAX))
611 return 1;
612
613 if(!(regex = pcre_compile(mask, RG_PCREFLAGS, &error, &erroroffset, NULL))) {
614 Error("regexgline", ERR_WARNING, "Error compiling expression %s at offset %d: %s", mask, erroroffset, error);
615 return 2;
616 } else {
617 hint = pcre_study(regex, 0, &error);
618 if(error) {
619 Error("regexgline", ERR_WARNING, "Error studying expression %s: %s", mask, error);
620 pcre_free(regex);
621 return 3;
622 }
623 }
624
625 *count = 0;
626 for(j=0;j<NICKHASHSIZE;j++) {
627 for(np=nicktable[j];np;np=np->next) {
d28e0741 628 hostlen = RGBuildHostname(hostname, np);
1e0c4014
CP
629 if(pcre_exec(regex, hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
630 (*count)++;
631 }
632 }
633 }
634
635 pcre_free(regex);
636 if(hint)
637 pcre_free(hint);
638
639 if(*count >= rg_max_casualties)
640 *count = -(*count);
641
642 return 0;
643}
644
645int rg_delgline(void *source, int cargc, char **cargv) {
646 nick *np = (nick *)source;
5f3eb6fa 647 rg_delay *delay;
1e0c4014
CP
648 struct rg_struct *rp = rg_list, *last = NULL;
649 int count = 0;
650
5f3eb6fa
CP
651 if(cargc < 1)
652 return CMD_USAGE;
1e0c4014
CP
653
654 rg_logevent(np, "regexdelgline", "%s", cargv[0]);
655 while(rp) {
656 if(RGMasksEqual(rp->mask->content, cargv[0])) {
657 count++;
5f3eb6fa
CP
658
659 /* Cleanup the delays */
660 for(delay=rg_delays;delay;delay=delay->next)
661 if(delay->reason==rp)
662 delay->reason = NULL;
663
eba15ac4 664 dbquery("DELETE FROM regexgline.glines WHERE id = %d", rp->id);
1e0c4014
CP
665 if(last) {
666 last->next = rp->next;
667 rg_freestruct(rp);
668 rp = last->next;
669 } else {
670 rg_list = rp->next;
671 rg_freestruct(rp);
672 rp = rg_list;
673 }
674 } else {
675 last = rp;
676 rp = rp->next;
677 }
678 }
679 if (count > 0) {
680 controlreply(np, "Deleted (matched: %d).", count);
5f3eb6fa
CP
681 /* If we are using NO, can we safely assume the user is authed here and use ->authname? */
682 controlwall(NO_OPER, NL_GLINES, "%s!%s@%s/%s removed regexgline: %s", np->nick, np->ident, np->host->name->content, np->authname, cargv[0]);
1e0c4014
CP
683 } else {
684 controlreply(np, "No glines matched: %s", cargv[0]);
685 }
686 return CMD_OK;
687}
688
689int rg_idlist(void *source, int cargc, char **cargv) {
690 nick *np = (nick *)source;
691
692 if(cargc < 1) {
5f3eb6fa 693 return CMD_USAGE;
1e0c4014
CP
694 } else if (strlen(cargv[0]) != 8) {
695 controlreply(np, "Invalid gline id!");
696 return CMD_ERROR;
697 } else {
698 struct rg_struct *rp;
699 unsigned long id = 0;
c4d6944b
CP
700 int i, longest = 0;
701 unsigned int m;
1e0c4014
CP
702
703 for(i=0;i<8;i++) {
704 if(0xff == rc_hexlookup[(int)cargv[0][i]]) {
705 controlreply(np, "Invalid gline id!");
706 return CMD_ERROR;
707 } else {
708 id = (id << 4) | rc_hexlookup[(int)cargv[0][i]];
709 }
710 }
711
c4d6944b
CP
712 m = getrgmarker();
713 controlreply(np, GLINE_HEADER);
714 for(rp=rg_list;rp;rp=rp->next) {
715 if(id == rp->glineid) {
716 rp->marker = m;
717 if(rp->mask->length > longest)
718 longest = rp->mask->length;
719 }
720 }
721
1e0c4014 722 for(rp=rg_list;rp;rp=rp->next)
c4d6944b
CP
723 if(rp->marker == m)
724 rg_displaygline(np, rp, longest);
1e0c4014
CP
725 controlreply(np, "Done.");
726
727 return CMD_OK;
728 }
729}
730
731int rg_glist(void *source, int cargc, char **cargv) {
732 nick *np = (nick *)source;
733 struct rg_struct *rp;
c4d6944b 734 int longest = 0;
1e0c4014
CP
735
736 if(cargc) {
737 int erroroffset;
738 pcre *regex;
739 pcre_extra *hint;
740 const char *error;
c4d6944b
CP
741 unsigned int m;
742
1e0c4014
CP
743 if(!(regex = pcre_compile(cargv[0], RG_PCREFLAGS, &error, &erroroffset, NULL))) {
744 controlreply(np, "Error compiling expression %s at offset %d: %s", cargv[0], erroroffset, error);
745 return CMD_ERROR;
746 } else {
747 hint = pcre_study(regex, 0, &error);
748 if(error) {
749 controlreply(np, "Error studying expression %s: %s", cargv[0], error);
750 pcre_free(regex);
751 return CMD_ERROR;
752 }
753 }
c4d6944b
CP
754
755 m = getrgmarker();
1e0c4014 756 rg_logevent(np, "regexglist", "%s", cargv[0]);
c4d6944b
CP
757 controlreply(np, GLINE_HEADER);
758 for(rp=rg_list;rp;rp=rp->next) {
759 if(pcre_exec(regex, hint, rp->mask->content, rp->mask->length, 0, 0, NULL, 0) >= 0) {
760 rp->marker = m;
761 if(rp->mask->length > longest)
762 longest = rp->mask->length;
763 }
764 }
765
1e0c4014 766 for(rp=rg_list;rp;rp=rp->next)
c4d6944b
CP
767 if(rp->marker == m)
768 rg_displaygline(np, rp, longest);
769
1e0c4014
CP
770 pcre_free(regex);
771 if(hint)
772 pcre_free(hint);
773
774 } else {
579d4f01 775 rg_logevent(np, "regexglist", "%s", "");
c4d6944b 776 controlreply(np, GLINE_HEADER);
1e0c4014 777 for(rp=rg_list;rp;rp=rp->next)
c4d6944b
CP
778 if(rp->mask->length > longest)
779 longest = rp->mask->length;
780
781 for(rp=rg_list;rp;rp=rp->next)
782 rg_displaygline(np, rp, longest);
1e0c4014
CP
783 }
784
785 controlreply(np, "Done.");
786 return CMD_OK;
787}
788
01d1f71c
CP
789char *displaytype(int type) {
790 char *ctype;
791 static char ctypebuf[10];
792
793 switch(type) {
794 case 1:
795 ctype = "igu";
796 break;
797 case 2:
798 ctype = "igh";
799 break;
800 case 3:
801 ctype = "ik";
802 break;
803 case 4:
804 ctype = "dgu";
805 break;
806 case 5:
807 ctype = "dgh";
808 break;
809 case 6:
810 ctype = "dk";
811 break;
812 default:
813 ctype = "??";
814 }
815
816 snprintf(ctypebuf, sizeof(ctype), "%1d:%s", type, ctype);
817 return ctypebuf;
818}
819
c4d6944b
CP
820char *getsep(int longest) {
821 static int lastlongest = -1;
822 static char lenbuf[1024];
823
824 longest = 125;
825/*
826 if(longest < 100)
827 longest = 100;
828
829 if(longest >= sizeof(lenbuf) - 20)
830 longest = sizeof(lenbuf) - 20;
831*/
832 longest+=4;
833 if(lastlongest == -1) {
834 int i;
835
836 for(i=0;i<sizeof(lenbuf)-1;i++)
837 lenbuf[i] = '-';
838 lenbuf[sizeof(lenbuf)-1] = '\0';
839 lastlongest = 0;
840 }
841
842 if(lastlongest != longest) {
843 lenbuf[lastlongest] = '-';
844 lenbuf[longest] = '\0';
845 lastlongest = longest;
846 }
847
848 return lenbuf;
849}
850
851void rg_displaygline(nick *np, struct rg_struct *rp, int longest) { /* could be a macro? I'll assume the C compiler inlines it */
852 char *sep = getsep(longest);
853/* 12345678 12345678901234567890 123456789012345 12345678 12345 12345678901234567890 1234567 1234567 123456
854 ID Expires Set by Class Type Last seen (ago) Hits(s) Hits Reason
855*/
856
857 char d[512];
858 time_t t = time(NULL);
859
860 if(rp->lastseen == 0) {
861 strlcpy(d, "(never)", sizeof(d));
862 } else {
863 strlcpy(d, longtoduration(t - rp->lastseen, 2), sizeof(d));
864 }
865
866 controlreply(np, "%s", rp->mask->content);
867 controlreply(np, " %08lx %-20s %-15s %-8s %-5s %-20s %-7lu %-7lu %s", rp->glineid, longtoduration(rp->expires - t, 2), rp->setby->content, rp->class, displaytype(rp->type), d, rp->hitssaved, rp->hits, rp->reason->content);
868 controlreply(np, "%s", sep);
1e0c4014
CP
869}
870
871int rg_spew(void *source, int cargc, char **cargv) {
872 nick *np = (nick *)source, *tnp;
873 int counter = 0, erroroffset, hostlen, j;
874 pcre *regex;
875 pcre_extra *hint;
876 const char *error;
877 char hostname[RG_MASKLEN];
d28e0741
CP
878 int ovector[30];
879 int pcreret;
880
5f3eb6fa
CP
881 if(cargc < 1)
882 return CMD_USAGE;
1e0c4014
CP
883
884 if(!(regex = pcre_compile(cargv[0], RG_PCREFLAGS, &error, &erroroffset, NULL))) {
885 controlreply(np, "Error compiling expression %s at offset %d: %s", cargv[0], erroroffset, error);
886 return CMD_ERROR;
887 } else {
888 hint = pcre_study(regex, 0, &error);
889 if(error) {
890 controlreply(np, "Error studying expression %s: %s", cargv[0], error);
891 pcre_free(regex);
892 return CMD_ERROR;
893 }
894 }
895
896 rg_logevent(np, "regexspew", "%s", cargv[0]);
897
898 for(j=0;j<NICKHASHSIZE;j++) {
899 for(tnp=nicktable[j];tnp;tnp=tnp->next) {
900 hostlen = RGBuildHostname(hostname, tnp);
d28e0741
CP
901 pcreret = pcre_exec(regex, hint, hostname, hostlen, 0, 0, ovector, sizeof(ovector) / sizeof(int));
902 if(pcreret >= 0) {
1e0c4014
CP
903 if(counter == rg_max_spew) {
904 controlreply(np, "Reached maximum spew count (%d) - aborting display.", rg_max_spew);
905 } else if (counter < rg_max_spew) {
d28e0741
CP
906 /* 15 should be number of bolds */
907 char boldbuf[RG_MASKLEN + 15], *tp, *fp, *realname = NULL;
908 int boldon = 0;
909 for(tp=hostname,fp=boldbuf;*tp;) {
910 if(tp - hostname == ovector[0]) {
911 *fp++ = '\002';
912 boldon = 1;
913 }
914 if(tp - hostname == ovector[1]) {
915 *fp++ = '\002';
916 boldon = 0;
917 }
918 if(*tp == '\r') {
919 if(boldon)
920 *fp++ = '\002';
921 *fp++ = '\0';
922 realname = fp;
923 if(boldon)
924 *fp++ = '\002';
925 tp++;
926 } else {
927 *fp++ = *tp++;
928 }
929 }
930 if(boldon)
931 *fp++ = '\002';
932 *fp++ = '\0';
767ee88c 933 controlreply(np, "%s (%s) (%dc)", boldbuf, realname, tnp->channels->cursi);
1e0c4014
CP
934 }
935 counter++;
936 }
937 }
938 }
939 controlreply(np, "Done - %d matches.", counter);
940
941 pcre_free(regex);
942 if(hint)
943 pcre_free(hint);
944
945 return CMD_OK;
946}
947
1e0c4014 948void rg_startup(void) {
fcec2be0 949 int j;
1e0c4014 950 nick *np;
1e0c4014 951 struct rg_glinelist gll;
1e0c4014
CP
952
953 rg_initglinelist(&gll);
954
fcec2be0
CP
955 for(j=0;j<NICKHASHSIZE;j++)
956 for(np=nicktable[j];np;np=np->next)
957 rg_scannick(np, rg_gline_match, &gll);
1e0c4014
CP
958
959 rg_flushglines(&gll);
960}
961
962void rg_freestruct(struct rg_struct *rp) {
963 freesstring(rp->mask);
964 freesstring(rp->setby);
965 freesstring(rp->reason);
966 pcre_free(rp->regex);
967 if(rp->hint)
968 pcre_free(rp->hint);
969 free(rp);
970}
971
972struct rg_struct *rg_newstruct(time_t expires) {
973 struct rg_struct *rp;
974
975 if (time(NULL) >= expires)
976 return NULL;
977
978 rp = (struct rg_struct*)malloc(sizeof(struct rg_struct));
979 if(rp) {
980 struct rg_struct *tp, *lp;
86178656 981
c4d6944b 982 memset(rp, 0, sizeof(rg_struct));
1e0c4014 983 rp->expires = expires;
1e0c4014
CP
984
985 for(lp=NULL,tp=rg_list;tp;lp=tp,tp=tp->next) {
986 if (expires <= tp->expires) { /* <= possible, slight speed increase */
987 rp->next = tp;
988 if (lp) {
989 lp->next = rp;
990 } else {
991 rg_list = rp;
992 }
993 break;
994 }
995 }
996 if (!tp) {
997 rp->next = NULL;
998 if (lp) {
999 lp->next = rp;
1000 } else {
1001 rg_list = rp;
1002 }
1003 }
1004
1005 }
1006 return rp;
1007}
1008
c4d6944b 1009struct rg_struct *rg_newsstruct(unsigned long id, char *mask, char *setby, char *reason, char *expires, char *type, time_t iexpires, char *class, time_t lastseen, unsigned int hitssaved) {
1e0c4014
CP
1010 struct rg_struct *newrow, *lp, *cp;
1011 time_t rexpires;
1012 char glineiddata[1024];
86178656
CP
1013 const char **p;
1014
1e0c4014 1015 if (iexpires == 0) {
5f3eb6fa
CP
1016 int qexpires;
1017 if(!protectedatoi(expires, &qexpires))
1e0c4014 1018 return NULL;
5f3eb6fa 1019 rexpires = (time_t)qexpires;
1e0c4014
CP
1020 } else {
1021 rexpires = iexpires;
1022 }
1023
1024 newrow = rg_newstruct(rexpires);
1025
1026 if(newrow) {
1027 const char *error;
1028 int erroroffset;
1029
86178656
CP
1030 for(p=classes;*p;p++) {
1031 if(!strcasecmp(class, *p)) {
1032 newrow->class = *p;
1033 break;
1034 }
1035 }
1036
1037 if(!*p)
1038 newrow->class = "unknown";
1039
1e0c4014
CP
1040 if(!(newrow->regex = pcre_compile(mask, RG_PCREFLAGS, &error, &erroroffset, NULL))) {
1041 Error("regexgline", ERR_WARNING, "Error compiling expression %s at offset %d: %s", mask, erroroffset, error);
1042 goto dispose;
1043 } else {
1044 newrow->hint = pcre_study(newrow->regex, 0, &error);
1045 if(error) {
1046 Error("regexgline", ERR_WARNING, "Error studying expression %s: %s", mask, error);
1047 pcre_free(newrow->regex);
1048 goto dispose;
1049 }
1050 }
1051
eba15ac4 1052 newrow->id = id;
c4d6944b
CP
1053 newrow->hitssaved = hitssaved;
1054 newrow->lastseen = lastseen;
86178656 1055
1e0c4014
CP
1056 newrow->mask = getsstring(mask, RG_REGEXGLINE_MAX);
1057 if(!newrow->mask) {
1058 Error("regexgline", ERR_WARNING, "Error allocating memory for mask!");
1059 goto dispose2;
1060 }
1061
1062 newrow->setby = getsstring(setby, ACCOUNTLEN);
1063 if(!newrow->setby) {
1064 Error("regexgline", ERR_WARNING, "Error allocating memory for setby!");
1065 goto dispose2;
1066 }
1067
1068 newrow->reason = getsstring(reason, RG_REASON_MAX);
1069 if(!newrow->reason) {
1070 Error("regexgline", ERR_WARNING, "Error allocating memory for reason!");
1071 goto dispose2;
1072 }
1073
1074 if(!protectedatoi(type, &newrow->type))
1075 newrow->type = 0; /* just in case */
1076
5f3eb6fa 1077 snprintf(glineiddata, sizeof(glineiddata), "%s regexgline %s %s %s %d %d", mynumeric->content, mask, setby, reason, (int)iexpires, newrow->type);
1e0c4014
CP
1078 newrow->glineid = crc32(glineiddata);
1079 }
1080
1081 return newrow;
1082
1083 dispose2:
1084 if(newrow->mask)
1085 freesstring(newrow->mask);
1086 if(newrow->setby)
1087 freesstring(newrow->setby);
1088 if(newrow->reason)
1089 freesstring(newrow->reason);
1090 pcre_free(newrow->regex);
1091 if(newrow->hint)
1092 pcre_free(newrow->hint);
1093
1094 dispose:
1095 for(lp=NULL,cp=rg_list;cp;lp=cp,cp=cp->next) {
1096 if(newrow == cp) {
1097 if(lp) {
1098 lp->next = cp->next;
1099 } else {
1100 rg_list = cp->next;
1101 }
1102 free(newrow);
1103 break;
1104 }
1105 }
1106 return NULL;
1107}
1108
de2bb012 1109int __rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched) { /* PPA: if multiple users match the same user@host or *@host it'll send multiple glines?! */
1e0c4014 1110 char hostname[RG_MASKLEN];
1fbb1306 1111 int usercount = 0;
444d2433 1112 int validdelay;
1e0c4014 1113
d28e0741 1114 rg_loggline(rp, np);
1e0c4014 1115
444d2433 1116 if (rp->type == INSTANT_HOST_GLINE) {
5f3eb6fa 1117 usercount = np->host->clonecount;
526e7c1d 1118 snprintf(hostname, sizeof(hostname), "*@%s", IPtostr(np->p_ipaddr));
5f3eb6fa
CP
1119 }
1120
444d2433 1121 if ((rp->type == INSTANT_IDENT_GLINE) || (usercount > rg_max_per_gline)) {
1e0c4014
CP
1122 nick *tnp;
1123
5f3eb6fa 1124 for(usercount=0,tnp=np->host->nicks;tnp;tnp=tnp->nextbyhost)
1e0c4014
CP
1125 if(!ircd_strcmp(np->ident, tnp->ident))
1126 usercount++;
1127
526e7c1d 1128 snprintf(hostname, sizeof(hostname), "%s@%s", np->ident, IPtostr(np->p_ipaddr));
0c4a5a3e
CP
1129 }
1130
e2fa40b2
CP
1131 if(!strcmp(rp->class, RESERVED_NICK_CLASS))
1132 irc_send("%s GL * +%s!*@* %d %jd :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, np->nick, RESERVED_NICK_GLINE_DURATION, (intmax_t)time(NULL), rp->reason->content, rp->glineid);
1133
444d2433
CP
1134 validdelay = (rp->type == INSTANT_KILL) || (rp->type == DELAYED_IDENT_GLINE) || (rp->type == DELAYED_HOST_GLINE) || (rp->type == DELAYED_KILL);
1135 if (validdelay || (usercount > rg_max_per_gline)) {
1e0c4014
CP
1136 struct rg_glinenode *nn = (struct rg_glinenode *)malloc(sizeof(struct rg_glinenode));
1137 if(nn) {
1138 nn->next = NULL;
1139 if(gll->end) {
1140 gll->end->next = nn;
1141 gll->end = nn;
1142 } else {
1143 gll->start = nn;
1144 gll->end = nn;
1145 }
444d2433 1146
1e0c4014
CP
1147 nn->np = np;
1148 nn->reason = rp;
444d2433
CP
1149 if(!validdelay) {
1150 nn->punish = INSTANT_KILL;
5f3eb6fa
CP
1151 } else {
1152 nn->punish = rp->type;
1153 }
1e0c4014 1154 }
de2bb012 1155 return usercount;
1e0c4014
CP
1156 }
1157
444d2433 1158 if (rp->type == INSTANT_IDENT_GLINE) {
fad97385 1159 controlwall(NO_OPER, NL_HITS, "%s matched user@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(np), rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
444d2433 1160 } else if(rp->type == INSTANT_HOST_GLINE) {
fad97385 1161 controlwall(NO_OPER, NL_HITS, "%s matched *@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(np), rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
444d2433
CP
1162 } else {
1163 return 0;
5f3eb6fa
CP
1164 }
1165
36c1eaf1 1166 rg_shadowserver(np, rp, rp->type);
58a4da4a 1167 irc_send("%s GL * +%s %d %jd :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, (intmax_t)time(NULL), rp->reason->content, rp->glineid);
de2bb012
CP
1168 return usercount;
1169}
1170
fad97385
CP
1171static char *gvhost(nick *np) {
1172 static char buf[NICKLEN+1+USERLEN+1+HOSTLEN+1+ACCOUNTLEN+4+REALLEN+1+10];
1173
1174 if(IsAccount(np)) {
1175 snprintf(buf, sizeof(buf), "%s!%s@%s/%s r(%s)", np->nick, np->ident, np->host->name->content, np->authname, np->realname->name->content);
1176 } else {
1177 snprintf(buf, sizeof(buf), "%s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
1178 }
1179
1180 return buf;
1181}
1182
481738a4
CP
1183static int floodprotection = 0;
1184static int lastfloodspam = 0;
de2bb012
CP
1185
1186void rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched) {
1187 int t = time(NULL);
1188
1189 if(t > floodprotection) {
1190 floodprotection = t;
1191 } else if((floodprotection - t) / 8 > RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC) {
b88719fa 1192 if(t > lastfloodspam + 3600) {
481738a4
CP
1193 channel *cp = findchannel("#twilightzone");
1194 if(cp)
1195 controlchanmsg(cp, "WARNING! REGEXGLINE DISABLED FOR AN HOUR DUE TO NETWORK WIDE LOOKING GLINE!: %d exceeded %d", (floodprotection - t) / 8, RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC);
1196 controlwall(NO_OPER, NL_MANAGEMENT, "WARNING! REGEXGLINE DISABLED FOR AN HOUR DUE TO NETWORK WIDE LOOKING GLINE!");
1197 lastfloodspam = t;
b88719fa 1198 floodprotection = t + RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC * 3600 * 8;
481738a4 1199 }
b88719fa 1200 return;
de2bb012
CP
1201 }
1202
1203 floodprotection+=__rg_dogline(gll, np, rp, matched);
1e0c4014
CP
1204}
1205
1206void rg_logevent(nick *np, char *event, char *details, ...) {
1207 char eeevent[RG_QUERY_BUF_SIZE], eedetails[RG_QUERY_BUF_SIZE], eemask[RG_QUERY_BUF_SIZE], eeaccount[RG_QUERY_BUF_SIZE];
1208 char buf[513], account[ACCOUNTLEN + 1], mask[RG_MASKLEN];
1209 int masklen;
526e7c1d 1210
1e0c4014 1211 va_list va;
16739dbe
CP
1212
1213 if(details) {
1214 va_start(va, details);
1215 vsnprintf(buf, sizeof(buf), details, va);
1216 va_end(va);
1217 } else {
1218 buf[0] = '\0';
1219 }
1220
1e0c4014
CP
1221 if(np) {
1222 if (IsAccount(np)) {
1223 strncpy(account, np->authname, sizeof(account) - 1);
1224 account[sizeof(account) - 1] = '\0';
1225 } else {
1226 account[0] = '\0';
1227 }
d28e0741 1228 masklen = RGBuildHostname(mask, np);
1e0c4014
CP
1229 } else {
1230 mask[0] = '\0';
1231 masklen = 0;
1232 }
1233
eba15ac4
CP
1234 dbescapestring(eeevent, event, strlen(event));
1235 dbescapestring(eedetails, buf, strlen(buf));
63c6ac26 1236 dbescapestring(eeaccount, account, strlen(account));
eba15ac4 1237 dbescapestring(eemask, mask, masklen);
1e0c4014 1238
63c6ac26 1239 dbquery("INSERT INTO regexgline.clog (host, account, event, arg, ts) VALUES ('%s', '%s', '%s', '%s', NOW())", eemask, eeaccount, eeevent, eedetails);
1e0c4014
CP
1240}
1241
d28e0741
CP
1242void rg_loggline(struct rg_struct *rg, nick *np) {
1243 char eenick[RG_QUERY_BUF_SIZE], eeuser[RG_QUERY_BUF_SIZE], eehost[RG_QUERY_BUF_SIZE], eereal[RG_QUERY_BUF_SIZE];
1e0c4014 1244
01d1f71c 1245 rg->hits++;
c4d6944b
CP
1246 rg->hitssaved++;
1247 rg->lastseen = time(NULL);
1248 rg->dirty = 1;
01d1f71c 1249
eba15ac4
CP
1250 /* @paul: disabled */
1251
526e7c1d 1252 return;
eba15ac4
CP
1253 dbescapestring(eenick, np->nick, strlen(np->nick));
1254 dbescapestring(eeuser, np->ident, strlen(np->ident));
1255 dbescapestring(eehost, np->host->name->content, strlen(np->host->name->content));
1256 dbescapestring(eereal, np->realname->name->content, strlen(np->realname->name->content));
1e0c4014 1257
63c6ac26 1258 dbquery("INSERT INTO regexgline.glog (glineid, nickname, username, hostname, realname, ts) VALUES (%d, '%s', '%s', '%s', '%s', NOW())", rg->id, eenick, eeuser, eehost, eereal);
1e0c4014 1259}
c4d6944b
CP
1260
1261static unsigned int getrgmarker(void) {
1262 static unsigned int marker = 0;
1263
1264 marker++;
1265 if(!marker) {
1266 struct rg_struct *l;
1267
1268 /* If we wrapped to zero, zap the marker on all hosts */
1269 for(l=rg_list;l;l=l->next)
1270 l->marker=0;
1271 marker++;
1272 }
1273
1274 return marker;
1275}
1276
1277void rg_flush_schedule(void *arg) {
1278 struct rg_struct *l;
1279
1280 for(l=rg_list;l;l=l->next) {
1281 if(!l->dirty)
1282 continue;
1283
58a4da4a 1284 dbquery("UPDATE regexgline.glines SET lastseen = %jd, hits = %lu WHERE id = %d", (intmax_t)l->lastseen, l->hitssaved, l->id);
c4d6944b
CP
1285
1286 l->dirty = 0;
1287 }
1288}
1289