From af703f2e2da595a55e45f185c7c855846de61127 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 2 Jun 2013 16:08:22 +0200
Subject: [PATCH] Finished support for temporary failures. This change is
 bigger than I would have liked, so this needs to be tested more.

---
 src/lib-sieve/cmd-discard.c                   |   6 +-
 src/lib-sieve/cmd-redirect.c                  |  23 +-
 src/lib-sieve/ext-reject.c                    |  14 +-
 src/lib-sieve/plugins/enotify/cmd-notify.c    |   6 +-
 src/lib-sieve/plugins/imap4flags/tag-flags.c  |   6 +-
 .../plugins/mailbox/tag-mailbox-create.c      |  27 +-
 src/lib-sieve/plugins/notify/cmd-notify.c     |   6 +-
 src/lib-sieve/plugins/vacation/cmd-vacation.c |  26 +-
 .../duplicate/ext-duplicate-common.c          |   6 +-
 src/lib-sieve/sieve-actions.c                 |  99 +--
 src/lib-sieve/sieve-actions.h                 |  10 +-
 src/lib-sieve/sieve-binary-file.c             |  14 +-
 src/lib-sieve/sieve-result.c                  | 139 ++--
 src/lib-sieve/sieve-result.h                  |   4 +-
 src/lib-sieve/sieve-script-dict.c             |  12 +-
 src/lib-sieve/sieve-script-file.c             |  16 +-
 src/lib-sieve/sieve-script.c                  |   4 +-
 src/lib-sieve/sieve-types.h                   |  13 +-
 src/lib-sieve/sieve.c                         |  74 +-
 src/lib-sieve/sieve.h                         |   2 +
 src/lib-sievestorage/sieve-storage.c          |   4 +-
 src/managesieve/managesieve-client.c          |   2 +-
 src/plugins/lda-sieve/lda-sieve-plugin.c      | 642 ++++++++++--------
 src/plugins/sieve-extprograms/cmd-pipe.c      |  13 +-
 .../sieve-extprograms-common.c                |  10 +-
 src/sieve-tools/sieve-filter.c                |   5 +-
 src/sieve-tools/sieve-test.c                  |   8 +-
 src/testsuite/testsuite.c                     |   3 +-
 28 files changed, 693 insertions(+), 501 deletions(-)

diff --git a/src/lib-sieve/cmd-discard.c b/src/lib-sieve/cmd-discard.c
index cf4c9265c..8afa7b2b6 100644
--- a/src/lib-sieve/cmd-discard.c
+++ b/src/lib-sieve/cmd-discard.c
@@ -58,7 +58,7 @@ const struct sieve_operation_def cmd_discard_operation = {
 static void act_discard_print
 	(const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
-static bool act_discard_commit
+static int act_discard_commit
 	(const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
 
@@ -128,7 +128,7 @@ static void act_discard_print
 	*keep = FALSE;
 }
 
-static bool act_discard_commit
+static int act_discard_commit
 (const struct sieve_action *action ATTR_UNUSED,
 	const struct sieve_action_exec_env *aenv,
 	void *tr_context ATTR_UNUSED, bool *keep)
@@ -138,6 +138,6 @@ static bool act_discard_commit
 		"(discard action)");
 	*keep = FALSE;
 
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index f1785b900..d3a27352d 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -87,7 +87,7 @@ static int act_redirect_check_duplicate
 static void act_redirect_print
 	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		bool *keep);
-static bool act_redirect_commit
+static int act_redirect_commit
 	(const struct sieve_action *action, const struct sieve_action_exec_env *aenv,
 		void *tr_context, bool *keep);
 
@@ -307,7 +307,7 @@ static void act_redirect_print
 	*keep = FALSE;
 }
 
-static bool act_redirect_send
+static int act_redirect_send
 (const struct sieve_action_exec_env *aenv, struct mail *mail,
 	struct act_redirect_context *ctx)
 {
@@ -326,11 +326,11 @@ static bool act_redirect_send
 	if ( !sieve_smtp_available(senv) ) {
 		sieve_result_global_warning
 			(aenv, "redirect action has no means to send mail.");
-		return TRUE;
+		return SIEVE_EXEC_FAILURE;
 	}
 
 	if (mail_get_stream(mail, NULL, NULL, &input) < 0)
-		return FALSE;
+		return SIEVE_EXEC_TEMP_FAILURE;
 
 	/* Open SMTP transport */
 	smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &output);
@@ -359,13 +359,13 @@ static bool act_redirect_send
 			"failed to redirect message to <%s> "
 			"(refer to server log for more information)",
 			str_sanitize(ctx->to_address, 80));
-		return FALSE;
+		return SIEVE_EXEC_FAILURE;
 	}
 
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
-static bool act_redirect_commit
+static int act_redirect_commit
 (const struct sieve_action *action,
 	const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED,
 	bool *keep)
@@ -378,6 +378,7 @@ static bool act_redirect_commit
 	const struct sieve_script_env *senv = aenv->scriptenv;
 	const char *orig_recipient = sieve_message_get_orig_recipient(aenv->msgctx);
 	const char *dupeid;
+	int ret;
 
 	/* Prevent mail loops if possible */
 	dupeid = msgdata->id == NULL ? NULL : t_strdup_printf
@@ -388,12 +389,12 @@ static bool act_redirect_commit
 			sieve_result_global_log(aenv, "discarded duplicate forward to <%s>",
 				str_sanitize(ctx->to_address, 128));
 			*keep = FALSE;
-			return TRUE;
+			return SIEVE_EXEC_OK;
 		}
 	}
 
 	/* Try to forward the message */
-	if ( act_redirect_send(aenv, mail, ctx) ) {
+	if ( (ret=act_redirect_send(aenv, mail, ctx)) == SIEVE_EXEC_OK) {
 
 		/* Mark this message id as forwarded to the specified destination */
 		if (dupeid != NULL) {
@@ -410,10 +411,10 @@ static bool act_redirect_commit
 		/* Cancel implicit keep */
 		*keep = FALSE;
 
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
-	return FALSE;
+	return ret;
 }
 
 
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index 4532677aa..025e4de6d 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -181,7 +181,7 @@ int act_reject_check_conflict
 static void act_reject_print
 	(const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
-static bool act_reject_commit
+static int act_reject_commit
 	(const struct sieve_action *action ATTR_UNUSED,
 		const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
 
@@ -379,7 +379,7 @@ static void act_reject_print
 	*keep = FALSE;
 }
 
-static bool act_reject_commit
+static int act_reject_commit
 (const struct sieve_action *action, const struct sieve_action_exec_env *aenv,
 	void *tr_context ATTR_UNUSED, bool *keep)
 {
@@ -391,7 +391,7 @@ static bool act_reject_commit
 	if ( recipient == NULL ) {
 		sieve_result_global_warning(aenv,
 			"reject action aborted: envelope recipient is <>");
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	if ( rj_ctx->reason == NULL ) {
@@ -399,14 +399,14 @@ static bool act_reject_commit
 			"not sending reject message (would cause second response to sender)");
 
 		*keep = FALSE;
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	if ( sender == NULL ) {
 		sieve_result_global_log(aenv, "not sending reject message to <>");
 
 		*keep = FALSE;
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	if ( sieve_action_reject_mail(aenv, sender, recipient, rj_ctx->reason) ) {
@@ -415,10 +415,10 @@ static bool act_reject_commit
 			( rj_ctx->ereject ? "ereject" : "reject" ));
 
 		*keep = FALSE;
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
-	return FALSE;
+	return SIEVE_EXEC_FAILURE;
 }
 
 
diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c
index 488fc18b9..993f95224 100644
--- a/src/lib-sieve/plugins/enotify/cmd-notify.c
+++ b/src/lib-sieve/plugins/enotify/cmd-notify.c
@@ -136,7 +136,7 @@ static int act_notify_check_duplicate
 static void act_notify_print
 	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		bool *keep);
-static bool act_notify_commit
+static int act_notify_commit
 	(const struct sieve_action *action,	const struct sieve_action_exec_env *aenv,
 		void *tr_context, bool *keep);
 
@@ -566,7 +566,7 @@ static void act_notify_print
 
 /* Result execution */
 
-static bool act_notify_commit
+static int act_notify_commit
 (const struct sieve_action *action,	const struct sieve_action_exec_env *aenv,
 	void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
 {
@@ -593,7 +593,7 @@ static bool act_notify_commit
 		sieve_error_handler_unref(&nenv.ehandler);
 	}
 
-	return result;
+	return ( result ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE );
 }
 
 
diff --git a/src/lib-sieve/plugins/imap4flags/tag-flags.c b/src/lib-sieve/plugins/imap4flags/tag-flags.c
index 001f69c72..6c64c1193 100644
--- a/src/lib-sieve/plugins/imap4flags/tag-flags.c
+++ b/src/lib-sieve/plugins/imap4flags/tag-flags.c
@@ -70,7 +70,7 @@ static int seff_flags_merge
 static void seff_flags_print
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
-static bool seff_flags_pre_execute
+static int seff_flags_pre_execute
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void **context, void *tr_context);
 
@@ -383,7 +383,7 @@ static void seff_flags_print
 
 /* Result execution */
 
-static bool seff_flags_pre_execute
+static int seff_flags_pre_execute
 (const struct sieve_side_effect *seffect,
 	const struct sieve_action *action ATTR_UNUSED,
 	const struct sieve_action_exec_env *aenv, void **context, void *tr_context)
@@ -402,7 +402,7 @@ static bool seff_flags_pre_execute
 
 	sieve_act_store_add_flags(aenv, tr_context, keywords, ctx->flags);
 
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
 
diff --git a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
index 9e3dbfdad..f1745b067 100644
--- a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
+++ b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
@@ -41,7 +41,7 @@ const struct sieve_argument_def mailbox_create_tag = {
 static void seff_mailbox_create_print
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
-static bool seff_mailbox_create_pre_execute
+static int seff_mailbox_create_pre_execute
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void **se_context,
 		void *tr_context);
@@ -113,7 +113,7 @@ static void seff_mailbox_create_print
 	sieve_result_seffect_printf(rpenv, "create mailbox if it does not exist");
 }
 
-static bool seff_mailbox_create_pre_execute
+static int seff_mailbox_create_pre_execute
 (const struct sieve_side_effect *seffect ATTR_UNUSED,
 	const struct sieve_action *action ATTR_UNUSED,
 	const struct sieve_action_exec_env *aenv ATTR_UNUSED,
@@ -126,17 +126,22 @@ static bool seff_mailbox_create_pre_execute
 
 	/* Check whether creation is necessary */
 	if ( trans->box == NULL || trans->disabled )
-		return TRUE;
+		return SIEVE_EXEC_OK;
 
 	/* Check whether creation has a chance of working */
-	if ( trans->error_code != MAIL_ERROR_NONE &&
-		trans->error_code != MAIL_ERROR_NOTFOUND )
-		return FALSE;
+	switch ( trans->error_code ) {
+	case MAIL_ERROR_NONE:
+	case MAIL_ERROR_NOTFOUND:
+		break;
+	case MAIL_ERROR_TEMP:
+		return SIEVE_EXEC_TEMP_FAILURE;
+	default:
+		return SIEVE_EXEC_FAILURE;
+	}
 
 	trans->error = NULL;
 	trans->error_code = MAIL_ERROR_NONE;
 
-
 	*storage = mailbox_get_storage(trans->box);
 
 	/* Create mailbox */
@@ -144,7 +149,8 @@ static bool seff_mailbox_create_pre_execute
 		(void)mail_storage_get_last_error(*storage, &error);
 		if ( error != MAIL_ERROR_EXISTS ) {
 			sieve_act_store_get_storage_error(aenv, trans);
-			return FALSE;
+			return ( trans->error_code == MAIL_ERROR_TEMP ?
+				SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE );
 		}
 	}
 
@@ -159,10 +165,11 @@ static bool seff_mailbox_create_pre_execute
 	if ( mailbox_open(trans->box) < 0 ) {
 		/* Failed definitively */
 		sieve_act_store_get_storage_error(aenv, trans);
-		return FALSE;
+		return ( trans->error_code == MAIL_ERROR_TEMP ?
+			SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE );
 	}
 
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
 
diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
index 849662373..d77d8bd00 100644
--- a/src/lib-sieve/plugins/notify/cmd-notify.c
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -146,7 +146,7 @@ static int act_notify_check_duplicate
 static void act_notify_print
 	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		bool *keep);
-static bool act_notify_commit
+static int act_notify_commit
 	(const struct sieve_action *action,	const struct sieve_action_exec_env *aenv,
 		void *tr_context, bool *keep);
 
@@ -789,7 +789,7 @@ static bool act_notify_send
 	return TRUE;
 }
 
