From d532a1e63ef37a1d8bfcc9c305a3117bbb082ffa Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Wed, 13 Aug 2008 22:51:59 +0200
Subject: [PATCH] Include: transformed import and export to actual code
 operations for runtime checking.

---
 src/lib-sieve/plugins/include/cmd-import.c    | 217 +++++++++++++++-
 src/lib-sieve/plugins/include/cmd-include.c   |  20 +-
 .../plugins/include/ext-include-binary.c      |   4 +-
 .../plugins/include/ext-include-common.h      |  54 ++--
 .../plugins/include/ext-include-variables.c   | 126 +++------
 .../plugins/include/ext-include-variables.h   |   8 +-
 src/lib-sieve/plugins/include/ext-include.c   |  13 +-
 src/lib-sieve/sieve-ast.c                     | 241 +++++++++++++-----
 src/lib-sieve/sieve-ast.h                     |   7 +
 src/lib-sieve/sieve-validator.c               |   6 +-
 tests/extensions/include/variables.svtest     |   1 +
 11 files changed, 498 insertions(+), 199 deletions(-)

diff --git a/src/lib-sieve/plugins/include/cmd-import.c b/src/lib-sieve/plugins/include/cmd-import.c
index 920c0ebbc..d39aa3f72 100644
--- a/src/lib-sieve/plugins/include/cmd-import.c
+++ b/src/lib-sieve/plugins/include/cmd-import.c
@@ -5,16 +5,24 @@
 #include "sieve-commands-private.h"
 #include "sieve-validator.h" 
 #include "sieve-generator.h"
+#include "sieve-binary.h"
 #include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
 #include "sieve-ext-variables.h"
 
 #include "ext-include-common.h"
+#include "ext-include-binary.h"
 #include "ext-include-variables.h"
 
-/* Forward declarations */
+/* 
+ * Commands 
+ */
 
 static bool cmd_import_validate
-  (struct sieve_validator *validator, struct sieve_command_context *cmd); 
+  (struct sieve_validator *validator, struct sieve_command_context *cmd);
+static bool cmd_import_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *cmd);
 		
 /* Import command 
  * 
@@ -27,7 +35,8 @@ const struct sieve_command cmd_import = {
 	1, 0, FALSE, FALSE,
 	NULL, NULL,
 	cmd_import_validate, 
-	NULL, NULL
+	cmd_import_generate, 
+	NULL
 };
 
 /* Export command 
@@ -41,9 +50,41 @@ const struct sieve_command cmd_export = {
 	1, 0, FALSE, FALSE,
 	NULL, NULL, 
 	cmd_import_validate, 
-	NULL, NULL
+	cmd_import_generate, 
+	NULL
+};
+
+/*
+ * Operations
+ */
+
+static bool opc_import_dump
+	(const struct sieve_operation *op,	
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int opc_import_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+/* Import operation */
+
+const struct sieve_operation import_operation = { 
+	"import",
+	&include_extension,
+	EXT_INCLUDE_OPERATION_IMPORT,
+	opc_import_dump, 
+	opc_import_execute
 };
 
+/* Export operation */
+
+const struct sieve_operation export_operation = { 
+	"export",
+	&include_extension,
+	EXT_INCLUDE_OPERATION_EXPORT,
+	opc_import_dump, 
+	opc_import_execute
+};
+ 
 /*
  * Validation
  */
@@ -68,7 +109,6 @@ static bool cmd_import_validate
 			cmd->command->identifier, cmd->command == &cmd_export ? "or import " : "");
 		return FALSE;
 	}
-
 	
 	if ( !sieve_ext_variables_is_active(validator) ) {
 		sieve_command_validate_error(validator, cmd, 
@@ -80,22 +120,28 @@ static bool cmd_import_validate
 	/* Register imported variable */
 	if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
 		/* Single string */
-		const char *variable = sieve_ast_argument_strc(arg);
+		const char *identifier = sieve_ast_argument_strc(arg);
+		struct sieve_variable *var;
 		
-		if ( !ext_include_variable_import_global
-			(validator, cmd, variable, cmd->command == &cmd_export) )
+		if ( (var=ext_include_variable_import_global
+			(validator, cmd, identifier, cmd->command == &cmd_export)) == NULL )
 			return FALSE;
+			
+		arg->context = (void *) var;
 
 	} else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
 		/* String list */
 		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
 		
 		while ( stritem != NULL ) {
-			const char *variable = sieve_ast_argument_strc(stritem);
+			const char *identifier = sieve_ast_argument_strc(stritem);
+			struct sieve_variable *var;
 			
-			if ( !ext_include_variable_import_global
-				(validator, cmd, variable, cmd->command == &cmd_export) )
+			if ( (var=ext_include_variable_import_global
+				(validator, cmd, identifier, cmd->command == &cmd_export)) == NULL )
 				return FALSE;
+
+			stritem->context = (void *) var;
 	
 			stritem = sieve_ast_strlist_next(stritem);
 		}
@@ -108,7 +154,154 @@ static bool cmd_import_validate
 		return FALSE;
 	}
 	
