diff --git a/Makefile.am b/Makefile.am
index a761fde6a42fe6314cc84e58207acadb6a872d52..832e2806ed2ffdaa06c9e7c4f711ab33eeea0079 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,6 +24,7 @@ if BUILD_ENOTIFY
 ENOTIFY_TESTS = \
 	tests/extensions/enotify/basic.svtest \
 	tests/extensions/enotify/encodeurl.svtest \
+	tests/extensions/enotify/errors.svtest \
 	tests/extensions/enotify/execute.svtest
 endif
 
@@ -77,6 +78,7 @@ test_cases = \
 	tests/extensions/subaddress/rfc.svtest \
 	tests/extensions/vacation/errors.svtest \
 	tests/extensions/vacation/execute.svtest \
+	tests/extensions/vacation/references.svtest \
 	$(ENOTIFY_TESTS)
 
 if HAVE_DOVECOT_LIBS
diff --git a/TODO b/TODO
index b6b343cff3c5140f3026566ddffcf49716ed7ee8..d7e3b3b3812c0dacf7ef0ce977a42bec9766f279 100644
--- a/TODO
+++ b/TODO
@@ -35,8 +35,6 @@ Next (in order of descending priority/precedence):
 	  any vacation response that is generated. UTF-8 characters can be used in
 	  the string argument; implementations MUST convert the string to [RFC2047]
  	  encoded words if and only if non-ASCII characters are present.
-	- Vacation: properly implement construction of a References header from
-	  the original message.
 	- Body: contains various issues that need to be resolved for standards
 	  compliance. Body test support currently matches but barely exceeds the
 	  original CMU Sieve implentation in terms of standards compliance.
diff --git a/configure.in b/configure.in
index 225094d01cc1a9d2a2c444180b71fa1f43682ce3..57bcea0342920e4fb926bdb86b305cc59ccc5d69 100644
--- a/configure.in
+++ b/configure.in
@@ -7,8 +7,10 @@ AC_CONFIG_SRCDIR([src])
 # real config header ourselves.
 AC_CONFIG_HEADERS([dummy-config.h dsieve-config.h])
 
-AC_DEFINE(SIEVE_NAME, [PACKAGE_NAME], [Define to the full name of this Sieve implementation.])
-AC_DEFINE(SIEVE_VERSION, [PACKAGE_VERSION], [Define to the version of this Sieve implementation.])
+AC_DEFINE_UNQUOTED(SIEVE_NAME, "$PACKAGE_NAME", 
+	[Define to the full name of this Sieve implementation.])
+AC_DEFINE_UNQUOTED(SIEVE_VERSION, "$PACKAGE_VERSION", 
+	[Define to the version of this Sieve implementation.])
 
 AM_INIT_AUTOMAKE(no-define)
 
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index b5dc36cf12224485d55896b9df431d8f8f76ab31..1d1a63ffac3525c006b77e019869f1a8a2bec394 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -7,6 +7,8 @@
 #include "istream.h"
 #include "istream-header-filter.h"
 
+#include "rfc2822.h"
+
 #include "sieve-common.h"
 #include "sieve-limits.h"
 #include "sieve-address.h"
@@ -267,7 +269,7 @@ static void act_redirect_print
 static bool act_redirect_send	
 (const struct sieve_action_exec_env *aenv, struct act_redirect_context *ctx)
 {
-	static const char *hide_headers[] = { "Return-Path" };
+	static const char *hide_headers[] = { "Return-Path", "X-Sieve" };
 
 	const struct sieve_message_data *msgdata = aenv->msgdata;
 	const struct sieve_script_env *senv = aenv->scriptenv;
@@ -295,6 +297,9 @@ static bool act_redirect_send
 		(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers,
 			N_ELEMENTS(hide_headers), null_header_filter_callback, NULL);
 
+	/* Prepend sieve version header (should not affect signatures) */
+	rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION);
+
 	/* Pipe the message to the outgoing SMTP transport */
 	while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {	
 		if (fwrite(data, size, 1, f) == 0)
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index 15d0aef2ea3bdf45c7b386efcba87434b2d4596b..6bac6c003907158b4ce3dacb6c77f91c2d009d69 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -20,6 +20,8 @@
 #include "istream.h"
 #include "istream-header-filter.h"
 
+#include "rfc2822.h"
+
 #include "sieve-common.h"
 #include "sieve-extensions.h"
 #include "sieve-commands.h"
@@ -125,7 +127,7 @@ static int act_reject_check_duplicate
 		const char *location1, const char *location2);
 int act_reject_check_conflict
 	(const struct sieve_runtime_env *renv, const struct sieve_action *action,
-    	const struct sieve_action *other_action, void *context,
+		const struct sieve_action *other_action, void *context,
 		const char *location1, const char *location2);
 static void act_reject_print
 	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
@@ -176,10 +178,10 @@ static bool cmd_reject_generate
 	sieve_operation_emit_code(cgenv->sbin, &reject_operation);
 
 	/* Emit line number */
-    sieve_code_source_line_emit(cgenv->sbin, sieve_command_source_line(ctx));
+	sieve_code_source_line_emit(cgenv->sbin, sieve_command_source_line(ctx));
 
 	/* Generate arguments */
-    return sieve_generate_arguments(cgenv, ctx, NULL);
+	return sieve_generate_arguments(cgenv, ctx, NULL);
 }
 
 /* 
@@ -194,11 +196,11 @@ static bool ext_reject_operation_dump
 	sieve_code_descend(denv);
 	
 	/* Source line */
-    if ( !sieve_code_source_line_dump(denv, address) )
-        return FALSE;
+	if ( !sieve_code_source_line_dump(denv, address) )
+		return FALSE;
 
 	if ( !sieve_code_dumper_print_optional_operands(denv, address) )
-        return FALSE;
+		return FALSE;
 	
 	return
 		sieve_opr_string_dump(denv, address, "reason");
@@ -220,9 +222,9 @@ static int ext_reject_operation_execute
 	int ret;
 
 	/* Source line */
