/src/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file |
2 | | */ |
3 | | |
4 | | #include "lib.h" |
5 | | #include "str.h" |
6 | | #include "str-sanitize.h" |
7 | | #include "array.h" |
8 | | |
9 | | #include "sieve-common.h" |
10 | | #include "sieve-ast.h" |
11 | | #include "sieve-commands.h" |
12 | | #include "sieve-code.h" |
13 | | #include "sieve-validator.h" |
14 | | #include "sieve-generator.h" |
15 | | #include "sieve-dump.h" |
16 | | |
17 | | #include "ext-variables-common.h" |
18 | | #include "ext-variables-limits.h" |
19 | | #include "ext-variables-name.h" |
20 | | #include "ext-variables-operands.h" |
21 | | #include "ext-variables-namespaces.h" |
22 | | #include "ext-variables-arguments.h" |
23 | | |
24 | | /* |
25 | | * Variable argument implementation |
26 | | */ |
27 | | |
28 | | static bool |
29 | | arg_variable_generate(const struct sieve_codegen_env *cgenv, |
30 | | struct sieve_ast_argument *arg, |
31 | | struct sieve_command *context); |
32 | | |
33 | | const struct sieve_argument_def variable_argument = { |
34 | | .identifier = "@variable", |
35 | | .generate = arg_variable_generate, |
36 | | }; |
37 | | |
38 | | static bool |
39 | | ext_variables_variable_argument_activate(const struct sieve_extension *var_ext, |
40 | | const struct sieve_extension *this_ext, |
41 | | struct sieve_validator *valdtr, |
42 | | struct sieve_ast_argument *arg, |
43 | | const char *variable) |
44 | 0 | { |
45 | 0 | struct sieve_ast *ast = arg->ast; |
46 | 0 | struct sieve_variable *var; |
47 | |
|
48 | 0 | var = ext_variables_validator_declare_variable( |
49 | 0 | this_ext, valdtr, variable); |
50 | |
|
51 | 0 | if (var == NULL) { |
52 | 0 | sieve_argument_validate_error( |
53 | 0 | valdtr, arg, |
54 | 0 | "(implicit) declaration of new variable '%s' exceeds the limit " |
55 | 0 | "(max variables: %u)", variable, |
56 | 0 | sieve_variables_get_max_scope_count(var_ext)); |
57 | 0 | return FALSE; |
58 | 0 | } |
59 | | |
60 | 0 | arg->argument = sieve_argument_create(ast, &variable_argument, |
61 | 0 | this_ext, 0); |
62 | 0 | arg->argument->data = var; |
63 | 0 | return TRUE; |
64 | 0 | } |
65 | | |
66 | | static struct sieve_ast_argument * |
67 | | ext_variables_variable_argument_create(const struct sieve_extension *this_ext, |
68 | | struct sieve_validator *valdtr, |
69 | | struct sieve_ast_argument *parent_arg, |
70 | | const char *variable) |
71 | 0 | { |
72 | 0 | struct sieve_ast *ast = parent_arg->ast; |
73 | 0 | struct sieve_ast_argument *new_arg; |
74 | |
|
75 | 0 | new_arg = sieve_ast_argument_create( |
76 | 0 | ast, sieve_ast_argument_line(parent_arg)); |
77 | 0 | new_arg->type = SAAT_STRING; |
78 | |
|
79 | 0 | if (!ext_variables_variable_argument_activate( |
80 | 0 | this_ext, this_ext, valdtr, new_arg, variable)) |
81 | 0 | return NULL; |
82 | | |
83 | 0 | return new_arg; |
84 | 0 | } |
85 | | |
86 | | static bool |
87 | | arg_variable_generate(const struct sieve_codegen_env *cgenv, |
88 | | struct sieve_ast_argument *arg, |
89 | | struct sieve_command *context ATTR_UNUSED) |
90 | 0 | { |
91 | 0 | struct sieve_argument *argument = arg->argument; |
92 | 0 | struct sieve_variable *var = (struct sieve_variable *) argument->data; |
93 | |
|
94 | 0 | sieve_variables_opr_variable_emit(cgenv->sblock, argument->ext, var); |
95 | 0 | return TRUE; |
96 | 0 | } |
97 | | |
98 | | /* |
99 | | * Match value argument implementation |
100 | | */ |
101 | | |
102 | | static bool |
103 | | arg_match_value_generate(const struct sieve_codegen_env *cgenv, |
104 | | struct sieve_ast_argument *arg, |
105 | | struct sieve_command *context ATTR_UNUSED); |
106 | | |
107 | | const struct sieve_argument_def match_value_argument = { |
108 | | .identifier = "@match_value", |
109 | | .generate = arg_match_value_generate, |
110 | | }; |
111 | | |
112 | | static bool |
113 | | ext_variables_match_value_argument_activate( |
114 | | const struct sieve_extension *this_ext, |
115 | | struct sieve_validator *valdtr, struct sieve_ast_argument *arg, |
116 | | unsigned int index, bool assignment) |
117 | 0 | { |
118 | 0 | struct sieve_ast *ast = arg->ast; |
119 | |
|
120 | 0 | if (assignment) { |
121 | 0 | sieve_argument_validate_error( |
122 | 0 | valdtr, arg, "cannot assign to match variable"); |
123 | 0 | return FALSE; |
124 | 0 | } |
125 | | |
126 | 0 | if (index > EXT_VARIABLES_MAX_MATCH_INDEX) { |
127 | 0 | sieve_argument_validate_error( |
128 | 0 | valdtr, arg, "match value index %u out of range " |
129 | 0 | "(max: %u)", index, EXT_VARIABLES_MAX_MATCH_INDEX); |
130 | 0 | return FALSE; |
131 | 0 | } |
132 | | |
133 | 0 | arg->argument = sieve_argument_create(ast, &match_value_argument, |
134 | 0 | this_ext, 0); |
135 | 0 | arg->argument->data = POINTER_CAST(index); |
136 | 0 | return TRUE; |
137 | 0 | } |
138 | | |
139 | | static struct sieve_ast_argument * |
140 | | ext_variables_match_value_argument_create( |
141 | | const struct sieve_extension *this_ext, struct sieve_validator *valdtr, |
142 | | struct sieve_ast_argument *parent_arg, unsigned int index) |
143 | 0 | { |
144 | 0 | struct sieve_ast *ast = parent_arg->ast; |
145 | 0 | struct sieve_ast_argument *new_arg; |
146 | |
|
147 | 0 | new_arg = sieve_ast_argument_create( |
148 | 0 | ast, sieve_ast_argument_line(parent_arg)); |
149 | 0 | new_arg->type = SAAT_STRING; |
150 | |
|
151 | 0 | if (!ext_variables_match_value_argument_activate( |
152 | 0 | this_ext, valdtr, new_arg, index, FALSE)) |
153 | 0 | return NULL; |
154 | 0 | return new_arg; |
155 | 0 | } |
156 | | |
157 | | static bool |
158 | | arg_match_value_generate(const struct sieve_codegen_env *cgenv, |
159 | | struct sieve_ast_argument *arg, |
160 | | struct sieve_command *context ATTR_UNUSED) |
161 | 0 | { |
162 | 0 | struct sieve_argument *argument = arg->argument; |
163 | 0 | unsigned int index = POINTER_CAST_TO(argument->data, unsigned int); |
164 | |
|
165 | 0 | sieve_variables_opr_match_value_emit(cgenv->sblock, |
166 | 0 | argument->ext, index); |
167 | 0 | return TRUE; |
168 | 0 | } |
169 | | |
170 | | /* |
171 | | * Variable string argument implementation |
172 | | */ |
173 | | |
174 | | static bool |
175 | | arg_variable_string_validate(struct sieve_validator *valdtr, |
176 | | struct sieve_ast_argument **arg, |
177 | | struct sieve_command *cmd); |
178 | | |
179 | | const struct sieve_argument_def variable_string_argument = { |
180 | | .identifier = "@variable-string", |
181 | | .validate = arg_variable_string_validate, |
182 | | .generate = sieve_arg_catenated_string_generate, |
183 | | }; |
184 | | |
185 | | static bool |
186 | | arg_variable_string_validate(struct sieve_validator *valdtr, |
187 | | struct sieve_ast_argument **arg, |
188 | | struct sieve_command *cmd) |
189 | 0 | { |
190 | 0 | const struct sieve_extension *this_ext = (*arg)->argument->ext; |
191 | 0 | enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE; |
192 | 0 | pool_t pool = sieve_ast_pool((*arg)->ast); |
193 | 0 | struct sieve_arg_catenated_string *catstr = NULL; |
194 | 0 | string_t *str = sieve_ast_argument_str(*arg); |
195 | 0 | const char *p, *strstart, *substart = NULL; |
196 | 0 | const char *strval = (const char *) str_data(str); |
197 | 0 | const char *strend = strval + str_len(str); |
198 | 0 | bool result = TRUE; |
199 | 0 | ARRAY_TYPE(sieve_variable_name) substitution; |
200 | 0 | int nelements = 0; |
201 | |
|
202 | 0 | T_BEGIN { |
203 | | /* Initialize substitution structure */ |
204 | 0 | t_array_init(&substitution, 2); |
205 | |
|
206 | 0 | p = strval; |
207 | 0 | strstart = p; |
208 | 0 | while (result && p < strend) { |
209 | 0 | switch (state) { |
210 | | /* Nothing found yet */ |
211 | 0 | case ST_NONE: |
212 | 0 | if (*p == '$') { |
213 | 0 | substart = p; |
214 | 0 | state = ST_OPEN; |
215 | 0 | } |
216 | 0 | p++; |
217 | 0 | break; |
218 | | /* Got '$' */ |
219 | 0 | case ST_OPEN: |
220 | 0 | if (*p == '{') { |
221 | 0 | state = ST_VARIABLE; |
222 | 0 | p++; |
223 | 0 | } else |
224 | 0 | state = ST_NONE; |
225 | 0 | break; |
226 | | /* Got '${' */ |
227 | 0 | case ST_VARIABLE: |
228 | 0 | nelements = ext_variable_name_parse( |
229 | 0 | &substitution, &p, strend); |
230 | |
|
231 | 0 | if (nelements < 0) |
232 | 0 | state = ST_NONE; |
233 | 0 | else |
234 | 0 | state = ST_CLOSE; |
235 | 0 | break; |
236 | | /* Finished parsing name, expecting '}' */ |
237 | 0 | case ST_CLOSE: |
238 | 0 | if (*p == '}') { |
239 | 0 | struct sieve_ast_argument *strarg; |
240 | | |
241 | | /* We now know that the substitution is valid */ |
242 | |
|
243 | 0 | if (catstr == NULL) |
244 | 0 | catstr = sieve_arg_catenated_string_create(*arg); |
245 | | |
246 | | /* Add the substring that is before the substitution to the |
247 | | variable-string AST. |
248 | | |
249 | | FIXME: For efficiency, if the variable is not found we should |
250 | | coalesce this substring with the one after the substitution. |
251 | | */ |
252 | 0 | if (substart > strstart) { |
253 | 0 | string_t *newstr = str_new(pool, substart - strstart); |
254 | 0 | str_append_data(newstr, strstart, substart - strstart); |
255 | |
|
256 | 0 | strarg = sieve_ast_argument_string_create_raw( |
257 | 0 | (*arg)->ast, newstr, (*arg)->source_line); |
258 | 0 | sieve_arg_catenated_string_add_element(catstr, strarg); |
259 | | |
260 | | /* Give other substitution extensions a chance to do |
261 | | their work. |
262 | | */ |
263 | 0 | if (!sieve_validator_argument_activate_super( |
264 | 0 | valdtr, cmd, strarg, FALSE)) { |
265 | 0 | result = FALSE; |
266 | 0 | break; |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | /* Find the variable */ |
271 | 0 | if (nelements == 1) { |
272 | 0 | const struct sieve_variable_name *cur_element = |
273 | 0 | array_idx(&substitution, 0); |
274 | |
|
275 | 0 | if (cur_element->num_variable == -1) { |
276 | | /* Add variable argument '${identifier}' */ |
277 | 0 | strarg = ext_variables_variable_argument_create( |
278 | 0 | this_ext, valdtr, *arg, |
279 | 0 | str_c(cur_element->identifier)); |
280 | |
|
281 | 0 | } else { |
282 | | /* Add match value argument '${000}' */ |
283 | 0 | strarg = ext_variables_match_value_argument_create( |
284 | 0 | this_ext, valdtr, *arg, |
285 | 0 | cur_element->num_variable); |
286 | 0 | } |
287 | 0 | } else { |
288 | 0 | strarg = ext_variables_namespace_argument_create( |
289 | 0 | this_ext, valdtr, *arg, cmd, &substitution); |
290 | 0 | } |
291 | |
|
292 | 0 | if (strarg != NULL) |
293 | 0 | sieve_arg_catenated_string_add_element(catstr, strarg); |
294 | |
|
295 | 0 | strstart = p + 1; |
296 | 0 | substart = strstart; |
297 | |
|
298 | 0 | p++; |
299 | 0 | } |
300 | | |
301 | | /* Finished, reset for the next substitution */ |
302 | 0 | state = ST_NONE; |
303 | 0 | } |
304 | 0 | } |
305 | 0 | } T_END; |
306 | | |
307 | | /* Bail out early if substitution is invalid */ |
308 | 0 | if (!result) |
309 | 0 | return FALSE; |
310 | | |
311 | | /* Check whether any substitutions were found */ |
312 | 0 | if (catstr == NULL) { |
313 | | /* No substitutions in this string, pass it on to any other |
314 | | substution extension. |
315 | | */ |
316 | 0 | return sieve_validator_argument_activate_super( |
317 | 0 | valdtr, cmd, *arg, TRUE); |
318 | 0 | } |
319 | | |
320 | | /* Add the final substring that comes after the last substitution to the |
321 | | variable-string AST. |
322 | | */ |
323 | 0 | if (strend > strstart) { |
324 | 0 | struct sieve_ast_argument *strarg; |
325 | 0 | string_t *newstr = str_new(pool, strend - strstart); |
326 | 0 | str_append_data(newstr, strstart, strend - strstart); |
327 | |
|
328 | 0 | strarg = sieve_ast_argument_string_create_raw( |
329 | 0 | (*arg)->ast, newstr, (*arg)->source_line); |
330 | 0 | sieve_arg_catenated_string_add_element(catstr, strarg); |
331 | | |
332 | | /* Give other substitution extensions a chance to do their work. |
333 | | */ |
334 | 0 | if (!sieve_validator_argument_activate_super( |
335 | 0 | valdtr, cmd, strarg, FALSE)) |
336 | 0 | return FALSE; |
337 | 0 | } |
338 | | |
339 | 0 | return TRUE; |
340 | 0 | } |
341 | | |
342 | | /* |
343 | | * Variable argument interface |
344 | | */ |
345 | | |
346 | | static bool |
347 | | _sieve_variable_argument_activate(const struct sieve_extension *var_ext, |
348 | | const struct sieve_extension *this_ext, |
349 | | struct sieve_validator *valdtr, |
350 | | struct sieve_command *cmd, |
351 | | struct sieve_ast_argument *arg, |
352 | | bool assignment) |
353 | 0 | { |
354 | 0 | bool result = FALSE; |
355 | 0 | string_t *variable; |
356 | 0 | const char *varstr, *varend; |
357 | 0 | ARRAY_TYPE(sieve_variable_name) vname; |
358 | 0 | int nelements = 0; |
359 | |
|
360 | 0 | T_BEGIN { |
361 | 0 | t_array_init(&vname, 2); |
362 | |
|
363 | 0 | variable = sieve_ast_argument_str(arg); |
364 | 0 | varstr = str_c(variable); |
365 | 0 | varend = PTR_OFFSET(varstr, str_len(variable)); |
366 | 0 | nelements = ext_variable_name_parse(&vname, &varstr, varend); |
367 | | |
368 | | /* Check whether name parsing succeeded */ |
369 | 0 | if (nelements <= 0 || varstr != varend) { |
370 | | /* Parse failed */ |
371 | 0 | sieve_argument_validate_error( |
372 | 0 | valdtr, arg, |
373 | 0 | "invalid variable name '%s'", |
374 | 0 | str_sanitize(str_c(variable),80)); |
375 | 0 | } else if (nelements == 1) { |
376 | | /* Normal (match) variable */ |
377 | |
|
378 | 0 | const struct sieve_variable_name *cur_element = |
379 | 0 | array_idx(&vname, 0); |
380 | |
|
381 | 0 | if (cur_element->num_variable < 0) { |
382 | | /* Variable */ |
383 | 0 | result = ext_variables_variable_argument_activate( |
384 | 0 | var_ext, this_ext, valdtr, arg, |
385 | 0 | str_c(cur_element->identifier)); |
386 | |
|
387 | 0 | } else { |
388 | | /* Match value */ |
389 | 0 | result = ext_variables_match_value_argument_activate( |
390 | 0 | this_ext, valdtr, arg, |
391 | 0 | cur_element->num_variable, assignment); |
392 | 0 | } |
393 | |
|
394 | 0 | } else { |
395 | | /* Namespace variable */ |
396 | 0 | result = ext_variables_namespace_argument_activate( |
397 | 0 | this_ext, valdtr, arg, cmd, &vname, assignment); |
398 | 0 | } |
399 | 0 | } T_END; |
400 | | |
401 | 0 | return result; |
402 | 0 | } |
403 | | |
404 | | bool sieve_variable_argument_activate(const struct sieve_extension *var_ext, |
405 | | const struct sieve_extension *this_ext, |
406 | | struct sieve_validator *valdtr, |
407 | | struct sieve_command *cmd, |
408 | | struct sieve_ast_argument *arg, |
409 | | bool assignment) |
410 | 0 | { |
411 | 0 | if (sieve_ast_argument_type(arg) == SAAT_STRING) { |
412 | | /* Single string */ |
413 | 0 | return _sieve_variable_argument_activate( |
414 | 0 | var_ext, this_ext, valdtr, cmd, arg, assignment); |
415 | 0 | } else if (sieve_ast_argument_type(arg) == SAAT_STRING_LIST) { |
416 | | /* String list */ |
417 | 0 | struct sieve_ast_argument *stritem; |
418 | |
|
419 | 0 | i_assert (!assignment); |
420 | | |
421 | 0 | stritem = sieve_ast_strlist_first(arg); |
422 | 0 | while (stritem != NULL) { |
423 | 0 | if (!_sieve_variable_argument_activate( |
424 | 0 | var_ext, this_ext, valdtr, cmd, stritem, |
425 | 0 | assignment)) |
426 | 0 | return FALSE; |
427 | | |
428 | 0 | stritem = sieve_ast_strlist_next(stritem); |
429 | 0 | } |
430 | | |
431 | 0 | arg->argument = sieve_argument_create( |
432 | 0 | arg->ast, &string_list_argument, NULL, 0); |
433 | |
|
434 | 0 | return TRUE; |
435 | 0 | } |
436 | | |
437 | 0 | return FALSE; |
438 | 0 | } |