-static bool act_notify_commit
+static int act_notify_commit
 (const struct sieve_action *action, const struct sieve_action_exec_env *aenv,
 	void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
 {
@@ -820,7 +820,7 @@ static bool act_notify_commit
 		result = act_notify_send(aenv, act);
 	} T_END;
 
-	return result;
+	return ( result ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE );
 }
 
 
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index e7196d1f8..098525a11 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -190,7 +190,7 @@ int act_vacation_check_conflict
 static void act_vacation_print
 	(const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
-static bool act_vacation_commit
+static int act_vacation_commit
 	(const struct sieve_action *action,	const struct sieve_action_exec_env *aenv,
 		void *tr_context, bool *keep);
 
@@ -1018,7 +1018,7 @@ static void act_vacation_hash
 	md5_final(&ctx, hash_r);
 }
 
-static bool act_vacation_commit
+static int act_vacation_commit
 (const struct sieve_action *action, const struct sieve_action_exec_env *aenv,
 	void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
 {
@@ -1043,14 +1043,14 @@ static bool act_vacation_commit
 	if ( recipient == NULL ) {
 		sieve_result_global_warning
 			(aenv, "vacation action aborted: envelope recipient is <>");
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	/* Is the return path unset ?
 	 */
 	if ( sender == NULL ) {
 		sieve_result_global_log(aenv, "discarded vacation reply to <>");
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	/* Are we perhaps trying to respond to ourselves ?
@@ -1059,7 +1059,7 @@ static bool act_vacation_commit
 		sieve_result_global_log(aenv,
 			"discarded vacation reply to own address <%s>",
 			str_sanitize(sender, 128));
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	/* Are we perhaps trying to respond to one of our alternative :addresses?
@@ -1073,7 +1073,7 @@ static bool act_vacation_commit
 					"discarded vacation reply to own address <%s> "
 					"(as specified using :addresses argument)",
 					str_sanitize(sender, 128));
-				return TRUE;
+				return SIEVE_EXEC_OK;
 			}
 			alt_address++;
 		}
@@ -1088,7 +1088,7 @@ static bool act_vacation_commit
 			sieve_result_global_log(aenv,
 				"discarded duplicate vacation response to <%s>",
 				str_sanitize(sender, 128));
-			return TRUE;
+			return SIEVE_EXEC_OK;
 		}
 	}
 
@@ -1101,7 +1101,7 @@ static bool act_vacation_commit
 			sieve_result_global_log(aenv,
 				"discarding vacation response to mailinglist recipient <%s>",
 				str_sanitize(sender, 128));
-			return TRUE;
+			return SIEVE_EXEC_OK;
 		}
 		hdsp++;
 	}
@@ -1116,7 +1116,7 @@ static bool act_vacation_commit
 				sieve_result_global_log(aenv,
 					"discarding vacation response to auto-submitted message from <%s>",
  					str_sanitize(sender, 128));
-					return TRUE;
+					return SIEVE_EXEC_OK;
 			}
 			hdsp++;
 		}
@@ -1133,7 +1133,7 @@ static bool act_vacation_commit
 				sieve_result_global_log(aenv,
 					"discarding vacation response to precedence=%s message from <%s>",
 					*hdsp, str_sanitize(sender, 128));
-					return TRUE;
+					return SIEVE_EXEC_OK;
 			}
 			hdsp++;
 		}
@@ -1144,7 +1144,7 @@ static bool act_vacation_commit
 		sieve_result_global_log(aenv,
 			"not sending vacation response to system address <%s>",
 			str_sanitize(sender, 128));
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	/* Fetch original recipient if necessary */
@@ -1213,7 +1213,7 @@ static bool act_vacation_commit
 				str_sanitize(recipient, 128), original_recipient,
 				(ctx->addresses == NULL ? " no" : ""));
 
-			return TRUE;
+			return SIEVE_EXEC_OK;
 		}
 	}
 
@@ -1243,7 +1243,7 @@ static bool act_vacation_commit
 		}
 	}
 
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
 
diff --git a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c
index 836baafb6..fb10e3f91 100644
--- a/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c
+++ b/src/lib-sieve/plugins/vnd.dovecot/duplicate/ext-duplicate-common.c
@@ -77,7 +77,7 @@ struct act_duplicate_mark_data {
 static void act_duplicate_mark_print
 	(const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
-static bool act_duplicate_mark_commit
+static int act_duplicate_mark_commit
 	(const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
 
@@ -106,7 +106,7 @@ static void act_duplicate_mark_print
 	}
 }
 
-static bool act_duplicate_mark_commit
+static int act_duplicate_mark_commit
 (const struct sieve_action *action,
 	const struct sieve_action_exec_env *aenv,
 	void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
@@ -121,7 +121,7 @@ static bool act_duplicate_mark_commit
 	sieve_action_duplicate_mark
 		(senv, data->hash, sizeof(data->hash), ioloop_time + data->period);
 	
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
 
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index e39db4935..7ae4ca297 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -190,13 +190,13 @@ static void act_store_print
 	(const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);
 
-static bool act_store_start
+static int act_store_start
 	(const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void **tr_context);
-static bool act_store_execute
+static int act_store_execute
 	(const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void *tr_context);
-static bool act_store_commit
+static int act_store_commit
 	(const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
 static void act_store_rollback
@@ -283,16 +283,6 @@ void sieve_act_store_add_flags
 	trans->flags_altered = TRUE;
 }
 
-void sieve_act_store_get_storage_error
-(const struct sieve_action_exec_env *aenv, struct act_store_transaction *trans)
-{
-	pool_t pool = sieve_result_pool(aenv->result);
-
-	trans->error = p_strdup(pool,
-		mail_storage_get_last_error(mailbox_get_storage(trans->box),
-		&trans->error_code));
-}
-
 /* Equality */
 
 static bool act_store_equals
@@ -353,6 +343,17 @@ static void act_store_print
 
 /* Action implementation */
 
+void sieve_act_store_get_storage_error
+(const struct sieve_action_exec_env *aenv,
+	struct act_store_transaction *trans)
+{
+	pool_t pool = sieve_result_pool(aenv->result);
+
+	trans->error = p_strdup(pool,
+		mail_storage_get_last_error(mailbox_get_storage(trans->box),
+		&trans->error_code));
+}
+
 static bool act_store_mailbox_open
 (const struct sieve_action_exec_env *aenv, const char *mailbox,
 	struct mailbox **box_r, enum mail_error *error_code_r, const char **error_r)
@@ -384,7 +385,7 @@ static bool act_store_mailbox_open
 	return TRUE;
 }
 
-static bool act_store_start
+static int act_store_start
 (const struct sieve_action *action,
 	const struct sieve_action_exec_env *aenv, void **tr_context)
 {
@@ -434,8 +435,17 @@ static bool act_store_start
 
 	*tr_context = (void *)trans;
 
-	return ( trans->error_code == MAIL_ERROR_NONE ||
-		trans->error_code == MAIL_ERROR_NOTFOUND );
+	switch ( trans->error_code ) {
+	case MAIL_ERROR_NONE:
+	case MAIL_ERROR_NOTFOUND:
+		return SIEVE_EXEC_OK;
+	case MAIL_ERROR_TEMP:
+		return SIEVE_EXEC_TEMP_FAILURE;
+	default:
+		break;
+	}
+	
+	return SIEVE_EXEC_FAILURE;
 }
 
 static struct mail_keywords *act_store_keywords_create
@@ -461,7 +471,7 @@ static struct mail_keywords *act_store_keywords_create
 	return box_keywords;
 }
 
-static bool act_store_execute
+static int act_store_execute
 (const struct sieve_action *action,
 	const struct sieve_action_exec_env *aenv, void *tr_context)
 {
@@ -472,17 +482,27 @@ static bool act_store_execute
 	struct mail *real_mail = mail_get_real_mail(mail);
 	struct mail_save_context *save_ctx;
 	struct mail_keywords *keywords = NULL;
-	bool result = TRUE;
+	int status = SIEVE_EXEC_OK;
 
 	/* Verify transaction */
-	if ( trans == NULL ) return FALSE;
+	if ( trans == NULL ) return SIEVE_EXEC_FAILURE;
 
 	/* Check whether we need to do anything */
-	if ( trans->disabled ) return TRUE;
+	if ( trans->disabled ) return SIEVE_EXEC_OK;
 
 	/* Exit early if mailbox is not available */
-	if ( trans->box == NULL || trans->error_code != MAIL_ERROR_NONE )
-		return FALSE;
+	if ( trans->box == NULL )
+		return SIEVE_EXEC_FAILURE;
+
+	/* Exit early if transaction already failed */
+ 	switch ( trans->error_code ) {
+	case MAIL_ERROR_NONE:
+		break;
+	case MAIL_ERROR_TEMP:
+		return SIEVE_EXEC_TEMP_FAILURE;
+	default:
+		return SIEVE_EXEC_FAILURE;
+	}
 
 	/* If the message originates from the target mailbox, only update the flags
 	 * and keywords (if not read-only)
@@ -504,7 +524,7 @@ static bool act_store_execute
 			mail_update_flags(mail, MODIFY_REPLACE, trans->flags);
 		}
 
-		return TRUE;
+		return SIEVE_EXEC_OK;
 
 	/* If the message is modified, only store it in the source mailbox when it is
 	 * not opened read-only. Mail structs of modified messages have their own
@@ -516,7 +536,7 @@ static bool act_store_execute
 		&& ( mailbox_backends_equal(trans->box, aenv->msgdata->mail->box) ) ) {
 
 		trans->redundant = TRUE;
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	/* Mark attempt to store in default mailbox */
@@ -551,7 +571,8 @@ static bool act_store_execute
 
 	if ( mailbox_copy(&save_ctx, mail) < 0 ) {
 		sieve_act_store_get_storage_error(aenv, trans);
- 		result = FALSE;
+		status = ( trans->error_code == MAIL_ERROR_TEMP ?
+			SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE );
 	}
 
 	/* Deallocate keywords */
@@ -559,7 +580,7 @@ static bool act_store_execute
  		mailbox_keywords_unref(&keywords);
  	}
 
-	return result;
+	return status;
 }
 
 static void act_store_log_status
@@ -597,13 +618,11 @@ static void act_store_log_status
 		const char *errstr;
 		enum mail_error error_code;
 
-		if ( trans->error != NULL ) {
-			errstr = trans->error;
-			error_code = trans->error_code;
-		} else {
-			errstr = mail_storage_get_last_error
-				(mailbox_get_storage(trans->box), &error_code);
-		}
+		if ( trans->error == NULL )
+			sieve_act_store_get_storage_error(aenv, trans);
+
+		errstr = trans->error;
+		error_code = trans->error_code;
 
 		if ( error_code == MAIL_ERROR_NOTFOUND ||
 			error_code == MAIL_ERROR_PARAMS ) {
@@ -629,7 +648,7 @@ static void act_store_log_status
 	}
 }
 
