From a6a5960239f355f1095849e53e76eeafd35aa0db Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 8 Feb 2009 00:10:26 +0100
Subject: [PATCH] Testsuite: added support for looping back outgoing SMTP
 messages back into the test.

---
 Makefile.am                                   |   1 +
 TODO                                          |   4 +-
 src/lib-sieve/plugins/include/cmd-import.c    |   6 +-
 src/lib-sieve/plugins/vacation/cmd-vacation.c |   1 +
 src/lib-sieve/tst-size.c                      |  30 +-
 src/testsuite/Makefile.am                     |   3 +-
 src/testsuite/cmd-test-message.c              | 329 ++++++++++++++++++
 src/testsuite/ext-testsuite.c                 |   5 +-
 src/testsuite/testsuite-common.h              |   5 +
 src/testsuite/testsuite-message.c             |  46 ++-
 src/testsuite/testsuite-message.h             |   4 +-
 src/testsuite/testsuite-objects.c             |   2 +-
 src/testsuite/testsuite-result.c              |   6 +-
 src/testsuite/testsuite-smtp.c                |  27 ++
 src/testsuite/testsuite-smtp.h                |   7 +
 src/testsuite/tst-test-result-execute.c       |   2 +
 tests/extensions/enotify/mailto.svtest        |  26 ++
 17 files changed, 466 insertions(+), 38 deletions(-)
 create mode 100644 src/testsuite/cmd-test-message.c
 create mode 100644 tests/extensions/enotify/mailto.svtest

diff --git a/Makefile.am b/Makefile.am
index b9d38a6a2..ba3abf74a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -79,6 +79,7 @@ test_cases = \
 	tests/extensions/enotify/notify_method_capability.svtest \
 	tests/extensions/enotify/errors.svtest \
 	tests/extensions/enotify/execute.svtest \
+	tests/extensions/enotify/mailto.svtest \
 	tests/multiscript/basic.svtest
 
 if HAVE_DOVECOT_LIBS
diff --git a/TODO b/TODO
index b0ccb34ad..9ac4ce2d9 100644
--- a/TODO
+++ b/TODO
@@ -41,8 +41,8 @@ Next (in order of descending priority/precedence):
 	- Verify validity of utf8 where necessary.
 	- Implement comparator-i;unicode-casemap.
 * Make testsuite much more exhaustive:
-	- add support for testing delivered and outgoing smtp messages by looping 
-	  these back as the active mail message
+	- add support for testing delivered messages by looping these back as the 
+	  tested mail message
 	- add support for testing the content of result actions
 	- test as many error/warning/info conditions as possible. 
 	- review the specification documents and check whether the given conditions
diff --git a/src/lib-sieve/plugins/include/cmd-import.c b/src/lib-sieve/plugins/include/cmd-import.c
index 1f17ba4dd..49536e06d 100644
--- a/src/lib-sieve/plugins/include/cmd-import.c
+++ b/src/lib-sieve/plugins/include/cmd-import.c
@@ -322,9 +322,9 @@ static int opc_import_execute
 			if ( varval == NULL ) {
 				sieve_runtime_error(renv,
 					sieve_error_script_location(renv->script, source_line),
-        	"include: imported variable '%s' not previously exported", 
-        		vars[index]->identifier);
-        return SIEVE_EXEC_FAILURE;
+					"include: imported variable '%s' not previously exported", 
+					vars[index]->identifier);
+				return SIEVE_EXEC_FAILURE;
 			}
 		}	else {
 			/* Make sure variable is initialized (export) */
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index 5ecaac59f..3b98abac9 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -472,6 +472,7 @@ static bool cmd_vacation_generate
 	if ( !sieve_generate_arguments(cgenv, ctx, NULL) )
 		return FALSE;	
 
+	/* FIXME: this will not allow the handle to be a variable */
 	sieve_opr_string_emit(cgenv->sbin, ctx_data->handle);
 		
 	return TRUE;
diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c
index 37b2bee22..fa89486c7 100644
--- a/src/lib-sieve/tst-size.c
+++ b/src/lib-sieve/tst-size.c
@@ -19,7 +19,8 @@
  */
 
 static bool tst_size_registered
-	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg);
+	(struct sieve_validator *validator, 
+		struct sieve_command_registration *cmd_reg);
 static bool tst_size_pre_validate
 	(struct sieve_validator *validator, struct sieve_command_context *tst);
 static bool tst_size_validate
