5 FUTURE: natural (sort of) language parsing
8 PPA: if multiple users match the same user@host or *@host it'll send multiple glines?!
11 #include "regexgline.h"
12 #include "../lib/version.h"
16 typedef struct rg_glinenode
{
18 struct rg_struct
*reason
;
20 struct rg_glinenode
*next
;
23 typedef struct rg_glinelist
{
24 struct rg_glinenode
*start
;
25 struct rg_glinenode
*end
;
28 typedef struct rg_delay
{
31 struct rg_struct
*reason
;
33 struct rg_delay
*next
;
38 void rg_setdelay(nick
*np
, struct rg_struct
*reason
, short punish
);
39 void rg_deletedelay(rg_delay
*delay
);
40 void rg_dodelay(void *arg
);
42 void rg_dogline(struct rg_glinelist
*gll
, nick
*np
, struct rg_struct
*rp
, char *matched
);
45 sstring
*max_casualties
, *max_spew
, *expiry_time
, *max_per_gline
;
47 max_casualties
= getcopyconfigitem("regexgline", "maxcasualties", RGStringise(RG_MAX_CASUALTIES_DEFAULT
), 8);
48 if(!protectedatoi(max_casualties
->content
, &rg_max_casualties
))
49 rg_max_casualties
= RG_MAX_CASUALTIES_DEFAULT
;
51 freesstring(max_casualties
);
53 max_spew
= getcopyconfigitem("regexgline", "maxspew", RGStringise(RG_MAX_SPEW_DEFAULT
), 8);
54 if(!protectedatoi(max_spew
->content
, &rg_max_spew
))
55 rg_max_spew
= RG_MAX_SPEW_DEFAULT
;
57 freesstring(max_spew
);
59 expiry_time
= getcopyconfigitem("regexgline", "expirytime", RGStringise(RG_EXPIRY_TIME_DEFAULT
), 8);
60 if(!protectedatoi(expiry_time
->content
, &rg_expiry_time
))
61 rg_expiry_time
= RG_EXPIRY_TIME_DEFAULT
;
63 freesstring(expiry_time
);
65 max_per_gline
= getcopyconfigitem("regexgline", "maxpergline", RGStringise(RG_MAX_PER_GLINE_DEFAULT
), 8);
66 if(!protectedatoi(max_per_gline
->content
, &rg_max_per_gline
))
67 rg_max_per_gline
= RG_MAX_PER_GLINE_DEFAULT
;
69 freesstring(max_per_gline
);
76 registercontrolhelpcmd("regexgline", NO_OPER
, 4, &rg_gline
, "Usage: regexgline <text>\nAdds a new regular expression pattern.");
77 registercontrolhelpcmd("regexdelgline", NO_OPER
, 1, &rg_delgline
, "Usage: regexdelgline <pattern>\nDeletes a regular expression pattern.");
78 registercontrolhelpcmd("regexglist", NO_OPER
, 1, &rg_glist
, "Usage: regexglist <pattern>\nLists regular expression patterns.");
79 registercontrolhelpcmd("regexspew", NO_OPER
, 1, &rg_spew
, "Usage: regexspew <pattern>\nLists users currently on the network which match the given pattern.");
80 registercontrolhelpcmd("regexidlookup", NO_OPER
, 1, &rg_idlist
, "Usage: regexidlookup <id>\nFinds a regular expression pattern by it's ID number.");
82 registerhook(HOOK_NICK_NEWNICK
, &rg_nick
);
83 registerhook(HOOK_NICK_RENAME
, &rg_nick
);
84 registerhook(HOOK_NICK_LOSTNICK
, &rg_lostnick
);
87 rg_schedule
= schedulerecurring(time(NULL
) + 1, 0, 1, rg_checkexpiry
, NULL
);
92 struct rg_struct
*gp
= rg_list
, *oldgp
;
93 rg_delay
*delay
, *delaynext
;
95 deregisterhook(HOOK_NICK_NEWNICK
, &rg_nick
);
96 deregisterhook(HOOK_NICK_RENAME
, &rg_nick
);
97 deregisterhook(HOOK_NICK_LOSTNICK
, &rg_lostnick
);
98 deregistercontrolcmd("regexspew", rg_spew
);
99 deregistercontrolcmd("regexglist", rg_glist
);
100 deregistercontrolcmd("regexdelgline", rg_delgline
);
101 deregistercontrolcmd("regexgline", rg_gline
);
102 deregistercontrolcmd("regexidlookup", rg_idlist
);
105 for(delay
=rg_delays
;delay
;delay
=delaynext
) {
106 delaynext
=delay
->next
;
107 deleteschedule(delay
->sch
, rg_dodelay
, delay
);
113 deleteschedule(rg_schedule
, &rg_checkexpiry
, NULL
);
117 for(gp
=rg_list
;gp
;) {
120 rg_freestruct(oldgp
);
127 void rg_checkexpiry(void *arg
) {
128 struct rg_struct
*rp
= rg_list
, *lp
= NULL
;
129 time_t current
= time(NULL
);
132 if (current
>= rp
->expires
) {
133 rg_sqlquery("DELETE FROM regexglines WHERE id = %d", rp
->id
);
150 void rg_setdelay(nick
*np
, rg_struct
*reason
, short punish
) {
152 delay
= (rg_delay
*)malloc(sizeof(rg_delay
));
156 killuser(NULL
, np
, "%s (ID: %08lx)", reason
->reason
->content
, reason
->glineid
);
161 delay
->reason
= reason
;
162 delay
->punish
= punish
;
163 delay
->next
= rg_delays
;
166 delay
->sch
= scheduleoneshot(time(NULL
) + (RG_MINIMUM_DELAY_TIME
+ (rand() % RG_MAXIMUM_RAND_TIME
)), rg_dodelay
, delay
);
169 void rg_deletedelay(rg_delay
*delay
) {
170 rg_delay
*temp
, *prev
;
172 for (temp
=rg_delays
;temp
;temp
=temp
->next
) {
175 rg_delays
= temp
->next
;
177 prev
->next
= temp
->next
;
187 void rg_dodelay(void *arg
) {
188 rg_delay
*delay
= (rg_delay
*)arg
;
189 char hostname
[RG_MASKLEN
];
190 int hostlen
, usercount
= 0;
192 /* User or regex gline no longer exists */
193 if((!delay
->np
) || (!delay
->reason
)) {
194 rg_deletedelay(delay
);
198 hostlen
= RGBuildHostname(hostname
, delay
->np
);
200 /* User has wisely changed nicknames */
201 if(pcre_exec(delay
->reason
->regex
, delay
->reason
->hint
, hostname
, hostlen
, 0, 0, NULL
, 0) < 0) {
202 rg_deletedelay(delay
);
206 if (delay
->reason
->type
== 5) {
207 usercount
= delay
->np
->host
->clonecount
;
208 snprintf(hostname
, sizeof(hostname
), "*@%s", IPtostr(delay
->np
->p_ipaddr
));
211 if((delay
->reason
->type
== 4) || (usercount
> rg_max_per_gline
)) {
214 for(usercount
=0,tnp
=delay
->np
->host
->nicks
;tnp
;tnp
=tnp
->nextbyhost
)
215 if(!ircd_strcmp(delay
->np
->ident
, tnp
->ident
))
218 snprintf(hostname
, sizeof(hostname
), "%s@%s", delay
->np
->ident
, IPtostr(delay
->np
->p_ipaddr
));
221 if ((delay
->reason
->type
== 6) || (usercount
> rg_max_per_gline
)) {
222 if (IsAccount(delay
->np
)) {
223 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s/%s matched delayed kill regex %08lx", delay
->np
->nick
, delay
->np
->ident
, delay
->np
->host
->name
->content
, delay
->np
->authname
, delay
->reason
->glineid
);
225 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s matched delayed kill regex %08lx", delay
->np
->nick
, delay
->np
->ident
, delay
->np
->host
->name
->content
, delay
->reason
->glineid
);
227 killuser(NULL
, delay
->np
, "%s (ID: %08lx)", delay
->reason
->reason
->content
, delay
->reason
->glineid
);
231 if ((delay
->reason
->type
<= 3) || (delay
->reason
->type
>= 6))
234 if (delay
->reason
->type
== 4) {
235 if (IsAccount(delay
->np
)) {
236 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s/%s matched delayed user@host gline regex %08lx (hit %d user%s)", delay
->np
->nick
, delay
->np
->ident
, delay
->np
->host
->name
->content
, delay
->np
->authname
, delay
->reason
->glineid
, usercount
, (usercount
!=1)?"s":"");
238 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s matched delayed user@host gline regex %08lx (hit %d user%s)", delay
->np
->nick
, delay
->np
->ident
, delay
->np
->host
->name
->content
, delay
->reason
->glineid
, usercount
, (usercount
!=1)?"s":"");
241 if (IsAccount(delay
->np
)) {
242 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s/%s matched delayed *@host gline regex %08lx (hit %d user%s)", delay
->np
->nick
, delay
->np
->ident
, delay
->np
->host
->name
->content
, delay
->np
->authname
, delay
->reason
->glineid
, usercount
, (usercount
!=1)?"s":"");
244 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s matched delayed *@host gline regex %08lx (hit %d user%s)", delay
->np
->nick
, delay
->np
->ident
, delay
->np
->host
->name
->content
, delay
->reason
->glineid
, usercount
, (usercount
!=1)?"s":"");
248 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
);
249 rg_deletedelay(delay
);
252 void rg_initglinelist(struct rg_glinelist
*gll
) {
257 void rg_flushglines(struct rg_glinelist
*gll
) {
258 struct rg_glinenode
*nn
, *pn
;
259 for(nn
=gll
->start
;nn
;nn
=pn
) {
261 if(nn
->punish
== 3) {
262 if ( IsAccount(nn
->np
) ) {
263 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s/%s matched kill regex %08lx", nn
->np
->nick
, nn
->np
->ident
, nn
->np
->host
->name
->content
, nn
->np
->authname
, nn
->reason
->glineid
);
265 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s matched kill regex %08lx", nn
->np
->nick
, nn
->np
->ident
, nn
->np
->host
->name
->content
, nn
->reason
->glineid
);
268 killuser(NULL
, nn
->np
, "%s (ID: %08lx)", nn
->reason
->reason
->content
, nn
->reason
->glineid
);
269 } else if ((nn
->punish
== 4) || (nn
->punish
== 5) || (nn
->punish
== 6)) {
270 rg_setdelay(nn
->np
, nn
->reason
, nn
->punish
);
275 rg_initglinelist(gll
);
278 int rg_dbconnect(void) {
279 sstring
*dbhost
, *dbusername
, *dbpassword
, *dbdatabase
, *dbport
;
281 dbhost
= getcopyconfigitem("regexgline", "dbhost", "localhost", HOSTLEN
);
282 dbusername
= getcopyconfigitem("regexgline", "dbusername", "regexgline", 20);
283 dbpassword
= getcopyconfigitem("regexgline", "dbpassword", "moo", 20);
284 dbdatabase
= getcopyconfigitem("regexgline", "dbdatabase", "regexgline", 20);
285 dbport
= getcopyconfigitem("regexgline", "dbport", "3306", 8);
287 if(rg_sqlconnect(dbhost
->content
, dbusername
->content
, dbpassword
->content
, dbdatabase
->content
, strtol(dbport
->content
, NULL
, 10))) {
288 Error("regexgline", ERR_FATAL
, "Cannot connect to database host!");
289 return 1; /* PPA: splidge: do something here 8]! */
295 freesstring(dbusername
);
296 freesstring(dbpassword
);
297 freesstring(dbdatabase
);
303 int rg_dbload(void) {
304 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
);
305 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
);
306 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
);
308 if(!rg_sqlquery("SELECT id, gline, setby, reason, expires, type FROM regexglines")) {
310 if((res
= rg_sqlstoreresult())) {
312 while((row
= rg_sqlgetrow(res
))) {
313 if (!rg_newsstruct(row
[0], row
[1], row
[2], row
[3], row
[4], row
[5], 0, 0))
314 rg_sqlquery("DELETE FROM regexglines WHERE id = %s", row
[0]);
323 void rg_nick(int hooknum
, void *arg
) {
324 nick
*np
= (nick
*)arg
;
325 struct rg_struct
*rp
;
326 char hostname
[RG_MASKLEN
];
328 struct rg_glinelist gll
;
330 rg_initglinelist(&gll
);
332 hostlen
= RGBuildHostname(hostname
, np
);
334 if(IsOper(np
) || IsService(np
) || IsXOper(np
))
337 for(rp
=rg_list
;rp
;rp
=rp
->next
) {
338 if(pcre_exec(rp
->regex
, rp
->hint
, hostname
, hostlen
, 0, 0, NULL
, 0) >= 0) {
339 rg_dogline(&gll
, np
, rp
, hostname
);
344 rg_flushglines(&gll
);
347 void rg_lostnick(int hooknum
, void *arg
) {
348 nick
*np
= (nick
*)arg
;
351 /* Cleanup the delays */
352 for(delay
=rg_delays
;delay
;delay
=delay
->next
)
357 int rg_gline(void *source
, int cargc
, char **cargv
) {
358 nick
*np
= (nick
*)source
, *tnp
;
360 const char *expirybuf
;
361 int expiry
, count
, j
, hostlen
;
362 struct rg_struct
*rp
;
363 struct rg_glinelist gll
;
365 char eemask
[RG_QUERY_BUF_SIZE
], eesetby
[RG_QUERY_BUF_SIZE
], eereason
[RG_QUERY_BUF_SIZE
];
366 char hostname
[RG_MASKLEN
];
371 if ((strlen(cargv
[2]) != 1) || ((cargv
[2][0] != '1') && (cargv
[2][0] != '2') && (cargv
[2][0] != '3') && (cargv
[2][0] != '4') && (cargv
[2][0] != '5') && (cargv
[2][0] != '6'))) {
372 controlreply(np
, "Invalid type specified!");
376 if (!(expiry
= durationtolong(cargv
[1]))) {
377 controlreply(np
, "Invalid duration specified!");
381 for(rp
=rg_list
;rp
;rp
=rp
->next
) {
382 if (RGMasksEqual(rp
->mask
->content
, cargv
[0])) {
383 controlreply(np
, "That regexp gline already exists!");
388 if (rg_sanitycheck(cargv
[0], &count
)) {
389 controlreply(np
, "Error in expression.");
391 } else if (count
< 0) {
392 controlreply(np
, "That expression would hit too many users (%d)!", -count
);
396 realexpiry
= expiry
+ time(NULL
);
398 rg_sqlescape_string(eemask
, cargv
[0], strlen(cargv
[0]));
399 rg_sqlescape_string(eesetby
, np
->nick
, strlen(np
->nick
));
400 rg_sqlescape_string(eereason
, cargv
[3], strlen(cargv
[3]));
402 rg_sqlquery("INSERT INTO regexglines (gline, setby, reason, expires, type) VALUES ('%s', '%s', '%s', %d, %s)", eemask
, eesetby
, eereason
, realexpiry
, cargv
[2]);
403 if (!rg_sqlquery("SELECT LAST_INSERT_ID()")) {
405 if((res
= rg_sqlstoreresult())) {
407 row
= rg_sqlgetrow(res
);
409 int id
= atoi(row
[0]);
413 rp
= rg_newsstruct(row
[0], cargv
[0], np
->nick
, cargv
[3], "", cargv
[2], realexpiry
, 0);
417 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]);
418 controlreply(np
, "Error allocating - regexgline NOT ADDED.");
423 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]);
424 controlreply(np
, "Error selecting ID from database - regexgline NOT ADDED.");
428 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]);
429 controlreply(np
, "Error fetching ID from database - regexgline NOT ADDED.");
433 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]);
434 controlreply(np
, "Error executing query - regexgline NOT ADDED.");
438 rg_initglinelist(&gll
);
440 for(j
=0;j
<NICKHASHSIZE
;j
++) {
441 for(tnp
=nicktable
[j
];tnp
;tnp
=tnp
->next
) {
442 if(IsOper(tnp
) || IsService(tnp
) || IsXOper(tnp
))
445 hostlen
= RGBuildHostname(hostname
, tnp
);
446 if(pcre_exec(rp
->regex
, rp
->hint
, hostname
, hostlen
, 0, 0, NULL
, 0) >= 0)
447 rg_dogline(&gll
, tnp
, rp
, hostname
);
451 rg_flushglines(&gll
);
453 expirybuf
= longtoduration(expiry
, 0);
455 rg_logevent(np
, "regexgline", "%s %d %d %s", cargv
[0], expiry
, count
, cargv
[3]);
456 controlreply(np
, "Added regexgline: %s (expires in: %s, hit %d user%s): %s", cargv
[0], expirybuf
, count
, (count
!=1)?"s":"", cargv
[3]);
457 /* If we are using NO, can we safely assume the user is authed here and use ->authname? */
458 controlwall(NO_OPER
, NL_GLINES
, "%s!%s@%s/%s added regexgline: %s (expires in: %s, hit %d user%s): %s", np
->nick
, np
->ident
, np
->host
->name
->content
, np
->authname
, cargv
[0], expirybuf
, count
, (count
!=1)?"s":"", cargv
[3]);
463 int rg_sanitycheck(char *mask
, int *count
) {
465 char hostname
[RG_MASKLEN
];
466 int erroroffset
, hostlen
, j
, masklen
= strlen(mask
);
471 if((masklen
< RG_MIN_MASK_LEN
) || (masklen
> RG_REGEXGLINE_MAX
))
474 if(!(regex
= pcre_compile(mask
, RG_PCREFLAGS
, &error
, &erroroffset
, NULL
))) {
475 Error("regexgline", ERR_WARNING
, "Error compiling expression %s at offset %d: %s", mask
, erroroffset
, error
);
478 hint
= pcre_study(regex
, 0, &error
);
480 Error("regexgline", ERR_WARNING
, "Error studying expression %s: %s", mask
, error
);
487 for(j
=0;j
<NICKHASHSIZE
;j
++) {
488 for(np
=nicktable
[j
];np
;np
=np
->next
) {
489 hostlen
= RGBuildHostname(hostname
, np
);
490 if(pcre_exec(regex
, hint
, hostname
, hostlen
, 0, 0, NULL
, 0) >= 0) {
500 if(*count
>= rg_max_casualties
)
506 int rg_delgline(void *source
, int cargc
, char **cargv
) {
507 nick
*np
= (nick
*)source
;
509 struct rg_struct
*rp
= rg_list
, *last
= NULL
;
515 rg_logevent(np
, "regexdelgline", "%s", cargv
[0]);
517 if(RGMasksEqual(rp
->mask
->content
, cargv
[0])) {
520 /* Cleanup the delays */
521 for(delay
=rg_delays
;delay
;delay
=delay
->next
)
522 if(delay
->reason
==rp
)
523 delay
->reason
= NULL
;
525 rg_sqlquery("DELETE FROM regexglines WHERE id = %d", rp
->id
);
527 last
->next
= rp
->next
;
541 controlreply(np
, "Deleted (matched: %d).", count
);
542 /* If we are using NO, can we safely assume the user is authed here and use ->authname? */
543 controlwall(NO_OPER
, NL_GLINES
, "%s!%s@%s/%s removed regexgline: %s", np
->nick
, np
->ident
, np
->host
->name
->content
, np
->authname
, cargv
[0]);
545 controlreply(np
, "No glines matched: %s", cargv
[0]);
550 int rg_idlist(void *source
, int cargc
, char **cargv
) {
551 nick
*np
= (nick
*)source
;
555 } else if (strlen(cargv
[0]) != 8) {
556 controlreply(np
, "Invalid gline id!");
559 struct rg_struct
*rp
;
560 unsigned long id
= 0;
564 if(0xff == rc_hexlookup
[(int)cargv
[0][i
]]) {
565 controlreply(np
, "Invalid gline id!");
568 id
= (id
<< 4) | rc_hexlookup
[(int)cargv
[0][i
]];
572 controlreply(np
, "Mask Expires Set by Type Reason");
573 for(rp
=rg_list
;rp
;rp
=rp
->next
)
574 if(id
== rp
->glineid
)
575 rg_displaygline(np
, rp
);
576 controlreply(np
, "Done.");
582 int rg_glist(void *source
, int cargc
, char **cargv
) {
583 nick
*np
= (nick
*)source
;
584 struct rg_struct
*rp
;
592 if(!(regex
= pcre_compile(cargv
[0], RG_PCREFLAGS
, &error
, &erroroffset
, NULL
))) {
593 controlreply(np
, "Error compiling expression %s at offset %d: %s", cargv
[0], erroroffset
, error
);
596 hint
= pcre_study(regex
, 0, &error
);
598 controlreply(np
, "Error studying expression %s: %s", cargv
[0], error
);
604 rg_logevent(np
, "regexglist", "%s", cargv
[0]);
605 controlreply(np
, "Mask Expires Set by Type Reason");
606 for(rp
=rg_list
;rp
;rp
=rp
->next
)
607 if(pcre_exec(regex
, hint
, rp
->mask
->content
, rp
->mask
->length
, 0, 0, NULL
, 0) >= 0)
608 rg_displaygline(np
, rp
);
615 rg_logevent(np
, "regexglist", "");
616 controlreply(np
, "Mask Expires Set by Type Reason");
617 for(rp
=rg_list
;rp
;rp
=rp
->next
)
618 rg_displaygline(np
, rp
);
621 controlreply(np
, "Done.");
625 void rg_displaygline(nick
*np
, struct rg_struct
*rp
) { /* could be a macro? I'll assume the C compiler inlines it */
626 controlreply(np
, "%-25s %-20s %-15s %-4d %s", rp
->mask
->content
, longtoduration(rp
->expires
- time(NULL
), 0), rp
->setby
->content
, rp
->type
, rp
->reason
->content
);
629 int rg_spew(void *source
, int cargc
, char **cargv
) {
630 nick
*np
= (nick
*)source
, *tnp
;
631 int counter
= 0, erroroffset
, hostlen
, j
;
635 char hostname
[RG_MASKLEN
];
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
);
646 hint
= pcre_study(regex
, 0, &error
);
648 controlreply(np
, "Error studying expression %s: %s", cargv
[0], error
);
654 rg_logevent(np
, "regexspew", "%s", cargv
[0]);
656 for(j
=0;j
<NICKHASHSIZE
;j
++) {
657 for(tnp
=nicktable
[j
];tnp
;tnp
=tnp
->next
) {
658 hostlen
= RGBuildHostname(hostname
, tnp
);
659 pcreret
= pcre_exec(regex
, hint
, hostname
, hostlen
, 0, 0, ovector
, sizeof(ovector
) / sizeof(int));
661 if(counter
== rg_max_spew
) {
662 controlreply(np
, "Reached maximum spew count (%d) - aborting display.", rg_max_spew
);
663 } else if (counter
< rg_max_spew
) {
664 /* 15 should be number of bolds */
665 char boldbuf
[RG_MASKLEN
+ 15], *tp
, *fp
, *realname
= NULL
;
667 for(tp
=hostname
,fp
=boldbuf
;*tp
;) {
668 if(tp
- hostname
== ovector
[0]) {
672 if(tp
- hostname
== ovector
[1]) {
691 controlreply(np
, "%s (%s) (%dc)", boldbuf
, realname
, tnp
->channels
->cursi
);
697 controlreply(np
, "Done - %d matches.", counter
);
706 int rg_sqlconnect(char *dbhost
, char *dbuser
, char *dbpass
, char *db
, unsigned int port
) {
708 if(!mysql_real_connect(&rg_sql
, dbhost
, dbuser
, dbpass
, db
, port
, NULL
, 0))
713 void rg_sqldisconnect(void) {
714 mysql_close(&rg_sql
);
717 void rg_sqlescape_string(char *dest
, char *source
, size_t length
) {
718 if(length
>= RG_QUERY_BUF_SIZE
)
719 length
= RG_QUERY_BUF_SIZE
- 1;
721 mysql_real_escape_string(&rg_sql
, dest
, source
, length
);
724 int rg_sqlquery(char *format
, ...) {
725 char rg_sqlquery
[RG_QUERY_BUF_SIZE
];
728 va_start(va
, format
);
729 vsnprintf(rg_sqlquery
, sizeof(rg_sqlquery
), format
, va
);
732 return mysql_query(&rg_sql
, rg_sqlquery
);
735 rg_sqlresult
rg_sqlstoreresult(void) {
736 return mysql_store_result(&rg_sql
);
739 rg_sqlrow
rg_sqlgetrow(rg_sqlresult res
) {
740 return mysql_fetch_row(res
);
743 void rg_sqlfree(rg_sqlresult res
) {
744 mysql_free_result(res
);
747 void rg_startup(void) {
750 struct rg_struct
*rp
;
751 struct rg_glinelist gll
;
752 char hostname
[RG_MASKLEN
];
754 rg_initglinelist(&gll
);
756 for(j
=0;j
<NICKHASHSIZE
;j
++) {
757 for(np
=nicktable
[j
];np
;np
=np
->next
) {
758 if(IsOper(np
) || IsService(np
) || IsXOper(np
))
760 hostlen
= RGBuildHostname(hostname
, np
);
761 for(rp
=rg_list
;rp
;rp
=rp
->next
) {
762 if(pcre_exec(rp
->regex
, rp
->hint
, hostname
, hostlen
, 0, 0, NULL
, 0) >= 0) {
763 rg_dogline(&gll
, np
, rp
, hostname
);
770 rg_flushglines(&gll
);
773 void rg_freestruct(struct rg_struct
*rp
) {
774 freesstring(rp
->mask
);
775 freesstring(rp
->setby
);
776 freesstring(rp
->reason
);
777 pcre_free(rp
->regex
);
783 struct rg_struct
*rg_newstruct(time_t expires
) {
784 struct rg_struct
*rp
;
786 if (time(NULL
) >= expires
)
789 rp
= (struct rg_struct
*)malloc(sizeof(struct rg_struct
));
791 struct rg_struct
*tp
, *lp
;
796 rp
->expires
= expires
;
801 for(lp
=NULL
,tp
=rg_list
;tp
;lp
=tp
,tp
=tp
->next
) {
802 if (expires
<= tp
->expires
) { /* <= possible, slight speed increase */
825 struct rg_struct
*rg_newsstruct(char *id
, char *mask
, char *setby
, char *reason
, char *expires
, char *type
, time_t iexpires
, int iid
) {
826 struct rg_struct
*newrow
, *lp
, *cp
;
828 char glineiddata
[1024];
831 if(!protectedatoi(expires
, &qexpires
))
833 rexpires
= (time_t)qexpires
;
838 newrow
= rg_newstruct(rexpires
);
844 if(!(newrow
->regex
= pcre_compile(mask
, RG_PCREFLAGS
, &error
, &erroroffset
, NULL
))) {
845 Error("regexgline", ERR_WARNING
, "Error compiling expression %s at offset %d: %s", mask
, erroroffset
, error
);
848 newrow
->hint
= pcre_study(newrow
->regex
, 0, &error
);
850 Error("regexgline", ERR_WARNING
, "Error studying expression %s: %s", mask
, error
);
851 pcre_free(newrow
->regex
);
857 if(!protectedatoi(id
, &newrow
->id
))
863 newrow
->mask
= getsstring(mask
, RG_REGEXGLINE_MAX
);
865 Error("regexgline", ERR_WARNING
, "Error allocating memory for mask!");
869 newrow
->setby
= getsstring(setby
, ACCOUNTLEN
);
871 Error("regexgline", ERR_WARNING
, "Error allocating memory for setby!");
875 newrow
->reason
= getsstring(reason
, RG_REASON_MAX
);
876 if(!newrow
->reason
) {
877 Error("regexgline", ERR_WARNING
, "Error allocating memory for reason!");
881 if(!protectedatoi(type
, &newrow
->type
))
882 newrow
->type
= 0; /* just in case */
884 snprintf(glineiddata
, sizeof(glineiddata
), "%s regexgline %s %s %s %d %d", mynumeric
->content
, mask
, setby
, reason
, (int)iexpires
, newrow
->type
);
885 newrow
->glineid
= crc32(glineiddata
);
892 freesstring(newrow
->mask
);
894 freesstring(newrow
->setby
);
896 freesstring(newrow
->reason
);
897 pcre_free(newrow
->regex
);
899 pcre_free(newrow
->hint
);
902 for(lp
=NULL
,cp
=rg_list
;cp
;lp
=cp
,cp
=cp
->next
) {
916 int __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?! */
917 char hostname
[RG_MASKLEN
];
923 usercount
= np
->host
->clonecount
;
924 snprintf(hostname
, sizeof(hostname
), "*@%s", IPtostr(np
->p_ipaddr
));
927 if ((rp
->type
== 1) || (usercount
> rg_max_per_gline
)) {
930 for(usercount
=0,tnp
=np
->host
->nicks
;tnp
;tnp
=tnp
->nextbyhost
)
931 if(!ircd_strcmp(np
->ident
, tnp
->ident
))
934 snprintf(hostname
, sizeof(hostname
), "%s@%s", np
->ident
, IPtostr(np
->p_ipaddr
));
937 if ((rp
->type
>= 3) || (usercount
> rg_max_per_gline
)) {
938 struct rg_glinenode
*nn
= (struct rg_glinenode
*)malloc(sizeof(struct rg_glinenode
));
954 nn
->punish
= rp
->type
;
960 if ((rp
->type
<= 0) || (rp
->type
>= 3))
965 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s/%s matched user@host gline regex %08lx (hit %d user%s)", np
->nick
, np
->ident
, np
->host
->name
->content
, np
->authname
, rp
->glineid
, usercount
, (usercount
!=1)?"s":"");
967 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s matched user@host gline regex %08lx (hit %d user%s)", np
->nick
, np
->ident
, np
->host
->name
->content
, rp
->glineid
, usercount
, (usercount
!=1)?"s":"");
971 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s/%s matched *@host gline regex %08lx (hit %d user%s)", np
->nick
, np
->ident
, np
->host
->name
->content
, np
->authname
, rp
->glineid
, usercount
, (usercount
!=1)?"s":"");
973 controlwall(NO_OPER
, NL_HITS
, "%s!%s@%s matched *@host gline regex %08lx (hit %d user%s)", np
->nick
, np
->ident
, np
->host
->name
->content
, rp
->glineid
, usercount
, (usercount
!=1)?"s":"");
977 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
);
981 static int floodprotection
= 0;
982 static int lastfloodspam
= 0;
984 void rg_dogline(struct rg_glinelist
*gll
, nick
*np
, struct rg_struct
*rp
, char *matched
) {
987 if(t
> floodprotection
) {
989 } else if((floodprotection
- t
) / 8 > RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC
) {
990 if(t
> lastfloodspam
+ 3600) {
991 channel
*cp
= findchannel("#twilightzone");
993 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
);
994 controlwall(NO_OPER
, NL_MANAGEMENT
, "WARNING! REGEXGLINE DISABLED FOR AN HOUR DUE TO NETWORK WIDE LOOKING GLINE!");
996 floodprotection
= t
+ RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC
* 3600 * 8;
1001 floodprotection
+=__rg_dogline(gll
, np
, rp
, matched
);
1004 void rg_logevent(nick
*np
, char *event
, char *details
, ...) {
1005 char eeevent
[RG_QUERY_BUF_SIZE
], eedetails
[RG_QUERY_BUF_SIZE
], eemask
[RG_QUERY_BUF_SIZE
], eeaccount
[RG_QUERY_BUF_SIZE
];
1006 char buf
[513], account
[ACCOUNTLEN
+ 1], mask
[RG_MASKLEN
];
1012 va_start(va
, details
);
1013 vsnprintf(buf
, sizeof(buf
), details
, va
);
1017 if (IsAccount(np
)) {
1018 strncpy(account
, np
->authname
, sizeof(account
) - 1);
1019 account
[sizeof(account
) - 1] = '\0';
1023 masklen
= RGBuildHostname(mask
, np
);
1029 rg_sqlescape_string(eeevent
, event
, strlen(event
));
1030 rg_sqlescape_string(eedetails
, buf
, strlen(buf
));
1031 rg_sqlescape_string(eeaccount
, event
, strlen(account
));
1032 rg_sqlescape_string(eemask
, mask
, masklen
);
1034 rg_sqlquery("INSERT INTO regexlogs (host, account, event, arg) VALUES ('%s', '%s', '%s', '%s')", eemask
, eeaccount
, eeevent
, eedetails
);
1037 void rg_loggline(struct rg_struct
*rg
, nick
*np
) {
1038 char eenick
[RG_QUERY_BUF_SIZE
], eeuser
[RG_QUERY_BUF_SIZE
], eehost
[RG_QUERY_BUF_SIZE
], eereal
[RG_QUERY_BUF_SIZE
];
1041 rg_sqlescape_string(eenick
, np
->nick
, strlen(np
->nick
));
1042 rg_sqlescape_string(eeuser
, np
->ident
, strlen(np
->ident
));
1043 rg_sqlescape_string(eehost
, np
->host
->name
->content
, strlen(np
->host
->name
->content
));
1044 rg_sqlescape_string(eereal
, np
->realname
->name
->content
, strlen(np
->realname
->name
->content
));
1046 rg_sqlquery("INSERT INTO regexglinelog (glineid, nickname, username, hostname, realname) VALUES (%d, '%s', '%s', '%s', '%s')", rg
->id
, eenick
, eeuser
, eehost
, eereal
);