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