diff --git a/configure.in b/configure.in
index 2bfc9724003bf2f6407699f99fb958a69cfc8fca..461d2c85932b6e7a5af2ea5eab0c73c22a682789 100644
--- a/configure.in
+++ b/configure.in
@@ -159,6 +159,7 @@ src/lib-sieve/plugins/include/Makefile
 src/lib-sieve/plugins/body/Makefile
 src/lib-sieve/plugins/variables/Makefile
 src/lib-sieve/plugins/enotify/Makefile
+src/lib-sieve/plugins/enotify/mailto/Makefile
 src/lib-sieve/plugins/notify/Makefile
 src/lib-sieve/plugins/environment/Makefile
 src/lib-sieve/plugins/mailbox/Makefile
diff --git a/src/lib-sieve/plugins/enotify/Makefile.am b/src/lib-sieve/plugins/enotify/Makefile.am
index 662bf4dc107d6bffb8ddc170bec720a66e0152d5..648fe6be1100e6e3eeef79169d8396eb7061718e 100644
--- a/src/lib-sieve/plugins/enotify/Makefile.am
+++ b/src/lib-sieve/plugins/enotify/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = mailto
+
 noinst_LTLIBRARIES = libsieve_ext_enotify.la
 
 AM_CPPFLAGS = \
@@ -16,15 +18,19 @@ var_modifiers = \
 	vmodf-encodeurl.c
 
 notify_methods = \
-	ntfy-mailto.c
+	./mailto/libsieve_ext_enotify_mailto.la
 
+libsieve_ext_enotify_la_DEPENDENCIES = \
+	$(notify_methods)
+libsieve_ext_enotify_la_LIBADD = \
+	$(notify_methods)
+	
 libsieve_ext_enotify_la_SOURCES = \
 	ext-enotify.c \
 	ext-enotify-common.c \
 	$(commands) \
 	$(tests) \
-	$(var_modifiers) \
-	$(notify_methods)
+	$(var_modifiers)
 
 public_headers = \
 	sieve-ext-enotify.h
diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c
index 5b325f822ee0ee5955aa0a092ffa137546866f95..7f160327f364a6b4c97dae7d549fb80ce7cb9669 100644
--- a/src/lib-sieve/plugins/enotify/cmd-notify.c
+++ b/src/lib-sieve/plugins/enotify/cmd-notify.c
@@ -4,6 +4,7 @@
 #include "lib.h"
 
 #include "sieve-common.h"
+#include "sieve-error.h"
 #include "sieve-code.h"
 #include "sieve-extensions.h"
 #include "sieve-commands.h"
@@ -523,7 +524,9 @@ static int act_notify_check_duplicate
 	const struct sieve_action *act_other ATTR_UNUSED)
 {
 	const struct sieve_enotify_action *nact1, *nact2;
-	struct sieve_enotify_log nlog;
+	const struct sieve_enotify_method_def *nmth_def;
+	struct sieve_enotify_env nenv;
+	bool result = TRUE;
 		
 	if ( act->context == NULL || act_other->context == NULL )
 		return 0;
@@ -531,15 +534,24 @@ static int act_notify_check_duplicate
 	nact1 = (const struct sieve_enotify_action *) act->context;
 	nact2 = (const struct sieve_enotify_action *) act_other->context;
 
-	if ( nact1->method == NULL || nact1->method->action_check_duplicates == NULL )
+	if ( nact1->method == NULL || nact1->method->def == NULL ) 
 		return 0;
 
-	memset(&nlog, 0, sizeof(nlog));
-	nlog.location = act->location;
-	nlog.ehandler = sieve_result_get_error_handler(renv->result);
+	nmth_def = nact1->method->def;
+	if ( nmth_def->action_check_duplicates == NULL )
+		return 0;
+
+	memset(&nenv, sizeof(nenv), 0);
+	nenv.method = nact1->method;	
+	nenv.ehandler = sieve_prefix_ehandler_create
+		(sieve_result_get_error_handler(renv->result), act->location, "notify");
 
-	return nact1->method->action_check_duplicates
-		(&nlog, nact1->method_context, nact2->method_context, act_other->location);
+	result = nmth_def->action_check_duplicates
+		(&nenv, nact1->method_context, nact2->method_context, act_other->location);
+
+	sieve_error_handler_unref(&nenv.ehandler);
+	
+	return result;
 }
 
 /* Result printing */
@@ -550,17 +562,22 @@ static void act_notify_print
 {
 	const struct sieve_enotify_action *act = 
 		(const struct sieve_enotify_action *) action->context;
+	const struct sieve_enotify_method *method;
 
-	sieve_result_action_printf
-		( rpenv, "send notification with method '%s:':", act->method->identifier);
-		
-	if ( act->method->action_print != NULL ) {
-		struct sieve_enotify_print_env penv;
+	method = act->method;
+
+	if ( method->def != NULL ) {
+		sieve_result_action_printf
+			( rpenv, "send notification with method '%s:':", method->def->identifier);
 
-		memset(&penv, 0, sizeof(penv));
-		penv.result_penv = rpenv;
+		if ( method->def->action_print != NULL ) {
+			struct sieve_enotify_print_env penv;
 
-		act->method->action_print(&penv, act);
+			memset(&penv, 0, sizeof(penv));
+			penv.result_penv = rpenv;
+
+			method->def->action_print(&penv, act);
+		}
 	}
 }
 
@@ -572,21 +589,25 @@ static bool act_notify_commit
 {
 	const struct sieve_enotify_action *act = 
 		(const struct sieve_enotify_action *) action->context;
+	const struct sieve_enotify_method *method = act->method;
 	struct sieve_enotify_exec_env nenv;
-	struct sieve_enotify_log nlog;
-		
-	memset(&nlog, 0, sizeof(nlog));
-	nlog.aenv = aenv;
+	bool result = TRUE;
+
+	if ( method->def != NULL && method->def->action_execute != NULL )	{	
+		/* Compose log structure */
+		memset(&nenv, sizeof(nenv), 0);
+		nenv.method = method;	
+		nenv.scriptenv = aenv->scriptenv;
+		nenv.msgdata = aenv->msgdata;
+		nenv.msgctx = aenv->msgctx;
 
-	nenv.scriptenv = aenv->scriptenv;
-	nenv.msgdata = aenv->msgdata;
-	nenv.msgctx = aenv->msgctx;
-	nenv.notify_log = &nlog;
+		nenv.ehandler = sieve_prefix_ehandler_create
+			(aenv->ehandler, action->location, "notify action");
 
-	if ( act->method->action_execute != NULL )
-		return act->method->action_execute(&nenv, act);
+		result = method->def->action_execute(&nenv, act);
+	}
 			
-	return TRUE;
+	return result;
 }
 
 
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
index fb27c315113bac4bc6fe1d367dda50629b42e4aa..5e34d842d68c1cc2e5017e26968b03461398e31d 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
@@ -51,30 +51,57 @@ const struct sieve_extension_capabilities notify_capabilities = {
 	ext_notify_get_methods_string
 };
 
+/*
+ * Core notification methods
+ */
+
+extern const struct sieve_enotify_method_def mailto_notify;
+
 /*
  * Notify method registry
  */
  
-static void ext_enotify_method_register
-(struct ext_enotify_context *ectx, const struct sieve_enotify_method *method) 
+static const struct sieve_enotify_method *ext_enotify_method_register
+(struct sieve_instance *svinst, struct ext_enotify_context *ectx, 
+	const struct sieve_enotify_method_def *nmth_def) 
 {
-	array_append(&ectx->notify_methods, &method, 1);
+	struct sieve_enotify_method *nmth;
+
+	nmth = array_append_space(&ectx->notify_methods);
+	nmth->def = nmth_def;
+	nmth->svinst = svinst;
+
+	if ( nmth_def->load != NULL )
+		nmth_def->load(nmth, &nmth->context);
+
+	return nmth;
 } 
 
-void ext_enotify_methods_init(struct ext_enotify_context *ectx)
+void ext_enotify_methods_init
+(struct sieve_instance *svinst, struct ext_enotify_context *ectx)
 {
 	p_array_init(&ectx->notify_methods, default_pool, 4);
 
-	ext_enotify_method_register(ectx, &mailto_notify);
+	ext_enotify_method_register(svinst, ectx, &mailto_notify);
 }
 
 void ext_enotify_methods_deinit(struct ext_enotify_context *ectx)
 {
+	const struct sieve_enotify_method *methods;
+	unsigned int meth_count, i;
+	 
+	methods = array_get(&ectx->notify_methods, &meth_count);
+	for ( i = 0; i < meth_count; i++ ) {
+		if ( methods[i].def != NULL && methods[i].def->unload != NULL )
+			methods[i].def->unload(&methods[i]);
+	}
+
 	array_free(&ectx->notify_methods);
 }
 
-void sieve_enotify_method_register
-(struct sieve_instance *svinst, const struct sieve_enotify_method *method) 
+const struct sieve_enotify_method *sieve_enotify_method_register
+(struct sieve_instance *svinst, 
+	const struct sieve_enotify_method_def *nmth_def)
 {
 	const struct sieve_extension *ntfy_ext =
 		sieve_extension_get_by_name(svinst, "enotify");
@@ -83,8 +110,10 @@ void sieve_enotify_method_register
 		struct ext_enotify_context *ectx = 
 			(struct ext_enotify_context *) ntfy_ext->context;
 
-		ext_enotify_method_register(ectx, method);
+		return ext_enotify_method_register(svinst, ectx, nmth_def);
 	}
+
+	return NULL;
 }
 
 const struct sieve_enotify_method *ext_enotify_method_find
@@ -93,13 +122,15 @@ const struct sieve_enotify_method *ext_enotify_method_find
 	struct ext_enotify_context *ectx = 
 		(struct ext_enotify_context *) ntfy_ext->context;
 	unsigned int meth_count, i;