-	(void)sieve_ast_arguments_detach(arg, 1);
+	/* Join emport and export commands with predecessors if possible */
+	if ( prev_context->command == cmd->command ) {
+		/* Join this command's string list with the previous one */
+		prev_context->first_positional = sieve_ast_stringlist_join
+			(prev_context->first_positional, cmd->first_positional);
+		
+		if ( prev_context->first_positional == NULL ) {
+			sieve_command_validate_error(validator, cmd, 
+				"compiler reached AST limit (script too complex)");
+			return FALSE;
+		}
+
+		/* Detach this command node */
+		sieve_ast_node_detach(cmd->ast_node);
+	}
+		
+	return TRUE;
+}
+
+/*
+ * Code generation
+ */
+ 
+static bool cmd_import_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *cmd) 
+{
+	struct sieve_ast_argument *arg = cmd->first_positional;
+
+	if ( cmd->command == &cmd_import )
+		sieve_operation_emit_code(cgenv->sbin, &import_operation);
+	else
+		sieve_operation_emit_code(cgenv->sbin, &export_operation);
+ 	 			
+	if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
+		/* Single string */
+		struct sieve_variable *var = (struct sieve_variable *) arg->context;
+		
+		(void)sieve_binary_emit_integer(cgenv->sbin, 1);
+		(void)sieve_binary_emit_integer(cgenv->sbin, var->index);
+		if ( cmd->command == &cmd_import )
+			(void)sieve_code_source_line_emit(cgenv->sbin, arg->source_line);
+		
+	} else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
+		/* String list */
+		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
+		
+		(void)sieve_binary_emit_integer(cgenv->sbin, sieve_ast_strlist_count(arg));
+						
+		while ( stritem != NULL ) {
+			struct sieve_variable *var = (struct sieve_variable *) stritem->context;
+			
+			(void)sieve_binary_emit_integer(cgenv->sbin, var->index);
+			
+			if ( cmd->command == &cmd_import )
+				(void)sieve_code_source_line_emit(cgenv->sbin, stritem->source_line);
+
+			stritem = sieve_ast_strlist_next(stritem);
+		}
+	} else {
+		i_unreached();
+	}
+ 	 		
+	return TRUE;
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool opc_import_dump
+(const struct sieve_operation *op,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	unsigned int count, i;
+	struct sieve_variable_scope *scope;
+	struct sieve_variable * const *vars;
+	unsigned var_count;
+	
+	if ( !sieve_binary_read_integer(denv->sbin, address, &count) )
+		return FALSE;
+
+	if ( op == &import_operation )
+		sieve_code_dumpf(denv, "IMPORT (count: %u):", count);
+	else
+		sieve_code_dumpf(denv, "EXPORT (count: %u):", count);
+
+	scope = ext_include_binary_get_global_scope(denv->sbin);
+	vars = sieve_variable_scope_get_variables(scope, &var_count);
+
+	sieve_code_descend(denv);
+
+	for ( i = 0; i < count; i++ ) {
+		unsigned int index;
+		
+		sieve_code_mark(denv);
+		if ( !sieve_binary_read_integer(denv->sbin, address, &index) ||
+			index >= var_count )
+			return FALSE;
+			
+		sieve_code_dumpf(denv, "GLOBAL VAR[%d]: '%s'", 
+			index, vars[index]->identifier); 
+		
+		if ( op == &import_operation ) {
+			sieve_code_descend(denv);
+
+			if ( !sieve_code_source_line_dump(denv, address) )
+				return FALSE;
+				
+			sieve_code_ascend(denv);
+		}
+	}
+	 
 	return TRUE;
 }
 
