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