-	const struct sieve_enotify_method *const *methods;
+	const struct sieve_enotify_method *methods;
 	 
 	methods = array_get(&ectx->notify_methods, &meth_count);
 		
 	for ( i = 0; i < meth_count; i++ ) {
-		if ( strcasecmp(methods[i]->identifier, identifier) == 0 ) {
-			return methods[i];
+		if ( methods[i].def == NULL ) continue;
+
+		if ( strcasecmp(methods[i].def->identifier, identifier) == 0 ) {
+			return &methods[i];
 		}
 	}
 	
@@ -112,17 +143,17 @@ static const char *ext_notify_get_methods_string
 	struct ext_enotify_context *ectx = 
 		(struct ext_enotify_context *) ntfy_ext->context;
 	unsigned int meth_count, i;
-	const struct sieve_enotify_method *const *methods;
+	const struct sieve_enotify_method *methods;
 	string_t *result = t_str_new(128);
 	 
 	methods = array_get(&ectx->notify_methods, &meth_count);
 		
 	if ( meth_count > 0 ) {
-		str_append(result, methods[0]->identifier);
+		str_append(result, methods[0].def->identifier);
 		
 		for ( i = 1; i < meth_count; i++ ) {
 			str_append_c(result, ' ');
-			str_append(result, methods[i]->identifier);
+			str_append(result, methods[i].def->identifier);
 		}
 		
 		return str_c(result);
@@ -173,7 +204,7 @@ static const char *ext_enotify_uri_scheme_parse(const char **uri_p)
 }
 
 static bool ext_enotify_option_parse
-(struct sieve_enotify_log *nlog, const char *option, bool name_only,
+(struct sieve_enotify_env *nenv, const char *option, bool name_only,
 	const char **opt_name_r, const char **opt_value_r)
 {
 	const char *p = option;
@@ -194,7 +225,7 @@ static bool ext_enotify_option_parse
 	
 	/* Explicitly report empty option as such */
 	if ( *p == '\0' ) {
-		sieve_enotify_error(nlog, "empty option specified");
+		sieve_enotify_error(nenv, "empty option specified");
 		return FALSE;
 	}
 
@@ -209,7 +240,7 @@ static bool ext_enotify_option_parse
 	
 	/* Parsing must end at '=' and we must parse at least one character */
 	if ( *p != '=' || p == option ) {
-		sieve_enotify_error(nlog, "invalid option name specified in option '%s'",
+		sieve_enotify_error(nenv, "invalid option name specified in option '%s'",
 				str_sanitize(option, 80));
 		return FALSE;
 	}
@@ -235,7 +266,7 @@ static bool ext_enotify_option_parse
 		
 	/* Parse must end at end of string */
 	if ( *p != '\0' ) {
-		sieve_enotify_error(nlog, 
+		sieve_enotify_error(nenv, 
 			"notify command: invalid option value specified in option '%s'",
 				str_sanitize(option, 80));
 		return FALSE;
@@ -260,20 +291,22 @@ static int _ext_enotify_option_check
 		(struct _ext_enotify_option_check_context *) context;
 	struct sieve_validator *valdtr = optn_context->valdtr;
 	const struct sieve_enotify_method *method = optn_context->method;
-	struct sieve_enotify_log nlog;
+	struct sieve_enotify_env nenv;
 	const char *option = sieve_ast_argument_strc(arg);
 	const char *opt_name = NULL, *opt_value = NULL;
-	bool literal = sieve_argument_is_string_literal(arg);
+	bool result = TRUE, check = TRUE;
 	
 	/* Compose log structure */
-	memset(&nlog, 0, sizeof(nlog));
-	nlog.ehandler = sieve_validator_error_handler(valdtr);
-	nlog.prefix = "notify command";
-	nlog.location = sieve_error_script_location
-		(sieve_validator_script(valdtr), arg->source_line);
+	memset(&nenv, sizeof(nenv), 0);
+	nenv.method = method;	
+	nenv.ehandler = sieve_prefix_ehandler_create
+		(sieve_validator_error_handler(valdtr), 
+			sieve_error_script_location
+				(sieve_validator_script(valdtr), arg->source_line), 
+			"notify command");
 		
 	/* Parse option */
-	if ( !literal ) {
+	if ( !sieve_argument_is_string_literal(arg) ) {
 		/* Variable string: partial option parse
 		 * 
 		 * If the string item is not a string literal, it cannot be validated fully
@@ -284,19 +317,22 @@ static int _ext_enotify_option_check
 		 * of the option itself and not the assigned value.
 		 */ 
 		if ( !ext_enotify_option_parse(NULL, option, TRUE, &opt_name, &opt_value) )
-			return TRUE;
+			check = FALSE;
 	} else {
 		/* Literal string: full option parse */
 		if ( !ext_enotify_option_parse
-			(&nlog, option, FALSE, &opt_name, &opt_value) )
-			return FALSE;
+			(&nenv, option, FALSE, &opt_name, &opt_value) )
+			result = FALSE;
 	}
 	
 	/* Call method's option check function */
-	if ( method->compile_check_option != NULL ) 
-		return method->compile_check_option(&nlog, opt_name, opt_value); 
+	if ( result && check && method->def != NULL && 
+		method->def->compile_check_option != NULL ) 
+		result = method->def->compile_check_option(&nenv, opt_name, opt_value); 
 	
-	return TRUE;
+	sieve_error_handler_unref(&nenv.ehandler);
+
+	return result;
 }
 
 bool ext_enotify_compile_check_arguments
@@ -308,7 +344,8 @@ bool ext_enotify_compile_check_arguments
 	const char *uri = sieve_ast_argument_strc(uri_arg);
 	const char *scheme;
 	const struct sieve_enotify_method *method;
-	struct sieve_enotify_log nlog;
+	struct sieve_enotify_env nenv;
+	bool result = TRUE;
 
 	/* If the uri string is not a constant literal, we cannot determine which
 	 * method is used, so we bail out successfully and defer checking to runtime.
@@ -331,67 +368,78 @@ bool ext_enotify_compile_check_arguments
 		return FALSE;
 	}
 
+	if ( method->def == NULL ) return TRUE;
+
 	/* Compose log structure */
-	memset(&nlog, 0, sizeof(nlog));
-	nlog.ehandler = sieve_validator_error_handler(valdtr);
-	nlog.prefix = "notify command";
+	memset(&nenv, sizeof(nenv), 0);
+	nenv.method = method;	
 	
 	/* Check URI itself */
-	if ( method->compile_check_uri != NULL ) {
+	if ( result && method->def->compile_check_uri != NULL ) {
 		/* Set log location to location of URI argument */
-		nlog.location = sieve_error_script_location
-			(sieve_validator_script(valdtr), uri_arg->source_line);
+		nenv.ehandler = sieve_prefix_ehandler_create
+		(sieve_validator_error_handler(valdtr), 
+			sieve_error_script_location
+				(sieve_validator_script(valdtr), uri_arg->source_line), 
+			"notify command");
 
 		/* Execute method check function */
-		if ( !method->compile_check_uri
-			(&nlog, sieve_ast_argument_strc(uri_arg), uri) )
-			return FALSE;
+		result = method->def->compile_check_uri
+			(&nenv, sieve_ast_argument_strc(uri_arg), uri);
 	}
 
 	/* Check :message argument */
-	if ( msg_arg != NULL && sieve_argument_is_string_literal(msg_arg) && 
-		method->compile_check_message != NULL ) {
+	if ( result && msg_arg != NULL && sieve_argument_is_string_literal(msg_arg)
+		&& method->def->compile_check_message != NULL ) {
 		/* Set log location to location of :message argument */
-		nlog.location = sieve_error_script_location
-			(sieve_validator_script(valdtr), msg_arg->source_line);
+		sieve_error_handler_unref(&nenv.ehandler);
+		nenv.ehandler = sieve_prefix_ehandler_create
+		(sieve_validator_error_handler(valdtr), 
+			sieve_error_script_location
+				(sieve_validator_script(valdtr), msg_arg->source_line), 
+			"notify command");
 
 		/* Execute method check function */
-		if ( !method->compile_check_message
-			(&nlog, sieve_ast_argument_str(msg_arg)) )
-			return FALSE;
+		result = method->def->compile_check_message
+			(&nenv, sieve_ast_argument_str(msg_arg));
 	}
 
 	/* Check :from argument */
-	if ( from_arg != NULL && sieve_argument_is_string_literal(from_arg) &&
-		method->compile_check_from != NULL ) {
+	if ( result && from_arg != NULL && sieve_argument_is_string_literal(from_arg)
+		&& method->def->compile_check_from != NULL ) {
 		/* Set log location to location of :from argument */
-		nlog.location = sieve_error_script_location
-			(sieve_validator_script(valdtr), from_arg->source_line);
+		sieve_error_handler_unref(&nenv.ehandler);
+		nenv.ehandler = sieve_prefix_ehandler_create
+		(sieve_validator_error_handler(valdtr), 
+			sieve_error_script_location
+				(sieve_validator_script(valdtr), from_arg->source_line), 
+				"notify command");
 
 		/* Execute method check function */
-		if ( !method->compile_check_from(&nlog, sieve_ast_argument_str(from_arg)) )
-			return FALSE;
+		result = method->def->compile_check_from
+			(&nenv, sieve_ast_argument_str(from_arg));
 	}
+
+	sieve_error_handler_unref(&nenv.ehandler);
 	
 	/* Check :options argument */
-	if ( options_arg != NULL ) {
+	if ( result && options_arg != NULL ) {
 		struct sieve_ast_argument *option = options_arg;
 		struct _ext_enotify_option_check_context optn_context = { valdtr, method };
 		
 		/* Parse and check options */
-		if ( sieve_ast_stringlist_map
-			(&option, (void *) &optn_context, _ext_enotify_option_check) <= 0 )
-			return FALSE;
+		result = ( sieve_ast_stringlist_map
+			(&option, (void *) &optn_context, _ext_enotify_option_check) > 0 );
 			
 		/* Discard argument if options are not accepted by method */
-		if ( method->compile_check_option == NULL ) {
+		if ( result && method->def->compile_check_option == NULL ) {
 			sieve_argument_validate_warning(valdtr, options_arg, 
 				"notify command: method '%s' accepts no options", scheme);
 			(void)sieve_ast_arguments_detach(options_arg,1);
 		}
 	}
 	
-	return TRUE;
+	return result;
 }
 
 /*
@@ -406,6 +454,7 @@ bool ext_enotify_runtime_method_validate
 	const struct sieve_enotify_method *method;
 	const char *uri = str_c(method_uri);
 	const char *scheme;
+	bool result = TRUE;
 	
 	/* Get the method */
 	
@@ -417,20 +466,23 @@ bool ext_enotify_runtime_method_validate
 		
 	/* Validate the provided URI */
 	
-	if ( method->runtime_check_uri != NULL ) {
-		struct sieve_enotify_log nlog;
+	if ( method->def != NULL && method->def->runtime_check_uri != NULL ) {
+		struct sieve_enotify_env nenv; 
 		
-		memset(&nlog, 0, sizeof(nlog));
-		nlog.location = sieve_error_script_location(renv->script, source_line);
-		nlog.ehandler = sieve_interpreter_get_error_handler(renv->interp);
-		nlog.prefix = "valid_notify_method test";
+		memset(&nenv, 0, sizeof(nenv));
+		nenv.method = method;
+		nenv.ehandler = sieve_prefix_ehandler_create
+			(sieve_interpreter_get_error_handler(renv->interp), 
+				sieve_error_script_location(renv->script, source_line), 
+				"valid_notify_method test");
 
 		/* Use the method check function to validate the URI */
-		return method->runtime_check_uri(&nlog, str_c(method_uri), uri);
+		result = method->def->runtime_check_uri(&nenv, str_c(method_uri), uri);
+
+		sieve_error_handler_unref(&nenv.ehandler);
 	}
 
-	/* Method has no check function */
-	return TRUE;
+	return result;
 }
  
 static const struct sieve_enotify_method *ext_enotify_get_method
@@ -471,28 +523,33 @@ const char *ext_enotify_runtime_get_method_capability
 	string_t *method_uri, const char *capability)
 {
 	const struct sieve_enotify_method *method;
-	const char *uri;
+	const char *uri_body;
+	const char *result = NULL;
 	
 	/* Get method */
-	method = ext_enotify_get_method(renv, source_line, method_uri, &uri);
+	method = ext_enotify_get_method(renv, source_line, method_uri, &uri_body);
 	if ( method == NULL ) return NULL;
 	
 	/* Get requested capability */
-	if ( method->runtime_get_method_capability != NULL ) {
-		struct sieve_enotify_log nlog;
+	if ( method->def != NULL && 
+		method->def->runtime_get_method_capability != NULL ) {
+		struct sieve_enotify_env nenv; 
+
+		memset(&nenv, 0, sizeof(nenv));
+		nenv.method = method;
+		nenv.ehandler = sieve_prefix_ehandler_create
+			(sieve_interpreter_get_error_handler(renv->interp), 
+				sieve_error_script_location(renv->script, source_line), 
+				"notify_method_capability test");
 		
-		/* Compose log structure */
-		memset(&nlog, 0, sizeof(nlog));
-		nlog.location = sieve_error_script_location(renv->script, source_line);
-		nlog.ehandler = sieve_interpreter_get_error_handler(renv->interp);
-		nlog.prefix = "notify_method_capability test";
-
 		/* Execute method function to acquire capability value */
-		return method->runtime_get_method_capability
-			(&nlog, str_c(method_uri), uri, capability);
+		result = method->def->runtime_get_method_capability
+			(&nenv, str_c(method_uri), uri_body, capability);
+
+		sieve_error_handler_unref(&nenv.ehandler);
 	}
 
-	return NULL;
+	return result;
 }
 
 int ext_enotify_runtime_check_operands
@@ -502,25 +559,28 @@ int ext_enotify_runtime_check_operands
 	const struct sieve_enotify_method **method_r, void **method_context)
 {
 	const struct sieve_enotify_method *method;
-	const char *uri;
+	const char *uri_body;
 	
 	/* Get method */
-	method = ext_enotify_get_method(renv, source_line, method_uri, &uri);
+	method = ext_enotify_get_method(renv, source_line, method_uri, &uri_body);
 	if ( method == NULL ) return SIEVE_EXEC_FAILURE;
 	
 	/* Check provided operands */
-	if ( method->runtime_check_operands != NULL ) {
-		struct sieve_enotify_log nlog;
-		
-		/* Compose log structure */
-		memset(&nlog, 0, sizeof(nlog));
-		nlog.location = sieve_error_script_location(renv->script, source_line);
-		nlog.ehandler = sieve_interpreter_get_error_handler(renv->interp);
-		nlog.prefix = "notify action";
+	if ( method->def != NULL && method->def->runtime_check_operands != NULL ) {
+		struct sieve_enotify_env nenv; 
+		int ret = SIEVE_EXEC_OK;
+
+		memset(&nenv, 0, sizeof(nenv));
+		nenv.method = method;
+		nenv.ehandler = sieve_prefix_ehandler_create
+			(sieve_interpreter_get_error_handler(renv->interp), 
+				sieve_error_script_location(renv->script, source_line), 
+				"notify action");
 
 		/* Execute check function */
-		if ( method->runtime_check_operands(&nlog, str_c(method_uri), uri, message, 
-			from, sieve_result_pool(renv->result), method_context) ) {
+		if ( method->def->runtime_check_operands
+			(&nenv, str_c(method_uri), uri_body, message, from, 
+				sieve_result_pool(renv->result), method_context) ) {
 			
 			/* Check any provided options */
 			if ( options != NULL ) {			
@@ -535,12 +595,12 @@ int ext_enotify_runtime_check_operands
 				
 					/* Parse option into <optionname> and <value> */
 					if ( ext_enotify_option_parse
-						(&nlog, str_c(option), FALSE, &opt_name, &opt_value) ) {
+						(&nenv, str_c(option), FALSE, &opt_name, &opt_value) ) {
 					
 						/* Set option */
-						if ( method->runtime_set_option != NULL ) {
-							(void) method->runtime_set_option
-								(&nlog, *method_context, opt_name, opt_value);
+						if ( method->def->runtime_set_option != NULL ) {
+							(void) method->def->runtime_set_option
+								(&nenv, *method_context, opt_name, opt_value);
 						}
 					}
 				}
@@ -549,21 +609,25 @@ int ext_enotify_runtime_check_operands
 				 */
 				if ( result ) {
 					*method_r = method;
-					return SIEVE_EXEC_OK;
+				} else {
+					/* Binary corrupt */
+					sieve_runtime_trace_error
+						(renv, "invalid item in options string list");
+					ret = SIEVE_EXEC_BIN_CORRUPT;
 				}
-	
-				/* Binary corrupt */
-				sieve_runtime_trace_error(renv, "invalid item in options string list");
-				return SIEVE_EXEC_BIN_CORRUPT;
+
+			} else {
+				/* No options */			
+				*method_r = method;
 			}
 
-			/* No options */			
-			*method_r = method;
-			return SIEVE_EXEC_OK;
+		} else { 	
+			/* Operand check failed */
+			ret = SIEVE_EXEC_FAILURE;
 		}
-		
-		/* Check failed */
-		return SIEVE_EXEC_FAILURE;
+
+		sieve_error_handler_unref(&nenv.ehandler);
+		return ret;
 	}
 
 	/* No check function defined: a most unlikely situation */
@@ -586,59 +650,3 @@ void sieve_enotify_method_printf
 	va_end(args);	 
 }
 
-/*
- * Method logging
- */
-
-static void sieve_enotify_vlog_message
-(const struct sieve_enotify_log *nlog, sieve_error_func_t log_func,
-	const char *fmt, va_list args) 
-{
-	if ( nlog == NULL ) return;
-	
-	T_BEGIN {
-		if ( nlog->aenv != NULL ) {
-			if ( nlog->prefix == NULL )
-				sieve_result_vlog_message(nlog->aenv, log_func, fmt, args);
-			else
-				sieve_result_log_message(nlog->aenv, log_func, "%s: %s", nlog->prefix, 
-					t_strdup_vprintf(fmt, args));
-		} else {
-			if ( nlog->prefix == NULL )
-				log_func(nlog->ehandler, nlog->location, "%s", 
-					t_strdup_vprintf(fmt, args));
-			else
-				log_func(nlog->ehandler, nlog->location, "%s: %s", nlog->prefix, 
-					t_strdup_vprintf(fmt, args));	
-		}
-	} T_END;
-}
-
-void sieve_enotify_error
-(const struct sieve_enotify_log *nlog, const char *fmt, ...) 
-{
-	va_list args;
-
-	va_start(args, fmt);	
-	sieve_enotify_vlog_message(nlog, sieve_error, fmt, args);
-	va_end(args);
-}
-
-void sieve_enotify_warning
-(const struct sieve_enotify_log *nlog, const char *fmt, ...) 
-{
-	va_list args;
-	va_start(args, fmt);
-	sieve_enotify_vlog_message(nlog, sieve_warning, fmt, args);
-	va_end(args);
-}
-
-void sieve_enotify_log
-(const struct sieve_enotify_log *nlog, const char *fmt, ...) 
-{
-	va_list args;
-	va_start(args, fmt);
-	sieve_enotify_vlog_message(nlog, sieve_info, fmt, args);
-	va_end(args);
-}
-
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h
index 01b52fe6bbdf5401af01fe099eb3cfa646e4af7f..97ab61746caff6f89a5d3d146c0f866cb00cfc16 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h
@@ -22,7 +22,7 @@ extern const struct sieve_extension_capabilities notify_capabilities;
 
 struct ext_enotify_context {
 	const struct sieve_extension *var_ext;
-	ARRAY_DEFINE(notify_methods, const struct sieve_enotify_method *);
+	ARRAY_DEFINE(notify_methods, struct sieve_enotify_method);
 };
 
 
@@ -78,11 +78,11 @@ extern const struct sieve_variables_modifier_def encodeurl_modifier;
 /*
  * Notify methods
  */
- 
-extern const struct sieve_enotify_method mailto_notify;
- 
-void ext_enotify_methods_init(struct ext_enotify_context *ectx);
-void ext_enotify_methods_deinit(struct ext_enotify_context *ectx);
+  
+void ext_enotify_methods_init
+	(struct sieve_instance *svinst, struct ext_enotify_context *ectx);
+void ext_enotify_methods_deinit
+	(struct ext_enotify_context *ectx);
 
 const struct sieve_enotify_method *ext_enotify_method_find
 	(const struct sieve_extension *ntfy_ext, const char *identifier);
@@ -123,17 +123,4 @@ struct sieve_enotify_print_env {
 	const struct sieve_result_print_env *result_penv;
 };
 
-/*
- * Method logging
- */ 
-
-struct sieve_enotify_log {
-	struct sieve_error_handler *ehandler;
-	const char *location;
-
-	const struct sieve_action_exec_env *aenv;
-
-	const char *prefix;
-};
-
 #endif /* __EXT_ENOTIFY_COMMON_H */
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c
index 4d5f44348670053b17a9e1e7b13d43aa122b5d6b..3507d593952bd7eebe70b76c66f097e985c260b7 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify.c
@@ -69,7 +69,7 @@ static bool ext_enotify_load(const struct sieve_extension *ext, void **context)
 	ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
 	*context = (void *) ectx;
 
-	ext_enotify_methods_init(ectx);
+	ext_enotify_methods_init(ext->svinst, ectx);
 
 	sieve_extension_capabilities_register(ext->svinst, ext, &notify_capabilities);
 
diff --git a/src/lib-sieve/plugins/enotify/mailto/Makefile.am b/src/lib-sieve/plugins/enotify/mailto/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..36a7ad005cb00700f9d85d4a50c9c65026a9e211
--- /dev/null
+++ b/src/lib-sieve/plugins/enotify/mailto/Makefile.am
@@ -0,0 +1,18 @@
+noinst_LTLIBRARIES = libsieve_ext_enotify_mailto.la
+
+AM_CPPFLAGS = \
+	-I../ \
+	-I../../../ \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(dovecot_incdir)/src/lib-mail \
+	-I$(dovecot_incdir)/src/lib-storage 
+
+libsieve_ext_enotify_mailto_la_SOURCES = \
+	uri-mailto.c \
+	ntfy-mailto.c
+
+noinst_HEADERS = \
+	uri-mailto.h
+
+
diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
new file mode 100644
index 0000000000000000000000000000000000000000..0352add26eb0ea15366191f97a7d134dbb9f2a0a
--- /dev/null
+++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
@@ -0,0 +1,585 @@
+/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file 
+ */
+ 
+/* Notify method mailto
+ * --------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5436
+ * Implementation: full
+ * Status: testing
+ * 
+ */
+ 
+/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
+ *   draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification
+ *   when it matures. This requires modifications to the address parser (no
+ *   whitespace allowed within the address itself) and UTF-8 support will be
+ *   required in the URL.
+ */
+ 
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "str-sanitize.h"
+#include "message-date.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-address.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+
+#include "sieve-ext-enotify.h"
+
+#include "rfc2822.h"
+
+#include "uri-mailto.h"
+
+/*
+ * Configuration
+ */
+ 
+#define NTFY_MAILTO_MAX_RECIPIENTS  8
+#define NTFY_MAILTO_MAX_HEADERS     16
+#define NTFY_MAILTO_MAX_SUBJECT     256
+
+/* 
+ * Mailto notification method
+ */
+ 
+static bool ntfy_mailto_compile_check_uri
+	(const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body);
+static bool ntfy_mailto_compile_check_from
+	(const struct sieve_enotify_env *nenv, string_t *from);
+
+static const char *ntfy_mailto_runtime_get_notify_capability
+	(const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body, 
+		const char *capability);
+static bool ntfy_mailto_runtime_check_uri
+	(const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body);
+static bool ntfy_mailto_runtime_check_operands
+	(const struct sieve_enotify_env *nenv, const char *uri,const char *uri_body, 
+		string_t *message, string_t *from, pool_t context_pool, 
+		void **method_context);
+
+static int ntfy_mailto_action_check_duplicates
+	(const struct sieve_enotify_env *nenv, void *method_ctx1, void *method_ctx2,
+		const char *dupl_location);
+
+static void ntfy_mailto_action_print
+	(const struct sieve_enotify_print_env *penv, 
+		const struct sieve_enotify_action *act);	
+
+static bool ntfy_mailto_action_execute
+	(const struct sieve_enotify_exec_env *nenv, 
+		const struct sieve_enotify_action *act);
+
+const struct sieve_enotify_method_def mailto_notify = {
+	"mailto",
+	NULL, NULL, 
+	ntfy_mailto_compile_check_uri,
+	NULL,
+	ntfy_mailto_compile_check_from,
+	NULL,
+	ntfy_mailto_runtime_check_uri,
+	ntfy_mailto_runtime_get_notify_capability,
+	ntfy_mailto_runtime_check_operands,
+	NULL,
+	ntfy_mailto_action_check_duplicates,
+	ntfy_mailto_action_print,
+	ntfy_mailto_action_execute
+};
+
+/*
+ * Reserved and unique headers 
+ */
+ 
+static const char *_reserved_headers[] = {
+	"auto-submitted",
+	"received",
+	"message-id",
+	"data",
+	"bcc",
+	"in-reply-to",
+	"references",
+	"resent-date",
+	"resent-from",
+	"resent-sender",
+	"resent-to",
+	"resent-cc",
+ 	"resent-bcc",
+	"resent-msg-id",
+	"from",
+	"sender",
+	NULL
+};
+
+static const char *_unique_headers[] = {
+	"reply-to",
+	NULL
+};
+
+/*
+ * Method context data
+ */
+
+struct ntfy_mailto_context {
+	struct uri_mailto *uri;
+	const char *from_normalized;
+};
+
+/*
+ * Validation
+ */
+
+static bool ntfy_mailto_compile_check_uri
+(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED,
+	const char *uri_body)
+{	
+	return uri_mailto_validate
+		(uri_body, _reserved_headers, _unique_headers,
+			NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, nenv->ehandler);
+}
+
+static bool ntfy_mailto_compile_check_from
+(const struct sieve_enotify_env *nenv, string_t *from)
+{
+	const char *error;
+	bool result = FALSE;
+
+	T_BEGIN {
+		result = sieve_address_validate(from, &error);
+
+		if ( !result ) {
+			sieve_enotify_error(nenv,
+				"specified :from address '%s' is invalid for "
+				"the mailto method: %s",
+				str_sanitize(str_c(from), 128), error);
+		}
+	} T_END;
+
+	return result;
+}
+
+/*
+ * Runtime
+ */
+ 
+static const char *ntfy_mailto_runtime_get_notify_capability
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED, 
+	const char *uri_body, const char *capability)
+{
+	if ( !uri_mailto_validate(uri_body, _reserved_headers, _unique_headers,
+			NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL) ) {
+		return NULL;
+	}
+	
+	if ( strcasecmp(capability, "online") == 0 ) 
+		return "maybe";
+	
+	return NULL;
+}
+
+static bool ntfy_mailto_runtime_check_uri
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED,
+	const char *uri_body)
+{
+	return uri_mailto_validate
+		(uri_body, _reserved_headers, _unique_headers,
+			NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL);
+}
+ 
+static bool ntfy_mailto_runtime_check_operands
+(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED,
+	const char *uri_body, string_t *message ATTR_UNUSED, string_t *from, 
+	pool_t context_pool, void **method_context)
+{
+	struct ntfy_mailto_context *mtctx;
+	struct uri_mailto *parsed_uri;
+	const char *error, *normalized;
+
+	/* Need to create context before validation to have arrays present */
+	mtctx = p_new(context_pool, struct ntfy_mailto_context, 1);
+
+	/* Validate :from */
+	if ( from != NULL ) {
+		T_BEGIN {
+			normalized = sieve_address_normalize(from, &error);
+
+			if ( normalized == NULL ) {
+				sieve_enotify_error(nenv,
+					"specified :from address '%s' is invalid for "
+					"the mailto method: %s",
+					str_sanitize(str_c(from), 128), error);
+			} else 
+				mtctx->from_normalized = p_strdup(context_pool, normalized);
+		} T_END;
+
+		if ( !normalized ) return FALSE;
+	}
+
+	if ( (parsed_uri=uri_mailto_parse
+		(uri_body, context_pool, _reserved_headers, 
+			_unique_headers, NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS,
+			nenv->ehandler)) == NULL ) {
+		return FALSE;
+	}
+
+	mtctx->uri = parsed_uri;
+	*method_context = (void *) mtctx;
+	return TRUE;	
+}
+
+/*
+ * Action duplicates
+ */
+
+static int ntfy_mailto_action_check_duplicates
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, 
+	void *method_ctx1, void *method_ctx2,
+	const char *dupl_location ATTR_UNUSED)
+{
+	struct ntfy_mailto_context *mt_new = 
+		(struct ntfy_mailto_context *) method_ctx1;
+	struct ntfy_mailto_context *mt_old = 
+		(struct ntfy_mailto_context *) method_ctx2;
+	const struct uri_mailto_recipient *new_rcpts, *old_rcpts;
+	unsigned int new_count, old_count, i, j;
+	unsigned int del_start = 0, del_len = 0;
+
+	new_rcpts = array_get(&mt_new->uri->recipients, &new_count);
+	old_rcpts = array_get(&mt_old->uri->recipients, &old_count);
+
+	for ( i = 0; i < new_count; i++ ) {
+		for ( j = 0; j < old_count; j++ ) {
+			if ( sieve_address_compare
+				(new_rcpts[i].normalized, old_rcpts[j].normalized, TRUE) == 0 )
+				break;				
+		}
+
+		if ( j == old_count ) {
+			/* Not duplicate */
+			if ( del_len > 0 ) {
+				/* Perform pending deletion */
+				array_delete(&mt_new->uri->recipients, del_start, del_len);
+
+				/* Make sure the loop integrity is maintained */
+				i -= del_len;
+				new_rcpts = array_get(&mt_new->uri->recipients, &new_count);
+			}
+			del_len = 0;		
+		} else {
+			/* Mark deletion */
+			if ( del_len == 0 )
+				del_start = i;
+			del_len++;
+		}
+	}
+
+	/* Perform pending deletion */
+	if ( del_len > 0 ) {
+		array_delete(&mt_new->uri->recipients, del_start, del_len);			
+	}
+
+	return ( array_count(&mt_new->uri->recipients) > 0 ? 0 : 1 );
+}
+
+/*
+ * Action printing
+ */
+ 
+static void ntfy_mailto_action_print
+(const struct sieve_enotify_print_env *penv, 
+	const struct sieve_enotify_action *act)
+{
+	unsigned int count, i;
+	const struct uri_mailto_recipient *recipients;
+	const struct uri_mailto_header_field *headers;
+	struct ntfy_mailto_context *mtctx = 
+		(struct ntfy_mailto_context *) act->method_context;
+	
+	/* Print main method parameters */
+
+	sieve_enotify_method_printf
+		(penv,   "    => importance   : %d\n", act->importance);
+
+	if ( act->message != NULL )
+		sieve_enotify_method_printf
+			(penv, "    => subject      : %s\n", act->message);
+	else if ( mtctx->uri->subject != NULL )
+		sieve_enotify_method_printf
+			(penv, "    => subject      : %s\n", mtctx->uri->subject);
+
+	if ( act->from != NULL )
+		sieve_enotify_method_printf
+			(penv, "    => from         : %s\n", act->from);
+
+	/* Print mailto: recipients */
+
+	sieve_enotify_method_printf(penv,   "    => recipients   :\n" );
+
+	recipients = array_get(&mtctx->uri->recipients, &count);
+	if ( count == 0 ) {
+		sieve_enotify_method_printf(penv,   "       NONE, action has no effect\n");
+	} else {
+		for ( i = 0; i < count; i++ ) {
+			if ( recipients[i].carbon_copy )
+				sieve_enotify_method_printf
+					(penv,   "       + Cc: %s\n", recipients[i].full);
+			else
+				sieve_enotify_method_printf
+					(penv,   "       + To: %s\n", recipients[i].full);
+		}
+	}
+
+	/* Print accepted headers for notification message */
+	
+	headers = array_get(&mtctx->uri->headers, &count);
+	if ( count > 0 ) {
+		sieve_enotify_method_printf(penv,   "    => headers      :\n" );	
+		for ( i = 0; i < count; i++ ) {
+			sieve_enotify_method_printf(penv,   "       + %s: %s\n", 
+				headers[i].name, headers[i].body);
+		}
+	}
+
+	/* Print body for notification message */
+	
+	if ( mtctx->uri->body != NULL )
+		sieve_enotify_method_printf
+			(penv, "    => body         : \n--\n%s\n--\n", mtctx->uri->body);
+
+	/* Finish output with an empty line */
+
+	sieve_enotify_method_printf(penv,   "\n");
+}
+
+/*
+ * Action execution
+ */
+
+static bool _contains_8bit(const char *msg)
+{
+	const unsigned char *s = (const unsigned char *)msg;
+
+	for (; *s != '\0'; s++) {
+		if ((*s & 0x80) != 0)
+			return TRUE;
+	}
+	
+	return FALSE;
+}
+
+static bool ntfy_mailto_send
+(const struct sieve_enotify_exec_env *nenv, 
+	const struct sieve_enotify_action *act, const char *recipient)
+{ 
+	const struct sieve_message_data *msgdata = nenv->msgdata;
+	const struct sieve_script_env *senv = nenv->scriptenv;
+	struct ntfy_mailto_context *mtctx = 
+		(struct ntfy_mailto_context *) act->method_context;	
+	const char *from = NULL, *from_smtp = NULL; 
+	const char *subject = mtctx->uri->subject;
+	const char *body = mtctx->uri->body;
+	string_t *to, *cc;
+	const struct uri_mailto_recipient *recipients;
+	void *smtp_handle;
+	unsigned int count, i;
+	FILE *f;
+	const char *outmsgid;
+
+	/* Get recipients */
+	recipients = array_get(&mtctx->uri->recipients, &count);
+	if ( count == 0  ) {
+		sieve_enotify_warning(nenv, 
+			"notify mailto uri specifies no recipients; action has no effect");
+		return TRUE;
+	}
+
+	/* Just to be sure */
+	if ( !sieve_smtp_available(senv) ) {
+		sieve_enotify_warning(nenv, 
+			"notify mailto method has no means to send mail");
+		return TRUE;
+	}
+	
+	/* Determine message from address */
+	if ( act->from == NULL ) {
+		from = t_strdup_printf("Postmaster <%s>", senv->postmaster_address);
+	} else {
+		from = act->from;
+	}
+
+	/* Determine SMTP from address */
+	if ( sieve_message_get_sender(nenv->msgctx) != NULL ) {
+		if ( mtctx->from_normalized == NULL ) {
+			from_smtp = senv->postmaster_address;
+		} else {
+			from_smtp = mtctx->from_normalized;
+		}
+	}
+	
+	/* Determine subject */
+	if ( act->message != NULL ) {
+		/* FIXME: handle UTF-8 */
+		subject = str_sanitize(act->message, NTFY_MAILTO_MAX_SUBJECT);
+	} else if ( subject == NULL ) {
+		const char *const *hsubject;
+		
+		/* Fetch subject from original message */
+		if ( mail_get_headers_utf8
+			(msgdata->mail, "subject", &hsubject) >= 0 )
+			subject = str_sanitize(t_strdup_printf("Notification: %s", hsubject[0]), 
+				NTFY_MAILTO_MAX_SUBJECT);
+		else
+			subject = "Notification: (no subject)";
+	}
+
+	/* Compose To and Cc headers */
+	to = NULL;
+	cc = NULL;
+	for ( i = 0; i < count; i++ ) {
+		if ( recipients[i].carbon_copy ) {
+			if ( cc == NULL ) {
+				cc = t_str_new(256);
+				str_append(cc, recipients[i].full);
+			} else {
+				str_append(cc, ", ");
+				str_append(cc, recipients[i].full);
+			}
+		} else {
+			if ( to == NULL ) {
+				to = t_str_new(256);
+				str_append(to, recipients[i].full);
+			} else {
+				str_append(to, ", ");
+				str_append(to, recipients[i].full);
+			}
+		}
+	}
+
+	/* Send message to all recipients */
+	for ( i = 0; i < count; i++ ) {
+		const struct uri_mailto_header_field *headers;
+		unsigned int h, hcount;
+
+        smtp_handle = sieve_smtp_open
+            (senv, recipients[i].normalized, from_smtp, &f);
+		outmsgid = sieve_message_get_new_id(senv);
+	
+		rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION);
+		rfc2822_header_field_write(f, "Message-ID", outmsgid);
+		rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time));
+		rfc2822_header_field_utf8_printf(f, "Subject", "%s", subject);
+
+		rfc2822_header_field_utf8_printf(f, "From", "%s", from);
+
+		if ( to != NULL )
+			rfc2822_header_field_utf8_printf(f, "To", "%s", str_c(to));
+		
+		if ( cc != NULL )
+			rfc2822_header_field_utf8_printf(f, "Cc", "%s", str_c(cc));
+			
+		rfc2822_header_field_printf(f, "Auto-Submitted", 
+			"auto-notified; owner-email=\"%s\"", recipient);
+		rfc2822_header_field_write(f, "Precedence", "bulk");
+
+		/* Set importance */
+		switch ( act->importance ) {
+		case 1:
+			rfc2822_header_field_write(f, "X-Priority", "1 (Highest)");
+			rfc2822_header_field_write(f, "Importance", "High");
+			break;
+		case 3:
+			rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)");
+			rfc2822_header_field_write(f, "Importance", "Low");
+			break;
+		case 2:
+		default:
+			rfc2822_header_field_write(f, "X-Priority", "3 (Normal)");
+			rfc2822_header_field_write(f, "Importance", "Normal");
+			break;
+		}
+		
+		/* Add custom headers */
+		
+		headers = array_get(&mtctx->uri->headers, &hcount);
+		for ( h = 0; h < hcount; h++ ) {
+			const char *name = rfc2822_header_field_name_sanitize(headers[h].name);
+		
+			rfc2822_header_field_write(f, name, headers[h].body);
+		}
+			
+		/* Generate message body */
+		if ( body != NULL ) {
+			if (_contains_8bit(body)) {
+				rfc2822_header_field_write(f, "MIME-Version", "1.0");
+				rfc2822_header_field_write
+					(f, "Content-Type", "text/plain; charset=UTF-8");
+				rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit");
+			}
+			
+			fprintf(f, "\r\n");
+			fprintf(f, "%s\r\n", body);
+			
+		} else {
+			fprintf(f, "\r\n");
+			fprintf(f, "Notification of new message.\r\n");
+		}
+	
+		if ( sieve_smtp_close(senv, smtp_handle) ) {
+			sieve_enotify_info(nenv, 
+				"sent mail notification to <%s>", 
+				str_sanitize(recipients[i].normalized, 80));
+		} else {
+			sieve_enotify_error(nenv,
+				"failed to send mail notification to <%s> "
+				"(refer to system log for more information)", 
+				str_sanitize(recipients[i].normalized, 80));
+		}
+	}
+
+	return TRUE;
+}
+
+static bool ntfy_mailto_action_execute
+(const struct sieve_enotify_exec_env *nenv, 
+	const struct sieve_enotify_action *act)
+{
+	const char *const *headers;
+	const char *sender = sieve_message_get_sender(nenv->msgctx);
+	const char *recipient = sieve_message_get_recipient(nenv->msgctx);
+
+	/* Is the recipient unset? 
+	 */
+	if ( recipient == NULL ) {
+		sieve_enotify_warning(nenv, 
+			"notify mailto action aborted: envelope recipient is <>");
+		return TRUE;
+	}
+	
+	/* Is the message an automatic reply ? */
+	if ( mail_get_headers
+		(nenv->msgdata->mail, "auto-submitted", &headers) >= 0 ) {
+		const char *const *hdsp = headers;
+
+		/* Theoretically multiple headers could exist, so lets make sure */
+		while ( *hdsp != NULL ) {
+			if ( strcasecmp(*hdsp, "no") != 0 ) {
+				sieve_enotify_info(nenv, 
+					"not sending notification for auto-submitted message from <%s>", 
+					str_sanitize(sender, 128));	
+					return TRUE;				 
+			}
+			hdsp++;
+		}
+	}
+
+	return ntfy_mailto_send(nenv, act, recipient);
+}
+
+
+
+
diff --git a/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c
new file mode 100644
index 0000000000000000000000000000000000000000..a7115a22c0c1189d1d9d2c04a3d160da98351e1d
--- /dev/null
+++ b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c
@@ -0,0 +1,612 @@
+/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file 
+ */
+  
+/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
+ *   draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification
+ *   when it matures. This requires modifications to the address parser (no
+ *   whitespace allowed within the address itself) and UTF-8 support will be
+ *   required in the URL.
+ */
+ 
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "str-sanitize.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-address.h"
+#include "sieve-message.h"
+
+#include "uri-mailto.h"
+
+/* Util macros */
+
+#define uri_mailto_error(PARSER, ...) \
+	sieve_error((PARSER)->ehandler, NULL, "invalid mailto URI: " __VA_ARGS__ )
+	
+#define uri_mailto_warning(PARSER, ...) \
+	sieve_warning((PARSER)->ehandler, NULL, "mailto URI: " __VA_ARGS__ )
+
+/* Parser object */
+
+struct uri_mailto_parser {
+	pool_t pool;
+	struct sieve_error_handler *ehandler;
+
+	struct uri_mailto *uri;
+
+	const char **reserved_headers;
+	const char **unique_headers;
+
+	int max_recipients;
+	int max_headers;
+};
+
+/* 
+ * Reserved and unique headers 
+ */
+ 
+static inline bool uri_mailto_header_is_reserved
+(struct uri_mailto_parser *parser, const char *field_name)
+{
+	const char **hdr = parser->reserved_headers;
+
+	if ( hdr == NULL ) return FALSE;
+
+	/* Check whether it is reserved */
+	while ( *hdr != NULL ) {
+		if ( strcasecmp(field_name, *hdr) == 0 )
+			return TRUE;
+		hdr++;
+	}
+
+	return FALSE;
+}
+
+static inline bool uri_mailto_header_is_unique
+(struct uri_mailto_parser *parser, const char *field_name)
+{
+	const char **hdr = parser->unique_headers;
+
+	if ( hdr == NULL ) return FALSE;
+
+	/* Check whether it is supposed to be unique */
+	while ( *hdr != NULL ) {
+		if ( strcasecmp(field_name, *hdr) == 0 )
+			return TRUE;
+		hdr++;
+	}
+
+	return FALSE;
+} 
+
+/* 
+ * Low-level URI parsing.
+ *
+ * FIXME: much of this implementation will be common to other URI schemes. This
+ *        should be merged into a common implementation.
+ */
+
+static const char _qchar_lookup[256] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 00
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 10
+	0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,  // 20
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,  // 30
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 40
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  // 50
+	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 60
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,  // 70
+
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A0
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B0
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C0
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D0
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E0
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // F0
+};
+
+static inline bool _is_qchar(unsigned char c)
+{
+	return _qchar_lookup[c];
+}
+  
+static inline int _decode_hex_digit(char digit)
+{
+	switch ( digit ) {
+	case '0': case '1': case '2': case '3': case '4': 
+	case '5': case '6': case '7': case '8': case '9': 
+		return (int) digit - '0';
+
+	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+		return (int) digit - 'a' + 0x0a;
+		
+	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+		return (int) digit - 'A' + 0x0A;
+	}
+	
+	return -1;
+}
+
+static bool _parse_hex_value(const char **in, char *out)
+{
+	char value;
+		
+	if ( **in == '\0' || (value=_decode_hex_digit(**in)) < 0 )
+		return FALSE;
+	
+	*out = value << 4;
+	(*in)++;
+	
+	if ( **in == '\0' || (value=_decode_hex_digit(**in)) < 0 )
+		return FALSE;	
+
+	*out |= value;
+	(*in)++;
+	return (*out != '\0');	
+}
+
+/* 
+ * URI recipient parsing 
+ */ 
+
+static bool uri_mailto_add_valid_recipient
+(struct uri_mailto_parser *parser, string_t *recipient, bool cc)
+{
+	struct uri_mailto *uri = parser->uri;
+	struct uri_mailto_recipient *new_recipient;
+	struct uri_mailto_recipient *rcpts;
+	unsigned int count, i;
+	const char *error;
+	const char *normalized;
+	 
+	/* Verify recipient */
+	if ( (normalized=sieve_address_normalize(recipient, &error)) == NULL ) {
+		uri_mailto_error(parser, "invalid recipient '%s': %s",
+			str_sanitize(str_c(recipient), 80), error);
+		return FALSE;
+	}
+	
+	/* Add recipient to the uri */
+	if ( uri != NULL ) { 				
+		/* Get current recipients */
+		rcpts = array_get_modifiable(&uri->recipients, &count);
+		
+		/* Enforce limits */
+		if ( parser->max_recipients > 0 && (int)count >= parser->max_recipients ) {
+			if ( (int)count == parser->max_recipients) {
+				uri_mailto_warning(parser, 
+					"more than the maximum %u recipients specified; "
+					"rest is discarded", parser->max_recipients);
+			}
+			return TRUE;	
+		}
+	
+		/* Check for duplicate first */
+		for ( i = 0; i < count; i++ ) {
+			if ( sieve_address_compare(rcpts[i].normalized, normalized, TRUE) == 0 ) 
+				{
+				/* Upgrade existing Cc: recipient to a To: recipient if possible */
+				rcpts[i].carbon_copy = ( rcpts[i].carbon_copy && cc );
+			
+				uri_mailto_warning(parser, "ignored duplicate recipient '%s'",
+					str_sanitize(str_c(recipient), 80));
+				return TRUE;
+			} 
+		}			
+
+		/* Add */
+		new_recipient = array_append_space(&uri->recipients);
+		new_recipient->carbon_copy = cc;
+		new_recipient->full = p_strdup(parser->pool, str_c(recipient));
+		new_recipient->normalized = p_strdup(parser->pool, normalized);
+	}
+
+	return TRUE;
+}
+
+static bool uri_mailto_parse_recipients
+(struct uri_mailto_parser *parser, const char **uri_p)
+{
+	string_t *to = t_str_new(128);
+	const char *p = *uri_p;
+	
+	if ( *p == '\0' || *p == '?' )
+		return TRUE;
+		
+	while ( *p != '\0' && *p != '?' ) {
+		if ( *p == '%' ) {
+			/* % encoded character */
+			char ch;
+			
+			p++;
+			
+			/* Parse 2-digit hex value */
+			if ( !_parse_hex_value(&p, &ch) ) {
+				uri_mailto_error(parser, "invalid %% encoding");
+				return FALSE;
+			}
+
+			/* Check for delimiter */
+			if ( ch == ',' ) {
+				/* Verify and add recipient */
+				if ( !uri_mailto_add_valid_recipient(parser, to, FALSE) )
+					return FALSE;
+			
+				/* Reset for next recipient */
+				str_truncate(to, 0);
+			}	else {
+				/* Content character */
+				str_append_c(to, ch);
+			}
+		} else {
+			if ( *p == ':' || *p == ';' || *p == ',' || !_is_qchar(*p) ) {
+				uri_mailto_error
+					(parser, "invalid character '%c' in 'to' part", *p);
+				return FALSE;
+			}
+
+			/* Content character */
+			str_append_c(to, *p);
+			p++;
+		}
+	}	
+	
+	/* Skip '?' */
+	if ( *p != '\0' ) p++;
+	
+	/* Verify and add recipient */
+	if ( !uri_mailto_add_valid_recipient(parser, to, FALSE) )
+		return FALSE;
+
+	*uri_p = p;
+	return TRUE;
+}
+
+static bool uri_mailto_parse_header_recipients
+(struct uri_mailto_parser *parser, string_t *rcpt_header, bool cc)
+{
+	string_t *to = t_str_new(128);
+	const char *p = (const char *) str_data(rcpt_header);
+	const char *pend = p + str_len(rcpt_header);
+		
+	while ( p < pend ) {
+		if ( *p == ',' ) {
+			/* Verify and add recipient */
+			if ( !uri_mailto_add_valid_recipient(parser, to, cc) )
+				return FALSE;
+			
+			/* Reset for next recipient */
+			str_truncate(to, 0);
+		} else {
+			/* Content character */
+			str_append_c(to, *p);
+		}
+		p++;
+	}	
+	
+	/* Verify and add recipient */
+	if ( !uri_mailto_add_valid_recipient(parser, to, cc) )
+		return FALSE;
+
+	return TRUE;	
+}
+
+/* URI header parsing */
+
+static bool uri_mailto_header_is_duplicate
+(struct uri_mailto_parser *parser, const char *field_name)
+{	
+	struct uri_mailto *uri = parser->uri;
+
+	if ( uri == NULL ) return FALSE;
+
+	if ( uri_mailto_header_is_unique(parser, field_name) ) {
+		const struct uri_mailto_header_field *hdrs;
+		unsigned int count, i;
+
+		hdrs = array_get(&uri->headers, &count);	
+		for ( i = 0; i < count; i++ ) {
+			if ( strcasecmp(hdrs[i].name, field_name) == 0 ) 
+				return TRUE;
+		}
+	}
+	
+	return FALSE;
+}
+
+static bool uri_mailto_parse_headers
+(struct uri_mailto_parser *parser, const char **uri_p)
+{
+	struct uri_mailto *uri = parser->uri;
+	unsigned int header_count = 0;
+	string_t *field = t_str_new(128);
+	const char *p = *uri_p;
+					
+	while ( *p != '\0' ) {
+		enum {
+			_HNAME_IGNORED, 
+			_HNAME_GENERIC,
+			_HNAME_TO,
+			_HNAME_CC,
+			_HNAME_SUBJECT, 
+			_HNAME_BODY 
+		} hname_type = _HNAME_GENERIC;
+		struct uri_mailto_header_field *hdrf = NULL;
+		const char *field_name;
+		
+		/* Parse field name */
+		while ( *p != '\0' && *p != '=' ) {
+			char ch = *p;
+			p++;
+			
+			if ( ch == '%' ) {
+				/* Encoded, parse 2-digit hex value */
+				if ( !_parse_hex_value(&p, &ch) ) {
+					uri_mailto_error(parser, "invalid %% encoding");
+					return FALSE;
+				}
+			} else if ( ch != '=' && !_is_qchar(ch) ) {
+				uri_mailto_error
+					(parser, "invalid character '%c' in header field name part", ch);
+				return FALSE;
+			}
+
+			str_append_c(field, ch);
+		}
+		if ( *p != '\0' ) p++;
+
+		/* Verify field name */
+		if ( !rfc2822_header_field_name_verify(str_c(field), str_len(field)) ) {
+			uri_mailto_error(parser, "invalid header field name");
+			return FALSE;
+		}
+
+		if ( parser->max_headers > -1 && 
+			(int)header_count >= parser->max_headers ) {
+			/* Refuse to accept more headers than allowed by policy */
+			if ( (int)header_count == parser->max_headers ) {
+				uri_mailto_warning(parser, "more than the maximum %u headers specified; "
+					"rest is discarded", parser->max_headers);
+			}
+			
+			hname_type = _HNAME_IGNORED;
+		} else {
+			/* Add new header field to array and assign its name */
+			
+			field_name = str_c(field);
+			if ( strcasecmp(field_name, "to") == 0 )
+				hname_type = _HNAME_TO;
+			else if ( strcasecmp(field_name, "cc") == 0 )
+				hname_type = _HNAME_CC;
+			else if ( strcasecmp(field_name, "subject") == 0 )
+				hname_type = _HNAME_SUBJECT;
+			else if ( strcasecmp(field_name, "body") == 0 )
+				hname_type = _HNAME_BODY;
+			else if ( !uri_mailto_header_is_reserved(parser, field_name) ) {
+				if ( uri != NULL ) {
+					if ( !uri_mailto_header_is_duplicate(parser, field_name) ) {
+						hdrf = array_append_space(&uri->headers);
+						hdrf->name = p_strdup(parser->pool, field_name);
+					} else {
+						uri_mailto_warning(parser, 
+							"ignored duplicate for unique header field '%s'",
+							str_sanitize(field_name, 32));
+						hname_type = _HNAME_IGNORED;
+					}
+				} else {
+					hname_type = _HNAME_IGNORED;
+				}
+			} else {
+				uri_mailto_warning(parser, "ignored reserved header field '%s'",
+					str_sanitize(field_name, 32));
+				hname_type = _HNAME_IGNORED;
+			}
+		}
+		
+		header_count++;
+			
+		/* Reset for field body */
+		str_truncate(field, 0);
+		
+		/* Parse field body */		
+		while ( *p != '\0' && *p != '&' ) {
+			char ch = *p;
+			p++;
+			
+			if ( ch == '%' ) {
+				/* Encoded, parse 2-digit hex value */
+				if ( !_parse_hex_value(&p, &ch) ) {
+					uri_mailto_error(parser, "invalid %% encoding");
+					return FALSE;
+				}
+			} else if ( ch != '=' && !_is_qchar(ch) ) {
+				uri_mailto_error
+					(parser, "invalid character '%c' in header field value part", ch);
+				return FALSE;
+			}
+			str_append_c(field, ch);
+		}
+		if ( *p != '\0' ) p++;
+		
+		/* Verify field body */
+		if ( hname_type == _HNAME_BODY ) {
+			// FIXME: verify body ... 
+		} else {
+			if ( !rfc2822_header_field_body_verify(str_c(field), str_len(field)) ) {
+				uri_mailto_error(parser, "invalid header field body");
+				return FALSE;
+			}
+		}
+		
+		/* Assign field body */
+
+		switch ( hname_type ) {
+		case _HNAME_IGNORED:
+			break;
+		case _HNAME_TO:
+			/* Gracefully allow duplicate To fields */
+			if ( !uri_mailto_parse_header_recipients(parser, field, FALSE) )
+				return FALSE;
+			break;
+		case _HNAME_CC:
+			/* Gracefully allow duplicate Cc fields */
+			if ( !uri_mailto_parse_header_recipients(parser, field, TRUE) )
+				return FALSE;
+			break;
+		case _HNAME_SUBJECT:
+			/* Igore duplicate subject field */
+			if ( uri != NULL ) {
+				if ( uri->subject == NULL )
+					uri->subject = p_strdup(parser->pool, str_c(field));
+				else
+					uri_mailto_warning(parser, "ignored duplicate subject field");
+			}
+			break;
+		case _HNAME_BODY:
+			/* Igore duplicate body field */
+			if ( uri != NULL ) {
+				if ( uri->body == NULL )
+					uri->body = p_strdup(parser->pool, str_c(field));
+				else 
+					uri_mailto_warning(parser, "ignored duplicate body field");
+			}
+			break;
+		case _HNAME_GENERIC:
+			if ( uri != NULL && hdrf != NULL ) 
+				hdrf->body = p_strdup(parser->pool, str_c(field));
+			break;
+		}
+			
+		/* Reset for next name */
+		str_truncate(field, 0);
+	}	
+	
+	/* Skip '&' */
+	if ( *p != '\0' ) p++;
+
+	*uri_p = p;
+	return TRUE;
+}
+
+static bool uri_mailto_parse_uri
+(struct uri_mailto_parser *parser, const char *uri_body)
+{
+	const char *p = uri_body;
+	
+	/* 
+	 * mailtoURI   = "mailto:" [ to ] [ hfields ]
+	 * to          = [ addr-spec *("%2C" addr-spec ) ]
+	 * hfields     = "?" hfield *( "&" hfield )
+	 * hfield      = hfname "=" hfvalue
+	 * hfname      = *qchar
+	 * hfvalue     = *qchar
+	 * addr-spec   = local-part "@" domain
+	 * local-part  = dot-atom / quoted-string
+	 * qchar       = unreserved / pct-encoded / some-delims
+	 * some-delims = "!" / "$" / "'" / "(" / ")" / "*"
+	 *               / "+" / "," / ";" / ":" / "@"
+	 *
+	 * to         ~= *tqchar
+	 * tqchar     ~= <qchar> without ";" and ":" 
+	 * 
+	 * Scheme 'mailto:' already parsed, starting parse after colon
+	 */
+
+	/* First extract to-part by searching for '?' and decoding % items
+	 */
+
+	if ( !uri_mailto_parse_recipients(parser, &p) )
+		return FALSE;	
+
+	/* Extract hfield items */	
+	
+	while ( *p != '\0' ) {		
+		/* Extract hfield item by searching for '&' and decoding '%' items */
+		if ( !uri_mailto_parse_headers(parser, &p) )
+			return FALSE;		
+	}
+	
+	return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+bool uri_mailto_validate
+(const char *uri_body, const char **reserved_headers, 
+	const char **unique_headers, int max_recipients, int max_headers, 
+	struct sieve_error_handler *ehandler)
+{
+	struct uri_mailto_parser parser;
+
+	memset(&parser, 0, sizeof(parser));
+	parser.ehandler = ehandler;
+	parser.max_recipients = max_recipients;
+	parser.max_headers = max_headers;
+	
+	/* If no errors are reported, we don't need to record any data */
+	if ( ehandler != NULL ) { 
+		parser.pool = pool_datastack_create();
+		parser.reserved_headers = reserved_headers;
+		parser.unique_headers = unique_headers;
+
+		parser.uri = p_new(parser.pool, struct uri_mailto, 1);
+		p_array_init(&parser.uri->recipients, parser.pool, max_recipients);
+		p_array_init(&parser.uri->headers, parser.pool, max_headers);
+	}
+	
+	if ( !uri_mailto_parse_uri(&parser, uri_body) )
+		return FALSE;
+		
+	if ( ehandler != NULL ) {
+		if ( array_count(&parser.uri->recipients) == 0 )
+			uri_mailto_warning(&parser, "notification URI specifies no recipients");
+	}
+   
+	return TRUE;
+}
+
+/*
+ * Parsing
+ */
+
+struct uri_mailto *uri_mailto_parse
+(const char *uri_body, pool_t pool, const char **reserved_headers, 
+	const char **unique_headers, int max_recipients, int max_headers, 
+	struct sieve_error_handler *ehandler)
+{
+	struct uri_mailto_parser parser;
+	
+	/* If no errors are reported, we don't need to record any data */
+	parser.pool = pool;
+	parser.ehandler = ehandler;
+	parser.max_recipients = max_recipients;
+	parser.max_headers = max_headers;
+	parser.reserved_headers = reserved_headers;
+	parser.unique_headers = unique_headers;
+
+	parser.uri = p_new(pool, struct uri_mailto, 1);
+	p_array_init(&parser.uri->recipients, pool, max_recipients);
+	p_array_init(&parser.uri->headers, pool, max_headers);
+	
+	if ( !uri_mailto_parse_uri(&parser, uri_body) )
+		return FALSE;
+		
+	if ( ehandler != NULL ) {
+		if ( array_count(&parser.uri->recipients) == 0 )
+			uri_mailto_warning(&parser, "notification URI specifies no recipients");
+	}
+
+	return parser.uri;
+}
+
+
+
+
+
diff --git a/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h
new file mode 100644
index 0000000000000000000000000000000000000000..11264739a81740e99c506478b026e06517a99b95
--- /dev/null
+++ b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file 
+ */
+
+#ifndef __URI_MAILTO_H
+#define __URI_MAILTO_H
+
+/* 
+ * Types 
+ */
+
+struct uri_mailto_header_field {
+	const char *name;
+	const char *body;
+};
+
+struct uri_mailto_recipient {
+	const char *full;
+	const char *normalized;
+	bool carbon_copy;
+};
+
+ARRAY_DEFINE_TYPE(recipients, struct uri_mailto_recipient);
+ARRAY_DEFINE_TYPE(headers, struct uri_mailto_header_field);
+
+struct uri_mailto {
+	ARRAY_TYPE(recipients) recipients;
+	ARRAY_TYPE(headers) headers;
+	const char *subject;
+	const char *body;
+	const char *from_normalized;
+};
+
+bool uri_mailto_validate
+	(const char *uri_body, const char **reserved_headers, 
+		const char **unique_headers, int max_recipients, int max_headers, 
+		struct sieve_error_handler *ehandler);
+
+struct uri_mailto *uri_mailto_parse
+(const char *uri_body, pool_t pool, const char **reserved_headers, 
+	const char **unique_headers, int max_recipients, int max_headers, 
+	struct sieve_error_handler *ehandler);
+
+#endif /* __URI_MAILTO_H */
+
+
diff --git a/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h b/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
index 636df9671a9ca8c76faf40200bc6656a02918120..17cdb493f6de822f90762eaf3144023b4e22f084 100644
--- a/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
+++ b/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
@@ -15,64 +15,55 @@
  * Forward declarations
  */
 
-struct sieve_enotify_log;
-struct sieve_enotify_context; 
+struct sieve_enotify_method;
+struct sieve_enotify_env;
 struct sieve_enotify_action;
 struct sieve_enotify_print_env;
 struct sieve_enotify_exec_env;
 
 /*
- * Notify context
- */
-
-struct sieve_enotify_context {
-	struct sieve_error_handler *ehandler;
-	
-	/* Script location */
-	const struct sieve_script *script;
-	unsigned int source_line;
-
-	const struct sieve_message_data *msgdata;
-	pool_t pool;
-};
-
-/*
- * Notify methods
+ * Notify method definition
  */ 
 
-struct sieve_enotify_method {
+struct sieve_enotify_method_def {
 	const char *identifier;
+
+	/* Registration */
+	bool (*load)
+		(const struct sieve_enotify_method *nmth, void **context);
+	void (*unload)
+		(const struct sieve_enotify_method *nmth);
 	
 	/* Validation */
 	bool (*compile_check_uri)
-		(const struct sieve_enotify_log *nlog, const char *uri,
+		(const struct sieve_enotify_env *nenv, const char *uri,
 			const char *uri_body);
 	bool (*compile_check_message)
-		(const struct sieve_enotify_log *nlog, string_t *message);
+		(const struct sieve_enotify_env *nenv, string_t *message);
 	bool (*compile_check_from)
-		(const struct sieve_enotify_log *nlog, string_t *from);
+		(const struct sieve_enotify_env *nenv, string_t *from);
 	bool (*compile_check_option)
-		(const struct sieve_enotify_log *nlog, const char *option, 
+		(const struct sieve_enotify_env *nenv, const char *option, 
 			const char *value);
 
 	/* Runtime */
 	bool (*runtime_check_uri)
-		(const struct sieve_enotify_log *nlog, const char *uri,
+		(const struct sieve_enotify_env *nenv, const char *uri,
 			const char *uri_body);
 	const char *(*runtime_get_method_capability)
-		(const struct sieve_enotify_log *nlog, const char *uri, 
+		(const struct sieve_enotify_env *nenv, const char *uri, 
 			const char *uri_body, const char *capability);
 	bool (*runtime_check_operands)
-		(const struct sieve_enotify_log *nlog, const char *uri, 
+		(const struct sieve_enotify_env *nenv, const char *uri, 
 			const char *uri_body, string_t *message, string_t *from, 
 			pool_t context_pool, void **method_context);
 	bool (*runtime_set_option)
-		(const struct sieve_enotify_log *nlog, void *method_context,
+		(const struct sieve_enotify_env *nenv, void *method_context,
 			const char *option, const char *value);
 
 	/* Action duplicates */
 	int (*action_check_duplicates)
-		(const struct sieve_enotify_log *nlog, void *method_ctx1, 
+		(const struct sieve_enotify_env *nenv, void *method_ctx1, 
 			void *method_ctx2, const char *dupl_location);
 		
 	/* Action print */
@@ -86,8 +77,30 @@ struct sieve_enotify_method {
 			const struct sieve_enotify_action *act);
 };
 
-void sieve_enotify_method_register
-(struct sieve_instance *svinst, const struct sieve_enotify_method *method); 
+/*
+ * Notify method instance
+ */
+
+struct sieve_enotify_method {
+	const struct sieve_enotify_method_def *def;
+
+	struct sieve_instance *svinst;
+	void *context;
+};
+
+const struct sieve_enotify_method *sieve_enotify_method_register
+	(struct sieve_instance *svinst, 
+		const struct sieve_enotify_method_def *nmth_def); 
+
+/*
+ * Notify method environment
+ */
+
+struct sieve_enotify_env {
+	const struct sieve_enotify_method *method;
+
+	struct sieve_error_handler *ehandler;
+};
 
 /*
  * Notify method printing
@@ -102,11 +115,13 @@ void sieve_enotify_method_printf
  */
 
 struct sieve_enotify_exec_env {
-	const struct sieve_enotify_log *notify_log;
+	const struct sieve_enotify_method *method;
 
 	const struct sieve_script_env *scriptenv;
 	const struct sieve_message_data *msgdata;
 	struct sieve_message_context *msgctx;
+
+	struct sieve_error_handler *ehandler;
 };
 
 /*
@@ -123,18 +138,18 @@ struct sieve_enotify_action {
 };
 
 /*
- * Logging
+ * Error handling
  */
 
-void sieve_enotify_error
-	(const struct sieve_enotify_log *nlog, const char *fmt, ...) 
-		ATTR_FORMAT(2, 3);
-void sieve_enotify_warning
-	(const struct sieve_enotify_log *nlog, const char *fmt, ...) 
-		ATTR_FORMAT(2, 3);
-void sieve_enotify_log
-	(const struct sieve_enotify_log *nlog, const char *fmt, ...) 
-		ATTR_FORMAT(2, 3);
+#define sieve_enotify_error(ENV, ...) \
+	sieve_error((ENV)->ehandler, NULL, __VA_ARGS__ )
+	
+#define sieve_enotify_warning(ENV, ...) \
+	sieve_warning((ENV)->ehandler, NULL, __VA_ARGS__ )
+
+#define sieve_enotify_info(ENV, ...) \
+	sieve_info((ENV)->ehandler, NULL, __VA_ARGS__ )
+
 
 #endif /* __SIEVE_EXT_ENOTIFY_H */
 
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index e8494fc6e43422a146e7ee0b34d7253c50158d8d..060e88ce163adb083ebce27d0b9d8f849bed0900 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -17,6 +17,8 @@
 
 struct sieve_action_exec_env { 
 	struct sieve_result *result;
+	struct sieve_error_handler *ehandler;
+
 	const struct sieve_message_data *msgdata;
 	struct sieve_message_context *msgctx;
 	const struct sieve_script_env *scriptenv;
diff --git a/src/lib-sieve/sieve-error-private.h b/src/lib-sieve/sieve-error-private.h
index 110c247ecd7112f10036b967d48b80bc283dc807..fa0793b0ae51e19d3118d01686c2afd094672d93 100644
--- a/src/lib-sieve/sieve-error-private.h
+++ b/src/lib-sieve/sieve-error-private.h
@@ -28,18 +28,10 @@ struct sieve_error_handler {
 	bool log_info;
 	bool log_debug;
 
-	void (*verror)
-		(struct sieve_error_handler *ehandler, const char *location,
-			const char *fmt, va_list args);
-	void (*vwarning)
-		(struct sieve_error_handler *ehandler, const char *location,
-			const char *fmt, va_list args);
-	void (*vinfo)
-		(struct sieve_error_handler *ehandler, const char *location,
-			const char *fmt, va_list args);
-	void (*vdebug)
-		(struct sieve_error_handler *ehandler, const char *location,
-			const char *fmt, va_list args);
+	sieve_error_vfunc_t verror;
+	sieve_error_vfunc_t vwarning;
+	sieve_error_vfunc_t vinfo;
+	sieve_error_vfunc_t vdebug;
 
 	void (*free)
 		(struct sieve_error_handler *ehandler);
@@ -48,4 +40,97 @@ struct sieve_error_handler {
 void sieve_error_handler_init
 	(struct sieve_error_handler *ehandler, pool_t pool, unsigned int max_errors);
 
+/*
+ * Direct handler calls
+ */
+
+static inline void sieve_direct_verror
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, va_list args)
+{
+	if ( sieve_errors_more_allowed(ehandler) ) {
+		if ( ehandler->verror != NULL )
+			ehandler->verror(ehandler, location, fmt, args);
+		
+		if ( ehandler->pool != NULL )
+			ehandler->errors++;
+	}
+}
+
+static inline void sieve_direct_vwarning
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, va_list args)
+{
+	if ( ehandler->vwarning != NULL )	
+		ehandler->vwarning(ehandler, location, fmt, args);
+
+	if ( ehandler->pool != NULL )
+		ehandler->warnings++;
+}
+
+static inline void sieve_direct_vinfo
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, va_list args)
+{
+	if ( ehandler->log_info && ehandler->vinfo != NULL )	
+		ehandler->vinfo(ehandler, location, fmt, args);
+}
+
+static inline void sieve_direct_vdebug
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, va_list args)
+{
+	if ( ehandler->log_info && ehandler->vdebug != NULL )	
+		ehandler->vdebug(ehandler, location, fmt, args);
+}
+
+static inline void sieve_direct_error
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	
+	sieve_direct_verror(ehandler, location, fmt, args);
+	
+	va_end(args);
+}
+
+static inline void sieve_direct_warning
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	
+	sieve_direct_vwarning(ehandler, location, fmt, args);
+	
+	va_end(args);
+}
+
+static inline void sieve_direct_info
+(struct sieve_error_handler *ehandler, const char *location, 
+	const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	
+	sieve_direct_vinfo(ehandler, location, fmt, args);
+	
+	va_end(args);
+}
+
+static inline void sieve_direct_debug
+(struct sieve_error_handler *ehandler, const char *location,
+	const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+
+	sieve_direct_vdebug(ehandler, location, fmt, args);
+
+	va_end(args);
+}
+
+
 #endif /* __SIEVE_ERROR_PRIVATE_H */
diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c
index fd82038add8dd317a262603ac3e4b1c6c3a7c73c..08af504302dc2679dde00118b36d03aa31de90d2 100644
--- a/src/lib-sieve/sieve-error.c
+++ b/src/lib-sieve/sieve-error.c
@@ -3,7 +3,9 @@
  
 #include "lib.h"
 #include "str.h"
+#include "array.h"
 #include "ostream.h"
+#include "var-expand.h"
 #include "eacces-error.h"
 
 #include "sieve-common.h"
@@ -35,16 +37,17 @@
 const char *sieve_error_script_location
 (const struct sieve_script *script, unsigned int source_line)
 {
-    const char *sname;
+	const char *sname;
 
 	sname = ( script == NULL ? NULL : sieve_script_name(script) );
 
-    if ( sname == NULL || *sname == '\0' )
-        return t_strdup_printf("line %d", source_line);
+	if ( sname == NULL || *sname == '\0' )
+		return t_strdup_printf("line %d", source_line);
 
-    return t_strdup_printf("%s: line %d", sname, source_line);
+	return t_strdup_printf("%s: line %d", sname, source_line);
 }
 
+
 /*
  * Main error functions
  */
@@ -69,13 +72,7 @@ void sieve_verror
 	if ( ehandler->log_master )
 		sieve_vcopy_master(location, sieve_verror, fmt, args);
 
-	if ( sieve_errors_more_allowed(ehandler) ) {
-		if ( ehandler->verror != NULL )
-			ehandler->verror(ehandler, location, fmt, args);
-		
-		if ( ehandler->pool != NULL )
-			ehandler->errors++;
-	}
+	sieve_direct_verror(ehandler, location, fmt, args);
 }
 
 void sieve_vwarning
@@ -87,11 +84,7 @@ void sieve_vwarning
 	if ( ehandler->log_master )
 		sieve_vcopy_master(location, sieve_vwarning, fmt, args);
 
-	if ( ehandler->vwarning != NULL )	
-		ehandler->vwarning(ehandler, location, fmt, args);
-
-	if ( ehandler->pool != NULL )
-		ehandler->warnings++;
+	sieve_direct_vwarning(ehandler, location, fmt, args);
 }
 
 void sieve_vinfo
@@ -103,8 +96,7 @@ void sieve_vinfo
 	if ( ehandler->log_master )
 		sieve_vcopy_master(location, sieve_vinfo, fmt, args);
 
-	if ( ehandler->log_info && ehandler->vinfo != NULL )	
-		ehandler->vinfo(ehandler, location, fmt, args);
+	sieve_direct_vinfo(ehandler, location, fmt, args);
 }
 
 void sieve_vdebug
@@ -116,8 +108,7 @@ void sieve_vdebug
 	if ( ehandler->log_master )
 		sieve_vcopy_master(location, sieve_vdebug, fmt, args);
 
-	if ( ehandler->log_debug && ehandler->vdebug != NULL )
-		ehandler->vdebug(ehandler, location, fmt, args);
+	sieve_direct_vdebug(ehandler, location, fmt, args);
 }
 
 void sieve_vcritical
@@ -792,3 +783,237 @@ struct sieve_error_handler *sieve_logfile_ehandler_create
 	return &(ehandler->handler);
 }
 
+/*
+ * Prefix error handler 
+ *
+ *   Encapsulates an existing error handler and prefixes all messages with
+ *   the given prefix.
+ */
+
+struct sieve_prefix_ehandler {
+	struct sieve_error_handler handler;
+
+	struct sieve_error_handler *parent;
+
+	const char *location;
+	const char *prefix;
+};
+
+static const char *_prefix_message
+(struct sieve_prefix_ehandler *ehandler,
+	const char *location, const char *fmt, va_list args) 
+{
+	string_t *str = t_str_new(256);
+
+	if ( ehandler->prefix != NULL)
+		str_printfa(str, "%s: ", ehandler->prefix);
+	if ( location != NULL)
+		str_printfa(str, "%s: ", location);
+	str_vprintfa(str, fmt, args);
+
+	return str_c(str);
+}
+
+static void sieve_prefix_verror
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_prefix_ehandler *ehandler =
+		(struct sieve_prefix_ehandler *) _ehandler;
+	
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_error(ehandler->parent, ehandler->location, "%s",
+		_prefix_message(ehandler, location, fmt, args)); 
+}
+
+static void sieve_prefix_vwarning
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_prefix_ehandler *ehandler =
+		(struct sieve_prefix_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_warning(ehandler->parent, ehandler->location, "%s",
+		_prefix_message(ehandler, location, fmt, args)); 
+}
+
+static void sieve_prefix_vinfo
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_prefix_ehandler *ehandler =
+		(struct sieve_prefix_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_info(ehandler->parent, ehandler->location, "%s",
+		_prefix_message(ehandler, location, fmt, args)); 
+}
+
+static void sieve_prefix_vdebug
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_prefix_ehandler *ehandler =
+		(struct sieve_prefix_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_debug(ehandler->parent, ehandler->location, "%s",
+		_prefix_message(ehandler, location, fmt, args)); 
+}
+
+struct sieve_error_handler *sieve_prefix_ehandler_create
+(struct sieve_error_handler *parent, const char *location, const char *prefix)
+{
+	pool_t pool;
+	struct sieve_prefix_ehandler *ehandler;
+
+	pool = pool_alloconly_create("sieve_prefix_error_handler", 256);	
+	ehandler = p_new(pool, struct sieve_prefix_ehandler, 1);
+	ehandler->parent = parent;
+	ehandler->location = p_strdup(pool, location);
+	ehandler->prefix = p_strdup(pool, prefix);
+
+	sieve_error_handler_init(&ehandler->handler, pool, parent->max_errors);
+
+	ehandler->handler.verror = sieve_prefix_verror;
+	ehandler->handler.vwarning = sieve_prefix_vwarning;
+	ehandler->handler.vinfo = sieve_prefix_vinfo;
+	ehandler->handler.vdebug = sieve_prefix_vdebug;
+
+	return &(ehandler->handler);
+}
+
+/*
+ * Varexpand error handler 
+ *
+ *   Encapsulates an existing error handler and formats all messages using the
+ *   provided format string and variables;
+ */
+
+struct sieve_varexpand_ehandler {
+	struct sieve_error_handler handler;
+
+	struct sieve_error_handler *parent;
+
+	const char *format;
+	ARRAY_DEFINE(table, struct var_expand_table);
+};
+
+static const char *_expand_message
+(struct sieve_varexpand_ehandler *ehandler,
+	const char *location, const char *fmt, va_list args) 
+{
+	struct var_expand_table *table = array_get_modifiable(&ehandler->table, NULL);
+	string_t *str = t_str_new(256);
+
+	/* Fill in substitution items */
+	table[0].value = location;
+	table[1].value = t_strdup_vprintf(fmt, args);
+
+	/* Expand variables */
+	var_expand(str, ehandler->format, table);
+
+	return str_c(str);
+}
+
+static void sieve_varexpand_verror
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_varexpand_ehandler *ehandler =
+		(struct sieve_varexpand_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+	
+	sieve_direct_error(ehandler->parent, location, "%s",
+		_expand_message(ehandler, location, fmt, args)); 
+}
+
+static void sieve_varexpand_vwarning
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_varexpand_ehandler *ehandler =
+		(struct sieve_varexpand_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_warning(ehandler->parent, location, "%s",
+		_expand_message(ehandler, location, fmt, args)); 
+}
+
+static void sieve_varexpand_vinfo
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_varexpand_ehandler *ehandler =
+		(struct sieve_varexpand_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_info(ehandler->parent, location, "%s",
+		_expand_message(ehandler, location, fmt, args)); 
+}
+
+static void sieve_varexpand_vdebug
+(struct sieve_error_handler *_ehandler, const char *location,
+	const char *fmt, va_list args) 
+{
+	struct sieve_varexpand_ehandler *ehandler =
+		(struct sieve_varexpand_ehandler *) _ehandler;
+
+	if ( ehandler->parent == NULL ) return;
+
+	sieve_direct_debug(ehandler->parent, location, "%s",
+		_expand_message(ehandler, location, fmt, args)); 
+}
+
+struct sieve_error_handler *sieve_varexpand_ehandler_create
+(struct sieve_error_handler *parent, const char *format,
+	struct var_expand_table *table)
+{
+	pool_t pool;
+	struct sieve_varexpand_ehandler *ehandler;
+	struct var_expand_table *entry;
+	int i;
+
+	pool = pool_alloconly_create("sieve_varexpand_error_handler", 256);	
+	ehandler = p_new(pool, struct sieve_varexpand_ehandler, 1);
+	ehandler->parent = parent;
+	ehandler->format = format;
+	p_array_init(&ehandler->table, pool, 10);
+
+	sieve_error_handler_init(&ehandler->handler, pool, parent->max_errors);
+
+	entry = array_append_space(&ehandler->table);
+	entry->key = '$';
+	entry = array_append_space(&ehandler->table);
+	entry->key = 'l';
+	entry->long_key = "location";
+
+	for (i = 0; table[i].key != '\0'; i++) {
+		entry = array_append_space(&ehandler->table);
+
+		/* Sanitize substitution items */
+		entry->key = table[i].key;
+
+		if ( table[i].value != NULL )
+			entry->value = p_strdup(pool, table[i].value);
+		if ( table[i].long_key != NULL )
+			entry->long_key = p_strdup(pool, table[i].long_key);
+	}
+
+	(void)array_append_space(&ehandler->table);
+
+	ehandler->handler.verror = sieve_varexpand_verror;
+	ehandler->handler.vwarning = sieve_varexpand_vwarning;
+	ehandler->handler.vinfo = sieve_varexpand_vinfo;
+	ehandler->handler.vdebug = sieve_varexpand_vdebug;
+
+	return &(ehandler->handler);
+}
diff --git a/src/lib-sieve/sieve-error.h b/src/lib-sieve/sieve-error.h
index 0b35e9e7de930e34c4afc80658222a9f9d951e4d..0f344b8b9767dcb1d6f2cf32515e3e3f3e964f36 100644
--- a/src/lib-sieve/sieve-error.h
+++ b/src/lib-sieve/sieve-error.h
@@ -13,6 +13,8 @@
  * Forward declarations
  */
 
+struct var_expand_table;
+
 struct sieve_script;
 struct sieve_error_handler;
 
@@ -65,7 +67,7 @@ void sieve_vdebug
 	(struct sieve_error_handler *ehandler, const char *location,
 		const char *fmt, va_list args);
 void sieve_vcritical
-	(struct sieve_error_handler *ehandler, const char *location, 
+	(struct sieve_error_handler *ehandler, const char *location,
 		const char *fmt, va_list args);
 
 void sieve_error
@@ -133,4 +135,14 @@ struct sieve_error_handler *sieve_strbuf_ehandler_create
 struct sieve_error_handler *sieve_logfile_ehandler_create
 	(const char *logfile, unsigned int max_errors);  
 
+/* Wrapper: prefix all log messages */
+struct sieve_error_handler *sieve_prefix_ehandler_create
+	(struct sieve_error_handler *parent, const char *location, 
+		const char *prefix);
+
+/* Wrapper: make messages part of var expansion */
+struct sieve_error_handler *sieve_varexpand_ehandler_create
+(struct sieve_error_handler *parent, const char *format,
+	struct var_expand_table *table);
+
 #endif /* __SIEVE_ERROR_H */
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index d5f5fe78b56c4bf3e3da7144dfdba4dc6bc85fcc..1947ecd18a470739f3619af0c819b21ad73b1e5f 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -220,94 +220,13 @@ const void *sieve_result_extension_get_context
  * Error handling 
  */
 
-static const char *_get_from_address(struct mail *mail)
-{
-	struct message_address *addr;
-	const char *str;
-
-	if ( mail_get_first_header(mail, "from", &str) <= 0 )
-		return NULL;
-
-	addr = message_address_parse
-		(pool_datastack_create(), (const unsigned char *)str, strlen(str), 1, 
-			FALSE);
-
-	return addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
-		*addr->mailbox == '\0' || *addr->domain == '\0' ?
-		NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL);
-}
-
-void sieve_result_vlog_message
-(const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func,
-	const char *fmt, va_list args)
-{
-	const struct sieve_message_data *msgdata = aenv->msgdata;
-	string_t *str;
-	const char *msgid, *msg;
-
-	if ( aenv->result->ehandler == NULL ) return;
-
-	msg = t_strdup_vprintf(fmt, args);
-
-	msgid = msgdata->id;
-	msgid = ( msgid == NULL ? "unspecified" : str_sanitize(msgid, 80) );
-
-	if ( aenv->scriptenv->action_log_format == NULL ) {
-		log_func(aenv->result->ehandler, NULL, "msgid=%s: %s", msgid, msg); 
-
-	} else {
-		static struct var_expand_table static_tab[] = {
-			{ '$', NULL, NULL },
-			{ 'm', NULL, "msgid" },
-			{ 's', NULL, "subject" },
-			{ 'f', NULL, "from" },
-			{ 'l', NULL, "location" },
-			{ '\0', NULL, NULL }
-		};
-		struct var_expand_table *tab;
-		unsigned int i;
-
-		tab = t_malloc(sizeof(static_tab));
-		memcpy(tab, static_tab, sizeof(static_tab));
-
-		/* Fill in substitution items */
-		tab[0].value = msg;
-		tab[1].value = msgid;
-		(void)mail_get_first_header_utf8(msgdata->mail, "Subject", &tab[2].value);
-		tab[3].value = _get_from_address(msgdata->mail);
-		tab[4].value = "";
-
-		/* Sanitize substitution items */
-		for (i = 1; tab[i].key != '\0'; i++)
-			tab[i].value = str_sanitize(tab[i].value, 80);
-
-		/* Expand variables */
-		str = t_str_new(256);
-		var_expand(str, aenv->scriptenv->action_log_format, tab);
-
-		/* Log message */
-		log_func(aenv->result->ehandler, NULL, "%s", str_c(str));
-	}
-}
-
-void sieve_result_log_message
-(const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func,
-	const char *fmt, ...)
-{
-	va_list args;
-	
-	va_start(args, fmt);	
-	sieve_result_vlog_message(aenv, log_func, fmt, args); 
-	va_end(args);
-}
-
 void sieve_result_error
 (const struct sieve_action_exec_env *aenv, const char *fmt, ...)
 {
 	va_list args;
 	
 	va_start(args, fmt);	
-	sieve_result_vlog_message(aenv, sieve_error, fmt, args); 
+	sieve_verror(aenv->ehandler, NULL, fmt, args); 
 	va_end(args);
 }
 
@@ -317,7 +236,7 @@ void sieve_result_warning
 	va_list args;
 	
 	va_start(args, fmt);	
-	sieve_result_vlog_message(aenv, sieve_warning, fmt, args); 
+	sieve_vwarning(aenv->ehandler, NULL, fmt, args);
 	va_end(args);
 }
 
@@ -327,7 +246,7 @@ void sieve_result_log
 	va_list args;
 	
 	va_start(args, fmt);	
-	sieve_result_vlog_message(aenv, sieve_info, fmt, args); 
+	sieve_vinfo(aenv->ehandler, NULL, fmt, args);
 	va_end(args);
 }
 