-    if ( !sieve_code_source_line_read(renv, address, &source_line) ) {
+	if ( !sieve_code_source_line_read(renv, address, &source_line) ) {
 		sieve_runtime_trace_error(renv, "invalid source line");
-        return SIEVE_EXEC_BIN_CORRUPT;
+		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
 	/* Optional operands (side effects) */
@@ -328,18 +330,21 @@ static bool act_reject_send
 	new_msgid = sieve_get_new_message_id(senv);
 	boundary = t_strdup_printf("%s/%s", my_pid, senv->hostname);
 
-	fprintf(f, "Message-ID: %s\r\n", new_msgid);
-	fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
-	fprintf(f, "From: Mail Delivery Subsystem <%s>\r\n",
+	rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION);
+	rfc2822_header_field_write(f, "Message-ID", new_msgid);
+	rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time));
+	rfc2822_header_field_printf(f, "From", "Mail Delivery Subsystem <%s>",
 		senv->postmaster_address);
-	fprintf(f, "To: <%s>\r\n", msgdata->return_path);
-	fprintf(f, "MIME-Version: 1.0\r\n");
-	fprintf(f, "Content-Type: "
-		"multipart/report; report-type=disposition-notification;\r\n"
-		"\tboundary=\"%s\"\r\n", boundary);
-	fprintf(f, "Subject: Automatically rejected mail\r\n");
-	fprintf(f, "Auto-Submitted: auto-replied (rejected)\r\n");
-	fprintf(f, "Precedence: bulk\r\n");
+	rfc2822_header_field_printf(f, "To", "<%s>", msgdata->return_path);
+	rfc2822_header_field_write(f, "Subject", "Automatically rejected mail");
+	rfc2822_header_field_write(f, "Auto-Submitted", "auto-replied (rejected)");
+	rfc2822_header_field_write(f, "Precedence", "bulk");
+	
+	rfc2822_header_field_write(f, "MIME-Version", "1.0");
+	rfc2822_header_field_printf(f, "Content-Type", 
+		"multipart/report; report-type=disposition-notification;\n"
+		"boundary=\"%s\"", boundary);
+	
 	fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
 
 	/* Human readable status report */
diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c
index ed38e46ebac1358014ffb6d900bcdfb18ec87f4a..89ac8c6b14d3002eb8d160e12e0dd248400c061e 100644
--- a/src/lib-sieve/plugins/enotify/cmd-notify.c
+++ b/src/lib-sieve/plugins/enotify/cmd-notify.c
@@ -160,17 +160,6 @@ const struct sieve_action act_notify = {
 	NULL
 };
 
-/* Action context information */
-		
-struct act_notify_context {
-	const char *method;
-
-	sieve_number_t importance;
-	const char *message;
-	const char *from;
-	const char *const *options;
-};
-
 /* 
  * Tag validation 
  */
@@ -388,12 +377,14 @@ static int cmd_notify_operation_execute
 	const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
 	struct sieve_side_effects_list *slist = NULL;
-	struct act_notify_context *act;
+	struct sieve_enotify_context *act;
+	void *method_context;
 	pool_t pool;
 	int opt_code = 1;
 	sieve_number_t importance = 1;
 	struct sieve_coded_stringlist *options = NULL;
-	string_t *method, *message = NULL, *from = NULL; 
+	const struct sieve_enotify_method *method;
+	string_t *method_uri, *message = NULL, *from = NULL; 
 	unsigned int source_line;
 
 	/*
@@ -456,7 +447,7 @@ static int cmd_notify_operation_execute
 	}
 	
 	/* Reason operand */
-	if ( !sieve_opr_string_read(renv, address, &method) ) {
+	if ( !sieve_opr_string_read(renv, address, &method_uri) ) {
 		sieve_runtime_trace_error(renv, "invalid method operand");
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
@@ -467,21 +458,31 @@ static int cmd_notify_operation_execute
 
 	sieve_runtime_trace(renv, "NOTIFY action");	
 
-	/* Add notify action to the result */
-
-	pool = sieve_result_pool(renv->result);
-	act = p_new(pool, struct act_notify_context, 1);
-	act->method = p_strdup(pool, str_c(method));
-	act->importance = importance;
-	if ( message != NULL )
-		act->message = p_strdup(pool, str_c(message));
-	if ( from != NULL )
-		act->from = p_strdup(pool, str_c(from));
-	if ( options != NULL )
-		sieve_coded_stringlist_read_all(options, pool, &(act->options));
+	/* Check operands */
+
+	if ( (method=ext_enotify_runtime_check_operands
+		(renv, source_line, str_c(method_uri), 
+			message == NULL ? NULL : str_c(message), 
+			from == NULL ? NULL : str_c(from), 
+			&method_context)) != NULL ) {
+		/* Add notify action to the result */
+
+		pool = sieve_result_pool(renv->result);
+		act = p_new(pool, struct sieve_enotify_context, 1);
+		act->method = method;
+		act->method_context = method_context;
+		act->importance = importance;
+		if ( message != NULL )
+			act->message = p_strdup(pool, str_c(message));
+		if ( from != NULL )
+			act->from = p_strdup(pool, str_c(from));
 		
-	return ( sieve_result_add_action
-		(renv, &act_notify, slist, source_line, (void *) act, 0) >= 0 );
+		return ( sieve_result_add_action
+			(renv, &act_notify, slist, source_line, (void *) act, 0) >= 0 );
+	}
+	
+	/* Erroneous notify action is no reason to kill the script */
+	return SIEVE_EXEC_OK;
 }
 
 /*
@@ -507,17 +508,14 @@ static void act_notify_print
 	const struct sieve_result_print_env *rpenv, void *context, 
 	bool *keep ATTR_UNUSED)	
 {
-	struct act_notify_context *ctx = (struct act_notify_context *) context;
-	
-	sieve_result_action_printf( rpenv, "send notification with method %s:", 
-		ctx->method);
-	sieve_result_printf(rpenv,   "    => importance   : %d\n", ctx->importance);
-	if ( ctx->message != NULL )
-		sieve_result_printf(rpenv, "    => message: \n%s\n", ctx->message);
-	if ( ctx->from != NULL )
-		sieve_result_printf(rpenv, "    => from   : %s\n", ctx->from);
+	const struct sieve_enotify_context *nctx = 
+		(const struct sieve_enotify_context *) context;
+
+	sieve_result_action_printf
+		( rpenv, "send notification with method '%s:':", nctx->method->identifier);
 		
-	/* FIXME: list options */
+	if ( nctx->method->action_print != NULL )
+		nctx->method->action_print(rpenv, nctx);
 }
 
 /* Result execution */
