mirror of
https://github.com/matrix-construct/construct
synced 2024-11-03 04:18:55 +01:00
61b517ca3c
* To benefit from the precompiled-header (PCH) it MUST provide "the first C token." Advantages: Never worry about the include stack again. Remember, this means one less thing for random module developers, community people learning C++, and new developers to deal with. It should reduce the learning curve and barrier for participation. Disadvantages: Makes overall compilation a bit slower, especially without any additional work to improve it again. There are several opportunities, places where the PCH is probably being ignored, etc that can be addressed.
268 lines
6.4 KiB
C++
268 lines
6.4 KiB
C++
/*
|
|
* Extban that combines other extbans.
|
|
*
|
|
* Basic example:
|
|
* $&:~a,m:*!*@gateway/web/cgi-irc*
|
|
* Which means: match unidentified webchat users.
|
|
* ("m" is another new extban type, which just does a normal match).
|
|
*
|
|
* More complicated example:
|
|
* $&:~a,|:(m:*!*@gateway/web/foo,m:*!*@gateway/web/bar)
|
|
* Which means: unidentified and using the foo or bar gateway.
|
|
*
|
|
* Rules:
|
|
*
|
|
* - Optional pair of parens around data.
|
|
*
|
|
* - component bans are separated by commas, but commas between
|
|
* matching pairs of parens are skipped.
|
|
*
|
|
* - Unbalanced parens are an error.
|
|
*
|
|
* - Parens, commas and backslashes can be escaped by backslashes.
|
|
*
|
|
* - A backslash before any character other than a paren or backslash
|
|
* is just a backslash (backslash and character are both used).
|
|
*
|
|
* - Non-existant extbans are invalid.
|
|
* This is primarily for consistency with non-combined bans:
|
|
* the ircd does not let you set +b $f unless the 'f' extban is loaded,
|
|
* so setting $&:f should be impossible too.
|
|
*
|
|
* Issues:
|
|
* - Backslashes double inside nested bans.
|
|
* Hopefully acceptable because they should be rare.
|
|
*
|
|
* - Is performance good enough?
|
|
* I suspect it is, but have done no load testing.
|
|
*/
|
|
|
|
using namespace ircd;
|
|
|
|
static const char extb_desc[] = "Combination ($&, $|) extban types";
|
|
|
|
// #define MOD_DEBUG(s) sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, (s))
|
|
#define MOD_DEBUG(s)
|
|
#define RETURN_INVALID { recursion_depth--; return EXTBAN_INVALID; }
|
|
|
|
static int _modinit(void);
|
|
static void _moddeinit(void);
|
|
static int eb_or(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);
|
|
static int eb_and(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);
|
|
static int eb_combi(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type, bool is_and);
|
|
static int recursion_depth = 0;
|
|
|
|
DECLARE_MODULE_AV2(extb_extended, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, extb_desc);
|
|
|
|
static int
|
|
_modinit(void)
|
|
{
|
|
extban_table['&'] = eb_and;
|
|
extban_table['|'] = eb_or;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_moddeinit(void)
|
|
{
|
|
extban_table['&'] = NULL;
|
|
extban_table['|'] = NULL;
|
|
}
|
|
|
|
static int eb_or(const char *data, struct Client *client_p,
|
|
struct Channel *chptr, long mode_type)
|
|
{
|
|
return eb_combi(data, client_p, chptr, mode_type, false);
|
|
}
|
|
|
|
static int eb_and(const char *data, struct Client *client_p,
|
|
struct Channel *chptr, long mode_type)
|
|
{
|
|
return eb_combi(data, client_p, chptr, mode_type, true);
|
|
}
|
|
|
|
static int eb_combi(const char *data, struct Client *client_p,
|
|
struct Channel *chptr, long mode_type, bool is_and)
|
|
{
|
|
const char *p, *banend;
|
|
bool have_result = false;
|
|
int allowed_nodes = 11;
|
|
size_t datalen;
|
|
|
|
if (recursion_depth >= 5) {
|
|
MOD_DEBUG("combo invalid: recursion depth too high");
|
|
return EXTBAN_INVALID;
|
|
}
|
|
|
|
if (EmptyString(data)) {
|
|
MOD_DEBUG("combo invalid: empty data");
|
|
return EXTBAN_INVALID;
|
|
}
|
|
|
|
datalen = strlen(data);
|
|
if (datalen > BANLEN) {
|
|
/* I'd be sad if this ever happened, but if it does we
|
|
* could overflow the buffer used below, so...
|
|
*/
|
|
MOD_DEBUG("combo invalid: > BANLEN");
|
|
return EXTBAN_INVALID;
|
|
}
|
|
banend = data + datalen;
|
|
|
|
if (data[0] == '(') {
|
|
p = data + 1;
|
|
banend--;
|
|
if (*banend != ')') {
|
|
MOD_DEBUG("combo invalid: starting but no closing paren");
|
|
return EXTBAN_INVALID;
|
|
}
|
|
} else {
|
|
p = data;
|
|
}
|
|
|
|
/* Empty combibans are invalid. */
|
|
if (banend == p) {
|
|
MOD_DEBUG("combo invalid: no data (after removing parens)");
|
|
return EXTBAN_INVALID;
|
|
}
|
|
|
|
/* Implementation note:
|
|
* I want it to be impossible to set a syntactically invalid combi-ban.
|
|
* (mismatched parens).
|
|
* That is: valid_extban should return false for those.
|
|
* Ideally we do not parse the entire ban when actually matching it:
|
|
* we can just short-circuit if we already know the ban is valid.
|
|
* Unfortunately there is no separate hook or mode_type for validation,
|
|
* so we always keep parsing even after we have determined a result.
|
|
*/
|
|
|
|
recursion_depth++;
|
|
|
|
while (--allowed_nodes) {
|
|
bool invert = false;
|
|
char *child_data, child_data_buf[BANLEN];
|
|
ExtbanFunc f;
|
|
|
|
if (*p == '~') {
|
|
invert = true;
|
|
p++;
|
|
if (p == banend) {
|
|
MOD_DEBUG("combo invalid: no data after ~");
|
|
RETURN_INVALID;
|
|
}
|
|
}
|
|
|
|
f = extban_table[(unsigned char) *p++];
|
|
if (!f) {
|
|
MOD_DEBUG("combo invalid: non-existant child extban");
|
|
RETURN_INVALID;
|
|
}
|
|
|
|
if (*p == ':') {
|
|
unsigned int parencount = 0;
|
|
bool escaped = false, done = false;
|
|
char *o;
|
|
|
|
p++;
|
|
|
|
/* Possible optimization: we can skip the actual copy if
|
|
* we already have_result.
|
|
*/
|
|
o = child_data = child_data_buf;
|
|
while (true) {
|
|
if (p == banend) {
|
|
if (parencount) {
|
|
MOD_DEBUG("combo invalid: EOD while in parens");
|
|
RETURN_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (escaped) {
|
|
if (*p != '(' && *p != ')' && *p != '\\' && *p != ',')
|
|
*o++ = '\\';
|
|
*o++ = *p++;
|
|
escaped = false;
|
|
} else {
|
|
switch (*p) {
|
|
case '\\':
|
|
escaped = true;
|
|
break;
|
|
case '(':
|
|
parencount++;
|
|
*o++ = *p;
|
|
break;
|
|
case ')':
|
|
if (!parencount) {
|
|
MOD_DEBUG("combo invalid: negative parencount");
|
|
RETURN_INVALID;
|
|
}
|
|
parencount--;
|
|
*o++ = *p;
|
|
break;
|
|
case ',':
|
|
if (parencount)
|
|
*o++ = *p;
|
|
else
|
|
done = true;
|
|
break;
|
|
default:
|
|
*o++ = *p;
|
|
break;
|
|
}
|
|
if (done)
|
|
break;
|
|
p++;
|
|
}
|
|
}
|
|
*o = '\0';
|
|
} else {
|
|
child_data = NULL;
|
|
}
|
|
|
|
if (!have_result) {
|
|
int child_result = f(child_data, client_p, chptr, mode_type);
|
|
|
|
if (child_result == EXTBAN_INVALID) {
|
|
MOD_DEBUG("combo invalid: child invalid");
|
|
RETURN_INVALID;
|
|
}
|
|
|
|
/* Convert child_result to a plain boolean result */
|
|
if (invert)
|
|
child_result = child_result == EXTBAN_NOMATCH;
|
|
else
|
|
child_result = child_result == EXTBAN_MATCH;
|
|
|
|
if (is_and ? !child_result : child_result)
|
|
have_result = true;
|
|
}
|
|
|
|
if (p == banend)
|
|
break;
|
|
|
|
if (*p++ != ',') {
|
|
MOD_DEBUG("combo invalid: no ',' after ban");
|
|
RETURN_INVALID;
|
|
}
|
|
|
|
if (p == banend) {
|
|
MOD_DEBUG("combo invalid: banend after ','");
|
|
RETURN_INVALID;
|
|
}
|
|
}
|
|
|
|
/* at this point, *p should == banend */
|
|
if (p != banend) {
|
|
MOD_DEBUG("combo invalid: more child extbans than allowed");
|
|
RETURN_INVALID;
|
|
}
|
|
|
|
recursion_depth--;
|
|
|
|
if (is_and)
|
|
return have_result ? EXTBAN_NOMATCH : EXTBAN_MATCH;
|
|
else
|
|
return have_result ? EXTBAN_MATCH : EXTBAN_NOMATCH;
|
|
}
|