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