From d30a8deeb88649ee44f47ff3cd613759f0ed92f2 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan.bosch@dovecot.fi>
Date: Mon, 27 May 2019 20:48:43 +0200
Subject: [PATCH] lib-sieve: Use common context for both script evaluation and
 action execution.

---
 src/lib-sieve/Makefile.am                     |   1 +
 src/lib-sieve/cmd-discard.c                   |   5 +-
 src/lib-sieve/cmd-redirect.c                  |  49 ++++---
 src/lib-sieve/ext-envelope.c                  |   9 +-
 src/lib-sieve/ext-reject.c                    |   9 +-
 .../plugins/duplicate/ext-duplicate-common.c  |   8 +-
 .../plugins/duplicate/tst-duplicate.c         |   3 +-
 src/lib-sieve/plugins/enotify/cmd-notify.c    |  14 +-
 .../plugins/enotify/ext-enotify-common.c      |   9 +-
 .../environment/ext-environment-common.c      |  16 ++-
 src/lib-sieve/plugins/ihave/tst-ihave.c       |   3 +-
 .../plugins/include/ext-include-common.c      |  24 ++--
 .../plugins/mailbox/tag-mailbox-create.c      |   7 +-
 .../plugins/mailbox/tst-mailboxexists.c       |   5 +-
 src/lib-sieve/plugins/metadata/tst-metadata.c |   3 +-
 .../plugins/metadata/tst-metadataexists.c     |   3 +-
 src/lib-sieve/plugins/notify/cmd-notify.c     |  14 +-
 .../plugins/notify/ext-notify-common.c        |   6 +-
 src/lib-sieve/plugins/vacation/cmd-vacation.c |  24 ++--
 src/lib-sieve/plugins/vacation/ext-vacation.c |   4 +-
 .../plugins/variables/ext-variables-common.c  |   3 +-
 .../environment/ext-vnd-environment-items.c   |  14 +-
 .../plugins/vnd.dovecot/report/cmd-report.c   |  23 ++--
 src/lib-sieve/sieve-actions.c                 |  69 ++++++----
 src/lib-sieve/sieve-actions.h                 |   7 +-
 src/lib-sieve/sieve-common.h                  |   3 +
 src/lib-sieve/sieve-execute.h                 |  16 +++
 src/lib-sieve/sieve-interpreter.c             |  49 +++----
 src/lib-sieve/sieve-interpreter.h             |  12 +-
 src/lib-sieve/sieve-result.c                  |  65 +++++----
 src/lib-sieve/sieve-result.h                  |  11 +-
 src/lib-sieve/sieve-runtime.h                 |   8 +-
 src/lib-sieve/sieve.c                         | 127 ++++++++++++------
 src/lib-sieve/tst-size.c                      |   3 +-
 .../imapsieve/ext-imapsieve-environment.c     |  22 ++-
 src/plugins/sieve-extprograms/cmd-execute.c   |   7 +-
 src/plugins/sieve-extprograms/cmd-filter.c    |   7 +-
 src/plugins/sieve-extprograms/cmd-pipe.c      |  12 +-
 src/testsuite/cmd-test-config.c               |   5 +-
 src/testsuite/testsuite-result.c              |  34 +++--
 src/testsuite/testsuite-result.h              |   4 +
 src/testsuite/testsuite-script.c              |  34 +++--
 src/testsuite/testsuite.c                     |  17 +--
 43 files changed, 461 insertions(+), 307 deletions(-)
 create mode 100644 src/lib-sieve/sieve-execute.h

diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 938bda0a3..9c55eb6f2 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -158,6 +158,7 @@ headers = \
 	sieve-address.h \
 	sieve-validator.h \
 	sieve-generator.h \
+	sieve-execute.h \
 	sieve-interpreter.h \
 	sieve-runtime-trace.h \
 	sieve-runtime.h \
