1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
6
#include "str-sanitize.h"
7
#include "home-expand.h"
9
#include "sieve-common.h"
10
#include "sieve-error.h"
11
#include "sieve-script.h"
12
#include "sieve-ast.h"
13
#include "sieve-binary.h"
14
#include "sieve-commands.h"
15
#include "sieve-generator.h"
16
#include "sieve-interpreter.h"
18
#include "ext-include-common.h"
19
#include "ext-include-limits.h"
20
#include "ext-include-binary.h"
21
#include "ext-include-variables.h"
26
* Forward declarations
29
/* Generator context */
31
struct ext_include_generator_context {
32
unsigned int nesting_level;
33
struct sieve_script *script;
34
struct ext_include_generator_context *parent;
37
static inline struct ext_include_generator_context *
38
ext_include_get_generator_context
39
(struct sieve_generator *gentr);
41
/* Interpreter context */
43
struct ext_include_interpreter_global {
44
ARRAY_DEFINE(included_scripts, struct sieve_script *);
46
struct sieve_variable_storage *variables;
49
struct ext_include_interpreter_context {
50
struct ext_include_interpreter_context *parent;
51
struct ext_include_interpreter_global *global;
53
struct sieve_interpreter *interp;
56
unsigned int nesting_level;
58
struct sieve_script *script;
59
const struct ext_include_script_info *script_info;
61
const struct ext_include_script_info *include;
69
const char *ext_include_get_script_directory
70
(enum ext_include_script_location location, const char *script_name)
72
const char *home, *sieve_dir;
75
case EXT_INCLUDE_LOCATION_PERSONAL:
76
sieve_dir = getenv("SIEVE_DIR");
77
home = getenv("HOME");
79
if (sieve_dir == NULL) {
82
"include: sieve_dir and home not set for :personal script include "
83
"(wanted script '%s')", str_sanitize(script_name, 80));
87
sieve_dir = "~/sieve";
91
sieve_dir = home_expand_tilde(sieve_dir, home);
94
case EXT_INCLUDE_LOCATION_GLOBAL:
95
sieve_dir = getenv("SIEVE_GLOBAL_DIR");
97
if (sieve_dir == NULL) {
99
"include: sieve_global_dir not set for :global script include "
100
"(wanted script '%s')", str_sanitize(script_name, 80));
114
* AST context management
117
static void ext_include_ast_free
118
(struct sieve_ast *ast ATTR_UNUSED, void *context)
120
struct ext_include_ast_context *actx =
121
(struct ext_include_ast_context *) context;
122
struct sieve_script **scripts;
123
unsigned int count, i;
125
/* Unreference included scripts */
126
scripts = array_get_modifiable(&actx->included_scripts, &count);
127
for ( i = 0; i < count; i++ ) {
128
sieve_script_unref(&scripts[i]);
131
/* Unreference variable scopes */
132
if ( actx->global_vars != NULL )
133
sieve_variable_scope_unref(&actx->global_vars);
136
static const struct sieve_ast_extension include_ast_extension = {
141
struct ext_include_ast_context *ext_include_create_ast_context
142
(struct sieve_ast *ast, struct sieve_ast *parent)
144
struct ext_include_ast_context *actx;
146
pool_t pool = sieve_ast_pool(ast);
147
actx = p_new(pool, struct ext_include_ast_context, 1);
148
p_array_init(&actx->included_scripts, pool, 32);
150
if ( parent != NULL ) {
151
struct ext_include_ast_context *parent_ctx =
152
(struct ext_include_ast_context *)
153
sieve_ast_extension_get_context(parent, &include_extension);
154
actx->global_vars = parent_ctx->global_vars;
156
i_assert( actx->global_vars != NULL );
158
sieve_variable_scope_ref(actx->global_vars);
160
actx->global_vars = sieve_variable_scope_create(&include_extension);
162
sieve_ast_extension_register(ast, &include_ast_extension, (void *) actx);
167
struct ext_include_ast_context *ext_include_get_ast_context
168
(struct sieve_ast *ast)
170
struct ext_include_ast_context *actx = (struct ext_include_ast_context *)
171
sieve_ast_extension_get_context(ast, &include_extension);
173
if ( actx != NULL ) return actx;
175
return ext_include_create_ast_context(ast, NULL);
178
void ext_include_ast_link_included_script
179
(struct sieve_ast *ast, struct sieve_script *script)
181
struct ext_include_ast_context *actx = ext_include_get_ast_context(ast);
183
array_append(&actx->included_scripts, &script, 1);
187
* Generator context management
190
static struct ext_include_generator_context *
191
ext_include_create_generator_context
192
(struct sieve_generator *gentr, struct ext_include_generator_context *parent,
193
struct sieve_script *script)
195
struct ext_include_generator_context *ctx;
197
pool_t pool = sieve_generator_pool(gentr);
198
ctx = p_new(pool, struct ext_include_generator_context, 1);
199
ctx->parent = parent;
200
ctx->script = script;
201
if ( parent == NULL ) {
202
ctx->nesting_level = 0;
204
ctx->nesting_level = parent->nesting_level + 1;
210
static inline struct ext_include_generator_context *
211
ext_include_get_generator_context
212
(struct sieve_generator *gentr)
214
return (struct ext_include_generator_context *)
215
sieve_generator_extension_get_context(gentr, &include_extension);
218
static inline void ext_include_initialize_generator_context
219
(struct sieve_generator *gentr, struct ext_include_generator_context *parent,
220
struct sieve_script *script)
222
sieve_generator_extension_set_context(gentr, &include_extension,
223
ext_include_create_generator_context(gentr, parent, script));
226
void ext_include_register_generator_context
227
(const struct sieve_codegen_env *cgenv)
229
struct ext_include_generator_context *ctx =
230
ext_include_get_generator_context(cgenv->gentr);
232
/* Initialize generator context if necessary */
234
ctx = ext_include_create_generator_context(
235
cgenv->gentr, NULL, cgenv->script);
237
sieve_generator_extension_set_context
238
(cgenv->gentr, &include_extension, (void *) ctx);
241
/* Initialize ast context if necessary */
242
(void)ext_include_get_ast_context(cgenv->ast);
243
(void)ext_include_binary_init(cgenv->sbin, cgenv->ast);
247
* Runtime initialization
250
static void ext_include_runtime_init
251
(const struct sieve_runtime_env *renv, void *context)
253
struct ext_include_interpreter_context *ctx =
254
(struct ext_include_interpreter_context *) context;
256
if ( ctx->parent == NULL ) {
257
ctx->global = p_new(ctx->pool, struct ext_include_interpreter_global, 1);
258
ctx->global->variables = sieve_variable_storage_create
259
(ctx->pool, ext_include_binary_get_global_scope(renv->sbin), 0);
260
p_array_init(&ctx->global->included_scripts, ctx->pool, 10);
262
ctx->global = ctx->parent->global;
265
sieve_ext_variables_set_storage
266
(renv->interp, ctx->global->variables, &include_extension);
269
static struct sieve_interpreter_extension include_interpreter_extension = {
271
ext_include_runtime_init,
276
* Interpreter context management
279
static struct ext_include_interpreter_context *
280
ext_include_interpreter_context_create
281
(struct sieve_interpreter *interp,
282
struct ext_include_interpreter_context *parent,
283
struct sieve_script *script, const struct ext_include_script_info *sinfo)
285
struct ext_include_interpreter_context *ctx;
287
pool_t pool = sieve_interpreter_pool(interp);
288
ctx = p_new(pool, struct ext_include_interpreter_context, 1);
290
ctx->parent = parent;
291
ctx->interp = interp;
292
ctx->script = script;
293
ctx->script_info = sinfo;
295
if ( parent == NULL ) {
296
ctx->nesting_level = 0;
298
ctx->nesting_level = parent->nesting_level + 1;
304
static inline struct ext_include_interpreter_context *
305
ext_include_get_interpreter_context
306
(struct sieve_interpreter *interp)
308
return (struct ext_include_interpreter_context *)
309
sieve_interpreter_extension_get_context(interp, &include_extension);
312
static inline struct ext_include_interpreter_context *
313
ext_include_interpreter_context_init_child
314
(struct sieve_interpreter *interp,
315
struct ext_include_interpreter_context *parent,
316
struct sieve_script *script, const struct ext_include_script_info *sinfo)
318
struct ext_include_interpreter_context *ctx =
319
ext_include_interpreter_context_create(interp, parent, script, sinfo);
321
sieve_interpreter_extension_register
322
(interp, &include_interpreter_extension, ctx);
327
void ext_include_interpreter_context_init
328
(struct sieve_interpreter *interp)
330
struct ext_include_interpreter_context *ctx =
331
ext_include_get_interpreter_context(interp);
333
/* Is this is the top-level interpreter ? */
335
struct sieve_script *script;
337
/* Initialize top context */
338
script = sieve_interpreter_script(interp);
339
ctx = ext_include_interpreter_context_create
340
(interp, NULL, script, NULL);
342
sieve_interpreter_extension_register
343
(interp, &include_interpreter_extension, (void *) ctx);
347
struct sieve_variable_storage *ext_include_interpreter_get_global_variables
348
(struct sieve_interpreter *interp)
350
struct ext_include_interpreter_context *ctx =
351
ext_include_get_interpreter_context(interp);
353
return ctx->global->variables;
357
* Including a script during code generation
360
bool ext_include_generate_include
361
(const struct sieve_codegen_env *cgenv, struct sieve_command_context *cmd,
362
enum ext_include_script_location location, struct sieve_script *script,
363
const struct ext_include_script_info **included_r, bool once)
366
struct sieve_ast *ast;
367
struct sieve_binary *sbin = cgenv->sbin;
368
struct sieve_generator *gentr = cgenv->gentr;
369
struct ext_include_binary_context *binctx;
370
struct sieve_generator *subgentr;
371
struct ext_include_generator_context *ctx =
372
ext_include_get_generator_context(gentr);
373
struct ext_include_generator_context *pctx;
374
struct sieve_error_handler *ehandler = sieve_generator_error_handler(gentr);
375
const struct ext_include_script_info *included;
379
/* Just to be sure: do not include more scripts when errors have occured
382
if ( sieve_get_errors(ehandler) > 0 )
385
/* Limit nesting level */
386
if ( ctx->nesting_level >= EXT_INCLUDE_MAX_NESTING_LEVEL ) {
387
sieve_command_generate_error
388
(gentr, cmd, "cannot nest includes deeper than %d levels",
389
EXT_INCLUDE_MAX_NESTING_LEVEL);
393
/* Check for circular include */
396
while ( pctx != NULL ) {
397
if ( sieve_script_equals(pctx->script, script) ) {
398
sieve_command_generate_error(gentr, cmd, "circular include");
407
/* Get binary context */
408
binctx = ext_include_binary_init(sbin, cgenv->ast);
410
/* Is the script already compiled into the current binary? */
411
if ( !ext_include_binary_script_is_included(binctx, script, &included) )
413
unsigned int inc_block_id, this_block_id;
414
const char *script_name = sieve_script_name(script);
416
/* Check whether include limit is exceeded */
417
if ( ext_include_binary_script_get_count(binctx) >=
418
EXT_INCLUDE_MAX_INCLUDES ) {
419
sieve_command_generate_error(gentr, cmd,
420
"failed to include script '%s': no more than %u includes allowed",
421
str_sanitize(script_name, 80), EXT_INCLUDE_MAX_INCLUDES);
425
/* No, allocate a new block in the binary and mark the script as included.
427
inc_block_id = sieve_binary_block_create(sbin);
428
included = ext_include_binary_script_include
429
(binctx, script, location, inc_block_id);
432
if ( (ast = sieve_parse(script, ehandler)) == NULL ) {
433
sieve_command_generate_error(gentr, cmd,
434
"failed to parse included script '%s'", str_sanitize(script_name, 80));
438
/* Included scripts inherit global variable scope */
439
(void)ext_include_create_ast_context(ast, cmd->ast_node->ast);
442
if ( !sieve_validate(ast, ehandler) ) {
443
sieve_command_generate_error(gentr, cmd,
444
"failed to validate included script '%s'", str_sanitize(script_name, 80));
445
sieve_ast_unref(&ast);
451
* FIXME: It might not be a good idea to recurse code generation for
454
if ( sieve_binary_block_set_active(sbin, inc_block_id, &this_block_id) ) {
455
subgentr = sieve_generator_create(ast, ehandler);
456
ext_include_initialize_generator_context(subgentr, ctx, script);
458
if ( !sieve_generator_run(subgentr, &sbin) ) {
459
sieve_command_generate_error(gentr, cmd,
460
"failed to generate code for included script '%s'",
461
str_sanitize(script_name, 80));
466
(void) sieve_binary_block_set_active(sbin, this_block_id, NULL);
467
sieve_generator_free(&subgentr);
469
sieve_sys_error("include: failed to activate binary block %d for "
470
"generating code for the included script", inc_block_id);
475
sieve_ast_unref(&ast);
478
if ( result ) *included_r = included;
484
* Executing an included script during interpretation
487
static int ext_include_runtime_check_circular
488
(struct ext_include_interpreter_context *ctx,
489
const struct ext_include_script_info *include)
491
struct ext_include_interpreter_context *pctx;
494
while ( pctx != NULL ) {
496
if ( sieve_script_equals(include->script, pctx->script) )
505
static bool ext_include_runtime_include_mark
506
(struct ext_include_interpreter_context *ctx,
507
const struct ext_include_script_info *include, bool once)
509
struct sieve_script *const *includes;
510
unsigned int count, i;
512
includes = array_get(&ctx->global->included_scripts, &count);
513
for ( i = 0; i < count; i++ ) {
514
if ( sieve_script_equals(include->script, includes[i]) )
518
array_append(&ctx->global->included_scripts, &include->script, 1);
523
int ext_include_execute_include
524
(const struct sieve_runtime_env *renv, unsigned int include_id, bool once)
526
int result = SIEVE_EXEC_OK;
527
struct ext_include_interpreter_context *ctx;
528
const struct ext_include_script_info *included;
529
struct ext_include_binary_context *binctx =
530
ext_include_binary_get_context(renv->sbin);
532
/* Check for invalid include id (== corrupt binary) */
533
included = ext_include_binary_script_get_included(binctx, include_id);
534
if ( included == NULL ) {
535
sieve_runtime_trace_error(renv, "invalid include id: %d", include_id);
536
return SIEVE_EXEC_BIN_CORRUPT;
539
ctx = ext_include_get_interpreter_context(renv->interp);
541
sieve_runtime_trace(renv,
542
"INCLUDE command (script: %s, id: %d block: %d) START::",
543
sieve_script_name(included->script), include_id, included->block_id);
545
/* If :once modifier is specified, check for duplicate include */
546
if ( !ext_include_runtime_include_mark(ctx, included, once) ) {
549
sieve_runtime_trace(renv,
550
"INCLUDE command (block: %d) SKIPPED ::", included->block_id);
554
/* Check circular include during interpretation as well.
555
* Let's not trust binaries.
557
if ( ext_include_runtime_check_circular(ctx, included) ) {
558
sieve_runtime_trace_error(renv,
559
"circular include for script: %s [%d]",
560
sieve_script_name(included->script), included->block_id);
562
/* Situation has no valid way to emerge at runtime */
563
return SIEVE_EXEC_BIN_CORRUPT;
566
if ( ctx->parent == NULL ) {
567
struct ext_include_interpreter_context *curctx = NULL;
568
struct sieve_error_handler *ehandler =
569
sieve_interpreter_get_error_handler(renv->interp);
570
struct sieve_interpreter *subinterp;
571
unsigned int this_block_id;
572
bool interrupted = FALSE;
574
/* We are the top-level interpreter instance */
576
/* Activate block for included script */
577
if ( !sieve_binary_block_set_active
578
(renv->sbin, included->block_id, &this_block_id) ) {
579
sieve_runtime_trace_error(renv, "invalid block id: %d",
581
result = SIEVE_EXEC_BIN_CORRUPT;
584
if ( result == SIEVE_EXEC_OK ) {
585
/* Create interpreter for top-level included script
586
* (first sub-interpreter)
588
subinterp = sieve_interpreter_create(renv->sbin, ehandler);
590
if ( subinterp != NULL ) {
591
curctx = ext_include_interpreter_context_init_child
592
(subinterp, ctx, included->script, included);
594
/* Activate and start the top-level included script */
595
result = ( sieve_interpreter_start
596
(subinterp, renv->msgdata, renv->scriptenv, renv->result,
597
&interrupted) == 1 );
599
result = SIEVE_EXEC_BIN_CORRUPT;
602
/* Included scripts can have includes of their own. This is not implemented
603
* recursively. Rather, the sub-interpreter interrupts and defers the
604
* include to the top-level interpreter, which is here.
606
if ( result == SIEVE_EXEC_OK && interrupted && !curctx->returned ) {
607
while ( result == SIEVE_EXEC_OK ) {
609
if ( ( (interrupted && curctx->returned) || (!interrupted) ) &&
610
curctx->parent != NULL ) {
612
/* Sub-interpreter ended or executed return */
614
sieve_runtime_trace(renv, "INCLUDE command (block: %d) END ::",
615
curctx->script_info->block_id);
617
/* Ascend interpreter stack */
618
curctx = curctx->parent;
619
sieve_interpreter_free(&subinterp);
621
/* This is the top-most sub-interpreter, bail out */
622
if ( curctx->parent == NULL ) break;
624
/* Reactivate parent */
625
(void) sieve_binary_block_set_active
626
(renv->sbin, curctx->script_info->block_id, NULL);
627
subinterp = curctx->interp;
629
/* Continue parent */
630
curctx->include = NULL;
631
curctx->returned = FALSE;
633
result = ( sieve_interpreter_continue(subinterp, &interrupted) == 1 );
635
if ( curctx->include != NULL ) {
637
/* Sub-include requested */
639
/* Activate the sub-include's block */
640
if ( !sieve_binary_block_set_active
641
(renv->sbin, curctx->include->block_id, NULL) ) {
642
sieve_runtime_trace_error(renv, "invalid block id: %d",
643
curctx->include->block_id);
644
result = SIEVE_EXEC_BIN_CORRUPT;
647
if ( result == SIEVE_EXEC_OK ) {
648
/* Create sub-interpreter */
649
subinterp = sieve_interpreter_create(renv->sbin, ehandler);
651
if ( subinterp != NULL ) {
652
curctx = ext_include_interpreter_context_init_child
653
(subinterp, curctx, curctx->include->script,
656
/* Start the sub-include's interpreter */
657
curctx->include = NULL;
658
curctx->returned = FALSE;
659
result = ( sieve_interpreter_start
660
(subinterp, renv->msgdata, renv->scriptenv, renv->result,
661
&interrupted) == 1 );
663
result = SIEVE_EXEC_BIN_CORRUPT;
666
/* Sub-interpreter was interrupted outside this extension, probably
667
* stop command was executed. Generate an interrupt ourselves,
668
* ending all script execution.
670
sieve_interpreter_interrupt(renv->interp);
676
sieve_runtime_trace(renv, "INCLUDE command (block: %d) END ::",
677
curctx->script_info->block_id);
679
/* Free any sub-interpreters that might still be active */
680
while ( curctx != NULL && curctx->parent != NULL ) {
681
struct ext_include_interpreter_context *nextctx = curctx->parent;
682
struct sieve_interpreter *killed_interp = curctx->interp;
684
/* This kills curctx too */
685
sieve_interpreter_free(&killed_interp);
687
/* Luckily we recorded the parent earlier */
691
/* Return to our own block */
692
(void) sieve_binary_block_set_active(renv->sbin, this_block_id, NULL);
694
/* We are an included script already, defer inclusion to main interpreter */
696
ctx->include = included;
697
sieve_interpreter_interrupt(renv->interp);
703
void ext_include_execute_return(const struct sieve_runtime_env *renv)
705
struct ext_include_interpreter_context *ctx =
706
ext_include_get_interpreter_context(renv->interp);
708
ctx->returned = TRUE;
709
sieve_interpreter_interrupt(renv->interp);