/src/pigeonhole/src/lib-sieve/sieve-commands.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 | | |
8 | | #include "rfc2822.h" |
9 | | |
10 | | #include "sieve-common.h" |
11 | | #include "sieve-ast.h" |
12 | | #include "sieve-validator.h" |
13 | | #include "sieve-generator.h" |
14 | | #include "sieve-binary.h" |
15 | | #include "sieve-commands.h" |
16 | | #include "sieve-code.h" |
17 | | #include "sieve-interpreter.h" |
18 | | |
19 | | /* |
20 | | * Literal arguments |
21 | | */ |
22 | | |
23 | | /* Forward declarations */ |
24 | | |
25 | | static bool |
26 | | arg_number_generate(const struct sieve_codegen_env *cgenv, |
27 | | struct sieve_ast_argument *arg, |
28 | | struct sieve_command *context); |
29 | | static bool |
30 | | arg_string_generate(const struct sieve_codegen_env *cgenv, |
31 | | struct sieve_ast_argument *arg, |
32 | | struct sieve_command *context); |
33 | | static bool |
34 | | arg_string_list_validate(struct sieve_validator *valdtr, |
35 | | struct sieve_ast_argument **arg, |
36 | | struct sieve_command *context); |
37 | | static bool |
38 | | arg_string_list_generate(const struct sieve_codegen_env *cgenv, |
39 | | struct sieve_ast_argument *arg, |
40 | | struct sieve_command *context); |
41 | | |
42 | | /* Argument objects */ |
43 | | |
44 | | const struct sieve_argument_def number_argument = { |
45 | | .identifier = "@number", |
46 | | .generate = arg_number_generate |
47 | | }; |
48 | | |
49 | | const struct sieve_argument_def string_argument = { |
50 | | .identifier = "@string", |
51 | | .generate = arg_string_generate |
52 | | }; |
53 | | |
54 | | const struct sieve_argument_def string_list_argument = { |
55 | | .identifier = "@string-list", |
56 | | .validate = arg_string_list_validate, |
57 | | .generate = arg_string_list_generate |
58 | | }; |
59 | | |
60 | | /* Argument implementations */ |
61 | | |
62 | | static bool |
63 | | arg_number_generate(const struct sieve_codegen_env *cgenv, |
64 | | struct sieve_ast_argument *arg, |
65 | | struct sieve_command *cmd ATTR_UNUSED) |
66 | 0 | { |
67 | 0 | sieve_opr_number_emit(cgenv->sblock, sieve_ast_argument_number(arg)); |
68 | 0 | return TRUE; |
69 | 0 | } |
70 | | |
71 | | static bool |
72 | | arg_string_generate(const struct sieve_codegen_env *cgenv, |
73 | | struct sieve_ast_argument *arg, |
74 | | struct sieve_command *cmd ATTR_UNUSED) |
75 | 0 | { |
76 | 0 | sieve_opr_string_emit(cgenv->sblock, sieve_ast_argument_str(arg)); |
77 | 0 | return TRUE; |
78 | 0 | } |
79 | | |
80 | | static bool |
81 | | arg_string_list_validate(struct sieve_validator *valdtr, |
82 | | struct sieve_ast_argument **arg, |
83 | | struct sieve_command *cmd) |
84 | 0 | { |
85 | 0 | struct sieve_ast_argument *stritem; |
86 | |
|
87 | 0 | stritem = sieve_ast_strlist_first(*arg); |
88 | 0 | while (stritem != NULL) { |
89 | 0 | if (!sieve_validator_argument_activate(valdtr, cmd, |
90 | 0 | stritem, FALSE)) |
91 | 0 | return FALSE; |
92 | | |
93 | 0 | stritem = sieve_ast_strlist_next(stritem); |
94 | 0 | } |
95 | 0 | return TRUE; |
96 | 0 | } |
97 | | |
98 | | static bool |
99 | | emit_string_list_operand(const struct sieve_codegen_env *cgenv, |
100 | | const struct sieve_ast_argument *strlist, |
101 | | struct sieve_command *cmd) |
102 | 0 | { |
103 | 0 | void *list_context; |
104 | 0 | struct sieve_ast_argument *stritem; |
105 | |
|
106 | 0 | sieve_opr_stringlist_emit_start(cgenv->sblock, |
107 | 0 | sieve_ast_strlist_count(strlist), |
108 | 0 | &list_context); |
109 | |
|
110 | 0 | stritem = sieve_ast_strlist_first(strlist); |
111 | 0 | while (stritem != NULL) { |
112 | 0 | if (!sieve_generate_argument(cgenv, stritem, cmd)) |
113 | 0 | return FALSE; |
114 | | |
115 | 0 | stritem = sieve_ast_strlist_next(stritem); |
116 | 0 | } |
117 | | |
118 | 0 | sieve_opr_stringlist_emit_end(cgenv->sblock, list_context); |
119 | 0 | return TRUE; |
120 | 0 | } |
121 | | |
122 | | static bool |
123 | | arg_string_list_generate(const struct sieve_codegen_env *cgenv, |
124 | | struct sieve_ast_argument *arg, |
125 | | struct sieve_command *cmd) |
126 | 0 | { |
127 | 0 | if (sieve_ast_argument_type(arg) == SAAT_STRING) { |
128 | 0 | return sieve_generate_argument(cgenv, arg, cmd); |
129 | 0 | } else if (sieve_ast_argument_type(arg) == SAAT_STRING_LIST) { |
130 | 0 | bool result = TRUE; |
131 | |
|
132 | 0 | if (sieve_ast_strlist_count(arg) == 1) { |
133 | 0 | return sieve_generate_argument( |
134 | 0 | cgenv, sieve_ast_strlist_first(arg), cmd); |
135 | 0 | } else T_BEGIN { |
136 | 0 | result = emit_string_list_operand(cgenv, arg, cmd); |
137 | 0 | } T_END; |
138 | | |
139 | 0 | return result; |
140 | 0 | } |
141 | 0 | return FALSE; |
142 | 0 | } |
143 | | |
144 | | /* |
145 | | * Abstract arguments |
146 | | * |
147 | | * (Generated by processing and not by parsing the grammar) |
148 | | */ |
149 | | |
150 | | /* Catenated string */ |
151 | | |
152 | | struct sieve_arg_catenated_string { |
153 | | struct sieve_ast_arg_list *str_parts; |
154 | | }; |
155 | | |
156 | | struct sieve_arg_catenated_string * |
157 | | sieve_arg_catenated_string_create(struct sieve_ast_argument *orig_arg) |
158 | 0 | { |
159 | 0 | pool_t pool = sieve_ast_pool(orig_arg->ast); |
160 | 0 | struct sieve_ast_arg_list *arglist; |
161 | 0 | struct sieve_arg_catenated_string *catstr; |
162 | |
|
163 | 0 | arglist = sieve_ast_arg_list_create(pool); |
164 | |
|
165 | 0 | catstr = p_new(pool, struct sieve_arg_catenated_string, 1); |
166 | 0 | catstr->str_parts = arglist; |
167 | 0 | (orig_arg)->argument->data = catstr; |
168 | |
|
169 | 0 | return catstr; |
170 | 0 | } |
171 | | |
172 | | void sieve_arg_catenated_string_add_element( |
173 | | struct sieve_arg_catenated_string *catstr, |
174 | | struct sieve_ast_argument *element) |
175 | 0 | { |
176 | 0 | i_assert(catstr->str_parts != NULL); |
177 | 0 | sieve_ast_arg_list_add(catstr->str_parts, element); |
178 | 0 | } |
179 | | |
180 | 0 | #define _cat_string_first(catstr) __AST_LIST_FIRST((catstr)->str_parts) |
181 | 0 | #define _cat_string_count(catstr) __AST_LIST_COUNT((catstr)->str_parts) |
182 | 0 | #define _cat_string_next(item) __AST_LIST_NEXT(item) |
183 | | |
184 | | bool sieve_arg_catenated_string_generate(const struct sieve_codegen_env *cgenv, |
185 | | struct sieve_ast_argument *arg, |
186 | | struct sieve_command *cmd) |
187 | 0 | { |
188 | 0 | struct sieve_arg_catenated_string *catstr = |
189 | 0 | (struct sieve_arg_catenated_string *)arg->argument->data; |
190 | 0 | struct sieve_ast_argument *strpart; |
191 | |
|
192 | 0 | if (_cat_string_count(catstr) == 1) |
193 | 0 | sieve_generate_argument(cgenv, _cat_string_first(catstr), cmd); |
194 | 0 | else { |
195 | 0 | sieve_opr_catenated_string_emit(cgenv->sblock, |
196 | 0 | _cat_string_count(catstr)); |
197 | |
|
198 | 0 | strpart = _cat_string_first(catstr); |
199 | 0 | while (strpart != NULL) { |
200 | 0 | if (!sieve_generate_argument(cgenv, strpart, cmd)) |
201 | 0 | return FALSE; |
202 | | |
203 | 0 | strpart = _cat_string_next(strpart); |
204 | 0 | } |
205 | 0 | } |
206 | 0 | return TRUE; |
207 | 0 | } |
208 | | |
209 | | /* |
210 | | * Argument creation |
211 | | */ |
212 | | |
213 | | struct sieve_argument * |
214 | | sieve_argument_create(struct sieve_ast *ast, |
215 | | const struct sieve_argument_def *def, |
216 | | const struct sieve_extension *ext, int id_code) |
217 | 0 | { |
218 | 0 | struct sieve_argument *arg; |
219 | 0 | pool_t pool; |
220 | |
|
221 | 0 | pool = sieve_ast_pool(ast); |
222 | 0 | arg = p_new(pool, struct sieve_argument, 1); |
223 | 0 | arg->def = def; |
224 | 0 | arg->ext = ext; |
225 | 0 | arg->id_code = id_code; |
226 | |
|
227 | 0 | return arg; |
228 | 0 | } |
229 | | |
230 | | /* |
231 | | * Core tests and commands |
232 | | */ |
233 | | |
234 | | const struct sieve_command_def *sieve_core_tests[] = { |
235 | | &tst_false, &tst_true, |
236 | | &tst_not, &tst_anyof, &tst_allof, |
237 | | &tst_address, &tst_header, &tst_exists, &tst_size |
238 | | }; |
239 | | |
240 | | const unsigned int sieve_core_tests_count = N_ELEMENTS(sieve_core_tests); |
241 | | |
242 | | const struct sieve_command_def *sieve_core_commands[] = { |
243 | | &cmd_require, |
244 | | &cmd_stop, &cmd_if, &cmd_elsif, &cmd_else, |
245 | | &cmd_keep, &cmd_discard, &cmd_redirect |
246 | | }; |
247 | | |
248 | | const unsigned int sieve_core_commands_count = N_ELEMENTS(sieve_core_commands); |
249 | | |
250 | | /* |
251 | | * Command context |
252 | | */ |
253 | | |
254 | | struct sieve_command *sieve_command_prev(struct sieve_command *cmd) |
255 | 0 | { |
256 | 0 | struct sieve_ast_node *node = sieve_ast_node_prev(cmd->ast_node); |
257 | |
|
258 | 0 | if (node != NULL) |
259 | 0 | return node->command; |
260 | 0 | return NULL; |
261 | 0 | } |
262 | | |
263 | | struct sieve_command *sieve_command_parent(struct sieve_command *cmd) |
264 | 0 | { |
265 | 0 | struct sieve_ast_node *node = sieve_ast_node_parent(cmd->ast_node); |
266 | |
|
267 | 0 | return (node != NULL ? node->command : NULL); |
268 | 0 | } |
269 | | |
270 | | struct sieve_command * |
271 | | sieve_command_create(struct sieve_ast_node *cmd_node, |
272 | | const struct sieve_extension *ext, |
273 | | const struct sieve_command_def *cmd_def, |
274 | | struct sieve_command_registration *cmd_reg) |
275 | 0 | { |
276 | 0 | struct sieve_command *cmd; |
277 | |
|
278 | 0 | cmd = p_new(sieve_ast_node_pool(cmd_node), struct sieve_command, 1); |
279 | |
|
280 | 0 | cmd->ast_node = cmd_node; |
281 | 0 | cmd->def = cmd_def; |
282 | 0 | cmd->ext = ext; |
283 | 0 | cmd->reg = cmd_reg; |
284 | |
|
285 | 0 | cmd->block_exit_command = NULL; |
286 | |
|
287 | 0 | return cmd; |
288 | 0 | } |
289 | | |
290 | | const char *sieve_command_def_type_name(const struct sieve_command_def *cmd_def) |
291 | 0 | { |
292 | 0 | switch (cmd_def->type) { |
293 | 0 | case SCT_NONE: |
294 | 0 | return "command of unspecified type (bug)"; |
295 | 0 | case SCT_TEST: |
296 | 0 | return "test"; |
297 | 0 | case SCT_COMMAND: |
298 | 0 | return "command"; |
299 | 0 | case SCT_HYBRID: |
300 | 0 | return "command or test"; |
301 | 0 | default: |
302 | 0 | break; |
303 | 0 | } |
304 | 0 | return "??COMMAND-TYPE??"; |
305 | 0 | } |
306 | | |
307 | | const char *sieve_command_type_name(const struct sieve_command *cmd) |
308 | 0 | { |
309 | 0 | switch (cmd->def->type) { |
310 | 0 | case SCT_NONE: |
311 | 0 | return "command of unspecified type (bug)"; |
312 | 0 | case SCT_TEST: |
313 | 0 | return "test"; |
314 | 0 | case SCT_COMMAND: |
315 | 0 | return "command"; |
316 | 0 | case SCT_HYBRID: |
317 | 0 | if (cmd->ast_node->type == SAT_TEST) |
318 | 0 | return "test"; |
319 | 0 | return "command"; |
320 | 0 | default: |
321 | 0 | break; |
322 | 0 | } |
323 | 0 | return "??COMMAND-TYPE??"; |
324 | 0 | } |
325 | | |
326 | | struct sieve_ast_argument * |
327 | | sieve_command_add_dynamic_tag(struct sieve_command *cmd, |
328 | | const struct sieve_extension *ext, |
329 | | const struct sieve_argument_def *tag, |
330 | | int id_code) |
331 | 0 | { |
332 | 0 | struct sieve_ast_argument *arg; |
333 | |
|
334 | 0 | if (cmd->first_positional != NULL) { |
335 | 0 | arg = sieve_ast_argument_tag_insert(cmd->first_positional, |
336 | 0 | tag->identifier, |
337 | 0 | cmd->ast_node->source_line); |
338 | 0 | } else { |
339 | 0 | arg = sieve_ast_argument_tag_create(cmd->ast_node, |
340 | 0 | tag->identifier, |
341 | 0 | cmd->ast_node->source_line); |
342 | 0 | } |
343 | |
|
344 | 0 | arg->argument = sieve_argument_create(cmd->ast_node->ast, tag, ext, |
345 | 0 | id_code); |
346 | 0 | return arg; |
347 | 0 | } |
348 | | |
349 | | struct sieve_ast_argument * |
350 | | sieve_command_find_argument(struct sieve_command *cmd, |
351 | | const struct sieve_argument_def *arg_def) |
352 | 0 | { |
353 | 0 | struct sieve_ast_argument *arg = |
354 | 0 | sieve_ast_argument_first(cmd->ast_node); |
355 | | |
356 | | /* Visit tagged and optional arguments */ |
357 | 0 | while (arg != NULL) { |
358 | 0 | if (arg->argument != NULL && arg->argument->def == arg_def) |
359 | 0 | return arg; |
360 | | |
361 | 0 | arg = sieve_ast_argument_next(arg); |
362 | 0 | } |
363 | 0 | return arg; |
364 | 0 | } |
365 | | |
366 | | /* Use this function with caution. The command commits to exiting the block. |
367 | | When it for some reason does not, the interpretation will break later on, |
368 | | because exiting jumps are not generated when they would otherwise be |
369 | | necessary. |
370 | | */ |
371 | | void sieve_command_exit_block_unconditionally(struct sieve_command *cmd) |
372 | 0 | { |
373 | 0 | struct sieve_command *parent = sieve_command_parent(cmd); |
374 | | |
375 | | /* Only the first unconditional exit is of importance */ |
376 | 0 | if (parent != NULL && parent->block_exit_command == NULL) |
377 | 0 | parent->block_exit_command = cmd; |
378 | 0 | } |
379 | | |
380 | | bool sieve_command_block_exits_unconditionally(struct sieve_command *cmd) |
381 | 0 | { |
382 | 0 | return ( cmd->block_exit_command != NULL ); |
383 | 0 | } |
384 | | |
385 | | /* |
386 | | * Command utility functions |
387 | | */ |
388 | | |
389 | | /* NOTE: this may be moved */ |
390 | | |
391 | | static int |
392 | | _verify_header_name_item(void *context, struct sieve_ast_argument *header) |
393 | 0 | { |
394 | 0 | struct sieve_validator *valdtr = (struct sieve_validator *)context; |
395 | 0 | string_t *name = sieve_ast_argument_str(header); |
396 | |
|
397 | 0 | if (sieve_argument_is_string_literal(header) && |
398 | 0 | !rfc2822_header_field_name_verify(str_c(name), str_len(name))) { |
399 | 0 | sieve_argument_validate_warning( |
400 | 0 | valdtr, header, |
401 | 0 | "specified header field name '%s' is invalid", |
402 | 0 | str_sanitize(str_c(name), 80)); |
403 | 0 | return 0; |
404 | 0 | } |
405 | 0 | return 1; |
406 | 0 | } |
407 | | |
408 | | bool sieve_command_verify_headers_argument(struct sieve_validator *valdtr, |
409 | | struct sieve_ast_argument *headers) |
410 | 0 | { |
411 | 0 | return (sieve_ast_stringlist_map(&headers, valdtr, |
412 | 0 | _verify_header_name_item) >= 0); |
413 | 0 | } |