From f5268a9c37a811fcf51855089f3a76fa095cf1f3 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 17 Jul 2009 03:28:04 +0200
Subject: [PATCH] Made deprecated notify extension implementation compatible
 with cmusieve, except for the denotify command.

---
 configure.in                                  |   1 +
 src/lib-sieve/Makefile.am                     |   1 +
 src/lib-sieve/plugins/Makefile.am             |   1 +
 src/lib-sieve/plugins/enotify/Makefile.am     |   1 -
 src/lib-sieve/plugins/enotify/ext-notify.c    | 374 --------
 src/lib-sieve/plugins/notify/Makefile.am      |  22 +
 src/lib-sieve/plugins/notify/cmd-denotify.c   |  61 ++
 src/lib-sieve/plugins/notify/cmd-notify.c     | 849 ++++++++++++++++++
 src/lib-sieve/plugins/notify/ext-enotify.c    |  88 ++
 .../plugins/notify/ext-notify-common.h        |  49 +
 .../plugins/notify/ext-notify-limits.h        |  10 +
 src/lib-sieve/plugins/notify/ext-notify.c     | 105 +++
 12 files changed, 1187 insertions(+), 375 deletions(-)
 delete mode 100644 src/lib-sieve/plugins/enotify/ext-notify.c
 create mode 100644 src/lib-sieve/plugins/notify/Makefile.am
 create mode 100644 src/lib-sieve/plugins/notify/cmd-denotify.c
 create mode 100644 src/lib-sieve/plugins/notify/cmd-notify.c
 create mode 100644 src/lib-sieve/plugins/notify/ext-enotify.c
 create mode 100644 src/lib-sieve/plugins/notify/ext-notify-common.h
 create mode 100644 src/lib-sieve/plugins/notify/ext-notify-limits.h
 create mode 100644 src/lib-sieve/plugins/notify/ext-notify.c

diff --git a/configure.in b/configure.in
index 02171ecc2..f323269e6 100644
--- a/configure.in
+++ b/configure.in
@@ -113,6 +113,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/notify/Makefile
 src/lib-sieve/plugins/environment/Makefile
 src/lib-sieve-tool/Makefile
 src/plugins/Makefile
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 366cf2910..ce9d26726 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -55,6 +55,7 @@ plugins = \
 	./plugins/body/libsieve_ext_body.la \
 	./plugins/variables/libsieve_ext_variables.la \
 	./plugins/enotify/libsieve_ext_enotify.la \
+	./plugins/notify/libsieve_ext_notify.la \
 	./plugins/environment/libsieve_ext_environment.la
 
 libsieve_la_DEPENDENCIES = $(plugins)
diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am
index c75e88566..92389755b 100644
--- a/src/lib-sieve/plugins/Makefile.am
+++ b/src/lib-sieve/plugins/Makefile.am
@@ -10,5 +10,6 @@ SUBDIRS = \
 	body \
 	variables \
 	enotify \
+	notify \
 	environment
 
diff --git a/src/lib-sieve/plugins/enotify/Makefile.am b/src/lib-sieve/plugins/enotify/Makefile.am
index ad0818c46..4d91fd8c4 100644
--- a/src/lib-sieve/plugins/enotify/Makefile.am
+++ b/src/lib-sieve/plugins/enotify/Makefile.am
@@ -23,7 +23,6 @@ notify_methods = \
 
 libsieve_ext_enotify_la_SOURCES = \
 	ext-enotify.c \
-	ext-notify.c \
 	ext-enotify-common.c \
 	$(commands) \
 	$(tests) \
