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