@@ -74,7 +75,8 @@ struct tst_size_context_data {
 };
 
 #define TST_SIZE_ERROR_DUP_TAG \
-	"exactly one of the ':under' or ':over' tags must be specified for the size test, but more were found"
+	"exactly one of the ':under' or ':over' tags must be specified " \
+	"for the size test, but more were found"
 
 /* 
  * Tag validation 
@@ -84,7 +86,8 @@ static bool tst_size_validate_over_tag
 (struct sieve_validator *validator, struct sieve_ast_argument **arg, 
 	struct sieve_command_context *tst)
 {
-	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data;	
+	struct tst_size_context_data *ctx_data = 
+		(struct tst_size_context_data *) tst->data;	
 	
 	if ( ctx_data->type != SIZE_UNASSIGNED ) {
 		sieve_argument_validate_error(validator, *arg, TST_SIZE_ERROR_DUP_TAG);
@@ -103,7 +106,8 @@ static bool tst_size_validate_under_tag
 (struct sieve_validator *validator, struct sieve_ast_argument **arg ATTR_UNUSED, 
 	struct sieve_command_context *tst)
 {
-	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data;	
+	struct tst_size_context_data *ctx_data = 
+		(struct tst_size_context_data *) tst->data;	
 	
 	if ( ctx_data->type != SIZE_UNASSIGNED ) {
 		sieve_argument_validate_error(validator, *arg, TST_SIZE_ERROR_DUP_TAG);
@@ -137,7 +141,7 @@ static const struct sieve_argument size_under_tag = {
 };
 
 static bool tst_size_registered
-	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
+(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
 {
 	/* Register our tags */
 	sieve_validator_register_tag(validator, cmd_reg, &size_over_tag, 0); 	
@@ -151,7 +155,8 @@ static bool tst_size_registered
  */
 
 static bool tst_size_pre_validate
-	(struct sieve_validator *validator ATTR_UNUSED, struct sieve_command_context *tst) 
+(struct sieve_validator *validator ATTR_UNUSED, 
+	struct sieve_command_context *tst) 
 {
 	struct tst_size_context_data *ctx_data;
 	
@@ -166,12 +171,14 @@ static bool tst_size_pre_validate
 static bool tst_size_validate
 	(struct sieve_validator *validator, struct sieve_command_context *tst) 
 {
-	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data;
+	struct tst_size_context_data *ctx_data = 
+		(struct tst_size_context_data *) tst->data;
 	struct sieve_ast_argument *arg = tst->first_positional;
 	
 	if ( ctx_data->type == SIZE_UNASSIGNED ) {
 		sieve_command_validate_error(validator, tst, 
-			"the size test requires either the :under or the :over tag to be specified");
+			"the size test requires either the :under or the :over tag "
+			"to be specified");
 		return FALSE;		
 	}
 		
@@ -190,7 +197,8 @@ static bool tst_size_validate
 bool tst_size_generate
 (const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx) 
 {
-	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) ctx->data;
+	struct tst_size_context_data *ctx_data = 
+		(struct tst_size_context_data *) ctx->data;
 
 	if ( ctx_data->type == SIZE_OVER ) 
 		sieve_operation_emit_code(cgenv->sbin, &tst_size_over_operation);
@@ -198,8 +206,8 @@ bool tst_size_generate
 		sieve_operation_emit_code(cgenv->sbin, &tst_size_under_operation);
 
  	/* Generate arguments */
-    if ( !sieve_generate_arguments(cgenv, ctx, NULL) )
-        return FALSE;
+	if ( !sieve_generate_arguments(cgenv, ctx, NULL) )
+		return FALSE;
 	  
 	return TRUE;
 }
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
index 3ac60626f..97c04fcab 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.am
@@ -37,7 +37,8 @@ commands = \
 	cmd-test.c \
 	cmd-test-fail.c \
 	cmd-test-set.c \
