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