diff --git a/src/lib-sieve/plugins/enotify/Makefile.am b/src/lib-sieve/plugins/enotify/Makefile.am
index a32fb02d22651f55d108e208b4c8abce3ccac745..d73dd10527e8fbdee801a1eabe9358fff3175b59 100644
--- a/src/lib-sieve/plugins/enotify/Makefile.am
+++ b/src/lib-sieve/plugins/enotify/Makefile.am
@@ -23,11 +23,13 @@ notify_methods = \
 
 libsieve_ext_enotify_la_SOURCES = \
 	ext-enotify.c \
+	ext-enotify-common.c \
 	$(commands) \
 	$(tests) \
 	$(var_modifiers) \
 	$(notify_methods)
 
 noinst_HEADERS = \
+	sieve-ext-enotify.h \
 	ext-enotify-common.h
 
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
new file mode 100644
index 0000000000000000000000000000000000000000..66c90a3e38f622e43e2434fdcf844e1320f32515
--- /dev/null
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
@@ -0,0 +1,26 @@
+#include "lib.h"
+
+#include "sieve-common.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * Notify capability
+ */
+
+static const char *ext_notify_get_methods_string(void);
+
+const struct sieve_extension_capabilities notify_capabilities = {
+	"notify",
+	ext_notify_get_methods_string
+};
+
+/*
+ * Notify method registry
+ */
+
+static const char *ext_notify_get_methods_string(void)
+{
+	return "mailto";
+}
+
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h
index 02a46d138d8115ba4f7f4b900c28c698eaac4dac..9e0e04112c7cfbb8e9ecd1c1ca3e53b97451f873 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h
@@ -6,11 +6,14 @@
 
 #include "sieve-ext-variables.h"
 
+#include "sieve-ext-enotify.h"
+
 /*
  * Extension
  */
 
 extern const struct sieve_extension enotify_extension;
+extern const struct sieve_extension_capabilities notify_capabilities;
 
 /*
  * Commands
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c
index 5fea70eb4221559dab7e851b98c1adf355032c59..e3ee477198b413a1a864445bf32007cca15a13e4 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify.c
@@ -61,6 +61,8 @@ static bool ext_enotify_load(int ext_id)
 {
 	ext_my_id = ext_id;
 
+	sieve_extension_capabilities_register(&notify_capabilities);
+
 	return TRUE;
 }
 
diff --git a/src/lib-sieve/plugins/enotify/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/ntfy-mailto.c
index 0c7cfcc4f7a28417da821c3148e808f04d1ae616..366c32b215e664ab91625d7b3286141b44aca8a5 100644
--- a/src/lib-sieve/plugins/enotify/ntfy-mailto.c
+++ b/src/lib-sieve/plugins/enotify/ntfy-mailto.c
@@ -35,6 +35,10 @@ static bool ntfy_mailto_parse_uri
 {
 	*recipient_r = "stephan@rename-it.nl";
 	*headers = NULL;
+
+	/* Scheme already parsed, starting parse after colon */
+
+	/* First parse e-mail address */
 }
 
 /*
diff --git a/src/lib-sieve/sieve-address.c b/src/lib-sieve/sieve-address.c
index 7117952522cdf0b4c7cbee1f32592ca53c378fc7..5ec44ccc305fc957746cf6622cb16b7279d9499e 100644
--- a/src/lib-sieve/sieve-address.c
+++ b/src/lib-sieve/sieve-address.c
@@ -55,14 +55,19 @@
  * Message address specification as allowed bij the RFC 5228 SIEVE 
  * specification:
  *   sieve-address   =       addr-spec                  ; simple address
- *                           / phrase "<" addr-spec ">" ; name & addr-spec
+ *                           / phrase "<" addr-spec ">" ; name & addr-spec\
+ *
+ * Which concisely is about equal to:
+ *   sieve-address   =       mailbox
  */ 