-static bool act_store_commit
+static int act_store_commit
 (const struct sieve_action *action ATTR_UNUSED,
 	const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep)
 {
@@ -638,7 +657,7 @@ static bool act_store_commit
 	bool status = TRUE;
 
 	/* Verify transaction */
-	if ( trans == NULL ) return FALSE;
+	if ( trans == NULL ) return SIEVE_EXEC_FAILURE;
 
 	/* Check whether we need to do anything */
 	if ( trans->disabled ) {
@@ -646,14 +665,14 @@ static bool act_store_commit
 		*keep = FALSE;
 		if ( trans->box != NULL )
 			mailbox_free(&trans->box);
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	} else if ( trans->redundant ) {
 		act_store_log_status(trans, aenv, FALSE, status);
 		aenv->exec_status->keep_original = TRUE;
 		aenv->exec_status->message_saved = TRUE;
 		if ( trans->box != NULL )
 			mailbox_free(&trans->box);
-		return TRUE;
+		return SIEVE_EXEC_OK;
 	}
 
 	/* Mark attempt to use storage. Can only get here when all previous actions
@@ -682,7 +701,11 @@ static bool act_store_commit
 	if ( trans->box != NULL )
 		mailbox_free(&trans->box);
 
-	return status;
+	if (status)
+		return SIEVE_EXEC_OK;
+
+	return ( trans->error_code == MAIL_ERROR_TEMP ?
+			SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE );
 }
 
 static void act_store_rollback
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index f932228f9..3b76274c4 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -69,13 +69,13 @@ struct sieve_action_def {
 
 	/* Result execution */
 
-	bool (*start)
+	int (*start)
 		(const struct sieve_action *action,
 			const struct sieve_action_exec_env *aenv, void **tr_context);
-	bool (*execute)
+	int (*execute)
 		(const struct sieve_action *action,
 			const struct sieve_action_exec_env *aenv, void *tr_context);
-	bool (*commit)
+	int (*commit)
 		(const struct sieve_action *action,
 			const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
 	void (*rollback)
@@ -138,11 +138,11 @@ struct sieve_side_effect_def {
 
 	/* Result execution */
 
-	bool (*pre_execute)
+	int (*pre_execute)
 		(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 			const struct sieve_action_exec_env *aenv, void **context,
 			void *tr_context);
-	bool (*post_execute)
+	int (*post_execute)
 		(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 			const struct sieve_action_exec_env *aenv, void *tr_context);
 	void (*post_commit)
diff --git a/src/lib-sieve/sieve-binary-file.c b/src/lib-sieve/sieve-binary-file.c
index 0f68b37eb..555c776e5 100644
--- a/src/lib-sieve/sieve-binary-file.c
+++ b/src/lib-sieve/sieve-binary-file.c
@@ -301,13 +301,13 @@ int sieve_binary_save
 				"binary save: failed to create temporary file: %s",
 				eacces_error_get_creating("open", str_c(temp_path)));
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NO_PERM;
+				*error_r = SIEVE_ERROR_NO_PERMISSION;
 		} else {
 			sieve_sys_error(sbin->svinst,
 				"binary save: failed to create temporary file: open(%s) failed: %m",
 				str_c(temp_path));
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		}
 		return -1;
 	}
@@ -318,7 +318,7 @@ int sieve_binary_save
 	if ( !_sieve_binary_save(sbin, stream) ) {
 		result = -1;
 		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
 	}
 	o_stream_destroy(&stream);
 
@@ -335,12 +335,12 @@ int sieve_binary_save
 			sieve_sys_error(sbin->svinst, "binary save: failed to save binary: %s",
 				eacces_error_get_creating("rename", path));
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NO_PERM;
+				*error_r = SIEVE_ERROR_NO_PERMISSION;
 		} else {
 			sieve_sys_error(sbin->svinst, "binary save: failed to save binary: "
 				"rename(%s, %s) failed: %m", str_c(temp_path), path);
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		}
 		result = -1;
 	}
@@ -386,13 +386,13 @@ bool sieve_binary_file_open
 			sieve_sys_error(svinst, "binary open: failed to open: %s",
 				eacces_error_get("open", path));
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NO_PERM;
+				*error_r = SIEVE_ERROR_NO_PERMISSION;
 			break;
 		default:
 			sieve_sys_error(svinst, "binary open: failed to open: "
 				"open(%s) failed: %m", path);
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 			break;
 		}
 		return FALSE;
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index a67dc4e77..0198f370d 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -93,6 +93,8 @@ struct sieve_result {
 
 	HASH_TABLE(const struct sieve_action_def *,
 			   struct sieve_result_action_context *) action_contexts;
+
+	unsigned int executed:1;
 };
 
 struct sieve_result *sieve_result_create
@@ -925,11 +927,11 @@ static void _sieve_result_prepare_execution(struct sieve_result *result)
 	}
 }
 
-static bool _sieve_result_implicit_keep
+static int _sieve_result_implicit_keep
 (struct sieve_result *result, bool rollback)
 {
 	struct sieve_result_action *rac;
-	bool success = TRUE;
+	int status = SIEVE_EXEC_OK;
 	struct sieve_result_side_effect *rsef, *rsef_first = NULL;
 	void *tr_context = NULL;
 	struct sieve_action act_keep;
@@ -943,7 +945,7 @@ static bool _sieve_result_implicit_keep
 	}
 
 	/* If keep is a non-action, return right away */
-	if ( act_keep.def == NULL ) return TRUE;
+	if ( act_keep.def == NULL ) return SIEVE_EXEC_OK;
 
 	/* Scan for execution of keep-equal actions */
 	rac = result->first_action;
@@ -952,7 +954,7 @@ static bool _sieve_result_implicit_keep
 			act_keep.def->equals
 				(result->action_env.scriptenv, NULL, &rac->action) &&
 					rac->action.executed )
-			return TRUE;
+			return SIEVE_EXEC_OK;
 
 		rac = rac->next;
 	}
@@ -970,42 +972,42 @@ static bool _sieve_result_implicit_keep
 
 	/* Start keep action */
 	if ( act_keep.def->start != NULL )
-		success = act_keep.def->start
+		status = act_keep.def->start
 			(&act_keep, &result->action_env,  &tr_context);
 
 	/* Execute keep action */