@@ -527,7 +525,13 @@ static bool act_notify_commit
 	const struct sieve_action_exec_env *aenv, void *tr_context, 
 	bool *keep ATTR_UNUSED)
 {
-	return FALSE;
+	const struct sieve_enotify_context *nctx = 
+		(const struct sieve_enotify_context *) tr_context;
+
+	if ( nctx->method->action_execute != NULL )
+		return nctx->method->action_execute(aenv, nctx);
+			
+	return TRUE;
 }
 
 
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
index edf5b5895d67a72b8cc05eb57c0d904b09c823fa..d991e21d3a3de9f3e68307fba5c03bbb55c1fc67 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
@@ -10,6 +10,7 @@
 #include "sieve-ast.h"
 #include "sieve-commands.h"
 #include "sieve-validator.h"
+#include "sieve-interpreter.h"
 
 #include "ext-enotify-limits.h"
 #include "ext-enotify-common.h"
@@ -156,3 +157,38 @@ bool ext_enotify_uri_validate
 	return TRUE;
 }
 
+const struct sieve_enotify_method *ext_enotify_runtime_check_operands
+(const struct sieve_runtime_env *renv, unsigned int source_line,
+	const char *method_uri,	const char *message, const char *from, void **context)
+{
+	const char *uri = method_uri;
+	const char *scheme;
+	const struct sieve_enotify_method *method;
+	
+	if ( (scheme=ext_enotify_uri_scheme_parse(&uri)) == NULL ) {
+		sieve_runtime_error
+			(renv, sieve_error_script_location(renv->script, source_line),
+				"invalid scheme part for method URI '%s'", 
+				str_sanitize(method_uri, 80));
+		return NULL;
+	}
+	
+	if ( (method=ext_enotify_method_find(scheme)) == NULL ) {
+		sieve_runtime_error
+			(renv, sieve_error_script_location(renv->script, source_line),
+				"invalid notify method '%s'", scheme);
+		return NULL;
+	}
+
+	if ( method->runtime_check_operands != NULL ) {
+		if ( method->runtime_check_operands
+			(renv, source_line, method_uri, uri, message, from, context) )
+			return method;
+		
+		return NULL;
+	}
+
+	*context = NULL;	
+	return method;
+}
+
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h
index 8353188b41197cd954dd3b3115c5f22158510ebb..0ec6b3d1997e8c837c1c2f5c737c4fe7126f74bb 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h
@@ -67,10 +67,19 @@ const struct sieve_enotify_method *ext_enotify_method_find
 	(const char *identifier);
 	
 /*
- * URI validation
+ * Validation
  */
  
 bool ext_enotify_uri_validate
 	(struct sieve_validator *valdtr, struct sieve_ast_argument *arg);
 
+/*
+ * Runtime
+ */
+
+const struct sieve_enotify_method *ext_enotify_runtime_check_operands
+	(const struct sieve_runtime_env *renv, unsigned int source_line,
+		const char *method_uri,	const char *message, const char *from, 
+		void **context);
+
 #endif /* __EXT_ENOTIFY_COMMON_H */
diff --git a/src/lib-sieve/plugins/enotify/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/ntfy-mailto.c
index 7ce3873b56a9157c0aa56580b92e665140ba9f34..07df45a5145fcf4df4694722b7593420ef857cef 100644
--- a/src/lib-sieve/plugins/enotify/ntfy-mailto.c
+++ b/src/lib-sieve/plugins/enotify/ntfy-mailto.c
@@ -14,6 +14,7 @@
 #include "sieve-ast.h"
 #include "sieve-commands.h"
 #include "sieve-validator.h"
+#include "sieve-interpreter.h"
 #include "sieve-actions.h"
 #include "sieve-result.h"
 
@@ -24,6 +25,19 @@
  */
  
 #define NTFY_MAILTO_MAX_RECIPIENTS 4
+#define NTFY_MAILTO_MAX_HEADERS 16
+
+/* 
+ * Types 
+ */
+
+struct ntfy_mailto_header_field {
+	const char *name;
+	const char *body;
+};
+
+ARRAY_DEFINE_TYPE(recipients, const char *);
+ARRAY_DEFINE_TYPE(headers, struct ntfy_mailto_header_field);
 
 /* 
  * Mailto notification method
@@ -32,16 +46,62 @@
 static bool ntfy_mailto_validate_uri
 	(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
 		const char *uri_body);
-static bool ntfy_mailto_execute
+static bool ntfy_mailto_runtime_check_operands
+	(const struct sieve_runtime_env *renv, unsigned int source_line, 
+		const char *uri, const char *uri_body, const char *message, 
+		const char *from, void **context);
+static void ntfy_mailto_action_print
+	(const struct sieve_result_print_env *rpenv, 
+		const struct sieve_enotify_context *nctx);	
+static bool ntfy_mailto_action_execute
 	(const struct sieve_action_exec_env *aenv, 
 		const struct sieve_enotify_context *nctx);
 
 const struct sieve_enotify_method mailto_notify = {
 	"mailto",
 	ntfy_mailto_validate_uri,
-	ntfy_mailto_execute
+	ntfy_mailto_runtime_check_operands,
+	ntfy_mailto_action_print,
+	ntfy_mailto_action_execute
+};
+
+/*
+ * Method context data
+ */
+ 
+struct ntfy_mailto_context {
+	ARRAY_TYPE(recipients) recipients;
+	ARRAY_TYPE(headers) headers;
+	const char *subject;
+	const char *body;
 };
 
+/*
+ * Reserved headers
+ */
+ 
+static const char *_reserved_headers[] = {
+	"auto-submitted",
+	"received",
+	"message-id",
+	"data",
+	NULL
+};
+
+static inline bool _ntfy_mailto_header_allowed(const char *field_name)
+{
+	const char **rhdr = _reserved_headers;
+
+	/* Check whether it is reserved */
+	while ( *rhdr != NULL ) {
+		if ( strcasecmp(field_name, *rhdr) == 0 )
+			return FALSE;
+		rhdr++;
+	}
+
+	return TRUE;
+}
+
 /*
  * Mailto URI parsing
  */
