]> jfr.im git - solanum.git/blame - extensions/extb_combi.c
AV2 descriptions for m_[no]*
[solanum.git] / extensions / extb_combi.c
CommitLineData
4ef511eb
AC
1/*
2 * Extban that combines other extbans.
3 *
4 * Basic example:
5 * $&:~a,m:*!*@gateway/web/cgi-irc*
6 * Which means: match unidentified webchat users.
7 * ("m" is another new extban type, which just does a normal match).
8 *
9 * More complicated example:
10 * $&:~a,|:(m:*!*@gateway/web/foo,m:*!*@gateway/web/bar)
11 * Which means: unidentified and using the foo or bar gateway.
12 *
13 * Rules:
14 *
15 * - Optional pair of parens around data.
16 *
17 * - component bans are separated by commas, but commas between
18 * matching pairs of parens are skipped.
19 *
20 * - Unbalanced parens are an error.
21 *
22 * - Parens, commas and backslashes can be escaped by backslashes.
23 *
24 * - A backslash before any character other than a paren or backslash
25 * is just a backslash (backslash and character are both used).
26 *
27 * - Non-existant extbans are invalid.
28 * This is primarily for consistency with non-combined bans:
29 * the ircd does not let you set +b $f unless the 'f' extban is loaded,
30 * so setting $&:f should be impossible too.
31 *
32 * Issues:
33 * - Backslashes double inside nested bans.
34 * Hopefully acceptable because they should be rare.
35 *
36 * - Is performance good enough?
37 * I suspect it is, but have done no load testing.
38 */
39
40#include "stdinc.h"
41#include "modules.h"
42#include "client.h"
43#include "ircd.h"
44
45// #define DEBUG(s) sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, (s))
46#define DEBUG(s)
d63f3f80 47#define RETURN_INVALID { recursion_depth--; return EXTBAN_INVALID; }
4ef511eb
AC
48
49static int _modinit(void);
50static void _moddeinit(void);
51static int eb_or(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);
52static int eb_and(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);
53static int eb_combi(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type, int is_and);
d63f3f80 54static int recursion_depth = 0;
c81afd15 55static const char extb_desc[] = "Combination ($&, $|) extban types";
4ef511eb 56
c81afd15 57DECLARE_MODULE_AV2(extb_extended, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, extb_desc);
4ef511eb
AC
58
59static int
60_modinit(void)
61{
62 extban_table['&'] = eb_and;
63 extban_table['|'] = eb_or;
64
65 return 0;
66}
67
68static void
69_moddeinit(void)
70{
71 extban_table['&'] = NULL;
72 extban_table['|'] = NULL;
73}
74
75static int eb_or(const char *data, struct Client *client_p,
76 struct Channel *chptr, long mode_type)
77{
78 return eb_combi(data, client_p, chptr, mode_type, FALSE);
79}
80
81static int eb_and(const char *data, struct Client *client_p,
82 struct Channel *chptr, long mode_type)
83{
84 return eb_combi(data, client_p, chptr, mode_type, TRUE);
85}
86
87static int eb_combi(const char *data, struct Client *client_p,
88 struct Channel *chptr, long mode_type, int is_and)
89{
90 const char *p, *banend;
91 int have_result = FALSE;
a2bc8af8 92 int allowed_nodes = 11;
4ef511eb
AC
93 size_t datalen;
94
a2bc8af8 95 if (recursion_depth >= 5) {
d63f3f80
AC
96 DEBUG("combo invalid: recursion depth too high");
97 return EXTBAN_INVALID;
98 }
99
4ef511eb
AC
100 if (EmptyString(data)) {
101 DEBUG("combo invalid: empty data");
102 return EXTBAN_INVALID;
103 }
104
105 datalen = strlen(data);
106 if (datalen > BANLEN) {
107 /* I'd be sad if this ever happened, but if it does we
108 * could overflow the buffer used below, so...
109 */
110 DEBUG("combo invalid: > BANLEN");
111 return EXTBAN_INVALID;
112 }
113 banend = data + datalen;
114
115 if (data[0] == '(') {
116 p = data + 1;
117 banend--;
118 if (*banend != ')') {
119 DEBUG("combo invalid: starting but no closing paren");
120 return EXTBAN_INVALID;
121 }
122 } else {
123 p = data;
124 }
125
126 /* Empty combibans are invalid. */
127 if (banend == p) {
128 DEBUG("combo invalid: no data (after removing parens)");
129 return EXTBAN_INVALID;
130 }
131
132 /* Implementation note:
133 * I want it to be impossible to set a syntactically invalid combi-ban.
134 * (mismatched parens).
135 * That is: valid_extban should return false for those.
136 * Ideally we do not parse the entire ban when actually matching it:
137 * we can just short-circuit if we already know the ban is valid.
138 * Unfortunately there is no separate hook or mode_type for validation,
139 * so we always keep parsing even after we have determined a result.
140 */
141
d63f3f80
AC
142 recursion_depth++;
143
2e548a8a 144 while (--allowed_nodes) {
4ef511eb
AC
145 int invert = FALSE;
146 char *child_data, child_data_buf[BANLEN];
147 ExtbanFunc f;
148
149 if (*p == '~') {
150 invert = TRUE;
151 p++;
152 if (p == banend) {
153 DEBUG("combo invalid: no data after ~");
d63f3f80 154 RETURN_INVALID;
4ef511eb
AC
155 }
156 }
157
158 f = extban_table[(unsigned char) *p++];
159 if (!f) {
160 DEBUG("combo invalid: non-existant child extban");
d63f3f80 161 RETURN_INVALID;
4ef511eb
AC
162 }
163
164 if (*p == ':') {
165 unsigned int parencount = 0;
166 int escaped = FALSE, done = FALSE;
167 char *o;
168
169 p++;
170
171 /* Possible optimization: we can skip the actual copy if
172 * we already have_result.
173 */
174 o = child_data = child_data_buf;
175 while (TRUE) {
176 if (p == banend) {
177 if (parencount) {
178 DEBUG("combo invalid: EOD while in parens");
d63f3f80 179 RETURN_INVALID;
4ef511eb
AC
180 }
181 break;
182 }
183
184 if (escaped) {
185 if (*p != '(' && *p != ')' && *p != '\\' && *p != ',')
186 *o++ = '\\';
187 *o++ = *p++;
188 escaped = FALSE;
189 } else {
190 switch (*p) {
191 case '\\':
192 escaped = TRUE;
193 break;
194 case '(':
195 parencount++;
196 *o++ = *p;
197 break;
198 case ')':
199 if (!parencount) {
200 DEBUG("combo invalid: negative parencount");
d63f3f80 201 RETURN_INVALID;
4ef511eb
AC
202 }
203 parencount--;
204 *o++ = *p;
205 break;
206 case ',':
207 if (parencount)
208 *o++ = *p;
209 else
210 done = TRUE;
211 break;
212 default:
213 *o++ = *p;
214 break;
215 }
216 if (done)
217 break;
218 p++;
219 }
220 }
221 *o = '\0';
222 } else {
223 child_data = NULL;
224 }
225
226 if (!have_result) {
227 int child_result = f(child_data, client_p, chptr, mode_type);
228
229 if (child_result == EXTBAN_INVALID) {
230 DEBUG("combo invalid: child invalid");
d63f3f80 231 RETURN_INVALID;
4ef511eb
AC
232 }
233
234 /* Convert child_result to a plain boolean result */
235 if (invert)
236 child_result = child_result == EXTBAN_NOMATCH;
237 else
238 child_result = child_result == EXTBAN_MATCH;
239
240 if (is_and ? !child_result : child_result)
241 have_result = TRUE;
242 }
243
244 if (p == banend)
245 break;
246
247 if (*p++ != ',') {
248 DEBUG("combo invalid: no ',' after ban");
d63f3f80 249 RETURN_INVALID;
4ef511eb
AC
250 }
251
252 if (p == banend) {
253 DEBUG("combo invalid: banend after ','");
d63f3f80 254 RETURN_INVALID;
4ef511eb
AC
255 }
256 }
257
5984986b
AC
258 /* at this point, *p should == banend */
259 if (p != banend) {
260 DEBUG("combo invalid: more child extbans than allowed");
261 RETURN_INVALID;
262 }
263
d63f3f80
AC
264 recursion_depth--;
265
4ef511eb
AC
266 if (is_and)
267 return have_result ? EXTBAN_NOMATCH : EXTBAN_MATCH;
268 else
269 return have_result ? EXTBAN_MATCH : EXTBAN_NOMATCH;
270}