+/* 
+ * Execution
+ */
+ 
+static int opc_import_execute
+(const struct sieve_operation *op,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	unsigned int count, i;
+		
+	if ( !sieve_binary_read_integer(renv->sbin, address, &count) ) {
+		sieve_runtime_trace_error(renv, "invalid count operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	for ( i = 0; i < count; i++ ) {
+		unsigned int index, source_line;
+		
+		if ( !sieve_binary_read_integer(renv->sbin, address, &index) ) {
+			sieve_runtime_trace_error(renv, "invalid global variable operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
+		}
+		
+		if ( op == &import_operation &&
+			!sieve_code_source_line_read(renv, address, &source_line) ) {
+			sieve_runtime_trace_error(renv, "invalid source line operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
+		}
+		
+		/* FIXME: do something */
+	}
+
+	return SIEVE_EXEC_OK;
+}
+
+
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index 5500578d2..85ad0a5da 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -230,8 +230,8 @@ static bool cmd_include_generate
 		(cgenv, cmd, ctx_data->location, ctx_data->script, &included) )
  		return FALSE;
  		
- 	sieve_operation_emit_code(cgenv->sbin, &include_operation);
-	sieve_binary_emit_offset(cgenv->sbin, included->id); 
+ 	(void)sieve_operation_emit_code(cgenv->sbin, &include_operation);
+	(void)sieve_binary_emit_integer(cgenv->sbin, included->id); 
  	 		
 	return TRUE;
 }
@@ -246,9 +246,12 @@ static bool opc_include_dump
 {
 	const struct ext_include_script_info *included;
 	struct ext_include_binary_context *binctx;
-	int include_id;
+	unsigned int include_id;
+
+	sieve_code_dumpf(denv, "INCLUDE:");
 	
-	if ( !sieve_binary_read_offset(denv->sbin, address, &include_id) )
+	sieve_code_mark(denv);
+	if ( !sieve_binary_read_integer(denv->sbin, address, &include_id) )
 		return FALSE;
 
 	binctx = ext_include_binary_get_context(denv->sbin);
@@ -256,7 +259,8 @@ static bool opc_include_dump
 	if ( included == NULL )
 		return FALSE;
 		
-	sieve_code_dumpf(denv, "INCLUDE %s [ID: %d, BLOCK: %d]", 
+	sieve_code_descend(denv);
+	sieve_code_dumpf(denv, "SCRIPT: %s [ID: %d, BLOCK: %d]", 
 		sieve_script_filename(included->script), include_id, included->block_id);
 	 
 	return TRUE;
@@ -270,14 +274,14 @@ static int opc_include_execute
 (const struct sieve_operation *op ATTR_UNUSED,
 	const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	int include_id;
+	unsigned int include_id;
 		
-	if ( !sieve_binary_read_offset(renv->sbin, address, &include_id) ) {
+	if ( !sieve_binary_read_integer(renv->sbin, address, &include_id) ) {
 		sieve_runtime_trace_error(renv, "invalid include-id operand");
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
-	return ext_include_execute_include(renv, (unsigned int) include_id);
+	return ext_include_execute_include(renv, include_id);
 }
 
 
diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c
index 8b98392b9..8815f977a 100644
--- a/src/lib-sieve/plugins/include/ext-include-binary.c
+++ b/src/lib-sieve/plugins/include/ext-include-binary.c
@@ -221,7 +221,7 @@ static bool ext_include_binary_save(struct sieve_binary *sbin)
 		sieve_binary_emit_cstring(sbin, sieve_script_name(incscript->script));
 	}
 
-	result = ext_include_variables_save(sbin, binctx, binctx->global_vars);
+	result = ext_include_variables_save(sbin, binctx->global_vars);
 	
 	(void) sieve_binary_block_set_active(sbin, prvblk, NULL);
 
@@ -296,7 +296,7 @@ static bool ext_include_binary_open(struct sieve_binary *sbin)
 		sieve_script_unref(&script);
 	}
 
-	if ( !ext_include_variables_load(sbin, binctx, &offset, block, &binctx->global_vars) )
+	if ( !ext_include_variables_load(sbin, &offset, block, &binctx->global_vars) )
 		return FALSE;
 	
 	/* Restore previously active block */
diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h
index 494abb8cb..9eb415910 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.h
+++ b/src/lib-sieve/plugins/include/ext-include-common.h
@@ -13,44 +13,60 @@
 struct ext_include_script_info;
 struct ext_include_binary_context;
 
-/* Extension */
+/* 
+ * Types 
+ */
+
+enum ext_include_script_location { 
+	EXT_INCLUDE_LOCATION_PERSONAL, 
+	EXT_INCLUDE_LOCATION_GLOBAL,
+	EXT_INCLUDE_LOCATION_INVALID 
+}; 
+
+/* 
+ * Extension 
+ */
 
-extern int ext_include_my_id;
 extern const struct sieve_extension include_extension;
 extern const struct sieve_binary_extension include_binary_ext;
 
-/* Commands */
+/* 
+ * Commands 
+ */
 
 extern const struct sieve_command cmd_include;
 extern const struct sieve_command cmd_return;
 extern const struct sieve_command cmd_import;
 extern const struct sieve_command cmd_export;
 
-/* Types */
-
+/*
+ * Operations
+ */
+ 
 enum ext_include_opcode {
 	EXT_INCLUDE_OPERATION_INCLUDE,
 	EXT_INCLUDE_OPERATION_RETURN,
 	EXT_INCLUDE_OPERATION_IMPORT,
 	EXT_INCLUDE_OPERATION_EXPORT
 };
+ 
+extern const struct sieve_operation include_operation;
+extern const struct sieve_operation return_operation;
+extern const struct sieve_operation import_operation;
+extern const struct sieve_operation export_operation;
 
-enum ext_include_script_location { 
-	EXT_INCLUDE_LOCATION_PERSONAL, 
-	EXT_INCLUDE_LOCATION_GLOBAL,
-	EXT_INCLUDE_LOCATION_INVALID 
-}; 
-
-/* Script access */
+/* 
+ * Script access 
+ */
 
 const char *ext_include_get_script_directory
 	(enum ext_include_script_location location, const char *script_name);
 
 /* 
- * AST Context 
+ * Context 
  */
-
-/* AST context */
+ 
+/* AST Context */
 
 struct ext_include_ast_context {
     struct sieve_variable_scope *import_vars;
@@ -67,9 +83,7 @@ struct ext_include_ast_context *ext_include_get_ast_context
 void ext_include_ast_link_included_script
 	(struct sieve_ast *ast, struct sieve_script *script);
 
-/* 
- * Generator context
- */
+/* Generator context */
 
 void ext_include_register_generator_context
 	(const struct sieve_codegen_env *cgenv);
@@ -79,9 +93,7 @@ bool ext_include_generate_include
 		enum ext_include_script_location location, struct sieve_script *script, 
 		const struct ext_include_script_info **included_r);
 
-/* 
- * Interpreter context
- */
+/* Interpreter context */
 
 void ext_include_interpreter_context_init(struct sieve_interpreter *interp);
 
diff --git a/src/lib-sieve/plugins/include/ext-include-variables.c b/src/lib-sieve/plugins/include/ext-include-variables.c
index 9efbff3e1..ae1ff5295 100644
--- a/src/lib-sieve/plugins/include/ext-include-variables.c
+++ b/src/lib-sieve/plugins/include/ext-include-variables.c
@@ -28,14 +28,13 @@ enum ext_include_variable_type {
 struct ext_include_variable {
 	enum ext_include_variable_type type;
 	unsigned int source_line;
-	struct sieve_script *script;
 };
 
 /* 
  * Variable import-export
  */
  
-bool ext_include_variable_import_global
+struct sieve_variable *ext_include_variable_import_global
 (struct sieve_validator *valdtr, struct sieve_command_context *cmd, 
 	const char *variable, bool export)
 {
@@ -52,7 +51,7 @@ bool ext_include_variable_import_global
 			/* Yes, and now export is attempted. ERROR */
 			sieve_command_validate_error(valdtr, cmd, 
 				"cannot export imported variable '%s'", variable);
-			return FALSE;
+			return NULL;
 		} else {
 			/* Yes, and it is imported again. Warn the user */
 			if ( impvar->context != NULL ) {
@@ -88,7 +87,6 @@ bool ext_include_variable_import_global
 		varctx = p_new(pool, struct ext_include_variable, 1);
 		varctx->type = export ? 
 			EXT_INCLUDE_VAR_EXPORTED : EXT_INCLUDE_VAR_IMPORTED;
-		varctx->script = sieve_validator_script(valdtr);
 		varctx->source_line = cmd->ast_node->source_line;
 		var->context = varctx;
 	}
@@ -107,21 +105,19 @@ bool ext_include_variable_import_global
 		i_assert( impvar != NULL );
 
 		varctx = p_new(pool, struct ext_include_variable, 1);
-        varctx->type = EXT_INCLUDE_VAR_IMPORTED;
-        varctx->source_line = cmd->ast_node->source_line;
-        impvar->context = varctx;
+		varctx->type = EXT_INCLUDE_VAR_IMPORTED;
+		varctx->source_line = cmd->ast_node->source_line;
+		impvar->context = varctx;
 	}
 
-	return TRUE;	
+	return var;	
 }
 
 
 bool ext_include_variables_save
-(struct sieve_binary *sbin, struct ext_include_binary_context *binctx,
-	struct sieve_variable_scope *global_vars)
+(struct sieve_binary *sbin, struct sieve_variable_scope *global_vars)
 {
 	unsigned int count = sieve_variable_scope_size(global_vars);
-	struct sieve_script *main_script = sieve_binary_script(sbin);
 
 	sieve_binary_emit_integer(sbin, count);
 
@@ -133,35 +129,21 @@ bool ext_include_variables_save
 		for ( i = 0; i < size; i++ ) {
 			struct ext_include_variable *varctx =
 				(struct ext_include_variable *) vars[i]->context;
-			unsigned int include_id = 0;
 
 			i_assert( varctx != NULL );
-
-			if ( varctx->script != main_script ) {
-				const struct ext_include_script_info *included = 
-					ext_include_binary_script_get(binctx, varctx->script);
-
-				i_assert( included != NULL );
-
-				include_id = included->id;
-			}
 			
-    	    sieve_binary_emit_byte(sbin, varctx->type);
-			sieve_binary_emit_integer(sbin, varctx->source_line);			
-			sieve_binary_emit_integer(sbin, include_id);
+			sieve_binary_emit_byte(sbin, varctx->type);
 			sieve_binary_emit_cstring(sbin, vars[i]->identifier);
 		}
-    }
+	}
 
 	return TRUE;
 }
 
 bool ext_include_variables_load
-(struct sieve_binary *sbin, struct ext_include_binary_context *binctx,
-	sieve_size_t *offset, unsigned int block,
+(struct sieve_binary *sbin, sieve_size_t *offset, unsigned int block,
 	struct sieve_variable_scope **global_vars_r)
 {
-	struct sieve_script *main_script = sieve_binary_script(sbin);
 	sieve_size_t count = 0;
 	unsigned int i;
 	pool_t pool;
@@ -170,79 +152,57 @@ bool ext_include_variables_load
 	i_assert( *global_vars_r == NULL );
 
 	if ( !sieve_binary_read_integer(sbin, offset, &count) ) {
-        sieve_sys_error("include: failed to read global variables count "
-            "from dependency block %d of binary %s", block, sieve_binary_path(sbin));
-        return FALSE;
-    }
+		sieve_sys_error("include: failed to read global variables count "
+			"from dependency block %d of binary %s", block, sieve_binary_path(sbin));
+		return FALSE;
+	}
 
 	if ( count > SIEVE_VARIABLES_MAX_SCOPE_SIZE ) {
-        sieve_sys_error("include: global variable scope size of binary %s "
+		sieve_sys_error("include: global variable scope size of binary %s "
 			"exceeds the limit (%u > %u)", sieve_binary_path(sbin),
 			count, SIEVE_VARIABLES_MAX_SCOPE_SIZE );
-        return FALSE;
-    }
+		return FALSE;
+	}
 
 	*global_vars_r = sieve_variable_scope_create(&include_extension);
 	pool = sieve_variable_scope_pool(*global_vars_r);
 
-    /* Read global variable scope */
-    for ( i = 0; i < count; i++ ) {
-		struct sieve_script *script;
+	/* Read global variable scope */
+	for ( i = 0; i < count; i++ ) {
 		struct sieve_variable *var;
-        struct ext_include_variable *varctx;
+		struct ext_include_variable *varctx;
 		enum ext_include_variable_type type;
-		sieve_size_t source_line, include_id;
 		string_t *identifier;
 
-        if (
-            !sieve_binary_read_byte(sbin, offset, &type) ||
-            !sieve_binary_read_integer(sbin, offset, &source_line) ||
-            !sieve_binary_read_integer(sbin, offset, &include_id) ||
-            !sieve_binary_read_string(sbin, offset, &identifier) ) {
-            /* Binary is corrupt, recompile */
-            sieve_sys_error("include: failed to read global variable specification "
-                "from dependency block %d of binary %s", block, sieve_binary_path(sbin));
-            return FALSE;
-        }
-
-        if ( type >= EXT_INCLUDE_VAR_INVALID ) {
-            /* Binary is corrupt, recompile */
-            sieve_sys_error("include: dependency block %d of binary %s "
-                "reports invalid global variable type (id %d).",
-                block, sieve_binary_path(sbin), type);
-            return FALSE;
-        }
-
-		if ( include_id != 0 ) {
-			const struct ext_include_script_info *included = 
-				ext_include_binary_script_get_included(binctx, include_id);
-
-			if ( included == NULL ) {
-				sieve_sys_error("include: dependency block %d of binary %s "
-                	"has invalid global variable script reference (id %d).",
-                block, sieve_binary_path(sbin), include_id);
-	            return FALSE;
-			}
+		if (
+			!sieve_binary_read_byte(sbin, offset, &type) ||
+			!sieve_binary_read_string(sbin, offset, &identifier) ) {
+			/* Binary is corrupt, recompile */
+			sieve_sys_error("include: failed to read global variable specification "
+				"from dependency block %d of binary %s", block, sieve_binary_path(sbin));
+			return FALSE;
+		}
 
-			script = included->script;
-		} else {
-			script = main_script;
+		if ( type >= EXT_INCLUDE_VAR_INVALID ) {
+			/* Binary is corrupt, recompile */
+			sieve_sys_error("include: dependency block %d of binary %s "
+				"reports invalid global variable type (id %d).",
+				block, sieve_binary_path(sbin), type);
+			return FALSE;
 		}
 		
-        var = sieve_variable_scope_declare(*global_vars_r, str_c(identifier));
+		var = sieve_variable_scope_declare(*global_vars_r, str_c(identifier));
 
 		i_assert( var != NULL );
 
-        varctx = p_new(pool, struct ext_include_variable, 1);
-        varctx->type = type;
-        varctx->source_line = source_line;
-		varctx->script = script;
-        var->context = varctx;
+		varctx = p_new(pool, struct ext_include_variable, 1);
+		varctx->type = type;
+		var->context = varctx;
 
 		i_assert(var->index == i);
-    }
+	}
 	
-    return TRUE;
+	return TRUE;
 }
 
 bool ext_include_variables_dump
@@ -264,11 +224,9 @@ bool ext_include_variables_dump
 			struct ext_include_variable *varctx =
 				(struct ext_include_variable *) vars[i]->context;
 
-			sieve_binary_dumpf(denv, "%3d: %s '%s' (%s:%d)\n", i, 
+			sieve_binary_dumpf(denv, "%3d: %s '%s' \n", i, 
 				varctx->type == EXT_INCLUDE_VAR_EXPORTED ? "export" : "import", 
-				vars[i]->identifier, 
-				varctx->script == NULL ? "<main>" : sieve_script_filename(varctx->script), 
-				varctx->source_line);
+				vars[i]->identifier);
 		}	
 	}
 