diff --git a/src/lib-sieve/plugins/enotify/ext-notify.c b/src/lib-sieve/plugins/enotify/ext-notify.c
deleted file mode 100644
index 776fae4dc..000000000
--- a/src/lib-sieve/plugins/enotify/ext-notify.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
- */
-
-/* Extension notify
- * ----------------
- *
- * Authors: Stephan Bosch
- * Specification: draft-ietf-sieve-notify-00.txt
- * Implementation: deprecated; provided for backwards compatibility
- *                 denotify command is explicitly not supported.
- * Status: deprecated
- * 
- */
-	
-#include <stdio.h>
-
-#include "sieve-common.h"
-
-#include "sieve-code.h"
-#include "sieve-extensions.h"
-#include "sieve-actions.h"
-#include "sieve-commands.h"
-#include "sieve-validator.h"
-#include "sieve-generator.h"
-#include "sieve-interpreter.h"
-#include "sieve-result.h"
-
-#include "ext-enotify-common.h"
-
-/*
- * Commands
- */
-
-static bool cmd_notify_registered
-	(struct sieve_validator *valdtr,
-		struct sieve_command_registration *cmd_reg);
-static bool cmd_notify_pre_validate
-	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
-static bool cmd_notify_validate
-	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
-static bool cmd_notify_generate
-	(const struct sieve_codegen_env *cgenv, 
-		struct sieve_command_context *ctx);
-
-static bool cmd_denotify_pre_validate
-	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
-
-/* Notify command
- *
- * Syntax:
- *   notify [":method" string] [":id" string] 
- *          [<":low" / ":normal" / ":high">] [":message" string]
- */
-
-static const struct sieve_command cmd_notify = {
-	"notify",
-	SCT_COMMAND,
-	0, 0, FALSE, FALSE,
-	cmd_notify_registered,
-	cmd_notify_pre_validate,
-	cmd_notify_validate,
-	cmd_notify_generate, 
-	NULL,
-};
-
-/* Denotify command (NOT IMPLEMENTED)
- *
- * Syntax:
- *   denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">]
- */
-static const struct sieve_command cmd_denotify = {
-	"denotify",
-	SCT_COMMAND,
-	0, 0, FALSE, FALSE,
-	NULL,
-	cmd_denotify_pre_validate,
-	NULL, NULL, NULL
-};
-
-/* 
- * Extension
- */
-
-static bool ext_notify_load(void);
-static bool ext_notify_validator_load(struct sieve_validator *valdtr);
-
-static int ext_notify_my_id = -1;
-
-const struct sieve_extension notify_extension = { 
-	"notify", 
-	&ext_notify_my_id,
-	ext_notify_load,
-	NULL,
-	ext_notify_validator_load, 
-	NULL, NULL, NULL, NULL, NULL,
-	SIEVE_EXT_DEFINE_NO_OPERATIONS,
-	SIEVE_EXT_DEFINE_NO_OPERANDS,
-};
-
-static bool ext_notify_load(void)
-{
-	/* Make sure real extension is registered, it is needed by the binary */
-    (void)sieve_extension_require(&enotify_extension);
-
-	return TRUE;
-}
-
-/*
- * Extension validation
- */
-
-static bool ext_notify_validator_extension_validate
-	(struct sieve_validator *valdtr, void *context, struct sieve_ast_argument *require_arg);
-
-const struct sieve_validator_extension notify_validator_extension = {
-	&notify_extension,
-	ext_notify_validator_extension_validate,
-	NULL
-};
-
-static bool ext_notify_validator_load(struct sieve_validator *valdtr)
-{
-	/* Register validator extension to check for conflict with enotify */
-	sieve_validator_extension_register
-		(valdtr, &notify_validator_extension, NULL);
-
-	/* Register new commands */
-	sieve_validator_register_command(valdtr, &cmd_notify);
-	sieve_validator_register_command(valdtr, &cmd_denotify);
-	
-	return TRUE;
-}
-
-static bool ext_notify_validator_extension_validate
-(struct sieve_validator *valdtr, void *context ATTR_UNUSED,
-    struct sieve_ast_argument *require_arg)
-{
-	/* Check for conflict with enotify */
-	if ( sieve_validator_extension_loaded(valdtr, &enotify_extension) ) {
-		sieve_argument_validate_error(valdtr, require_arg,
-			"the (deprecated) notify extension cannot be used "
-			"together with the enotify extension");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-/*
- * Denotify/Notify command tags
- */
-
-/* Forward declarations */
-
-static bool cmd_notify_validate_string_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 */
-
-static const struct sieve_argument notify_method_tag = {
-	"method",
-	NULL, NULL,
-	cmd_notify_validate_string_tag,
-	NULL, NULL
-};
-
-static const struct sieve_argument notify_id_tag = {
-	"id",
-	NULL, NULL,
-	cmd_notify_validate_string_tag,
-	NULL, NULL
-};
-
-static const struct sieve_argument notify_message_tag = {
-	"message",
-	NULL, NULL,
-	cmd_notify_validate_string_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
-};
-
-/*
- * Command validation context
- */
-
-struct cmd_notify_context_data {
-	struct sieve_ast_argument *method;
-	struct sieve_ast_argument *message;
-};
-
-/*
- * Tag validation
- */
-
-static bool cmd_notify_validate_string_tag
-(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
-    struct sieve_command_context *cmd)
-{
-	struct sieve_ast_argument *tag = *arg;
-	struct cmd_notify_context_data *ctx_data =
-		(struct cmd_notify_context_data *) cmd->data;
-
-	/* Detach the tag itself */
-	*arg = sieve_ast_arguments_detach(*arg, 1);
-
-    /* Check syntax:
-     *   :id <string>
-     *   :method <string>
-     *   :message <string>
-     */
-	if ( !sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, SAAT_STRING) )
-		return FALSE;
-
-	if ( tag->argument == &notify_method_tag ) {
-		ctx_data->method = *arg;
-	
-		/* Removed */
-		*arg = sieve_ast_arguments_detach(*arg, 1);
-
-	} else if ( tag->argument == &notify_id_tag ) {
-
-		/* Ignored */
-		*arg = sieve_ast_arguments_detach(*arg, 1);
-
-	} else if ( tag->argument == &notify_message_tag ) {
-		ctx_data->message = *arg;
-
-		/* Skip parameter */
-		*arg = sieve_ast_argument_next(*arg);
-	}
-
-	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, 1);
-	else if ( tag->argument == &notify_normal_tag )
-		sieve_ast_argument_number_substitute(tag, 2);
-	else
-		sieve_ast_argument_number_substitute(tag, 3);
-
-	tag->argument = &number_argument;
-
-	/* Skip parameter */
-	*arg = sieve_ast_argument_next(*arg);
-
-	return TRUE;
-}
-
-/*
- * Command registration
- */
-
-static bool cmd_notify_registered
-(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg)
-{
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_method_tag, 0);
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_id_tag, 0);
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_message_tag, CMD_NOTIFY_OPT_MESSAGE);
-
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_low_tag, CMD_NOTIFY_OPT_IMPORTANCE);
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_normal_tag, CMD_NOTIFY_OPT_IMPORTANCE);
-	sieve_validator_register_tag
-		(valdtr, cmd_reg, &notify_high_tag, CMD_NOTIFY_OPT_IMPORTANCE);
-
-	return TRUE;
-}
-
-/*
- * Command validation
- */
-
-/* Notify */
-
-static bool cmd_notify_pre_validate
-(struct sieve_validator *valdtr ATTR_UNUSED,
-	struct sieve_command_context *cmd)
-{
-	struct cmd_notify_context_data *ctx_data;
-	
-	/* Assign context */
-	ctx_data = p_new(sieve_command_pool(cmd),	
-		struct cmd_notify_context_data, 1);
-	cmd->data = ctx_data;
-
-	return TRUE;
-}
-
-static bool cmd_notify_validate
-(struct sieve_validator *valdtr, struct sieve_command_context *cmd)
-{
-	struct cmd_notify_context_data *ctx_data =
-		(struct cmd_notify_context_data *) cmd->data;
-
-	if ( ctx_data->method == NULL )	{
-		sieve_command_validate_error(valdtr, cmd,
-            "the notify command must have a ':method' argument "
-			"(the deprecated notify extension is not fully implemented)");
-        return FALSE;
-	}
-
-	if ( !sieve_ast_argument_attach(cmd->ast_node, ctx_data->method) ) {
-		/* Very unlikely */
-		sieve_command_validate_error(valdtr, cmd,
-			"generate notify command; script is too complex");
-		return FALSE;
-	}
-
-	return ext_enotify_compile_check_arguments
-		(valdtr, ctx_data->method, ctx_data->message, NULL, NULL);
-}
-
-/* Denotify */
-
-static bool cmd_denotify_pre_validate
-(struct sieve_validator *valdtr, struct sieve_command_context *cmd)
-{
-	sieve_command_validate_error(valdtr, cmd,
-		"the denotify command cannot be used "
-		"(the deprecated notify extension is not fully implemented)");
-	return FALSE;
-}
-
-/*
- * Code generation
- */
-
-static bool cmd_notify_generate
-(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx)
-{
-    sieve_operation_emit_code(cgenv->sbin, &notify_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);
-}
-
diff --git a/src/lib-sieve/plugins/notify/Makefile.am b/src/lib-sieve/plugins/notify/Makefile.am
new file mode 100644
index 000000000..26e00b129
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/Makefile.am
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libsieve_ext_notify.la
+
+AM_CPPFLAGS = \
+	-I../../ \
+	-I../variables \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(dovecot_incdir)/src/lib-mail \
+	-I$(dovecot_incdir)/src/lib-storage 
+
+commands = \
+	cmd-notify.c \
+	cmd-denotify.c
+
+libsieve_ext_notify_la_SOURCES = \
+	ext-notify.c \
+	$(commands)
+
+noinst_HEADERS = \
+	ext-notify-common.h \
+	ext-notify-limits.h
+
diff --git a/src/lib-sieve/plugins/notify/cmd-denotify.c b/src/lib-sieve/plugins/notify/cmd-denotify.c
new file mode 100644
index 000000000..7f6f33bb5
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/cmd-denotify.c
@@ -0,0 +1,61 @@
+/* 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"
+ 
+/* 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);
+
+const struct sieve_command cmd_denotify = {
+	"denotify",
+	SCT_COMMAND,
+	0, 0, FALSE, FALSE,
+	NULL,
+	cmd_denotify_pre_validate,
+	NULL, NULL, NULL
+};
+
+/* 
+ * Denotify operation 
+ */
+
+const struct sieve_operation denotify_operation = { 
+	"DENOTIFY",
+	&notify_extension,
+	EXT_NOTIFY_OPERATION_DENOTIFY,
+	NULL, NULL
+};
+
+/*
+ * Command validation
+ */
+
+static bool cmd_denotify_pre_validate
+(struct sieve_validator *valdtr, struct sieve_command_context *cmd)
+{
+	sieve_command_validate_error(valdtr, cmd,
+		"the denotify command cannot be used "
+		"(the deprecated notify extension is not fully implemented)");
+	return FALSE;
+}
+
+
diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
new file mode 100644
index 000000000..41fa21ff0
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -0,0 +1,849 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#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 "rfc2822.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 "sieve-address.h"
+#include "sieve-message.h"
+
+#include "ext-notify-common.h"
+#include "ext-notify-limits.h"
+
+/* Notify command
+ *
+ * Syntax:
+ *   notify [":method" string] [":id" string] [":options" string-list]
+ *          [<":low" / ":normal" / ":high">] ["message:" string]
+ *
+ */
+
+static bool cmd_notify_registered
+	(struct sieve_validator *valdtr,
+		struct sieve_command_registration *cmd_reg);
+static bool cmd_notify_pre_validate
+	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
+static bool cmd_notify_validate
+	(struct sieve_validator *valdtr, struct sieve_command_context *cmd);
+static bool cmd_notify_generate
+	(const struct sieve_codegen_env *cgenv, 
+		struct sieve_command_context *ctx);
+
+const struct sieve_command cmd_notify_old = {
+	"notify",
+	SCT_COMMAND,
+	0, 0, FALSE, FALSE,
+	cmd_notify_registered,
+	cmd_notify_pre_validate,
+	cmd_notify_validate,
+	cmd_notify_generate, 
+	NULL,
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool cmd_notify_validate_string_tag
+	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+		struct sieve_command_context *cmd);
+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 */
+
+static const struct sieve_argument notify_method_tag = {
+	"method",
+	NULL, NULL,
+	cmd_notify_validate_string_tag,
+	NULL, NULL
+};
+
+static const struct sieve_argument notify_options_tag = { 
+	"options", 
+	NULL, NULL,
+	cmd_notify_validate_stringlist_tag, 
+	NULL, NULL 
+};
+
+static const struct sieve_argument notify_id_tag = {
+	"id",
+	NULL, NULL,
+	cmd_notify_validate_string_tag,
+	NULL, NULL
+};
+
+static const struct sieve_argument notify_message_tag = {
+	"message",
+	NULL, NULL,
+	cmd_notify_validate_string_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 
+ */
+
+static bool cmd_notify_operation_dump
+	(const struct sieve_operation *op,	
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_notify_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation notify_old_operation = { 
+	"NOTIFY",
+	&notify_extension,
+	EXT_NOTIFY_OPERATION_NOTIFY,
+	cmd_notify_operation_dump, 
+	cmd_notify_operation_execute
+};
+
+/* Codes for optional operands */
+
+enum cmd_notify_optional {
+  OPT_END,
+  OPT_MESSAGE,
+  OPT_IMPORTANCE,
+  OPT_OPTIONS,
+  OPT_ID
+};
+
+/* 
+ * Notify action 
+ */
+
+/* Forward declarations */
+
+static int act_notify_check_duplicate
+	(const struct sieve_runtime_env *renv, 
+		const struct sieve_action_data *act,
+		const struct sieve_action_data *act_other);
+static void act_notify_print
+	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
+		void *context, bool *keep);	
+static bool act_notify_commit
+	(const struct sieve_action *action,	const struct sieve_action_exec_env *aenv, 
+		void *tr_context, bool *keep);
+
+/* Action object */
+
+const struct sieve_action act_notify_old = {
+	"notify",
+	0,
+	NULL,
+	act_notify_check_duplicate, 
+	NULL,
+	act_notify_print,
+	NULL, NULL,
+	act_notify_commit,
+	NULL
+};
+
+/*
+ * Command validation context
+ */
+
+struct cmd_notify_context_data {
+	struct sieve_ast_argument *id;
+	struct sieve_ast_argument *method;
+	struct sieve_ast_argument *options;
+	struct sieve_ast_argument *message;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool cmd_notify_validate_string_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+    struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+	struct cmd_notify_context_data *ctx_data =
+		(struct cmd_notify_context_data *) cmd->data;
+
+	/* Detach the tag itself */
+	*arg = sieve_ast_arguments_detach(*arg, 1);
+
+    /* Check syntax:
+     *   :id <string>
+     *   :method <string>
+     *   :message <string>
+     */
+	if ( !sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, SAAT_STRING) )
+		return FALSE;
+
+	if ( tag->argument == &notify_method_tag ) {
+		ctx_data->method = *arg;
+	
+		/* Removed */
+		*arg = sieve_ast_arguments_detach(*arg, 1);
+
+	} else if ( tag->argument == &notify_id_tag ) {
+		ctx_data->id = *arg;
+
+		/* Skip parameter */
+		*arg = sieve_ast_argument_next(*arg);
+
+	} else if ( tag->argument == &notify_message_tag ) {
+		ctx_data->message = *arg;
+
+		/* Skip parameter */
+		*arg = sieve_ast_argument_next(*arg);
+	}
+
+	return TRUE;
+}
+
+static bool cmd_notify_validate_stringlist_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+	struct cmd_notify_context_data *ctx_data = 
+		(struct cmd_notify_context_data *) cmd->data; 
+
+	/* Detach the tag itself */
+	*arg = sieve_ast_arguments_detach(*arg,1);
+	
+	/* Check syntax:
+	 *   :options string-list
+	 */
+	if ( !sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, SAAT_STRING_LIST) ) 
+		return FALSE;
+		
+	/* Assign context */
+	ctx_data->options = *arg;	
+	
+	/* Skip parameter */
+	*arg = sieve_ast_argument_next(*arg);
+
+	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
+ */
+
+static bool cmd_notify_registered
+(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg)
+{
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &notify_method_tag, 0);
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &notify_id_tag, OPT_ID);
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &notify_message_tag, OPT_MESSAGE);
+	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);
+
+	return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_notify_pre_validate
+(struct sieve_validator *valdtr ATTR_UNUSED,
+	struct sieve_command_context *cmd)
+{
+	struct cmd_notify_context_data *ctx_data;
+	
+	/* Create context */
+	ctx_data = p_new(sieve_command_pool(cmd),	struct cmd_notify_context_data, 1);
+	cmd->data = ctx_data;
+
+	return TRUE;
+}
+
+static int cmd_notify_address_validate
+(void *context, struct sieve_ast_argument *arg)
+{
+	struct sieve_validator *valdtr = (struct sieve_validator *) context;
+
+	if ( sieve_argument_is_string_literal(arg) ) {
+		string_t *address = sieve_ast_argument_str(arg);
+		const char *error;
+		bool result = FALSE;
+
+		T_BEGIN {
+			result = sieve_address_validate(address, &error);
+
+			if ( !result ) {
+				sieve_argument_validate_error(valdtr, arg,
+					"specified :options address '%s' is invalid for "
+					"the mailto notify method: %s",
+					str_sanitize(str_c(address), 128), error);
+			}
+		} T_END;
+
+		return result;
+	}
+
+	return TRUE;
+}
+
+static bool cmd_notify_validate
+(struct sieve_validator *valdtr, struct sieve_command_context *cmd)
+{
+	struct cmd_notify_context_data *ctx_data =
+		(struct cmd_notify_context_data *) cmd->data;
+
+	/* Check :method argument */
+	if ( ctx_data->method != NULL )	{
+		const char *method = sieve_ast_argument_strc(ctx_data->method);
+		
+		if ( strcasecmp(method, "mailto") != 0 ) {
+			sieve_command_validate_error(valdtr, cmd,
+				"the notify command of the deprecated notify extension "
+				"only supports the 'mailto' notification method");
+			return FALSE;
+		}
+	}
+
+	/* Check :options argument */
+	if ( ctx_data->options != NULL ) {
+		struct sieve_ast_argument *option = ctx_data->options;
+		
+		/* Parse and check options */
+		if ( sieve_ast_stringlist_map
+			(&option, (void *) &valdtr, cmd_notify_address_validate) <= 0 ) {
+			return FALSE;
+		}
+	} else {
+		sieve_command_validate_warning(valdtr, cmd,
+			"no :options (and hence recipients) specified for the notify command");
+	}
+
+	return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_notify_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx)
+{
+	sieve_operation_emit_code(cgenv->sbin, &notify_old_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_notify_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, "NOTIFY");
+	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_IMPORTANCE:
+				if ( !sieve_opr_number_dump(denv, address, "importance") )
+					return FALSE;
+				break;
+			case OPT_ID:
+				if ( !sieve_opr_string_dump(denv, address, "id") )
+					return FALSE;
+				break;
+			case OPT_OPTIONS:
+				if ( !sieve_opr_stringlist_dump(denv, address, "options") )
+					return FALSE;
+				break;
+			case OPT_MESSAGE:
+				if ( !sieve_opr_string_dump(denv, address, "message") )
+					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;
+	struct sieve_coded_stringlist *options = NULL;
+	string_t *message = NULL, *id = 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_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;
+			case OPT_ID:
+				if ( !sieve_opr_string_read(renv, address, &id) ) {
+					sieve_runtime_trace_error(renv, "invalid id operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				}
+				break;
+			case OPT_MESSAGE:
+				if ( !sieve_opr_string_read(renv, address, &message) ) {
+					sieve_runtime_trace_error(renv, "invalid from operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				}
+				break;
+			case OPT_OPTIONS:
+				if ( (options=sieve_opr_stringlist_read(renv, address)) == NULL ) {
+					sieve_runtime_trace_error(renv, "invalid options operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				}
+				break;
+			default:
+				sieve_runtime_trace_error(renv, "unknown optional operand: %d", 
+					opt_code);
+				return SIEVE_EXEC_BIN_CORRUPT;
+			}
+		}
+	}
+		
+	/*
+	 * Perform operation
+	 */
+
+	sieve_runtime_trace(renv, "NOTIFY action");	
+
+	/* Normalize and verify all :options addresses */
+	if ( options != NULL ) {
+		string_t *raw_address;
+		bool result;	
+
+		pool = sieve_result_pool(renv->result);
+		act = p_new(pool, struct ext_notify_action, 1);
+		if ( id != NULL )
+				act->id = p_strdup(pool, str_c(id));
+		if ( message != NULL )
+			act->message = p_strdup(pool, str_c(message));
+		act->importance = importance;
+			
+		sieve_coded_stringlist_reset(options);
+			
+		p_array_init(&act->recipients, pool, 4);
+		
+		raw_address = NULL;
+		while ( (result=sieve_coded_stringlist_next_item(options, &raw_address))
+			&& raw_address != NULL ) {
+			const char *error;
+			const char *addr_norm = sieve_address_normalize(raw_address, &error);
+			
+			if ( addr_norm != NULL ) {
+				struct ext_notify_recipient recipient;
+
+				recipient.full = p_strdup(pool, str_c(raw_address));
+				recipient.normalized = p_strdup(pool, addr_norm);
+		
+				array_append(&act->recipients, &recipient, 1);			
+			} else {
+				sieve_runtime_error(renv, 
+					sieve_error_script_location(renv->script, source_line),
+					"specified :options address '%s' is invalid for "
+					"the mailto notify method: %s", 
+					str_sanitize(str_c(raw_address), 128), error);
+				return SIEVE_EXEC_FAILURE;
+			}
+		}
+		
+		if ( !result ) {
+			sieve_runtime_trace_error(renv, "invalid options stringlist");
+			return SIEVE_EXEC_BIN_CORRUPT;
+		}
+		
+		return ( sieve_result_add_action
+			(renv, &act_notify_old, NULL, source_line, (void *) act, 0) >= 0 );
+	}
+
+	return SIEVE_EXEC_OK;
+}
+
+/*
+ * Action
+ */
+
+/* Runtime verification */
+
+static int act_notify_check_duplicate
+(const struct sieve_runtime_env *renv ATTR_UNUSED, 
+	const struct sieve_action_data *act ATTR_UNUSED,
+	const struct sieve_action_data *act_other ATTR_UNUSED)
+{
+	struct ext_notify_action *new_nact, *old_nact;
+	const struct ext_notify_recipient *new_rcpts;
+	const struct ext_notify_recipient *old_rcpts;
+	unsigned int new_count, old_count, i, j;
+	unsigned int del_start = 0, del_len = 0;
+		
+	if ( act->context == NULL || act_other->context == NULL )
+		return 0;
+
+	new_nact = (struct ext_notify_action *) act->context;
+	old_nact = (struct ext_notify_action *) act_other->context;
+
+	new_rcpts = array_get(&new_nact->recipients, &new_count);
+	old_rcpts = array_get(&old_nact->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(&new_nact->recipients, del_start, del_len);
+
+				/* Make sure the loop integrity is maintained */
+				i -= del_len;
+				new_rcpts = array_get(&new_nact->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(&new_nact->recipients, del_start, del_len);			
+	}
+
+	return ( array_count(&new_nact->recipients) > 0 ? 0 : 1 );
+}
+
+/* Result printing */
+ 
+static void act_notify_print
+(const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_result_print_env *rpenv, void *context, 
+	bool *keep ATTR_UNUSED)	
+{
+	const struct ext_notify_action *act = 
+		(const struct ext_notify_action *) context;
+	const struct ext_notify_recipient *recipients;
+	unsigned int count, i;
+
+	sieve_result_action_printf
+		( rpenv, "send (depricated) notification with method 'mailto':");
+	
+	/* Print main method parameters */
+
+	sieve_result_printf
+		( rpenv, "    => importance    : %d\n", act->importance);
+
+	if ( act->message != NULL )
+		sieve_result_printf
+			( rpenv, "    => message       : %s\n", act->message);
+
+	if ( act->id != NULL )
+		sieve_result_printf
+			( rpenv, "    => id            : %s \n", act->id);
+
+	/* Print mailto: recipients */
+
+	sieve_result_printf
+		( rpenv, "    => recipients    :\n" );
+
+	recipients = array_get(&act->recipients, &count);
+	if ( count == 0 ) {
+		sieve_result_printf(rpenv, "       NONE, action has no effect\n");
+	} else {
+		for ( i = 0; i < count; i++ ) {
+			sieve_result_printf
+				( rpenv, "       + To: %s\n", recipients[i].full);
+		}
+	}
+
+	/* Finish output with an empty line */
+
+	sieve_result_printf(rpenv, "\n");
+}
+
+/* Result 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 act_notify_send
+(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;
+	unsigned int count, i;
+	FILE *f;
+	const char *outmsgid;
+
+	/* Get recipients */
+	recipients = array_get(&act->recipients, &count);
+	if ( count == 0  ) {
+		sieve_result_warning(aenv, 
+			"notify action specifies no recipients; action has no effect");
+		return TRUE;
+	}
+
+	/* Just to be sure */
+	if ( senv->smtp_open == NULL || senv->smtp_close == NULL ) {
+		sieve_result_warning(aenv, 
+			"notify action has no means to send mail");
+		return TRUE;
+	}
+	
+	/* Send message to all recipients */
+	for ( i = 0; i < count; i++ ) {
+
+		if ( msgdata->return_path != NULL )
+			smtp_handle = senv->smtp_open(recipients[i].normalized, 
+				senv->postmaster_address, &f);
+		else		
+			smtp_handle = senv->smtp_open(recipients[i].normalized, NULL, &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));
+
+		/* 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;
+		}
+
+		rfc2822_header_field_printf(f, "From", "%s", 
+			t_strdup_printf("Postmaster <%s>", senv->postmaster_address));
+
+		rfc2822_header_field_printf(f, "To", "%s", recipients[i].full);
+
+		rfc2822_header_field_write(f, "Subject", "[SIEVE] New mail notification");
+
+		rfc2822_header_field_write(f, "Auto-Submitted", "auto-generated (notify)");
+		rfc2822_header_field_write(f, "Precedence", "bulk");
+
+		if (contains_8bit(act->message)) {
+			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");
+		}
+
+		/* Generate message body */
+		fprintf(f, "\r\n");
+		fprintf(f, "%s\r\n", act->message);
+			
+		if ( senv->smtp_close(smtp_handle) ) {
+			sieve_result_log(aenv, 
+				"sent mail notification to <%s>", 
+				str_sanitize(recipients[i].normalized, 80));
+		} else {
+			sieve_result_error(aenv,
+				"failed to send mail notification to <%s> "
+				"(refer to system log for more information)", 
+				str_sanitize(recipients[i].normalized, 80));
+		}
+	}
+
+	return TRUE;
+}
+
+static bool act_notify_commit
+(const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_action_exec_env *aenv, void *tr_context, 
+	bool *keep ATTR_UNUSED)
+{
+	const struct ext_notify_action *act = 
+		(const struct ext_notify_action *) tr_context;
+	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const char *const *headers;
+
+	/* Is the message an automatic reply ? */
+	if ( mail_get_headers
+		(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_result_log(aenv, 
+					"not sending notification for auto-submitted message from <%s>", 
+					str_sanitize(msgdata->return_path, 128));	
+					return TRUE;				 
+			}
+			hdsp++;
+		}
+	}
+
+	return act_notify_send(aenv, act);
+}
+
+
+
+
+
+
+
diff --git a/src/lib-sieve/plugins/notify/ext-enotify.c b/src/lib-sieve/plugins/notify/ext-enotify.c
new file mode 100644
index 000000000..2ed08298d
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/ext-enotify.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+/* Extension enotify
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5435
+ * Implementation: full
+ * Status: testing
+ * 
+ */
+	
+#include <stdio.h>
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation *ext_enotify_operations[] = {
+	&notify_operation,
+	&valid_notify_method_operation,
+	&notify_method_capability_operation
+};
+
+/* 
+ * Extension
+ */
+
+static bool ext_enotify_load(void);
+static void ext_enotify_unload(void);
+static bool ext_enotify_validator_load(struct sieve_validator *valdtr);
+
+static int ext_my_id = -1;
+
+const struct sieve_extension enotify_extension = { 
+	"enotify", 
+	&ext_my_id,
+	ext_enotify_load,
+	ext_enotify_unload,
+	ext_enotify_validator_load, 
+	NULL, NULL, NULL, NULL, NULL,
+	SIEVE_EXT_DEFINE_OPERATIONS(ext_enotify_operations),
+	SIEVE_EXT_DEFINE_OPERAND(encodeurl_operand)
+};
+
+static bool ext_enotify_load(void)
+{
+	ext_enotify_methods_init();
+
+	sieve_extension_capabilities_register(&notify_capabilities);
+
+	return TRUE;
+}
+
+static void ext_enotify_unload(void)
+{
+	ext_enotify_methods_deinit();
+}
+
+static bool ext_enotify_validator_load(struct sieve_validator *valdtr)
+{
+	/* Register new commands */
+	sieve_validator_register_command(valdtr, &notify_command);
+	sieve_validator_register_command(valdtr, &valid_notify_method_test);
+	sieve_validator_register_command(valdtr, &notify_method_capability_test);
+	
+	/* Register new set modifier for variables extension */
+	sieve_variables_modifier_register(valdtr, &encodeurl_modifier);
+	
+	return TRUE;
+}
+
diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.h b/src/lib-sieve/plugins/notify/ext-notify-common.h
new file mode 100644
index 000000000..8f07f5397
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/ext-notify-common.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#ifndef __EXT_NOTIFY_COMMON_H
+#define __EXT_NOTIFY_COMMON_H
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension notify_extension;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command cmd_notify_old;
+extern const struct sieve_command cmd_denotify;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation notify_old_operation;
+extern const struct sieve_operation denotify_operation;
+
+enum ext_notify_opcode {
+	EXT_NOTIFY_OPERATION_NOTIFY,
+	EXT_NOTIFY_OPERATION_DENOTIFY,
+};
+
+/* Action context */
+
+struct ext_notify_recipient {
+	const char *full;
+	const char *normalized;
+};
+
+ARRAY_DEFINE_TYPE(recipients, struct ext_notify_recipient);
+		
+struct ext_notify_action {
+	const char *id;
+	const char *message;
+	sieve_number_t importance;
+
+	ARRAY_TYPE(recipients) recipients;
+};
+
+#endif /* __EXT_NOTIFY_COMMON_H */
diff --git a/src/lib-sieve/plugins/notify/ext-notify-limits.h b/src/lib-sieve/plugins/notify/ext-notify-limits.h
new file mode 100644
index 000000000..d6fc88eca
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/ext-notify-limits.h
@@ -0,0 +1,10 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+ 
+#ifndef __EXT_NOTIFY_LIMITS_H
+#define __EXT_NOTIFY_LIMITS_H
+
+#define EXT_NOTIFY_MAX_RECIPIENTS  8
+#define EXT_NOTIFY_MAX_MESSAGE     256
+
+#endif /* __EXT_NOTIFY_LIMITS_H */
diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c
new file mode 100644
index 000000000..53426863c
--- /dev/null
+++ b/src/lib-sieve/plugins/notify/ext-notify.c
@@ -0,0 +1,105 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+/* Extension notify
+ * ----------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: draft-ietf-sieve-notify-00.txt
+ * Implementation: deprecated; provided for backwards compatibility
+ *                 denotify command is explicitly not supported.
+ * Status: deprecated
+ * 
+ */
+	
+#include <stdio.h>
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-notify-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation *ext_notify_operations[] = {
+	&notify_old_operation,
+	&denotify_operation
+};
+
+/* 
+ * Extension
+ */
+
+static bool ext_notify_validator_load(struct sieve_validator *valdtr);
+
+static int ext_notify_my_id = -1;
+
+const struct sieve_extension notify_extension = { 
+	"notify", 
+	&ext_notify_my_id,
+	NULL,
+	NULL,
+	ext_notify_validator_load, 
+	NULL, NULL, NULL, NULL, NULL,
+	SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations),
+	SIEVE_EXT_DEFINE_NO_OPERANDS,
+};
+
+/*
+ * Extension validation
+ */
+
+static bool ext_notify_validator_extension_validate
+	(struct sieve_validator *valdtr, void *context, 
+		struct sieve_ast_argument *require_arg);
+
+const struct sieve_validator_extension notify_validator_extension = {
+	&notify_extension,
+	ext_notify_validator_extension_validate,
+	NULL
+};
+
+static bool ext_notify_validator_load(struct sieve_validator *valdtr)
+{
+	/* Register validator extension to check for conflict with enotify */
+	sieve_validator_extension_register
+		(valdtr, &notify_validator_extension, NULL);
+
+	/* Register new commands */
+	sieve_validator_register_command(valdtr, &cmd_notify_old);
+	sieve_validator_register_command(valdtr, &cmd_denotify);
+	
+	return TRUE;
+}
+
+static bool ext_notify_validator_extension_validate
+(struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+    struct sieve_ast_argument *require_arg)
+{
+	const struct sieve_extension *ext;
+
+	if ( (ext=sieve_extension_get_by_name("enotify")) != NULL ) {
+
+		/* Check for conflict with enotify */
+		if ( sieve_validator_extension_loaded(valdtr, ext) ) {
+			sieve_argument_validate_error(valdtr, require_arg,
+				"the (deprecated) notify extension cannot be used "
+				"together with the enotify extension");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+
-- 
GitLab