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