diff --git a/src/lib-sieve/plugins/include/ext-include-variables.h b/src/lib-sieve/plugins/include/ext-include-variables.h
index 1fa924cbf..6458b8381 100644
--- a/src/lib-sieve/plugins/include/ext-include-variables.h
+++ b/src/lib-sieve/plugins/include/ext-include-variables.h
@@ -11,7 +11,7 @@
  * Variable import-export
  */
  
-bool ext_include_variable_import_global
+struct sieve_variable *ext_include_variable_import_global
 	(struct sieve_validator *valdtr, struct sieve_command_context *cmd, 
 		const char *variable, bool export);
 
@@ -20,11 +20,9 @@ bool ext_include_variable_import_global
  */
 
 bool ext_include_variables_save
-	(struct sieve_binary *sbin, struct ext_include_binary_context *binctx,
-		struct sieve_variable_scope *global_vars);
+	(struct sieve_binary *sbin, struct sieve_variable_scope *global_vars);
 bool ext_include_variables_load
-	(struct sieve_binary *sbin, struct ext_include_binary_context *binctx,
-		sieve_size_t *offset, unsigned int block,
+	(struct sieve_binary *sbin, sieve_size_t *offset, unsigned int block,
 		struct sieve_variable_scope **global_vars_r);
 bool ext_include_variables_dump
 	(struct sieve_dumptime_env *denv, struct sieve_variable_scope *global_vars);
diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c
index 781b5cb3d..b1b7b0951 100644
--- a/src/lib-sieve/plugins/include/ext-include.c
+++ b/src/lib-sieve/plugins/include/ext-include.c
@@ -37,21 +37,20 @@ static bool ext_include_interpreter_load(struct sieve_interpreter *interp);
 
 /* Operations */
 
-extern const struct sieve_operation include_operation;
-extern const struct sieve_operation return_operation;
-
 static const struct sieve_operation *ext_include_operations[] = { 
 	&include_operation, 
-	&return_operation
+	&return_operation,
+	&import_operation,
+	&export_operation
 };
 
 /* Extension definitions */
 
-int ext_include_my_id;
+static int ext_my_id;
 
 const struct sieve_extension include_extension = { 
 	"include", 
-	&ext_include_my_id,
+	&ext_my_id,
 	ext_include_load,
 	ext_include_validator_load, 
 	ext_include_generator_load,
@@ -64,7 +63,7 @@ const struct sieve_extension include_extension = {
 
 static bool ext_include_load(int ext_id)
 {
-	ext_include_my_id = ext_id;
+	ext_my_id = ext_id;
 
 	return TRUE;
 }
diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c
index d21653f38..ea13b89b5 100644
--- a/src/lib-sieve/sieve-ast.c
+++ b/src/lib-sieve/sieve-ast.c
@@ -161,8 +161,8 @@ void sieve_ast_error
 	} T_END; 
 }
  
-
 /* Very simplistic linked list implementation
+ * FIXME: Move to separate file
  */
 #define __LIST_CREATE(pool, type) { \
 		type *list = p_new(pool, type, 1); \
@@ -208,9 +208,66 @@ void sieve_ast_error
 		node->list = list; \
 		\
 		return TRUE; \
+	}
+
+#define __LIST_JOIN(list, items) { \
+		typeof(items->head) node; \
+		\
+		if ( list->len + items->len < list->len ) \
+			return FALSE; \
+		\
+		if ( items->len == 0 ) return TRUE; \
+		\
+		if ( list->head == NULL ) { \
+			list->head = items->head; \
+			list->tail = items->tail; \
+		} else { \
+			list->tail->next = items->head; \
+			items->head->prev = list->tail; \
+			list->tail = items->tail; \
+		} \
+		list->len += items->len; \
+		\
+		node = items->head; \
+		while ( node != NULL ) { \
+			node->list = list; \
+			node = node->next; \
+		} \
+		return TRUE; \
 	}	 
 
