1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "array.h"
6 #include "str-sanitize.h"
7 #include "home-expand.h"
8
9 #include "sieve-common.h"
10 #include "sieve-settings.h"
11 #include "sieve-error.h"
12 #include "sieve-script.h"
13 #include "sieve-storage.h"
14 #include "sieve-ast.h"
15 #include "sieve-binary.h"
16 #include "sieve-commands.h"
17 #include "sieve-validator.h"
18 #include "sieve-generator.h"
19 #include "sieve-interpreter.h"
20
21 #include "ext-include-common.h"
22 #include "ext-include-limits.h"
23 #include "ext-include-binary.h"
24 #include "ext-include-variables.h"
25
26
27 /*
28 * Forward declarations
29 */
30
31 /* Generator context */
32
33 struct ext_include_generator_context {
34 unsigned int nesting_depth;
35 struct sieve_script *script;
36 struct ext_include_generator_context *parent;
37 };
38
39 static inline struct ext_include_generator_context *
40 ext_include_get_generator_context(const struct sieve_extension *ext_this,
41 struct sieve_generator *gentr);
42
43 /* Interpreter context */
44
45 struct ext_include_interpreter_global {
46 ARRAY(struct sieve_script *) included_scripts;
47
48 struct sieve_variable_scope_binary *var_scope;
49 struct sieve_variable_storage *var_storage;
50 };
51
52 struct ext_include_interpreter_context {
53 struct ext_include_interpreter_context *parent;
54 struct ext_include_interpreter_global *global;
55
56 struct sieve_interpreter *interp;
57 pool_t pool;
58
59 unsigned int nesting_depth;
60
61 struct sieve_script *script;
62 const struct ext_include_script_info *script_info;
63
64 const struct ext_include_script_info *include;
65 bool returned;
66 };
67
68 /*
69 * Extension configuration
70 */
71
72 /* Extension hooks */
73
ext_include_load(const struct sieve_extension * ext,void ** context)74 bool ext_include_load(const struct sieve_extension *ext, void **context)
75 {
76 struct sieve_instance *svinst = ext->svinst;
77 struct ext_include_context *ctx;
78 const char *location;
79 unsigned long long int uint_setting;
80
81 if (*context != NULL)
82 ext_include_unload(ext);
83
84 ctx = i_new(struct ext_include_context, 1);
85
86 /* Get location for :global scripts */
87 location = sieve_setting_get(svinst, "sieve_global");
88 if (location == NULL)
89 location = sieve_setting_get(svinst, "sieve_global_dir");
90
91 if (location == NULL) {
92 e_debug(svinst->event, "include: "
93 "sieve_global is not set; "
94 "it is currently not possible to include `:global' scripts.");
95 }
96
97 ctx->global_location = i_strdup(location);
98
99 /* Get limits */
100 ctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH;
101 ctx->max_includes = EXT_INCLUDE_DEFAULT_MAX_INCLUDES;
102
103 if (sieve_setting_get_uint_value(
104 svinst, "sieve_include_max_nesting_depth", &uint_setting))
105 ctx->max_nesting_depth = (unsigned int)uint_setting;
106 if (sieve_setting_get_uint_value(
107 svinst, "sieve_include_max_includes", &uint_setting))
108 ctx->max_includes = (unsigned int)uint_setting;
109
110 /* Extension dependencies */
111 ctx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
112
113 *context = (void *)ctx;
114 return TRUE;
115 }
116
ext_include_unload(const struct sieve_extension * ext)117 void ext_include_unload(const struct sieve_extension *ext)
118 {
119 struct ext_include_context *ctx =
120 (struct ext_include_context *)ext->context;
121
122 if (ctx->global_storage != NULL)
123 sieve_storage_unref(&ctx->global_storage);
124 if (ctx->personal_storage != NULL)
125 sieve_storage_unref(&ctx->personal_storage);
126
127 i_free(ctx->global_location);
128 i_free(ctx);
129 }
130
131 /*
132 * Script access
133 */
134
135 struct sieve_storage *
ext_include_get_script_storage(const struct sieve_extension * ext,enum ext_include_script_location location,const char * script_name,enum sieve_error * error_r)136 ext_include_get_script_storage(const struct sieve_extension *ext,
137 enum ext_include_script_location location,
138 const char *script_name,
139 enum sieve_error *error_r)
140 {
141 struct sieve_instance *svinst = ext->svinst;
142 struct ext_include_context *ctx =
143 (struct ext_include_context *)ext->context;
144
145 switch (location) {
146 case EXT_INCLUDE_LOCATION_PERSONAL:
147 if (ctx->personal_storage == NULL) {
148 ctx->personal_storage =
149 sieve_storage_create_main(svinst, NULL, 0,
150 error_r);
151 }
152 return ctx->personal_storage;
153 case EXT_INCLUDE_LOCATION_GLOBAL:
154 if (ctx->global_location == NULL) {
155 e_info(svinst->event, "include: "
156 "sieve_global is unconfigured; "
157 "include of `:global' script `%s' is therefore not possible",
158 str_sanitize(script_name, 80));
159 if (error_r != NULL)
160 *error_r = SIEVE_ERROR_NOT_FOUND;
161 return NULL;
162 }
163 if (ctx->global_storage == NULL) {
164 ctx->global_storage = sieve_storage_create(
165 svinst, ctx->global_location, 0, error_r);
166 }
167 return ctx->global_storage;
168 default:
169 break;
170 }
171
172 i_unreached();
173 return NULL;
174 }
175
176 /*
177 * AST context management
178 */
179
180 static void
ext_include_ast_free(const struct sieve_extension * ext ATTR_UNUSED,struct sieve_ast * ast ATTR_UNUSED,void * context)181 ext_include_ast_free(const struct sieve_extension *ext ATTR_UNUSED,
182 struct sieve_ast *ast ATTR_UNUSED, void *context)
183 {
184 struct ext_include_ast_context *actx =
185 (struct ext_include_ast_context *)context;
186 struct sieve_script **scripts;
187 unsigned int count, i;
188
189 /* Unreference included scripts */
190 scripts = array_get_modifiable(&actx->included_scripts, &count);
191 for (i = 0; i < count; i++) {
192 sieve_script_unref(&scripts[i]);
193 }
194
195 /* Unreference variable scopes */
196 if (actx->global_vars != NULL)
197 sieve_variable_scope_unref(&actx->global_vars);
198 }
199
200 static const struct sieve_ast_extension include_ast_extension = {
201 &include_extension,
202 ext_include_ast_free
203 };
204
205 struct ext_include_ast_context *
ext_include_create_ast_context(const struct sieve_extension * this_ext,struct sieve_ast * ast,struct sieve_ast * parent)206 ext_include_create_ast_context(const struct sieve_extension *this_ext,
207 struct sieve_ast *ast, struct sieve_ast *parent)
208 {
209 struct ext_include_ast_context *actx;
210
211 pool_t pool = sieve_ast_pool(ast);
212 actx = p_new(pool, struct ext_include_ast_context, 1);
213 p_array_init(&actx->included_scripts, pool, 32);
214
215 if (parent != NULL) {
216 struct ext_include_ast_context *parent_ctx =
217 (struct ext_include_ast_context *)
218 sieve_ast_extension_get_context(parent, this_ext);
219
220 actx->global_vars = parent_ctx->global_vars;
221 i_assert(actx->global_vars != NULL);
222
223 sieve_variable_scope_ref(actx->global_vars);
224 } else {
225 struct ext_include_context *ectx =
226 ext_include_get_context(this_ext);
227
228 actx->global_vars = sieve_variable_scope_create(
229 this_ext->svinst, ectx->var_ext, this_ext);
230 }
231
232 sieve_ast_extension_register(ast, this_ext, &include_ast_extension,
233 (void *)actx);
234 return actx;
235 }
236
237 struct ext_include_ast_context *
ext_include_get_ast_context(const struct sieve_extension * this_ext,struct sieve_ast * ast)238 ext_include_get_ast_context(const struct sieve_extension *this_ext,
239 struct sieve_ast *ast)
240 {
241 struct ext_include_ast_context *actx =
242 (struct ext_include_ast_context *)
243 sieve_ast_extension_get_context(ast, this_ext);
244
245 if (actx != NULL)
246 return actx;
247 return ext_include_create_ast_context(this_ext, ast, NULL);
248 }
249
ext_include_ast_link_included_script(const struct sieve_extension * this_ext,struct sieve_ast * ast,struct sieve_script * script)250 void ext_include_ast_link_included_script(
251 const struct sieve_extension *this_ext, struct sieve_ast *ast,
252 struct sieve_script *script)
253 {
254 struct ext_include_ast_context *actx =
255 ext_include_get_ast_context(this_ext, ast);
256
257 array_append(&actx->included_scripts, &script, 1);
258 }
259
ext_include_validator_have_variables(const struct sieve_extension * this_ext,struct sieve_validator * valdtr)260 bool ext_include_validator_have_variables(
261 const struct sieve_extension *this_ext, struct sieve_validator *valdtr)
262 {
263 struct ext_include_context *ectx = ext_include_get_context(this_ext);
264
265 return sieve_ext_variables_is_active(ectx->var_ext, valdtr);
266 }
267
268 /*
269 * Generator context management
270 */
271
272 static struct ext_include_generator_context *
ext_include_create_generator_context(struct sieve_generator * gentr,struct ext_include_generator_context * parent,struct sieve_script * script)273 ext_include_create_generator_context(
274 struct sieve_generator *gentr,
275 struct ext_include_generator_context *parent,
276 struct sieve_script *script)
277 {
278 struct ext_include_generator_context *ctx;
279
280 pool_t pool = sieve_generator_pool(gentr);
281 ctx = p_new(pool, struct ext_include_generator_context, 1);
282 ctx->parent = parent;
283 ctx->script = script;
284 if (parent == NULL)
285 ctx->nesting_depth = 0;
286 else
287 ctx->nesting_depth = parent->nesting_depth + 1;
288
289 return ctx;
290 }
291
292 static inline struct ext_include_generator_context *
ext_include_get_generator_context(const struct sieve_extension * this_ext,struct sieve_generator * gentr)293 ext_include_get_generator_context(const struct sieve_extension *this_ext,
294 struct sieve_generator *gentr)
295 {
296 return (struct ext_include_generator_context *)
297 sieve_generator_extension_get_context(gentr, this_ext);
298 }
299
300 static inline void
ext_include_initialize_generator_context(const struct sieve_extension * this_ext,struct sieve_generator * gentr,struct ext_include_generator_context * parent,struct sieve_script * script)301 ext_include_initialize_generator_context(
302 const struct sieve_extension *this_ext, struct sieve_generator *gentr,
303 struct ext_include_generator_context *parent,
304 struct sieve_script *script)
305 {
306 sieve_generator_extension_set_context(
307 gentr, this_ext,
308 ext_include_create_generator_context(gentr, parent, script));
309 }
310
ext_include_register_generator_context(const struct sieve_extension * this_ext,const struct sieve_codegen_env * cgenv)311 void ext_include_register_generator_context(
312 const struct sieve_extension *this_ext,
313 const struct sieve_codegen_env *cgenv)
314 {
315 struct ext_include_generator_context *ctx =
316 ext_include_get_generator_context(this_ext, cgenv->gentr);
317
318 /* Initialize generator context if necessary */
319 if (ctx == NULL) {
320 ctx = ext_include_create_generator_context(
321 cgenv->gentr, NULL, cgenv->script);
322
323 sieve_generator_extension_set_context(
324 cgenv->gentr, this_ext, (void *)ctx);
325 }
326
327 /* Initialize ast context if necessary */
328 (void)ext_include_get_ast_context(this_ext, cgenv->ast);
329 (void)ext_include_binary_init(this_ext, cgenv->sbin, cgenv->ast);
330 }
331
332 /*
333 * Runtime initialization
334 */
335
336 static int
ext_include_runtime_init(const struct sieve_extension * this_ext,const struct sieve_runtime_env * renv,void * context,bool deferred ATTR_UNUSED)337 ext_include_runtime_init(const struct sieve_extension *this_ext,
338 const struct sieve_runtime_env *renv,
339 void *context, bool deferred ATTR_UNUSED)
340 {
341 struct ext_include_interpreter_context *ctx =
342 (struct ext_include_interpreter_context *)context;
343 struct ext_include_context *ectx = ext_include_get_context(this_ext);
344
345 if (ctx->parent == NULL) {
346 ctx->global = p_new(ctx->pool,
347 struct ext_include_interpreter_global, 1);
348 p_array_init(&ctx->global->included_scripts, ctx->pool, 10);
349
350 ctx->global->var_scope =
351 ext_include_binary_get_global_scope(
352 this_ext, renv->sbin);
353 ctx->global->var_storage =
354 sieve_variable_storage_create(ectx->var_ext, ctx->pool,
355 ctx->global->var_scope);
356 } else {
357 ctx->global = ctx->parent->global;
358 }
359
360 sieve_ext_variables_runtime_set_storage(ectx->var_ext, renv, this_ext,
361 ctx->global->var_storage);
362 return SIEVE_EXEC_OK;
363 }
364
365 static struct sieve_interpreter_extension include_interpreter_extension = {
366 .ext_def = &include_extension,
367 .run = ext_include_runtime_init
368 };
369
370 /*
371 * Interpreter context management
372 */
373
374 static struct ext_include_interpreter_context *
ext_include_interpreter_context_create(struct sieve_interpreter * interp,struct ext_include_interpreter_context * parent,struct sieve_script * script,const struct ext_include_script_info * sinfo)375 ext_include_interpreter_context_create(
376 struct sieve_interpreter *interp,
377 struct ext_include_interpreter_context *parent,
378 struct sieve_script *script,
379 const struct ext_include_script_info *sinfo)
380 {
381 struct ext_include_interpreter_context *ctx;
382
383 pool_t pool = sieve_interpreter_pool(interp);
384 ctx = p_new(pool, struct ext_include_interpreter_context, 1);
385 ctx->pool = pool;
386 ctx->parent = parent;
387 ctx->interp = interp;
388 ctx->script = script;
389 ctx->script_info = sinfo;
390
391 if (parent == NULL)
392 ctx->nesting_depth = 0;
393 else
394 ctx->nesting_depth = parent->nesting_depth + 1;
395 return ctx;
396 }
397
398 static inline struct ext_include_interpreter_context *
ext_include_get_interpreter_context(const struct sieve_extension * this_ext,struct sieve_interpreter * interp)399 ext_include_get_interpreter_context(const struct sieve_extension *this_ext,
400 struct sieve_interpreter *interp)
401 {
402 return (struct ext_include_interpreter_context *)
403 sieve_interpreter_extension_get_context(interp, this_ext);
404 }
405
406 static inline struct ext_include_interpreter_context *
ext_include_interpreter_context_init_child(const struct sieve_extension * this_ext,struct sieve_interpreter * interp,struct ext_include_interpreter_context * parent,struct sieve_script * script,const struct ext_include_script_info * sinfo)407 ext_include_interpreter_context_init_child(
408 const struct sieve_extension *this_ext,
409 struct sieve_interpreter *interp,
410 struct ext_include_interpreter_context *parent,
411 struct sieve_script *script,
412 const struct ext_include_script_info *sinfo)
413 {
414 struct ext_include_interpreter_context *ctx =
415 ext_include_interpreter_context_create(interp, parent,
416 script, sinfo);
417
418 sieve_interpreter_extension_register(interp, this_ext,
419 &include_interpreter_extension,
420 ctx);
421 return ctx;
422 }
423
ext_include_interpreter_context_init(const struct sieve_extension * this_ext,struct sieve_interpreter * interp)424 void ext_include_interpreter_context_init(
425 const struct sieve_extension *this_ext,
426 struct sieve_interpreter *interp)
427 {
428 struct ext_include_interpreter_context *ctx =
429 ext_include_get_interpreter_context(this_ext, interp);
430
431 /* Is this is the top-level interpreter ? */
432 if (ctx == NULL) {
433 struct sieve_script *script;
434
435 /* Initialize top context */
436 script = sieve_interpreter_script(interp);
437 ctx = ext_include_interpreter_context_create(interp, NULL,
438 script, NULL);
439
440 sieve_interpreter_extension_register(
441 interp, this_ext, &include_interpreter_extension,
442 (void *)ctx);
443 }
444 }
445
446 struct sieve_variable_storage *
ext_include_interpreter_get_global_variables(const struct sieve_extension * this_ext,struct sieve_interpreter * interp)447 ext_include_interpreter_get_global_variables(
448 const struct sieve_extension *this_ext,
449 struct sieve_interpreter *interp)
450 {
451 struct ext_include_interpreter_context *ctx =
452 ext_include_get_interpreter_context(this_ext, interp);
453
454 return ctx->global->var_storage;
455 }
456
457 /*
458 * Including a script during code generation
459 */
460
ext_include_generate_include(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd,enum ext_include_script_location location,enum ext_include_flags flags,struct sieve_script * script,const struct ext_include_script_info ** included_r)461 int ext_include_generate_include(
462 const struct sieve_codegen_env *cgenv, struct sieve_command *cmd,
463 enum ext_include_script_location location, enum ext_include_flags flags,
464 struct sieve_script *script,
465 const struct ext_include_script_info **included_r)
466 {
467 const struct sieve_extension *this_ext = cmd->ext;
468 struct ext_include_context *ext_ctx =
469 (struct ext_include_context *)this_ext->context;
470 int result = 1;
471 struct sieve_ast *ast;
472 struct sieve_binary *sbin = cgenv->sbin;
473 struct sieve_generator *gentr = cgenv->gentr;
474 struct ext_include_binary_context *binctx;
475 struct sieve_generator *subgentr;
476 struct ext_include_generator_context *ctx =
477 ext_include_get_generator_context(this_ext, gentr);
478 struct ext_include_generator_context *pctx;
479 struct sieve_error_handler *ehandler =
480 sieve_generator_error_handler(gentr);
481 struct ext_include_script_info *included;
482
483 *included_r = NULL;
484
485 /* Just to be sure: do not include more scripts when errors have occured
486 already.
487 */
488 if (sieve_get_errors(ehandler) > 0)
489 return -1;
490
491 /* Limit nesting level */
492 if (ctx->nesting_depth >= ext_ctx->max_nesting_depth) {
493 sieve_command_generate_error(
494 gentr, cmd,
495 "cannot nest includes deeper than %d levels",
496 ext_ctx->max_nesting_depth);
497 return -1;
498 }
499
500 /* Check for circular include */
501 if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0) {
502 pctx = ctx;
503 while (pctx != NULL) {
504 if (sieve_script_equals(pctx->script, script)) {
505 /* Just drop circular include when uploading
506 inactive script; not an error
507 */
508 if ((cgenv->flags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 &&
509 (cgenv->flags & SIEVE_COMPILE_FLAG_ACTIVATED) == 0) {
510 sieve_command_generate_warning(
511 gentr, cmd,
512 "circular include (ignored during upload)");
513 return 0;
514 }
515
516 sieve_command_generate_error(gentr, cmd,
517 "circular include");
518 return -1;
519 }
520
521 pctx = pctx->parent;
522 }
523 }
524
525 /* Get binary context */
526 binctx = ext_include_binary_init(this_ext, sbin, cgenv->ast);
527
528 /* Is the script already compiled into the current binary? */
529 included = ext_include_binary_script_get_include_info(binctx, script);
530 if (included != NULL) {
531 /* Yes, only update flags */
532 if ((flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0)
533 included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_OPTIONAL);
534 if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0)
535 included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_ONCE);
536 } else {
537 const char *script_name = sieve_script_name(script);
538 enum sieve_compile_flags cpflags = cgenv->flags;
539
540 /* No, include new script */
541
542 /* Check whether include limit is exceeded */
543 if (ext_include_binary_script_get_count(binctx) >=
544 ext_ctx->max_includes) {
545 sieve_command_generate_error(
546 gentr, cmd, "failed to include script '%s': "
547 "no more than %u includes allowed",
548 str_sanitize(script_name, 80),
549 ext_ctx->max_includes);
550 return -1;
551 }
552
553 /* Allocate a new block in the binary and mark the script as
554 included. */
555 if (!sieve_script_is_open(script)) {
556 /* Just making an empty entry to mark a missing script
557 */
558 i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 ||
559 (flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0);
560 included = ext_include_binary_script_include(
561 binctx, location, flags, script, NULL);
562 result = 0;
563
564 } else {
565 struct sieve_binary_block *inc_block =
566 sieve_binary_block_create(sbin);
567
568 /* Real include */
569 included = ext_include_binary_script_include(
570 binctx, location, flags, script, inc_block);
571
572 /* Parse */
573 if ((ast = sieve_parse(script, ehandler,
574 NULL)) == NULL) {
575 sieve_command_generate_error(
576 gentr, cmd,
577 "failed to parse included script '%s'",
578 str_sanitize(script_name, 80));
579 return -1;
580 }
581
582 /* Included scripts inherit global variable scope */
583 (void)ext_include_create_ast_context(
584 this_ext, ast, cmd->ast_node->ast);
585
586 if (location == EXT_INCLUDE_LOCATION_GLOBAL) {
587 cpflags &=
588 ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL);
589 } else {
590 cpflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
591 }
592
593 /* Validate */
594 if (!sieve_validate(ast, ehandler, cpflags, NULL)) {
595 sieve_command_generate_error(
596 gentr, cmd,
597 "failed to validate included script '%s'",
598 str_sanitize(script_name, 80));
599 sieve_ast_unref(&ast);
600 return -1;
601 }
602
603 /* Generate
604
605 FIXME: It might not be a good idea to recurse code
606 generation for included scripts.
607 */
608 subgentr = sieve_generator_create(ast, ehandler, cpflags);
609 ext_include_initialize_generator_context(
610 cmd->ext, subgentr, ctx, script);
611
612 if (sieve_generator_run(subgentr, &inc_block) == NULL) {
613 sieve_command_generate_error(
614 gentr, cmd,
615 "failed to generate code for included script '%s'",
616 str_sanitize(script_name, 80));
617 result = -1;
618 }
619
620 sieve_generator_free(&subgentr);
621
622 /* Cleanup */
623 sieve_ast_unref(&ast);
624 }
625 }
626
627 if (result > 0)
628 *included_r = included;
629 return result;
630 }
631
632 /*
633 * Executing an included script during interpretation
634 */
635
636 static bool
ext_include_runtime_check_circular(struct ext_include_interpreter_context * ctx,const struct ext_include_script_info * include)637 ext_include_runtime_check_circular(
638 struct ext_include_interpreter_context *ctx,
639 const struct ext_include_script_info *include)
640 {
641 struct ext_include_interpreter_context *pctx;
642
643 pctx = ctx;
644 while (pctx != NULL) {
645
646 if (sieve_script_equals(include->script, pctx->script))
647 return TRUE;
648
649 pctx = pctx->parent;
650 }
651
652 return FALSE;
653 }
654
655 static bool
ext_include_runtime_include_mark(struct ext_include_interpreter_context * ctx,const struct ext_include_script_info * include,bool once)656 ext_include_runtime_include_mark(struct ext_include_interpreter_context *ctx,
657 const struct ext_include_script_info *include,
658 bool once)
659 {
660 struct sieve_script *const *includes;
661 unsigned int count, i;
662
663 includes = array_get(&ctx->global->included_scripts, &count);
664 for (i = 0; i < count; i++) {
665 if (sieve_script_equals(include->script, includes[i]))
666 return (!once);
667 }
668
669 array_append(&ctx->global->included_scripts, &include->script, 1);
670 return TRUE;
671 }
672
ext_include_execute_include(const struct sieve_runtime_env * renv,unsigned int include_id,enum ext_include_flags flags)673 int ext_include_execute_include(const struct sieve_runtime_env *renv,
674 unsigned int include_id,
675 enum ext_include_flags flags)
676 {
677 const struct sieve_execute_env *eenv = renv->exec_env;
678 const struct sieve_extension *this_ext = renv->oprtn->ext;
679 int result = SIEVE_EXEC_OK;
680 struct ext_include_interpreter_context *ctx;
681 const struct ext_include_script_info *included;
682 struct ext_include_binary_context *binctx =
683 ext_include_binary_get_context(this_ext, renv->sbin);
684 bool once = ((flags & EXT_INCLUDE_FLAG_ONCE) != 0);
685 unsigned int block_id;
686
687 /* Check for invalid include id (== corrupt binary) */
688 included = ext_include_binary_script_get_included(binctx, include_id);
689 if (included == NULL) {
690 sieve_runtime_trace_error(
691 renv, "include: include id %d is invalid", include_id);
692 return SIEVE_EXEC_BIN_CORRUPT;
693 }
694
695 ctx = ext_include_get_interpreter_context(this_ext, renv->interp);
696 block_id = sieve_binary_block_get_id(included->block);
697
698 /* If :once modifier is specified, check for duplicate include */
699 if (ext_include_runtime_include_mark(ctx, included, once)) {
700 sieve_runtime_trace(
701 renv, SIEVE_TRLVL_NONE,
702 "include: start script '%s' [inc id: %d, block: %d]",
703 sieve_script_name(included->script),
704 include_id, block_id);
705 } else {
706 /* skip */
707 sieve_runtime_trace(
708 renv, SIEVE_TRLVL_NONE,
709 "include: skipped include for script '%s' "
710 "[inc id: %d, block: %d]; already run once",
711 sieve_script_name(included->script),
712 include_id, block_id);
713 return result;
714 }
715
716 /* Check circular include during interpretation as well.
717 * Let's not trust binaries.
718 */
719 if (ext_include_runtime_check_circular(ctx, included)) {
720 sieve_runtime_trace_error(renv,
721 "include: circular include of script '%s' "
722 "[inc id: %d, block: %d]",
723 sieve_script_name(included->script),
724 include_id, block_id);
725
726 /* Situation has no valid way to emerge at runtime */
727 return SIEVE_EXEC_BIN_CORRUPT;
728 }
729
730 if (ctx->parent == NULL) {
731 struct ext_include_interpreter_context *curctx = NULL;
732 struct sieve_error_handler *ehandler = renv->ehandler;
733 struct sieve_interpreter *subinterp;
734 bool interrupted = FALSE;
735
736 /* We are the top-level interpreter instance */
737 if (result == SIEVE_EXEC_OK) {
738 struct sieve_execute_env eenv_new = *eenv;
739
740 if (included->location != EXT_INCLUDE_LOCATION_GLOBAL)
741 eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
742 else {
743 eenv_new.flags &=
744 ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL);
745 }
746
747 /* Create interpreter for top-level included script
748 (first sub-interpreter)
749 */
750 subinterp = sieve_interpreter_create_for_block(
751 included->block, included->script, renv->interp,
752 &eenv_new, ehandler);
753 if (subinterp != NULL) {
754 curctx = ext_include_interpreter_context_init_child(
755 this_ext, subinterp, ctx, included->script,
756 included);
757
758 /* Activate and start the top-level included script */
759 result = sieve_interpreter_start(
760 subinterp, renv->result, &interrupted);
761 } else {
762 result = SIEVE_EXEC_BIN_CORRUPT;
763 }
764 }
765
766 /* Included scripts can have includes of their own. This is not
767 implemented recursively. Rather, the sub-interpreter
768 interrupts and defers the include to the top-level
769 interpreter, which is here. */
770 if (result == SIEVE_EXEC_OK && interrupted &&
771 !curctx->returned) {
772 while (result == SIEVE_EXEC_OK) {
773 if (((interrupted && curctx->returned) ||
774 (!interrupted)) &&
775 curctx->parent != NULL) {
776 const struct ext_include_script_info *ended_script =
777 curctx->script_info;
778
779 /* Sub-interpreter ended or executed
780 return */
781
782 /* Ascend interpreter stack */
783 curctx = curctx->parent;
784 sieve_interpreter_free(&subinterp);
785
786 sieve_runtime_trace(renv, SIEVE_TRLVL_NONE,
787 "include: script '%s' ended "
788 "[inc id: %d, block: %d]",
789 sieve_script_name(ended_script->script),
790 ended_script->id,
791 sieve_binary_block_get_id(ended_script->block));
792
793 /* This is the top-most sub-interpreter,
794 bail out */
795 if (curctx->parent == NULL)
796 break;
797
798 subinterp = curctx->interp;
799
800 /* Continue parent */
801 curctx->include = NULL;
802 curctx->returned = FALSE;
803
804 result = sieve_interpreter_continue(
805 subinterp, &interrupted);
806 } else {
807 if (curctx->include != NULL) {
808 /* Sub-include requested */
809
810 if (result == SIEVE_EXEC_OK) {
811 struct sieve_execute_env eenv_new = *eenv;
812
813 if (curctx->include->location != EXT_INCLUDE_LOCATION_GLOBAL)
814 eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
815 else {
816 eenv_new.flags &=
817 ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL);
818 }
819
820 /* Create sub-interpreter */
821 subinterp = sieve_interpreter_create_for_block(
822 curctx->include->block, curctx->include->script,
823 curctx->interp, &eenv_new, ehandler);
824 if (subinterp != NULL) {
825 curctx = ext_include_interpreter_context_init_child(
826 this_ext, subinterp, curctx,
827 curctx->include->script, curctx->include);
828
829 /* Start the sub-include's interpreter */
830 curctx->include = NULL;
831 curctx->returned = FALSE;
832 result = sieve_interpreter_start(
833 subinterp, renv->result, &interrupted);
834 } else {
835 result = SIEVE_EXEC_BIN_CORRUPT;
836 }
837 }
838 } else {
839 /* Sub-interpreter was interrupted outside
840 this extension, probably stop command was
841 executed. Generate an interrupt ourselves,
842 ending all script execution. */
843 sieve_interpreter_interrupt(renv->interp);
844 break;
845 }
846 }
847 }
848 }
849
850 /* Free any sub-interpreters that might still be active */
851 while (curctx != NULL && curctx->parent != NULL) {
852 struct ext_include_interpreter_context *nextctx =
853 curctx->parent;
854 struct sieve_interpreter *killed_interp = curctx->interp;
855 const struct ext_include_script_info *ended_script =
856 curctx->script_info;
857
858 /* This kills curctx too */
859 sieve_interpreter_free(&killed_interp);
860
861 sieve_runtime_trace(
862 renv, SIEVE_TRLVL_NONE,
863 "include: script '%s' ended [id: %d, block: %d]",
864 sieve_script_name(ended_script->script),
865 ended_script->id,
866 sieve_binary_block_get_id(ended_script->block));
867
868 /* Luckily we recorded the parent earlier */
869 curctx = nextctx;
870 }
871
872 } else {
873 /* We are an included script already, defer inclusion to main
874 interpreter */
875 ctx->include = included;
876 sieve_interpreter_interrupt(renv->interp);
877 }
878
879 return result;
880 }
881
ext_include_execute_return(const struct sieve_runtime_env * renv)882 void ext_include_execute_return(const struct sieve_runtime_env *renv)
883 {
884 const struct sieve_extension *this_ext = renv->oprtn->ext;
885 struct ext_include_interpreter_context *ctx =
886 ext_include_get_interpreter_context(this_ext, renv->interp);
887
888 sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
889 "return: exiting included script");
890 ctx->returned = TRUE;
891 sieve_interpreter_interrupt(renv->interp);
892 }
893