From e6379fcc4c597c0b56924daa08fc0ce705962d13 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 10 Apr 2009 00:56:15 +0200
Subject: [PATCH] Major rework of the multiscript support for better error
 handling.

---
 src/lib-sieve/sieve-error-private.h      |   2 +-
 src/lib-sieve/sieve-error.c              |  10 +-
 src/lib-sieve/sieve-result.c             |  24 +-
 src/lib-sieve/sieve-result.h             |   2 +
 src/lib-sieve/sieve-script.c             |   6 +-
 src/lib-sieve/sieve.c                    |  37 +-
 src/lib-sieve/sieve.h                    |  11 +-
 src/plugins/lda-sieve/lda-sieve-plugin.c | 496 +++++++++++++----------
 src/sieve-tools/sieve-test.c             |  10 +-
 9 files changed, 347 insertions(+), 251 deletions(-)

diff --git a/src/lib-sieve/sieve-error-private.h b/src/lib-sieve/sieve-error-private.h
index 0a22c4e73..7746e04c1 100644
--- a/src/lib-sieve/sieve-error-private.h
+++ b/src/lib-sieve/sieve-error-private.h
@@ -13,7 +13,7 @@
 struct sieve_error_handler {
 	pool_t pool;
 	int refcount;
-	
+
 	unsigned int max_errors;
 
 	unsigned int errors;
diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c
index 48605df0d..c2f1e628f 100644
--- a/src/lib-sieve/sieve-error.c
+++ b/src/lib-sieve/sieve-error.c
@@ -235,9 +235,11 @@ void sieve_error_handler_reset(struct sieve_error_handler *ehandler)
  */
 
 static void sieve_master_verror
-(struct sieve_error_handler *ehandler ATTR_UNUSED, const char *location, 
+(struct sieve_error_handler *ehandler, const char *location, 
 	const char *fmt, va_list args) 
 {
+	if ( ehandler->log_master ) return;
+
 	if ( location == NULL || *location == '\0' )
 		i_error("sieve: %s", t_strdup_vprintf(fmt, args));
 	else
@@ -248,6 +250,8 @@ static void sieve_master_vwarning
 (struct sieve_error_handler *ehandler ATTR_UNUSED, const char *location, 
 	const char *fmt, va_list args) 
 {
+	if ( ehandler->log_master ) return;
+
 	if ( location == NULL || *location == '\0' )
 		i_warning("sieve: %s", t_strdup_vprintf(fmt, args));
 	else
@@ -258,6 +262,8 @@ static void sieve_master_vinfo
 (struct sieve_error_handler *ehandler ATTR_UNUSED, const char *location, 
 	const char *fmt, va_list args) 
 {
+	if ( ehandler->log_master ) return;
+
 	if ( location == NULL || *location == '\0' )
 		i_info("sieve: %s", t_strdup_vprintf(fmt, args));
 	else
@@ -286,7 +292,7 @@ struct sieve_error_handler *sieve_master_ehandler_create
 }
 
 struct sieve_error_handler _sieve_system_ehandler_object = {
-        NULL, 0, 0, 0, 0,
+	NULL, 0, 0, 0, 0,
 	FALSE,
 	TRUE,
 	sieve_master_verror,
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index a9a5c0a4e..b1a3ee964 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -94,9 +94,10 @@ struct sieve_result *sieve_result_create
 	result->pool = pool;
 	
 	p_array_init(&result->ext_contexts, pool, 4);
-	
+
+	if ( ehandler != NULL )
+		sieve_error_handler_ref(ehandler);	
 	result->ehandler = ehandler;
-	sieve_error_handler_ref(ehandler);
 
 	result->action_env.result = result;
 		
@@ -126,7 +127,8 @@ void sieve_result_unref(struct sieve_result **result)
 	if ( (*result)->action_contexts != NULL )
         hash_table_destroy(&(*result)->action_contexts);
 
-	sieve_error_handler_unref(&(*result)->ehandler);
+	if ( (*result)->ehandler != NULL )
+		sieve_error_handler_unref(&(*result)->ehandler);
 
 	pool_unref(&(*result)->pool);
 
@@ -144,6 +146,16 @@ struct sieve_error_handler *sieve_result_get_error_handler
 	return result->ehandler;
 }
 
+void sieve_result_set_error_handler
+(struct sieve_result *result, struct sieve_error_handler *ehandler)
+{
+	if ( result->ehandler != ehandler ) {
+		sieve_error_handler_ref(ehandler);
+		sieve_error_handler_unref(&result->ehandler);
+		result->ehandler = ehandler;
+	}
+}
+
 /*
  * Extension support
  */
@@ -178,6 +190,8 @@ void sieve_result_error
 {
 	va_list args;
 	
+	if ( aenv->result->ehandler == NULL ) return;
+
 	va_start(args, fmt);	
 	sieve_verror(aenv->result->ehandler, sieve_action_get_location(aenv), fmt, 
 		args); 
@@ -188,6 +202,8 @@ void sieve_result_warning
 	(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
 {
 	va_list args;
+
+	if ( aenv->result->ehandler == NULL ) return;
 	
 	va_start(args, fmt);	
 	sieve_vwarning(aenv->result->ehandler, sieve_action_get_location(aenv), fmt, 
@@ -200,6 +216,8 @@ void sieve_result_log
 {
 	va_list args;
 	
+	if ( aenv->result->ehandler == NULL ) return;
+
 	va_start(args, fmt);	
 	sieve_vinfo(aenv->result->ehandler, sieve_action_get_location(aenv), fmt, 
 		args); 
diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h
index 6692f2b13..48fc5836c 100644
--- a/src/lib-sieve/sieve-result.h
+++ b/src/lib-sieve/sieve-result.h
@@ -29,6 +29,8 @@ pool_t sieve_result_pool(struct sieve_result *result);
 
 struct sieve_error_handler *sieve_result_get_error_handler
 	(struct sieve_result *result);
+void sieve_result_set_error_handler
+	(struct sieve_result *result, struct sieve_error_handler *ehandler);
 
 /*
  * Extension support
diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c
index 5ec687434..75908ba30 100644
--- a/src/lib-sieve/sieve-script.c
+++ b/src/lib-sieve/sieve-script.c
@@ -90,7 +90,11 @@ struct sieve_script *sieve_script_init
 		basename = _sieve_scriptfile_get_basename(filename);
 		binpath = t_strconcat(dirpath, "/", basename, ".svbin", NULL);
 				
-		if ( name != NULL && *name != '\0' ) {
+		if ( name == NULL ) {
+			name = basename; 
+		} else if ( *name == '\0' ) {
+			name = NULL;
+		} else {
 			basename = name;
 		}
 			
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 474b8a296..d54371d08 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -186,8 +186,10 @@ static int sieve_run
 	/* Create result object */
 	if ( *result == NULL )
 		*result = sieve_result_create(ehandler);
-	else
+	else {
 		sieve_result_ref(*result);
+		sieve_result_set_error_handler(*result, ehandler);
+	}
 							
 	/* Run the interpreter */
 	ret = sieve_interpreter_run(interp, msgdata, senv, *result);
@@ -345,7 +347,6 @@ struct sieve_multiscript {
 	struct sieve_result *result;
 	const struct sieve_message_data *msgdata;
 	const struct sieve_script_env *scriptenv;
-	struct sieve_error_handler *ehandler;
 
 	int status;
 	bool active;
@@ -355,14 +356,13 @@ struct sieve_multiscript {
 };
  
 struct sieve_multiscript *sieve_multiscript_start_execute
-(const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
-	struct sieve_error_handler *ehandler)
+(const struct sieve_message_data *msgdata, const struct sieve_script_env *senv)
 {
 	pool_t pool;
 	struct sieve_result *result;
 	struct sieve_multiscript *mscript;
 	
-	result = sieve_result_create(ehandler);
+	result = sieve_result_create(NULL);
 	pool = sieve_result_pool(result);
 	
 	sieve_result_set_keep_action(result, NULL);
@@ -371,7 +371,6 @@ struct sieve_multiscript *sieve_multiscript_start_execute
 	mscript->result = result;
 	mscript->msgdata = msgdata;
 	mscript->scriptenv = senv;
-	mscript->ehandler = ehandler;
 	mscript->status = SIEVE_EXEC_OK;
 	mscript->active = TRUE;
 	
@@ -380,10 +379,10 @@ struct sieve_multiscript *sieve_multiscript_start_execute
 
 struct sieve_multiscript *sieve_multiscript_start_test
 (const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
-	struct sieve_error_handler *ehandler, struct ostream *stream)
+	struct ostream *stream)
 {
 	struct sieve_multiscript *mscript = 
-		sieve_multiscript_start_execute(msgdata, senv, ehandler);
+		sieve_multiscript_start_execute(msgdata, senv);
 	
 	mscript->teststream = stream;
 
@@ -391,10 +390,12 @@ struct sieve_multiscript *sieve_multiscript_start_test
 }
 
 static void sieve_multiscript_test
-(struct sieve_multiscript *mscript)
+(struct sieve_multiscript *mscript, struct sieve_error_handler *ehandler)
 {						
 	bool keep = FALSE;
 
+	sieve_result_set_error_handler(mscript->result, ehandler);
+
 	mscript->status = sieve_result_print
 		(mscript->result, mscript->scriptenv, mscript->teststream, &keep);
 		
@@ -404,10 +405,12 @@ static void sieve_multiscript_test
 }
 
 static void sieve_multiscript_execute
-(struct sieve_multiscript *mscript)
+(struct sieve_multiscript *mscript, struct sieve_error_handler *ehandler)
 {
 	bool keep = FALSE;
 			
+	sieve_result_set_error_handler(mscript->result, ehandler);
+
 	if ( mscript->status > 0 )
 		mscript->status = sieve_result_execute
 			(mscript->result, mscript->msgdata, mscript->scriptenv, &keep);
@@ -422,7 +425,7 @@ static void sieve_multiscript_execute
 
 bool sieve_multiscript_run
 (struct sieve_multiscript *mscript, struct sieve_binary *sbin,
-	bool final)
+	struct sieve_error_handler *ehandler, bool final)
 {
 	if ( !mscript->active ) return FALSE;
 	
@@ -431,13 +434,13 @@ bool sieve_multiscript_run
 	
 	/* Run the script */
 	mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata, 
-		mscript->scriptenv, mscript->ehandler);
+		mscript->scriptenv, ehandler);
 
 	if ( mscript->status >= 0 ) {
 		if ( mscript->teststream != NULL ) 
-			sieve_multiscript_test(mscript);
+			sieve_multiscript_test(mscript, ehandler);
 		else
-			sieve_multiscript_execute(mscript);
+			sieve_multiscript_execute(mscript, ehandler);
 
 		if ( final ) mscript->active = FALSE;
 	}	
@@ -453,11 +456,15 @@ int sieve_multiscript_status(struct sieve_multiscript *mscript)
 	return mscript->status;
 }
 
-int sieve_multiscript_finish(struct sieve_multiscript **mscript)
+int sieve_multiscript_finish(struct sieve_multiscript **mscript, 
+	struct sieve_error_handler *ehandler)
 {
 	struct sieve_result *result = (*mscript)->result;
 	int ret = (*mscript)->status;
 
+	if ( ehandler != NULL )
+		sieve_result_set_error_handler((*mscript)->result, ehandler);	
+
 	if ( (*mscript)->active ) {
 		ret = SIEVE_EXEC_FAILURE;
 
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index fa236e6a7..7648e8262 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -121,18 +121,19 @@ int sieve_execute
 struct sieve_multiscript;
  
 struct sieve_multiscript *sieve_multiscript_start_execute
-	(const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
-		struct sieve_error_handler *ehandler);
+	(const struct sieve_message_data *msgdata, const struct sieve_script_env *senv);
 struct sieve_multiscript *sieve_multiscript_start_test
 	(const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
-		struct sieve_error_handler *ehandler, struct ostream *stream);
+		struct ostream *stream);
 
 bool sieve_multiscript_run
-	(struct sieve_multiscript *mscript, struct sieve_binary *sbin, bool final);
+	(struct sieve_multiscript *mscript, struct sieve_binary *sbin, 
+		struct sieve_error_handler *ehandler, bool final);
 
 int sieve_multiscript_status(struct sieve_multiscript *mscript);
 
-int sieve_multiscript_finish(struct sieve_multiscript **mscript);
+int sieve_multiscript_finish
+	(struct sieve_multiscript **mscript, struct sieve_error_handler *ehandler);
 
 /*
  * Script directory
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index 2ca4527c4..727e4bcbd 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -22,7 +22,8 @@
 
 #define SIEVE_SCRIPT_PATH "~/.dovecot.sieve"
 
-#define LDA_SIEVE_MAX_ERRORS 10
+#define LDA_SIEVE_MAX_USER_ERRORS 10
+#define LDA_SIEVE_MAX_SYSTEM_ERRORS 100
 
 /*
  * Global variables 
@@ -53,6 +54,20 @@ static bool lda_sieve_smtp_close(void *handle)
  * Plugin implementation
  */
 
+struct lda_sieve_run_context {
+	const char *const *script_files;
+	unsigned int script_count;
+
+	const char *user_script;
+
+	const struct sieve_message_data *msgdata;
+	const struct sieve_script_env *scriptenv;
+
+	struct sieve_error_handler *user_ehandler;
+	struct sieve_error_handler *master_ehandler;
+	const char *userlog;
+};
+
 static const char *lda_sieve_get_path(void)
 {
 	const char *script_path, *home;
@@ -106,35 +121,77 @@ static const char *lda_sieve_get_path(void)
 	return script_path;
 }
 
+static void lda_sieve_multiscript_get_scriptfiles
+(const char *script_path, ARRAY_TYPE(const_string) *scriptfiles)
+{
+	struct sieve_directory *sdir = sieve_directory_open(script_path);
+
+	if ( sdir != NULL ) {
+		const char *file;
+
+		while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) {
+			const char *const *scripts;
+			unsigned int count, i;
+
+			/* Insert into sorted array */
+
+			scripts = array_get(scriptfiles, &count);
+			for ( i = 0; i < count; i++ ) {
+				if ( strcmp(file, scripts[i]) < 0 )
+					break;			
+			}
+	
+			if ( i == count ) 
+				array_append(scriptfiles, &file, 1);
+			else
+				array_insert(scriptfiles, i, &file, 1);
+		}
+
+		sieve_directory_close(&sdir);
+	} 
+}
+
 static int lda_sieve_open
-(const char *script_path, struct sieve_error_handler *ehandler, 
-	const char *scriptlog, struct sieve_binary **sbin)
+(struct lda_sieve_run_context *srctx, unsigned int script_index,
+	struct sieve_binary **sbin)
 {
+	const char *script_path = srctx->script_files[script_index];
+	struct sieve_error_handler *ehandler;
+	bool user_script = ( script_path == srctx->user_script );
 	bool exists = TRUE;
 	int ret = 0;
 
+	if ( user_script )
+		ehandler = srctx->user_ehandler;
+	else
+		ehandler = srctx->master_ehandler;
+
 	if ( lda_sieve_debug )
-		sieve_sys_info("opening script %s", script_path);
+		sieve_sys_info("opening script %s", script_path);		
+
+	sieve_error_handler_reset(ehandler);
 
-	if ( (*sbin=sieve_open(script_path, "main script", ehandler, &exists)) 
+	if ( (*sbin=sieve_open(script_path, user_script ? "main script" : NULL, ehandler, &exists)) 
 		== NULL ) {
 
 		ret = sieve_get_errors(ehandler) > 0 ? -1 : 0;
 
-		if ( lda_sieve_debug ) {
-			if ( !exists && ret == 0 ) 
-				sieve_sys_info
-					("script file %s is missing; reverting to default delivery", 
-						script_path);
-			else
-				sieve_sys_info
+		if ( !exists && ret == 0 ) {
+			if ( lda_sieve_debug )
+				sieve_sys_info("script file %s is missing", script_path);
+		} else {
+			if ( user_script && srctx->userlog != NULL ) {
+				sieve_sys_error
 					("failed to open script %s "
-						"(view logfile %s for more information); "
-						"reverting to default delivery", 
-						script_path, scriptlog);
+						"(view logfile %s for more information)", 
+						script_path, srctx->userlog);
+			} else {
+				sieve_sys_error
+					("failed to open script %s", 
+						script_path);
+			}
 		}
 
-		sieve_error_handler_unref(&ehandler);
 		return ret;
 	}
 
@@ -142,31 +199,41 @@ static int lda_sieve_open
 }
 
 static struct sieve_binary *lda_sieve_recompile
-(const char *script_path, struct sieve_error_handler *ehandler, 
-	const char *scriptlog)
+(struct lda_sieve_run_context *srctx, unsigned int script_index)
 {
+	const char *script_path = srctx->script_files[script_index];
+    struct sieve_error_handler *ehandler;
+    bool user_script = ( script_path == srctx->user_script );
 	struct sieve_binary *sbin;
 
 	/* Warn */
+
 	sieve_sys_warning("encountered corrupt binary: recompiling script %s", 
 		script_path);
 
 	/* Recompile */	
 
-	sieve_error_handler_copy_masterlog(ehandler, FALSE);
+	if ( user_script )
+		ehandler = srctx->user_ehandler;
+	else
+		ehandler = srctx->master_ehandler;
+
+	if ( (sbin=sieve_compile(script_path,
+		user_script ? "main script" : NULL, ehandler)) == NULL ) {
+
+		if ( user_script && srctx->userlog != NULL ) {
+			sieve_sys_error
+				("failed to re-compile script %s "
+					"(view logfile %s for more information)",
+					script_path, srctx->userlog);
+		} else {
+			sieve_sys_error
+				("failed to re-compile script %s", script_path);
+		}
 
-	if ( (sbin=sieve_compile(script_path, NULL, ehandler)) == NULL ) {
-		sieve_sys_error
-				("failed to compile script %s "
-					"(view logfile %s for more information); "
-					"reverting to default delivery", 
-					script_path, scriptlog);
-		sieve_error_handler_unref(&ehandler);
 		return NULL;
 	}
 
-	sieve_error_handler_copy_masterlog(ehandler, TRUE);
-
 	return sbin;
 }
 
@@ -202,235 +269,220 @@ static int lda_sieve_handle_exec_status(const char *script_path, int status)
 }
 
 static int lda_sieve_singlescript_execute
-(const char *script_path, struct sieve_binary **sbin, 
-	const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
-	struct sieve_error_handler *ehandler, const char *scriptlog)
+(struct lda_sieve_run_context *srctx)
 {
+	const char *script_file = srctx->script_files[0];
+    bool user_script = ( script_file == srctx->user_script );
+	struct sieve_error_handler *ehandler;
+	struct sieve_binary *sbin;
 	int ret;
 
-	if ( lda_sieve_debug )
-		sieve_sys_info("executing compiled script %s", script_path);
+	/* Open the script */
+
+	if ( (ret=lda_sieve_open(srctx, 0, &sbin)) <= 0 )
+		return ret;
 
 	/* Execute */
 
-	ret = sieve_execute(*sbin, msgdata, senv, ehandler);
+	if ( lda_sieve_debug )
+		sieve_sys_info("executing compiled script %s", script_file);
+
+	if ( user_script ) {
+		ehandler = srctx->user_ehandler;
+		sieve_error_handler_copy_masterlog(ehandler, TRUE);	
+	} else {
+		ehandler = srctx->master_ehandler;
+	}
+
+	ret = sieve_execute(sbin, srctx->msgdata, srctx->scriptenv, ehandler);
+
+	sieve_error_handler_copy_masterlog(ehandler, FALSE);	
 
-	/* Recompile corrupt binary */
+	/* Recompile if corrupt binary */
 
 	if ( ret == SIEVE_EXEC_BIN_CORRUPT ) {
 		/* Close corrupt script */
-		sieve_close(sbin);
+
+		sieve_close(&sbin);
 
 		/* Recompile */
-		if ( (*sbin=lda_sieve_recompile(script_path, ehandler, scriptlog)) == NULL )
+
+		if ( (sbin=lda_sieve_recompile(srctx, 0)) == NULL ) {
 			return -1;
+		}
 
 		/* Execute again */
-	
-		ret = sieve_execute(*sbin, msgdata, senv, ehandler);
+
+		if ( user_script )
+        	sieve_error_handler_copy_masterlog(ehandler, TRUE);
+
+		ret = sieve_execute(sbin, srctx->msgdata, srctx->scriptenv, ehandler);
+
+		sieve_error_handler_copy_masterlog(ehandler, FALSE);
 
 		/* Save new version */
-		
+
 		if ( ret != SIEVE_EXEC_BIN_CORRUPT )
-			sieve_save(*sbin, NULL);
+			sieve_save(sbin, NULL);
 	}
 
-	/* Report status */
+	sieve_close(&sbin);
 
-	return lda_sieve_handle_exec_status(script_path, ret);
+	/* Report status */
+	return lda_sieve_handle_exec_status(script_file, ret);
 }
 
-static int lda_sieve_multiscript_execute_script
-(struct sieve_multiscript *mscript, const char *script, bool final, 
-	struct sieve_error_handler *ehandler, const char *scriptlog)
+static int lda_sieve_multiscript_execute
+(struct lda_sieve_run_context *srctx)
 {
-	struct sieve_binary *sbin = NULL;
-	bool more = FALSE;
+	const char *const *scripts = srctx->script_files;
+	unsigned int count = srctx->script_count;
+	struct sieve_multiscript *mscript;
+	struct sieve_error_handler *ehandler;
+	const char *last_script;
+	bool user_script = FALSE;
+	unsigned int i;
+	int ret = 1; 
+	bool more = TRUE;
 
-	if ( lda_sieve_open(script, ehandler, scriptlog, &sbin) <= 0 )
-		return -1;
+	/* Start execution */
 
-	if ( !(more=sieve_multiscript_run(mscript, sbin, final)) ) {
-		if ( sieve_multiscript_status(mscript) == SIEVE_EXEC_BIN_CORRUPT ) {
-			/* Close corrupt script */
-			sieve_close(&sbin);
+	mscript = sieve_multiscript_start_execute(srctx->msgdata, srctx->scriptenv);
 
-			/* Recompile */
+	/* Execute scripts before main script */
 
-			if ( (sbin=lda_sieve_recompile(script, ehandler, scriptlog)) == NULL )
-				return -1;
+	for ( i = 0; i < count && more; i++ ) {
+		struct sieve_binary *sbin = NULL;
+		const char *script_file = scripts[i];
+		bool final = ( i == count - 1 );
 
-			/* Execute again */
+		user_script = ( script_file == srctx->user_script );
+		last_script = script_file;		
 
-			more = sieve_multiscript_run(mscript, sbin, final);
+		if ( user_script )
+			ehandler = srctx->user_ehandler;
+		else
+			ehandler = srctx->master_ehandler;
 
-			/* Save new version */
+		/* Open */
 	
-			if ( more && 
-				sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT )
-				sieve_save(sbin, NULL);
-		}
-	}
+		if ( (ret=lda_sieve_open(srctx, i, &sbin)) <= 0 )
+			break;
 
-	return (int) more;
-}
+		/* Execute */
 
-static void lda_sieve_multiscript_get_scriptfiles
-(const char *script_path, ARRAY_TYPE(const_string) *scriptfiles)
-{
-	struct sieve_directory *sdir = sieve_directory_open(script_path);
+		if ( user_script )	
+			sieve_error_handler_copy_masterlog(ehandler, TRUE);
 
-	if ( sdir != NULL ) {
-		const char *file;
+		more = sieve_multiscript_run(mscript, sbin, ehandler, final);
 
-		while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) {
-			const char *const *scripts;
-			unsigned int count, i;
-
-			/* Insert into sorted array */
+		sieve_error_handler_copy_masterlog(ehandler, FALSE);
 
-			scripts = array_get(scriptfiles, &count);
-			for ( i = 0; i < count; i++ ) {
-				if ( strcmp(file, scripts[i]) < 0 )
-					break;			
-			}
-	
-			if ( i == count ) 
-				array_append(scriptfiles, &file, 1);
-			else
-				array_insert(scriptfiles, i, &file, 1);
-		}
-
-		sieve_directory_close(&sdir);
-	} 
-}
-
-static int lda_sieve_multiscript_execute_scripts
-(struct sieve_multiscript *mscript, ARRAY_TYPE(const_string) *scripts, 
-	bool final, struct sieve_error_handler *ehandler, const char *scriptlog, 
-	const char **last_script_r)
-{
-	const char *const *scriptfiles;
-	unsigned int count, i;
-	int ret = 0;
-		 
-	scriptfiles = array_get(scripts, &count);
-	for ( i = 0; i < count; i++ ) {
-		*last_script_r = scriptfiles[i];
-
-		if ( (ret=lda_sieve_multiscript_execute_script
-			(mscript, scriptfiles[i], ( final && i == count - 1 ), ehandler, 
-				scriptlog)) <= 0 )
-			return ret;
-	}
+		if ( !more ) {
+			if ( sieve_multiscript_status(mscript) == SIEVE_EXEC_BIN_CORRUPT ) {
+				/* Close corrupt script */
 
-	return 1;
-}
+				sieve_close(&sbin);
 
-static int lda_sieve_multiscript_execute
-(const char *script_path, struct sieve_binary **main_sbin,
-	ARRAY_TYPE (const_string) *scripts_before, 
-	ARRAY_TYPE (const_string) *scripts_after, 
-	const struct sieve_message_data *msgdata, const struct sieve_script_env *senv, 
-	struct sieve_error_handler *ehandler, const char *scriptlog)
-{
-	/* Multiple scripts */
-	struct sieve_multiscript *mscript = sieve_multiscript_start_execute
-		(msgdata, senv, ehandler);
-	bool final = (*main_sbin == NULL ) && ( array_count(scripts_after) == 0 ); 
-	const char *last_script = NULL;
-	int ret = 1; 
+				/* Recompile */
 
-	/* Execute scripts before main script */
-	ret = lda_sieve_multiscript_execute_scripts
-		(mscript, scripts_before, final, ehandler, scriptlog, &last_script);
+				if ( (sbin=lda_sieve_recompile(srctx, i))
+					== NULL ) {
+					ret = -1;
+					break;
+				}
 
-	/* Execute main script */
-	if ( *main_sbin != NULL && ret > 0 ) {
-		final = ( array_count(scripts_after) == 0 );
+				/* Execute again */
 
-		last_script = script_path;
+				if ( user_script )
+		            sieve_error_handler_copy_masterlog(ehandler, TRUE);
 
-		if ( !(ret=sieve_multiscript_run(mscript, *main_sbin, final)) ) {
+				more = sieve_multiscript_run(mscript, sbin, ehandler, final);
 
-			if ( sieve_multiscript_status(mscript) == SIEVE_EXEC_BIN_CORRUPT ) {
-				/* Close corrupt script */
-				sieve_close(main_sbin);
+				sieve_error_handler_copy_masterlog(ehandler, FALSE);
 
-				/* Recompile */
-				if ( (*main_sbin=lda_sieve_recompile(script_path, ehandler, scriptlog))
-					== NULL ) {
-					ret = -1;
-				} else {
+				/* Save new version */
 
-					/* Execute again */
-	
-					ret = sieve_multiscript_run(mscript, *main_sbin, final);
-
-					/* Save new version */
-		
-					if ( sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT )
-						sieve_save(*main_sbin, NULL);
-				}
+				if ( more && 
+					sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT )
+					sieve_save(sbin, NULL);
 			}
 		}
-	}
 
-	/* Execute scripts after main script */
-	if ( ret > 0 )
-		ret = lda_sieve_multiscript_execute_scripts
-			(mscript, scripts_after, TRUE, ehandler, scriptlog, &last_script); 
+		sieve_close(&sbin);
+	}
 
 	/* Finish execution */
-	ret = sieve_multiscript_finish(&mscript);
+
+	if ( user_script )	
+		sieve_error_handler_copy_masterlog(ehandler, TRUE);
+
+	ret = sieve_multiscript_finish(&mscript, ehandler);
+
+	sieve_error_handler_copy_masterlog(ehandler, FALSE);
 
 	return lda_sieve_handle_exec_status(last_script, ret);
 }
 
 static int lda_sieve_run
-(struct mail_namespace *namespaces, struct mail *mail, const char *script_path,
-    ARRAY_TYPE (const_string) *scripts_before,
-	ARRAY_TYPE (const_string) *scripts_after,
-	const char *destaddr, const char *username, const char *mailbox,
+(struct mail_namespace *namespaces, struct mail *mail, const char *user_script,
+	const ARRAY_TYPE (const_string) *scripts_before, 
+	const ARRAY_TYPE (const_string) *scripts_after, 
+	const char *destaddr, const char *username, const char *mailbox, 
 	struct mail_storage **storage_r)
 {
+	ARRAY_TYPE (const_string) scripts;
+
+	struct lda_sieve_run_context srctx;
 	struct sieve_message_data msgdata;
 	struct sieve_script_env scriptenv;
 	struct sieve_exec_status estatus;
-	struct sieve_error_handler *ehandler;
-	struct sieve_binary *sbin = NULL;
-	const char *scriptlog;
 	int ret = 0;
 
 	*storage_r = NULL;
 
-	/* Create error handler */
+	/* Initialize */
 
-	scriptlog = t_strconcat(script_path, ".log", NULL);
-	ehandler = sieve_logfile_ehandler_create(scriptlog, LDA_SIEVE_MAX_ERRORS);
+	memset(&srctx, 0, sizeof(srctx));
 
-	/* Open the script */
+	/* Compose execution sequence */
+
+	t_array_init(&scripts, 32);
+
+	array_append_array(&scripts, scripts_before);
+
+	if ( user_script != NULL )
+		array_append(&scripts, &user_script, 1);
+	srctx.user_script = user_script;
+
+	array_append_array(&scripts, scripts_after);
 
-	if ( script_path != NULL ) {
-		if ( (ret=lda_sieve_open(script_path, ehandler, scriptlog, &sbin)) <= 0 )
-			return ret;
+	/* Create error handlers */
+
+	if ( user_script != NULL ) {
+		srctx.userlog = t_strconcat(user_script, ".log", NULL);
+		srctx.user_ehandler = sieve_logfile_ehandler_create(srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS);
 	}
-	
-	/* Log the messages to the system error handlers as well from this moment
-	 * on.
-	 */
-	sieve_error_handler_copy_masterlog(ehandler, TRUE);
+
+	srctx.master_ehandler = sieve_master_ehandler_create(LDA_SIEVE_MAX_SYSTEM_ERRORS);
+	sieve_error_handler_accept_infolog(srctx.master_ehandler, TRUE);
 
 	/* Collect necessary message data */
+
 	memset(&msgdata, 0, sizeof(msgdata));
+
 	msgdata.mail = mail;
 	msgdata.return_path = deliver_get_return_address(mail);
 	msgdata.to_address = destaddr;
 	msgdata.auth_user = username;
 	(void)mail_get_first_header(mail, "Message-ID", &msgdata.id);
 
+	srctx.msgdata = &msgdata;
+
 	/* Compose script execution environment */
+
 	memset(&scriptenv, 0, sizeof(scriptenv));
+
 	scriptenv.default_mailbox = mailbox;
 	scriptenv.mailbox_autocreate = deliver_set->mailbox_autocreate;
 	scriptenv.mailbox_autosubscribe = deliver_set->mailbox_autosubscribe;
@@ -444,13 +496,18 @@ static int lda_sieve_run
 	scriptenv.duplicate_check = duplicate_check;
 	scriptenv.exec_status = &estatus;
 
-	if ( array_count(scripts_before) == 0 && array_count(scripts_after) == 0 )
-		ret = lda_sieve_singlescript_execute
-			(script_path, &sbin, &msgdata, &scriptenv, ehandler, scriptlog);
+	srctx.scriptenv = &scriptenv;
+
+	/* Assign script sequence */
+
+	srctx.script_files = array_get(&scripts, &srctx.script_count);
+
+	/* Execute script(s) */
+
+	if ( srctx.script_count == 1 )
+		ret = lda_sieve_singlescript_execute(&srctx);
 	else
-		ret = lda_sieve_multiscript_execute
-			(script_path, &sbin, scripts_before, scripts_after, &msgdata, 
-				&scriptenv, ehandler, scriptlog);	
+		ret = lda_sieve_multiscript_execute(&srctx);	
 
 	/* Record status */
 
@@ -458,9 +515,10 @@ static int lda_sieve_run
 	*storage_r = estatus.last_storage;
 
 	/* Clean up */
-	if ( sbin != NULL )
-		sieve_close(&sbin);
-	sieve_error_handler_unref(&ehandler);
+
+	if ( srctx.user_ehandler != NULL )
+		sieve_error_handler_unref(&srctx.user_ehandler);
+	sieve_error_handler_unref(&srctx.master_ehandler);
 
 	return ret;
 }
@@ -469,7 +527,7 @@ static int lda_sieve_deliver_mail
 (struct mail_namespace *namespaces, struct mail_storage **storage_r, 
 	struct mail *mail, const char *destaddr, const char *mailbox)
 {
-	const char *script_path, *sieve_before, *sieve_after;
+	const char *user_script, *sieve_before, *sieve_after;
 	ARRAY_TYPE (const_string) scripts_before;
 	ARRAY_TYPE (const_string) scripts_after;
 	int ret = 0;
@@ -478,59 +536,59 @@ static int lda_sieve_deliver_mail
 
 		/* Find the personal script to execute */
 	
-		script_path = lda_sieve_get_path();
+		user_script = lda_sieve_get_path();
+
+		if ( lda_sieve_debug ) {
+			if ( user_script == NULL )			
+				sieve_sys_info("user has no valid personal script");
+			else
+				sieve_sys_info("using sieve path for user's script: %s", user_script);
+		}
 
 		/* Check for multiscript */
 
-		t_array_init(&scripts_after, 16);
 		t_array_init(&scripts_before, 16);
+		t_array_init(&scripts_after, 16);
 
 		sieve_before = getenv("SIEVE_BEFORE");
 		sieve_after = getenv("SIEVE_AFTER");
 
-		if ( sieve_before != NULL && *sieve_before != '\0' )
+		if ( sieve_before != NULL && *sieve_before != '\0' ) {
 			lda_sieve_multiscript_get_scriptfiles(sieve_before, &scripts_before);
+		}
 
-		if ( sieve_after != NULL && *sieve_after != '\0' )
+		if ( sieve_after != NULL && *sieve_after != '\0' ) {
 			lda_sieve_multiscript_get_scriptfiles(sieve_after, &scripts_after);
+		}
+
+		if ( lda_sieve_debug ) {
+			const char *const *scriptfiles;
+			unsigned int count, i;
+
+			scriptfiles = array_get(&scripts_before, &count);
+			for ( i = 0; i < count; i ++ ) {
+				sieve_sys_info("executed before user's script(%d): %s", i+1, scriptfiles[i]);				
+			}
+
+			scriptfiles = array_get(&scripts_after, &count);
+			for ( i = 0; i < count; i ++ ) {
+				sieve_sys_info("executed after user's script(%d): %s", i+1, scriptfiles[i]);				
+			}
+		}
 	
 		/* Check whether there are any scripts to execute */
 
-		if ( script_path == NULL && array_count(&scripts_before) == 0 && 
-			array_count(&scripts_after) == 0 ) {
-
+		if ( array_count(&scripts_before) == 0 && array_count(&scripts_before) == 0 &&
+			user_script == NULL ) {
 			if ( lda_sieve_debug )
 				sieve_sys_info("no scripts to execute: reverting to default delivery.");
 
 			ret = 0;
 		} else {
-
-			/* Show configuration */
-
-			if ( lda_sieve_debug ) {
-				const char *const *scripts;
-				unsigned int count, i;
-
-				if ( script_path == NULL )			
-					sieve_sys_info("user has no valid personal script");
-				else
-					sieve_sys_info("using sieve path for user's script: %s", script_path);
-
-				scripts = array_get(&scripts_before, &count);
-				for ( i = 0; i < count; i ++ ) {
-					sieve_sys_info("executed before user's script(%d): %s", i+1, scripts[i]);				
-				}
-
-				scripts = array_get(&scripts_after, &count);
-				for ( i = 0; i < count; i ++ ) {
-					sieve_sys_info("executed after user's script(%d): %s", i+1, scripts[i]);				
-				}
-			}
-	
 			/* Run the script(s) */
-	
+				
 			ret = lda_sieve_run
-				(namespaces, mail, script_path, &scripts_before, &scripts_after, destaddr, 
+				(namespaces, mail, user_script, &scripts_before, &scripts_after, destaddr,
 					getenv("USER"), mailbox, storage_r);
 		}
 
diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c
index 828361ba7..08083d907 100644
--- a/src/sieve-tools/sieve-test.c
+++ b/src/sieve-tools/sieve-test.c
@@ -305,10 +305,10 @@ int main(int argc, char **argv)
 
 			if ( execute )
 				mscript = sieve_multiscript_start_execute
-					(&msgdata, &scriptenv, ehandler);
+					(&msgdata, &scriptenv);
 			else
 				mscript = sieve_multiscript_start_test
-					(&msgdata, &scriptenv, ehandler, teststream);
+					(&msgdata, &scriptenv, teststream);
 		
 			/* Execute scripts sequentially */
 			sfiles = array_get(&scriptfiles, &count); 
@@ -335,7 +335,7 @@ int main(int argc, char **argv)
 				}
 			
 				/* Execute/Test script */
-				more = sieve_multiscript_run(mscript, sbin, FALSE);
+				more = sieve_multiscript_run(mscript, sbin, ehandler, FALSE);
 			}
 		
 			/* Execute/Test main script */
@@ -351,10 +351,10 @@ int main(int argc, char **argv)
 				sbin = main_sbin;
 				main_sbin = NULL;
 			
-				sieve_multiscript_run(mscript, sbin, TRUE);
+				sieve_multiscript_run(mscript, sbin, ehandler, TRUE);
 			}
 			
-			result = sieve_multiscript_finish(&mscript);
+			result = sieve_multiscript_finish(&mscript, ehandler);
 			
 			ret = ret > 0 ? result : ret;
 		}
-- 
GitLab