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