-	
+#define __LIST_DETACH(first, count) { \
+		typeof(*first) *last, *result; \
+		unsigned int left; \
+		\
+		i_assert(first->list != NULL); \
+		\
+		left = count - 1; \
+		last = first; \
+		while ( left > 0 && last->next != NULL ) { \
+			left--; \
+			last = last->next; \
+		} \
+		\
+		if ( first->list->head == first ) \
+			first->list->head = last->next; \
+		if ( first->list->tail == last ) \
+			first->list->tail = first->prev; \
+		\
+		if ( first->prev != NULL ) \
+			first->prev->next = last->next;	\
+		if ( last->next != NULL ) \
+			last->next->prev = first->prev; \
+		\
+		first->list->len -= count - left; \
+		\
+		result = last->next; \
+		first->prev = NULL; \
+		last->next = NULL; \
+		\
+		return result; \
+	}
+
 /* List of AST nodes */
 static struct sieve_ast_list *sieve_ast_list_create(pool_t pool) 
 	__LIST_CREATE(pool, struct sieve_ast_list)
@@ -219,6 +276,10 @@ static bool sieve_ast_list_add
 (struct sieve_ast_list *list, struct sieve_ast_node *node) 
 	__LIST_ADD(list, node)
 
+static struct sieve_ast_node *sieve_ast_list_detach
+(struct sieve_ast_node *first, unsigned int count) 
+	__LIST_DETACH(first, count)
+
 /* List of argument AST nodes */
 struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool) 
 	__LIST_CREATE(pool, struct sieve_ast_arg_list)
