]> jfr.im git - irc/quakenet/newserv.git/blob - regexgline/regexgline.c
504b9fbc4d630384ef70b6cfaf0c5b736adf08bf
[irc/quakenet/newserv.git] / regexgline / regexgline.c
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"
12
13 typedef struct rg_glinenode {
14 nick *np;
15 struct rg_struct *reason;
16 struct rg_glinenode *next;
17 } rg_glinenode;
18
19 typedef struct rg_glinelist {
20 struct rg_glinenode *start;
21 struct rg_glinenode *end;
22 } rg_glinelist;
23
24 void rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched);
25
26 void _init(void) {
27 sstring *max_casualties, *max_spew, *expiry_time, *max_per_gline;
28
29 max_casualties = getcopyconfigitem("regexgline", "maxcasualties", RGStringise(RG_MAX_CASUALTIES_DEFAULT), 8);
30 if(!protectedatoi(max_casualties->content, &rg_max_casualties))
31 rg_max_casualties = RG_MAX_CASUALTIES_DEFAULT;
32
33 freesstring(max_casualties);
34
35 max_spew = getcopyconfigitem("regexgline", "maxspew", RGStringise(RG_MAX_SPEW_DEFAULT), 8);
36 if(!protectedatoi(max_spew->content, &rg_max_spew))
37 rg_max_spew = RG_MAX_SPEW_DEFAULT;
38
39 freesstring(max_spew);
40
41 expiry_time = getcopyconfigitem("regexgline", "expirytime", RGStringise(RG_EXPIRY_TIME_DEFAULT), 8);
42 if(!protectedatoi(expiry_time->content, &rg_expiry_time))
43 rg_expiry_time = RG_EXPIRY_TIME_DEFAULT;
44
45 freesstring(expiry_time);
46
47 max_per_gline = getcopyconfigitem("regexgline", "maxpergline", RGStringise(RG_MAX_PER_GLINE_DEFAULT), 8);
48 if(!protectedatoi(max_per_gline->content, &rg_max_per_gline))
49 rg_max_per_gline = RG_MAX_PER_GLINE_DEFAULT;
50
51 freesstring(max_per_gline);
52
53 if(!rg_dbconnect()) {
54 rg_dbload();
55
56 registercontrolcmd("regexgline", 10, 4, rg_gline);
57 registercontrolcmd("regexdelgline", 10, 1, rg_delgline);
58 registercontrolcmd("regexglist", 10, 1, rg_glist);
59 registercontrolcmd("regexspew", 10, 1, rg_spew);
60 registercontrolcmd("regexidlookup", 10, 1, rg_idlist);
61
62 registerhook(HOOK_NICK_NEWNICK, &rg_nick);
63 registerhook(HOOK_NICK_RENAME, &rg_nick);
64 rg_startup();
65
66 rg_schedule = schedulerecurring(time(NULL) + 1, 0, 1, rg_checkexpiry, NULL);
67 }
68 }
69
70 void _fini(void) {
71 struct rg_struct *gp = rg_list, *oldgp;
72
73 deregisterhook(HOOK_NICK_NEWNICK, &rg_nick);
74 deregisterhook(HOOK_NICK_RENAME, &rg_nick);
75 deregistercontrolcmd("regexspew", rg_spew);
76 deregistercontrolcmd("regexglist", rg_glist);
77 deregistercontrolcmd("regexdelgline", rg_delgline);
78 deregistercontrolcmd("regexgline", rg_gline);
79 deregistercontrolcmd("regexidlookup", rg_idlist);
80
81 if(rg_schedule) {
82 deleteschedule(rg_schedule, &rg_checkexpiry, NULL);
83 rg_schedule = NULL;
84 }
85
86 for(gp=rg_list;gp;) {
87 oldgp = gp;
88 gp = gp->next;
89 rg_freestruct(oldgp);
90 }
91
92 if(rg_sqlconnected)
93 rg_sqldisconnect();
94 }
95
96 void rg_checkexpiry(void *arg) {
97 struct rg_struct *rp = rg_list, *lp = NULL;
98 time_t current = time(NULL);
99
100 while(rp) {
101 if (current >= rp->expires) {
102 if (lp) {
103 lp->next = rp->next;
104 rg_freestruct(rp);
105 rp = lp->next;
106 } else {
107 rg_list = rp->next;
108 rg_freestruct(rp);
109 rp = rg_list;
110 }
111 } else {
112 lp = rp;
113 rp = rp->next;
114 }
115 }
116 }
117
118 void rg_initglinelist(struct rg_glinelist *gll) {
119 gll->start = NULL;
120 gll->end = NULL;
121 }
122
123 void rg_flushglines(struct rg_glinelist *gll) {
124 struct rg_glinenode *nn, *pn;
125 for(nn=gll->start;nn;nn=pn) {
126 pn = nn->next;
127 if(nn->reason->type == 3)
128 killuser(NULL, nn->np, "%s (ID: %08lx)", nn->reason->reason->content, nn->reason->glineid);
129 free(nn);
130 }
131
132 rg_initglinelist(gll);
133 }
134
135 int rg_dbconnect(void) {
136 sstring *dbhost, *dbusername, *dbpassword, *dbdatabase, *dbport;
137
138 dbhost = getcopyconfigitem("regexgline", "dbhost", "localhost", HOSTLEN);
139 dbusername = getcopyconfigitem("regexgline", "dbusername", "regexgline", 20);
140 dbpassword = getcopyconfigitem("regexgline", "dbpassword", "moo", 20);
141 dbdatabase = getcopyconfigitem("regexgline", "dbdatabase", "regexgline", 20);
142 dbport = getcopyconfigitem("regexgline", "dbport", "3306", 8);
143
144 if(rg_sqlconnect(dbhost->content, dbusername->content, dbpassword->content, dbdatabase->content, strtol(dbport->content, NULL, 10))) {
145 Error("regexgline", ERR_FATAL, "Cannot connect to database host!");
146 return 1; /* PPA: splidge: do something here 8]! */
147 } else {
148 rg_sqlconnected = 1;
149 }
150
151 freesstring(dbhost);
152 freesstring(dbusername);
153 freesstring(dbpassword);
154 freesstring(dbdatabase);
155 freesstring(dbport);
156
157 return 0;
158 }
159
160 int rg_dbload(void) {
161 rg_sqlquery("CREATE TABLE regexglines (id INT(10) PRIMARY KEY AUTO_INCREMENT, gline TEXT NOT NULL, setby VARCHAR(%d) NOT NULL, reason VARCHAR(%d) NOT NULL, expires BIGINT NOT NULL, type TINYINT(4) NOT NULL DEFAULT 1)", ACCOUNTLEN, RG_REASON_MAX);
162 rg_sqlquery("CREATE TABLE regexlogs (id INT(10) PRIMARY KEY AUTO_INCREMENT, host VARCHAR(%d) NOT NULL, account VARCHAR(%d) NOT NULL, event TEXT NOT NULL, arg TEXT NOT NULL, ts TIMESTAMP)", RG_MASKLEN - 1, ACCOUNTLEN);
163 rg_sqlquery("CREATE TABLE regexglinelog (id INT(10) PRIMARY KEY AUTO_INCREMENT, glineid INT(10) NOT NULL, ts TIMESTAMP, matched TEXT NOT NULL)");
164
165 if(!rg_sqlquery("SELECT id, gline, setby, reason, expires, type FROM regexglines")) {
166 rg_sqlresult res;
167 if((res = rg_sqlstoreresult())) {
168 rg_sqlrow row;
169 while((row = rg_sqlgetrow(res))) {
170 if (!rg_newsstruct(row[0], row[1], row[2], row[3], row[4], row[5], 0, 0))
171 rg_sqlquery("DELETE FROM regexglines WHERE id = %s", row[0]);
172 }
173 rg_sqlfree(res);
174 }
175 }
176
177 return 0;
178 }
179
180 void rg_nick(int hooknum, void *arg) {
181 nick *np = (nick *)arg;
182 struct rg_struct *rp;
183 char hostname[RG_MASKLEN];
184 int hostlen;
185 struct rg_glinelist gll;
186
187 rg_initglinelist(&gll);
188
189 hostlen = RGBuildMatchHostname(hostname, np);
190
191 for(rp=rg_list;rp;rp=rp->next) {
192 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
193 rg_dogline(&gll, np, rp, hostname);
194 break;
195 }
196 }
197
198 rg_flushglines(&gll);
199 }
200
201 int rg_gline(void *source, int cargc, char **cargv) {
202 nick *np = (nick *)source, *tnp;
203 time_t realexpiry;
204 const char *expirybuf;
205 int expiry, count, j, hostlen;
206 struct rg_struct *rp;
207 struct rg_glinelist gll;
208
209 char eemask[RG_QUERY_BUF_SIZE], eesetby[RG_QUERY_BUF_SIZE], eereason[RG_QUERY_BUF_SIZE];
210 char hostname[RG_MASKLEN];
211
212 if(cargc < 4) {
213 controlreply(np, "syntax: regexgline [mask] [duration] [type: 1: gline user@host 2: gline *@host 3: KILL] [reason]");
214 return CMD_ERROR;
215 }
216
217 if ((strlen(cargv[2]) != 1) || ((cargv[2][0] != '1') && (cargv[2][0] != '2') && (cargv[2][0] != '3'))) {
218 controlreply(np, "Invalid type specified!");
219 return CMD_ERROR;
220 }
221
222 if (!(expiry = durationtolong(cargv[1]))) {
223 controlreply(np, "Invalid duration specified!");
224 return CMD_ERROR;
225 }
226
227 for(rp=rg_list;rp;rp=rp->next) {
228 if (RGMasksEqual(rp->mask->content, cargv[0])) {
229 controlreply(np, "That regexp gline already exists!");
230 return CMD_ERROR;
231 }
232 }
233
234 if (rg_sanitycheck(cargv[0], &count)) {
235 controlreply(np, "Error in expression.");
236 return CMD_ERROR;
237 } else if (count < 0) {
238 controlreply(np, "That expression would hit too many users (%d)!", -count);
239 return CMD_ERROR;
240 }
241
242 realexpiry = expiry + time(NULL);
243
244 rg_sqlescape_string(eemask, cargv[0], strlen(cargv[0]));
245 rg_sqlescape_string(eesetby, np->nick, strlen(np->nick));
246 rg_sqlescape_string(eereason, cargv[3], strlen(cargv[3]));
247
248 rg_sqlquery("INSERT INTO regexglines (gline, setby, reason, expires, type) VALUES ('%s', '%s', '%s', %d, %s)", eemask, eesetby, eereason, realexpiry, cargv[2]);
249 if (!rg_sqlquery("SELECT id FROM regexglines WHERE gline = '%s' AND setby = '%s' AND reason = '%s' AND expires = %d AND type = %s ORDER BY ID DESC LIMIT 1", eemask, eesetby, eereason, realexpiry, cargv[2])) {
250 rg_sqlresult res;
251 if((res = rg_sqlstoreresult())) {
252 rg_sqlrow row;
253 row = rg_sqlgetrow(res);
254 if (row) {
255 rp = rg_newsstruct(row[0], cargv[0], np->nick, cargv[3], "", cargv[2], realexpiry, 0);
256 rg_sqlfree(res);
257 if(!rp) {
258 rg_sqlquery("DELETE FROM regexglines WHERE gline = '%s' AND setby = '%s' AND reason = '%s' AND expires = %d AND type = %s ORDER BY ID DESC LIMIT 1", eemask, eesetby, eereason, realexpiry, cargv[2]);
259 controlreply(np, "Error allocating - regexgline NOT ADDED.");
260 return CMD_ERROR;
261 }
262 } else {
263 rg_sqlfree(res);
264 rg_sqlquery("DELETE FROM regexglines WHERE gline = '%s' AND setby = '%s' AND reason = '%s' AND expires = %d AND type = %s ORDER BY ID DESC LIMIT 1", eemask, eesetby, eereason, realexpiry, cargv[2]);
265 controlreply(np, "Error selecting ID from database - regexgline NOT ADDED.");
266 return CMD_ERROR;
267 }
268 } else {
269 rg_sqlquery("DELETE FROM regexglines WHERE gline = '%s' AND setby = '%s' AND reason = '%s' AND expires = %d AND type = %s ORDER BY ID DESC LIMIT 1", eemask, eesetby, eereason, realexpiry, cargv[2]);
270 controlreply(np, "Error fetching ID from database - regexgline NOT ADDED.");
271 return CMD_ERROR;
272 }
273 } else {
274 rg_sqlquery("DELETE FROM regexglines WHERE gline = '%s' AND setby = '%s' AND reason = '%s' AND expires = %d AND type = %s ORDER BY ID DESC LIMIT 1", eemask, eesetby, eereason, realexpiry, cargv[2]);
275 controlreply(np, "Error executing query - regexgline NOT ADDED.");
276 return CMD_ERROR;
277 }
278
279 rg_initglinelist(&gll);
280
281 for(j=0;j<NICKHASHSIZE;j++) {
282 for(tnp=nicktable[j];tnp;tnp=tnp->next) {
283 hostlen = RGBuildMatchHostname(hostname, tnp);
284 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0)
285 rg_dogline(&gll, tnp, rp, hostname);
286 }
287 }
288
289 rg_flushglines(&gll);
290
291 expirybuf = longtoduration(expiry, 0);
292
293 rg_logevent(np, "regexgline", "%s %d %d %s", cargv[0], expiry, count, cargv[3]);
294 controlreply(np, "Added regexgline: %s (expires in: %s, hit %d user%s): %s", cargv[0], expirybuf, count, (count!=1)?"s":"", cargv[3]);
295 controlnoticeopers("%s added regexgline: %s (expires in: %s, hit %d user%s): %s", np->nick, cargv[0], expirybuf, count, (count!=1)?"s":"", cargv[3]);
296
297 return CMD_OK;
298 }
299
300 int rg_sanitycheck(char *mask, int *count) {
301 const char *error;
302 char hostname[RG_MASKLEN];
303 int erroroffset, hostlen, j, masklen = strlen(mask);
304 pcre *regex;
305 pcre_extra *hint;
306 nick *np;
307
308 if((masklen < RG_MIN_MASK_LEN) || (masklen > RG_REGEXGLINE_MAX))
309 return 1;
310
311 if(!(regex = pcre_compile(mask, RG_PCREFLAGS, &error, &erroroffset, NULL))) {
312 Error("regexgline", ERR_WARNING, "Error compiling expression %s at offset %d: %s", mask, erroroffset, error);
313 return 2;
314 } else {
315 hint = pcre_study(regex, 0, &error);
316 if(error) {
317 Error("regexgline", ERR_WARNING, "Error studying expression %s: %s", mask, error);
318 pcre_free(regex);
319 return 3;
320 }
321 }
322
323 *count = 0;
324 for(j=0;j<NICKHASHSIZE;j++) {
325 for(np=nicktable[j];np;np=np->next) {
326 hostlen = RGBuildMatchHostname(hostname, np);
327 if(pcre_exec(regex, hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
328 (*count)++;
329 }
330 }
331 }
332
333 pcre_free(regex);
334 if(hint)
335 pcre_free(hint);
336
337 if(*count >= rg_max_casualties)
338 *count = -(*count);
339
340 return 0;
341 }
342
343 int rg_delgline(void *source, int cargc, char **cargv) {
344 nick *np = (nick *)source;
345 struct rg_struct *rp = rg_list, *last = NULL;
346 int count = 0;
347
348 if(cargc < 1) {
349 controlreply(np, "syntax: regexdelgline [mask]");
350 return CMD_ERROR;
351 }
352
353 rg_logevent(np, "regexdelgline", "%s", cargv[0]);
354 while(rp) {
355 if(RGMasksEqual(rp->mask->content, cargv[0])) {
356 count++;
357 rg_sqlquery("DELETE FROM regexglines WHERE id = %d", rp->id);
358 if(last) {
359 last->next = rp->next;
360 rg_freestruct(rp);
361 rp = last->next;
362 } else {
363 rg_list = rp->next;
364 rg_freestruct(rp);
365 rp = rg_list;
366 }
367 } else {
368 last = rp;
369 rp = rp->next;
370 }
371 }
372 if (count > 0) {
373 controlreply(np, "Deleted (matched: %d).", count);
374 controlnoticeopers("%s removed regexgline: %s (matches: %d)", np->nick, cargv[0], count);
375 } else {
376 controlreply(np, "No glines matched: %s", cargv[0]);
377 }
378 return CMD_OK;
379 }
380
381 int rg_idlist(void *source, int cargc, char **cargv) {
382 nick *np = (nick *)source;
383
384 if(cargc < 1) {
385 controlreply(np, "syntax: regexidlookup [gline id]");
386 return CMD_ERROR;
387 } else if (strlen(cargv[0]) != 8) {
388 controlreply(np, "Invalid gline id!");
389 return CMD_ERROR;
390 } else {
391 struct rg_struct *rp;
392 unsigned long id = 0;
393 int i;
394
395 for(i=0;i<8;i++) {
396 if(0xff == rc_hexlookup[(int)cargv[0][i]]) {
397 controlreply(np, "Invalid gline id!");
398 return CMD_ERROR;
399 } else {
400 id = (id << 4) | rc_hexlookup[(int)cargv[0][i]];
401 }
402 }
403
404 controlreply(np, "Mask Expires Set by Type Reason");
405 for(rp=rg_list;rp;rp=rp->next)
406 if(id == rp->glineid)
407 rg_displaygline(np, rp);
408 controlreply(np, "Done.");
409
410 return CMD_OK;
411 }
412 }
413
414 int rg_glist(void *source, int cargc, char **cargv) {
415 nick *np = (nick *)source;
416 struct rg_struct *rp;
417
418 if(cargc) {
419 int erroroffset;
420 pcre *regex;
421 pcre_extra *hint;
422 const char *error;
423
424 if(!(regex = pcre_compile(cargv[0], RG_PCREFLAGS, &error, &erroroffset, NULL))) {
425 controlreply(np, "Error compiling expression %s at offset %d: %s", cargv[0], erroroffset, error);
426 return CMD_ERROR;
427 } else {
428 hint = pcre_study(regex, 0, &error);
429 if(error) {
430 controlreply(np, "Error studying expression %s: %s", cargv[0], error);
431 pcre_free(regex);
432 return CMD_ERROR;
433 }
434 }
435
436 rg_logevent(np, "regexglist", "%s", cargv[0]);
437 controlreply(np, "Mask Expires Set by Type Reason");
438 for(rp=rg_list;rp;rp=rp->next)
439 if(pcre_exec(regex, hint, rp->mask->content, rp->mask->length, 0, 0, NULL, 0) >= 0)
440 rg_displaygline(np, rp);
441
442 pcre_free(regex);
443 if(hint)
444 pcre_free(hint);
445
446 } else {
447 rg_logevent(np, "regexglist", "");
448 controlreply(np, "Mask Expires Set by Type Reason");
449 for(rp=rg_list;rp;rp=rp->next)
450 rg_displaygline(np, rp);
451 }
452
453 controlreply(np, "Done.");
454 return CMD_OK;
455 }
456
457 void rg_displaygline(nick *np, struct rg_struct *rp) { /* could be a macro? I'll assume the C compiler inlines it */
458 controlreply(np, "%-25s %-20s %-15s %-4d %s", rp->mask->content, longtoduration(rp->expires - time(NULL), 0), rp->setby->content, rp->type, rp->reason->content);
459 }
460
461 int rg_spew(void *source, int cargc, char **cargv) {
462 nick *np = (nick *)source, *tnp;
463 int counter = 0, erroroffset, hostlen, j;
464 pcre *regex;
465 pcre_extra *hint;
466 const char *error;
467 char hostname[RG_MASKLEN];
468
469 if(cargc < 1) {
470 controlreply(np, "syntax: regexspew [mask]");
471 return CMD_ERROR;
472 }
473
474 if(!(regex = pcre_compile(cargv[0], RG_PCREFLAGS, &error, &erroroffset, NULL))) {
475 controlreply(np, "Error compiling expression %s at offset %d: %s", cargv[0], erroroffset, error);
476 return CMD_ERROR;
477 } else {
478 hint = pcre_study(regex, 0, &error);
479 if(error) {
480 controlreply(np, "Error studying expression %s: %s", cargv[0], error);
481 pcre_free(regex);
482 return CMD_ERROR;
483 }
484 }
485
486 rg_logevent(np, "regexspew", "%s", cargv[0]);
487
488 for(j=0;j<NICKHASHSIZE;j++) {
489 for(tnp=nicktable[j];tnp;tnp=tnp->next) {
490 hostlen = RGBuildHostname(hostname, tnp);
491 if(pcre_exec(regex, hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
492 if(counter == rg_max_spew) {
493 controlreply(np, "Reached maximum spew count (%d) - aborting display.", rg_max_spew);
494 } else if (counter < rg_max_spew) {
495 controlreply(np, "%s=(%s) (%s)", hostname, IPtostr(tnp->ipaddress), tnp->realname->name->content);
496 }
497 counter++;
498 }
499 }
500 }
501 controlreply(np, "Done - %d matches.", counter);
502
503 pcre_free(regex);
504 if(hint)
505 pcre_free(hint);
506
507 return CMD_OK;
508 }
509
510 int rg_sqlconnect(char *dbhost, char *dbuser, char *dbpass, char *db, unsigned int port) {
511 mysql_init(&rg_sql);
512 if(!mysql_real_connect(&rg_sql, dbhost, dbuser, dbpass, db, port, NULL, 0))
513 return -1;
514 return 0;
515 }
516
517 void rg_sqldisconnect(void) {
518 mysql_close(&rg_sql);
519 }
520
521 void rg_sqlescape_string(char *dest, char *source, size_t length) {
522 if(length >= RG_QUERY_BUF_SIZE)
523 length = RG_QUERY_BUF_SIZE - 1;
524
525 mysql_escape_string(dest, source, length);
526 }
527
528 int rg_sqlquery(char *format, ...) {
529 char rg_sqlquery[RG_QUERY_BUF_SIZE];
530 va_list va;
531
532 va_start(va, format);
533 vsnprintf(rg_sqlquery, sizeof(rg_sqlquery), format, va);
534 va_end(va);
535
536 return mysql_query(&rg_sql, rg_sqlquery);
537 }
538
539 rg_sqlresult rg_sqlstoreresult(void) {
540 return mysql_store_result(&rg_sql);
541 }
542
543 rg_sqlrow rg_sqlgetrow(rg_sqlresult res) {
544 return mysql_fetch_row(res);
545 }
546
547 void rg_sqlfree(rg_sqlresult res) {
548 mysql_free_result(res);
549 }
550
551 void rg_startup(void) {
552 int j, hostlen;
553 nick *np;
554 struct rg_struct *rp;
555 struct rg_glinelist gll;
556 char hostname[RG_MASKLEN];
557
558 rg_initglinelist(&gll);
559
560 for(j=0;j<NICKHASHSIZE;j++) {
561 for(np=nicktable[j];np;np=np->next) {
562 hostlen = RGBuildMatchHostname(hostname, np);
563 for(rp=rg_list;rp;rp=rp->next) {
564 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
565 rg_dogline(&gll, np, rp, hostname);
566 break;
567 }
568 }
569 }
570 }
571
572 rg_flushglines(&gll);
573 }
574
575 void rg_freestruct(struct rg_struct *rp) {
576 freesstring(rp->mask);
577 freesstring(rp->setby);
578 freesstring(rp->reason);
579 pcre_free(rp->regex);
580 if(rp->hint)
581 pcre_free(rp->hint);
582 free(rp);
583 }
584
585 struct rg_struct *rg_newstruct(time_t expires) {
586 struct rg_struct *rp;
587
588 if (time(NULL) >= expires)
589 return NULL;
590
591 rp = (struct rg_struct*)malloc(sizeof(struct rg_struct));
592 if(rp) {
593 struct rg_struct *tp, *lp;
594 rp->id = 0;
595 rp->mask = NULL;
596 rp->setby = NULL;
597 rp->reason = NULL;
598 rp->expires = expires;
599 rp->type = 0;
600 rp->regex = NULL;
601 rp->hint = NULL;
602
603 for(lp=NULL,tp=rg_list;tp;lp=tp,tp=tp->next) {
604 if (expires <= tp->expires) { /* <= possible, slight speed increase */
605 rp->next = tp;
606 if (lp) {
607 lp->next = rp;
608 } else {
609 rg_list = rp;
610 }
611 break;
612 }
613 }
614 if (!tp) {
615 rp->next = NULL;
616 if (lp) {
617 lp->next = rp;
618 } else {
619 rg_list = rp;
620 }
621 }
622
623 }
624 return rp;
625 }
626
627 struct rg_struct *rg_newsstruct(char *id, char *mask, char *setby, char *reason, char *expires, char *type, time_t iexpires, int iid) {
628 struct rg_struct *newrow, *lp, *cp;
629 time_t rexpires;
630 char glineiddata[1024];
631 if (iexpires == 0) {
632 if(!protectedatoi(expires, &rexpires))
633 return NULL;
634 } else {
635 rexpires = iexpires;
636 }
637
638 newrow = rg_newstruct(rexpires);
639
640 if(newrow) {
641 const char *error;
642 int erroroffset;
643
644 if(!(newrow->regex = pcre_compile(mask, RG_PCREFLAGS, &error, &erroroffset, NULL))) {
645 Error("regexgline", ERR_WARNING, "Error compiling expression %s at offset %d: %s", mask, erroroffset, error);
646 goto dispose;
647 } else {
648 newrow->hint = pcre_study(newrow->regex, 0, &error);
649 if(error) {
650 Error("regexgline", ERR_WARNING, "Error studying expression %s: %s", mask, error);
651 pcre_free(newrow->regex);
652 goto dispose;
653 }
654 }
655
656 if (!iid) {
657 if(!protectedatoi(id, &newrow->id))
658 goto dispose2;
659 } else {
660 newrow->id = iid;
661 }
662
663 newrow->mask = getsstring(mask, RG_REGEXGLINE_MAX);
664 if(!newrow->mask) {
665 Error("regexgline", ERR_WARNING, "Error allocating memory for mask!");
666 goto dispose2;
667 }
668
669 newrow->setby = getsstring(setby, ACCOUNTLEN);
670 if(!newrow->setby) {
671 Error("regexgline", ERR_WARNING, "Error allocating memory for setby!");
672 goto dispose2;
673 }
674
675 newrow->reason = getsstring(reason, RG_REASON_MAX);
676 if(!newrow->reason) {
677 Error("regexgline", ERR_WARNING, "Error allocating memory for reason!");
678 goto dispose2;
679 }
680
681 if(!protectedatoi(type, &newrow->type))
682 newrow->type = 0; /* just in case */
683
684 snprintf(glineiddata, sizeof(glineiddata), "%s regexgline %s %s %s %d %d", mynumeric->content, mask, setby, reason, iexpires, newrow->type);
685 newrow->glineid = crc32(glineiddata);
686 }
687
688 return newrow;
689
690 dispose2:
691 if(newrow->mask)
692 freesstring(newrow->mask);
693 if(newrow->setby)
694 freesstring(newrow->setby);
695 if(newrow->reason)
696 freesstring(newrow->reason);
697 pcre_free(newrow->regex);
698 if(newrow->hint)
699 pcre_free(newrow->hint);
700
701 dispose:
702 for(lp=NULL,cp=rg_list;cp;lp=cp,cp=cp->next) {
703 if(newrow == cp) {
704 if(lp) {
705 lp->next = cp->next;
706 } else {
707 rg_list = cp->next;
708 }
709 free(newrow);
710 break;
711 }
712 }
713 return NULL;
714 }
715
716 void 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?! */
717 char hostname[RG_MASKLEN];
718 int usercount;
719
720 rg_loggline(rp, matched);
721
722 if (rp->type == 1) {
723 nick *tnp;
724
725 for(usercount=0,tnp=np->host->nicks;tnp;tnp=tnp->nextbyhost)
726 if(!ircd_strcmp(np->ident, tnp->ident))
727 usercount++;
728
729 snprintf(hostname, sizeof(hostname), "%s@%s", np->ident, IPtostr(np->ipaddress));
730 } else if (rp->type == 2) {
731 usercount = np->host->clonecount;
732 snprintf(hostname, sizeof(hostname), "*@%s", IPtostr(np->ipaddress));
733 } else if (rp->type == 3) {
734 struct rg_glinenode *nn = (struct rg_glinenode *)malloc(sizeof(struct rg_glinenode));
735 if(nn) {
736 nn->next = NULL;
737 if(gll->end) {
738 gll->end->next = nn;
739 gll->end = nn;
740 } else {
741 gll->start = nn;
742 gll->end = nn;
743 }
744 nn->np = np;
745 nn->reason = rp;
746 }
747 return;
748 }
749
750 if (usercount > rg_max_per_gline) { /* too many users on this host, so we're ignoring it */
751 /* controlchanmsg(findchannel("#twilightzone"), "Looks like I'm ignoring another Nomad gline: GL * +%s %d :%s due to %d clones\r\n", hostname, time(NULL) + rg_expiry_time, rp->reason->content, usercount); */
752 return;
753 }
754
755 irc_send("%s GL * +%s %d :%s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, rp->reason->content, rp->glineid);
756 }
757
758 void rg_logevent(nick *np, char *event, char *details, ...) {
759 char eeevent[RG_QUERY_BUF_SIZE], eedetails[RG_QUERY_BUF_SIZE], eemask[RG_QUERY_BUF_SIZE], eeaccount[RG_QUERY_BUF_SIZE];
760 char buf[513], account[ACCOUNTLEN + 1], mask[RG_MASKLEN];
761 int masklen;
762
763 va_list va;
764
765 va_start(va, details);
766 vsnprintf(buf, sizeof(buf), details, va);
767 va_end(va);
768
769 if(np) {
770 if (IsAccount(np)) {
771 strncpy(account, np->authname, sizeof(account) - 1);
772 account[sizeof(account) - 1] = '\0';
773 } else {
774 account[0] = '\0';
775 }
776 masklen = RGBuildMatchHostname(mask, np);
777 } else {
778 mask[0] = '\0';
779 masklen = 0;
780 }
781
782 rg_sqlescape_string(eeevent, event, strlen(event));
783 rg_sqlescape_string(eedetails, buf, strlen(buf));
784 rg_sqlescape_string(eeaccount, event, strlen(account));
785 rg_sqlescape_string(eemask, mask, masklen);
786
787 rg_sqlquery("INSERT INTO regexlogs (host, account, event, arg) VALUES ('%s', '%s', '%s', '%s')", eemask, eeaccount, eeevent, eedetails);
788 }
789
790 void rg_loggline(struct rg_struct *rg, char *matched) {
791 char eematched[RG_QUERY_BUF_SIZE];
792
793 rg_sqlescape_string(eematched, matched, strlen(matched));
794
795 rg_sqlquery("INSERT INTO regexglinelog (glineid, matched) VALUES (%d, '%s')", rg->id, eematched);
796 }