@@ -49,7 +109,7 @@ const struct sieve_enotify_method mailto_notify = {
 /* FIXME: much of this implementation will be common to other URI schemes. This
  *        should be merged into a common implementation.
  */
-
+  
 static inline int _decode_hex_digit(char digit)
 {
 	switch ( digit ) {
@@ -61,7 +121,7 @@ static inline int _decode_hex_digit(char digit)
 		return (int) digit - 'a' + 0x0a;
 		
 	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
-		return (int) digit - 'a' + 0x0A;
+		return (int) digit - 'A' + 0x0A;
 	}
 	
 	return -1;
@@ -85,17 +145,24 @@ static bool _parse_hex_value(const char **in, char *out)
 	return (*out != '\0');	
 }
 
+static inline pool_t array_get_pool_i(struct array *array)
+{
+	return buffer_get_pool(array->buffer);
+}
+#define array_get_pool(array) \
+	array_get_pool_i(&(array)->arr)
+
 static bool _uri_parse_recipients
-(const char **uri_p, const char *const **recipients_r, const char **error_r)
+(const char **uri_p, ARRAY_TYPE(recipients) *recipients_r, const char **error_r)
 {
-	ARRAY_DEFINE(recipients, const char *);
 	string_t *to = t_str_new(128);
 	const char *recipient;
 	const char *p = *uri_p;
+	pool_t pool = NULL;
 	
 	if ( recipients_r != NULL )
-		t_array_init(&recipients, NTFY_MAILTO_MAX_RECIPIENTS);
-	
+		pool = array_get_pool(recipients_r);
+		
 	while ( *p != '\0' && *p != '?' ) {
 		if ( *p == '%' ) {
 			/* % encoded character */
@@ -114,11 +181,13 @@ static bool _uri_parse_recipients
 				recipient = str_c(to);
 				
 				/* Verify recipient */
+			
+				// FIXME ....
 				
 				/* Add recipient to the list */
 				if ( recipients_r != NULL ) {
-					recipient = t_strdup(recipient);
-					array_append(&recipients, &recipient, 1);
+					recipient = p_strdup(pool, recipient);
+					array_append(recipients_r, &recipient, 1);
 				}
 			
 				/* Reset for next recipient */
@@ -141,40 +210,34 @@ static bool _uri_parse_recipients
 	
 	/* Verify recipient */
 
-	// ....
+	// FIXME ....
 		
 	if ( recipients_r != NULL ) {
 		/* Add recipient to the list */
-		recipient = t_strdup(recipient);
-		array_append(&recipients, &recipient, 1);
-	
-		/* Return recipients */
-		(void)array_append_space(&recipients);
-		*recipients_r = array_idx(&recipients, 0);
+		recipient = p_strdup(pool, recipient);
+		array_append(recipients_r, &recipient, 1);
 	}
 	
 	*uri_p = p;
 	return TRUE;
 }
 
-struct _header_field {
-	const char *name;
-	const char *body;
-};
-
 static bool _uri_parse_headers
-(const char **uri_p, struct _header_field *const *headers_r, 
-	const char **error_r)
+(const char **uri_p, ARRAY_TYPE(headers) *headers_r, const char **body, 
+	const char **subject, const char **error_r)
 {
-	ARRAY_DEFINE(headers, struct _header_field);
 	string_t *field = t_str_new(128);
 	const char *p = *uri_p;
+	pool_t pool = NULL;
 	
 	if ( headers_r != NULL )
-		t_array_init(&headers, NTFY_MAILTO_MAX_RECIPIENTS);
-	
+		pool = array_get_pool(headers_r);
+		
 	while ( *p != '\0' ) {
-		struct _header_field *hdrf;
+		enum { _HNAME_GENERIC, _HNAME_SUBJECT, _HNAME_BODY } hname_type = 
+			_HNAME_GENERIC;
+		struct ntfy_mailto_header_field *hdrf = NULL;
+		const char *field_name;
 		
 		/* Parse field name */
 		while ( *p != '\0' && *p != '=' ) {
@@ -184,6 +247,7 @@ static bool _uri_parse_headers
 			if ( ch == '%' ) {
 				/* Encoded, parse 2-digit hex value */
 				if ( !_parse_hex_value(&p, &ch) ) {
+					printf("F: %s\n", p);
 					*error_r = "invalid % encoding";
 					return FALSE;
 				}
@@ -192,11 +256,28 @@ static bool _uri_parse_headers
 		}
 		if ( *p != '\0' ) p++;
 
-		if ( headers_r != NULL ) {
-			hdrf = array_append_space(&headers);
-			hdrf->name = t_strdup(str_c(field));
+		/* Verify field name */
+		if ( !rfc2822_header_field_name_verify(str_c(field), str_len(field)) ) {
+			*error_r = "invalid header field name";
+			return FALSE;
+		}
+
+		/* Add new header field to array and assign its name */
+		field_name = str_c(field);
+		if ( strcasecmp(field_name, "subject") == 0 )
+			hname_type = _HNAME_SUBJECT;
+		else if ( strcasecmp(field_name, "body") == 0 )
+			hname_type = _HNAME_BODY;
+		else if ( _ntfy_mailto_header_allowed(field_name) ) {
+			if ( headers_r != NULL ) {
+				hdrf = array_append_space(headers_r);
+				hdrf->name = p_strdup(pool, field_name);
+			}
+		} else {
+			hdrf = NULL;
 		}
 		
+		/* Reset for field body */
 		str_truncate(field, 0);
 		
 		/* Parse field body */		
@@ -207,6 +288,7 @@ static bool _uri_parse_headers
 			if ( ch == '%' ) {
 				/* Encoded, parse 2-digit hex value */
 				if ( !_parse_hex_value(&p, &ch) ) {
+					printf("F: %s\n", p);
 					*error_r = "invalid % encoding";
 					return FALSE;
 				}
@@ -215,10 +297,31 @@ static bool _uri_parse_headers
 		}
 		if ( *p != '\0' ) p++;
 		
+		/* Verify field body */
+		
+		// FIXME ....
+		
+		/* Assign field body */
+
 		if ( headers_r != NULL ) {
-			hdrf->body = t_strdup(str_c(field));
-			str_truncate(field, 0);
+			switch ( hname_type ) {
+			case _HNAME_SUBJECT:
+				if ( subject != NULL )
+					*subject = p_strdup(pool, str_c(field));
+				break;
+			case _HNAME_BODY:
+				if ( subject != NULL )
+					*body = p_strdup(pool, str_c(field));
+				break;
+			case _HNAME_GENERIC:
+				if ( hdrf != NULL ) 
+					hdrf->body = p_strdup(pool, str_c(field));
+				break;
+			}
 		}
+			
+		/* Reset for next name */
+		str_truncate(field, 0);
 	}	
 	
 	/* Skip '&' */
@@ -229,8 +332,9 @@ static bool _uri_parse_headers
 }
 
 static bool ntfy_mailto_parse_uri
-(const char *uri_body, const char *const **recipients_r, 
-	struct _header_field *const *headers_r, const char **error_r)
+(const char *uri_body, ARRAY_TYPE(recipients) *recipients_r, 
+	ARRAY_TYPE(headers) *headers_r, const char **body, const char **subject,
+	const char **error_r)
 {
 	const char *p = uri_body;
 	
@@ -260,7 +364,7 @@ static bool ntfy_mailto_parse_uri
 	
 	while ( *p != '\0' ) {		
 		/* Extract hfield item by searching for '&' and decoding '%' items */
-		if ( !_uri_parse_headers(&p, headers_r, error_r) )
+		if ( !_uri_parse_headers(&p, headers_r, body, subject, error_r) )
 			return FALSE;		
 	}
 	
@@ -277,7 +381,7 @@ static bool ntfy_mailto_validate_uri
 {	
 	const char *error;
 	
-	if ( !ntfy_mailto_parse_uri(uri_body, NULL, NULL, &error) ) {
+	if ( !ntfy_mailto_parse_uri(uri_body, NULL, NULL, NULL, NULL, &error) ) {
 		sieve_argument_validate_error(valdtr, arg, 
 			"invalid mailto URI '%s': %s", 
 			str_sanitize(sieve_ast_argument_strc(arg), 80), error);
@@ -288,7 +392,81 @@ static bool ntfy_mailto_validate_uri
 }
 
 /*
- * Execution
+ * Runtime
+ */
+ 
+static bool ntfy_mailto_runtime_check_operands
+(const struct sieve_runtime_env *renv, unsigned int source_line, 
+  const char *mailto_uri, const char *uri_body, 
+  const char *message ATTR_UNUSED, const char *from ATTR_UNUSED, 
+  void **context)
+{
+	pool_t pool;
+	struct ntfy_mailto_context *nctx;
+	const char *error;
+	
+	/* Need to create context before validation to have arrays present */
+	pool = sieve_result_pool(renv->result);
+	nctx = p_new(pool, struct ntfy_mailto_context, 1);
+	p_array_init(&nctx->recipients, pool, NTFY_MAILTO_MAX_RECIPIENTS);
+	p_array_init(&nctx->headers, pool, NTFY_MAILTO_MAX_HEADERS);
+
+	if ( !ntfy_mailto_parse_uri
+		(uri_body, &nctx->recipients, &nctx->headers, &nctx->body, &nctx->subject, 
+			&error) ) {
+		sieve_runtime_error
+			(renv, sieve_error_script_location(renv->script, source_line),
+				"invalid mailto URI '%s': %s", str_sanitize(mailto_uri, 80), error);
+		return FALSE;
+	}
+	
+	*context = (void *) nctx;
+
+	return TRUE;	
+}
+
+/*
+ * Action printing
+ */
+ 
+static void ntfy_mailto_action_print
+(const struct sieve_result_print_env *rpenv, 
+	const struct sieve_enotify_context *nctx)
+{
+	unsigned int count, i;
+	const char *const *recipients;
+	const struct ntfy_mailto_header_field *headers;
+	struct ntfy_mailto_context *mtctx = 
+		(struct ntfy_mailto_context *) nctx->method_context;
+	
+	sieve_result_printf(rpenv,   "    => importance   : %d\n", nctx->importance);
+	if ( nctx->message != NULL )
+		sieve_result_printf(rpenv, "    => subject      : %s\n", nctx->message);
+	else if ( mtctx->subject != NULL )
+		sieve_result_printf(rpenv, "    => subject      : %s\n", mtctx->subject);
+	if ( nctx->from != NULL )
+		sieve_result_printf(rpenv, "    => from         : %s\n", nctx->from);
+
+	sieve_result_printf(rpenv,   "    => recipients   :\n" );
+	recipients = array_get(&mtctx->recipients, &count);
+	for ( i = 0; i < count; i++ ) {
+		sieve_result_printf(rpenv,   "       + %s\n", recipients[i]);
+	}
+	
+	sieve_result_printf(rpenv,   "    => headers      :\n" );
+	headers = array_get(&mtctx->headers, &count);
+	for ( i = 0; i < count; i++ ) {
+		sieve_result_printf(rpenv,   "       + %s: %s\n", 
+			headers[i].name, headers[i].body);
+	}
+	
+	if ( mtctx->body != NULL )
+		sieve_result_printf(rpenv, "    => body         : \n--\n%s\n--\n\n", 
+			mtctx->body);
+}
+
+/*
+ * Action execution
  */
 
 static bool _contains_8bit(const char *msg)
@@ -302,18 +480,24 @@ static bool _contains_8bit(const char *msg)
 	
 	return FALSE;
 }
- 
-static bool ntfy_mailto_execute
+
+static bool ntfy_mailto_action_execute
 (const struct sieve_action_exec_env *aenv, 
 	const struct sieve_enotify_context *nctx)
 { 
 	const struct sieve_message_data *msgdata = aenv->msgdata;
 	const struct sieve_script_env *senv = aenv->scriptenv;
+	struct ntfy_mailto_context *mtctx = 
+		(struct ntfy_mailto_context *) nctx->method_context;	
+	const char *from = NULL; 
+	const char *subject = mtctx->subject;
+	const char *body = mtctx->body;
+	const char *const *recipients;
 	void *smtp_handle;
+	unsigned int count, i;
 	FILE *f;
 	const char *outmsgid;
-	const char *recipient = "BOGUS";
-	
+
 	/* Just to be sure */
 	if ( senv->smtp_open == NULL || senv->smtp_close == NULL ) {
 		sieve_result_warning(aenv, 
@@ -321,52 +505,106 @@ static bool ntfy_mailto_execute
 		return FALSE;
 	}
 	
-	smtp_handle = senv->smtp_open(msgdata->return_path, NULL, &f);
-	outmsgid = sieve_get_new_message_id(senv);
-	
-	fprintf(f, "Message-ID: %s\r\n", outmsgid);
-	fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
-	fprintf(f, "X-Sieve: %s\r\n", SIEVE_IMPLEMENTATION);
-	
-	switch ( nctx->importance ) {
-	case 1:
-		fprintf(f, "X-Priority: 1 (Highest)\r\n");
-		fprintf(f, "Importance: High\r\n");
-		break;
-	case 3:
-    fprintf(f, "X-Priority: 5 (Lowest)\r\n");
-    fprintf(f, "Importance: Low\r\n");
-    break;
-	case 2:
-	default:
-		fprintf(f, "X-Priority: 3 (Normal)\r\n");
-		fprintf(f, "Importance: Normal\r\n");
-		break;
+	/* Determine from address */
+	if ( msgdata->return_path != NULL ) {
+		if ( nctx->from == NULL )
+			from = senv->postmaster_address;
+		else
+			/* FIXME: validate */
+			from = nctx->from;
 	}
-	 
-	fprintf(f, "From: Postmaster <%s>\r\n", senv->postmaster_address);
-	fprintf(f, "To: <%s>\r\n", recipient);
-	fprintf(f, "Subject: [SIEVE] New mail notification\r\n");
-	fprintf(f, "Auto-Submitted: auto-generated (notify)\r\n");
-	fprintf(f, "Precedence: bulk\r\n");
 	
-	if (_contains_8bit(nctx->message)) {
-			fprintf(f, "MIME-Version: 1.0\r\n");
-	    fprintf(f, "Content-Type: text/plain; charset=UTF-8\r\n");
-	    fprintf(f, "Content-Transfer-Encoding: 8bit\r\n");
+	/* Determine subject */
+	if ( nctx->message != NULL ) {
+		/* FIXME: handle UTF-8 */
+		subject = str_sanitize(nctx->message, 256);
+	} else if ( subject == NULL ) {
+		const char *const *hsubject;
+		
+		/* Fetch subject from original message */
+		if ( mail_get_headers_utf8
+			(aenv->msgdata->mail, "subject", &hsubject) >= 0 )
+			subject = t_strdup_printf("Notification: %s", hsubject[0]);
+		else
+			subject = "Notification: (no subject)";
 	}
-	fprintf(f, "\r\n");
-	fprintf(f, "%s\r\n", nctx->message);
+
+	/* Send message to all recipients */
+	recipients = array_get(&mtctx->recipients, &count);
+	for ( i = 0; i < count; i++ ) {
+		const struct ntfy_mailto_header_field *headers;
+		unsigned int h, hcount;
+
+		smtp_handle = senv->smtp_open(recipients[i], from, &f);
+		outmsgid = sieve_get_new_message_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_printf(f, "From", "<%s>", from);
+		rfc2822_header_field_printf(f, "To", "<%s>", recipients[i]);
+		rfc2822_header_field_write(f, "Subject", subject);
+			
+		rfc2822_header_field_write(f, "Auto-Submitted", "auto-notified");
+		rfc2822_header_field_write(f, "Precedence", "bulk");
+
+		/* Set importance */
+		switch ( nctx->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 */
+		
+		/* FIXME: ignore from and auto-submitted and recognize body, subject, to and
+		 * cc.
+		 */
+		headers = array_get(&mtctx->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 ( senv->smtp_close(smtp_handle) ) {
-		sieve_result_log(aenv, 
-			"sent mail notification to <%s>", str_sanitize(recipient, 80));
-	} else {
-		sieve_result_error(aenv,
-			"failed to send mail notification to <%s> "
-			"(refer to system log for more information)", 
-			str_sanitize(recipient, 80));
+		if ( senv->smtp_close(smtp_handle) ) {
+			sieve_result_log(aenv, 
+				"sent mail notification to <%s>", str_sanitize(recipients[i], 80));
+		} else {
+			sieve_result_error(aenv,
+				"failed to send mail notification to <%s> "
+				"(refer to system log for more information)", 
+				str_sanitize(recipients[i], 80));
+		}
 	}
 
 	return TRUE;
 }
+
diff --git a/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h b/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
index e635e9144ad6f0008a652a8da3fbc3341d095451..5dbed7d7f07d1919b5dba43f5bbb81d117b1f342 100644
--- a/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
+++ b/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
@@ -8,14 +8,11 @@
 
 #include "sieve-common.h"
 
-struct sieve_enotify_context {
-	const char *method_uri;
-
-	sieve_number_t importance;
-	const char *message;
-	const char *from;
-	const char *const *options;
-};
+/*
+ * Forward declarations
+ */
+ 
+struct sieve_enotify_context;
 
 /*
  * Notify methods
@@ -28,14 +25,39 @@ struct sieve_enotify_method {
 	bool (*validate_uri)
 		(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
 			const char *uri_body);
+
+	/* Runtime */
+	bool (*runtime_check_operands)
+		(const struct sieve_runtime_env *renv, unsigned int source_line,
+			const char *uri, const char *uri_body, const char *message, 
+			const char *from, void **context);
+			
+	/* Action print */
+	void (*action_print)
+		(const struct sieve_result_print_env *rpenv, 
+			const struct sieve_enotify_context *nctx);	
 			
-	/* Execution */
-	bool (*execute)
+	/* Action execution */
+	bool (*action_execute)
 		(const struct sieve_action_exec_env *aenv, 
 			const struct sieve_enotify_context *nctx);
 };
 
 void sieve_enotify_method_register(const struct sieve_enotify_method *method);
 
+/*
+ * Action context
+ */
+ 
+struct sieve_enotify_context {
+	const struct sieve_enotify_method *method;
+	void *method_context;
+	
+	sieve_number_t importance;
+	const char *message;
+	const char *from;
+};
+
+
 #endif /* __SIEVE_EXT_ENOTIFY_H */
 
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index d1b1dc5daafea68847f01ce340a970258129f5c7..55a92020c6815bde2e3ff0173ce3ed430f1bfcf4 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -2,6 +2,8 @@
  */
 
 #include "lib.h"
+#include "str.h"
+#include "strfuncs.h"
 #include "md5.h"
 #include "hostpid.h"
 #include "str-sanitize.h"
@@ -9,6 +11,8 @@
 #include "message-date.h"
 #include "ioloop.h"
 
+#include "rfc2822.h"
+
 #include "sieve-common.h"
 #include "sieve-code.h"
 #include "sieve-address.h"
@@ -796,6 +800,8 @@ static bool act_vacation_send
 	void *smtp_handle;
 	FILE *f;
  	const char *outmsgid;
+ 	const char *const *headers;
+	int references;
 
 	/* Check smpt functions just to be sure */
 
@@ -811,31 +817,40 @@ static bool act_vacation_send
 
 	/* Produce a proper reply */
     
-	fprintf(f, "Message-ID: %s\r\n", outmsgid);
-	fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
+	rfc2822_header_field_write(f, "Message-ID", outmsgid);
+	rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time));
 	if ( ctx->from != NULL && *(ctx->from) != '\0' )
-		fprintf(f, "From: <%s>\r\n", ctx->from);
+		rfc2822_header_field_printf(f, "From", "<%s>", ctx->from);
 	else
-		fprintf(f, "From: <%s>\r\n", msgdata->to_address);
+		rfc2822_header_field_printf(f, "From", "<%s>", msgdata->to_address);
 		
-	fprintf(f, "To: <%s>\r\n", msgdata->return_path);
-	fprintf(f, "Subject: %s\r\n", str_sanitize(ctx->subject, 80));
-	
+	rfc2822_header_field_printf(f, "To", "<%s>", msgdata->return_path);
+	rfc2822_header_field_printf(f, "Subject", "%s", 
+		str_sanitize(ctx->subject, 256));
+
+	references = mail_get_headers_utf8
+		(aenv->msgdata->mail, "references", &headers);
+			
 	if ( msgdata->id != NULL ) {
-		fprintf(f, "In-Reply-To: %s\r\n", msgdata->id);
-		
-		/* FIXME: Update References header */
+		rfc2822_header_field_write(f, "In-Reply-To", msgdata->id);
+	
+		if ( references >= 0 )
+			rfc2822_header_field_write
+				(f, "References", t_strconcat(headers[0], " ", msgdata->id, NULL));
+		else
+			rfc2822_header_field_write(f, "References", msgdata->id);
+	} else if ( references > 0 ) {
+		rfc2822_header_field_write(f, "References", headers[0]);
 	}
-	fprintf(f, "Auto-Submitted: auto-replied (vacation)\r\n");
-
-	fprintf(f, "X-Sieve: %s\r\n", SIEVE_IMPLEMENTATION);
-
-	fprintf(f, "Precedence: bulk\r\n");
-	fprintf(f, "MIME-Version: 1.0\r\n");
+			
+	rfc2822_header_field_write(f, "Auto-Submitted", "auto-replied (vacation)");
+	rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION);
+	rfc2822_header_field_write(f, "Precedence", "bulk");
+	rfc2822_header_field_write(f, "MIME-Version", "1.0");
     
 	if ( !ctx->mime ) {
-		fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n");
-		fprintf(f, "Content-Transfer-Encoding: 8bit\r\n");
+		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");
 	}
 
diff --git a/src/lib-sieve/rfc2822.c b/src/lib-sieve/rfc2822.c
index 329432a6c550687e781b0b13ec8b046197dc53ef..6aed0c81522200703082833962d0b7a3ec5c77cd 100644
--- a/src/lib-sieve/rfc2822.c
+++ b/src/lib-sieve/rfc2822.c
@@ -2,9 +2,13 @@
  */
 
 #include "lib.h"
+#include "str.h"
 
 #include "rfc2822.h"
 
+#include <stdio.h>
+#include <ctype.h>
+
 /* NOTE: much of the functionality implemented here should eventually appear
  * somewhere in Dovecot itself.
  */
@@ -31,3 +35,117 @@ bool rfc2822_header_field_name_verify
 	return TRUE;
 }
 
+/*
+ *
+ */
+
+const char *rfc2822_header_field_name_sanitize(const char *name)
+{
+	char *result = t_strdup_noconst(name);
+	char *p;
+	
+	/* Make the whole name lower case ... */
+	result = str_lcase(result);
+
+	/* ... except for the first letter and those that follow '-' */
+	p = result;
+	*p = i_toupper(*p);
+	while ( *p != '\0' ) {
+		if ( *p == '-' ) {
+			p++;
+			
+			if ( *p != '\0' )
+				*p = i_toupper(*p);
+			
+			continue;
+		}
+		
+		p++;
+	}
+	
+	return result;
+}
+
+/*
+ * Message construction
+ */
+ 
+/* FIXME: This should be collected into a Dovecot API for composing internet
+ * mail messages. These functions now use FILE * output streams, but this should
+ * be changed to proper dovecot streams.
+ */
+
+void rfc2822_header_field_write
+(FILE *f, const char *name, const char *body)
+{
+	static const unsigned int max_line = 80;
+	
+	const char *bp = body;  /* Pointer */ 
+	const char *sp = body;  /* Start pointer */
+	const char *wp = NULL;  /* Whitespace pointer */ 
+	const char *nlp = NULL; /* New-line pointer */
+	unsigned int len = strlen(name);
+	
+	/* Write header field name first */
+	fwrite(name, len, 1, f);
+	fwrite(": ", 2, 1, f);
+		
+	/* Add field body; fold it if necessary and account for existing folding */
+	len +=  2;
+	while ( *bp != '\0' ) {
+		while ( *bp != '\0' && nlp == NULL && (wp == NULL || len < max_line) ) {
+			if ( *bp == ' ' || *bp == '\t' ) {
+			 	wp = bp;
+			} else if ( *bp == '\r' || *bp == '\n' ) {
+				nlp = bp;			
+				break;
+			}
+
+			bp++; len++;
+		}
+		
+		if ( *bp == '\0' ) break;
+		
+		/* Existing newline ? */
+		if ( nlp != NULL ) {
+			/* Replace any sort of newline with proper CRLF */
+			while ( *bp == '\r' || *bp == '\n' )
+				bp++;
+			
+			fwrite(sp, nlp-sp, 1, f);
+			
+			if ( *bp != '\0' && *bp != ' ' && *bp != '\t' )
+				fwrite("\r\n\t", 3, 1, f);
+			else
+				fwrite("\r\n", 2, 1, f);
+				
+			sp = bp;
+		} else {
+			/* Insert newline at last whitespace within the max_line limit */
+			fwrite(sp, wp-sp, 1, f);
+			fwrite("\r\n", 2, 1, f);
+			sp = wp;
+		}
+		
+		len = bp - sp;		
+		wp = NULL;
+		nlp = NULL;
+	}
+	
+	fwrite(sp, bp-sp, 1, f);
+	fwrite("\r\n", 2, 1, f);
+}
+
+void rfc2822_header_field_printf
+(FILE *f, const char *name, const char *body_fmt, ...)
+{
+	string_t *body = t_str_new(256);
+	va_list args;
+
+	va_start(args, body_fmt);
+	str_vprintfa(body, body_fmt, args);
+	va_end(args);
+	
+	rfc2822_header_field_write(f, name, str_c(body));
+}
+
diff --git a/src/lib-sieve/rfc2822.h b/src/lib-sieve/rfc2822.h
index 3748c950f0902ffe70d4974a546c9ccc7df149ae..fc85ac2a67ab7f97a49a63103867fcf21ca7561f 100644
--- a/src/lib-sieve/rfc2822.h
+++ b/src/lib-sieve/rfc2822.h
@@ -6,7 +6,29 @@
 
 #include "lib.h"
 
+#include <stdio.h>
+
+/*
+ * Verification
+ */ 
+ 
 bool rfc2822_header_field_name_verify
 	(const char *field_name, unsigned int len);
 
+/*
+ *
+ */
+
+const char *rfc2822_header_field_name_sanitize(const char *name);
+
+/*
+ * Message composition
+ */
+
+void rfc2822_header_field_write
+	(FILE *f, const char *name, const char *body);
+	
+void rfc2822_header_field_printf
+	(FILE *f, const char *name, const char *body_fmt, ...) ATTR_FORMAT(3, 4);
+
 #endif /* __RFC2822_H */
diff --git a/src/sieve-tools/sieve-exec.c b/src/sieve-tools/sieve-exec.c
index cc66524767ee7816e38ac63acac5abe4b23e9a32..4741016288ca8eddc5adbbdc424a5ffc6944189a 100644
--- a/src/sieve-tools/sieve-exec.c
+++ b/src/sieve-tools/sieve-exec.c
@@ -29,7 +29,7 @@
 static void *sieve_smtp_open(const char *destination,
 	const char *return_path, FILE **file_r)
 {
-	i_info("sending mesage from <%s> to <%s>:",
+	i_info("sending message from <%s> to <%s>:",
 		return_path == NULL || *return_path == '\0' ? "" : return_path, 
 		destination);
 	printf("\nSTART MESSAGE:\n");
diff --git a/tests/extensions/enotify/errors.svtest b/tests/extensions/enotify/errors.svtest
new file mode 100644
index 0000000000000000000000000000000000000000..40fb6d1eb72ca05d256a9f60baa8afeb7b31554b
--- /dev/null
+++ b/tests/extensions/enotify/errors.svtest
@@ -0,0 +1,26 @@
+require "vnd.dovecot.testsuite";
+require "comparator-i;ascii-numeric";
+require "relational";
+
+require "enotify";
+
+
+test "Invalid URL (FIXME: count only)" {
+	if test_compile "errors/url.sieve" {
+		test_fail "compile should have failed";
+	}
+
+	if not test_error :count "eq" :comparator "i;ascii-numeric" "2" {
+		test_fail "wrong number of errors reported";
+	}
+}
+
+test "Invalid mailto URL (FIXME: count only)" {
+	if test_compile "errors/url-mailto.sieve" {
+		test_fail "compile should have failed";
+	}
+
+	if not test_error :count "eq" :comparator "i;ascii-numeric" "2" {
+		test_fail "wrong number of errors reported";
+	}
+}
diff --git a/tests/extensions/enotify/errors/url-mailto.sieve b/tests/extensions/enotify/errors/url-mailto.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..5a4d32f6c40bbf0276fbacd5bfeb0f4cb59f8f5b
--- /dev/null
+++ b/tests/extensions/enotify/errors/url-mailto.sieve
@@ -0,0 +1,5 @@
+require "enotify";
+
+# 1: Invalid header name 
+notify "mailto:stephan@rename-it.nl?header:=frop";
+
diff --git a/tests/extensions/enotify/errors/url.sieve b/tests/extensions/enotify/errors/url.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..06e86d764f6d384ce86443addb3580132d7b6c23
--- /dev/null
+++ b/tests/extensions/enotify/errors/url.sieve
@@ -0,0 +1,5 @@
+require "enotify";
+
+# 1: Invalid url scheme
+notify "snailto:stephan@rename-it.nl";
+
diff --git a/tests/extensions/vacation/references.sieve b/tests/extensions/vacation/references.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..77658f2fa0b0e4f50ffacb6c8ab3b43e2054c701
--- /dev/null
+++ b/tests/extensions/vacation/references.sieve
@@ -0,0 +1,4 @@
+require "vacation";
+
+vacation "I am on vacation.";
+discard;
diff --git a/tests/extensions/vacation/references.svtest b/tests/extensions/vacation/references.svtest
new file mode 100644
index 0000000000000000000000000000000000000000..f67b1e69643c9c86056d6a35897fc9f2cfd30182
--- /dev/null
+++ b/tests/extensions/vacation/references.svtest
@@ -0,0 +1,18 @@
+require "vnd.dovecot.testsuite";
+require "vacation";
+
+test_set "message" text:
+From: stephan@rename-it.nl
+Subject: frop
+References: <1234@local.machine.example> <3456@example.net>
+ <435444@ttms.com> <4223@froop.nl> <m345444444@message-id.exp>
+Message-ID: <432df324@rename-it.nl>
+To: nico@vestingbar.nl
+
+Frop
+.
+;
+
+test "References" {
+	vacation "I am not in today!";
+}