/src/pigeonhole/src/lib-sieve/sieve-interpreter.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ostream.h" |
5 | | #include "mempool.h" |
6 | | #include "array.h" |
7 | | #include "hash.h" |
8 | | #include "cpu-limit.h" |
9 | | #include "mail-storage.h" |
10 | | |
11 | | #include "sieve-common.h" |
12 | | #include "sieve-limits.h" |
13 | | #include "sieve-script.h" |
14 | | #include "sieve-error.h" |
15 | | #include "sieve-extensions.h" |
16 | | #include "sieve-message.h" |
17 | | #include "sieve-commands.h" |
18 | | #include "sieve-code.h" |
19 | | #include "sieve-actions.h" |
20 | | #include "sieve-generator.h" |
21 | | #include "sieve-binary.h" |
22 | | #include "sieve-result.h" |
23 | | #include "sieve-comparators.h" |
24 | | #include "sieve-runtime-trace.h" |
25 | | |
26 | | #include "sieve-interpreter.h" |
27 | | |
28 | | #include <string.h> |
29 | | |
30 | | static struct event_category event_category_sieve_runtime = { |
31 | | .parent = &event_category_sieve, |
32 | | .name = "sieve-runtime", |
33 | | }; |
34 | | |
35 | | /* |
36 | | * Interpreter extension |
37 | | */ |
38 | | |
39 | | struct sieve_interpreter_extension_reg { |
40 | | const struct sieve_interpreter_extension *intext; |
41 | | const struct sieve_extension *ext; |
42 | | |
43 | | void *context; |
44 | | |
45 | | bool deferred:1; |
46 | | bool started:1; |
47 | | }; |
48 | | |
49 | | /* |
50 | | * Code loop |
51 | | */ |
52 | | |
53 | | struct sieve_interpreter_loop { |
54 | | unsigned int level; |
55 | | sieve_size_t begin, end; |
56 | | const struct sieve_extension_def *ext_def; |
57 | | pool_t pool; |
58 | | void *context; |
59 | | }; |
60 | | |
61 | | /* |
62 | | * Interpreter |
63 | | */ |
64 | | |
65 | | struct sieve_interpreter { |
66 | | pool_t pool; |
67 | | struct sieve_interpreter *parent; |
68 | | |
69 | | /* Runtime data for extensions */ |
70 | | ARRAY(struct sieve_interpreter_extension_reg) extensions; |
71 | | |
72 | | sieve_size_t reset_vector; |
73 | | |
74 | | /* Execution status */ |
75 | | sieve_size_t pc; /* Program counter */ |
76 | | |
77 | | /* Loop stack */ |
78 | | ARRAY(struct sieve_interpreter_loop) loop_stack; |
79 | | sieve_size_t loop_limit; |
80 | | unsigned int parent_loop_level; |
81 | | |
82 | | /* Runtime environment */ |
83 | | struct sieve_runtime_env runenv; |
84 | | struct sieve_runtime_trace trace; |
85 | | struct sieve_resource_usage rusage; |
86 | | |
87 | | /* CPU time limit for the current sieve_interpreter_continue() call; |
88 | | NULL when no limit is configured or not currently executing. Exposed |
89 | | via sieve_runtime_cpu_limit_exceeded() so long-running runtime code |
90 | | can enforce the limit without waiting for the next bytecode |
91 | | boundary. */ |
92 | | struct cpu_limit *climit; |
93 | | |
94 | | /* Current operation */ |
95 | | struct sieve_operation oprtn; |
96 | | |
97 | | /* Location information */ |
98 | | struct sieve_binary_debug_reader *dreader; |
99 | | unsigned int command_line; |
100 | | |
101 | | bool running:1; /* Interpreter is running |
102 | | (may be interrupted) */ |
103 | | bool interrupted:1; /* Interpreter interrupt requested */ |
104 | | bool test_result:1; /* Result of previous test command */ |
105 | | }; |
106 | | |
107 | | static struct sieve_interpreter * |
108 | | _sieve_interpreter_create(struct sieve_binary *sbin, |
109 | | struct sieve_binary_block *sblock, |
110 | | struct sieve_script *script, |
111 | | struct sieve_interpreter *parent, |
112 | | const struct sieve_execute_env *eenv, |
113 | | struct sieve_error_handler *ehandler) ATTR_NULL(3, 4) |
114 | 0 | { |
115 | 0 | const struct sieve_script_env *senv = eenv->scriptenv; |
116 | 0 | unsigned int i, ext_count; |
117 | 0 | struct sieve_interpreter *interp; |
118 | 0 | pool_t pool; |
119 | 0 | struct sieve_instance *svinst; |
120 | 0 | const struct sieve_extension *const *ext_preloaded; |
121 | 0 | unsigned int debug_block_id; |
122 | 0 | sieve_size_t *address; |
123 | 0 | bool success = TRUE; |
124 | |
|
125 | 0 | pool = pool_alloconly_create("sieve_interpreter", 4096); |
126 | 0 | interp = p_new(pool, struct sieve_interpreter, 1); |
127 | 0 | interp->parent = parent; |
128 | 0 | interp->pool = pool; |
129 | |
|
130 | 0 | interp->runenv.ehandler = ehandler; |
131 | 0 | sieve_error_handler_ref(ehandler); |
132 | |
|
133 | 0 | interp->runenv.exec_env = eenv; |
134 | 0 | interp->runenv.interp = interp; |
135 | 0 | interp->runenv.oprtn = &interp->oprtn; |
136 | 0 | interp->runenv.sbin = sbin; |
137 | 0 | interp->runenv.sblock = sblock; |
138 | 0 | sieve_binary_ref(sbin); |
139 | |
|
140 | 0 | interp->runenv.event = event_create(eenv->event); |
141 | 0 | event_add_category(interp->runenv.event, &event_category_sieve_runtime); |
142 | 0 | event_add_str(interp->runenv.event, "script_name", |
143 | 0 | sieve_binary_script_name(sbin)); |
144 | 0 | event_add_str(interp->runenv.event, "script_location", |
145 | 0 | sieve_binary_script_location(sbin)); |
146 | 0 | event_add_str(interp->runenv.event, "binary_path", |
147 | 0 | sieve_binary_path(sbin)); |
148 | |
|
149 | 0 | svinst = sieve_binary_svinst(sbin); |
150 | |
|
151 | 0 | if (senv->trace_log != NULL) { |
152 | 0 | interp->trace.log = senv->trace_log; |
153 | 0 | interp->trace.config = senv->trace_config; |
154 | 0 | interp->trace.indent = 0; |
155 | 0 | interp->runenv.trace = &interp->trace; |
156 | 0 | } |
157 | |
|
158 | 0 | if (script == NULL) |
159 | 0 | interp->runenv.script = sieve_binary_script(sbin); |
160 | 0 | else |
161 | 0 | interp->runenv.script = script; |
162 | |
|
163 | 0 | interp->runenv.pc = 0; |
164 | 0 | address = &(interp->runenv.pc); |
165 | |
|
166 | 0 | sieve_runtime_trace_begin(&(interp->runenv)); |
167 | |
|
168 | 0 | p_array_init(&interp->extensions, pool, |
169 | 0 | sieve_extensions_get_count(svinst)); |
170 | |
|
171 | 0 | interp->parent_loop_level = 0; |
172 | 0 | if (parent != NULL && array_is_created(&parent->loop_stack)) { |
173 | 0 | interp->parent_loop_level = parent->parent_loop_level + |
174 | 0 | array_count(&parent->loop_stack); |
175 | 0 | } |
176 | | |
177 | | /* Pre-load core language features implemented as 'extensions' */ |
178 | 0 | ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count); |
179 | 0 | for (i = 0; i < ext_count; i++) { |
180 | 0 | const struct sieve_extension_def *ext_def = |
181 | 0 | ext_preloaded[i]->def; |
182 | |
|
183 | 0 | if (ext_def != NULL && ext_def->interpreter_load != NULL) { |
184 | 0 | (void)ext_def->interpreter_load(ext_preloaded[i], |
185 | 0 | &interp->runenv, |
186 | 0 | address); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | /* Load debug block */ |
191 | 0 | if (sieve_binary_read_unsigned(sblock, address, &debug_block_id)) { |
192 | 0 | struct sieve_binary_block *debug_block = |
193 | 0 | sieve_binary_block_get(sbin, debug_block_id); |
194 | |
|
195 | 0 | if (debug_block == NULL) { |
196 | 0 | sieve_runtime_trace_error(&interp->runenv, |
197 | 0 | "invalid id for debug block"); |
198 | 0 | success = FALSE; |
199 | 0 | } else { |
200 | | /* Initialize debug reader */ |
201 | 0 | interp->dreader = |
202 | 0 | sieve_binary_debug_reader_init(debug_block); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | /* Load other extensions listed in code */ |
207 | 0 | if (success && sieve_binary_read_unsigned(sblock, address, |
208 | 0 | &ext_count)) { |
209 | |
|
210 | 0 | for (i = 0; i < ext_count; i++) { |
211 | 0 | unsigned int code = 0, deferred; |
212 | 0 | struct sieve_interpreter_extension_reg *reg; |
213 | 0 | const struct sieve_extension *ext; |
214 | |
|
215 | 0 | if (!sieve_binary_read_extension(sblock, address, |
216 | 0 | &code, &ext) || |
217 | 0 | !sieve_binary_read_byte(sblock, address, |
218 | 0 | &deferred)) { |
219 | 0 | success = FALSE; |
220 | 0 | break; |
221 | 0 | } |
222 | | |
223 | 0 | if (deferred > 0 && ext->id >= 0) { |
224 | 0 | reg = array_idx_get_space( |
225 | 0 | &interp->extensions, |
226 | 0 | (unsigned int)ext->id); |
227 | 0 | reg->deferred = TRUE; |
228 | 0 | } |
229 | |
|
230 | 0 | if (ext->def != NULL) { |
231 | 0 | if (ext->global && |
232 | 0 | (eenv->flags & SIEVE_EXECUTE_FLAG_NOGLOBAL) != 0) { |
233 | 0 | sieve_runtime_error(&interp->runenv, NULL, |
234 | 0 | "failed to enable extension '%s': " |
235 | 0 | "its use is restricted to global scripts", |
236 | 0 | sieve_extension_name(ext)); |
237 | 0 | success = FALSE; |
238 | 0 | break; |
239 | 0 | } |
240 | | |
241 | 0 | if (ext->def->interpreter_load != NULL && |
242 | 0 | !ext->def->interpreter_load(ext, &interp->runenv, |
243 | 0 | address)) { |
244 | 0 | success = FALSE; |
245 | 0 | break; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | } |
249 | 0 | } else { |
250 | 0 | success = FALSE; |
251 | 0 | } |
252 | |
|
253 | 0 | if (!success) { |
254 | 0 | sieve_interpreter_free(&interp); |
255 | 0 | interp = NULL; |
256 | 0 | } else { |
257 | 0 | interp->reset_vector = *address; |
258 | 0 | } |
259 | |
|
260 | 0 | return interp; |
261 | 0 | } |
262 | | |
263 | | struct sieve_interpreter * |
264 | | sieve_interpreter_create(struct sieve_binary *sbin, |
265 | | struct sieve_interpreter *parent, |
266 | | const struct sieve_execute_env *eenv, |
267 | | struct sieve_error_handler *ehandler) |
268 | 0 | { |
269 | 0 | struct sieve_binary_block *sblock; |
270 | |
|
271 | 0 | if ((sblock = sieve_binary_block_get( |
272 | 0 | sbin, SBIN_SYSBLOCK_MAIN_PROGRAM)) == NULL) |
273 | 0 | return NULL; |
274 | | |
275 | 0 | return _sieve_interpreter_create(sbin, sblock, NULL, parent, eenv, |
276 | 0 | ehandler); |
277 | 0 | } |
278 | | |
279 | | struct sieve_interpreter * |
280 | | sieve_interpreter_create_for_block(struct sieve_binary_block *sblock, |
281 | | struct sieve_script *script, |
282 | | struct sieve_interpreter *parent, |
283 | | const struct sieve_execute_env *eenv, |
284 | | struct sieve_error_handler *ehandler) |
285 | 0 | { |
286 | 0 | if (sblock == NULL) return NULL; |
287 | | |
288 | 0 | return _sieve_interpreter_create(sieve_binary_block_get_binary(sblock), |
289 | 0 | sblock, script, parent, eenv, |
290 | 0 | ehandler); |
291 | 0 | } |
292 | | |
293 | | void sieve_interpreter_free(struct sieve_interpreter **_interp) |
294 | 0 | { |
295 | 0 | struct sieve_interpreter *interp = *_interp; |
296 | 0 | struct sieve_runtime_env *renv = &interp->runenv; |
297 | 0 | const struct sieve_interpreter_extension_reg *eregs; |
298 | 0 | struct sieve_interpreter_loop *loops; |
299 | 0 | unsigned int count, i; |
300 | |
|
301 | 0 | if (interp->running) { |
302 | 0 | struct event_passthrough *e = |
303 | 0 | event_create_passthrough(interp->runenv.event)-> |
304 | 0 | set_name("sieve_runtime_script_finished")-> |
305 | 0 | add_str("error", "Aborted"); |
306 | 0 | e_debug(e->event(), "Aborted running script '%s'", |
307 | 0 | sieve_binary_source(interp->runenv.sbin)); |
308 | |
|
309 | 0 | interp->running = FALSE; |
310 | 0 | } |
311 | |
|
312 | 0 | if (array_is_created(&interp->loop_stack)) { |
313 | 0 | loops = array_get_modifiable(&interp->loop_stack, &count); |
314 | 0 | for (i = 0; i < count; i++) |
315 | 0 | pool_unref(&loops[i].pool); |
316 | 0 | } |
317 | |
|
318 | 0 | interp->trace.indent = 0; |
319 | 0 | sieve_runtime_trace_end(renv); |
320 | | |
321 | | /* Signal registered extensions that the interpreter is being destroyed */ |
322 | 0 | eregs = array_get(&interp->extensions, &count); |
323 | 0 | for (i = 0; i < count; i++) { |
324 | 0 | if (eregs[i].intext != NULL && eregs[i].intext->free != NULL) { |
325 | 0 | eregs[i].intext->free(eregs[i].ext, interp, |
326 | 0 | eregs[i].context); |
327 | 0 | } |
328 | 0 | } |
329 | |
|
330 | 0 | sieve_binary_debug_reader_deinit(&interp->dreader); |
331 | 0 | sieve_binary_unref(&renv->sbin); |
332 | 0 | sieve_result_unref(&interp->runenv.result); |
333 | 0 | sieve_error_handler_unref(&renv->ehandler); |
334 | 0 | event_unref(&renv->event); |
335 | |
|
336 | 0 | pool_unref(&interp->pool); |
337 | 0 | *_interp = NULL; |
338 | 0 | } |
339 | | |
340 | | /* |
341 | | * Accessors |
342 | | */ |
343 | | |
344 | | pool_t sieve_interpreter_pool(struct sieve_interpreter *interp) |
345 | 0 | { |
346 | 0 | return interp->pool; |
347 | 0 | } |
348 | | |
349 | | struct sieve_interpreter * |
350 | | sieve_interpreter_get_parent(struct sieve_interpreter *interp) |
351 | 0 | { |
352 | 0 | return interp->parent; |
353 | 0 | } |
354 | | |
355 | | struct sieve_script *sieve_interpreter_script(struct sieve_interpreter *interp) |
356 | 0 | { |
357 | 0 | return interp->runenv.script; |
358 | 0 | } |
359 | | |
360 | | struct sieve_error_handler * |
361 | | sieve_interpreter_get_error_handler(struct sieve_interpreter *interp) |
362 | 0 | { |
363 | 0 | return interp->runenv.ehandler; |
364 | 0 | } |
365 | | |
366 | | struct sieve_instance * |
367 | | sieve_interpreter_svinst(struct sieve_interpreter *interp) |
368 | 0 | { |
369 | 0 | return interp->runenv.exec_env->svinst; |
370 | 0 | } |
371 | | |
372 | | bool sieve_runtime_cpu_limit_exceeded(const struct sieve_runtime_env *renv) |
373 | 0 | { |
374 | 0 | struct sieve_interpreter *interp = renv->interp; |
375 | |
|
376 | 0 | if (interp->climit == NULL) |
377 | 0 | return FALSE; |
378 | 0 | return cpu_limit_exceeded(interp->climit); |
379 | 0 | } |
380 | | |
381 | | /* Do not use this function for normal sieve extensions. This is intended for |
382 | | * the testsuite only. |
383 | | */ |
384 | | void sieve_interpreter_set_result(struct sieve_interpreter *interp, |
385 | | struct sieve_result *result) |
386 | 0 | { |
387 | 0 | sieve_result_unref(&interp->runenv.result); |
388 | 0 | interp->runenv.result = result; |
389 | 0 | interp->runenv.msgctx = sieve_result_get_message_context(result); |
390 | 0 | sieve_result_ref(result); |
391 | 0 | } |
392 | | |
393 | | /* |
394 | | * Source location |
395 | | */ |
396 | | |
397 | | unsigned int |
398 | | sieve_runtime_get_source_location(const struct sieve_runtime_env *renv, |
399 | | sieve_size_t code_address) |
400 | 0 | { |
401 | 0 | struct sieve_interpreter *interp = renv->interp; |
402 | |
|
403 | 0 | if (interp->dreader == NULL) |
404 | 0 | return 0; |
405 | | |
406 | 0 | if (interp->command_line == 0) { |
407 | 0 | interp->command_line = |
408 | 0 | sieve_binary_debug_read_line(interp->dreader, |
409 | 0 | renv->oprtn->address); |
410 | 0 | } |
411 | |
|
412 | 0 | return sieve_binary_debug_read_line(interp->dreader, code_address); |
413 | 0 | } |
414 | | |
415 | | unsigned int |
416 | | sieve_runtime_get_command_location(const struct sieve_runtime_env *renv) |
417 | 0 | { |
418 | 0 | struct sieve_interpreter *interp = renv->interp; |
419 | |
|
420 | 0 | if (interp->dreader == NULL) |
421 | 0 | return 0; |
422 | | |
423 | 0 | if (interp->command_line == 0) { |
424 | 0 | interp->command_line = |
425 | 0 | sieve_binary_debug_read_line(interp->dreader, |
426 | 0 | renv->oprtn->address); |
427 | 0 | } |
428 | |
|
429 | 0 | return interp->command_line; |
430 | 0 | } |
431 | | |
432 | | const char * |
433 | | sieve_runtime_get_full_command_location(const struct sieve_runtime_env *renv) |
434 | 0 | { |
435 | 0 | return sieve_error_script_location( |
436 | 0 | renv->script, sieve_runtime_get_command_location(renv)); |
437 | 0 | } |
438 | | |
439 | | /* |
440 | | * Extension support |
441 | | */ |
442 | | |
443 | | void sieve_interpreter_extension_register( |
444 | | struct sieve_interpreter *interp, const struct sieve_extension *ext, |
445 | | const struct sieve_interpreter_extension *intext, void *context) |
446 | 0 | { |
447 | 0 | struct sieve_interpreter_extension_reg *reg; |
448 | |
|
449 | 0 | if (ext->id < 0) |
450 | 0 | return; |
451 | | |
452 | 0 | reg = array_idx_get_space(&interp->extensions, (unsigned int) ext->id); |
453 | 0 | reg->intext = intext; |
454 | 0 | reg->ext = ext; |
455 | 0 | reg->context = context; |
456 | 0 | } |
457 | | |
458 | | void sieve_interpreter_extension_set_context(struct sieve_interpreter *interp, |
459 | | const struct sieve_extension *ext, |
460 | | void *context) |
461 | 0 | { |
462 | 0 | struct sieve_interpreter_extension_reg *reg; |
463 | |
|
464 | 0 | if (ext->id < 0) |
465 | 0 | return; |
466 | | |
467 | 0 | reg = array_idx_get_space(&interp->extensions, (unsigned int) ext->id); |
468 | 0 | reg->context = context; |
469 | 0 | } |
470 | | |
471 | | void *sieve_interpreter_extension_get_context(struct sieve_interpreter *interp, |
472 | | const struct sieve_extension *ext) |
473 | 0 | { |
474 | 0 | const struct sieve_interpreter_extension_reg *reg; |
475 | |
|
476 | 0 | if (ext->id < 0 || ext->id >= (int) array_count(&interp->extensions)) |
477 | 0 | return NULL; |
478 | | |
479 | 0 | reg = array_idx(&interp->extensions, (unsigned int) ext->id); |
480 | |
|
481 | 0 | return reg->context; |
482 | 0 | } |
483 | | |
484 | | int sieve_interpreter_extension_start(struct sieve_interpreter *interp, |
485 | | const struct sieve_extension *ext) |
486 | 0 | { |
487 | 0 | struct sieve_interpreter_extension_reg *reg; |
488 | 0 | int ret; |
489 | |
|
490 | 0 | i_assert(ext->id >= 0); |
491 | | |
492 | 0 | if (ext->id >= (int) array_count(&interp->extensions)) |
493 | 0 | return SIEVE_EXEC_OK; |
494 | | |
495 | 0 | reg = array_idx_modifiable(&interp->extensions, (unsigned int)ext->id); |
496 | |
|
497 | 0 | if (!reg->deferred) |
498 | 0 | return SIEVE_EXEC_OK; |
499 | 0 | reg->deferred = FALSE; |
500 | 0 | reg->started = TRUE; |
501 | |
|
502 | 0 | if (reg->intext != NULL && reg->intext->run != NULL && |
503 | 0 | (ret = reg->intext->run(ext, &interp->runenv, |
504 | 0 | reg->context, TRUE)) <= 0) |
505 | 0 | return ret; |
506 | 0 | return SIEVE_EXEC_OK; |
507 | 0 | } |
508 | | |
509 | | /* |
510 | | * Loop handling |
511 | | */ |
512 | | |
513 | | int sieve_interpreter_loop_start(struct sieve_interpreter *interp, |
514 | | sieve_size_t loop_end, |
515 | | const struct sieve_extension_def *ext_def, |
516 | | struct sieve_interpreter_loop **loop_r) |
517 | 0 | { |
518 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
519 | 0 | struct sieve_interpreter_loop *loop; |
520 | |
|
521 | 0 | i_assert(loop_end > interp->runenv.pc); |
522 | | |
523 | | /* Check supplied end offset */ |
524 | 0 | if (loop_end > sieve_binary_block_get_size(renv->sblock)) { |
525 | 0 | sieve_runtime_trace_error(renv, "loop end offset out of range"); |
526 | 0 | return SIEVE_EXEC_BIN_CORRUPT; |
527 | 0 | } |
528 | | |
529 | | /* Trace */ |
530 | 0 | if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { |
531 | 0 | unsigned int line = |
532 | 0 | sieve_runtime_get_source_location(renv, loop_end); |
533 | |
|
534 | 0 | if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { |
535 | 0 | sieve_runtime_trace(renv, 0, |
536 | 0 | "loop ends at line %d [%08llx]", |
537 | 0 | line, |
538 | 0 | (long long unsigned int) loop_end); |
539 | 0 | } else { |
540 | 0 | sieve_runtime_trace(renv, 0, |
541 | 0 | "loop ends at line %d", line); |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | /* Check loop nesting limit */ |
546 | 0 | if (!array_is_created(&interp->loop_stack)) |
547 | 0 | p_array_init(&interp->loop_stack, interp->pool, 8); |
548 | 0 | if ((interp->parent_loop_level + |
549 | 0 | array_count(&interp->loop_stack)) >= SIEVE_MAX_LOOP_DEPTH) { |
550 | | /* Should normally be caught at compile time */ |
551 | 0 | sieve_runtime_error(renv, NULL, |
552 | 0 | "new program loop exceeds " |
553 | 0 | "the nesting limit (<= %u levels)", |
554 | 0 | SIEVE_MAX_LOOP_DEPTH); |
555 | 0 | return SIEVE_EXEC_FAILURE; |
556 | 0 | } |
557 | | |
558 | | /* Create new loop */ |
559 | 0 | loop = array_append_space(&interp->loop_stack); |
560 | 0 | loop->level = array_count(&interp->loop_stack)-1; |
561 | 0 | loop->ext_def = ext_def; |
562 | 0 | loop->begin = interp->runenv.pc; |
563 | 0 | loop->end = loop_end; |
564 | 0 | loop->pool = pool_alloconly_create("sieve_interpreter", 128); |
565 | | |
566 | | /* Set new loop limit */ |
567 | 0 | interp->loop_limit = loop_end; |
568 | |
|
569 | 0 | *loop_r = loop; |
570 | 0 | return SIEVE_EXEC_OK; |
571 | 0 | } |
572 | | |
573 | | struct sieve_interpreter_loop * |
574 | | sieve_interpreter_loop_get(struct sieve_interpreter *interp, |
575 | | sieve_size_t loop_end, |
576 | | const struct sieve_extension_def *ext_def) |
577 | 0 | { |
578 | 0 | struct sieve_interpreter_loop *loops; |
579 | 0 | unsigned int count, i; |
580 | |
|
581 | 0 | if (!array_is_created(&interp->loop_stack)) |
582 | 0 | return NULL; |
583 | | |
584 | 0 | loops = array_get_modifiable(&interp->loop_stack, &count); |
585 | 0 | for (i = count; i > 0; i--) { |
586 | | /* We're really making sure our loop matches */ |
587 | 0 | if (loops[i-1].end == loop_end && |
588 | 0 | loops[i-1].ext_def == ext_def) |
589 | 0 | return &loops[i-1]; |
590 | 0 | } |
591 | 0 | return NULL; |
592 | 0 | } |
593 | | |
594 | | int sieve_interpreter_loop_next(struct sieve_interpreter *interp, |
595 | | struct sieve_interpreter_loop *loop, |
596 | | sieve_size_t loop_begin) |
597 | 0 | { |
598 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
599 | 0 | struct sieve_interpreter_loop *loops; |
600 | 0 | unsigned int count; |
601 | | |
602 | | /* Trace */ |
603 | 0 | if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { |
604 | 0 | unsigned int line = |
605 | 0 | sieve_runtime_get_source_location(renv, loop_begin); |
606 | |
|
607 | 0 | if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { |
608 | 0 | sieve_runtime_trace(renv, 0, |
609 | 0 | "looping back to line %d [%08llx]", |
610 | 0 | line, |
611 | 0 | (long long unsigned int) loop_begin); |
612 | 0 | } else { |
613 | 0 | sieve_runtime_trace(renv, 0, |
614 | 0 | "looping back to line %d", line); |
615 | 0 | } |
616 | 0 | } |
617 | | |
618 | | /* Check the code for corruption */ |
619 | 0 | if (loop->begin != loop_begin) { |
620 | 0 | sieve_runtime_trace_error(renv, "loop begin offset invalid"); |
621 | 0 | return SIEVE_EXEC_BIN_CORRUPT; |
622 | 0 | } |
623 | | |
624 | | /* Check invariants */ |
625 | 0 | i_assert(array_is_created(&interp->loop_stack)); |
626 | 0 | loops = array_get_modifiable(&interp->loop_stack, &count); |
627 | 0 | i_assert(&loops[count-1] == loop); |
628 | | |
629 | | /* Return to beginning */ |
630 | 0 | interp->runenv.pc = loop_begin; |
631 | 0 | return SIEVE_EXEC_OK; |
632 | 0 | } |
633 | | |
634 | | int sieve_interpreter_loop_break(struct sieve_interpreter *interp, |
635 | | struct sieve_interpreter_loop *loop) |
636 | 0 | { |
637 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
638 | 0 | struct sieve_interpreter_loop *loops; |
639 | 0 | sieve_size_t loop_end = loop->end; |
640 | 0 | unsigned int count, i; |
641 | | |
642 | | /* Find the loop */ |
643 | 0 | i_assert(array_is_created(&interp->loop_stack)); |
644 | 0 | loops = array_get_modifiable(&interp->loop_stack, &count); |
645 | 0 | i_assert(count > 0); |
646 | | |
647 | 0 | i = count; |
648 | 0 | do { |
649 | 0 | pool_unref(&loops[i-1].pool); |
650 | 0 | i--; |
651 | 0 | } while (i > 0 && &loops[i] != loop); |
652 | 0 | i_assert(&loops[i] == loop); |
653 | | |
654 | | /* Set new loop limit */ |
655 | 0 | if (i > 0) |
656 | 0 | interp->loop_limit = loops[i].end; |
657 | 0 | else |
658 | 0 | interp->loop_limit = 0; |
659 | | |
660 | | /* Delete it and all deeper loops */ |
661 | 0 | array_delete(&interp->loop_stack, i, count - i); |
662 | | |
663 | | /* Trace */ |
664 | 0 | if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { |
665 | 0 | unsigned int jmp_line = |
666 | 0 | sieve_runtime_get_source_location(renv, loop_end); |
667 | |
|
668 | 0 | if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { |
669 | 0 | sieve_runtime_trace(renv, 0, |
670 | 0 | "exiting loops at line %d [%08llx]", |
671 | 0 | jmp_line, |
672 | 0 | (long long unsigned int) loop_end); |
673 | 0 | } else { |
674 | 0 | sieve_runtime_trace(renv, 0, |
675 | 0 | "exiting loops at line %d", |
676 | 0 | jmp_line); |
677 | 0 | } |
678 | 0 | } |
679 | | |
680 | | /* Exit loop */ |
681 | 0 | interp->runenv.pc = loop->end; |
682 | 0 | return SIEVE_EXEC_OK; |
683 | 0 | } |
684 | | |
685 | | static int |
686 | | sieve_interpreter_loop_break_out(struct sieve_interpreter *interp, |
687 | | sieve_size_t target) |
688 | 0 | { |
689 | 0 | struct sieve_interpreter_loop *loops; |
690 | 0 | unsigned int count, i; |
691 | |
|
692 | 0 | if (!array_is_created(&interp->loop_stack)) |
693 | 0 | return SIEVE_EXEC_OK; |
694 | | |
695 | 0 | loops = array_get_modifiable(&interp->loop_stack, &count); |
696 | 0 | for (i = count; i > 0; i--) { |
697 | | /* We're really making sure our loop matches */ |
698 | 0 | if (loops[i-1].end > target) |
699 | 0 | break; |
700 | 0 | } |
701 | 0 | if (i == count) |
702 | 0 | return SIEVE_EXEC_OK; |
703 | | |
704 | 0 | return sieve_interpreter_loop_break(interp, &loops[i]); |
705 | 0 | } |
706 | | |
707 | | struct sieve_interpreter_loop * |
708 | | sieve_interpreter_loop_get_local(struct sieve_interpreter *interp, |
709 | | struct sieve_interpreter_loop *loop, |
710 | | const struct sieve_extension_def *ext_def) |
711 | 0 | { |
712 | 0 | struct sieve_interpreter_loop *loops; |
713 | 0 | unsigned int count, i; |
714 | |
|
715 | 0 | if (!array_is_created(&interp->loop_stack)) |
716 | 0 | return NULL; |
717 | | |
718 | 0 | loops = array_get_modifiable(&interp->loop_stack, &count); |
719 | 0 | i_assert(loop == NULL || loop->level < count); |
720 | | |
721 | 0 | for (i = (loop == NULL ? count : loop->level); i > 0; i--) { |
722 | 0 | if (ext_def == NULL || loops[i-1].ext_def == ext_def) |
723 | 0 | return &loops[i-1]; |
724 | 0 | } |
725 | 0 | return NULL; |
726 | 0 | } |
727 | | |
728 | | struct sieve_interpreter_loop * |
729 | | sieve_interpreter_loop_get_global(struct sieve_interpreter *interp, |
730 | | struct sieve_interpreter_loop *loop, |
731 | | const struct sieve_extension_def *ext_def) |
732 | 0 | { |
733 | 0 | struct sieve_interpreter_loop *result; |
734 | |
|
735 | 0 | while (interp != NULL) { |
736 | 0 | result = sieve_interpreter_loop_get_local(interp, loop, |
737 | 0 | ext_def); |
738 | 0 | if (result != NULL) |
739 | 0 | return result; |
740 | 0 | interp = interp->parent; |
741 | 0 | } |
742 | 0 | return NULL; |
743 | 0 | } |
744 | | |
745 | | pool_t sieve_interpreter_loop_get_pool(struct sieve_interpreter_loop *loop) |
746 | 0 | { |
747 | 0 | return loop->pool; |
748 | 0 | } |
749 | | |
750 | | void *sieve_interpreter_loop_get_context(struct sieve_interpreter_loop *loop) |
751 | 0 | { |
752 | 0 | return loop->context; |
753 | 0 | } |
754 | | |
755 | | void sieve_interpreter_loop_set_context(struct sieve_interpreter_loop *loop, |
756 | | void *context) |
757 | 0 | { |
758 | 0 | loop->context = context; |
759 | 0 | } |
760 | | |
761 | | /* |
762 | | * Program flow |
763 | | */ |
764 | | |
765 | | void sieve_interpreter_reset(struct sieve_interpreter *interp) |
766 | 0 | { |
767 | 0 | interp->runenv.pc = interp->reset_vector; |
768 | 0 | interp->interrupted = FALSE; |
769 | 0 | interp->test_result = FALSE; |
770 | 0 | sieve_result_unref(&interp->runenv.result); |
771 | 0 | } |
772 | | |
773 | | void sieve_interpreter_interrupt(struct sieve_interpreter *interp) |
774 | 0 | { |
775 | 0 | interp->interrupted = TRUE; |
776 | 0 | } |
777 | | |
778 | | sieve_size_t sieve_interpreter_program_counter(struct sieve_interpreter *interp) |
779 | 0 | { |
780 | 0 | return interp->runenv.pc; |
781 | 0 | } |
782 | | |
783 | | static int |
784 | | sieve_interpreter_check_program_jump(struct sieve_interpreter *interp, |
785 | | sieve_size_t jmp_target, bool break_loops) |
786 | 0 | { |
787 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
788 | 0 | sieve_size_t loop_limit = (break_loops ? 0 : interp->loop_limit); |
789 | |
|
790 | 0 | if (jmp_target == 0 || |
791 | 0 | jmp_target > sieve_binary_block_get_size(renv->sblock) || |
792 | 0 | (loop_limit > 0 && jmp_target >= loop_limit)) { |
793 | 0 | if (interp->loop_limit != 0) { |
794 | 0 | sieve_runtime_trace_error( |
795 | 0 | renv, "jump target crosses loop boundary"); |
796 | 0 | } else { |
797 | 0 | sieve_runtime_trace_error( |
798 | 0 | renv, "jump target out of range"); |
799 | 0 | } |
800 | 0 | return SIEVE_EXEC_BIN_CORRUPT; |
801 | 0 | } |
802 | 0 | return SIEVE_EXEC_OK; |
803 | 0 | } |
804 | | |
805 | | static int |
806 | | sieve_interpreter_do_program_jump(struct sieve_interpreter *interp, |
807 | | sieve_size_t jmp_target, bool break_loops) |
808 | 0 | { |
809 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
810 | 0 | sieve_size_t *address = &(interp->runenv.pc); |
811 | 0 | int ret; |
812 | |
|
813 | 0 | if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { |
814 | 0 | unsigned int jmp_line = |
815 | 0 | sieve_runtime_get_source_location(renv, jmp_target); |
816 | |
|
817 | 0 | if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { |
818 | 0 | sieve_runtime_trace(renv, 0, "jumping to line %d [%08llx]", |
819 | 0 | jmp_line, |
820 | 0 | (long long unsigned int)jmp_target); |
821 | 0 | } else { |
822 | 0 | sieve_runtime_trace(renv, 0, "jumping to line %d", |
823 | 0 | jmp_line); |
824 | 0 | } |
825 | 0 | } |
826 | |
|
827 | 0 | if (break_loops && |
828 | 0 | (ret = sieve_interpreter_loop_break_out(interp, |
829 | 0 | jmp_target)) <= 0) |
830 | 0 | return ret; |
831 | | |
832 | 0 | *address = jmp_target; |
833 | 0 | return SIEVE_EXEC_OK; |
834 | 0 | } |
835 | | |
836 | | int sieve_interpreter_program_jump_to(struct sieve_interpreter *interp, |
837 | | sieve_size_t jmp_target, |
838 | | bool break_loops) |
839 | 0 | { |
840 | 0 | int ret; |
841 | |
|
842 | 0 | ret = sieve_interpreter_check_program_jump(interp, jmp_target, |
843 | 0 | break_loops); |
844 | 0 | if (ret <= 0) |
845 | 0 | return ret; |
846 | | |
847 | 0 | return sieve_interpreter_do_program_jump( |
848 | 0 | interp, jmp_target, break_loops); |
849 | 0 | } |
850 | | |
851 | | int sieve_interpreter_program_jump(struct sieve_interpreter *interp, |
852 | | bool jump, bool break_loops) |
853 | 0 | { |
854 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
855 | 0 | sieve_size_t *address = &(interp->runenv.pc); |
856 | 0 | sieve_size_t jmp_start = *address, jmp_target; |
857 | 0 | sieve_offset_t jmp_offset; |
858 | 0 | int ret; |
859 | |
|
860 | 0 | if (!sieve_binary_read_offset(renv->sblock, address, &jmp_offset)) { |
861 | 0 | sieve_runtime_trace_error(renv, "invalid jump offset"); |
862 | 0 | return SIEVE_EXEC_BIN_CORRUPT; |
863 | 0 | } |
864 | 0 | jmp_target = jmp_start + jmp_offset; |
865 | |
|
866 | 0 | ret = sieve_interpreter_check_program_jump(interp, jmp_target, |
867 | 0 | break_loops); |
868 | 0 | if (ret <= 0) |
869 | 0 | return ret; |
870 | | |
871 | 0 | if (!jump) { |
872 | 0 | sieve_runtime_trace(renv, 0, "not jumping"); |
873 | 0 | return SIEVE_EXEC_OK; |
874 | 0 | } |
875 | | |
876 | 0 | return sieve_interpreter_do_program_jump( |
877 | 0 | interp, jmp_target, break_loops); |
878 | 0 | } |
879 | | |
880 | | /* |
881 | | * Test results |
882 | | */ |
883 | | |
884 | | void sieve_interpreter_set_test_result(struct sieve_interpreter *interp, |
885 | | bool result) |
886 | 0 | { |
887 | 0 | interp->test_result = result; |
888 | 0 | } |
889 | | |
890 | | bool sieve_interpreter_get_test_result(struct sieve_interpreter *interp) |
891 | 0 | { |
892 | 0 | return interp->test_result; |
893 | 0 | } |
894 | | |
895 | | /* |
896 | | * Code execute |
897 | | */ |
898 | | |
899 | | static int sieve_interpreter_operation_execute(struct sieve_interpreter *interp) |
900 | 0 | { |
901 | 0 | struct sieve_operation *oprtn = &(interp->oprtn); |
902 | 0 | sieve_size_t *address = &(interp->runenv.pc); |
903 | |
|
904 | 0 | sieve_runtime_trace_toplevel(&interp->runenv); |
905 | | |
906 | | /* Read the operation */ |
907 | 0 | if (sieve_operation_read(interp->runenv.sblock, address, oprtn)) { |
908 | 0 | const struct sieve_operation_def *op = oprtn->def; |
909 | 0 | int result = SIEVE_EXEC_OK; |
910 | | |
911 | | /* Reset cached command location */ |
912 | 0 | interp->command_line = 0; |
913 | | |
914 | | /* Execute the operation */ |
915 | 0 | i_assert(op != NULL); |
916 | 0 | if (op->execute != NULL) { /* Noop ? */ |
917 | 0 | T_BEGIN { |
918 | 0 | result = op->execute(&(interp->runenv), |
919 | 0 | address); |
920 | 0 | } T_END; |
921 | 0 | } else { |
922 | 0 | sieve_runtime_trace(&interp->runenv, |
923 | 0 | SIEVE_TRLVL_COMMANDS, |
924 | 0 | "OP: %s (NOOP)", |
925 | 0 | sieve_operation_mnemonic(oprtn)); |
926 | 0 | } |
927 | | |
928 | 0 | return result; |
929 | 0 | } |
930 | | |
931 | | /* Binary corrupt */ |
932 | 0 | sieve_runtime_trace_error(&interp->runenv, |
933 | 0 | "Encountered invalid operation"); |
934 | 0 | return SIEVE_EXEC_BIN_CORRUPT; |
935 | 0 | } |
936 | | |
937 | | int sieve_interpreter_continue(struct sieve_interpreter *interp, |
938 | | bool *interrupted) |
939 | 0 | { |
940 | 0 | const struct sieve_runtime_env *renv = &interp->runenv; |
941 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
942 | 0 | struct cpu_limit *climit = NULL; |
943 | 0 | sieve_size_t *address = &(interp->runenv.pc); |
944 | 0 | struct sieve_instance *svinst = eenv->svinst; |
945 | 0 | struct sieve_exec_status *exec_status = eenv->exec_status; |
946 | 0 | struct sieve_resource_usage rusage; |
947 | 0 | int ret = SIEVE_EXEC_OK; |
948 | |
|
949 | 0 | interp->interrupted = FALSE; |
950 | |
|
951 | 0 | if (interrupted != NULL) |
952 | 0 | *interrupted = FALSE; |
953 | |
|
954 | 0 | if (svinst->set->max_cpu_time > 0) { |
955 | 0 | climit = cpu_limit_init(svinst->set->max_cpu_time, |
956 | 0 | CPU_LIMIT_TYPE_USER); |
957 | 0 | } |
958 | 0 | interp->climit = climit; |
959 | |
|
960 | 0 | while (ret == SIEVE_EXEC_OK && !interp->interrupted && |
961 | 0 | *address < sieve_binary_block_get_size(renv->sblock)) { |
962 | 0 | if (climit != NULL && cpu_limit_exceeded(climit)) { |
963 | 0 | sieve_runtime_error( |
964 | 0 | renv, NULL, |
965 | 0 | "execution exceeded CPU time limit"); |
966 | 0 | ret = SIEVE_EXEC_RESOURCE_LIMIT; |
967 | 0 | break; |
968 | 0 | } |
969 | 0 | if (interp->loop_limit != 0 && *address > interp->loop_limit) { |
970 | 0 | sieve_runtime_trace_error( |
971 | 0 | renv, "program crossed loop boundary"); |
972 | 0 | ret = SIEVE_EXEC_BIN_CORRUPT; |
973 | 0 | break; |
974 | 0 | } |
975 | | |
976 | 0 | ret = sieve_interpreter_operation_execute(interp); |
977 | 0 | } |
978 | |
|
979 | 0 | interp->climit = NULL; |
980 | |
|
981 | 0 | if (climit != NULL) { |
982 | 0 | sieve_resource_usage_init(&rusage); |
983 | 0 | rusage.cpu_time_msecs = |
984 | 0 | cpu_limit_get_usage_msecs(climit, CPU_LIMIT_TYPE_USER); |
985 | 0 | sieve_resource_usage_add(&interp->rusage, &rusage); |
986 | |
|
987 | 0 | cpu_limit_deinit(&climit); |
988 | 0 | } |
989 | |
|
990 | 0 | if (ret != SIEVE_EXEC_OK) { |
991 | 0 | sieve_runtime_trace(&interp->runenv, SIEVE_TRLVL_NONE, |
992 | 0 | "[[EXECUTION ABORTED]]"); |
993 | 0 | } |
994 | |
|
995 | 0 | if (interrupted != NULL) |
996 | 0 | *interrupted = interp->interrupted; |
997 | |
|
998 | 0 | if (!interp->interrupted) { |
999 | 0 | exec_status->resource_usage = interp->rusage; |
1000 | |
|
1001 | 0 | struct event_passthrough *e = |
1002 | 0 | event_create_passthrough(interp->runenv.event)-> |
1003 | 0 | set_name("sieve_runtime_script_finished"); |
1004 | 0 | switch (ret) { |
1005 | 0 | case SIEVE_EXEC_OK: |
1006 | 0 | break; |
1007 | 0 | case SIEVE_EXEC_FAILURE: |
1008 | 0 | e->add_str("error", "Failed"); |
1009 | 0 | break; |
1010 | 0 | case SIEVE_EXEC_TEMP_FAILURE: |
1011 | 0 | e->add_str("error", "Failed temporarily"); |
1012 | 0 | break; |
1013 | 0 | case SIEVE_EXEC_BIN_CORRUPT: |
1014 | 0 | e->add_str("error", "Binary corrupt"); |
1015 | 0 | break; |
1016 | 0 | case SIEVE_EXEC_RESOURCE_LIMIT: |
1017 | 0 | e->add_str("error", "Resource limit exceeded"); |
1018 | 0 | break; |
1019 | 0 | case SIEVE_EXEC_KEEP_FAILED: |
1020 | | /* Not supposed to occur at runtime */ |
1021 | 0 | i_unreached(); |
1022 | 0 | } |
1023 | 0 | e_debug(e->event(), "Finished running script '%s' " |
1024 | 0 | "(status=%s, resource usage: %s)", |
1025 | 0 | sieve_binary_source(interp->runenv.sbin), |
1026 | 0 | sieve_execution_exitcode_to_str(ret), |
1027 | 0 | sieve_resource_usage_get_summary(&interp->rusage)); |
1028 | 0 | interp->running = FALSE; |
1029 | 0 | } |
1030 | | |
1031 | 0 | return ret; |
1032 | 0 | } |
1033 | | |
1034 | | int sieve_interpreter_start(struct sieve_interpreter *interp, |
1035 | | struct sieve_result *result, bool *interrupted) |
1036 | 0 | { |
1037 | 0 | struct sieve_interpreter_extension_reg *eregs; |
1038 | 0 | unsigned int ext_count, i; |
1039 | 0 | int ret; |
1040 | |
|
1041 | 0 | struct event_passthrough *e = |
1042 | 0 | event_create_passthrough(interp->runenv.event)-> |
1043 | 0 | set_name("sieve_runtime_script_started"); |
1044 | 0 | e_debug(e->event(), "Started running script '%s'", |
1045 | 0 | sieve_binary_source(interp->runenv.sbin)); |
1046 | |
|
1047 | 0 | interp->running = TRUE; |
1048 | 0 | interp->runenv.result = result; |
1049 | 0 | interp->runenv.msgctx = sieve_result_get_message_context(result); |
1050 | 0 | sieve_result_ref(result); |
1051 | |
|
1052 | 0 | sieve_resource_usage_init(&interp->rusage); |
1053 | | |
1054 | | /* Signal registered extensions that the interpreter is being run */ |
1055 | 0 | eregs = array_get_modifiable(&interp->extensions, &ext_count); |
1056 | 0 | for (i = 0; i < ext_count; i++) { |
1057 | 0 | if (!eregs[i].deferred) { |
1058 | 0 | eregs[i].started = TRUE; |
1059 | 0 | if (eregs[i].intext != NULL && |
1060 | 0 | eregs[i].intext->run != NULL && |
1061 | 0 | (ret = eregs[i].intext->run( |
1062 | 0 | eregs[i].ext, &interp->runenv, |
1063 | 0 | eregs[i].context, FALSE)) <= 0) |
1064 | 0 | return ret; |
1065 | 0 | } |
1066 | 0 | } |
1067 | | |
1068 | 0 | return sieve_interpreter_continue(interp, interrupted); |
1069 | 0 | } |
1070 | | |
1071 | | int sieve_interpreter_run(struct sieve_interpreter *interp, |
1072 | | struct sieve_result *result) |
1073 | 0 | { |
1074 | 0 | sieve_interpreter_reset(interp); |
1075 | |
|
1076 | 0 | return sieve_interpreter_start(interp, result, NULL); |
1077 | 0 | } |
1078 | | |
1079 | | /* |
1080 | | * Error handling |
1081 | | */ |
1082 | | |
1083 | | static inline void ATTR_FORMAT(3, 0) |
1084 | | sieve_runtime_logv(const struct sieve_runtime_env *renv, |
1085 | | const struct sieve_error_params *params, |
1086 | | const char *fmt, va_list args) |
1087 | 0 | { |
1088 | 0 | struct sieve_error_params new_params = *params; |
1089 | |
|
1090 | 0 | new_params.event = renv->event; |
1091 | 0 | T_BEGIN { |
1092 | 0 | if (new_params.location == NULL) { |
1093 | 0 | new_params.location = |
1094 | 0 | sieve_runtime_get_full_command_location(renv); |
1095 | 0 | } |
1096 | |
|
1097 | 0 | sieve_logv(renv->ehandler, params, fmt, args); |
1098 | 0 | } T_END; |
1099 | 0 | } |
1100 | | |
1101 | | #undef sieve_runtime_error |
1102 | | void sieve_runtime_error(const struct sieve_runtime_env *renv, |
1103 | | const char *csrc_filename, unsigned int csrc_linenum, |
1104 | | const char *location, const char *fmt, ...) |
1105 | 0 | { |
1106 | 0 | struct sieve_error_params params = { |
1107 | 0 | .log_type = LOG_TYPE_ERROR, |
1108 | 0 | .csrc = { |
1109 | 0 | .filename = csrc_filename, |
1110 | 0 | .linenum = csrc_linenum, |
1111 | 0 | }, |
1112 | 0 | .location = location, |
1113 | 0 | }; |
1114 | 0 | va_list args; |
1115 | |
|
1116 | 0 | va_start(args, fmt); |
1117 | 0 | sieve_runtime_logv(renv, ¶ms, fmt, args); |
1118 | 0 | va_end(args); |
1119 | 0 | } |
1120 | | |
1121 | | #undef sieve_runtime_warning |
1122 | | void sieve_runtime_warning(const struct sieve_runtime_env *renv, |
1123 | | const char *csrc_filename, unsigned int csrc_linenum, |
1124 | | const char *location, const char *fmt, ...) |
1125 | 0 | { |
1126 | 0 | struct sieve_error_params params = { |
1127 | 0 | .log_type = LOG_TYPE_WARNING, |
1128 | 0 | .csrc = { |
1129 | 0 | .filename = csrc_filename, |
1130 | 0 | .linenum = csrc_linenum, |
1131 | 0 | }, |
1132 | 0 | .location = location, |
1133 | 0 | }; |
1134 | 0 | va_list args; |
1135 | |
|
1136 | 0 | va_start(args, fmt); |
1137 | 0 | sieve_runtime_logv(renv, ¶ms, fmt, args); |
1138 | 0 | va_end(args); |
1139 | 0 | } |
1140 | | |
1141 | | #undef sieve_runtime_log |
1142 | | void sieve_runtime_log(const struct sieve_runtime_env *renv, |
1143 | | const char *csrc_filename, unsigned int csrc_linenum, |
1144 | | const char *location, const char *fmt, ...) |
1145 | 0 | { |
1146 | 0 | struct sieve_error_params params = { |
1147 | 0 | .log_type = LOG_TYPE_INFO, |
1148 | 0 | .csrc = { |
1149 | 0 | .filename = csrc_filename, |
1150 | 0 | .linenum = csrc_linenum, |
1151 | 0 | }, |
1152 | 0 | .location = location, |
1153 | 0 | }; |
1154 | 0 | va_list args; |
1155 | |
|
1156 | 0 | va_start(args, fmt); |
1157 | 0 | sieve_runtime_logv(renv, ¶ms, fmt, args); |
1158 | 0 | va_end(args); |
1159 | 0 | } |
1160 | | |
1161 | | #undef sieve_runtime_debug |
1162 | | void sieve_runtime_debug(const struct sieve_runtime_env *renv, |
1163 | | const char *csrc_filename, unsigned int csrc_linenum, |
1164 | | const char *location, const char *fmt, ...) |
1165 | 0 | { |
1166 | 0 | struct sieve_error_params params = { |
1167 | 0 | .log_type = LOG_TYPE_DEBUG, |
1168 | 0 | .csrc = { |
1169 | 0 | .filename = csrc_filename, |
1170 | 0 | .linenum = csrc_linenum, |
1171 | 0 | }, |
1172 | 0 | .location = location, |
1173 | 0 | }; |
1174 | 0 | va_list args; |
1175 | |
|
1176 | 0 | va_start(args, fmt); |
1177 | 0 | sieve_runtime_logv(renv, ¶ms, fmt, args); |
1178 | 0 | va_end(args); |
1179 | 0 | } |
1180 | | |
1181 | | #undef sieve_runtime_critical |
1182 | | void sieve_runtime_critical(const struct sieve_runtime_env *renv, |
1183 | | const char *csrc_filename, |
1184 | | unsigned int csrc_linenum, |
1185 | | const char *location, const char *user_prefix, |
1186 | | const char *fmt, ...) |
1187 | 0 | { |
1188 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
1189 | 0 | struct sieve_error_params params = { |
1190 | 0 | .log_type = LOG_TYPE_ERROR, |
1191 | 0 | .csrc = { |
1192 | 0 | .filename = csrc_filename, |
1193 | 0 | .linenum = csrc_linenum, |
1194 | 0 | }, |
1195 | 0 | .location = location, |
1196 | 0 | }; |
1197 | 0 | va_list args; |
1198 | |
|
1199 | 0 | va_start(args, fmt); |
1200 | |
|
1201 | 0 | params.event = renv->event; |
1202 | 0 | T_BEGIN { |
1203 | 0 | if (params.location == NULL) { |
1204 | 0 | params.location = |
1205 | 0 | sieve_runtime_get_full_command_location(renv); |
1206 | 0 | } |
1207 | |
|
1208 | 0 | sieve_criticalv(eenv->svinst, renv->ehandler, ¶ms, |
1209 | 0 | user_prefix, fmt, args); |
1210 | 0 | } T_END; |
1211 | | |
1212 | 0 | va_end(args); |
1213 | 0 | } |
1214 | | |
1215 | | #undef sieve_runtime_mail_error |
1216 | | int sieve_runtime_mail_error(const struct sieve_runtime_env *renv, |
1217 | | struct mail *mail, |
1218 | | const char *csrc_filename, |
1219 | | unsigned int csrc_linenum, |
1220 | | const char *fmt, ...) |
1221 | 0 | { |
1222 | 0 | const char *error_msg, *user_prefix; |
1223 | 0 | va_list args; |
1224 | |
|
1225 | 0 | error_msg = mailbox_get_last_internal_error(mail->box, NULL); |
1226 | |
|
1227 | 0 | va_start(args, fmt); |
1228 | 0 | user_prefix = t_strdup_vprintf(fmt, args); |
1229 | 0 | sieve_runtime_critical(renv, csrc_filename, csrc_linenum, |
1230 | 0 | NULL, user_prefix, "%s: %s", |
1231 | 0 | user_prefix, error_msg); |
1232 | 0 | va_end(args); |
1233 | |
|
1234 | 0 | return SIEVE_EXEC_TEMP_FAILURE; |
1235 | 0 | } |