@@ -918,8 +837,61 @@ bool sieve_result_print
  * Result execution
  */
 
+static const char *_get_from_address(struct mail *mail)
+{
+	struct message_address *addr;
+	const char *str;
+
+	if ( mail_get_first_header(mail, "from", &str) <= 0 )
+		return NULL;
+
+	addr = message_address_parse
+		(pool_datastack_create(), (const unsigned char *)str, strlen(str), 1, 
+			FALSE);
+
+	return addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
+		*addr->mailbox == '\0' || *addr->domain == '\0' ?
+		NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL);
+}
+
+static void _sieve_result_prepare_environment(struct sieve_result *result)
+{
+	const struct sieve_message_data *msgdata = result->action_env.msgdata;
+	const struct sieve_script_env *senv = result->action_env.scriptenv;
+	const struct var_expand_table static_tab[] = {
+		{ 'm', NULL, "msgid" },
+		{ 's', NULL, "subject" },
+		{ 'f', NULL, "from" },
+		{ '\0', NULL, NULL }
+	};
+	const char *msgid = msgdata->id;
+	struct var_expand_table *tab;
+	unsigned int i;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	msgid = ( msgid == NULL ? "unspecified" : str_sanitize(msgid, 80) );
+
+	/* Fill in substitution items */
+	tab[0].value = msgid;
+	(void)mail_get_first_header_utf8(msgdata->mail, "Subject", &tab[1].value);
+	tab[2].value = _get_from_address(msgdata->mail);
+	tab[3].value = "";
+
+	/* Sanitize substitution items */
+	for (i = 0; tab[i].key != '\0'; i++)
+		tab[i].value = str_sanitize(tab[i].value, 80);
+
+	result->action_env.exec_status = 
+		( senv->exec_status == NULL ? 
+			t_new(struct sieve_exec_status, 1) : senv->exec_status );
+	result->action_env.ehandler = sieve_varexpand_ehandler_create
+		(result->ehandler, senv->action_log_format, tab);
+}
+
 static bool _sieve_result_implicit_keep