-	if ( success ) {
+	if ( status == SIEVE_EXEC_OK ) {
 		rsef = rsef_first;
-		while ( success && rsef != NULL ) {
+		while ( status == SIEVE_EXEC_OK && rsef != NULL ) {
 			struct sieve_side_effect *sef = &rsef->seffect;
 
 			if ( sef->def->pre_execute != NULL )
-				success = success && sef->def->pre_execute
+				status = sef->def->pre_execute
 					(sef, &act_keep, &result->action_env, &sef->context, tr_context);
 			rsef = rsef->next;
 		}
 
-		if ( act_keep.def->execute != NULL )
-			success = success && act_keep.def->execute
+		if ( status == SIEVE_EXEC_OK && act_keep.def->execute != NULL )
+			status = act_keep.def->execute
 				(&act_keep, &result->action_env, tr_context);
 
 		rsef = rsef_first;
-		while ( success && rsef != NULL ) {
+		while ( status == SIEVE_EXEC_OK && rsef != NULL ) {
 			struct sieve_side_effect *sef = &rsef->seffect;
 
 			if ( sef->def->post_execute != NULL )
-				success = success && sef->def->post_execute
+				status = sef->def->post_execute
 					(sef, &act_keep, &result->action_env, tr_context);
 			rsef = rsef->next;
 		}
 	}
 
 	/* Finish keep action */
-	if ( success ) {
+	if ( status == SIEVE_EXEC_OK ) {
 		bool dummy = TRUE;
 
 		if ( act_keep.def->commit != NULL )
-			success = act_keep.def->commit
+			status = act_keep.def->commit
 				(&act_keep, &result->action_env, tr_context, &dummy);
 
 		rsef = rsef_first;
@@ -1019,18 +1021,18 @@ static bool _sieve_result_implicit_keep
 			rsef = rsef->next;
 		}
 
-		return success;
+		return status;
 	}
 
 	/* Failed, rollback */
 	if ( act_keep.def->rollback != NULL )
-		act_keep.def->rollback
-			(&act_keep, &result->action_env, tr_context, success);
+		act_keep.def->rollback(&act_keep,
+			&result->action_env, tr_context, ( status == SIEVE_EXEC_OK ));
 
-	return FALSE;
+	return status;
 }
 
-bool sieve_result_implicit_keep
+int sieve_result_implicit_keep
 (struct sieve_result *result)
 {
 	_sieve_result_prepare_execution(result);
@@ -1055,13 +1057,20 @@ void sieve_result_mark_executed(struct sieve_result *result)
 	}
 }
 
+
+bool sieve_result_executed(struct sieve_result *result)
+{
+	return result->executed;
+}
+
 int sieve_result_execute
 (struct sieve_result *result, bool *keep)
 {
-	bool implicit_keep = TRUE;
-	bool success = TRUE, commit_ok;
+	bool implicit_keep = TRUE, executed = result->executed;
+	int status = SIEVE_EXEC_OK, commit_status;
 	struct sieve_result_action *rac, *first_action;
 	struct sieve_result_action *last_attempted;
+	int ret;
 
 	if ( keep != NULL ) *keep = FALSE;
 
@@ -1080,7 +1089,7 @@ int sieve_result_execute
 	 */
 
 	rac = first_action;
-	while ( success && rac != NULL ) {
+	while ( status == SIEVE_EXEC_OK && rac != NULL ) {
 		struct sieve_action *act = &rac->action;
 
 		/* Skip non-actions (inactive keep) and executed ones */
@@ -1090,9 +1099,9 @@ int sieve_result_execute
 		}
 
 		if ( act->def->start != NULL ) {
-			rac->success = act->def->start
+			status = act->def->start
 				(act, &result->action_env, &rac->tr_context);
-			success = success && rac->success;
+			rac->success = ( status == SIEVE_EXEC_OK );
 		}
 
 		rac = rac->next;
@@ -1104,7 +1113,7 @@ int sieve_result_execute
 
 	last_attempted = rac;
 	rac = first_action;
-	while ( success && rac != NULL ) {
+	while ( status == SIEVE_EXEC_OK && rac != NULL ) {
 		struct sieve_action *act = &rac->action;
 		struct sieve_result_side_effect *rsef;
 		struct sieve_side_effect *sef;
@@ -1117,27 +1126,28 @@ int sieve_result_execute
 
 		/* Execute pre-execute event of side effects */
 		rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
-		while ( success && rsef != NULL ) {
+		while ( status == SIEVE_EXEC_OK && rsef != NULL ) {
 			sef = &rsef->seffect;
 			if ( sef->def != NULL && sef->def->pre_execute != NULL )
-				success = success & sef->def->pre_execute
+				status = sef->def->pre_execute
 					(sef, act, &result->action_env, &sef->context, rac->tr_context);
 			rsef = rsef->next;
 		}
 
 		/* Execute the action itself */
-		if ( success && act->def != NULL && act->def->execute != NULL ) {
-			rac->success = act->def->execute
+		if ( status == SIEVE_EXEC_OK && act->def != NULL &&
+			act->def->execute != NULL ) {
+			status = act->def->execute
 				(act, &result->action_env, rac->tr_context);
-			success = success && rac->success;
+			rac->success = ( status == SIEVE_EXEC_OK );
 		}
 
 		/* Execute post-execute event of side effects */
 		rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
-		while ( success && rsef != NULL ) {
+		while ( status == SIEVE_EXEC_OK && rsef != NULL ) {
 			sef = &rsef->seffect;
 			if ( sef->def != NULL && sef->def->post_execute != NULL )
-				success = success && sef->def->post_execute
+				status = sef->def->post_execute
 					(sef, act, &result->action_env, rac->tr_context);
 			rsef = rsef->next;
 		}
@@ -1149,15 +1159,16 @@ int sieve_result_execute
 	 * Transaction commit/rollback
 	 */
 
-	commit_ok = success;
+	commit_status = status;
 	rac = first_action;
 	while ( rac != NULL && rac != last_attempted ) {
 		struct sieve_action *act = &rac->action;
 		struct sieve_result_side_effect *rsef;
 		struct sieve_side_effect *sef;
 
-		if ( success ) {
+		if ( status == SIEVE_EXEC_OK ) {
 			bool impl_keep = TRUE;
+			int cstatus = SIEVE_EXEC_OK;
 
 			if ( rac->keep && keep != NULL ) *keep = TRUE;
 
@@ -1168,19 +1179,35 @@ int sieve_result_execute
 			}
 
 			if ( act->def->commit != NULL ) {
-				act->executed = act->def->commit
+				cstatus = act->def->commit
 					(act, &result->action_env, rac->tr_context, &impl_keep);
-				commit_ok = act->executed && commit_ok;
+				if ( cstatus == SIEVE_EXEC_OK ) {
+					act->executed = TRUE;
+					result->executed = TRUE;
+					executed = TRUE;
+				} else {
+					/* This is bad; try to salvage as much as possible */
+					if (commit_status == SIEVE_EXEC_OK) {
+						commit_status = cstatus;
+						if (!executed) {
+							/* We haven't executed anything yet; continue as rollback */
+							status = cstatus;
+						}
+					}
+					impl_keep = TRUE;
+				}
 			}
 
-			/* Execute post_commit event of side effects */
-			rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
-			while ( rsef != NULL ) {
-				sef = &rsef->seffect;
-				if ( sef->def->post_commit != NULL )
-					sef->def->post_commit
-						(sef, act, &result->action_env, rac->tr_context, &impl_keep);
-				rsef = rsef->next;
+			if ( cstatus == SIEVE_EXEC_OK ) {
+				/* Execute post_commit event of side effects */
+				rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
+				while ( rsef != NULL ) {
+					sef = &rsef->seffect;
+					if ( sef->def->post_commit != NULL )
+						sef->def->post_commit
+							(sef, act, &result->action_env, rac->tr_context, &impl_keep);
+					rsef = rsef->next;
+				}
 			}
 
 			implicit_keep = implicit_keep && impl_keep;
@@ -1191,9 +1218,10 @@ int sieve_result_execute
 				continue;
 			}
 
-			if ( act->def->rollback != NULL )
+			if ( act->def->rollback != NULL ) {
 				act->def->rollback
 					(act, &result->action_env, rac->tr_context, rac->success);
+			}
 
 			/* Rollback side effects */
 			rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
@@ -1217,19 +1245,28 @@ int sieve_result_execute
 	 * though.
 	 */
 
+	if ( !executed && commit_status == SIEVE_EXEC_TEMP_FAILURE )
+		return commit_status;
+
 	/* Execute implicit keep if the transaction failed or when the implicit keep
 	 * was not canceled during transaction.
 	 */
-	if ( !commit_ok || implicit_keep ) {
-		if ( !_sieve_result_implicit_keep(result, !commit_ok) )
+	if ( commit_status != SIEVE_EXEC_OK || implicit_keep ) {
+		switch ((ret=_sieve_result_implicit_keep
+			(result, ( commit_status != SIEVE_EXEC_OK ))) ) {
+		case SIEVE_EXEC_OK:
+			break;
+		case SIEVE_EXEC_TEMP_FAILURE:
+			if (!executed)
+				return ret;
+		default:
 			return SIEVE_EXEC_KEEP_FAILED;
+		}
 
-		return ( commit_ok ?
-			SIEVE_EXEC_OK            /* Success */ :
-			SIEVE_EXEC_FAILURE       /* Implicit keep executed */ );
+		return commit_status;
 	}
 
-	/* Unconditional success */
+	/* success */
 	return SIEVE_EXEC_OK;
 }
 
diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h
index 804bdc6a3..f52f132cf 100644
--- a/src/lib-sieve/sieve-result.h
+++ b/src/lib-sieve/sieve-result.h
@@ -141,12 +141,14 @@ void sieve_result_set_failure_action
  * Result execution
  */
 
-bool sieve_result_implicit_keep(struct sieve_result *result);
+int sieve_result_implicit_keep(struct sieve_result *result);
 
 void sieve_result_mark_executed(struct sieve_result *result);
 
 int sieve_result_execute(struct sieve_result *result, bool *keep);
 
+bool sieve_result_executed(struct sieve_result *result);
+
 /*
  * Result evaluation
  */
diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c
index 626052819..14358662d 100644
--- a/src/lib-sieve/sieve-script-dict.c
+++ b/src/lib-sieve/sieve-script-dict.c
@@ -80,7 +80,7 @@ static int sieve_dict_script_open
 			} else {
 				sieve_critical(svinst, ehandler, NULL, "failed to open sieve script",
 					"sieve dict backend: invalid option `%s'", option);
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 				return -1;
 			}
 
@@ -96,7 +96,7 @@ static int sieve_dict_script_open
 		if ( svinst->username == NULL ) {
 			sieve_critical(svinst, ehandler, name, "failed to open sieve script",
 				"sieve dict backend: no username specified");
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
 			return -1;
 		}
 		username = svinst->username;
@@ -106,7 +106,7 @@ static int sieve_dict_script_open
 		sieve_critical(svinst, ehandler, name, "failed to open sieve script",
 			"sieve dict backend: BUG: Sieve interpreter is initialized without "
 			"a base_dir");
-		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		return -1;
 	}
 
@@ -122,7 +122,7 @@ static int sieve_dict_script_open
 		sieve_critical(svinst, ehandler, name, "failed to open sieve script",
 			"sieve dict backend: failed to initialize dict with data `%s' "
 			"for user `%s': %s", data, username, error);
-		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		return -1;
 	}
 
@@ -135,7 +135,7 @@ static int sieve_dict_script_open
 		if ( ret < 0 ) {
 			sieve_critical(svinst, ehandler, name, "failed to open sieve script",
 				"sieve dict backend: failed to lookup script id from path %s", path);
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		} else {
 			if ( svinst->debug ) {
 				sieve_sys_debug(svinst, "sieve dict backend: "
@@ -193,7 +193,7 @@ static int sieve_dict_script_get_stream
 				"sieve dict backend: data with id `%s' for script `%s' "
 				"not found at path %s",	script->data_id, name, path);
 		}
-		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		return -1;
 	}
 
diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c
index 18df9fb39..eee6f5082 100644
--- a/src/lib-sieve/sieve-script-file.c
+++ b/src/lib-sieve/sieve-script-file.c
@@ -72,12 +72,12 @@ static void sieve_file_script_handle_error
 	case EACCES:
 		sieve_critical(svinst, ehandler, name, "failed to open sieve script",
 			"failed to stat sieve script: %s", eacces_error_get("stat", path));
-		*error_r = SIEVE_ERROR_NO_PERM;
+		*error_r = SIEVE_ERROR_NO_PERMISSION;
 		break;
 	default:
 		sieve_critical(svinst, ehandler, name, "failed to open sieve script",
 			"failed to stat sieve script: stat(%s) failed: %m", path);
-		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		break;
 	}
 }
@@ -133,7 +133,7 @@ static int sieve_file_script_open
 
 		sieve_critical(svinst, ehandler, NULL, "failed to open sieve script",
 			"sieve file backend: invalid option `%s'", option);
-		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		return -1;
 	}
 
@@ -153,7 +153,7 @@ static int sieve_file_script_open
 					"failed to open sieve script",
 					"sieve script file path %s is relative to home directory, "
 					"but home directory is not available.", path);
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 				success = FALSE;
 			}
 		}
@@ -165,7 +165,7 @@ static int sieve_file_script_open
 					sieve_critical(svinst, ehandler, NULL,
 						"failed to open sieve script",
 						"sieve script file path '%s' is a directory.", path);
-					*error_r = SIEVE_ERROR_TEMP_FAIL;
+					*error_r = SIEVE_ERROR_TEMP_FAILURE;
 					success = FALSE;
 				}	else {
 					/* Extend path with filename */
@@ -211,7 +211,7 @@ static int sieve_file_script_open
 				sieve_critical(svinst, ehandler, name,
 					"failed to open sieve script",
 					"sieve script file '%s' is not a regular file.", path);
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 				success = FALSE;
 			}
 		}
@@ -269,7 +269,7 @@ static int sieve_file_script_get_stream
 		sieve_critical(svinst, ehandler, name,
 			"failed to open sieve script",
 			"failed to open sieve script: fstat(fd=%s) failed: %m", script->path);
-		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		result = NULL;
 	} else {
 		/* Re-check the file type just to be sure */
@@ -277,7 +277,7 @@ static int sieve_file_script_get_stream
 			sieve_critical(svinst, ehandler, name,
 				"failed to open sieve script",
 				"sieve script file '%s' is not a regular file", script->path);
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
 			result = NULL;
 		} else {
 			result = i_stream_create_fd(fd, SIEVE_FILE_READ_BLOCK_SIZE, TRUE);
diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c
index ced71c999..19869483b 100644
--- a/src/lib-sieve/sieve-script.c
+++ b/src/lib-sieve/sieve-script.c
@@ -234,7 +234,7 @@ struct sieve_script *sieve_script_create
 
 	if ( script_class == NULL ) {
 		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		return NULL;
 	}
 
@@ -265,7 +265,7 @@ int sieve_script_open
 			"failed to access sieve script", "failed to parse script location: %s",
 			parse_error);
 		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
 		return -1;
 	}
 
diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h
index 375a0f7ca..33f2f27a5 100644
--- a/src/lib-sieve/sieve-types.h
+++ b/src/lib-sieve/sieve-types.h
@@ -83,13 +83,13 @@ enum sieve_error {
 	SIEVE_ERROR_NONE = 0,
 
 	/* Temporary internal error */
-	SIEVE_ERROR_TEMP_FAIL,
+	SIEVE_ERROR_TEMP_FAILURE,
 	/* It's not possible to do the wanted operation */
 	SIEVE_ERROR_NOT_POSSIBLE,
 	/* Invalid parameters (eg. script name not valid) */
 	SIEVE_ERROR_BAD_PARAMS,
 	/* No permission to do the request */
-	SIEVE_ERROR_NO_PERM,
+	SIEVE_ERROR_NO_PERMISSION,
 	/* Out of disk space */
 	SIEVE_ERROR_NO_SPACE,
 	/* Out of disk space */
@@ -235,10 +235,11 @@ struct sieve_exec_status {
  */
 
 enum sieve_execution_exitcode {
-	SIEVE_EXEC_OK          = 1,
-	SIEVE_EXEC_FAILURE     = 0,
-	SIEVE_EXEC_BIN_CORRUPT = -1,
-	SIEVE_EXEC_KEEP_FAILED = -2
+	SIEVE_EXEC_OK           = 1,
+	SIEVE_EXEC_FAILURE      = 0,
+	SIEVE_EXEC_TEMP_FAILURE = -1,
+	SIEVE_EXEC_BIN_CORRUPT  = -2,
+	SIEVE_EXEC_KEEP_FAILED  = -3
 };
 
 #endif /* __SIEVE_TYPES_H */
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index c7bfd0ad9..f3ae31f96 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -385,7 +385,6 @@ struct sieve_binary *sieve_open_script
 		} else {
 			sbin = sieve_compile_script(script, ehandler, flags, error_r);
 
-			/* Save the binary if compile was successful */
 			if ( sbin != NULL ) {
 				if ( svinst->debug )
 					sieve_sys_debug(svinst, "script `%s' from %s successfully compiled",
@@ -535,12 +534,17 @@ int sieve_execute
 	if ( ret > 0 ) {
 		/* Execute result */
 		ret = sieve_result_execute(result, keep);
-	} else if ( ret == 0 ) {
+	} else if ( ret == SIEVE_EXEC_FAILURE ) {
 		/* Perform implicit keep if script failed with a normal runtime error */
-		if ( !sieve_result_implicit_keep(result) ) {
-			ret = SIEVE_EXEC_KEEP_FAILED;
-		} else {
+		switch ( sieve_result_implicit_keep(result) ) {
+		case SIEVE_EXEC_OK:
 			if ( keep != NULL ) *keep = TRUE;
+			break;
+		case SIEVE_EXEC_TEMP_FAILURE:
+			ret = SIEVE_EXEC_TEMP_FAILURE;
+			break;
+		default:
+			ret = SIEVE_EXEC_KEEP_FAILED;
 		}
 	}
 
@@ -674,6 +678,39 @@ int sieve_multiscript_status(struct sieve_multiscript *mscript)
 	return mscript->status;
 }
 
+int sieve_multiscript_tempfail(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(result, ehandler);
+
+	if ( (*mscript)->active ) {
+		ret = SIEVE_EXEC_TEMP_FAILURE;
+
+		if ( !(*mscript)->teststream && sieve_result_executed(result) ) {
+			/* Part of the result is already executed, need to fall back to
+			 * to implicit keep (FIXME)
+			 */
+			switch ( sieve_result_implicit_keep(result) ) {
+			case SIEVE_EXEC_OK:
+				ret = SIEVE_EXEC_FAILURE;
+				break;
+			default:
+				ret = SIEVE_EXEC_KEEP_FAILED;
+			}
+		}
+	}
+
+	/* Cleanup */
+	sieve_result_unref(&result);
+	*mscript = NULL;
+
+	return ret;
+}
+
 int sieve_multiscript_finish(struct sieve_multiscript **mscript,
 	struct sieve_error_handler *ehandler, bool *keep)
 {
@@ -681,7 +718,7 @@ int sieve_multiscript_finish(struct sieve_multiscript **mscript,
 	int ret = (*mscript)->status;
 
 	if ( ehandler != NULL )
-		sieve_result_set_error_handler((*mscript)->result, ehandler);
+		sieve_result_set_error_handler(result, ehandler);
 
 	if ( (*mscript)->active ) {
 		ret = SIEVE_EXEC_FAILURE;
@@ -689,10 +726,19 @@ int sieve_multiscript_finish(struct sieve_multiscript **mscript,
 		if ( (*mscript)->teststream ) {
 			(*mscript)->keep = TRUE;
 		} else {
-			if ( !sieve_result_implicit_keep((*mscript)->result) )
-				ret = SIEVE_EXEC_KEEP_FAILED;
-			else
+			switch ( sieve_result_implicit_keep(result) ) {
+			case SIEVE_EXEC_OK:
 				(*mscript)->keep = TRUE;
+				break;
+			case SIEVE_EXEC_TEMP_FAILURE:
+				if (!sieve_result_executed(result)) {
+					ret = SIEVE_EXEC_TEMP_FAILURE;
+					break;
+				}
+				/* fall through */
+			default:
+				ret = SIEVE_EXEC_KEEP_FAILED;
+			}
 		}
 	}
 
@@ -760,7 +806,7 @@ struct sieve_directory *sieve_directory_open
 				"sieve dir path %s is relative to home directory, "
 				"but home directory is not available.", path);
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 			return NULL;
 		}
 	}
@@ -776,13 +822,13 @@ struct sieve_directory *sieve_directory_open
 			sieve_sys_error(svinst, "failed to open sieve dir: %s",
 				eacces_error_get("stat", path));
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NO_PERM;
+				*error_r = SIEVE_ERROR_NO_PERMISSION;
 			break;
 		default:
 			sieve_sys_error(svinst, "failed to open sieve dir: stat(%s) failed: %m",
 				path);
 			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 			break;
 		}
 		return NULL;
@@ -801,13 +847,13 @@ struct sieve_directory *sieve_directory_open
 				sieve_sys_error(svinst, "failed to open sieve dir: %s",
 					eacces_error_get("opendir", path));
 				if ( error_r != NULL )
-					*error_r = SIEVE_ERROR_NO_PERM;
+					*error_r = SIEVE_ERROR_NO_PERMISSION;
 				break;
 			default:
 				sieve_sys_error(svinst, "failed to open sieve dir: opendir(%s) failed: "
 					"%m", path);
 				if ( error_r != NULL )
-					*error_r = SIEVE_ERROR_TEMP_FAIL;
+					*error_r = SIEVE_ERROR_TEMP_FAILURE;
 				break;
 			}
 			return NULL;
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index b756c1dc5..b2b260168 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -191,6 +191,8 @@ bool sieve_multiscript_run
 
 int sieve_multiscript_status(struct sieve_multiscript *mscript);
 
+int sieve_multiscript_tempfail(struct sieve_multiscript **mscript,
+	struct sieve_error_handler *ehandler);
 int sieve_multiscript_finish
 	(struct sieve_multiscript **mscript, struct sieve_error_handler *ehandler,
 		bool *keep);
diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c
index eb6a4a43d..fb1d65c8e 100644
--- a/src/lib-sievestorage/sieve-storage.c
+++ b/src/lib-sievestorage/sieve-storage.c
@@ -596,7 +596,7 @@ static void ATTR_FORMAT(4, 0) sieve_storage_verror
 	if (fmt != NULL) {
 		storage->error = i_strdup_vprintf(fmt, args);
 	}
-	storage->error_code = SIEVE_ERROR_TEMP_FAIL;
+	storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
 }
 
 void sieve_storage_clear_error(struct sieve_storage *storage)
@@ -631,7 +631,7 @@ void sieve_storage_set_critical
 	sieve_storage_clear_error(storage);
 	if (fmt != NULL) {
 		i_free(storage->error);
-		storage->error_code = SIEVE_ERROR_TEMP_FAIL;
+		storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
 
 		if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) {
 			struct tm *tm;
diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c
index de97723c6..2e9b96102 100644
--- a/src/managesieve/managesieve-client.c
+++ b/src/managesieve/managesieve-client.c
@@ -382,7 +382,7 @@ void client_send_storage_error
 	error = sieve_storage_get_last_error(storage, &error_code);
 
 	switch ( error_code ) {
-	case SIEVE_ERROR_TEMP_FAIL:
+	case SIEVE_ERROR_TEMP_FAILURE:
 		client_send_noresp(client, "TRYLATER", error);
 		break;
 
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index 77a185082..bf1ea939b 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -128,6 +128,7 @@ struct lda_sieve_run_context {
 	struct sieve_instance *svinst;
 
 	struct mail_deliver_context *mdctx;
+	const char *home_dir;
 
 	struct sieve_script **scripts;
 	unsigned int script_count;
@@ -183,18 +184,18 @@ static const char *lda_sieve_get_default_location
 
 static int lda_sieve_multiscript_get_scripts
 (struct sieve_instance *svinst, const char *label, const char *location,
-	struct sieve_error_handler *ehandler, ARRAY_TYPE(sieve_scripts) *scripts)
+	struct sieve_error_handler *ehandler, ARRAY_TYPE(sieve_scripts) *scripts,
+	enum sieve_error *error_r)
 {
 	struct sieve_directory *sdir;
-	enum sieve_error error;
 	ARRAY_TYPE(const_string) script_files;
 	const char *const *files;
 	unsigned int count, i;
 	const char *file;
 
 	// FIXME: make this a generic iteration API
-	if ( (sdir=sieve_directory_open(svinst, location, &error)) == NULL )
-		return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
+	if ( (sdir=sieve_directory_open(svinst, location, error_r)) == NULL )
+		return ( *error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 
 	t_array_init(&script_files, 16);
 
@@ -218,19 +219,23 @@ static int lda_sieve_multiscript_get_scripts
 	files = array_get(&script_files, &count);
 	for ( i = 0; i < count; i++ ) {
 		struct sieve_script *script = sieve_script_create_open
-			(svinst, files[i], NULL, ehandler, &error);
+			(svinst, files[i], NULL, ehandler, error_r);
 
 		if ( script == NULL ) {
-			switch ( error ) {
+			switch ( *error_r ) {
 			case SIEVE_ERROR_NOT_FOUND:
 				/* Shouldn't normally happen, but the script could have disappeared */
-				sieve_sys_warning
-					(svinst, "%s script %s doesn't exist", label, files[i]);
+				sieve_sys_warning(svinst,
+					"%s script %s doesn't exist", label, files[i]);
 				break;
-
+			case SIEVE_ERROR_TEMP_FAILURE:
+				sieve_sys_error(svinst,
+					"failed to access %s script %s (temporary failure)",
+					label, files[i]);
+				return -1;
 			default:
-				sieve_sys_error
-					(svinst, "failed to access %s script %s", label, files[i]);
+				sieve_sys_error(svinst,
+					"failed to access %s script %s", label, files[i]);
 				break;
 			}
 
@@ -240,6 +245,7 @@ static int lda_sieve_multiscript_get_scripts
 		array_append(scripts, &script, 1);
 	}
 
+	*error_r = SIEVE_ERROR_NONE;
 	return 1;
 }
 
@@ -251,7 +257,7 @@ static void lda_sieve_binary_save
 
 	/* Save binary when compiled */
 	if ( sieve_save(sbin, FALSE, &error) < 0 &&
-		error == SIEVE_ERROR_NO_PERM && script != srctx->user_script ) {
+		error == SIEVE_ERROR_NO_PERMISSION && script != srctx->user_script ) {
 
 		/* Cannot save binary for global script */
 		sieve_sys_error(srctx->svinst,
@@ -264,105 +270,76 @@ static void lda_sieve_binary_save
 
 static struct sieve_binary *lda_sieve_open
 (struct lda_sieve_run_context *srctx, struct sieve_script *script,
-	enum sieve_compile_flags cpflags, enum sieve_error *error_r)
+	enum sieve_compile_flags cpflags, bool recompile, enum sieve_error *error_r)
 {
 	struct sieve_instance *svinst = srctx->svinst;
 	struct sieve_error_handler *ehandler;
 	struct sieve_binary *sbin;
 	bool debug = srctx->mdctx->dest_user->mail_debug;
+	const char *compile_name = "compile";
+
+	if ( recompile ) {
+		/* Warn */
+		sieve_sys_warning(svinst,
+			"encountered corrupt binary: re-compiling script %s",
+			sieve_script_location(script));
+		compile_name = "re-compile";
+	} else 	if ( debug ) {
+		sieve_sys_debug(svinst,
+			"loading script %s", sieve_script_location(script));
+	}
 
 	if ( script == srctx->user_script )
 		ehandler = srctx->user_ehandler;
 	else
 		ehandler = srctx->master_ehandler;
 
-	if ( debug )
-		sieve_sys_debug(svinst, "loading script %s", sieve_script_location(script));
-
 	sieve_error_handler_reset(ehandler);
 
+	if ( recompile )
+		sbin = sieve_compile_script(script, ehandler,	cpflags, error_r);
+	else 
+		sbin = sieve_open_script(script, ehandler, cpflags, error_r);
+
 	/* Load or compile the sieve script */
-	if ( (sbin=sieve_open_script(script, ehandler, cpflags, error_r)) == NULL ) {
+	if ( sbin == NULL ) {
 		switch ( *error_r ) {
 		/* Script not found */
 		case SIEVE_ERROR_NOT_FOUND:
 			if ( debug ) {
-				sieve_sys_debug(svinst, "script file %s is missing",
-					sieve_script_location(script));
+				sieve_sys_debug(svinst, "script file %s is missing for %s",
+					sieve_script_location(script), compile_name);
 			}
 			break;
+		/* Temporary failure */
+		case SIEVE_ERROR_TEMP_FAILURE:
+			sieve_sys_error(svinst,
+				"failed to open script %s for %s (temporary failure)",
+				sieve_script_location(script), compile_name);
+			break;
 		/* Compile failed */
 		case SIEVE_ERROR_NOT_VALID:
 			if (script == srctx->user_script && srctx->userlog != NULL ) {
-				sieve_sys_info(svinst, "failed to compile script %s "
+				sieve_sys_info(svinst, "failed to %s script %s "
 					"(view user logfile %s for more information)",
-					sieve_script_location(script), srctx->userlog);
+					compile_name, sieve_script_location(script), srctx->userlog);
 				break;
 			}
-			/* Fall through */
+			sieve_sys_error(svinst,	"failed to %s script %s",
+				compile_name, sieve_script_location(script));
+			break;
 		/* Something else */
 		default:
-			sieve_sys_error(svinst,	"failed to open script %s",
-				sieve_script_location(script));
-			break;
-		}
-
-		return NULL;
-	}
-
-	lda_sieve_binary_save(srctx, sbin, script);
-	return sbin;
-}
-
-static struct sieve_binary *lda_sieve_recompile
-(struct lda_sieve_run_context *srctx, struct sieve_script *script,
-	enum sieve_compile_flags cpflags, enum sieve_error *error_r)
-{
-	struct sieve_instance *svinst = srctx->svinst;
-	struct sieve_error_handler *ehandler;
-	struct sieve_binary *sbin;
-	bool debug = srctx->mdctx->dest_user->mail_debug;
-
-	/* Warn */
-
-	sieve_sys_warning(svinst,
-		"encountered corrupt binary: re-compiling script %s",
-		sieve_script_location(script));
-
-	/* Recompile */
-
-	if ( script == srctx->user_script )
-		ehandler = srctx->user_ehandler;
-	else
-		ehandler = srctx->master_ehandler;
-
-	if ( (sbin=sieve_compile_script(script, ehandler,	cpflags, error_r))
-		== NULL ) {
-
-		switch ( *error_r ) {
-		case SIEVE_ERROR_NOT_FOUND:
-			if ( debug ) {
-				sieve_sys_debug(svinst, "script file %s is missing for re-compile",
-					sieve_script_location(script));
-			}
+			sieve_sys_error(svinst,	"failed to open script %s for %s",
+				sieve_script_location(script), compile_name);
 			break;
-		case SIEVE_ERROR_NOT_VALID:
-			if ( script == srctx->user_script && srctx->userlog != NULL ) {
-				sieve_sys_info(svinst,
-					"failed to re-compile script %s "
-					"(view user logfile %s for more information)",
-					sieve_script_location(script), srctx->userlog);
-				break;
-			}
-			/* Fall through */
-		default:
-			sieve_sys_error(svinst,	"failed to open script %s for re-compile",
-				sieve_script_location(script));
 		}
 
 		return NULL;
 	}
 
+	if (!recompile)
+		lda_sieve_binary_save(srctx, sbin, script);
 	return sbin;
 }
 
@@ -370,16 +347,16 @@ static int lda_sieve_handle_exec_status
 (struct lda_sieve_run_context *srctx, struct sieve_script *script, int status)
 {
 	struct sieve_instance *svinst = srctx->svinst;
+	struct mail_deliver_context *mdctx = srctx->mdctx;
 	struct sieve_exec_status *estatus = srctx->scriptenv->exec_status;
 	const char *userlog_notice = "";
 	sieve_sys_error_func_t error_func, user_error_func; 
+	enum mail_error mail_error = MAIL_ERROR_NONE;
 	int ret;
 
 	error_func = user_error_func = sieve_sys_error;
 
 	if ( estatus != NULL && estatus->last_storage != NULL ) {
-		enum mail_error mail_error;
-
 		mail_storage_get_last_error(estatus->last_storage, &mail_error);
 
 		/* Don't bother administrator too much with benign errors */
@@ -391,7 +368,7 @@ static int lda_sieve_handle_exec_status
 
 	if ( script == srctx->user_script && srctx->userlog != NULL ) {
 		userlog_notice = t_strdup_printf
-			(" (user logfile %s should reveal additional details)", srctx->userlog);
+			(" (user logfile %s may reveal additional details)", srctx->userlog);
 		user_error_func = sieve_sys_info;
 	}
 
@@ -402,6 +379,16 @@ static int lda_sieve_handle_exec_status
 			sieve_script_location(script), userlog_notice);
 		ret = 1;
 		break;
+	case SIEVE_EXEC_TEMP_FAILURE:
+		error_func(svinst,
+			"execution of script %s was aborted due to temporary failure%s",
+			sieve_script_location(script), userlog_notice);
+		if ( mail_error != MAIL_ERROR_TEMP && mdctx->tempfail_error == NULL ) {
+			mdctx->tempfail_error =
+				"Execution of Sieve filters was aborted due to temporary failure";
+		}
+		ret = -1;
+		break;
 	case SIEVE_EXEC_BIN_CORRUPT:
 		sieve_sys_error(svinst,
 			"!!BUG!!: binary compiled from %s is still corrupt; "
@@ -411,7 +398,7 @@ static int lda_sieve_handle_exec_status
 		break;
 	case SIEVE_EXEC_KEEP_FAILED:
 		error_func(svinst,
-			"script %s failed with unsuccessful implicit keep%s",
+			"execution of script %s failed with unsuccessful implicit keep%s",
 			sieve_script_location(script), userlog_notice);
 		ret = -1;
 		break;
@@ -447,8 +434,18 @@ static int lda_sieve_singlescript_execute
 
 	/* Open the script */
 
-	if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL )
-		return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
+	sbin = lda_sieve_open(srctx, script, cpflags, FALSE, &error);
+	if ( sbin == NULL ) {
+		switch ( error ) {
+		case SIEVE_ERROR_NOT_FOUND:
+			return 0;
+		case SIEVE_ERROR_TEMP_FAILURE:
+			return lda_sieve_handle_exec_status
+				(srctx, script, SIEVE_EXEC_TEMP_FAILURE);
+		default:
+			return -1;
+		}
+	}
 
 	/* Execute */
 
@@ -467,8 +464,18 @@ static int lda_sieve_singlescript_execute
 
 		/* Recompile */
 
-		if ( (sbin=lda_sieve_recompile(srctx, script, cpflags, &error)) == NULL )
-			return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
+		sbin = lda_sieve_open(srctx, script, cpflags, TRUE, &error);
+		if ( sbin == NULL ) {
+			switch ( error ) {
+			case SIEVE_ERROR_NOT_FOUND:
+				return 0;
+			case SIEVE_ERROR_TEMP_FAILURE:
+				return lda_sieve_handle_exec_status
+					(srctx, script, SIEVE_EXEC_TEMP_FAILURE);
+			default:
+				return -1;
+			}
+		}
 
 		/* Execute again */
 
@@ -502,9 +509,9 @@ static int lda_sieve_multiscript_execute
 	bool debug = srctx->mdctx->dest_user->mail_debug;
 	struct sieve_script *last_script = NULL;
 	bool user_script = FALSE, more = TRUE, compile_error = FALSE;
-	unsigned int i;
-	int ret = 1;
 	enum sieve_error error;
+	unsigned int i;
+	int ret;
 
 	/* Start execution */
 
@@ -539,7 +546,8 @@ static int lda_sieve_multiscript_execute
 					sieve_script_location(script));
 		}
 
-		if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) {
+		sbin = lda_sieve_open(srctx, script, cpflags, FALSE, &error);
+		if ( sbin == NULL ) {
 			compile_error = TRUE;
 			break;
 		}
@@ -562,9 +570,11 @@ static int lda_sieve_multiscript_execute
 
 				/* Recompile */
 
-				sbin=lda_sieve_recompile(srctx, script, cpflags, &error);
-				if ( sbin == NULL )
+				sbin = lda_sieve_open(srctx, script, cpflags, TRUE, &error);
+				if ( sbin == NULL ) {
+					compile_error = TRUE;
 					break;
+				}
 
 				/* Execute again */
 
@@ -583,138 +593,129 @@ static int lda_sieve_multiscript_execute
 	}
 
 	/* Finish execution */
-
-	ret = sieve_multiscript_finish(&mscript, ehandler, NULL);
+	if ( compile_error && error == SIEVE_ERROR_TEMP_FAILURE )
+		ret = sieve_multiscript_tempfail(&mscript, ehandler);
+	else
+		ret = sieve_multiscript_finish(&mscript, ehandler, NULL);
 
 	/* Don't log additional messages about compile failure */
 	if ( compile_error && ret == SIEVE_EXEC_FAILURE ) {
 		sieve_sys_info(svinst,
-                        "aborted script execution sequence with successful implicit keep");
+			"aborted script execution sequence with successful implicit keep");
 		return 1;
 	}
 
 	return lda_sieve_handle_exec_status(srctx, last_script, ret);
 }
 
-static int lda_sieve_deliver_mail
-(struct mail_deliver_context *mdctx, struct mail_storage **storage_r)
+static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 {
-	struct lda_sieve_run_context srctx;
-	struct sieve_environment svenv;
-	struct sieve_instance *svinst;
-	struct sieve_message_data msgdata;
-	struct sieve_script_env scriptenv;
-	struct sieve_exec_status estatus;
-	struct sieve_error_handler *master_ehandler;
+	struct mail_deliver_context *mdctx = srctx->mdctx;
+	struct sieve_instance *svinst = srctx->svinst;
+	struct sieve_error_handler *master_ehandler = srctx->master_ehandler;
 	const char *user_location, *default_location, *sieve_before, *sieve_after;
 	const char *setting_name;
+	enum sieve_error error;
 	ARRAY_TYPE(sieve_scripts) script_sequence;
-	unsigned int after_index;
+	struct sieve_script *const *scripts;
 	bool debug = mdctx->dest_user->mail_debug;
-	enum sieve_error error;
-	int ret = 0;
-
-	/* Initialize Sieve engine */
-
-	memset((void*)&svenv, 0, sizeof(svenv));
-	svenv.username = mdctx->dest_user->username;
-	(void)mail_user_get_home(mdctx->dest_user, &svenv.home_dir);
-	svenv.hostname = mdctx->set->hostname;
-	svenv.base_dir = mdctx->dest_user->set->base_dir;
-	svenv.flags = SIEVE_FLAG_HOME_RELATIVE;
-	svenv.location = SIEVE_ENV_LOCATION_MDA;
-	svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING;
-
-	svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug);
-
-	/* Initialize master error handler */
-
-	master_ehandler = sieve_master_ehandler_create(svinst, mdctx->session_id, 0);
-	sieve_system_ehandler_set(master_ehandler);
-
-	sieve_error_handler_accept_infolog(master_ehandler, TRUE);
-	sieve_error_handler_accept_debuglog(master_ehandler, debug);
-
-	*storage_r = NULL;
-
-	/* Find scripts and run them */
-
-	T_BEGIN {
-		struct sieve_script *const *scripts;
-		unsigned int count, i;
-
-		/* Initialize run context */
-
-		memset(&srctx, 0, sizeof(srctx));
-		srctx.svinst = svinst;
-		srctx.mdctx = mdctx;
-		srctx.master_ehandler = master_ehandler;
+	unsigned int after_index, count, i;
+	int ret = 1;
 
-		/* Find the personal script to execute */
+	/* Find the personal script to execute */
 
-		user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user);
-		if ( user_location != NULL ) {
-			srctx.user_script = sieve_script_create_open_as
-				(svinst, user_location, "main script", master_ehandler, &error);
+	user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user);
+	if ( user_location != NULL ) {
+		srctx->user_script = sieve_script_create_open_as
+			(svinst, user_location, "main script", master_ehandler, &error);
 
-			if ( srctx.user_script == NULL ) {
-				switch ( error ) {
-				case SIEVE_ERROR_NOT_FOUND:
-					if ( debug )
-						sieve_sys_debug(svinst, "user's script %s doesn't exist "
-							"(using default script location instead)", user_location);
-					break;
-				default:
-					sieve_sys_error(svinst, "failed to access user's sieve script %s "
-						"(using default script location instead)",
-						user_location);
-					break;
+		if ( srctx->user_script == NULL ) {
+			switch ( error ) {
+			case SIEVE_ERROR_NOT_FOUND:
+				if ( debug ) {
+					sieve_sys_debug(svinst, "user's script %s doesn't exist "
+						"(trying default script location instead)", user_location);
 				}
-			} else {
-				srctx.main_script = srctx.user_script;
+				break;
+			case SIEVE_ERROR_TEMP_FAILURE:
+				sieve_sys_error(svinst,
+					"failed to access user's Sieve script %s (temporary failure)",
+					user_location);
+				ret = -1;
+				break;
+			default:
+				sieve_sys_error(svinst,
+					"failed to access user's Sieve script %s "
+					"(trying default script location instead)",
+					user_location);
+				break;
 			}
+		} else {
+			srctx->main_script = srctx->user_script;
 		}
+	}
 
-		if ( srctx.user_script == NULL ) {
-			default_location = lda_sieve_get_default_location(mdctx->dest_user);
-			if ( default_location != NULL ) {
-				srctx.main_script = sieve_script_create_open_as
-					(svinst, default_location, "main script", master_ehandler, &error);
+	if ( ret >= 0 && srctx->user_script == NULL ) {
+		default_location = lda_sieve_get_default_location(mdctx->dest_user);
+		if ( default_location != NULL ) {
+			srctx->main_script = sieve_script_create_open_as
+				(svinst, default_location, "main script", master_ehandler, &error);
 
-				if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND &&
-					debug ) {
-					sieve_sys_debug(svinst, "default user script %s doesn't exist",
+			if ( srctx->main_script == NULL ) {
+				switch ( error ) {
+				case SIEVE_ERROR_NOT_FOUND: 
+					if ( debug ) {
+						sieve_sys_debug(svinst, "default user script %s doesn't exist",
+							default_location);
+					}
+					break;
+				case SIEVE_ERROR_TEMP_FAILURE:
+					sieve_sys_error(svinst,
+						"failed to access default user script %s (temporary failure)",
 						default_location);
+					ret = -1;
+					break;
+				default:
+					sieve_sys_error(svinst, "failed to access default user script %s",
+						default_location);
+					break;
 				}
-			} else {
-				sieve_sys_debug(svinst, "no default script configured for user");
 			}
+		} else {
+			sieve_sys_debug(svinst, "no default script configured for user");
 		}
+	}
 
-		if ( debug && srctx.main_script == NULL ) {
-			sieve_sys_debug(svinst,
-				"user has no valid location for a personal script");
-		}
-
-		/* Compose script array */
+	if ( debug && ret >= 0 && srctx->main_script == NULL ) {
+		sieve_sys_debug(svinst,
+			"user has no valid location for a personal script");
+	}
 
-		t_array_init(&script_sequence, 16);
+	/* Compose script array */
 
+	t_array_init(&script_sequence, 16);
+	
+	/* before */
+	if ( ret >= 0 ) {
 		i = 2;
 		setting_name = "sieve_before";
 		sieve_before = mail_user_plugin_getenv(mdctx->dest_user, setting_name);
-		while ( sieve_before != NULL && *sieve_before != '\0' ) {
-			if ( lda_sieve_multiscript_get_scripts
-				(svinst, setting_name, sieve_before, master_ehandler,
-					&script_sequence) == 0 && debug ) {
+		while ( ret >= 0 && sieve_before != NULL && *sieve_before != '\0' ) {
+			ret = lda_sieve_multiscript_get_scripts(svinst, setting_name,
+				sieve_before, master_ehandler, &script_sequence, &error);
+			if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) {
+				ret = -1;
+				break;
+			} else if (ret == 0 && debug ) {
 				sieve_sys_debug(svinst, "%s location not found: %s",
 					setting_name, sieve_before);
 			}
+			ret = 0;
 			setting_name = t_strdup_printf("sieve_before%u", i++);
 			sieve_before = mail_user_plugin_getenv(mdctx->dest_user, setting_name);
 		}
 
-		if ( debug ) {
+		if ( ret >= 0 && debug ) {
 			scripts = array_get(&script_sequence, &count);
 			for ( i = 0; i < count; i ++ ) {
 				sieve_sys_debug(svinst,
@@ -722,156 +723,229 @@ static int lda_sieve_deliver_mail
 					i+1, sieve_script_location(scripts[i]));
 			}
 		}
+	}
 
-		if ( srctx.main_script != NULL ) {
-			array_append(&script_sequence, &srctx.main_script, 1);
+	/* main */
+	if ( srctx->main_script != NULL ) {
+		array_append(&script_sequence, &srctx->main_script, 1);
 
-			if ( debug ) {
-				sieve_sys_debug(svinst,
-					"using the following location for user's Sieve script: %s",
-					sieve_script_location(srctx.main_script));
-			}
+		if ( ret >= 0 && debug ) {
+			sieve_sys_debug(svinst,
+				"using the following location for user's Sieve script: %s",
+				sieve_script_location(srctx->main_script));
 		}
+	}
 
-		after_index = array_count(&script_sequence);
+	after_index = array_count(&script_sequence);
 
+	/* after */
+	if ( ret >= 0 ) {
 		i = 2;
 		setting_name = "sieve_after";
 		sieve_after = mail_user_plugin_getenv(mdctx->dest_user, setting_name);
 		while ( sieve_after != NULL && *sieve_after != '\0' ) {
-			if ( lda_sieve_multiscript_get_scripts(svinst, setting_name,
-				sieve_after, master_ehandler, &script_sequence) == 0 && debug ) {
+			ret = lda_sieve_multiscript_get_scripts(svinst, setting_name,
+				sieve_after, master_ehandler, &script_sequence, &error);
+			if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) {
+				ret = -1;
+				break;
+			} else if (ret == 0 && debug ) {
 				sieve_sys_debug(svinst, "%s location not found: %s",
 					setting_name, sieve_after);
 			}
+			ret = 0;
 			setting_name = t_strdup_printf("sieve_after%u", i++);
 			sieve_after = mail_user_plugin_getenv(mdctx->dest_user, setting_name);
 		}
 
-		if ( debug ) {
+		if ( ret >= 0 && debug ) {
 			scripts = array_get(&script_sequence, &count);
 			for ( i = after_index; i < count; i ++ ) {
 				sieve_sys_debug(svinst, "executed after user's Sieve script(%d): %s",
 					i+1, sieve_script_location(scripts[i]));
 			}
 		}
+	}
 
-		srctx.scripts =
-			array_get_modifiable(&script_sequence, &srctx.script_count);
+	if (ret < 0) {
+		mdctx->tempfail_error =
+			"Temporarily unable to access necessary Sieve scripts";
+	}
+	srctx->scripts =
+		array_get_modifiable(&script_sequence, &srctx->script_count);
+	return ret;
+}
 
-		/* Check whether there are any scripts to execute at all */
+static int lda_sieve_execute
+(struct lda_sieve_run_context *srctx, struct mail_storage **storage_r)
+{
+	struct mail_deliver_context *mdctx = srctx->mdctx;
+	struct sieve_instance *svinst = srctx->svinst;
+	struct sieve_message_data msgdata;
+	struct sieve_script_env scriptenv;
+	struct sieve_exec_status estatus;
+	bool debug = mdctx->dest_user->mail_debug;
+	int ret;
 
-		if ( srctx.script_count == 0 ) {
-			if ( debug ) {
-				sieve_sys_debug(svinst,
-					"no scripts to execute: reverting to default delivery.");
-			}
+	/* Check whether there are any scripts to execute at all */
 
-			/* No error, but no delivery by this plugin either. A return value of <= 0
-			 * for a deliver plugin is is considered a failure. In deliver itself,
-			 * saved_mail and tried_default_save remain unset, meaning that deliver
-			 * will then attempt the default delivery. We return 0 to signify the lack
-			 * of a real error.
-			 */
-			ret = 0;
-		} else {
-			/* Initialize user error handler */
-
-			if ( srctx.user_script != NULL ) {
-				const char *log_path = NULL;
-
-				/* Determine user log file path */
-				if ( (log_path=mail_user_plugin_getenv
-					(mdctx->dest_user, "sieve_user_log")) == NULL ) {
-					const char *path;
-
-					if ( (path=sieve_file_script_get_path(srctx.user_script)) == NULL ) {
-						/* Default */
-						if ( svenv.home_dir != NULL ) {
-							log_path = t_strconcat
-								(svenv.home_dir, "/.dovecot.sieve.log", NULL);
-						}
-					} else {
-						/* Use script file as a basic (legacy behavior) */
-						log_path = t_strconcat(path, ".log", NULL);
-					}
-				} else if ( svenv.home_dir != NULL ) {
-					/* Expand home dir if necessary */
-					if ( log_path[0] == '~' ) {
-						log_path = home_expand_tilde(log_path, svenv.home_dir);
-					} else if ( log_path[0] != '/' ) {
-						log_path = t_strconcat(svenv.home_dir, "/", log_path, NULL);
+	if ( srctx->script_count == 0 ) {
+		if ( debug ) {
+			sieve_sys_debug(svinst,
+				"no scripts to execute: reverting to default delivery.");
+		}
+
+		/* No error, but no delivery by this plugin either. A return value of <= 0
+		 * for a deliver plugin is is considered a failure. In deliver itself,
+		 * saved_mail and tried_default_save remain unset, meaning that deliver
+		 * will then attempt the default delivery. We return 0 to signify the lack
+		 * of a real error.
+		 */
+		ret = 0;
+	} else {
+		/* Initialize user error handler */
+
+		if ( srctx->user_script != NULL ) {
+			const char *log_path = NULL;
+
+			/* Determine user log file path */
+			if ( (log_path=mail_user_plugin_getenv
+				(mdctx->dest_user, "sieve_user_log")) == NULL ) {
+				const char *path;
+
+				if ( (path=sieve_file_script_get_path(srctx->user_script)) == NULL ) {
+					/* Default */
+					if ( srctx->home_dir != NULL ) {
+						log_path = t_strconcat
+							(srctx->home_dir, "/.dovecot.sieve.log", NULL);
 					}
+				} else {
+					/* Use script file as a basic (legacy behavior) */
+					log_path = t_strconcat(path, ".log", NULL);
 				}
-
-				if ( log_path != NULL ) {
-					srctx.userlog = log_path;
-					srctx.user_ehandler = sieve_logfile_ehandler_create
-						(svinst, srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS);
+			} else if ( srctx->home_dir != NULL ) {
+				/* Expand home dir if necessary */
+				if ( log_path[0] == '~' ) {
+					log_path = home_expand_tilde(log_path, srctx->home_dir);
+				} else if ( log_path[0] != '/' ) {
+					log_path = t_strconcat(srctx->home_dir, "/", log_path, NULL);
 				}
 			}
 
-			/* Collect necessary message data */
+			if ( log_path != NULL ) {
+				srctx->userlog = log_path;
+				srctx->user_ehandler = sieve_logfile_ehandler_create
+					(svinst, srctx->userlog, LDA_SIEVE_MAX_USER_ERRORS);
+			}
+		}
 
-			memset(&msgdata, 0, sizeof(msgdata));
+		/* Collect necessary message data */
 
-			msgdata.mail = mdctx->src_mail;
-			msgdata.return_path = mail_deliver_get_return_address(mdctx);
-			msgdata.orig_envelope_to = mdctx->dest_addr;
-			msgdata.final_envelope_to = mdctx->final_dest_addr;
-			msgdata.auth_user = mdctx->dest_user->username;
-			(void)mail_get_first_header(msgdata.mail, "Message-ID", &msgdata.id);
+		memset(&msgdata, 0, sizeof(msgdata));
 
-			srctx.msgdata = &msgdata;
+		msgdata.mail = mdctx->src_mail;
+		msgdata.return_path = mail_deliver_get_return_address(mdctx);
+		msgdata.orig_envelope_to = mdctx->dest_addr;
+		msgdata.final_envelope_to = mdctx->final_dest_addr;
+		msgdata.auth_user = mdctx->dest_user->username;
+		(void)mail_get_first_header(msgdata.mail, "Message-ID", &msgdata.id);
 
-			/* Compose script execution environment */
+		srctx->msgdata = &msgdata;
 
-			memset(&scriptenv, 0, sizeof(scriptenv));
-			memset(&estatus, 0, sizeof(estatus));
+		/* Compose script execution environment */
 
-			scriptenv.action_log_format = mdctx->set->deliver_log_format;
-			scriptenv.default_mailbox = mdctx->dest_mailbox_name;
-			scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate;
-			scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe;
-			scriptenv.user = mdctx->dest_user;
-			scriptenv.postmaster_address = mdctx->set->postmaster_address;
-			scriptenv.smtp_open = lda_sieve_smtp_open;
-			scriptenv.smtp_close = lda_sieve_smtp_close;
-			scriptenv.duplicate_mark = lda_sieve_duplicate_mark;
-			scriptenv.duplicate_check = lda_sieve_duplicate_check;
-			scriptenv.reject_mail = lda_sieve_reject_mail;
-			scriptenv.script_context = (void *) mdctx;
-			scriptenv.exec_status = &estatus;
+		memset(&scriptenv, 0, sizeof(scriptenv));
+		memset(&estatus, 0, sizeof(estatus));
 
-			srctx.scriptenv = &scriptenv;
+		scriptenv.action_log_format = mdctx->set->deliver_log_format;
+		scriptenv.default_mailbox = mdctx->dest_mailbox_name;
+		scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate;
+		scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe;
+		scriptenv.user = mdctx->dest_user;
+		scriptenv.postmaster_address = mdctx->set->postmaster_address;
+		scriptenv.smtp_open = lda_sieve_smtp_open;
+		scriptenv.smtp_close = lda_sieve_smtp_close;
+		scriptenv.duplicate_mark = lda_sieve_duplicate_mark;
+		scriptenv.duplicate_check = lda_sieve_duplicate_check;
+		scriptenv.reject_mail = lda_sieve_reject_mail;
+		scriptenv.script_context = (void *) mdctx;
+		scriptenv.exec_status = &estatus;
 
-			/* Execute script(s) */
+		srctx->scriptenv = &scriptenv;
 
-			if ( srctx.script_count == 1 )
-				ret = lda_sieve_singlescript_execute(&srctx);
-			else
-				ret = lda_sieve_multiscript_execute(&srctx);
+		/* Execute script(s) */
 
-			/* Record status */
+		if ( srctx->script_count == 1 )
+			ret = lda_sieve_singlescript_execute(srctx);
+		else
+			ret = lda_sieve_multiscript_execute(srctx);
 
-			mdctx->tried_default_save = estatus.tried_default_save;
-			*storage_r = estatus.last_storage;
+		/* Record status */
 
-			/* Clean up user error handlers */
+		mdctx->tried_default_save = estatus.tried_default_save;
+		*storage_r = estatus.last_storage;
+	}
 
-			if ( srctx.user_ehandler != NULL )
-				sieve_error_handler_unref(&srctx.user_ehandler);
-		}
+	return ret;
+}
 
-		/* Cleanup scripts */
-		for ( i = 0; i < srctx.script_count; i++ ) {
-			sieve_script_unref(&srctx.scripts[i]);
-		}
+static int lda_sieve_deliver_mail
+(struct mail_deliver_context *mdctx, struct mail_storage **storage_r)
+{
+	struct lda_sieve_run_context srctx;
+	bool debug = mdctx->dest_user->mail_debug;
+	struct sieve_environment svenv;
+	unsigned int i;
+	int ret = 0;
+
+	/* Initialize run context */
+
+	memset(&srctx, 0, sizeof(srctx));
+	srctx.mdctx = mdctx;
+	(void)mail_user_get_home(mdctx->dest_user, &srctx.home_dir);
+
+	/* Initialize Sieve engine */
+
+	memset((void*)&svenv, 0, sizeof(svenv));
+	svenv.username = mdctx->dest_user->username;
+	svenv.home_dir = srctx.home_dir;
+	svenv.hostname = mdctx->set->hostname;
+	svenv.base_dir = mdctx->dest_user->set->base_dir;
+	svenv.flags = SIEVE_FLAG_HOME_RELATIVE;
+	svenv.location = SIEVE_ENV_LOCATION_MDA;
+	svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING;
+
+	srctx.svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug);
+
+	/* Initialize master error handler */
 
+	srctx.master_ehandler =
+		sieve_master_ehandler_create(srctx.svinst, mdctx->session_id, 0);
+	sieve_system_ehandler_set(srctx.master_ehandler);
+
+	sieve_error_handler_accept_infolog(srctx.master_ehandler, TRUE);
+	sieve_error_handler_accept_debuglog(srctx.master_ehandler, debug);
+
+	*storage_r = NULL;
+
+	/* Find Sieve scripts and run them */
+
+	T_BEGIN {
+		if (lda_sieve_find_scripts(&srctx) < 0)
+			ret = -1;
+		else
+			ret = lda_sieve_execute(&srctx, storage_r);
 	} T_END;
 
-	sieve_deinit(&svinst);
-	sieve_error_handler_unref(&master_ehandler);
+	/* Clean up */
+
+	for ( i = 0; i < srctx.script_count; i++ )
+		sieve_script_unref(&srctx.scripts[i]);
+	if ( srctx.user_ehandler != NULL )
+		sieve_error_handler_unref(&srctx.user_ehandler);
+	sieve_error_handler_unref(&srctx.master_ehandler);
+	sieve_deinit(&srctx.svinst);
+
 	return ret;
 }
 
diff --git a/src/plugins/sieve-extprograms/cmd-pipe.c b/src/plugins/sieve-extprograms/cmd-pipe.c
index 94f05cea2..fbbdbc81c 100644
--- a/src/plugins/sieve-extprograms/cmd-pipe.c
+++ b/src/plugins/sieve-extprograms/cmd-pipe.c
@@ -92,7 +92,7 @@ static int act_pipe_check_duplicate
 static void act_pipe_print
 	(const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, bool *keep);	
-static bool act_pipe_commit
+static int act_pipe_commit
 	(const struct sieve_action *action,	
 		const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
 
@@ -328,7 +328,7 @@ static void act_pipe_print
 
 /* Result execution */
 
-static bool act_pipe_commit
+static int act_pipe_commit
 (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, 
 	void *tr_context ATTR_UNUSED, bool *keep)
 {
@@ -354,6 +354,9 @@ static bool act_pipe_commit
 	if ( ret > 0 ) {
 		sieve_result_global_log(aenv, "pipe action: "
 			"piped message to program `%s'", str_sanitize(act->program_name, 128));
+
+		/* Indicate that message was successfully 'forwarded' */
+		aenv->exec_status->message_forwarded = TRUE;
 	} else {
 		if ( ret < 0 ) {
 			if ( error == SIEVE_ERROR_NOT_FOUND ) {
@@ -371,13 +374,13 @@ static bool act_pipe_commit
 				str_sanitize(act->program_name, 80));
 		}
 
-		if ( act->try ) return TRUE;
+		if ( act->try ) return SIEVE_EXEC_OK;
 
-		return FALSE;
+		return SIEVE_EXEC_FAILURE;
 	}
 
 	*keep = FALSE;
-	return TRUE;
+	return SIEVE_EXEC_OK;
 }
 
 
diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.c b/src/plugins/sieve-extprograms/sieve-extprograms-common.c
index 59f39f138..283bd2a07 100644
--- a/src/plugins/sieve-extprograms/sieve-extprograms-common.c
+++ b/src/plugins/sieve-extprograms/sieve-extprograms-common.c
@@ -439,12 +439,12 @@ struct sieve_extprogram *sieve_extprogram_create
 			case EACCES:
 				sieve_sys_error(svinst, "action %s: "
 					"failed to stat socket: %s", action, eacces_error_get("stat", path));
-				*error_r = SIEVE_ERROR_NO_PERM;
+				*error_r = SIEVE_ERROR_NO_PERMISSION;
 				return NULL;
 			default:
 				sieve_sys_error(svinst, "action %s: "
 					"failed to stat socket `%s': %m", action, path);
-				*error_r = SIEVE_ERROR_NOT_POSSIBLE;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 				return NULL;
 			}
 			path = NULL;
@@ -476,12 +476,12 @@ struct sieve_extprogram *sieve_extprogram_create
 			case EACCES:
 				sieve_sys_error(svinst, "action %s: "
 					"failed to stat program: %s", action, eacces_error_get("stat", path));
-				*error_r = SIEVE_ERROR_NO_PERM;
+				*error_r = SIEVE_ERROR_NO_PERMISSION;
 				break;
 			default:
 				sieve_sys_error(svinst, "action %s: "
 					"failed to stat program `%s': %m", action, path);
-				*error_r = SIEVE_ERROR_NOT_POSSIBLE;
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
 				break;
 			}
 
@@ -496,7 +496,7 @@ struct sieve_extprogram *sieve_extprogram_create
 			sieve_sys_error(svinst, "action %s: "
 				"executable `%s' for program `%s' is world-writable",
 				action, path, program_name);
-			*error_r = SIEVE_ERROR_NO_PERM;
+			*error_r = SIEVE_ERROR_NO_PERMISSION;
 			return NULL;
 		}
 	}
diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c
index af3554f09..d0548068d 100644
--- a/src/sieve-tools/sieve-filter.c
+++ b/src/sieve-tools/sieve-filter.c
@@ -218,15 +218,12 @@ static int filter_message
 		sieve_error(ehandler, NULL, "sieve script binary is corrupt");
 		return -1;
 	case SIEVE_EXEC_FAILURE:
+	case SIEVE_EXEC_TEMP_FAILURE:
 	case SIEVE_EXEC_KEEP_FAILED:
 		sieve_error(ehandler, NULL,
 			"sieve script execution failed for this message; "
 			"message left in source mailbox");
 		return 0;
-	default:
-		sieve_error(ehandler, NULL,
-			"sieve execution result: unrecognized return value?!");
-		return -1;
 	}
 
 	return 1;
diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c
index 3c78153ce..63e62e3dd 100644
--- a/src/sieve-tools/sieve-test.c
+++ b/src/sieve-tools/sieve-test.c
@@ -371,12 +371,12 @@ int main(int argc, char **argv)
 			i_info("final result: failed; resolved with successful implicit keep");
 			exit_status = EXIT_FAILURE;
 			break;
-		case SIEVE_EXEC_KEEP_FAILED:
-			i_info("final result: utter failure");
+		case SIEVE_EXEC_TEMP_FAILURE:
+			i_info("final result: temporary failure");
 			exit_status = EXIT_FAILURE;
 			break;
-		default:
-			i_info("final result: unrecognized return value?!");
+		case SIEVE_EXEC_KEEP_FAILED:
+			i_info("final result: utter failure");
 			exit_status = EXIT_FAILURE;
 			break;
 		}
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 722ad2a6d..ab2911a94 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -197,13 +197,12 @@ int main(int argc, char **argv)
 			break;
 		case SIEVE_EXEC_FAILURE:
 		case SIEVE_EXEC_KEEP_FAILED:
+		case SIEVE_EXEC_TEMP_FAILURE:
 			testsuite_testcase_fail("test script execution aborted due to error");
 			break;
 		case SIEVE_EXEC_BIN_CORRUPT:
 			testsuite_testcase_fail("compiled test script binary is corrupt");
 			break;
-		default:
-			testsuite_testcase_fail("unknown execution exit code");
 		}
 
 		sieve_close(&sbin);
-- 
GitLab