diff --git a/src/lib-sieve/cmd-discard.c b/src/lib-sieve/cmd-discard.c
index 17b63a356..7857eee0d 100644
--- a/src/lib-sieve/cmd-discard.c
+++ b/src/lib-sieve/cmd-discard.c
@@ -159,7 +159,10 @@ act_discard_commit(const struct sieve_action *action ATTR_UNUSED,
 		   const struct sieve_action_exec_env *aenv,
 		   void *tr_context ATTR_UNUSED, bool *keep)
 {
-	aenv->exec_status->significant_action_executed = TRUE;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+
+	eenv->exec_status->significant_action_executed = TRUE;
+
 	sieve_result_global_log(aenv,
 		"marked message to be discarded if not explicitly delivered "
 		"(discard action)");
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index 73411f1a7..3cf8f39d2 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -194,7 +194,8 @@ static int
 cmd_redirect_operation_execute(const struct sieve_runtime_env *renv,
 			       sieve_size_t *address)
 {
-	struct sieve_instance *svinst = renv->svinst;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	struct sieve_side_effects_list *slist = NULL;
 	string_t *redirect;
 	const struct smtp_address *to_address;
@@ -265,11 +266,13 @@ act_redirect_equals(const struct sieve_script_env *senv ATTR_UNUSED,
 }
 
 static int
-act_redirect_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED,
+act_redirect_check_duplicate(const struct sieve_runtime_env *renv,
 			     const struct sieve_action *act,
 			     const struct sieve_action *act_other)
 {
-	return (act_redirect_equals(renv->scriptenv, act, act_other) ? 1 : 0);
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return (act_redirect_equals(eenv->scriptenv, act, act_other) ? 1 : 0);
 }
 
 static void
@@ -290,9 +293,10 @@ act_redirect_send(const struct sieve_action_exec_env *aenv, struct mail *mail,
 		  ATTR_NULL(4)
 {
 	static const char *hide_headers[] = { "Return-Path" };
-	struct sieve_instance *svinst = aenv->svinst;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	struct sieve_message_context *msgctx = aenv->msgctx;
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct sieve_address_source env_from = svinst->redirect_from;
 	struct istream *input;
 	struct ostream *output;
@@ -327,18 +331,18 @@ act_redirect_send(const struct sieve_action_exec_env *aenv, struct mail *mail,
 	   when then returns a delivery status notification that also ends up
 	   being redirected to the same invalid address.
 	 */
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
 		/* Envelope available */
 		sender = sieve_message_get_sender(msgctx);
 		if (sender != NULL &&
 		    sieve_address_source_get_address(&env_from, svinst, senv,
-						     msgctx, aenv->flags,
+						     msgctx, eenv->flags,
 						     &sender) < 0)
 			sender = NULL;
 	} else {
 		/* No envelope available */
 		ret = sieve_address_source_get_address(&env_from, svinst, senv,
-						       msgctx, aenv->flags,
+						       msgctx, eenv->flags,
 						       &sender);
 		if (ret < 0)
 			sender = NULL;
@@ -363,10 +367,10 @@ act_redirect_send(const struct sieve_action_exec_env *aenv, struct mail *mail,
 		rfc2822_header_append(hdr, "X-Sieve", SIEVE_IMPLEMENTATION,
 				      FALSE, NULL);
 		if (svinst->user_email == NULL &&
-		    (aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
+		    (eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
 			user_email = sieve_message_get_final_recipient(msgctx);
 		else
-			user_email = sieve_get_user_email(aenv->svinst);
+			user_email = sieve_get_user_email(svinst);
 		if (user_email != NULL) {
 			rfc2822_header_append(hdr, "X-Sieve-Redirected-From",
 					      smtp_address_encode(user_email),
@@ -423,8 +427,9 @@ act_redirect_get_duplicate_id(struct act_redirect_context *ctx,
 			      const struct sieve_action_exec_env *aenv,
 			      const char *msg_id, const char **dupeid_r)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_message_context *msgctx = aenv->msgctx;
-	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	struct mail *mail = msgdata->mail;
 	const struct smtp_address *recipient;
 	const char *resent_id = NULL, *list_id = NULL;
@@ -447,10 +452,10 @@ act_redirect_get_duplicate_id(struct act_redirect_context *ctx,
 			"failed to read header field `list-id'");
 	}
 
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
 		recipient = sieve_message_get_orig_recipient(msgctx);
 	else
-		recipient = sieve_get_user_email(aenv->svinst);
+		recipient = sieve_get_user_email(eenv->svinst);
 
 	/* Base the duplicate ID on:
 	   - the message id
@@ -473,6 +478,7 @@ static int
 act_redirect_check_loop_header(const struct sieve_action_exec_env *aenv,
 			       struct mail *mail, bool *loop_detected_r)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_message_context *msgctx = aenv->msgctx;
 	const char *const *headers;
 	const char *recipient, *user_email;
@@ -493,12 +499,12 @@ act_redirect_check_loop_header(const struct sieve_action_exec_env *aenv,
 		return SIEVE_EXEC_OK;
 
 	recipient = user_email = NULL;
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
 		addr = sieve_message_get_final_recipient(msgctx);
 		if (addr != NULL)
 			recipient = smtp_address_encode(addr);
 	}
-	addr = sieve_get_user_email(aenv->svinst);
+	addr = sieve_get_user_email(eenv->svinst);
 	if (addr != NULL)
 		user_email = smtp_address_encode(addr);
 
@@ -523,14 +529,15 @@ act_redirect_commit(const struct sieve_action *action,
 		    const struct sieve_action_exec_env *aenv,
 		    void *tr_context ATTR_UNUSED, bool *keep)
 {
-	struct sieve_instance *svinst = aenv->svinst;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	struct act_redirect_context *ctx =
 		(struct act_redirect_context *)action->context;
 	struct sieve_message_context *msgctx = aenv->msgctx;
 	struct mail *mail = (action->mail != NULL ?
 			     action->mail : sieve_message_get_mail(msgctx));
-	const struct sieve_message_data *msgdata = aenv->msgdata;
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	const char *msg_id = msgdata->id, *new_msg_id = NULL;
 	const char *dupeid = NULL;
 	bool loop_detected = FALSE;
@@ -542,7 +549,7 @@ act_redirect_commit(const struct sieve_action *action,
 
 	/* Create Message-ID for the message if it has none */
 	if (msg_id == NULL)
-		msg_id = new_msg_id = sieve_message_get_new_id(aenv->svinst);
+		msg_id = new_msg_id = sieve_message_get_new_id(eenv->svinst);
 
 	/* Create ID for duplicate database lookup */
 	ret = act_redirect_get_duplicate_id(ctx, aenv, msg_id, &dupeid);
@@ -585,13 +592,13 @@ act_redirect_commit(const struct sieve_action *action,
 		sieve_action_duplicate_mark(senv, dupeid, strlen(dupeid),
 			ioloop_time + svinst->redirect_duplicate_period);
 
-		aenv->exec_status->significant_action_executed = TRUE;
+		eenv->exec_status->significant_action_executed = TRUE;
 		sieve_result_global_log(
 			aenv, "redirect action: forwarded to <%s>",
 			smtp_address_encode(ctx->to_address));
 
 		/* Indicate that message was successfully forwarded */
-		aenv->exec_status->message_forwarded = TRUE;
+		eenv->exec_status->message_forwarded = TRUE;
 
 		/* Cancel implicit keep */
 		*keep = FALSE;
diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c
index b2abc54ef..e889cff43 100644
--- a/src/lib-sieve/ext-envelope.c
+++ b/src/lib-sieve/ext-envelope.c
@@ -130,7 +130,9 @@ ext_envelope_interpreter_run(const struct sieve_extension *ext,
 			     const struct sieve_runtime_env *renv,
 			     void *context ATTR_UNUSED, bool deferred)
 {
-	if ((renv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
 		if (!deferred) {
 			sieve_runtime_error(
 				renv, NULL,
@@ -339,12 +341,13 @@ _to_part_get_values(const struct sieve_runtime_env *renv)
 static const char *const *
 _auth_part_get_values(const struct sieve_runtime_env *renv)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	ARRAY(const char *) envelope_values;
 
 	t_array_init(&envelope_values, 2);
 
-	if (renv->msgdata->auth_user != NULL)
-		array_append(&envelope_values, &renv->msgdata->auth_user, 1);
+	if (eenv->msgdata->auth_user != NULL)
+		array_append(&envelope_values, &eenv->msgdata->auth_user, 1);
 
 	(void)array_append_space(&envelope_values);
 
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index 263cb8885..5d7014705 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -190,7 +190,9 @@ ext_reject_interpreter_run(const struct sieve_extension *ext,
 			   const struct sieve_runtime_env *renv,
 			   void *context ATTR_UNUSED, bool deferred)
 {
-	if ((renv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
 		if (!deferred) {
 			sieve_runtime_error(
 				renv, NULL,
@@ -506,6 +508,7 @@ act_reject_commit(const struct sieve_action *action,
 		  const struct sieve_action_exec_env *aenv,
 		  void *tr_context ATTR_UNUSED, bool *keep)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct act_reject_context *rj_ctx =
 		(struct act_reject_context *)action->context;
 	const struct smtp_address *sender, *recipient;
@@ -514,7 +517,7 @@ act_reject_commit(const struct sieve_action *action,
 	sender = sieve_message_get_sender(aenv->msgctx);
 	recipient = sieve_message_get_orig_recipient(aenv->msgctx);
 
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) {
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) {
 		sieve_result_global_log(
 			aenv, "not sending reject message (skipped)");
 		return SIEVE_EXEC_OK;
@@ -542,7 +545,7 @@ act_reject_commit(const struct sieve_action *action,
 					    rj_ctx->reason)) <= 0)
 		return ret;
 
-	aenv->exec_status->significant_action_executed = TRUE;
+	eenv->exec_status->significant_action_executed = TRUE;
 	sieve_result_global_log(
 		aenv, "rejected message from <%s> (%s)",
 		smtp_address_encode(sender),
diff --git a/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c b/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c
index e980eaf85..41360ed58 100644
--- a/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c
+++ b/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c
@@ -110,7 +110,8 @@ act_duplicate_mark_finish(const struct sieve_action *action,
 			  const struct sieve_action_exec_env *aenv,
 			  void *tr_context ATTR_UNUSED, int status)
 {
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct act_duplicate_mark_data *data =
 		(struct act_duplicate_mark_data *)action->context;
 
@@ -118,7 +119,7 @@ act_duplicate_mark_finish(const struct sieve_action *action,
 		/* Message was handled successfully, so track duplicate for this
 		 * message.
 		 */
-		aenv->exec_status->significant_action_executed = TRUE;
+		eenv->exec_status->significant_action_executed = TRUE;
 		sieve_action_duplicate_mark(senv, data->hash,
 					    sizeof(data->hash),
 					    ioloop_time + data->period);
@@ -169,8 +170,9 @@ int ext_duplicate_check(const struct sieve_runtime_env *renv, string_t *handle,
 			const char *value, size_t value_len,
 			sieve_number_t period, bool last)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *this_ext = renv->oprtn->ext;
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct ext_duplicate_context *rctx;
 	bool duplicate = FALSE;
 	pool_t msg_pool = NULL, result_pool = NULL;
diff --git a/src/lib-sieve/plugins/duplicate/tst-duplicate.c b/src/lib-sieve/plugins/duplicate/tst-duplicate.c
index 1b42919f2..db3756b36 100644
--- a/src/lib-sieve/plugins/duplicate/tst-duplicate.c
+++ b/src/lib-sieve/plugins/duplicate/tst-duplicate.c
@@ -323,10 +323,11 @@ static int
 tst_duplicate_operation_execute(const struct sieve_runtime_env *renv,
 				sieve_size_t *address ATTR_UNUSED)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *ext = renv->oprtn->ext;
 	const struct ext_duplicate_config *config =
 		(const struct ext_duplicate_config *)ext->context;
-	struct mail *mail = renv->msgdata->mail;
+	struct mail *mail = eenv->msgdata->mail;
 	int opt_code = 0;
 	string_t *handle = NULL, *header = NULL, *uniqueid = NULL;
 	const char *val = NULL;
diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c
index b75b8a981..54e9eb29f 100644
--- a/src/lib-sieve/plugins/enotify/cmd-notify.c
+++ b/src/lib-sieve/plugins/enotify/cmd-notify.c
@@ -524,6 +524,7 @@ act_notify_check_duplicate(const struct sieve_runtime_env *renv,
 			   const struct sieve_action *act,
 			   const struct sieve_action *act_other)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_enotify_action *nact, *nact_other;
 	const struct sieve_enotify_method_def *nmth_def;
 	struct sieve_enotify_env nenv;
@@ -543,7 +544,7 @@ act_notify_check_duplicate(const struct sieve_runtime_env *renv,
 		return 0;
 
 	i_zero(&nenv);
-	nenv.svinst = renv->svinst;
+	nenv.svinst = eenv->svinst;
 	nenv.method = nact->method;
 	nenv.ehandler =
 		sieve_prefix_ehandler_create(renv->ehandler, act->location,
@@ -592,6 +593,7 @@ act_notify_commit(const struct sieve_action *action,
 		  const struct sieve_action_exec_env *aenv,
 		  void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	const struct sieve_enotify_action *act =
 		(const struct sieve_enotify_action *)action->context;
 	const struct sieve_enotify_method *method = act->method;
@@ -601,11 +603,11 @@ act_notify_commit(const struct sieve_action *action,
 	if (method->def != NULL && method->def->action_execute != NULL)	{
 		/* Compose log structure */
 		i_zero(&nenv);
-		nenv.svinst = aenv->svinst;
-		nenv.flags = aenv->flags;
+		nenv.svinst = eenv->svinst;
+		nenv.flags = eenv->flags;
 		nenv.method = method;
-		nenv.scriptenv = aenv->scriptenv;
-		nenv.msgdata = aenv->msgdata;
+		nenv.scriptenv = eenv->scriptenv;
+		nenv.msgdata = eenv->msgdata;
 		nenv.msgctx = aenv->msgctx;
 
 		nenv.ehandler = sieve_prefix_ehandler_create(
@@ -613,7 +615,7 @@ act_notify_commit(const struct sieve_action *action,
 
 		ret = method->def->action_execute(&nenv, act);
 		if (ret >= 0)
-			aenv->exec_status->significant_action_executed = TRUE;
+			eenv->exec_status->significant_action_executed = TRUE;
 
 		sieve_error_handler_unref(&nenv.ehandler);
 	}
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
index 2bdf6259e..7c02f6b0d 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
@@ -492,6 +492,7 @@ bool ext_enotify_compile_check_arguments(struct sieve_validator *valdtr,
 bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv,
 					 string_t *method_uri)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	const struct sieve_enotify_method *method;
 	const char *uri = str_c(method_uri);
@@ -511,7 +512,7 @@ bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv,
 		struct sieve_enotify_env nenv;
 
 		i_zero(&nenv);
-		nenv.svinst = renv->svinst;
+		nenv.svinst = eenv->svinst;
 		nenv.method = method;
 		nenv.ehandler = sieve_prefix_ehandler_create(
 			renv->ehandler,
@@ -565,6 +566,7 @@ ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv,
 					  string_t *method_uri,
 					  const char *capability)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_enotify_method *method;
 	const char *uri_body;
 	const char *result = NULL;
@@ -579,7 +581,7 @@ ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv,
 		struct sieve_enotify_env nenv;
 
 		i_zero(&nenv);
-		nenv.svinst = renv->svinst;
+		nenv.svinst = eenv->svinst;
 		nenv.method = method;
 		nenv.ehandler = sieve_prefix_ehandler_create(
 			renv->ehandler,
@@ -600,6 +602,7 @@ int ext_enotify_runtime_check_operands(
 	string_t *message, string_t *from, struct sieve_stringlist *options,
 	const struct sieve_enotify_method **method_r, void **method_context)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_enotify_method *method;
 	const char *uri_body;
 
@@ -615,7 +618,7 @@ int ext_enotify_runtime_check_operands(
 		int result = SIEVE_EXEC_OK;
 
 		i_zero(&nenv);
-		nenv.svinst = renv->svinst;
+		nenv.svinst = eenv->svinst;
 		nenv.method = method;
 		nenv.ehandler = sieve_prefix_ehandler_create(
 			renv->ehandler,
diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c
index 674a74fc2..9b7924059 100644
--- a/src/lib-sieve/plugins/environment/ext-environment-common.c
+++ b/src/lib-sieve/plugins/environment/ext-environment-common.c
@@ -221,7 +221,9 @@ static const char *
 envit_domain_get_value(const struct sieve_runtime_env *renv,
 		       const char *name ATTR_UNUSED)
 {
-	return renv->svinst->domainname;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return eenv->svinst->domainname;
 }
 
 const struct sieve_environment_item domain_env_item = {
@@ -239,7 +241,9 @@ static const char *
 envit_host_get_value(const struct sieve_runtime_env *renv,
 		     const char *name ATTR_UNUSED)
 {
-	return renv->svinst->hostname;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return eenv->svinst->hostname;
 }
 
 const struct sieve_environment_item host_env_item = {
@@ -262,7 +266,9 @@ static const char *
 envit_location_get_value(const struct sieve_runtime_env *renv,
 			 const char *name ATTR_UNUSED)
 {
-	switch (renv->svinst->env_location) {
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	switch (eenv->svinst->env_location ) {
 	case SIEVE_ENV_LOCATION_MDA:
 		return "MDA";
 	case SIEVE_ENV_LOCATION_MTA:
@@ -292,7 +298,9 @@ static const char *
 envit_phase_get_value(const struct sieve_runtime_env *renv,
 		      const char *name ATTR_UNUSED)
 {
-	switch (renv->svinst->delivery_phase) {
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	switch (eenv->svinst->delivery_phase) {
 	case SIEVE_DELIVERY_PHASE_PRE:
 		return "pre";
 	case SIEVE_DELIVERY_PHASE_DURING:
diff --git a/src/lib-sieve/plugins/ihave/tst-ihave.c b/src/lib-sieve/plugins/ihave/tst-ihave.c
index b9f43606e..c6c8e8f47 100644
--- a/src/lib-sieve/plugins/ihave/tst-ihave.c
+++ b/src/lib-sieve/plugins/ihave/tst-ihave.c
@@ -229,7 +229,8 @@ static int
 tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
 			    sieve_size_t *address)
 {
-	struct sieve_instance *svinst = renv->svinst;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	struct sieve_stringlist *capabilities;
 	string_t *cap_item;
 	bool matched;
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index ec2f3dd25..e440a7949 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -672,6 +672,7 @@ int ext_include_execute_include(const struct sieve_runtime_env *renv,
 				unsigned int include_id,
 				enum ext_include_flags flags)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	int result = SIEVE_EXEC_OK;
 	struct ext_include_interpreter_context *ctx;
@@ -732,27 +733,25 @@ int ext_include_execute_include(const struct sieve_runtime_env *renv,
 
 		/* We are the top-level interpreter instance */
 		if (result == SIEVE_EXEC_OK) {
-			enum sieve_execute_flags exflags = renv->flags;
+			struct sieve_execute_env eenv_new = *eenv;
 
 			if (included->location != EXT_INCLUDE_LOCATION_GLOBAL)
-				exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
+				eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
 			else
-				exflags &= ~SIEVE_EXECUTE_FLAG_NOGLOBAL;
+				eenv_new.flags &= ~SIEVE_EXECUTE_FLAG_NOGLOBAL;
 
 			/* Create interpreter for top-level included script
 			   (first sub-interpreter)
 			 */
 			subinterp = sieve_interpreter_create_for_block(
-				included->block, included->script,
-				renv->interp, renv->msgdata, renv->scriptenv,
-				ehandler, exflags);
+				included->block, included->script, renv->interp,
+				&eenv_new, ehandler);
 			if (subinterp != NULL) {
 				curctx = ext_include_interpreter_context_init_child(
 					this_ext, subinterp, ctx, included->script,
 					included);
 
-				/* Activate and start the top-level included script
-				 */
+				/* Activate and start the top-level included script */
 				result = sieve_interpreter_start(
 					subinterp, renv->result, &interrupted);
 			} else {
@@ -805,18 +804,17 @@ int ext_include_execute_include(const struct sieve_runtime_env *renv,
 						/* Sub-include requested */
 
 						if (result == SIEVE_EXEC_OK) {
-							enum sieve_execute_flags exflags = renv->flags;
+							struct sieve_execute_env eenv_new = *eenv;
 
 							if (curctx->include->location != EXT_INCLUDE_LOCATION_GLOBAL)
-								exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
+								eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
 							else
-								exflags &= ~SIEVE_EXECUTE_FLAG_NOGLOBAL;
+								eenv_new.flags &= ~SIEVE_EXECUTE_FLAG_NOGLOBAL;
 
 							/* Create sub-interpreter */
 							subinterp = sieve_interpreter_create_for_block(
 								curctx->include->block, curctx->include->script,
-								curctx->interp, renv->msgdata,
-								renv->scriptenv, ehandler, exflags);
+								curctx->interp, &eenv_new, ehandler);
 							if (subinterp != NULL) {
 								curctx = ext_include_interpreter_context_init_child(
 									this_ext, subinterp, curctx,
diff --git a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
index b55735106..15865a7d8 100644
--- a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
+++ b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
@@ -119,12 +119,13 @@ 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,
+	const struct sieve_action_exec_env *aenv,
 	void **se_context ATTR_UNUSED, void *tr_context ATTR_UNUSED)
 {
 	struct act_store_transaction *trans =
 		(struct act_store_transaction *)tr_context;
-	struct mail_storage **storage = &(aenv->exec_status->last_storage);
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct mail_storage **storage = &(eenv->exec_status->last_storage);
 	enum mail_error error;
 
 	/* Check whether creation is necessary */
@@ -159,7 +160,7 @@ seff_mailbox_create_pre_execute(
 	}
 
 	/* Subscribe to it if necessary */
-	if (aenv->scriptenv->mailbox_autosubscribe) {
+	if (eenv->scriptenv->mailbox_autosubscribe) {
 		(void)mailbox_list_set_subscribed(
 			mailbox_get_namespace(trans->box)->list,
 			mailbox_get_name(trans->box), TRUE);
diff --git a/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c b/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
index 68b31018d..ced5b11f5 100644
--- a/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
+++ b/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
@@ -154,6 +154,7 @@ static int
 tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
 				    sieve_size_t *address)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	struct sieve_stringlist *mailbox_names;
 	string_t *mailbox_item;
 	bool trace = FALSE;
@@ -180,7 +181,7 @@ tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
 		trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
 	}
 
-	if (renv->scriptenv->user != NULL) {
+	if (eenv->scriptenv->user != NULL) {
 		int ret;
 
 		mailbox_item = NULL;
@@ -192,7 +193,7 @@ tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
 
 			/* Find the namespace */
 			ns = mail_namespace_find(
-				renv->scriptenv->user->namespaces, mailbox);
+				eenv->scriptenv->user->namespaces, mailbox);
 			if (ns == NULL) {
 				if (trace) {
 					sieve_runtime_trace(
diff --git a/src/lib-sieve/plugins/metadata/tst-metadata.c b/src/lib-sieve/plugins/metadata/tst-metadata.c
index 9df961834..2370f8233 100644
--- a/src/lib-sieve/plugins/metadata/tst-metadata.c
+++ b/src/lib-sieve/plugins/metadata/tst-metadata.c
@@ -279,7 +279,8 @@ tst_metadata_get_annotation(const struct sieve_runtime_env *renv,
 			    const char *mailbox, const char *aname,
 			    const char **annotation_r)
 {
-	struct mail_user *user = renv->scriptenv->user;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	struct mail_user *user = eenv->scriptenv->user;
 	struct mailbox *box;
 	struct imap_metadata_transaction *imtrans;
 	struct mail_attribute_value avalue;
diff --git a/src/lib-sieve/plugins/metadata/tst-metadataexists.c b/src/lib-sieve/plugins/metadata/tst-metadataexists.c
index 0b90ddd0e..25ca49b11 100644
--- a/src/lib-sieve/plugins/metadata/tst-metadataexists.c
+++ b/src/lib-sieve/plugins/metadata/tst-metadataexists.c
@@ -255,7 +255,8 @@ tst_metadataexists_check_annotations(const struct sieve_runtime_env *renv,
 				     struct sieve_stringlist *anames,
 				     bool *all_exist_r)
 {
-	struct mail_user *user = renv->scriptenv->user;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	struct mail_user *user = eenv->scriptenv->user;
 	struct mailbox *box = NULL;
 	struct imap_metadata_transaction *imtrans;
 	string_t *aname;
diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
index ed57668a1..81982074c 100644
--- a/src/lib-sieve/plugins/notify/cmd-notify.c
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -720,7 +720,8 @@ static bool
 act_notify_send(const struct sieve_action_exec_env *aenv,
 		const struct ext_notify_action *act)
 {
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	const struct ext_notify_recipient *recipients;
 	struct sieve_smtp_context *sctx;
 	unsigned int count, i;
@@ -785,10 +786,10 @@ act_notify_send(const struct sieve_action_exec_env *aenv,
 		rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
 	}
 
-	outmsgid = sieve_message_get_new_id(aenv->svinst);
+	outmsgid = sieve_message_get_new_id(eenv->svinst);
 	rfc2822_header_write(msg, "Message-ID", outmsgid);
 
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 &&
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 &&
 	    sieve_message_get_sender(aenv->msgctx) != NULL) {
 		sctx = sieve_smtp_start(senv, sieve_get_postmaster_smtp(senv));
 	} else {
@@ -846,7 +847,8 @@ act_notify_commit(const struct sieve_action *action,
 		  const struct sieve_action_exec_env *aenv,
 		  void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
 {
-	struct mail *mail = aenv->msgdata->mail;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct mail *mail = eenv->msgdata->mail;
 	const struct ext_notify_action *act =
 		(const struct ext_notify_action *)action->context;
 	const char *const *hdsp;
@@ -867,7 +869,7 @@ act_notify_commit(const struct sieve_action *action,
 				const struct smtp_address *sender = NULL;
 				const char *from;
 
-				if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
+				if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
 					sender = sieve_message_get_sender(aenv->msgctx);
 				from = (sender == NULL ? "" :
 					t_strdup_printf(" from <%s>",
@@ -889,6 +891,6 @@ act_notify_commit(const struct sieve_action *action,
 
 	if (!result)
 		return SIEVE_EXEC_FAILURE;
-	aenv->exec_status->significant_action_executed = TRUE;
+	eenv->exec_status->significant_action_executed = TRUE;
 	return SIEVE_EXEC_OK;
 }
diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c
index 0b5f011f6..700b79d18 100644
--- a/src/lib-sieve/plugins/notify/ext-notify-common.c
+++ b/src/lib-sieve/plugins/notify/ext-notify-common.c
@@ -154,9 +154,10 @@ static int
 cmd_notify_extract_body_text(const struct sieve_runtime_env *renv,
 			     const char **body_text_r, size_t *body_size_r)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_notify_message_context *mctx;
-	struct mail *mail = renv->msgdata->mail;
+	struct mail *mail = eenv->msgdata->mail;
 	struct message_parser_ctx *parser;
 	struct message_decoder_context *decoder;
 	struct message_part *parts;
@@ -255,7 +256,8 @@ int ext_notify_construct_message(const struct sieve_runtime_env *renv,
 				 const char *msg_format,
 				 string_t *out_msg)
 {
-	const struct sieve_message_data *msgdata = renv->msgdata;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	struct sieve_message_context *msgctx = renv->msgctx;
 	const struct smtp_address *return_path =
 		sieve_message_get_sender(msgctx);
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index fc0ec9a0a..4259879ed 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -940,7 +940,8 @@ _get_full_reply_recipient(const struct sieve_action_exec_env *aenv,
 			  const struct smtp_address *smtp_to,
 			  struct message_address *reply_to_r)
 {
-	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	const char *const *hdsp;
 	int ret;
 
@@ -1009,7 +1010,8 @@ act_vacation_get_default_subject(const struct sieve_action_exec_env *aenv,
 				 const struct ext_vacation_config *config,
 				 const char **subject_r)
 {
-	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	const char *header, *error;
 	string_t *str;
 	const struct var_expand_table *tab;
@@ -1052,8 +1054,9 @@ act_vacation_send(const struct sieve_action_exec_env *aenv,
 		  const struct smtp_address *smtp_from,
 		  const struct message_address *reply_from)
 {
-	const struct sieve_message_data *msgdata = aenv->msgdata;
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct sieve_smtp_context *sctx;
 	struct ostream *output;
 	string_t *msg;
@@ -1094,7 +1097,7 @@ act_vacation_send(const struct sieve_action_exec_env *aenv,
 
 	sctx = sieve_smtp_start_single(senv, smtp_to, smtp_from, &output);
 
-	outmsgid = sieve_message_get_new_id(aenv->svinst);
+	outmsgid = sieve_message_get_new_id(eenv->svinst);
 
 	/* Produce a proper reply */
 
@@ -1182,7 +1185,7 @@ act_vacation_send(const struct sieve_action_exec_env *aenv,
 		return SIEVE_EXEC_FAILURE;
 	}
 
-	aenv->exec_status->significant_action_executed = TRUE;
+	eenv->exec_status->significant_action_executed = TRUE;
 	return SIEVE_EXEC_OK;
 }
 
@@ -1207,10 +1210,11 @@ act_vacation_commit(const struct sieve_action *action,
 		    void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
 {
 	const struct sieve_extension *ext = action->ext;
-	struct sieve_instance *svinst = aenv->svinst;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	const struct ext_vacation_config *config =
 		(const struct ext_vacation_config *)ext->context;
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct act_vacation_context *ctx =
 		(struct act_vacation_context *)action->context;
 	unsigned char dupl_hash[MD5_RESULTLEN];
@@ -1222,7 +1226,7 @@ act_vacation_commit(const struct sieve_action *action,
 	const char *const *hdsp, *const *headers;
 	int ret;
 
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) {
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) {
 		sieve_result_global_log(
 			aenv, "not sending vacation reply (skipped)");
 		return SIEVE_EXEC_OK;
@@ -1514,7 +1518,7 @@ act_vacation_commit(const struct sieve_action *action,
 	if (ret == SIEVE_EXEC_OK) {
 		sieve_number_t seconds;
 
-		aenv->exec_status->significant_action_executed = TRUE;
+		eenv->exec_status->significant_action_executed = TRUE;
 		sieve_result_global_log(aenv, "sent vacation response to <%s>",
 					smtp_address_encode(sender));
 
diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c
index ab00bb725..8d3d9a738 100644
--- a/src/lib-sieve/plugins/vacation/ext-vacation.c
+++ b/src/lib-sieve/plugins/vacation/ext-vacation.c
@@ -115,7 +115,9 @@ ext_vacation_interpreter_run(const struct sieve_extension *ext,
 			     const struct sieve_runtime_env *renv,
 			     void *context ATTR_UNUSED, bool deferred)
 {
-	if ((renv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
 		if (!deferred) {
 			sieve_runtime_error(
 				renv, NULL,
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c
index 7942d5b84..99ca79e3c 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-common.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -881,9 +881,10 @@ bool ext_variables_interpreter_load(const struct sieve_extension *ext,
 				    const struct sieve_runtime_env *renv,
 				    sieve_size_t *address)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	struct sieve_variable_scope_binary *scpbin;
 
-	scpbin = sieve_variable_scope_binary_read(renv->svinst, ext, NULL,
+	scpbin = sieve_variable_scope_binary_read(eenv->svinst, ext, NULL,
 						  renv->sblock, address);
 	if (scpbin == NULL)
 		return FALSE;
diff --git a/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c
index 863a63e42..af0325599 100644
--- a/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c
+++ b/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c
@@ -29,8 +29,10 @@ static const char *
 envit_default_mailbox_get_value(const struct sieve_runtime_env *renv,
 				const char *name ATTR_UNUSED)
 {
-	i_assert(renv->scriptenv->default_mailbox != NULL);
-	return renv->scriptenv->default_mailbox;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	i_assert(eenv->scriptenv->default_mailbox != NULL);
+	return eenv->scriptenv->default_mailbox;
 }
 
 const struct sieve_environment_item default_mailbox_env_item = {
@@ -44,7 +46,9 @@ static const char *
 envit_username_get_value(const struct sieve_runtime_env *renv,
 			 const char *name ATTR_UNUSED)
 {
-	return renv->svinst->username;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return eenv->svinst->username;
 }
 
 const struct sieve_environment_item username_env_item = {
@@ -57,10 +61,12 @@ const struct sieve_environment_item username_env_item = {
 static const char *
 envit_config_get_value(const struct sieve_runtime_env *renv, const char *name)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
 	if (*name == '\0')
 		return NULL;
 
-	return sieve_setting_get(renv->svinst,
+	return sieve_setting_get(eenv->svinst,
 				 t_strconcat("sieve_env_", name, NULL));
 }
 
diff --git a/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c b/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c
index 56818c82a..5a4799fc3 100644
--- a/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c
+++ b/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c
@@ -409,11 +409,13 @@ act_report_equals(const struct sieve_script_env *senv ATTR_UNUSED,
 }
 
 static int
-act_report_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED,
+act_report_check_duplicate(const struct sieve_runtime_env *renv,
 			   const struct sieve_action *act,
 			   const struct sieve_action *act_other)
 {
-	return (act_report_equals(renv->scriptenv, act, act_other) ? 1 : 0);
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return (act_report_equals(eenv->scriptenv, act, act_other) ? 1 : 0);
 }
 
 /* Result printing */
@@ -450,10 +452,11 @@ act_report_send(const struct sieve_action_exec_env *aenv,
 		const struct ext_report_config *config,
 		const struct act_report_data *act)
 {
-	struct sieve_instance *svinst = aenv->svinst;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	struct sieve_message_context *msgctx = aenv->msgctx;
-	const struct sieve_script_env *senv = aenv->scriptenv;
-	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_script_env *senv = eenv->scriptenv;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	struct sieve_address_source report_from = config->report_from;
 	const struct smtp_address *sender, *user;
 	struct sieve_smtp_context *sctx;
@@ -489,7 +492,7 @@ act_report_send(const struct sieve_action_exec_env *aenv,
 		report_from.address = NULL;
 	}
 	if ((ret = sieve_address_source_get_address(
-		&report_from, svinst, senv, msgctx, aenv->flags,
+		&report_from, svinst, senv, msgctx, eenv->flags,
 		&sender)) > 0 && sender != NULL)
 		from = smtp_address_encode_path(sender);
 	else
@@ -498,7 +501,7 @@ act_report_send(const struct sieve_action_exec_env *aenv,
 	/* Start message */
 	sctx = sieve_smtp_start_single(senv, act->to_address, NULL, &output);
 
-	outmsgid = sieve_message_get_new_id(aenv->svinst);
+	outmsgid = sieve_message_get_new_id(svinst);
 	boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname);
 
 	/* Compose main report headers */
@@ -553,7 +556,7 @@ act_report_send(const struct sieve_action_exec_env *aenv,
 			     PACKAGE_NAME "/" PACKAGE_VERSION " "
 			     PIGEONHOLE_NAME "/" PIGEONHOLE_VERSION);
 
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
 		const struct smtp_address *sender, *orig_recipient;
 
 		sender = sieve_message_get_sender(msgctx);
@@ -569,7 +572,7 @@ act_report_send(const struct sieve_action_exec_env *aenv,
 	}
 	if (svinst->user_email != NULL)
 		user = svinst->user_email;
-	else if ((aenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0 ||
+	else if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0 ||
 		 (user = sieve_message_get_orig_recipient(msgctx)) == NULL)
 		user = sieve_get_user_email(svinst);
 	if (user != NULL) {
@@ -648,7 +651,7 @@ act_report_send(const struct sieve_action_exec_env *aenv,
 				str_sanitize(error, 512));
 		}
 	} else {
-		aenv->exec_status->significant_action_executed = TRUE;
+		eenv->exec_status->significant_action_executed = TRUE;
 		sieve_result_global_log(
 			aenv, "sent `%s' report to <%s>",
 			str_sanitize(act->feedback_type, 32),
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index 81487ef22..f48fc95cf 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -329,7 +329,9 @@ act_store_check_duplicate(const struct sieve_runtime_env *renv,
 			  const struct sieve_action *act,
 			  const struct sieve_action *act_other)
 {
-	return (act_store_equals(renv->scriptenv, act, act_other) ? 1 : 0);
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return (act_store_equals(eenv->scriptenv, act, act_other) ? 1 : 0);
 }
 
 /* Result printing */
@@ -369,8 +371,9 @@ 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)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct mailbox *box;
-	struct mail_storage **storage = &(aenv->exec_status->last_storage);
+	struct mail_storage **storage = &(eenv->exec_status->last_storage);
 	enum mailbox_flags flags = 0;
 
 	*box_r = NULL;
@@ -386,12 +389,12 @@ act_store_mailbox_open(const struct sieve_action_exec_env *aenv,
 		return FALSE;
 	}
 
-	if (aenv->scriptenv->mailbox_autocreate)
+	if (eenv->scriptenv->mailbox_autocreate)
 		flags |= MAILBOX_FLAG_AUTO_CREATE;
-	if (aenv->scriptenv->mailbox_autosubscribe)
+	if (eenv->scriptenv->mailbox_autosubscribe)
 		flags |= MAILBOX_FLAG_AUTO_SUBSCRIBE;
 	*box_r = box =
-		mailbox_alloc_delivery(aenv->scriptenv->user, mailbox, flags);
+		mailbox_alloc_delivery(eenv->scriptenv->user, mailbox, flags);
 	*storage = mailbox_get_storage(box);
 
 	if (mailbox_open(box) == 0)
@@ -406,7 +409,8 @@ act_store_start(const struct sieve_action *action,
 {
 	struct act_store_context *ctx =
 		(struct act_store_context *)action->context;
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct act_store_transaction *trans;
 	struct mailbox *box = NULL;
 	pool_t pool = sieve_result_pool(aenv->result);
@@ -521,10 +525,11 @@ static int
 act_store_execute(const struct sieve_action *action,
 		  const struct sieve_action_exec_env *aenv, void *tr_context)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct act_store_transaction *trans =
 		(struct act_store_transaction *)tr_context;
 	struct mail *mail = (action->mail != NULL ?
-			     action->mail : aenv->msgdata->mail);
+			     action->mail : eenv->msgdata->mail);
 	struct mail_save_context *save_ctx;
 	struct mail_keywords *keywords = NULL;
 	bool backends_equal = FALSE;
@@ -576,14 +581,14 @@ act_store_execute(const struct sieve_action *action,
 
 			if (keywords != NULL) {
 				if (!have_equal_keywords(mail, keywords)) {
-					aenv->exec_status->significant_action_executed = TRUE;
+					eenv->exec_status->significant_action_executed = TRUE;
 					mail_update_keywords(mail, MODIFY_REPLACE, keywords);
 				}
 				mailbox_keywords_unref(&keywords);
 			}
 
 			if ((mail_get_flags(mail) & MAIL_FLAGS_NONRECENT) != trans->flags) {
-				aenv->exec_status->significant_action_executed = TRUE;
+				eenv->exec_status->significant_action_executed = TRUE;
 				mail_update_flags(mail, MODIFY_REPLACE, trans->flags);
 			}
 		}
@@ -594,23 +599,24 @@ act_store_execute(const struct sieve_action *action,
 	 * mailbox, unrelated to the orignal mail, so this case needs to be handled
 	 * separately.
 	 */
-	} else if (mail != aenv->msgdata->mail &&
-		   mailbox_is_readonly(aenv->msgdata->mail->box) &&
+	} else if (mail != eenv->msgdata->mail &&
+		   mailbox_is_readonly(eenv->msgdata->mail->box) &&
 		   (mailbox_backends_equal(trans->box,
-					   aenv->msgdata->mail->box))) {
+					   eenv->msgdata->mail->box))) {
+
 		trans->redundant = TRUE;
 		return SIEVE_EXEC_OK;
 	}
 
 	/* Mark attempt to store in default mailbox */
 	if (strcmp(trans->context->mailbox,
-		   SIEVE_SCRIPT_DEFAULT_MAILBOX(aenv->scriptenv)) == 0)
-		aenv->exec_status->tried_default_save = TRUE;
+		   SIEVE_SCRIPT_DEFAULT_MAILBOX(eenv->scriptenv)) == 0)
+		eenv->exec_status->tried_default_save = TRUE;
 
 	/* Mark attempt to use storage. Can only get here when all previous actions
 	 * succeeded.
 	 */
-	aenv->exec_status->last_storage = mailbox_get_storage(trans->box);
+	eenv->exec_status->last_storage = mailbox_get_storage(trans->box);
 
 	/* Start mail transaction */
 	trans->mail_trans = mailbox_transaction_begin(
@@ -625,7 +631,7 @@ act_store_execute(const struct sieve_action *action,
 						     trans->box, FALSE);
 
 		if (trans->flags != 0 || keywords != NULL) {
-			aenv->exec_status->significant_action_executed = TRUE;
+			eenv->exec_status->significant_action_executed = TRUE;
 			mailbox_save_set_flags(save_ctx, trans->flags, keywords);
 		}
 	} else {
@@ -637,7 +643,7 @@ act_store_execute(const struct sieve_action *action,
 		status = (trans->error_code == MAIL_ERROR_TEMP ?
 			  SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE);
 	} else {
-		aenv->exec_status->significant_action_executed = TRUE;
+		eenv->exec_status->significant_action_executed = TRUE;
 	}
 
 	/* Deallocate keywords */
@@ -720,6 +726,7 @@ act_store_commit(const struct sieve_action *action ATTR_UNUSED,
 		 const struct sieve_action_exec_env *aenv, void *tr_context,
 		 bool *keep)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct act_store_transaction *trans =
 		(struct act_store_transaction *)tr_context;
 	bool status = TRUE;
@@ -737,8 +744,8 @@ act_store_commit(const struct sieve_action *action ATTR_UNUSED,
 		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;
+		eenv->exec_status->keep_original = TRUE;
+		eenv->exec_status->message_saved = TRUE;
 		if (trans->box != NULL)
 			mailbox_free(&trans->box);
 		return SIEVE_EXEC_OK;
@@ -747,16 +754,16 @@ act_store_commit(const struct sieve_action *action ATTR_UNUSED,
 	/* Mark attempt to use storage. Can only get here when all previous actions
 	 * succeeded.
 	 */
-	aenv->exec_status->last_storage = mailbox_get_storage(trans->box);
+	eenv->exec_status->last_storage = mailbox_get_storage(trans->box);
 
 	/* Commit mailbox transaction */
 	status = (mailbox_transaction_commit(&trans->mail_trans) == 0);
 
 	/* Note the fact that the message was stored at least once */
 	if (status)
-		aenv->exec_status->message_saved = TRUE;
+		eenv->exec_status->message_saved = TRUE;
 	else
-		aenv->exec_status->store_failed = TRUE;
+		eenv->exec_status->store_failed = TRUE;
 
 	/* Log our status */
 	act_store_log_status(trans, aenv, FALSE, status);
@@ -780,6 +787,7 @@ act_store_rollback(const struct sieve_action *action ATTR_UNUSED,
 		   const struct sieve_action_exec_env *aenv, void *tr_context,
 		   bool success)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct act_store_transaction *trans =
 		(struct act_store_transaction *)tr_context;
 
@@ -789,9 +797,9 @@ act_store_rollback(const struct sieve_action *action ATTR_UNUSED,
 	i_assert(trans->box != NULL);
 
 	if (!success) {
-		aenv->exec_status->last_storage =
+		eenv->exec_status->last_storage =
 			mailbox_get_storage(trans->box);
-		aenv->exec_status->store_failed = TRUE;
+		eenv->exec_status->store_failed = TRUE;
 	}
 
 	/* Log status */
@@ -813,7 +821,8 @@ int sieve_act_redirect_add_to_result(const struct sieve_runtime_env *renv,
 				     struct sieve_side_effects_list *seffects,
 				     const struct smtp_address *to_address)
 {
-	struct sieve_instance *svinst = renv->svinst;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
 	struct act_redirect_context *act;
 	pool_t pool;
 
@@ -873,9 +882,10 @@ sieve_action_do_reject_mail(const struct sieve_action_exec_env *aenv,
 			    const struct smtp_address *recipient,
 			    const char *reason)
 {
-	struct sieve_instance *svinst = aenv->svinst;
-	const struct sieve_script_env *senv = aenv->scriptenv;
-	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	struct sieve_instance *svinst = eenv->svinst;
+	const struct sieve_script_env *senv = eenv->scriptenv;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	const struct smtp_address *sender, *orig_recipient;
 	struct istream *input;
 	struct ostream *output;
@@ -1000,7 +1010,8 @@ int sieve_action_reject_mail(const struct sieve_action_exec_env *aenv,
 			     const struct smtp_address *recipient,
 			     const char *reason)
 {
-	const struct sieve_script_env *senv = aenv->scriptenv;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	int result;
 
 	T_BEGIN {
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index 838700d20..5dd55c4bd 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -8,22 +8,19 @@
 #include "sieve-common.h"
 #include "sieve-objects.h"
 #include "sieve-extensions.h"
+#include "sieve-execute.h"
 
 /*
  * Action execution environment
  */
 
 struct sieve_action_exec_env {
-	struct sieve_instance *svinst;
+	const struct sieve_execute_env *exec_env;
 
 	struct sieve_result *result;
-	enum sieve_execute_flags flags;
 	struct sieve_error_handler *ehandler;
 
-	const struct sieve_message_data *msgdata;
 	struct sieve_message_context *msgctx;
-	const struct sieve_script_env *scriptenv;
-	struct sieve_exec_status *exec_status;
 };
 
 const char *sieve_action_get_location(const struct sieve_action_exec_env *aenv);
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index 432437787..6e3de82b6 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -93,6 +93,9 @@ struct sieve_binary_block;
 struct sieve_binary_debug_writer;
 struct sieve_binary_debug_reader;
 
+/* sieve-execute.h */
+struct sieve_execute;
+
 /* sieve-objects.h */
 struct sieve_object_def;
 struct sieve_object;
diff --git a/src/lib-sieve/sieve-execute.h b/src/lib-sieve/sieve-execute.h
new file mode 100644
index 000000000..899ed8dff
--- /dev/null
+++ b/src/lib-sieve/sieve-execute.h
@@ -0,0 +1,16 @@
+#ifndef SIEVE_EXECUTE_H
+#define SIEVE_EXECUTE_H
+
+#include "sieve-common.h"
+
+struct sieve_execute_env {
+	struct sieve_instance *svinst;
+	enum sieve_execute_flags flags;
+
+	const struct sieve_message_data *msgdata;
+	const struct sieve_script_env *scriptenv;
+
+	struct sieve_exec_status *exec_status;
+};
+
+#endif
diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index cb79a7567..04f286409 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -94,11 +94,10 @@ _sieve_interpreter_create(struct sieve_binary *sbin,
 			  struct sieve_binary_block *sblock,
 			  struct sieve_script *script,
 			  struct sieve_interpreter *parent,
-			  const struct sieve_message_data *msgdata,
-			  const struct sieve_script_env *senv,
-			  struct sieve_error_handler *ehandler,
-			  enum sieve_execute_flags flags) ATTR_NULL(3, 4)
+			  const struct sieve_execute_env *eenv,
+			  struct sieve_error_handler *ehandler) ATTR_NULL(3, 4)
 {
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	unsigned int i, ext_count;
 	struct sieve_interpreter *interp;
 	pool_t pool;
@@ -116,19 +115,15 @@ _sieve_interpreter_create(struct sieve_binary *sbin,
 	interp->runenv.ehandler = ehandler;
 	sieve_error_handler_ref(ehandler);
 
+	interp->runenv.exec_env = eenv;
 	interp->runenv.interp = interp;
 	interp->runenv.oprtn = &interp->oprtn;
 	interp->runenv.sbin = sbin;
 	interp->runenv.sblock = sblock;
-	interp->runenv.flags = flags;
 	sieve_binary_ref(sbin);
 
 	svinst = sieve_binary_svinst(sbin);
 
-	interp->runenv.svinst = svinst;
-	interp->runenv.msgdata = msgdata;
-	interp->runenv.scriptenv = senv;
-
 	if (senv->trace_log != NULL) {
 		interp->trace.log = senv->trace_log;
 		interp->trace.config = senv->trace_config;
@@ -136,13 +131,6 @@ _sieve_interpreter_create(struct sieve_binary *sbin,
 		interp->runenv.trace = &interp->trace;
 	}
 
-	if (senv->exec_status == NULL) {
-		interp->runenv.exec_status =
-			p_new(interp->pool, struct sieve_exec_status, 1);
-	} else {
-		interp->runenv.exec_status = senv->exec_status;
-	}
-
 	if (script == NULL)
 		interp->runenv.script = sieve_binary_script(sbin);
 	else
@@ -217,7 +205,7 @@ _sieve_interpreter_create(struct sieve_binary *sbin,
 
 			if (ext->def != NULL) {
 				if (ext->global &&
-				    (flags & SIEVE_EXECUTE_FLAG_NOGLOBAL) != 0) {
+				    (eenv->flags & SIEVE_EXECUTE_FLAG_NOGLOBAL) != 0) {
 					sieve_runtime_error(&interp->runenv, NULL,
 						"failed to enable extension `%s': "
 						"its use is restricted to global scripts",
@@ -251,10 +239,8 @@ _sieve_interpreter_create(struct sieve_binary *sbin,
 struct sieve_interpreter *
 sieve_interpreter_create(struct sieve_binary *sbin,
 			 struct sieve_interpreter *parent,
-			 const struct sieve_message_data *msgdata,
-			 const struct sieve_script_env *senv,
-			 struct sieve_error_handler *ehandler,
-			 enum sieve_execute_flags flags)
+			 const struct sieve_execute_env *eenv,
+			 struct sieve_error_handler *ehandler)
 {
 	struct sieve_binary_block *sblock;
 
@@ -262,24 +248,22 @@ sieve_interpreter_create(struct sieve_binary *sbin,
 		sbin, SBIN_SYSBLOCK_MAIN_PROGRAM)) == NULL)
 		return NULL;
 
- 	return _sieve_interpreter_create(sbin, sblock, NULL, parent, msgdata,
-					 senv, ehandler, flags);
+	return _sieve_interpreter_create(sbin, sblock, NULL, parent, eenv,
+					 ehandler);
 }
 
 struct sieve_interpreter *
 sieve_interpreter_create_for_block(struct sieve_binary_block *sblock,
 				   struct sieve_script *script,
 				   struct sieve_interpreter *parent,
-				   const struct sieve_message_data *msgdata,
-				   const struct sieve_script_env *senv,
-				   struct sieve_error_handler *ehandler,
-				   enum sieve_execute_flags flags)
+				   const struct sieve_execute_env *eenv,
+				   struct sieve_error_handler *ehandler)
 {
 	if (sblock == NULL) return NULL;
 
- 	return _sieve_interpreter_create(sieve_binary_block_get_binary(sblock),
-					 sblock, script, parent, msgdata,
-					 senv, ehandler, flags);
+	return _sieve_interpreter_create(sieve_binary_block_get_binary(sblock),
+					 sblock, script, parent, eenv,
+					 ehandler);
 }
 
 void sieve_interpreter_free(struct sieve_interpreter **_interp)
@@ -345,7 +329,7 @@ sieve_interpreter_get_error_handler(struct sieve_interpreter *interp)
 struct sieve_instance *
 sieve_interpreter_svinst(struct sieve_interpreter *interp)
 {
-	return interp->runenv.svinst;
+	return interp->runenv.exec_env->svinst;
 }
 
 /* Do not use this function for normal sieve extensions. This is intended for
@@ -1031,6 +1015,7 @@ void sieve_runtime_critical(const struct sieve_runtime_env *renv,
 			    const char *location, const char *user_prefix,
 			    const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_ERROR,
 		.csrc = {
@@ -1049,7 +1034,7 @@ void sieve_runtime_critical(const struct sieve_runtime_env *renv,
 				sieve_runtime_get_full_command_location(renv);
 		}
 
-		sieve_criticalv(renv->svinst, renv->ehandler, &params,
+		sieve_criticalv(eenv->svinst, renv->ehandler, &params,
 				user_prefix, fmt, args);
 	} T_END;
 
diff --git a/src/lib-sieve/sieve-interpreter.h b/src/lib-sieve/sieve-interpreter.h
index 2a81c8d05..89b98e523 100644
--- a/src/lib-sieve/sieve-interpreter.h
+++ b/src/lib-sieve/sieve-interpreter.h
@@ -15,18 +15,14 @@
 struct sieve_interpreter *
 sieve_interpreter_create(struct sieve_binary *sbin,
 			 struct sieve_interpreter *parent,
-			 const struct sieve_message_data *msgdata,
-			 const struct sieve_script_env *senv,
-			 struct sieve_error_handler *ehandler,
-			 enum sieve_execute_flags flags) ATTR_NULL(2);
+			 const struct sieve_execute_env *eenv,
+			 struct sieve_error_handler *ehandler) ATTR_NULL(2);
 struct sieve_interpreter *
 sieve_interpreter_create_for_block(struct sieve_binary_block *sblock,
 				   struct sieve_script *script,
 				   struct sieve_interpreter *parent,
-				   const struct sieve_message_data *msgdata,
-				   const struct sieve_script_env *senv,
-				   struct sieve_error_handler *ehandler,
-				   enum sieve_execute_flags flags)
+				   const struct sieve_execute_env *eenv,
+				   struct sieve_error_handler *ehandler)
 				   ATTR_NULL(3);
 void sieve_interpreter_free(struct sieve_interpreter **_interp);
 
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index 9cecc5a12..24df20a7b 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -91,14 +91,15 @@ struct sieve_result {
 };
 
 struct sieve_result *
-sieve_result_create(struct sieve_instance *svinst,
-		    const struct sieve_message_data *msgdata,
-		    const struct sieve_script_env *senv)
+sieve_result_create(struct sieve_instance *svinst, pool_t pool,
+		    const struct sieve_execute_env *eenv)
 {
-	pool_t pool;
+	const struct sieve_script_env *senv = eenv->scriptenv;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 	struct sieve_result *result;
 
-	pool = pool_alloconly_create("sieve_result", 4096);
+	pool_ref(pool);
+
 	result = p_new(pool, struct sieve_result, 1);
 	result->refcount = 1;
 	result->pool = pool;
@@ -106,10 +107,8 @@ sieve_result_create(struct sieve_instance *svinst,
 
 	p_array_init(&result->ext_contexts, pool, 4);
 
-	result->action_env.svinst = svinst;
 	result->action_env.result = result;
-	result->action_env.scriptenv = senv;
-	result->action_env.msgdata = msgdata;
+	result->action_env.exec_env = eenv;
 	result->action_env.msgctx =
 		sieve_message_context_create(svinst, senv->user, msgdata);
 
@@ -161,13 +160,13 @@ pool_t sieve_result_pool(struct sieve_result *result)
 const struct sieve_script_env *
 sieve_result_get_script_env(struct sieve_result *result)
 {
-	return result->action_env.scriptenv;
+	return result->action_env.exec_env->scriptenv;
 }
 
 const struct sieve_message_data *
 sieve_result_get_message_data(struct sieve_result *result)
 {
-	return result->action_env.msgdata;
+	return result->action_env.exec_env->msgdata;
 }
 
 struct sieve_message_context *
@@ -370,7 +369,7 @@ _sieve_result_add_action(const struct sieve_runtime_env *renv,
 {
 	int ret = 0;
 	unsigned int instance_count = 0;
-	struct sieve_instance *svinst = renv->svinst;
+	struct sieve_instance *svinst = renv->exec_env->svinst;
 	struct sieve_result *result = renv->result;
 	struct sieve_result_action *raction = NULL, *kaction = NULL;
 	struct sieve_action action;
@@ -831,29 +830,23 @@ bool sieve_result_print(struct sieve_result *result,
 
 static void
 _sieve_result_prepare_execution(struct sieve_result *result,
-				struct sieve_error_handler *ehandler,
-				enum sieve_execute_flags flags)
+				struct sieve_error_handler *ehandler)
 {
-	const struct sieve_script_env *senv = result->action_env.scriptenv;
-
-	result->action_env.flags = flags;
 	result->action_env.ehandler = ehandler;
-	result->action_env.exec_status =
-		(senv->exec_status == NULL ?
-		 t_new(struct sieve_exec_status, 1) : senv->exec_status);
 }
 
 static int
 _sieve_result_implicit_keep(struct sieve_result *result, bool rollback)
 {
 	const struct sieve_action_exec_env *aenv = &result->action_env;
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_result_action *rac, *kac;
 	int status = SIEVE_EXEC_OK;
 	struct sieve_result_side_effect *rsef, *rsef_first = NULL;
 	void *tr_context = NULL;
 	struct sieve_action act_keep;
 
-	if ((aenv->flags & SIEVE_EXECUTE_FLAG_DEFER_KEEP) != 0)
+	if ((eenv->flags & SIEVE_EXECUTE_FLAG_DEFER_KEEP) != 0)
 		return SIEVE_EXEC_OK;
 
 	if (rollback)
@@ -883,7 +876,7 @@ _sieve_result_implicit_keep(struct sieve_result *result, bool rollback)
 		while (rac != NULL) {
 			if (rac->action.def == act_keep.def &&
 			    act_keep.def->equals != NULL &&
-			    act_keep.def->equals(aenv->scriptenv, NULL,
+			    act_keep.def->equals(eenv->scriptenv, NULL,
 						 &rac->action) &&
 			    rac->action.executed)
 				return SIEVE_EXEC_OK;
@@ -988,11 +981,11 @@ _sieve_result_implicit_keep(struct sieve_result *result, bool rollback)
 
 int sieve_result_implicit_keep(struct sieve_result *result,
 			       struct sieve_error_handler *ehandler,
-			       enum sieve_execute_flags flags, bool success)
+			       bool success)
 {
 	int ret;
 
-	_sieve_result_prepare_execution(result, ehandler, flags);
+	_sieve_result_prepare_execution(result, ehandler);
 
 	ret = _sieve_result_implicit_keep(result, !success);
 
@@ -1035,7 +1028,8 @@ sieve_result_transaction_start(struct sieve_result *result,
 			       struct sieve_result_action *first,
 			       struct sieve_result_action **last_r)
 {
-	const struct sieve_script_env *senv = result->action_env.scriptenv;
+	const struct sieve_execute_env *eenv = result->action_env.exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct sieve_result_action *rac = first;
 	int status = SIEVE_EXEC_OK;
 	bool dup_flushed = FALSE;
@@ -1327,8 +1321,7 @@ sieve_result_transaction_finish(struct sieve_result *result,
 }
 
 int sieve_result_execute(struct sieve_result *result, bool *keep,
-			 struct sieve_error_handler *ehandler,
-			 enum sieve_execute_flags flags)
+			 struct sieve_error_handler *ehandler)
 {
 	int status = SIEVE_EXEC_OK, result_status;
 	struct sieve_result_action *first_action, *last_action;
@@ -1340,7 +1333,7 @@ int sieve_result_execute(struct sieve_result *result, bool *keep,
 
 	/* Prepare environment */
 
-	_sieve_result_prepare_execution(result, ehandler, flags);
+	_sieve_result_prepare_execution(result, ehandler);
 
 	/* Make notice of this attempt */
 
@@ -1552,6 +1545,7 @@ void sieve_result_global_error(const struct sieve_action_exec_env *aenv,
 			       const char *csrc_filename,
 			       unsigned int csrc_linenum, const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_ERROR,
 		.csrc = {
@@ -1562,7 +1556,7 @@ void sieve_result_global_error(const struct sieve_action_exec_env *aenv,
 	va_list args;
 
 	va_start(args, fmt);
-	sieve_global_logv(aenv->svinst, aenv->ehandler, &params, fmt, args);
+	sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
 	va_end(args);
 }
 
@@ -1591,6 +1585,7 @@ void sieve_result_global_warning(const struct sieve_action_exec_env *aenv,
 				 unsigned int csrc_linenum,
 				 const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_WARNING,
 		.csrc = {
@@ -1601,7 +1596,7 @@ void sieve_result_global_warning(const struct sieve_action_exec_env *aenv,
 	va_list args;
 
 	va_start(args, fmt);
-	sieve_global_logv(aenv->svinst, aenv->ehandler, &params, fmt, args);
+	sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
 	va_end(args);
 }
 
@@ -1629,6 +1624,7 @@ void sieve_result_global_log(const struct sieve_action_exec_env *aenv,
 			     const char *csrc_filename,
 			     unsigned int csrc_linenum, const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_INFO,
 		.csrc = {
@@ -1639,7 +1635,7 @@ void sieve_result_global_log(const struct sieve_action_exec_env *aenv,
 	va_list args;
 
 	va_start(args, fmt);
-	sieve_global_logv(aenv->svinst, aenv->ehandler, &params, fmt, args);
+	sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
 	va_end(args);
 }
 
@@ -1649,6 +1645,7 @@ void sieve_result_global_log_error(const struct sieve_action_exec_env *aenv,
 				   unsigned int csrc_linenum,
 				   const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_ERROR,
 		.csrc = {
@@ -1659,7 +1656,7 @@ void sieve_result_global_log_error(const struct sieve_action_exec_env *aenv,
 	va_list args;
 
 	va_start(args, fmt);
-	sieve_global_info_logv(aenv->svinst, aenv->ehandler, &params,
+	sieve_global_info_logv(eenv->svinst, aenv->ehandler, &params,
 			       fmt, args);
 	va_end(args);
 }
@@ -1670,6 +1667,7 @@ void sieve_result_global_log_warning(const struct sieve_action_exec_env *aenv,
 				     unsigned int csrc_linenum,
 				     const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_WARNING,
 		.csrc = {
@@ -1680,7 +1678,7 @@ void sieve_result_global_log_warning(const struct sieve_action_exec_env *aenv,
 	va_list args;
 
 	va_start(args, fmt);
-	sieve_global_info_logv(aenv->svinst, aenv->ehandler, &params,
+	sieve_global_info_logv(eenv->svinst, aenv->ehandler, &params,
 			       fmt, args);
 	va_end(args);
 }
@@ -1690,6 +1688,7 @@ void sieve_result_critical(const struct sieve_action_exec_env *aenv,
 			   const char *csrc_filename, unsigned int csrc_linenum,
 			   const char *user_prefix, const char *fmt, ...)
 {
+	const struct sieve_execute_env *eenv = aenv->exec_env;
 	struct sieve_error_params params = {
 		.log_type = LOG_TYPE_ERROR,
 		.csrc = {
@@ -1702,7 +1701,7 @@ void sieve_result_critical(const struct sieve_action_exec_env *aenv,
 	va_start(args, fmt);
 
 	T_BEGIN {
-		sieve_criticalv(aenv->svinst, aenv->ehandler, &params,
+		sieve_criticalv(eenv->svinst, aenv->ehandler, &params,
 				user_prefix, fmt, args);
 	} T_END;
 
diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h
index 5d7f4ecf5..ed699aedc 100644
--- a/src/lib-sieve/sieve-result.h
+++ b/src/lib-sieve/sieve-result.h
@@ -3,6 +3,7 @@
 
 #include "sieve-common.h"
 #include "sieve-error.h"
+#include "sieve-execute.h"
 
 /*
  * Types
@@ -17,9 +18,8 @@ struct sieve_side_effects_list;
 struct sieve_result;
 
 struct sieve_result *
-sieve_result_create(struct sieve_instance *svinst,
-		    const struct sieve_message_data *msgdata,
-		    const struct sieve_script_env *senv);
+sieve_result_create(struct sieve_instance *svinst, pool_t pool,
+		    const struct sieve_execute_env *eenv);
 
 void sieve_result_ref(struct sieve_result *result);
 
@@ -103,13 +103,12 @@ void sieve_result_set_failure_action(struct sieve_result *result,
 
 int sieve_result_implicit_keep(struct sieve_result *result,
 			       struct sieve_error_handler *ehandler,
-			       enum sieve_execute_flags flags, bool success);
+			       bool success);
 
 void sieve_result_mark_executed(struct sieve_result *result);
 
 int sieve_result_execute(struct sieve_result *result, bool *keep,
-			 struct sieve_error_handler *ehandler,
-			 enum sieve_execute_flags flags);
+			 struct sieve_error_handler *ehandler);
 
 bool sieve_result_executed(struct sieve_result *result);
 
diff --git a/src/lib-sieve/sieve-runtime.h b/src/lib-sieve/sieve-runtime.h
index 1657641f2..8efe23d7c 100644
--- a/src/lib-sieve/sieve-runtime.h
+++ b/src/lib-sieve/sieve-runtime.h
@@ -2,22 +2,21 @@
 #define SIEVE_RUNTIME_H
 
 #include "sieve-common.h"
+#include "sieve-execute.h"
 
 /*
  * Runtime environment
  */
 
 struct sieve_runtime_env {
+	const struct sieve_execute_env *exec_env;
+
 	/* Interpreter */
-	struct sieve_instance *svinst;
 	struct sieve_interpreter *interp;
-	enum sieve_execute_flags flags;
 	struct sieve_error_handler *ehandler;
 
 	/* Executing script */
 	struct sieve_script *script;
-	const struct sieve_script_env *scriptenv;
-	struct sieve_exec_status *exec_status;
 
 	/* Executing binary */
 	struct sieve_binary *sbin;
@@ -28,7 +27,6 @@ struct sieve_runtime_env {
 	const struct sieve_operation *oprtn;
 
 	/* Tested message */
-	const struct sieve_message_data *msgdata;
 	struct sieve_message_context *msgctx;
 
 	/* Filter result */
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 647dd9a9f..626d434e5 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -333,32 +333,20 @@ struct sieve_binary *sieve_compile(struct sieve_instance *svinst,
  * Sieve runtime
  */
 
-static int sieve_run(struct sieve_binary *sbin, struct sieve_result **result,
-		     const struct sieve_message_data *msgdata,
-		     const struct sieve_script_env *senv,
-		     struct sieve_error_handler *ehandler,
-		     enum sieve_execute_flags flags)
+static int sieve_run(struct sieve_binary *sbin, struct sieve_result *result,
+		     struct sieve_execute_env *eenv,
+		     struct sieve_error_handler *ehandler)
 {
 	struct sieve_interpreter *interp;
 	int ret = 0;
 
 	/* Create the interpreter */
-	if ((interp = sieve_interpreter_create(sbin, NULL, msgdata, senv,
-					       ehandler, flags)) == NULL)
+	if ((interp = sieve_interpreter_create(
+		sbin, NULL, eenv, ehandler)) == NULL)
 		return SIEVE_EXEC_BIN_CORRUPT;
 
-	/* Reset execution status */
-	if (senv->exec_status != NULL)
-		i_zero(senv->exec_status);
-
-	/* Create result object */
-	if (*result == NULL) {
-		*result = sieve_result_create(sieve_binary_svinst(sbin),
-					      msgdata, senv);
-	}
-
 	/* Run the interpreter */
-	ret = sieve_interpreter_run(interp, *result);
+	ret = sieve_interpreter_run(interp, result);
 
 	/* Free the interpreter */
 	sieve_interpreter_free(&interp);
@@ -526,13 +514,31 @@ int sieve_test(struct sieve_binary *sbin,
 	       struct sieve_error_handler *ehandler, struct ostream *stream,
 	       enum sieve_execute_flags flags, bool *keep)
 {
-	struct sieve_result *result = NULL;
+	struct sieve_result *result;
+	struct sieve_execute_env eenv;
+	struct sieve_exec_status exec_status;
+	pool_t pool;
 	int ret;
 
+	pool = pool_alloconly_create("sieve execution", 4096);
+
+	i_zero(&eenv);
+	eenv.flags = flags;
+	eenv.msgdata = msgdata;
+	eenv.scriptenv = senv;
+
+	eenv.exec_status = senv->exec_status;
+	if (eenv.exec_status == NULL)
+		eenv.exec_status = &exec_status;
+	i_zero(eenv.exec_status);
+
 	if (keep != NULL) *keep = FALSE;
 
+	/* Create result object */
+	result = sieve_result_create(sieve_binary_svinst(sbin), pool, &eenv);
+
 	/* Run the script */
-	ret = sieve_run(sbin, &result, msgdata, senv, ehandler, flags);
+	ret = sieve_run(sbin, result, &eenv, ehandler);
 
 	/* Print result if successful */
 	if (ret > 0) {
@@ -545,6 +551,7 @@ int sieve_test(struct sieve_binary *sbin,
 	/* Cleanup */
 	if (result != NULL)
 		sieve_result_unref(&result);
+	pool_unref(&pool);
 
 	return ret;
 }
@@ -579,12 +586,30 @@ int sieve_execute(struct sieve_binary *sbin,
 		  enum sieve_execute_flags flags, bool *keep)
 {
 	struct sieve_result *result = NULL;
+	struct sieve_execute_env eenv;
+	struct sieve_exec_status exec_status;
+	pool_t pool;
 	int ret;
 
+	pool = pool_alloconly_create("sieve execution", 4096);
+
+	i_zero(&eenv);
+	eenv.flags = flags;
+	eenv.msgdata = msgdata;
+	eenv.scriptenv = senv;
+
+	eenv.exec_status = senv->exec_status;
+	if (eenv.exec_status == NULL)
+		eenv.exec_status = &exec_status;
+	i_zero(eenv.exec_status);
+
 	if (keep != NULL) *keep = FALSE;
 
+	/* Create result object */
+	result = sieve_result_create(sieve_binary_svinst(sbin), pool, &eenv);
+
 	/* Run the script */
-	ret = sieve_run(sbin, &result, msgdata, senv, exec_ehandler, flags);
+	ret = sieve_run(sbin, result, &eenv, exec_ehandler);
 
 	/* Evaluate status and execute the result:
 	   Strange situations, e.g. currupt binaries, must be handled by the
@@ -593,14 +618,13 @@ int sieve_execute(struct sieve_binary *sbin,
 	 */
 	if (ret > 0) {
 		/* Execute result */
-		ret = sieve_result_execute(result, keep,
-					   action_ehandler, flags);
+		ret = sieve_result_execute(result, keep, action_ehandler);
 	} else if (ret == SIEVE_EXEC_FAILURE) {
 		/* Perform implicit keep if script failed with a normal runtime
 		   error
 		 */
 		switch (sieve_result_implicit_keep(result, action_ehandler,
-						   flags, FALSE)) {
+						   FALSE)) {
 		case SIEVE_EXEC_OK:
 			if (keep != NULL) *keep = TRUE;
 			break;
@@ -615,6 +639,7 @@ int sieve_execute(struct sieve_binary *sbin,
 	/* Cleanup */
 	if (result != NULL)
 		sieve_result_unref(&result);
+	pool_unref(&pool);
 
 	return ret;
 }
@@ -624,10 +649,10 @@ int sieve_execute(struct sieve_binary *sbin,
  */
 
 struct sieve_multiscript {
-	struct sieve_instance *svinst;
+	pool_t pool;
+	struct sieve_execute_env exec_env;
+	struct sieve_exec_status exec_status;
 	struct sieve_result *result;
-	const struct sieve_message_data *msgdata;
-	const struct sieve_script_env *scriptenv;
 
 	int status;
 	bool keep;
@@ -647,16 +672,22 @@ sieve_multiscript_start_execute(struct sieve_instance *svinst,
 	struct sieve_result *result;
 	struct sieve_multiscript *mscript;
 
-	result = sieve_result_create(svinst, msgdata, senv);
-	pool = sieve_result_pool(result);
+	pool = pool_alloconly_create("sieve execution", 4096);
+	mscript = p_new(pool, struct sieve_multiscript, 1);
+	mscript->pool = pool;
+	mscript->exec_env.svinst = svinst;
+	mscript->exec_env.msgdata = msgdata;
+	mscript->exec_env.scriptenv = senv;
 
-	sieve_result_set_keep_action(result, NULL, NULL);
+	mscript->exec_env.exec_status = senv->exec_status;
+	if (mscript->exec_env.exec_status == NULL)
+		mscript->exec_env.exec_status = &mscript->exec_status;
+	i_zero(mscript->exec_env.exec_status);
 
-	mscript = p_new(pool, struct sieve_multiscript, 1);
-	mscript->svinst = svinst;
+	result = sieve_result_create(svinst, pool, &mscript->exec_env);
+	sieve_result_set_keep_action(result, NULL, NULL);
 	mscript->result = result;
-	mscript->msgdata = msgdata;
-	mscript->scriptenv = senv;
+
 	mscript->status = SIEVE_EXEC_OK;
 	mscript->active = TRUE;
 	mscript->keep = TRUE;
@@ -681,9 +712,11 @@ sieve_multiscript_start_test(struct sieve_instance *svinst,
 static void
 sieve_multiscript_test(struct sieve_multiscript *mscript, bool *keep)
 {
+	const struct sieve_script_env *senv = mscript->exec_env.scriptenv;
+
 	if (mscript->status > 0) {
 		mscript->status =
-			(sieve_result_print(mscript->result, mscript->scriptenv,
+			(sieve_result_print(mscript->result, senv,
 					    mscript->teststream, keep) ?
 			 SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE);
 	} else {
@@ -698,12 +731,14 @@ static void sieve_multiscript_execute(struct sieve_multiscript *mscript,
 				      enum sieve_execute_flags flags,
 				      bool *keep)
 {
+	mscript->exec_env.flags = flags;
+
 	if (mscript->status > 0) {
 		mscript->status = sieve_result_execute(mscript->result, keep,
-						       ehandler, flags);
+						       ehandler);
 	} else {
 		if (sieve_result_implicit_keep(mscript->result, ehandler,
-					       flags, FALSE) <= 0)
+					       FALSE) <= 0)
 			mscript->status = SIEVE_EXEC_KEEP_FAILED;
 		else
 			if (keep != NULL) *keep = TRUE;
@@ -719,8 +754,9 @@ bool sieve_multiscript_run(struct sieve_multiscript *mscript,
 	if (!mscript->active) return FALSE;
 
 	/* Run the script */
-	mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata,
-				    mscript->scriptenv, exec_ehandler, flags);
+	mscript->exec_env.flags = flags;
+	mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env,
+				    exec_ehandler);
 
 	if (mscript->status >= 0) {
 		mscript->keep = FALSE;
@@ -763,8 +799,9 @@ void sieve_multiscript_run_discard(struct sieve_multiscript *mscript,
 
 	/* Run the discard script */
 	flags |= SIEVE_EXECUTE_FLAG_DEFER_KEEP;
-	mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata,
-				    mscript->scriptenv, exec_ehandler, flags);
+	mscript->exec_env.flags = flags;
+	mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env,
+				    exec_ehandler);
 
 	if (mscript->status >= 0) {
 		mscript->keep = FALSE;
@@ -796,6 +833,7 @@ int sieve_multiscript_tempfail(struct sieve_multiscript **_mscript,
 	struct sieve_result *result = mscript->result;
 	int ret = mscript->status;
 
+	mscript->exec_env.flags = flags;
 	sieve_result_set_keep_action(mscript->result, NULL, &act_store);
 
 	if (mscript->active) {
@@ -807,7 +845,7 @@ int sieve_multiscript_tempfail(struct sieve_multiscript **_mscript,
 			   back to to implicit keep (FIXME)
 			 */
 			switch (sieve_result_implicit_keep(
-				result, action_ehandler, flags, FALSE)) {
+				result, action_ehandler, FALSE)) {
 			case SIEVE_EXEC_OK:
 				ret = SIEVE_EXEC_FAILURE;
 				break;
@@ -819,6 +857,7 @@ int sieve_multiscript_tempfail(struct sieve_multiscript **_mscript,
 
 	/* Cleanup */
 	sieve_result_unref(&result);
+	pool_unref(&mscript->pool);
 	*_mscript = NULL;
 
 	return ret;
@@ -832,6 +871,7 @@ int sieve_multiscript_finish(struct sieve_multiscript **_mscript,
 	struct sieve_result *result = mscript->result;
 	int ret = mscript->status;
 
+	mscript->exec_env.flags = flags;
 	sieve_result_set_keep_action(mscript->result, NULL, &act_store);
 
 	if (mscript->active) {
@@ -839,7 +879,7 @@ int sieve_multiscript_finish(struct sieve_multiscript **_mscript,
 			mscript->keep = TRUE;
 		else {
 			switch (sieve_result_implicit_keep(
-				result, action_ehandler, flags, TRUE)) {
+				result, action_ehandler, TRUE)) {
 			case SIEVE_EXEC_OK:
 				mscript->keep = TRUE;
 				break;
@@ -859,6 +899,7 @@ int sieve_multiscript_finish(struct sieve_multiscript **_mscript,
 
 	/* Cleanup */
 	sieve_result_unref(&result);
+	pool_unref(&mscript->pool);
 	*_mscript = NULL;
 	return ret;
 }
diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c
index acc3bd994..d9371e780 100644
--- a/src/lib-sieve/tst-size.c
+++ b/src/lib-sieve/tst-size.c
@@ -257,6 +257,7 @@ static int
 tst_size_operation_execute(const struct sieve_runtime_env *renv,
 			   sieve_size_t *address)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	sieve_number_t mail_size, limit;
 	int ret;
 
@@ -275,7 +276,7 @@ tst_size_operation_execute(const struct sieve_runtime_env *renv,
 	/* Get the size of the message */
 	if (!tst_size_get(renv, &mail_size)) {
 		/* FIXME: improve this error */
-		e_error(renv->svinst->event, "failed to assess message size");
+		e_error(eenv->svinst->event, "failed to assess message size");
 		return SIEVE_EXEC_FAILURE;
 	}
 
diff --git a/src/plugins/imapsieve/ext-imapsieve-environment.c b/src/plugins/imapsieve/ext-imapsieve-environment.c
index 9d9a9e884..2b61cd4a2 100644
--- a/src/plugins/imapsieve/ext-imapsieve-environment.c
+++ b/src/plugins/imapsieve/ext-imapsieve-environment.c
@@ -25,7 +25,9 @@ static const char *
 envit_imap_user_get_value(const struct sieve_runtime_env *renv,
 			  const char *name ATTR_UNUSED)
 {
-	return renv->svinst->username;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+
+	return eenv->svinst->username;
 }
 
 const struct sieve_environment_item imap_user_env_item = {
@@ -39,8 +41,9 @@ static const char *
 envit_imap_email_get_value(const struct sieve_runtime_env *renv,
 			   const char *name ATTR_UNUSED)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct smtp_address *user_email =
-		sieve_get_user_email(renv->svinst);
+		sieve_get_user_email(eenv->svinst);
 
 	if (user_email == NULL)
 		return NULL;
@@ -58,7 +61,8 @@ static const char *
 envit_imap_cause_get_value(const struct sieve_runtime_env *renv,
 			   const char *name ATTR_UNUSED)
 {
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct imap_sieve_context *isctx =
 		(struct imap_sieve_context *)senv->script_context;
 
@@ -76,7 +80,8 @@ static const char *
 envit_imap_mailbox_get_value(const struct sieve_runtime_env *renv,
 			     const char *name ATTR_UNUSED)
 {
-	const struct sieve_message_data *msgdata = renv->msgdata;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_message_data *msgdata = eenv->msgdata;
 
 	return mailbox_get_vname(msgdata->mail->box);
 }
@@ -93,7 +98,8 @@ static const char *
 envit_imap_changedflags_get_value(const struct sieve_runtime_env *renv,
 				  const char *name ATTR_UNUSED)
 {
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct imap_sieve_context *isctx =
 		(struct imap_sieve_context *)senv->script_context;
 
@@ -111,7 +117,8 @@ static const char *
 envit_vnd_mailbox_from_get_value(const struct sieve_runtime_env *renv,
 				 const char *name ATTR_UNUSED)
 {
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct imap_sieve_context *isctx =
 		(struct imap_sieve_context *)senv->script_context;
 
@@ -129,7 +136,8 @@ static const char *
 envit_vnd_mailbox_to_get_value(const struct sieve_runtime_env *renv,
 			       const char *name ATTR_UNUSED)
 {
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct imap_sieve_context *isctx =
 		(struct imap_sieve_context *)senv->script_context;
 
diff --git a/src/plugins/sieve-extprograms/cmd-execute.c b/src/plugins/sieve-extprograms/cmd-execute.c
index aa98b7ddc..79f6663a5 100644
--- a/src/plugins/sieve-extprograms/cmd-execute.c
+++ b/src/plugins/sieve-extprograms/cmd-execute.c
@@ -312,7 +312,8 @@ cmd_execute_operation_dump(const struct sieve_dumptime_env *denv,
 static int
 cmd_execute_operation_execute(const struct sieve_runtime_env *renv,
 			      sieve_size_t *address)
-{	
+{
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct sieve_side_effects_list *slist = NULL;
 	int opt_code = 0;
@@ -398,8 +399,8 @@ cmd_execute_operation_execute(const struct sieve_runtime_env *renv,
 			    "execute program `%s'",
 			    str_sanitize(program_name, 128));
 
-	sprog = sieve_extprogram_create(this_ext, renv->scriptenv,
-					renv->msgdata, "execute",
+	sprog = sieve_extprogram_create(this_ext, eenv->scriptenv,
+					eenv->msgdata, "execute",
 					program_name, args, &error);
 	if (sprog != NULL) {
 		if (var_storage != NULL) {
diff --git a/src/plugins/sieve-extprograms/cmd-filter.c b/src/plugins/sieve-extprograms/cmd-filter.c
index b290125a9..7118ff1c4 100644
--- a/src/plugins/sieve-extprograms/cmd-filter.c
+++ b/src/plugins/sieve-extprograms/cmd-filter.c
@@ -129,7 +129,8 @@ cmd_filter_operation_dump(const struct sieve_dumptime_env *denv,
 static int
 cmd_filter_operation_execute(const struct sieve_runtime_env *renv,
 			     sieve_size_t *address)
-{	
+{
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	unsigned int is_test = 0;
 	struct sieve_stringlist *args_list = NULL;
@@ -183,8 +184,8 @@ cmd_filter_operation_execute(const struct sieve_runtime_env *renv,
 	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
 		"execute program `%s'", str_sanitize(program_name, 128));
 
-	sprog = sieve_extprogram_create(this_ext, renv->scriptenv,
-					renv->msgdata, "filter",
+	sprog = sieve_extprogram_create(this_ext, eenv->scriptenv,
+					eenv->msgdata, "filter",
 					program_name, args, &error);
 	if (sprog != NULL) {
 		struct mail *mail = sieve_message_get_mail(renv->msgctx);
diff --git a/src/plugins/sieve-extprograms/cmd-pipe.c b/src/plugins/sieve-extprograms/cmd-pipe.c
index 338449473..f25b58177 100644
--- a/src/plugins/sieve-extprograms/cmd-pipe.c
+++ b/src/plugins/sieve-extprograms/cmd-pipe.c
@@ -334,16 +334,18 @@ act_pipe_commit(const struct sieve_action *action,
 		const struct sieve_action_exec_env *aenv,
 		void *tr_context ATTR_UNUSED, bool *keep)
 {
-	const struct ext_pipe_action *act = 
+	const struct sieve_execute_env *eenv = aenv->exec_env;
+	const struct ext_pipe_action *act =
 		(const struct ext_pipe_action *) action->context;
 	enum sieve_error error = SIEVE_ERROR_NONE;
-	struct mail *mail = (action->mail != NULL ? action->mail :
+	struct mail *mail = (action->mail != NULL ?
+			     action->mail :
 			     sieve_message_get_mail(aenv->msgctx));
 	struct sieve_extprogram *sprog;
 	int ret;
 
-	sprog = sieve_extprogram_create(action->ext, aenv->scriptenv,
-					aenv->msgdata, "pipe",
+	sprog = sieve_extprogram_create(action->ext, eenv->scriptenv,
+					eenv->msgdata, "pipe",
 					act->program_name, act->args, &error);
 	if (sprog != NULL) {
 		if (sieve_extprogram_set_input_mail(sprog, mail) < 0) {
@@ -365,7 +367,7 @@ act_pipe_commit(const struct sieve_action *action,
 					str_sanitize(act->program_name, 128));
 
 		/* Indicate that message was successfully 'forwarded' */
-		aenv->exec_status->message_forwarded = TRUE;
+		eenv->exec_status->message_forwarded = TRUE;
 	} else {
 		if (ret < 0) {
 			if (error == SIEVE_ERROR_NOT_FOUND) {
diff --git a/src/testsuite/cmd-test-config.c b/src/testsuite/cmd-test-config.c
index b056e099c..75b7247b2 100644
--- a/src/testsuite/cmd-test-config.c
+++ b/src/testsuite/cmd-test-config.c
@@ -429,6 +429,7 @@ static int
 cmd_test_config_reload_operation_execute(const struct sieve_runtime_env *renv,
 					 sieve_size_t *address)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	const struct sieve_extension *ext;
 	int opt_code = 0;
 	string_t *extension = NULL;
@@ -481,7 +482,7 @@ cmd_test_config_reload_operation_execute(const struct sieve_runtime_env *renv,
 				"reload configuration for sieve engine");
 		}
 
-		sieve_settings_load(renv->svinst);
+		sieve_settings_load(eenv->svinst);
 	} else {
 		if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) {
 			sieve_runtime_trace(
@@ -490,7 +491,7 @@ cmd_test_config_reload_operation_execute(const struct sieve_runtime_env *renv,
 				str_c(extension));
 		}
 
-		ext = sieve_extension_get_by_name(renv->svinst,
+		ext = sieve_extension_get_by_name(eenv->svinst,
 						  str_c(extension));
 		if (ext == NULL) {
 			testsuite_test_failf("test_config_reload: "
diff --git a/src/testsuite/testsuite-result.c b/src/testsuite/testsuite-result.c
index 7bc03a5bc..4a8d6fb7c 100644
--- a/src/testsuite/testsuite-result.c
+++ b/src/testsuite/testsuite-result.c
@@ -18,31 +18,48 @@
 
 #include "testsuite-result.h"
 
+struct sieve_execute_env testsuite_execute_env;
+
+static pool_t testsuite_execute_pool;
 static struct sieve_result *_testsuite_result;
 
 void testsuite_result_init(void)
 {
 	struct sieve_instance *svinst = testsuite_sieve_instance;
 
-	_testsuite_result = sieve_result_create(svinst, &testsuite_msgdata,
-						testsuite_scriptenv);
+	i_zero(&testsuite_execute_env);
+	testsuite_execute_env.svinst = testsuite_sieve_instance;
+	testsuite_execute_env.msgdata = &testsuite_msgdata;
+	testsuite_execute_env.scriptenv = testsuite_scriptenv;
+	testsuite_execute_env.exec_status = testsuite_scriptenv->exec_status;
+
+	testsuite_execute_pool = pool_alloconly_create("sieve execution", 4096);
+	_testsuite_result = sieve_result_create(svinst, testsuite_execute_pool,
+						&testsuite_execute_env);
 }
 
 void testsuite_result_deinit(void)
 {
-	if (_testsuite_result != NULL)
+	if (_testsuite_result != NULL) {
 		sieve_result_unref(&_testsuite_result);
+		pool_unref(&testsuite_execute_pool);
+	}
 }
 
 void testsuite_result_reset(const struct sieve_runtime_env *renv)
 {
 	struct sieve_instance *svinst = testsuite_sieve_instance;
 
-	if (_testsuite_result != NULL)
+	if (_testsuite_result != NULL) {
 		sieve_result_unref(&_testsuite_result);
+		pool_unref(&testsuite_execute_pool);
+	}
+
+	i_zero(testsuite_execute_env.exec_status);
 
-	_testsuite_result = sieve_result_create(svinst, &testsuite_msgdata,
-						testsuite_scriptenv);
+	testsuite_execute_pool = pool_alloconly_create("sieve execution", 4096);
+	_testsuite_result = sieve_result_create(svinst, testsuite_execute_pool,
+						&testsuite_execute_env);
 	sieve_interpreter_set_result(renv->interp, _testsuite_result);
 }
 
@@ -74,20 +91,21 @@ bool testsuite_result_execute(const struct sieve_runtime_env *renv)
 
 	/* Execute the result */
 	ret = sieve_result_execute(_testsuite_result, NULL,
-				   testsuite_log_ehandler, 0);
+				   testsuite_log_ehandler);
 
 	return (ret > 0);
 }
 
 void testsuite_result_print(const struct sieve_runtime_env *renv)
 {
+	const struct sieve_execute_env *eenv = renv->exec_env;
 	struct ostream *out;
 
 	out = o_stream_create_fd(1, 0);
 	o_stream_set_no_error_handling(out, TRUE);
 
 	o_stream_nsend_str(out, "\n--");
-	sieve_result_print(_testsuite_result, renv->scriptenv, out, NULL);
+	sieve_result_print(_testsuite_result, eenv->scriptenv, out, NULL);
 	o_stream_nsend_str(out, "--\n\n");
 
 	o_stream_destroy(&out);
diff --git a/src/testsuite/testsuite-result.h b/src/testsuite/testsuite-result.h
index 88b364011..3e232d277 100644
--- a/src/testsuite/testsuite-result.h
+++ b/src/testsuite/testsuite-result.h
@@ -1,6 +1,10 @@
 #ifndef TESTSUITE_RESULT_H
 #define TESTSUITE_RESULT_H
 
+#include "sieve-execute.h"
+
+extern struct sieve_execute_env testsuite_execute_env;
+
 void testsuite_result_init(void);
 void testsuite_result_deinit(void);
 
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index 2c00f6e6b..39b6eec8c 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -89,12 +89,15 @@ bool testsuite_script_is_subtest(const struct sieve_runtime_env *renv)
 
 bool testsuite_script_run(const struct sieve_runtime_env *renv)
 {
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct testsuite_interpreter_context *ictx =
 		testsuite_interpreter_context_get(renv->interp, testsuite_ext);
 	struct sieve_script_env scriptenv;
+	struct sieve_exec_status exec_status;
 	struct sieve_result *result;
 	struct sieve_interpreter *interp;
+	struct sieve_execute_env exec_env;
 	const char *error;
 	int ret;
 
@@ -108,6 +111,8 @@ bool testsuite_script_run(const struct sieve_runtime_env *renv)
 
 	testsuite_log_clear_messages();
 
+	i_zero(&exec_status);
+
 	/* Compose script execution environment */
 	if (sieve_script_env_init(&scriptenv, senv->user, &error) < 0) {
 		sieve_runtime_error(renv, NULL,	"testsuite: "
@@ -122,15 +127,23 @@ bool testsuite_script_run(const struct sieve_runtime_env *renv)
 	scriptenv.smtp_finish = testsuite_smtp_finish;
 	scriptenv.duplicate_mark = NULL;
 	scriptenv.duplicate_check = NULL;
-	scriptenv.trace_log = renv->scriptenv->trace_log;
-	scriptenv.trace_config = renv->scriptenv->trace_config;
+	scriptenv.trace_log = eenv->scriptenv->trace_log;
+	scriptenv.trace_config = eenv->scriptenv->trace_config;
+	scriptenv.exec_status = &exec_status;
 
 	result = testsuite_result_get();
 
+	i_zero(&exec_env);
+	exec_env.svinst = eenv->svinst;
+	exec_env.flags = eenv->flags;
+	exec_env.msgdata = eenv->msgdata;
+	exec_env.scriptenv = &scriptenv;
+	exec_env.exec_status = &exec_status;
+
 	/* Execute the script */
 	interp = sieve_interpreter_create(ictx->compiled_script, NULL,
-					  renv->msgdata, &scriptenv,
-					  testsuite_log_ehandler, 0);
+					  &exec_env, testsuite_log_ehandler);
+
 	if (interp == NULL)
 		return FALSE;
 
@@ -176,8 +189,10 @@ bool testsuite_script_multiscript(const struct sieve_runtime_env *renv,
 				  ARRAY_TYPE (const_string) *scriptfiles)
 {
 	struct sieve_instance *svinst = testsuite_sieve_instance;
-	const struct sieve_script_env *senv = renv->scriptenv;
+	const struct sieve_execute_env *eenv = renv->exec_env;
+	const struct sieve_script_env *senv = eenv->scriptenv;
 	struct sieve_script_env scriptenv;
+	struct sieve_exec_status exec_status;
 	struct sieve_multiscript *mscript;
 	const char *const *scripts;
 	const char *error;
@@ -202,12 +217,13 @@ bool testsuite_script_multiscript(const struct sieve_runtime_env *renv,
 	scriptenv.smtp_finish = testsuite_smtp_finish;
 	scriptenv.duplicate_mark = NULL;
 	scriptenv.duplicate_check = NULL;
-	scriptenv.trace_log = renv->scriptenv->trace_log;
-	scriptenv.trace_config = renv->scriptenv->trace_config;
+	scriptenv.trace_log = eenv->scriptenv->trace_log;
+	scriptenv.trace_config = eenv->scriptenv->trace_config;
+	scriptenv.exec_status = &exec_status;
 
 	/* Start execution */
 
-	mscript = sieve_multiscript_start_execute(svinst, renv->msgdata,
+	mscript = sieve_multiscript_start_execute(svinst, eenv->msgdata,
 						  &scriptenv);
 
 	/* Execute scripts before main script */
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index b83503071..d96bd19bd 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -55,18 +55,16 @@ static void print_help(void)
 }
 
 static int
-testsuite_run(struct sieve_binary *sbin,
-	      const struct sieve_message_data *msgdata,
-	      const struct sieve_script_env *senv,
-	      struct sieve_error_handler *ehandler)
+testsuite_run(struct sieve_binary *sbin, struct sieve_error_handler *ehandler)
 {
 	struct sieve_interpreter *interp;
 	struct sieve_result *result;
 	int ret = 0;
 
 	/* Create the interpreter */
-	if ((interp = sieve_interpreter_create(sbin, NULL, msgdata, senv,
-					       ehandler, 0)) == NULL)
+	interp = sieve_interpreter_create(sbin, NULL, &testsuite_execute_env,
+					  ehandler);
+	if (interp == NULL)
 		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Run the interpreter */
@@ -169,6 +167,7 @@ int main(int argc, char **argv)
 				  testsuite_log_main_ehandler,
 				  0, NULL)) != NULL) {
 		struct sieve_trace_log *trace_log = NULL;
+		struct sieve_exec_status exec_status;
 		struct sieve_script_env scriptenv;
 
 		/* Dump script */
@@ -190,6 +189,8 @@ int main(int argc, char **argv)
 				error);
 		}
 
+		i_zero(&exec_status);
+
 		scriptenv.default_mailbox = "INBOX";
 		scriptenv.smtp_start = testsuite_smtp_start;
 		scriptenv.smtp_add_rcpt = testsuite_smtp_add_rcpt;
@@ -198,14 +199,14 @@ int main(int argc, char **argv)
 		scriptenv.smtp_finish = testsuite_smtp_finish;
 		scriptenv.trace_log = trace_log;
 		scriptenv.trace_config = trace_config;
+		scriptenv.exec_status = &exec_status;
 
 		testsuite_scriptenv = &scriptenv;
 
 		testsuite_result_init();
 
 		/* Run the test */
-		ret = testsuite_run(sbin, &testsuite_msgdata,
-				    &scriptenv, testsuite_log_main_ehandler);
+		ret = testsuite_run(sbin, testsuite_log_main_ehandler);
 
 		switch (ret) {
 		case SIEVE_EXEC_OK:
-- 
GitLab