-	cmd-test-result-print.c
+	cmd-test-result-print.c \
+	cmd-test-message.c
 
 tests = \
 	tst-test-script-compile.c \
diff --git a/src/testsuite/cmd-test-message.c b/src/testsuite/cmd-test-message.c
new file mode 100644
index 000000000..05a4e1be7
--- /dev/null
+++ b/src/testsuite/cmd-test-message.c
@@ -0,0 +1,329 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+#include "sieve-dump.h"
+
+#include "testsuite-common.h"
+#include "testsuite-smtp.h"
+
+/*
+ * Test_message command
+ *
+ * Syntax:   
+ *   test_message ( :smtp / :mailbox <mailbox: string> ) <index: number>
+ */
+
+static bool cmd_test_message_registered
+	(struct sieve_validator *valdtr, 
+		struct sieve_command_registration *cmd_reg);
+static bool cmd_test_message_validate
+	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
+static bool cmd_test_message_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
+
+const struct sieve_command cmd_test_message = { 
+	"test_message", 
+	SCT_COMMAND, 
+	1, 0, FALSE, FALSE,
+	cmd_test_message_registered, 
+	NULL,
+	cmd_test_message_validate, 
+	cmd_test_message_generate, 
+	NULL 
+};
+
+/* 
+ * Operations
+ */ 
+ 
+/* Test_message_smtp operation */
+
+static bool cmd_test_message_smtp_operation_dump
+	(const struct sieve_operation *op,
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_test_message_smtp_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation test_message_smtp_operation = { 
+	"TEST_MESSAGE_SMTP",
+	&testsuite_extension, 
+	TESTSUITE_OPERATION_TEST_MESSAGE_SMTP,
+	cmd_test_message_smtp_operation_dump, 
+	cmd_test_message_smtp_operation_execute 
+};
+
+/* Test_message_mailbox operation */
+
+static bool cmd_test_message_mailbox_operation_dump
+	(const struct sieve_operation *op,
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_test_message_mailbox_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation test_message_mailbox_operation = { 
+	"TEST_MESSAGE_MAILBOX",
+	&testsuite_extension, 
+	TESTSUITE_OPERATION_TEST_MESSAGE_MAILBOX,
+	cmd_test_message_mailbox_operation_dump, 
+	cmd_test_message_mailbox_operation_execute 
+};
+
+/*
+ * Compiler context data
+ */
+ 
+enum test_message_source {
+	MSG_SOURCE_SMTP, 
+	MSG_SOURCE_MAILBOX,
+	MSG_SOURCE_LAST
+};
+
+const struct sieve_operation *test_message_operations[] = {
+	&test_message_smtp_operation,
+	&test_message_mailbox_operation
+};
+
+struct cmd_test_message_context_data {
+	enum test_message_source msg_source;
+	const char *folder;
+};
+
+#define CMD_TEST_MESSAGE_ERROR_DUP_TAG \
+	"exactly one of the ':smtp' or ':folder' tags must be specified " \
+	"for the test_message command, but more were found"
+
+/* 
+ * Command tags 
+ */
+ 
+static bool cmd_test_message_validate_smtp_tag
+	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+		struct sieve_command_context *cmd);
+static bool cmd_test_message_validate_folder_tag
+	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+		struct sieve_command_context *cmd);
+
+static const struct sieve_argument test_message_smtp_tag = { 
+	"smtp", 
+	NULL, NULL,
+	cmd_test_message_validate_smtp_tag,
+	NULL, NULL 
+};
+
+static const struct sieve_argument test_message_folder_tag = { 
+	"folder", 
+	NULL, NULL, 
+	cmd_test_message_validate_folder_tag,
+	NULL, NULL 
+};
+
+static bool cmd_test_message_registered
+(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) 
+{
+	/* Register our tags */
+	sieve_validator_register_tag(valdtr, cmd_reg, &test_message_folder_tag, 0); 	
+	sieve_validator_register_tag(valdtr, cmd_reg, &test_message_smtp_tag, 0); 	
+
+	return TRUE;
+}
+
+static struct cmd_test_message_context_data *cmd_test_message_validate_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct cmd_test_message_context_data *ctx_data = 
+		(struct cmd_test_message_context_data *) cmd->data;	
+	
+	if ( ctx_data != NULL ) {
+		sieve_argument_validate_error
+			(valdtr, *arg, CMD_TEST_MESSAGE_ERROR_DUP_TAG);
+		return NULL;		
+	}
+	
+	ctx_data = p_new
+		(sieve_command_pool(cmd), struct cmd_test_message_context_data, 1);
+	cmd->data = ctx_data;
+	
+	/* Delete this tag */
+	*arg = sieve_ast_arguments_detach(*arg, 1);
+
+	return ctx_data;
+}
+
+static bool cmd_test_message_validate_smtp_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct cmd_test_message_context_data *ctx_data = 
+		cmd_test_message_validate_tag(valdtr, arg, cmd);
+
+	/* Return value is NULL on error */
+	if ( ctx_data == NULL ) return FALSE;
+	
+	/* Assign chosen message source */
+	ctx_data->msg_source = MSG_SOURCE_SMTP;
+			
+	return TRUE;
+}
+
+static bool cmd_test_message_validate_folder_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+	struct cmd_test_message_context_data *ctx_data = 
+		cmd_test_message_validate_tag(valdtr, arg, cmd);
+	
+	/* Return value is NULL on error */
+	if ( ctx_data == NULL ) return FALSE;
+
+	/* Assign chose message source */
+	ctx_data->msg_source = MSG_SOURCE_MAILBOX;
+	
+	/* Check syntax:
+	 *   :folder string
+	 */
+	if ( !sieve_validate_tag_parameter
+		(valdtr, cmd, tag, *arg, SAAT_STRING) ) {
+		return FALSE;
+	}
+			
+	return TRUE;
+}
+
+/* 
+ * Validation 
+ */
+
+static bool cmd_test_message_validate
+(struct sieve_validator *valdtr, struct sieve_command_context *cmd) 
+{
+	struct sieve_ast_argument *arg = cmd->first_positional;
+	
+	if ( cmd->data == NULL ) {
+		sieve_command_validate_error(valdtr, cmd, 
+			"the test_message command requires either the :smtp or the :mailbox tag "
+			"to be specified");
+		return FALSE;		
+	}
+		
+	if ( !sieve_validate_positional_argument
+		(valdtr, cmd, arg, "index", 1, SAAT_NUMBER) ) {
+		return FALSE;
+	}
+	
+	return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE);
+}
+
+/* 
+ * Code generation 
+ */
+
+static bool cmd_test_message_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *cmd)
+{
+	struct cmd_test_message_context_data *ctx_data =
+		(struct cmd_test_message_context_data *) cmd->data; 
+
+	i_assert( ctx_data->msg_source < MSG_SOURCE_LAST );
+	
+	/* Emit operation */
+	sieve_operation_emit_code(cgenv->sbin, 
+		test_message_operations[ctx_data->msg_source]);
+	
+ 	/* Generate arguments */
+	if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+		return FALSE;
+	  
+	return TRUE;
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool cmd_test_message_smtp_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	sieve_code_dumpf(denv, "TEST_MESSAGE_SMTP:");
+	
+	sieve_code_descend(denv);
+	
+	return sieve_opr_number_dump(denv, address, "index");
+}
+
+static bool cmd_test_message_mailbox_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	sieve_code_dumpf(denv, "TEST_MESSAGE_MAILBOX:");
+	
+	sieve_code_descend(denv);
+
+	return 
+		sieve_opr_string_dump(denv, address, "folder") &&
+		sieve_opr_number_dump(denv, address, "index");
+}
+
+/*
+ * Intepretation
+ */
+ 
+static int cmd_test_message_smtp_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	sieve_number_t msg_index;
+
+	/* Index */
+	if ( !sieve_opr_number_read(renv, address, &msg_index) ) {
+		sieve_runtime_trace_error(renv, "invalid index operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+		
+	sieve_runtime_trace(renv, "TEST_MESSAGE_SMTP [%d]", msg_index);
+
+	return testsuite_smtp_get(renv, msg_index);
+}
+
+static int cmd_test_message_mailbox_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	string_t *folder;
+	sieve_number_t msg_index;
+
+	/* Folder */
+	if ( !sieve_opr_string_read(renv, address, &folder) ) {
+		sieve_runtime_trace_error(renv, "invalid folder operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+	
+	/* Index */
+	if ( !sieve_opr_number_read(renv, address, &msg_index) ) {
+		sieve_runtime_trace_error(renv, "invalid index operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+		
+	sieve_runtime_trace(renv, "TEST_MESSAGE_MAILBOX \"%s\" [%d]", 
+		str_c(folder), msg_index);
+
+	/* FIXME: to be implemented */
+
+	return SIEVE_EXEC_OK;
+}
+
+
+
+
+
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
index 31ac001b2..43f92e231 100644
--- a/src/testsuite/ext-testsuite.c
+++ b/src/testsuite/ext-testsuite.c
@@ -61,7 +61,9 @@ const struct sieve_operation *testsuite_operations[] = {
 	&test_error_operation,
 	&test_result_operation,
 	&test_result_execute_operation,
-	&test_result_print_operation
+	&test_result_print_operation,
+	&test_message_smtp_operation,
+	&test_message_mailbox_operation
 };
 
 /* 
@@ -108,6 +110,7 @@ static bool ext_testsuite_validator_load(struct sieve_validator *valdtr)
 	sieve_validator_register_command(valdtr, &cmd_test_fail);
 	sieve_validator_register_command(valdtr, &cmd_test_set);
 	sieve_validator_register_command(valdtr, &cmd_test_result_print);
+	sieve_validator_register_command(valdtr, &cmd_test_message);
 
 	sieve_validator_register_command(valdtr, &tst_test_script_compile);
 	sieve_validator_register_command(valdtr, &tst_test_script_run);
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
index 258e14663..d5c89904c 100644
--- a/src/testsuite/testsuite-common.h
+++ b/src/testsuite/testsuite-common.h
@@ -42,6 +42,7 @@ extern const struct sieve_command cmd_test;
 extern const struct sieve_command cmd_test_fail;
 extern const struct sieve_command cmd_test_set;
 extern const struct sieve_command cmd_test_result_print;
+extern const struct sieve_command cmd_test_message;
 
 /*
  * Tests
@@ -68,6 +69,8 @@ enum testsuite_operation_code {
 	TESTSUITE_OPERATION_TEST_RESULT,
 	TESTSUITE_OPERATION_TEST_RESULT_EXECUTE,
 	TESTSUITE_OPERATION_TEST_RESULT_PRINT,
+	TESTSUITE_OPERATION_TEST_MESSAGE_SMTP,
+	TESTSUITE_OPERATION_TEST_MESSAGE_MAILBOX
 };
 
 extern const struct sieve_operation test_operation;
@@ -80,6 +83,8 @@ extern const struct sieve_operation test_error_operation;
 extern const struct sieve_operation test_result_operation;
 extern const struct sieve_operation test_result_execute_operation;
 extern const struct sieve_operation test_result_print_operation;
+extern const struct sieve_operation test_message_smtp_operation;
+extern const struct sieve_operation test_message_mailbox_operation;
 
 /* 
  * Operands 
diff --git a/src/testsuite/testsuite-message.c b/src/testsuite/testsuite-message.c
index 493421a09..26a4cad69 100644
--- a/src/testsuite/testsuite-message.c
+++ b/src/testsuite/testsuite-message.c
@@ -36,18 +36,10 @@ static string_t *envelope_auth;
 
 pool_t message_pool;
 
-static void _testsuite_message_set(string_t *message)
+static void _testsuite_message_set_data(struct mail *mail)
 {
-	struct mail *mail;
 	const char *recipient = NULL, *sender = NULL;
 	
-	/*
-	 * Open message as mail struct
-	 */
-	 
-	_raw_message = mail_raw_open_data(message);
-	mail = _raw_message->mail;
-
 	/* 
 	 * Collect necessary message data 
 	 */
@@ -86,23 +78,38 @@ void testsuite_message_init(const char *user)
 
 	testsuite_user = user;
 	mail_raw_init(user);
-	_testsuite_message_set(default_message);
+	_raw_message = mail_raw_open_data(default_message);
+	_testsuite_message_set_data(_raw_message->mail);
 
 	envelope_to = str_new(message_pool, 256);
 	envelope_from = str_new(message_pool, 256);
 	envelope_auth = str_new(message_pool, 256);
 }
 
-void testsuite_message_set
+void testsuite_message_set_string
 (const struct sieve_runtime_env *renv, string_t *message)
 {
 	mail_raw_close(_raw_message);
 
-	_testsuite_message_set(message);	
+	_raw_message = mail_raw_open_data(message);
+
+	_testsuite_message_set_data(_raw_message->mail);
 
 	sieve_message_context_flush(renv->msgctx);
 }
 
+void testsuite_message_set_file
+(const struct sieve_runtime_env *renv, const char *file_path)
+{
+	mail_raw_close(_raw_message);
+	 
+	_raw_message = mail_raw_open_file(file_path);
+
+	_testsuite_message_set_data(_raw_message->mail);
+
+	sieve_message_context_flush(renv->msgctx);
+}
+	
 void testsuite_message_deinit(void)
 {
 	mail_raw_close(_raw_message);
@@ -114,21 +121,30 @@ void testsuite_message_deinit(void)
 void testsuite_envelope_set_sender(const char *value)
 {
 	str_truncate(envelope_from, 0);
-	str_append(envelope_from, value);
+
+	if ( value != NULL )
+		str_append(envelope_from, value);
+
 	testsuite_msgdata.return_path = str_c(envelope_from);
 }
 
 void testsuite_envelope_set_recipient(const char *value)
 {
 	str_truncate(envelope_to, 0);
-	str_append(envelope_to, value);
+
+	if ( value != NULL )
+		str_append(envelope_to, value);
+
 	testsuite_msgdata.to_address = str_c(envelope_to);
 }
 
 void testsuite_envelope_set_auth_user(const char *value)
 {
 	str_truncate(envelope_auth, 0);
-	str_append(envelope_auth, value);
+
+	if ( value != NULL )
+		str_append(envelope_auth, value);
+
 	testsuite_msgdata.auth_user = str_c(envelope_auth);
 } 
  
diff --git a/src/testsuite/testsuite-message.h b/src/testsuite/testsuite-message.h
index 53190f1ce..526c35389 100644
--- a/src/testsuite/testsuite-message.h
+++ b/src/testsuite/testsuite-message.h
@@ -11,8 +11,10 @@ extern struct sieve_message_data testsuite_msgdata;
 void testsuite_message_init(const char *user);
 void testsuite_message_deinit(void);
 
-void testsuite_message_set
+void testsuite_message_set_string
 	(const struct sieve_runtime_env *renv, string_t *message);
+void testsuite_message_set_file
+	(const struct sieve_runtime_env *renv, const char *file_path);
 
 void testsuite_envelope_set_sender(const char *value);
 void testsuite_envelope_set_recipient(const char *value);
diff --git a/src/testsuite/testsuite-objects.c b/src/testsuite/testsuite-objects.c
index 6fe6d4f09..bfbf74d9c 100644
--- a/src/testsuite/testsuite-objects.c
+++ b/src/testsuite/testsuite-objects.c
@@ -285,7 +285,7 @@ static bool tsto_message_set_member
 {
 	if ( id != -1 ) return FALSE;
 	
-	testsuite_message_set(renv, value);
+	testsuite_message_set_string(renv, value);
 	
 	return TRUE;
 }
diff --git a/src/testsuite/testsuite-result.c b/src/testsuite/testsuite-result.c
index 18ecd3862..a1e47a84c 100644
--- a/src/testsuite/testsuite-result.c
+++ b/src/testsuite/testsuite-result.c
@@ -63,7 +63,7 @@ bool testsuite_result_execute(const struct sieve_runtime_env *renv)
 
 	testsuite_log_clear_messages();
 
-	/* Compose script execution environment */
+	/* Compose script execution environment * /
 	memset(&scriptenv, 0, sizeof(scriptenv));
 	scriptenv.default_mailbox = "INBOX";
 	scriptenv.namespaces = NULL;
@@ -73,11 +73,11 @@ bool testsuite_result_execute(const struct sieve_runtime_env *renv)
 	scriptenv.smtp_open = NULL;
 	scriptenv.smtp_close = NULL;
 	scriptenv.duplicate_mark = NULL;
-	scriptenv.duplicate_check = NULL;
+	scriptenv.duplicate_check = NULL;*/
 	
 	/* Execute the result */	
 	ret=sieve_result_execute
-		(_testsuite_result, renv->msgdata, &scriptenv, NULL);
+		(_testsuite_result, renv->msgdata, renv->scriptenv, NULL);
 	
 	return ( ret > 0 );
 }
diff --git a/src/testsuite/testsuite-smtp.c b/src/testsuite/testsuite-smtp.c
index 7b3f091dc..85307280d 100644
--- a/src/testsuite/testsuite-smtp.c
+++ b/src/testsuite/testsuite-smtp.c
@@ -6,7 +6,10 @@
 #include "unlink-directory.h"
 
 #include "sieve-common.h" 
+#include "sieve-error.h"
+#include "sieve-interpreter.h"
  
+#include "testsuite-message.h"
 #include "testsuite-common.h"
 #include "testsuite-smtp.h"
 
@@ -103,3 +106,27 @@ bool testsuite_smtp_close(void *handle)
 	return TRUE;
 }
 
+/*
+ * Access
+ */
+
+int testsuite_smtp_get
+(const struct sieve_runtime_env *renv, unsigned int index)
+{
+	const struct testsuite_smtp_message *smtp_msg;
+
+	if ( index >= array_count(&testsuite_smtp_messages) ) {
+		sieve_runtime_error(renv,
+			sieve_error_script_location(renv->script, 0),
+			"no outgoing smtp message with index %d", index);
+		return SIEVE_EXEC_FAILURE;
+	}
+
+	smtp_msg = array_idx(&testsuite_smtp_messages, index);
+
+	testsuite_message_set_file(renv, smtp_msg->file);
+	testsuite_envelope_set_sender(smtp_msg->envelope_from);
+	testsuite_envelope_set_recipient(smtp_msg->envelope_to);
+
+	return SIEVE_EXEC_OK;
+}
diff --git a/src/testsuite/testsuite-smtp.h b/src/testsuite/testsuite-smtp.h
index cc73a653f..e7491e2b4 100644
--- a/src/testsuite/testsuite-smtp.h
+++ b/src/testsuite/testsuite-smtp.h
@@ -16,4 +16,11 @@ void *testsuite_smtp_open
 	(const char *destination, const char *return_path, FILE **file_r);
 bool testsuite_smtp_close(void *handle);
 
+/*
+ * Access
+ */
+
+int testsuite_smtp_get
+	(const struct sieve_runtime_env *renv, unsigned int index);
+
 #endif /* __TESTSUITE_SMTP_H */
diff --git a/src/testsuite/tst-test-result-execute.c b/src/testsuite/tst-test-result-execute.c
index 7129e6da1..830227fdd 100644
--- a/src/testsuite/tst-test-result-execute.c
+++ b/src/testsuite/tst-test-result-execute.c
@@ -78,6 +78,8 @@ static int tst_test_result_execute_operation_execute
 	 * Perform operation
 	 */
 
+	sieve_runtime_trace(renv, "TEST_RESULT_EXECUTE test");
+
 	result = testsuite_result_execute(renv);
 
 	/* Set result */
diff --git a/tests/extensions/enotify/mailto.svtest b/tests/extensions/enotify/mailto.svtest
new file mode 100644
index 000000000..a191c9d34
--- /dev/null
+++ b/tests/extensions/enotify/mailto.svtest
@@ -0,0 +1,26 @@
+require "vnd.dovecot.testsuite";
+require "enotify";
+
+test_set "message" text:
+From: stephan@rename-it.nl
+To: nico@vestingbar.nl
+Subject: Frop!
+
+Klutsefluts.
+.
+;
+
+test "Simple" {
+	notify "mailto:stephan@rename-it.nl";
+
+	if not test_result_execute {
+		test_fail "failed to execute notify";
+	}
+
+	test_message :smtp 0;
+
+	if not header :matches "Auto-Submitted" "auto-notified*" {
+		test_fail "auto-submitted header set inappropriately";
+	}
+}
+
-- 
GitLab