diff --git a/src/lib-sieve/plugins/notify/Makefile.am b/src/lib-sieve/plugins/notify/Makefile.am
index 26e00b129b05a7350ea3cb805a291f6723b910ca..2b4f711bd6583a438fdb8ffff0614b389fe3f3bf 100644
--- a/src/lib-sieve/plugins/notify/Makefile.am
+++ b/src/lib-sieve/plugins/notify/Makefile.am
@@ -14,6 +14,7 @@ commands = \
 
 libsieve_ext_notify_la_SOURCES = \
 	ext-notify.c \
+	ext-notify-common.c \
 	$(commands)
 
 noinst_HEADERS = \
diff --git a/src/lib-sieve/plugins/notify/cmd-denotify.c b/src/lib-sieve/plugins/notify/cmd-denotify.c
index 7f6f33bb5ef31e8a214003ed86b7aeaea507b9e9..dbc72b22e60b9e8f10a74c61c66d244a87fbbb8e 100644
--- a/src/lib-sieve/plugins/notify/cmd-denotify.c
+++ b/src/lib-sieve/plugins/notify/cmd-denotify.c
@@ -6,7 +6,10 @@
 #include "sieve-common.h"
 #include "sieve-code.h"
 #include "sieve-extensions.h"
+#include "sieve-ast.h"
 #include "sieve-commands.h"
+#include "sieve-match-types.h"
+#include "sieve-comparators.h"
 #include "sieve-actions.h"
 #include "sieve-validator.h"
 #include "sieve-generator.h"
@@ -16,46 +19,287 @@
 
 #include "ext-notify-common.h"
  
-/* Denotify command (NOT IMPLEMENTED)
+/* 
+ * Denotify command (NOT IMPLEMENTED)
  *
  * Syntax:
  *   denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">]
  */
 
-static bool cmd_denotify_pre_validate
-	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
+static bool cmd_denotify_registered
+	(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg);
+static bool cmd_denotify_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
 
 const struct sieve_command cmd_denotify = {
 	"denotify",
 	SCT_COMMAND,
 	0, 0, FALSE, FALSE,
+	cmd_denotify_registered,
+	NULL,
+	NULL, 
+	cmd_denotify_generate, 
+	NULL
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool tag_match_type_is_instance_of
+	(struct sieve_validator *validator, struct sieve_command_context *cmd,
+		struct sieve_ast_argument *arg);
+static bool tag_match_type_validate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg,
+		struct sieve_command_context *cmd);
+
+/* Argument object */
+
+const struct sieve_argument denotify_match_tag = {
+	"MATCH-TYPE-STRING",
+	tag_match_type_is_instance_of,
 	NULL,
-	cmd_denotify_pre_validate,
-	NULL, NULL, NULL
+	tag_match_type_validate,
+	NULL, NULL
+};
+
+/* Codes for optional operands */
+
+enum cmd_denotify_optional {
+  OPT_END,
+  OPT_IMPORTANCE,
+  OPT_MATCH_TYPE,
+	OPT_MATCH_KEY
 };
 
 /* 
  * Denotify operation 
  */
 
+static bool cmd_denotify_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address);
+
 const struct sieve_operation denotify_operation = { 
 	"DENOTIFY",
 	&notify_extension,
 	EXT_NOTIFY_OPERATION_DENOTIFY,
-	NULL, NULL
+	cmd_denotify_operation_dump,
+	NULL
 };
 
 /*
- * Command validation
+ * Tag validation
  */
 
-static bool cmd_denotify_pre_validate
-(struct sieve_validator *valdtr, struct sieve_command_context *cmd)
+static bool tag_match_type_is_instance_of
+(struct sieve_validator *valdtr, struct sieve_command_context *cmd,
+	struct sieve_ast_argument *arg)
 {
-	sieve_command_validate_error(valdtr, cmd,
-		"the denotify command cannot be used "
-		"(the deprecated notify extension is not fully implemented)");
-	return FALSE;
+	return match_type_tag.is_instance_of(valdtr, cmd, arg);
 }
 