@@ -232,6 +293,14 @@ bool sieve_ast_arg_list_insert
 	struct sieve_ast_argument *argument)
 	__LIST_INSERT(list, before, argument)
 
+static bool sieve_ast_arg_list_join
+(struct sieve_ast_arg_list *list, struct sieve_ast_arg_list *items)
+	__LIST_JOIN(list, items)
+
+static struct sieve_ast_argument *sieve_ast_arg_list_detach
+	(struct sieve_ast_argument *first, const unsigned int count)
+	__LIST_DETACH(first, count)
+
 void sieve_ast_arg_list_substitute
 (struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument, 
 	struct sieve_ast_argument *replacement)
@@ -253,48 +322,7 @@ void sieve_ast_arg_list_substitute
 	argument->next = NULL;
 	argument->prev = NULL;
 }
-
-static struct sieve_ast_argument *sieve_ast_arg_list_detach
-	(struct sieve_ast_argument *first, const unsigned int count)
-{
-	struct sieve_ast_argument *last, *result;
-	unsigned int left;
-	
-	i_assert(first->list != NULL);
-	
-	/* Find the last of the deleted arguments */
-	left = count - 1;
-	last = first;
-	while ( left > 0 && last->next != NULL ) {
-		left--;
-		last = last->next;
-	}
-
-	/* Perform substitution */
-		
-	if ( first->list->head == first ) 
-		first->list->head = last->next;
-
-	if ( first->list->tail == last )
-		first->list->tail = first->prev;
-		
-	if ( first->prev != NULL )
-		first->prev->next = last->next;
-		
-	if ( last->next != NULL )
-		last->next->prev = first->prev;
-		
-	first->list->len -= count - left;
-	
-	result = last->next;
-	first->prev = NULL;
-	last->next = NULL;
 	
-	/* Detached objects will eventually freed along with AST pool */
-	
-	return result;
-}
-
 /* AST Node */
 
 static struct sieve_ast_node *sieve_ast_node_create