+
+/*
+ * Address parse context
+ */
  
 struct sieve_message_address_parser {
 	struct rfc822_parser_context parser;
 
-	string_t *address;
-
 	string_t *str;
 	string_t *local_part;
 	string_t *domain;
@@ -70,6 +75,10 @@ struct sieve_message_address_parser {
 	string_t *error;
 };
 
+/*
+ * Error handling
+ */
+
 static inline void sieve_address_error
 	(struct sieve_message_address_parser *ctx, const char *fmt, ...) 
 		ATTR_FORMAT(2, 3);
@@ -85,6 +94,14 @@ static inline void sieve_address_error
 		va_end(args);
 	}
 }
+
+/*
+ * Partial RFC 2822 address parser
+ *
+ *   FIXME: lots of overlap with dovecot/src/lib-mail/message-parser.c
+ *          --> this implementation adds textual error reporting
+ *          MERGE!
+ */
 	
 static int parse_local_part(struct sieve_message_address_parser *ctx)
 {
@@ -143,7 +160,7 @@ static int parse_addr_spec(struct sieve_message_address_parser *ctx)
 	return -1;
 }
 
-static int parse_name_addr(struct sieve_message_address_parser *ctx)
+static int parse_mailbox(struct sieve_message_address_parser *ctx)
 {
 	int ret;
 	const unsigned char *start;
@@ -188,14 +205,15 @@ static int parse_name_addr(struct sieve_message_address_parser *ctx)
 	return ret;
 }
 
-static bool parse_sieve_address(struct sieve_message_address_parser *ctx)
+static bool parse_mailbox_address
+(struct sieve_message_address_parser *ctx, const unsigned char *address, 
+	unsigned int addr_size)
 {
 	int ret;
 	
 	/* Initialize parser */
 	
-	rfc822_parser_init(&ctx->parser, str_data(ctx->address), str_len(ctx->address), 
-		t_str_new(128));
+	rfc822_parser_init(&ctx->parser, address, addr_size, NULL);
 
 	/* Parse */
 	
@@ -206,7 +224,7 @@ static bool parse_sieve_address(struct sieve_message_address_parser *ctx)
 		return FALSE;
 	}
 	
-	if ((ret = parse_name_addr(ctx)) < 0) {
+	if ((ret = parse_mailbox(ctx)) < 0) {
 		return FALSE;
 	}
 			
@@ -215,6 +233,7 @@ static bool parse_sieve_address(struct sieve_message_address_parser *ctx)
 		sieve_address_error(ctx, "missing domain");
 		return FALSE;
 	}
+
 	if ( str_len(ctx->local_part) == 0 ) {
 		sieve_address_error(ctx, "missing local part");
 		return FALSE;
@@ -223,20 +242,48 @@ static bool parse_sieve_address(struct sieve_message_address_parser *ctx)
 	return TRUE;
 }
 
