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