@@ -352,10 +380,10 @@ static bool sieve_ast_node_add_argument
 	return sieve_ast_arg_list_add(node->arguments, argument);
 }
 
-static void sieve_ast_argument_substitute
-	(struct sieve_ast_argument *argument, struct sieve_ast_argument *replacement) 
-{
-	sieve_ast_arg_list_substitute(argument->list, argument, replacement);
+struct sieve_ast_node *sieve_ast_node_detach
+	(struct sieve_ast_node *first) 
+{	
+	return sieve_ast_list_detach(first, 1);
 }
 
 /* Argument AST node */
@@ -379,6 +407,12 @@ struct sieve_ast_argument *sieve_ast_argument_create
 	return arg;
 }
 
+static void sieve_ast_argument_substitute
+	(struct sieve_ast_argument *argument, struct sieve_ast_argument *replacement) 
+{
+	sieve_ast_arg_list_substitute(argument->list, argument, replacement);
+}
+
 struct sieve_ast_argument *sieve_ast_argument_string_create
 	(struct sieve_ast_node *node, const string_t *str, unsigned int source_line) 
 {	
@@ -440,24 +474,39 @@ struct sieve_ast_argument *sieve_ast_argument_stringlist_substitute
 	return argument;
 }
 
-static bool _sieve_ast_stringlist_add
-	(struct sieve_ast_argument *list, string_t *str, unsigned int source_line) 
+static inline bool _sieve_ast_stringlist_add_item
+(struct sieve_ast_argument *list, struct sieve_ast_argument *item) 
 {
-	struct sieve_ast_argument *stritem;
-	
 	i_assert( list->type == SAAT_STRING_LIST );
 	
 	if ( list->_value.strlist == NULL ) 
 		list->_value.strlist = sieve_ast_arg_list_create(list->ast->pool);
 	
-	stritem = sieve_ast_argument_create(list->ast, source_line);
-		
-	stritem->type = SAAT_STRING;
+	return sieve_ast_arg_list_add(list->_value.strlist, item);
+}
+
+static bool sieve_ast_stringlist_add_stringlist
+(struct sieve_ast_argument *list, struct sieve_ast_argument *items) 
+{
+	i_assert( list->type == SAAT_STRING_LIST );
+	i_assert( items->type == SAAT_STRING_LIST );
+
+	if ( list->_value.strlist == NULL ) 
+		list->_value.strlist = sieve_ast_arg_list_create(list->ast->pool);
 	
-	/* Clone string */
+	return sieve_ast_arg_list_join(list->_value.strlist, items->_value.strlist);
+}
+
+static bool _sieve_ast_stringlist_add_str
+(struct sieve_ast_argument *list, string_t *str, unsigned int source_line) 
+{
+	struct sieve_ast_argument *stritem;
+	
+	stritem = sieve_ast_argument_create(list->ast, source_line);		
+	stritem->type = SAAT_STRING;
 	stritem->_value.str = str;
 
-	return sieve_ast_arg_list_add(list->_value.strlist, stritem);
+	return _sieve_ast_stringlist_add_item(list, stritem);
 }
 
 bool sieve_ast_stringlist_add
@@ -466,7 +515,7 @@ bool sieve_ast_stringlist_add
 	string_t *copied_str = str_new(list->ast->pool, str_len(str));
 	str_append_str(copied_str, str);
 
-	return _sieve_ast_stringlist_add(list, copied_str, source_line);
+	return _sieve_ast_stringlist_add_str(list, copied_str, source_line);
 }
 
 bool sieve_ast_stringlist_add_strc
