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