-	(struct sieve_result *result, bool rollback)
+(struct sieve_result *result, bool rollback)
 {	
 	struct sieve_result_action *rac;
 	bool success = TRUE;
@@ -1024,11 +996,7 @@ static bool _sieve_result_implicit_keep
 bool sieve_result_implicit_keep
 (struct sieve_result *result)
 {
-	const struct sieve_script_env *senv = result->action_env.scriptenv;
-	struct sieve_exec_status dummy_status;
-
-	result->action_env.exec_status = 
-		( senv->exec_status == NULL ? &dummy_status : senv->exec_status );
+	_sieve_result_prepare_environment(result);
 
 	return _sieve_result_implicit_keep(result, TRUE);	
 }
@@ -1053,8 +1021,6 @@ void sieve_result_mark_executed(struct sieve_result *result)
 int sieve_result_execute
 (struct sieve_result *result, bool *keep)
 {
-	const struct sieve_script_env *senv = result->action_env.scriptenv;
-	struct sieve_exec_status dummy_status;
 	bool implicit_keep = TRUE;
 	bool success = TRUE, commit_ok;
 	struct sieve_result_action *rac, *first_action;
@@ -1064,8 +1030,7 @@ int sieve_result_execute
 
 	/* Prepare environment */
 
-	result->action_env.exec_status = 
-		( senv->exec_status == NULL ? &dummy_status : senv->exec_status );
+	_sieve_result_prepare_environment(result);
 	
 	/* Make notice of this attempt */
 	
diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h
index 8840544d6fb850b28e52d6e480a235b8022a577e..0bb4e64912658fadcd4f2995e17799ce2c610b2e 100644
--- a/src/lib-sieve/sieve-result.h
+++ b/src/lib-sieve/sieve-result.h
@@ -86,22 +86,15 @@ bool sieve_result_print
  * Error handling 
  */
 
-void sieve_result_vlog_message
-	(const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func,
-		const char *fmt, va_list args);
-void sieve_result_log_message
-	(const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func,
-		const char *fmt, ...) ATTR_FORMAT(3, 4);
-
-void sieve_result_log
-	(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
-		ATTR_FORMAT(2, 3);
-void sieve_result_warning
-	(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
-		ATTR_FORMAT(2, 3);
 void sieve_result_error
-	(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
-		ATTR_FORMAT(2, 3);
+(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+void sieve_result_warning
+(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+void sieve_result_log
+(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
 
 /*
  * Result composition