+bool sieve_validate_rfc2822_mailbox(const char *address, const char **error_r)
+{
+	struct sieve_message_address_parser ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	
+	ctx.local_part = t_str_new(128);
+	ctx.domain = t_str_new(128);
+	ctx.str = t_str_new(128);
+	ctx.error = t_str_new(128);
+
+	if ( !parse_mailbox_address(&ctx, (const unsigned char *) address, 
+		strlen(address)) ) {
+		if ( error_r != NULL )	
+			*error_r = str_c(ctx.error);
+		return FALSE;
+	}
+
+	if ( error_r != NULL )
+		*error_r = NULL;
+
+	return TRUE;
+}
+
+
+/*
+ * Sieve address
+ */
+
 const char *sieve_address_normalize
 (string_t *address, const char **error_r)
 {
 	struct sieve_message_address_parser ctx;
 
 	memset(&ctx, 0, sizeof(ctx));
-	ctx.address = address;
 	
 	ctx.local_part = t_str_new(128);
 	ctx.domain = t_str_new(128);
 	ctx.str = t_str_new(128);
 	ctx.error = t_str_new(128);
 
-	if ( !parse_sieve_address(&ctx) )
+	if ( !parse_mailbox_address(&ctx, str_data(address), str_len(address)) )
 	{
 		*error_r = str_c(ctx.error);
 		return NULL;
@@ -254,12 +301,11 @@ bool sieve_address_validate
 	struct sieve_message_address_parser ctx;
 
 	memset(&ctx, 0, sizeof(ctx));
-	ctx.address = address;
 
 	ctx.local_part = ctx.domain = ctx.str = t_str_new(128);
 	ctx.error = t_str_new(128);
 
-	if ( !parse_sieve_address(&ctx) )
+	if ( !parse_mailbox_address(&ctx, str_data(address), str_len(address)) )
 	{
 		*error_r = str_c(ctx.error);
 		return FALSE;
@@ -411,7 +457,7 @@ static int path_parse_domain
 	int ret;
 
 	/* Domain = (sub-domain 1*("." sub-domain)) / address-literal
-     * sub-domain = Let-dig [Ldh-str]
+	 * sub-domain = Let-dig [Ldh-str]
 	 * Let-dig = ALPHA / DIGIT
 	 * Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
 	 */
@@ -474,13 +520,13 @@ static int path_skip_source_route(struct sieve_envelope_address_parser *parser)
 	
 		for (;;) {
 			if ( (ret=path_skip_white_space(parser)) <= 0 )
-		        return -1;	
+				return -1;	
 
 			if ( (ret=path_parse_domain(parser, TRUE)) <= 0 )
-		        return -1;	
+				return -1;	
 
 			if ( (ret=path_skip_white_space(parser)) <= 0 )
-            	return ret;
+				return ret;
 
 			/* Next? */
 			if ( *parser->data != ',' )
@@ -488,7 +534,7 @@ static int path_skip_source_route(struct sieve_envelope_address_parser *parser)
 			parser->data++;
 
 			if ( (ret=path_skip_white_space(parser)) <= 0 )
-        	    return -1;
+				return -1;
 
 			if ( *parser->data != '@' )
 				return -1;
@@ -517,7 +563,7 @@ static int path_parse_local_part(struct sieve_envelope_address_parser *parser)
 	str_truncate(parser->str, 0);
 	if ( *parser->data == '"' ) {
 		str_append_c(parser->str, *parser->data);
-        parser->data++;
+		parser->data++;
 
 		while ( parser->data < parser->end ) {
 			if ( *parser->data == '\\' ) {
@@ -544,12 +590,12 @@ static int path_parse_local_part(struct sieve_envelope_address_parser *parser)
 			return -1;
 
 		str_append_c(parser->str, *parser->data);
-        parser->data++;
+		parser->data++;
 		
 		if ( (ret=path_skip_white_space(parser)) < 0 )
 			return ret;
-    } else {
-       for (;;) {
+	} else {
+		for (;;) {
 			if ( !IS_ATEXT(*parser->data) ) 
 				return -1;
 			str_append_c(parser->str, *parser->data);
@@ -610,9 +656,9 @@ static int path_parse(struct sieve_envelope_address_parser *parser)
 	if ( (ret=path_skip_white_space(parser)) <= 0 ) 
 		return ret;
 	
-    /* We allow angle brackets to be missing */
-    if ( *parser->data == '<' ) {
-        parser->data++;
+	/* We allow angle brackets to be missing */
+	if ( *parser->data == '<' ) {
+		parser->data++;
 		brackets = TRUE;
 
 		if ( (ret=path_skip_white_space(parser)) <= 0 ) 
diff --git a/src/lib-sieve/sieve-address.h b/src/lib-sieve/sieve-address.h
index 2373d4284369570104917992d3fd8e7d303f2d52..e4a51148dc5ec2e3aceefc065c0b88b49576afeb 100644
--- a/src/lib-sieve/sieve-address.h
+++ b/src/lib-sieve/sieve-address.h
@@ -17,6 +17,8 @@ struct sieve_address {
  * RFC 2822 addresses
  */ 
 
+bool sieve_validate_rfc2822_mailbox(const char *address, const char **error_r);
+
 const char *sieve_address_normalize
 	(string_t *address, const char **error_r);
 bool sieve_address_validate
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index ec57b738f7d99319400e4fb0cd9b9c9726482794..05d98e3617ee10e4b6bb17a7b8d9780ec3828df3 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -17,6 +17,9 @@
 static void sieve_extensions_init_registry(void);
 static void sieve_extensions_deinit_registry(void);
 
+static void sieve_extensions_init_capabilities(void);
+static void sieve_extensions_deinit_capabilities(void);
+
 /* 
  * Pre-loaded 'extensions' 
  */
@@ -122,6 +125,7 @@ bool sieve_extensions_init(const char *sieve_plugins ATTR_UNUSED)
 	unsigned int i;
 	
 	sieve_extensions_init_registry();
+	sieve_extensions_init_capabilities();
 	
 	/* Pre-load core extensions */
 	for ( i = 0; i < sieve_core_extensions_count; i++ ) {
@@ -135,6 +139,7 @@ bool sieve_extensions_init(const char *sieve_plugins ATTR_UNUSED)
 
 void sieve_extensions_deinit(void)
 {	
+	sieve_extensions_deinit_capabilities();
 	sieve_extensions_deinit_registry();
 }
 
@@ -211,7 +216,7 @@ const struct sieve_extension *sieve_extension_get_by_name(const char *name)
 	return ereg->extension;
 }
 
-static bool _list_extension(const struct sieve_extension *ext)
+static inline bool _list_extension(const struct sieve_extension *ext)
 {
 	return ( ext->id != NULL && *ext->name != '@' );
 }
@@ -266,4 +271,43 @@ static void sieve_extensions_deinit_registry(void)
 	hash_destroy(&extension_index);
 }
 
+/*
+ * Extension capabilities
+ */
+
+static struct hash_table *capabilities_index; 
+
+static void sieve_extensions_init_capabilities(void)
+{	
+	capabilities_index = hash_create
+		(default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
+}
+
+static void sieve_extensions_deinit_capabilities(void) 
+{
+	hash_destroy(&capabilities_index);
+}
+
+void sieve_extension_capabilities_register
+	(const struct sieve_extension_capabilities *cap) 
+{	
+	hash_insert
+		(capabilities_index, (void *) cap->name, (void *) cap);
+}
+
+const char *sieve_extension_capabilities_get_string
+	(const char *cap_name) 
+{
+  const struct sieve_extension_capabilities *cap = 
+		(const struct sieve_extension_capabilities *) 
+			hash_lookup(capabilities_index, cap_name);
+
+	if ( cap == NULL || cap->get_string == NULL )
+		return NULL;
+		
+	return cap->get_string();
+}
+
+
+
 
diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h
index 8f1738b38f36c5b0cc3a4afc75db9f54a0285961..9c24962457928f11d740d89ec1e9717c52609ed1 100644
--- a/src/lib-sieve/sieve-extensions.h
+++ b/src/lib-sieve/sieve-extensions.h
@@ -91,4 +91,19 @@ const struct sieve_extension *sieve_extension_get_by_name(const char *name);
 
 const char *sieve_extensions_get_string(void);
 
+/*
+ * Capability registries
+ */
+
+struct sieve_extension_capabilities {
+	const char *name;
+
+	const char *(*get_string)(void);	
+};
+
+void sieve_extension_capabilities_register
+	(const struct sieve_extension_capabilities *cap);
+const char *sieve_extension_capabilities_get_string
+	(const char *cap_name);
+
 #endif /* __SIEVE_EXTENSIONS_H */
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 718cd09dc1d698a7053acff7cdc71c6a789b996f..760f6eb68d68d40de7427109d7eed113a4a8aba7 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -43,9 +43,12 @@ void sieve_deinit(void)
 	sieve_extensions_deinit();
 }
 
-const char *sieve_get_capabilities(void) 
+const char *sieve_get_capabilities(const char *name) 
 {
-	return sieve_extensions_get_string();
+	if ( name == NULL || *name == '\0' )
+		return sieve_extensions_get_string();
+	
+	return sieve_extension_capabilities_get_string(name);
 }
 
 /*
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index 417393eacac14c6d2ff048f8c21b5ec39520d7c5..b4f110f7b20acde0ea92f7648ac1c72100728db4 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -31,7 +31,7 @@ void sieve_deinit(void);
 /* sieve_get_capabilities:
  *
  */
-const char *sieve_get_capabilities(void);
+const char *sieve_get_capabilities(const char *name);
 
 /*
  * Script compilation