@@ -475,7 +524,7 @@ bool sieve_ast_stringlist_add_strc
 	string_t *copied_str = str_new(list->ast->pool, strlen(str));
 	str_append(copied_str, str);
 	
-	return _sieve_ast_stringlist_add(list, copied_str, source_line);
+	return _sieve_ast_stringlist_add_str(list, copied_str, source_line);
 }
 
 struct sieve_ast_argument *sieve_ast_argument_tag_create
@@ -607,6 +656,82 @@ int sieve_ast_stringlist_map
 	return -1;
 }
 
+struct sieve_ast_argument *sieve_ast_stringlist_join
+(struct sieve_ast_argument *list, struct sieve_ast_argument *items)
+{
+	enum sieve_ast_argument_type list_type, items_type;
+	struct sieve_ast_argument *newlist;
+	
+	list_type = sieve_ast_argument_type(list);
+	items_type = sieve_ast_argument_type(items);
+	
+	switch ( list_type ) {
+	
+	case SAAT_STRING:
+		switch ( items_type ) {
+		
+		case SAAT_STRING:
+			newlist = 
+				sieve_ast_argument_create(list->ast, list->source_line);
+			newlist->type = SAAT_STRING_LIST;
+			newlist->_value.strlist = NULL;
+			
+			sieve_ast_argument_substitute(list, newlist);
+			sieve_ast_arguments_detach(items, 1);
+			
+			if ( !_sieve_ast_stringlist_add_item(newlist, list) ||
+				!_sieve_ast_stringlist_add_item(newlist, items) ) {
+				return NULL;
+			}
+			
+			return newlist;
+			
+		case SAAT_STRING_LIST:
+			/* Adding stringlist to string; make them swith places and add one to the
+			 * other.
+			 */
+			sieve_ast_arguments_detach(items, 1);
+			sieve_ast_argument_substitute(list, items);
+			if ( !_sieve_ast_stringlist_add_item(items, list) ) 
+				return NULL;
+			
+			return list;
+			
+		default:
+			i_unreached();
+		}
+		break;
+		
+	case SAAT_STRING_LIST:
+		switch ( items_type ) {
+		
+		case SAAT_STRING:
+			/* Adding string to stringlist; straightforward add */
+			sieve_ast_arguments_detach(items, 1);
+			if ( !_sieve_ast_stringlist_add_item(list, items) )
+				return NULL;
+			
+			return list;
+			
+		case SAAT_STRING_LIST:
+			/* Adding stringlist to stringlist; perform actual join */
+			sieve_ast_arguments_detach(items, 1);
+			if ( !sieve_ast_stringlist_add_stringlist(list, items) )
+				return NULL;
+			
+			return list;
+			
+		default:
+			i_unreached();
+		}
+		
+		break;
+	default:
+		i_unreached();
+	}
+	
+	return NULL;
+}
 
 
 /* Debug */
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index b96261258..acb64673b 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -179,6 +179,11 @@ void sieve_ast_error
 (struct sieve_error_handler *ehandler, sieve_error_vfunc_t vfunc, 
 	struct sieve_ast_node *node, const char *fmt, va_list args);
 	
+/* sieve_ast_node */
+
+struct sieve_ast_node *sieve_ast_node_detach
+	(struct sieve_ast_node *first);
+	
 /* sieve_ast_argument */
 
 struct sieve_ast_argument *sieve_ast_argument_create
@@ -245,6 +250,8 @@ struct sieve_ast_node *sieve_ast_command_create
 int sieve_ast_stringlist_map
 	(struct sieve_ast_argument **listitem, void *context,
 		int (*map_function)(void *context, struct sieve_ast_argument *arg));
+struct sieve_ast_argument *sieve_ast_stringlist_join
+	(struct sieve_ast_argument *list, struct sieve_ast_argument *items);
 	
 /* 
  * Debug 
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index 4b0df0a09..bfeb384c5 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -988,14 +988,16 @@ static bool sieve_validate_block
 (struct sieve_validator *valdtr, struct sieve_ast_node *block) 
 {
 	bool result = TRUE;
-	struct sieve_ast_node *command;
+	struct sieve_ast_node *command, *next;
 
 	T_BEGIN {	
 		command = sieve_ast_command_first(block);
 		while ( command != NULL && (result || 
 			sieve_errors_more_allowed(valdtr->ehandler)) ) {	
+			
+			next = sieve_ast_command_next(command);
 			result = sieve_validate_command(valdtr, command) && result;	
-			command = sieve_ast_command_next(command);
+			command = next;
 		}		
 	} T_END;
 	
diff --git a/tests/extensions/include/variables.svtest b/tests/extensions/include/variables.svtest
index f363fd508..2642f9074 100644
--- a/tests/extensions/include/variables.svtest
+++ b/tests/extensions/include/variables.svtest
@@ -3,6 +3,7 @@ require "vnd.dovecot.testsuite";
 require "include";
 require "variables";
 
+#export ["value1", "value2", "value3", "value4","result"];
 export ["value1", "value2"];
 export ["value3", "value4"];
 export "result";
-- 
GitLab