+static bool tag_match_type_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+	struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+
+	if ( !match_type_tag.validate(valdtr, arg, cmd) )
+		return FALSE;
+
+	if ( *arg == NULL ) {
+		sieve_argument_validate_error(valdtr, tag, 
+			"the MATCH-TYPE argument (:%s) for the denotify command requires "
+			"an additional key-string paramterer, but no more arguments were found", 
+			sieve_ast_argument_tag(tag));
+		return FALSE;	
+	}
+	
+	if ( sieve_ast_argument_type(*arg) != SAAT_STRING ) 
+	{
+		sieve_argument_validate_error(valdtr, *arg, 
+			"the MATCH-TYPE argument (:%s) for the denotify command requires "
+			"an additional key-string parameter, but %s was found", 
+			sieve_ast_argument_tag(tag), sieve_ast_argument_name(*arg));
+		return FALSE;
+	}
+
+	if ( !sieve_validator_argument_activate(valdtr, cmd, *arg, FALSE) ) 
+		return FALSE;
+
+	if ( !sieve_match_type_validate
+		(valdtr, cmd, *arg, &is_match_type, &i_octet_comparator) )
+		return FALSE;
+
+	tag->argument = &match_type_tag;
+
+	(*arg)->arg_id_code = OPT_MATCH_KEY;
+
+	*arg = sieve_ast_argument_next(*arg);
+
+	return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool cmd_denotify_registered
+(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg)
+{
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &denotify_match_tag, OPT_MATCH_TYPE);
+
+	ext_notify_register_importance_tags(valdtr, cmd_reg, OPT_IMPORTANCE);
+
+	return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_denotify_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx)
+{
+	sieve_operation_emit_code(cgenv->sbin, &denotify_operation);
+
+	/* Emit source line */
+	sieve_code_source_line_emit(cgenv->sbin, sieve_command_source_line(ctx));
+
+	/* Generate arguments */
+	return sieve_generate_arguments(cgenv, ctx, NULL);
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool cmd_denotify_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{	
+	int opt_code = 1;
+	
+	sieve_code_dumpf(denv, "%s", op->mnemonic);
+	sieve_code_descend(denv);	
+
+	/* Source line */
+	if ( !sieve_code_source_line_dump(denv, address) )
+		return FALSE;
+
+	/* Dump optional operands */
+	if ( sieve_operand_optional_present(denv->sbin, address) ) {
+		while ( opt_code != 0 ) {
+			sieve_code_mark(denv);
+			
+			if ( !sieve_operand_optional_read(denv->sbin, address, &opt_code) ) 
+				return FALSE;
+
+			switch ( opt_code ) {
+			case 0:
+				break;
+			case OPT_MATCH_KEY:
+				if ( !sieve_opr_string_dump(denv, address, "key-string") )
+					return FALSE;
+				break;
+			case OPT_MATCH_TYPE:
+				if ( !sieve_opr_match_type_dump(denv, address) )
+					return FALSE;
+				break;
+			case OPT_IMPORTANCE:
+				if ( !sieve_opr_number_dump(denv, address, "importance") )
+					return FALSE;
+				break;
+			default:
+				return FALSE;
+			}
+		}
+	}
+	
+	return TRUE;
+}
+
+/* 
+ * Code execution
+ */
+
+static int cmd_notify_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{	
+	struct ext_notify_action *act;
+	pool_t pool;
+	int opt_code = 1;
+	sieve_number_t importance = 1;
+	const struct sieve_match_type *match_type = NULL;
+	string_t *match_key = NULL; 
+	unsigned int source_line;
+
+	/*
+	 * Read operands
+	 */
+		
+	/* 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;
+	}
+	
+	/* Optional operands */	
+	if ( sieve_operand_optional_present(renv->sbin, address) ) {
+		while ( opt_code != 0 ) {
+			if ( !sieve_operand_optional_read(renv->sbin, address, &opt_code) ) {
+				sieve_runtime_trace_error(renv, "invalid optional operand");
+				return SIEVE_EXEC_BIN_CORRUPT;
+			}
+
+			switch ( opt_code ) {
+			case 0:
+				break;
+			case OPT_MATCH_TYPE:
+				if ( (match_type = sieve_opr_match_type_read(renv, address)) == NULL ) {
+					sieve_runtime_trace_error(renv, "invalid match type operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				}
+				break;
+			case OPT_MATCH_KEY:
+				if ( !sieve_opr_string_read(renv, address, &match_key) ) {
+					sieve_runtime_trace_error(renv, "invalid from operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				}
+				break;
+			case OPT_IMPORTANCE:
+				if ( !sieve_opr_number_read(renv, address, &importance) ) {
+					sieve_runtime_trace_error(renv, "invalid importance operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				}
+	
+				/* Enforce 0 < importance < 4 (just to be sure) */
+				if ( importance < 1 ) 
+					importance = 1;
+				else if ( importance > 3 )
+					importance = 3;
+				break;
+			default:
+				sieve_runtime_trace_error(renv, "unknown optional operand: %d", 
+					opt_code);
+				return SIEVE_EXEC_BIN_CORRUPT;
+			}
+		}
+	}
+		
+	/*
+	 * Perform operation
+	 */
+
+	sieve_runtime_trace(renv, "DENOTIFY action");	
+
+	return SIEVE_EXEC_OK;
+}
+
+
 
diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
index 39f55e57f851ec13df3d8ea4a541e79ba2ce37fe..39b7afa5fd6de23c032b5d9fc47b4252752d234f 100644
--- a/src/lib-sieve/plugins/notify/cmd-notify.c
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -72,9 +72,6 @@ static bool cmd_notify_validate_string_tag
 static bool cmd_notify_validate_stringlist_tag
 	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
 		struct sieve_command_context *cmd);
-static bool cmd_notify_validate_importance_tag
-	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
-		struct sieve_command_context *cmd);
 
 /* Argument objects */
 
@@ -106,27 +103,6 @@ static const struct sieve_argument notify_message_tag = {
 	NULL, NULL
 };
 
-static const struct sieve_argument notify_low_tag = {
-	"low",
-	NULL, NULL,
-	cmd_notify_validate_importance_tag,
-	NULL, NULL
-};
-
-static const struct sieve_argument notify_normal_tag = {
-	"normal",
-	NULL, NULL,
-	cmd_notify_validate_importance_tag,
-	NULL, NULL
-};
-
-static const struct sieve_argument notify_high_tag = {
-	"high",
-	NULL, NULL,
-	cmd_notify_validate_importance_tag,
-	NULL, NULL
-};
-
 /* 
  * Notify operation 
  */
@@ -213,11 +189,11 @@ static bool cmd_notify_validate_string_tag
 	/* Detach the tag itself */
 	*arg = sieve_ast_arguments_detach(*arg, 1);
 
-    /* Check syntax:
-     *   :id <string>
-     *   :method <string>
-     *   :message <string>
-     */
+	/* Check syntax:
+	 *   :id <string>
+	 *   :method <string>
+	 *   :message <string>
+	 */
 	if ( !sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, SAAT_STRING) )
 		return FALSE;
 
@@ -269,27 +245,6 @@ static bool cmd_notify_validate_stringlist_tag
 	return TRUE;
 }
 
-static bool cmd_notify_validate_importance_tag
-(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
-	struct sieve_command_context *cmd ATTR_UNUSED)
-{
-	struct sieve_ast_argument *tag = *arg;
-
-	if ( tag->argument == &notify_low_tag )
-		sieve_ast_argument_number_substitute(tag, 3);
-	else if ( tag->argument == &notify_normal_tag )
-		sieve_ast_argument_number_substitute(tag, 2);
-	else
-		sieve_ast_argument_number_substitute(tag, 1);
-
-	tag->argument = &number_argument;
-
-	/* Skip parameter */
-	*arg = sieve_ast_argument_next(*arg);
-
-	return TRUE;
-}
-
 /*
  * Command registration
  */
@@ -306,12 +261,7 @@ static bool cmd_notify_registered
 	sieve_validator_register_tag
 		(valdtr, cmd_reg, &notify_options_tag, OPT_OPTIONS);
 
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_low_tag, OPT_IMPORTANCE);
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_normal_tag, OPT_IMPORTANCE);
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_high_tag, OPT_IMPORTANCE);
+	ext_notify_register_importance_tags(valdtr, cmd_reg, OPT_IMPORTANCE);
 
 	return TRUE;
 }
