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