@@ -473,14 +423,14 @@ static void cmd_notify_construct_message
 	string_t *out_msg)
 {
 	const struct sieve_message_data *msgdata = renv->msgdata;
-	const char *p;
+  const char *p;
 
-	if ( msg_format == NULL )
+  if ( msg_format == NULL )
 		msg_format = "$from$: $subject$";
  
  	/* Scan message for substitutions */
 	p = msg_format;
-	while ( *p != '\0' ) {
+  while ( *p != '\0' ) {
 		const char *const *header;
 
 		if ( strncasecmp(p, "$from$", 6) == 0 ) {
@@ -491,11 +441,10 @@ static void cmd_notify_construct_message
 				 str_append(out_msg, header[0]); 
 
 		} else if ( strncasecmp(p, "$env-from$", 10) == 0 ) {
-			const char *from = sieve_message_get_sender(renv->msgctx);
 			p += 10;
 
-			if ( from != NULL ) 
-				str_append(out_msg, from);
+			if ( msgdata->return_path != NULL ) 
+				str_append(out_msg, msgdata->return_path);
 
 		} else if ( strncasecmp(p, "$subject$", 9) == 0 ) {	
 			p += 9;
@@ -510,7 +459,7 @@ static void cmd_notify_construct_message
 			const char *begin = p;
 			bool valid = TRUE;
 
-			p += 5;
+    	p += 5;
 			if ( *p == '[' ) {
 				p += 1;
 
@@ -835,8 +784,10 @@ static bool contains_8bit(const char *msg)
 }
 
 static bool act_notify_send
-(const struct sieve_action_exec_env *aenv, const struct ext_notify_action *act)
+(const struct sieve_action_exec_env *aenv, 
+	const struct ext_notify_action *act)
 { 
+	const struct sieve_message_data *msgdata = aenv->msgdata;
 	const struct sieve_script_env *senv = aenv->scriptenv;
 	const struct ext_notify_recipient *recipients;
 	void *smtp_handle;
@@ -948,7 +899,7 @@ static bool act_notify_commit
 			if ( strcasecmp(*hdsp, "no") != 0 ) {
 				sieve_result_log(aenv, 
 					"not sending notification for auto-submitted message from <%s>", 
-					str_sanitize(sieve_message_get_sender(aenv->msgctx), 128));	
+					str_sanitize(msgdata->return_path, 128));	
 					return TRUE;				 
 			}
 			hdsp++;
diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c
new file mode 100644
index 0000000000000000000000000000000000000000..1c4f8de8348a9b13bb5b1649d0bea9a9c1665785
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/ext-notify-common.c
@@ -0,0 +1,77 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+
+#include "ext-notify-common.h"
+
+/*
+ * Importance argument
+ */
+
+static bool tag_importance_validate
+	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+		struct sieve_command_context *cmd);
+
+static const struct sieve_argument importance_low_tag = {
+	"low",
+	NULL, NULL,
+	tag_importance_validate,
+	NULL, NULL
+};
+
+static const struct sieve_argument importance_normal_tag = {
+	"normal",
+	NULL, NULL,
+	tag_importance_validate,
+	NULL, NULL
+};
+
+static const struct sieve_argument importance_high_tag = {
+	"high",
+	NULL, NULL,
+	tag_importance_validate,
+	NULL, NULL
+};
+
+static bool tag_importance_validate
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
+	struct sieve_command_context *cmd ATTR_UNUSED)
+{
+	struct sieve_ast_argument *tag = *arg;
+
+	if ( tag->argument == &importance_low_tag )
+		sieve_ast_argument_number_substitute(tag, 3);
+	else if ( tag->argument == &importance_normal_tag )
+		sieve_ast_argument_number_substitute(tag, 2);
+	else
+		sieve_ast_argument_number_substitute(tag, 1);
+
+	tag->argument = &number_argument;
+
+	/* Skip parameter */
+	*arg = sieve_ast_argument_next(*arg);
+
+	return TRUE;
+}
+
+void ext_notify_register_importance_tags
+(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg,
+	unsigned int id_code)
+{
+	sieve_validator_register_tag(valdtr, cmd_reg, &importance_low_tag, id_code);
+	sieve_validator_register_tag(valdtr, cmd_reg, &importance_normal_tag, id_code);
+	sieve_validator_register_tag(valdtr, cmd_reg, &importance_high_tag, id_code);
+}
+
diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.h b/src/lib-sieve/plugins/notify/ext-notify-common.h
index 8f07f5397c941acda768ecb7b5dadb8997400c28..de00e3442a8d30f3085fb2e58c657c8937bdd3a2 100644
--- a/src/lib-sieve/plugins/notify/ext-notify-common.h
+++ b/src/lib-sieve/plugins/notify/ext-notify-common.h
@@ -17,6 +17,14 @@ extern const struct sieve_extension notify_extension;
 extern const struct sieve_command cmd_notify_old;
 extern const struct sieve_command cmd_denotify;
 
+/*
+ * Arguments
+ */
+
+void ext_notify_register_importance_tags
+	(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg,
+		unsigned int id_code);
+
 /*
  * Operations
  */
diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c
index 95c3bbabd1c0ed337dcff91dcffb1a12e9b15f9a..749062a3fad6a6f0278450a7a647f3993e5838b3 100644
--- a/src/lib-sieve/plugins/notify/ext-notify.c
+++ b/src/lib-sieve/plugins/notify/ext-notify.c
@@ -7,15 +7,13 @@
  * Authors: Stephan Bosch
  * Specification: draft-ietf-sieve-notify-00.txt
  * Implementation: deprecated; provided for backwards compatibility
- *                 denotify command is explicitly not supported.
  * Status: deprecated
  * 
  */
 
 /* FIXME: Currently the following CMUSieve features are not supported:
  * 
- * 1) The denotify command is not supported and causes a compile error when used.
- * 2) The $text$ substitution is not available for the :message argument.
+ * (*) The $text$ substitution is not available for the :message argument.
  */
 	
 #include <stdio.h>