From 2fbe78be82512408d7003c17d4fe7e5c8a0300d3 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 16 Nov 2007 19:35:58 +0100
Subject: [PATCH] Implemented support for the subaddress extension and fixed
 extension support to work properly.

---
 configure.in                                  |   1 +
 src/lib-sieve/ext-envelope.c                  |   2 +-
 src/lib-sieve/plugins/Makefile.am             |   2 +-
 src/lib-sieve/plugins/subaddress/Makefile.am  |  12 +
 .../plugins/subaddress/ext-subaddress.c       | 121 +++++++
 src/lib-sieve/plugins/subaddress/rfc3598.txt  | 339 ++++++++++++++++++
 .../plugins/subaddress/subaddress.sieve       |  13 +
 src/lib-sieve/plugins/vacation/ext-vacation.c |  12 +-
 src/lib-sieve/sieve-address-parts.c           | 229 ++++++++----
 src/lib-sieve/sieve-address-parts.h           |  40 ++-
 src/lib-sieve/sieve-ast.h                     |   3 +
 src/lib-sieve/sieve-binary.c                  |   6 +
 src/lib-sieve/sieve-binary.h                  |   1 +
 src/lib-sieve/sieve-commands.c                |   6 +-
 src/lib-sieve/sieve-commands.h                |   2 +
 src/lib-sieve/sieve-comparators.c             |   4 +-
 src/lib-sieve/sieve-extensions.c              |   9 +-
 src/lib-sieve/sieve-extensions.h              |   2 +-
 src/lib-sieve/sieve-interpreter.c             |  32 +-
 src/lib-sieve/sieve-validator.c               | 149 +++++---
 src/lib-sieve/tst-address.c                   |   4 +-
 src/lib-sieve/tst-size.c                      |   4 +-
 src/sieve-bin/Makefile.am                     |   4 +-
 23 files changed, 851 insertions(+), 146 deletions(-)
 create mode 100644 src/lib-sieve/plugins/subaddress/Makefile.am
 create mode 100644 src/lib-sieve/plugins/subaddress/ext-subaddress.c
 create mode 100644 src/lib-sieve/plugins/subaddress/rfc3598.txt
 create mode 100644 src/lib-sieve/plugins/subaddress/subaddress.sieve

diff --git a/configure.in b/configure.in
index 4c6cf3bd5..3934fdda4 100644
--- a/configure.in
+++ b/configure.in
@@ -60,6 +60,7 @@ src/Makefile
 src/lib-sieve/Makefile
 src/lib-sieve/plugins/Makefile
 src/lib-sieve/plugins/vacation/Makefile
+src/lib-sieve/plugins/subaddress/Makefile
 src/sieve-bin/Makefile
 stamp.h])
 
diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c
index 70698ceac..1eb7362f1 100644
--- a/src/lib-sieve/ext-envelope.c
+++ b/src/lib-sieve/ext-envelope.c
@@ -154,7 +154,7 @@ static bool ext_envelope_opcode_dump
             case OPT_MATCH_TYPE:
                 break;
 			case OPT_ADDRESS_PART:
-                sieve_opr_address_part_dump(sbin, address);
+                sieve_opr_address_part_dump(interp, sbin, address);
 				break;
             default:
                 return FALSE;
diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am
index d1b05da32..f4041635a 100644
--- a/src/lib-sieve/plugins/Makefile.am
+++ b/src/lib-sieve/plugins/Makefile.am
@@ -1 +1 @@
-SUBDIRS = vacation
+SUBDIRS = vacation subaddress
diff --git a/src/lib-sieve/plugins/subaddress/Makefile.am b/src/lib-sieve/plugins/subaddress/Makefile.am
new file mode 100644
index 000000000..befb510b6
--- /dev/null
+++ b/src/lib-sieve/plugins/subaddress/Makefile.am
@@ -0,0 +1,12 @@
+noinst_LIBRARIES = lib_ext_subaddress.a
+
+AM_CPPFLAGS = \
+	-I../../ \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(dovecot_incdir)/src/lib-mail \
+	-I$(dovecot_incdir)/src/lib-storage 
+
+lib_ext_subaddress_a_SOURCES = \
+	ext-subaddress.c
+
diff --git a/src/lib-sieve/plugins/subaddress/ext-subaddress.c b/src/lib-sieve/plugins/subaddress/ext-subaddress.c
new file mode 100644
index 000000000..25f881471
--- /dev/null
+++ b/src/lib-sieve/plugins/subaddress/ext-subaddress.c
@@ -0,0 +1,121 @@
+#include <stdio.h>
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-address-parts.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+/* Forward declarations */
+static bool ext_subaddress_load(int ext_id);
+static bool ext_subaddress_validator_load(struct sieve_validator *validator);
+static bool ext_subaddress_interpreter_load
+	(struct sieve_interpreter *interpreter);
+
+/* Extension definitions */
+
+int ext_my_id;
+
+const struct sieve_extension subaddress_extension = { 
+	"subaddress", 
+	ext_subaddress_load,
+	ext_subaddress_validator_load,
+	NULL, 
+	ext_subaddress_interpreter_load,  
+	NULL, 
+	NULL
+};
+
+static bool ext_subaddress_load(int ext_id)
+{
+	ext_my_id = ext_id;
+
+	return TRUE;
+}
+
+/* */
+
+static const char *subaddress_user_extract_from
+	(const struct message_address *address)
+{
+	i_info("subaddress: user: %s.", address->mailbox);
+	return address->mailbox;
+}
+
+static const char *subaddress_detail_extract_from
+	(const struct message_address *address)
+{
+	i_info("subaddress: detail: %s.", address->mailbox);
+	return address->mailbox;
+}
+
+enum ext_subaddress_address_part {
+  SUBADDRESS_USER,
+  SUBADDRESS_DETAIL
+};
+
+const struct sieve_address_part user_address_part = {
+	"user",
+	SIEVE_ADDRESS_PART_CUSTOM,
+	&subaddress_extension,
+	SUBADDRESS_USER,
+	subaddress_user_extract_from
+};
+
+const struct sieve_address_part detail_address_part = {
+	"detail",
+	SIEVE_ADDRESS_PART_CUSTOM,
+	&subaddress_extension,
+	SUBADDRESS_DETAIL,
+	subaddress_detail_extract_from
+};
+
+static const struct sieve_address_part *ext_subaddress_get_part 
+	(unsigned int code)
+{
+	switch ( code ) {
+	case SUBADDRESS_USER:
+		return &user_address_part;
+	case SUBADDRESS_DETAIL:
+		return &detail_address_part;
+	default:
+		break;
+	}
+	
+	return NULL;
+}
+
+const struct sieve_address_part_extension subaddress_addrp_extension = { 
+	NULL, 
+	
+	ext_subaddress_get_part
+};
+
+/* Load extension into validator */
+
+static bool ext_subaddress_validator_load(struct sieve_validator *validator)
+{
+	sieve_address_part_register
+		(validator, &user_address_part, ext_my_id); 
+	sieve_address_part_register
+		(validator, &detail_address_part, ext_my_id); 
+
+	return TRUE;
+}
+
+/* Load extension into interpreter */
+
+static bool ext_subaddress_interpreter_load
+	(struct sieve_interpreter *interpreter)
+{
+	sieve_address_part_extension_set
+		(interpreter, ext_my_id, &subaddress_addrp_extension);
+
+	return TRUE;
+}
+
+
diff --git a/src/lib-sieve/plugins/subaddress/rfc3598.txt b/src/lib-sieve/plugins/subaddress/rfc3598.txt
new file mode 100644
index 000000000..3bbc5976f
--- /dev/null
+++ b/src/lib-sieve/plugins/subaddress/rfc3598.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Network Working Group                                       K. Murchison
+Request for Comments: 3598                            Oceana Matrix Ltd.
+Category: Standards Track                                 September 2003
+
+
+             Sieve Email Filtering -- Subaddress Extension
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+Abstract
+
+   On email systems that allow for "subaddressing" or "detailed
+   addressing" (e.g., "ken+sieve@example.org"), it is sometimes
+   desirable to make comparisons against these sub-parts of addresses.
+   This document defines an extension to the Sieve mail filtering
+   language that allows users to compare against the user and detail
+   parts of an address.
+
+Table of Contents
+
+   1.  Introduction. . . . . . . . . . . . . . . . . . . . . . . . .   2
+   2.  Capability Identifier . . . . . . . . . . . . . . . . . . . .   2
+   3.  Subaddress Comparisons. . . . . . . . . . . . . . . . . . . .   2
+   4.  Security Considerations . . . . . . . . . . . . . . . . . . .   4
+   5.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .   4
+   6.  Normative References. . . . . . . . . . . . . . . . . . . . .   4
+   7.  Acknowledgments . . . . . . . . . . . . . . . . . . . . . . .   5
+   8.  Intellectual Property Statement . . . . . . . . . . . . . . .   5
+   9.  Author's Address. . . . . . . . . . . . . . . . . . . . . . .   5
+   10. Full Copyright Statement. . . . . . . . . . . . . . . . . . .   6
+
+
+
+
+
+
+
+
+
+
+
+Murchison                   Standards Track                     [Page 1]
+
+RFC 3598                 Sieve Email Filtering            September 2003
+
+
+1.  Introduction
+
+   Subaddressing is the practice of appending some "detail" information
+   to the local-part of an [IMAIL] address to indicate that the message
+   should be delivered to the mailbox specified by the "detail"
+   information.  The "detail" information is prefixed with a special
+   "separator character" (typically "+") which forms the boundary
+   between the "user" (original local-part) and the "detail" sub-parts
+   of the address, much like the "@" character forms the boundary
+   between the local-part and domain.
+
+   Typical uses of subaddressing might be:
+
+   -  A message addressed to "ken+sieve@example.org" is delivered into a
+      mailbox called "sieve" belonging to the user "ken".
+
+   -  A message addressed to "5551212#123@example.org" is delivered to
+      the voice mailbox number "123" at phone number "5551212".
+
+   This document describes an extension to the Sieve language defined by
+   [SIEVE] for comparing against the "user" and "detail" sub-parts of an
+   address.
+
+   Conventions for notations are as in [SIEVE] section 1.1, including
+   use of [KEYWORDS].
+
+2.  Capability Identifier
+
+   The capability string associated with the extension defined in this
+   document is "subaddress".
+
+3.  Subaddress Comparisons
+
+   Commands that act exclusively on addresses may take the optional
+   tagged arguments ":user"  and ":detail" to specify what sub-part of
+   the local-part of the address will be acted upon.
+
+   NOTE: In most cases, the envelope "to" address is the preferred
+   address to examine for subaddress information when the desire is to
+   sort messages based on how they were addressed so as to get to a
+   specific recipient.  The envelope address is, after all, the reason a
+   given message is being processed by a given sieve script for a given
+   user.  This is particularly true when mailing lists, aliases, and
+   "virtual domains" are involved since the envelope may be the only
+   source of detail information for the specific recipient.
+
+
+
+
+
+
+Murchison                   Standards Track                     [Page 2]
+
+RFC 3598                 Sieve Email Filtering            September 2003
+
+
+   The ":user" argument specifies that sub-part of the local-part which
+   lies to the left of the separator character (e.g., "ken" in
+   "ken+sieve@example.org").  If no separator character exists, then
+   ":user" specifies the entire left-side of the address (equivalent to
+   ":localpart").
+
+   The ":detail" argument specifies that sub-part of the local-part
+   which lies to the right of the separator character (e.g., "sieve" in
+   "ken+sieve@example.org").  If no separator character exists, the test
+   evaluates to false.  If nothing lies to the right of the separator
+   character, then ":detail" ":is" the null key ("").  Otherwise, the
+   ":detail" sub-part contains the null key.
+
+   Implementations MUST make sure that the separator character matches
+   that which is used and/or allowed by the encompassing mail system,
+   otherwise unexpected results might occur.  Implementations SHOULD
+   allow the separator character to be configurable so that they may be
+   used with a variety of mail systems.  Note that the mechanisms used
+   to define and/or query the separator character used by the mail
+   system are outside the scope of this document.
+
+   The ":user" and ":detail" address parts are subject to the same rules
+   and restrictions as the standard address parts defined in [SIEVE].
+   For convenience, the "ADDRESS-PART" syntax element defined in [SIEVE]
+   is augmented here as follows:
+
+      ADDRESS-PART  =/  ":user" / ":detail"
+
+   A diagram showing the ADDRESS-PARTs of a email address utilizing a
+   separator character of '+' is shown below:
+
+       :user "+" :detail  "@" :domain
+      `-----------------'
+          :local-part
+
+   Example:
+
+   require "subaddress";
+
+   # File mailing list messages (subscribed as "ken+mta-filters").
+   if envelope :detail "to" "mta-filters" {
+     fileinto "inbox.ietf-mta-filters";
+   }
+
+   # If a message is not to me (ignoring +detail), junk it.
+   if not allof (address :user ["to", "cc", "bcc"] "ken",
+        address :domain ["to", "cc", "bcc"] "example.org") {
+     discard;
+
+
+
+Murchison                   Standards Track                     [Page 3]
+
+RFC 3598                 Sieve Email Filtering            September 2003
+
+
+   }
+
+   # Redirect all mail sent to +foo.
+   if envelope :detail ["to", "cc", "bcc"] "foo" {
+     redirect "ken@example.edu";
+   }
+
+4.  Security Considerations
+
+   Security considerations are discussed in [SIEVE].  It is believed
+   that this extension does not introduce any additional security
+   concerns.
+
+5.  IANA Considerations
+
+   The following template specifies the IANA registration of the Sieve
+   extension specified in this document:
+
+   To: iana@iana.org
+   Subject: Registration of new Sieve extension
+
+   Capability name: subaddress
+   Capability keyword: subaddress
+   Capability arguments: N/A
+   Standards Track/RFC 3598
+   Person and email address to contact for further information:
+
+   Kenneth Murchison
+   ken@oceana.com
+
+   This information has been added to the list of sieve extensions given
+   on http://www.iana.org/assignments/sieve-extensions.
+
+6.  Normative References
+
+   [IMAIL]    Resnick, P., Ed., "Internet Message Format", RFC 2822,
+              April 2001.
+
+   [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [SIEVE]    Showalter, T., "Sieve: A Mail Filtering Language", RFC
+              3028, January 2001.
+
+
+
+
+
+
+
+
+Murchison                   Standards Track                     [Page 4]
+
+RFC 3598                 Sieve Email Filtering            September 2003
+
+
+7.  Acknowledgments
+
+   Thanks to Tim Showalter, Alexey Melnikov, Michael Salmon, Randall
+   Gellens, Philip Guenther and Jutta Degener for their help with this
+   document.
+
+8.  Intellectual Property Statement
+
+   The IETF takes no position regarding the validity or scope of any
+   intellectual property or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; neither does it represent that it
+   has made any effort to identify any such rights.  Information on the
+   IETF's procedures with respect to rights in standards-track and
+   standards-related documentation can be found in BCP-11.  Copies of
+   claims of rights made available for publication and any assurances of
+   licenses to be made available, or the result of an attempt made to
+   obtain a general license or permission for the use of such
+   proprietary rights by implementors or users of this specification can
+   be obtained from the IETF Secretariat.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights which may cover technology that may be required to practice
+   this standard.  Please address the information to the IETF Executive
+   Director.
+
+9.  Author's Address
+
+   Kenneth Murchison
+   Oceana Matrix Ltd.
+   21 Princeton Place
+   Orchard Park, NY 14127
+
+   EMail: ken@oceana.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Murchison                   Standards Track                     [Page 5]
+
+RFC 3598                 Sieve Email Filtering            September 2003
+
+
+10.  Full Copyright Statement
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assignees.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Murchison                   Standards Track                     [Page 6]
+
diff --git a/src/lib-sieve/plugins/subaddress/subaddress.sieve b/src/lib-sieve/plugins/subaddress/subaddress.sieve
new file mode 100644
index 000000000..76031f7e3
--- /dev/null
+++ b/src/lib-sieve/plugins/subaddress/subaddress.sieve
@@ -0,0 +1,13 @@
+require "subaddress";
+require "fileinto";
+
+if address :comparator "i;ascii-casemap" :user "from" "STEPHAN" {
+	discard;
+
+	if address :domain :comparator "i;octet" "from" "drunksnipers.com" {
+		keep;
+	}
+	stop;
+}
+
+keep;
diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c
index 13383a0d1..616c2652f 100644
--- a/src/lib-sieve/plugins/vacation/ext-vacation.c
+++ b/src/lib-sieve/plugins/vacation/ext-vacation.c
@@ -199,17 +199,17 @@ static bool cmd_vacation_validate_handle_tag
 /* Command registration */
 
 static const struct sieve_argument vacation_days_tag = 
-	{ "days", cmd_vacation_validate_days_tag, NULL };
+	{ "days", NULL, cmd_vacation_validate_days_tag, NULL };
 static const struct sieve_argument vacation_subject_tag =
-	{ "subject", cmd_vacation_validate_subject_tag, NULL };
+	{ "subject", NULL, cmd_vacation_validate_subject_tag, NULL };
 static const struct sieve_argument vacation_from_tag = 
-	{ "from", cmd_vacation_validate_from_tag, NULL };
+	{ "from", NULL, cmd_vacation_validate_from_tag, NULL };
 static const struct sieve_argument vacation_addresses_tag = 
-	{ "addresses", cmd_vacation_validate_addresses_tag, NULL };
+	{ "addresses", NULL, cmd_vacation_validate_addresses_tag, NULL };
 static const struct sieve_argument vacation_mime_tag = 
-	{ "mime", cmd_vacation_validate_mime_tag, NULL };
+	{ "mime", NULL, cmd_vacation_validate_mime_tag, NULL };
 static const struct sieve_argument vacation_handle_tag = 
-	{ "handle", cmd_vacation_validate_handle_tag, NULL };
+	{ "handle", NULL, cmd_vacation_validate_handle_tag, NULL };
 
 enum cmd_vacation_optional {
 	OPT_DAYS,
diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c
index f0f7dabb6..e553ed9fa 100644
--- a/src/lib-sieve/sieve-address-parts.c
+++ b/src/lib-sieve/sieve-address-parts.c
@@ -23,12 +23,12 @@
  */
  
 static void opr_address_part_emit
-	(struct sieve_binary *sbin, unsigned int code);
+	(struct sieve_binary *sbin, struct sieve_address_part *addrp);
 static void opr_address_part_emit_ext
-	(struct sieve_binary *sbin, int ext_id);
+	(struct sieve_binary *sbin, struct sieve_address_part *addrp, int ext_id);
 
 /* 
- * Comparator 'extension' 
+ * Address-part 'extension' 
  */
 
 static int ext_my_id = -1;
@@ -42,7 +42,7 @@ const struct sieve_extension address_part_extension = {
 	addrp_extension_load,
 	addrp_validator_load,
 	NULL,
-	NULL, //addrp_interpreter_load,
+	addrp_interpreter_load,
 	NULL,
 	NULL
 };
@@ -55,11 +55,16 @@ static bool addrp_extension_load(int ext_id)
 
 /* 
  * Validator context:
- *   name-based comparator registry. 
+ *   name-based address-part registry. 
  */
  
+struct addrp_validator_registration {
+	int ext_id;
+	const struct sieve_address_part *address_part;
+};
+ 
 struct addrp_validator_context {
-	struct hash_table *address_parts;
+	struct hash_table *registrations;
 };
 
 static inline struct addrp_validator_context *
@@ -68,25 +73,45 @@ static inline struct addrp_validator_context *
 	return (struct addrp_validator_context *) 
 		sieve_validator_extension_get_context(validator, ext_my_id);
 }
+
+static void _sieve_address_part_register
+	(pool_t pool, struct addrp_validator_context *ctx, 
+	const struct sieve_address_part *addrp, int ext_id) 
+{
+	struct addrp_validator_registration *reg;
+	
+	reg = p_new(pool, struct addrp_validator_registration, 1);
+	reg->address_part = addrp;
+	reg->ext_id = ext_id;
+	
+	hash_insert(ctx->registrations, (void *) addrp->identifier, (void *) reg);
+}
  
 void sieve_address_part_register
-	(struct sieve_validator *validator, const struct sieve_address_part *addrp) 
+	(struct sieve_validator *validator, 
+	const struct sieve_address_part *addrp, int ext_id) 
 {
+	pool_t pool = sieve_validator_pool(validator);
 	struct addrp_validator_context *ctx = get_validator_context(validator);
-	
-	hash_insert(ctx->address_parts, (void *) addrp->identifier, (void *) addrp);
+
+	_sieve_address_part_register(pool, ctx, addrp, ext_id);
 }
 
 const struct sieve_address_part *sieve_address_part_find
-		(struct sieve_validator *validator, const char *addrp_name) 
+	(struct sieve_validator *validator, const char *identifier,
+		int *ext_id) 
 {
 	struct addrp_validator_context *ctx = get_validator_context(validator);
+	struct addrp_validator_registration *reg =
+		(struct addrp_validator_registration *) 
+			hash_lookup(ctx->registrations, identifier);
 
-  return 	(const struct sieve_address_part *) 
-  	hash_lookup(ctx->address_parts, addrp_name);
+	if ( ext_id != NULL ) *ext_id = reg->ext_id;
+
+  return reg->address_part;
 }
 
-static bool addrp_validator_load(struct sieve_validator *validator)
+bool addrp_validator_load(struct sieve_validator *validator)
 {
 	unsigned int i;
 	pool_t pool = sieve_validator_pool(validator);
@@ -94,15 +119,15 @@ static bool addrp_validator_load(struct sieve_validator *validator)
 	struct addrp_validator_context *ctx = 
 		p_new(pool, struct addrp_validator_context, 1);
 	
-	/* Setup comparator registry */
-	ctx->address_parts = hash_create
+	/* Setup address-part registry */
+	ctx->registrations = hash_create
 		(pool, pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
 
-	/* Register core comparators */
+	/* Register core address-parts */
 	for ( i = 0; i < sieve_core_address_parts_count; i++ ) {
 		const struct sieve_address_part *addrp = sieve_core_address_parts[i];
 		
-		hash_insert(ctx->address_parts, (void *) addrp->identifier, (void *) addrp);
+		_sieve_address_part_register(pool, ctx, addrp, -1);
 	}
 
 	sieve_validator_extension_set_context(validator, ext_my_id, ctx);
@@ -112,20 +137,67 @@ static bool addrp_validator_load(struct sieve_validator *validator)
 
 void sieve_address_parts_link_tags
 	(struct sieve_validator *validator, 
-		struct sieve_command_registration *cmd_reg,
-		unsigned int id_code) 
+		struct sieve_command_registration *cmd_reg, unsigned int id_code) 
+{	
+	sieve_validator_register_tag
+		(validator, cmd_reg, &address_part_tag, id_code); 	
+}
+
+/*
+ * Interpreter context:
+ */
+
+struct addrp_interpreter_context {
+	ARRAY_DEFINE(addrp_extensions, 
+		const struct sieve_address_part_extension *); 
+};
+
+static inline struct addrp_interpreter_context *
+	get_interpreter_context(struct sieve_interpreter *interpreter)
 {
-	struct addrp_validator_context *ctx = get_validator_context(validator);
-	struct hash_iterate_context *itx = hash_iterate_init(ctx->address_parts);
-	void *key; 
-	void *addrp;
+	return (struct addrp_interpreter_context *) 
+		sieve_interpreter_extension_get_context(interpreter, ext_my_id);
+}
+
+const struct sieve_address_part_extension *sieve_address_part_extension_get
+	(struct sieve_interpreter *interpreter, int ext_id)
+{
+	struct addrp_interpreter_context *ctx = get_interpreter_context(interpreter);
 	
-	while ( hash_iterate(itx, &key, &addrp) ) {
-		sieve_validator_register_tag
-			(validator, cmd_reg, ((struct sieve_address_part *) addrp)->tag, id_code); 	
+	if ( (ctx != NULL) && (ext_id > 0) && (ext_id < (int) array_count(&ctx->addrp_extensions)) ) {
+		const struct sieve_address_part_extension * const *ext;
+
+		ext = array_idx(&ctx->addrp_extensions, (unsigned int) ext_id);
+
+		return *ext;
 	}
+	
+	return NULL;
+}
+
+void sieve_address_part_extension_set
+	(struct sieve_interpreter *interpreter, int ext_id,
+		const struct sieve_address_part_extension *ext)
+{
+	struct addrp_interpreter_context *ctx = get_interpreter_context(interpreter);
+
+	array_idx_set(&ctx->addrp_extensions, (unsigned int) ext_id, &ext);
+}
+
+
+static bool addrp_interpreter_load(struct sieve_interpreter *interpreter)
+{
+	pool_t pool = sieve_interpreter_pool(interpreter);
+	
+	struct addrp_interpreter_context *ctx = 
+		p_new(pool, struct addrp_interpreter_context, 1);
+	
+	/* Setup comparator registry */
+	p_array_init(&ctx->addrp_extensions, pool, 4);
 
-	hash_iterate_deinit(&itx); 	
+	sieve_interpreter_extension_set_context(interpreter, ext_my_id, ctx);
+	
+	return TRUE;
 }
 
 /*
@@ -140,12 +212,19 @@ struct sieve_operand address_part_operand =
 /* 
  * Address-part tag 
  */
+  
+static bool tag_address_part_is_instance_of
+	(struct sieve_validator *validator, const char *tag)
+{
+	return sieve_address_part_find(validator, tag, NULL) != NULL;
+}
  
 static bool tag_address_part_validate
-	(struct sieve_validator *validator ATTR_UNUSED, 
+	(struct sieve_validator *validator, 
 	struct sieve_ast_argument **arg, 
-	struct sieve_command_context *cmd ATTR_UNUSED)
+	struct sieve_command_context *cmd)
 {
+	int ext_id;
 	const struct sieve_address_part *addrp;
 
 	/* Syntax:   
@@ -153,7 +232,8 @@ static bool tag_address_part_validate
    */
 	
 	/* Get address_part from registry */
-	addrp = sieve_address_part_find(validator, sieve_ast_argument_tag(*arg));
+	addrp = sieve_address_part_find
+		(validator, sieve_ast_argument_tag(*arg), &ext_id);
 	
 	if ( addrp == NULL ) {
 		sieve_command_validate_error(validator, cmd, 
@@ -164,8 +244,9 @@ static bool tag_address_part_validate
 		return FALSE;
 	}
 
-	/* Store comparator in context */
+	/* Store address-part in context */
 	(*arg)->context = (void *) addrp;
+	(*arg)->ext_id = ext_id;
 	
 	/* Skip tag */
 	*arg = sieve_ast_argument_next(*arg);
@@ -176,24 +257,26 @@ static bool tag_address_part_validate
 /* Code generation */
 
 static void opr_address_part_emit
-	(struct sieve_binary *sbin, unsigned int code)
+	(struct sieve_binary *sbin, struct sieve_address_part *addrp)
 { 
 	(void) sieve_operand_emit_code(sbin, SIEVE_OPERAND_ADDRESS_PART);
-	(void) sieve_binary_emit_byte(sbin, code);
+	(void) sieve_binary_emit_byte(sbin, addrp->code);
 }
 
 static void opr_address_part_emit_ext
-	(struct sieve_binary *sbin, int ext_id)
+	(struct sieve_binary *sbin, struct sieve_address_part *addrp, int ext_id)
 { 
-	unsigned char cmp_code = SIEVE_ADDRESS_PART_CUSTOM + 
+	unsigned char addrp_code = SIEVE_ADDRESS_PART_CUSTOM + 
 		sieve_binary_extension_get_index(sbin, ext_id);
 	
 	(void) sieve_operand_emit_code(sbin, SIEVE_OPERAND_ADDRESS_PART);	
-	(void) sieve_binary_emit_byte(sbin, cmp_code);
+	(void) sieve_binary_emit_byte(sbin, addrp_code);
+	(void) sieve_binary_emit_byte(sbin, addrp->ext_code);
 }
 
 const struct sieve_address_part *sieve_opr_address_part_read
-  (struct sieve_binary *sbin, sieve_size_t *address)
+  (struct sieve_interpreter *interpreter, 
+		struct sieve_binary *sbin, sieve_size_t *address)
 {
 	unsigned int addrp_code;
 	const struct sieve_operand *operand = sieve_operand_read(sbin, address);
@@ -208,25 +291,39 @@ const struct sieve_address_part *sieve_opr_address_part_read
 			else
 				return NULL;
 		} else {
-		  /*const struct sieve_extension *ext = 
-		  	sieve_binary_get_extension(sbin, cmp_code - SIEVE_ADDRESS_PART_CUSTOM);
-		  
-		  if ( ext != NULL )
-		  	return (const struct sieve_address_part *) 
-		  		sieve_interpreter_registry_get(addrp_registry, ext);	
-		  else*/
-		  	return NULL;
+			int ext_id = -1;
+			const struct sieve_address_part_extension *ap_ext;
+
+			if ( sieve_binary_extension_get_by_index(sbin,
+				addrp_code - SIEVE_ADDRESS_PART_CUSTOM, &ext_id) == NULL )
+				return NULL; 
+
+			ap_ext = sieve_address_part_extension_get(interpreter, ext_id); 
+ 
+			if ( ap_ext != NULL ) {  	
+				unsigned int code;
+				if ( ap_ext->address_part != NULL )
+					return ap_ext->address_part;
+		  	
+				if ( sieve_binary_read_byte(sbin, address, &code) &&
+					ap_ext->get_part != NULL )
+				return ap_ext->get_part(code);
+			} else {
+				i_info("Unknown address-part modifier %d.", addrp_code); 
+			}
 		}
 	}		
 		
-	return NULL;
+	return NULL; 
 }
 
-bool sieve_opr_address_part_dump(struct sieve_binary *sbin, sieve_size_t *address)
+bool sieve_opr_address_part_dump
+	(struct sieve_interpreter *interpreter,
+		struct sieve_binary *sbin, sieve_size_t *address)
 {
 	sieve_size_t pc = *address;
 	const struct sieve_address_part *addrp = 
-		sieve_opr_address_part_read(sbin, address);
+		sieve_opr_address_part_read(interpreter, sbin, address);
 	
 	if ( addrp == NULL )
 		return FALSE;
@@ -241,16 +338,16 @@ static bool tag_address_part_generate
 	struct sieve_command_context *cmd ATTR_UNUSED)
 {
 	struct sieve_binary *sbin = sieve_generator_get_binary(generator);
-	struct sieve_address_part *addrp = 
+	struct sieve_address_part *addrp =
 		(struct sieve_address_part *) (*arg)->context;
 	
 	if ( addrp->extension == NULL ) {
 		if ( addrp->code < SIEVE_ADDRESS_PART_CUSTOM )
-			opr_address_part_emit(sbin, addrp->code);
+			opr_address_part_emit(sbin, addrp);
 		else
 			return FALSE;
 	} else {
-		//opr_comparator_emit_ext(sbin, cmp->extension);
+		opr_address_part_emit_ext(sbin, addrp, (*arg)->ext_id);
 	} 
 		
 	*arg = sieve_ast_argument_next(*arg);
@@ -263,8 +360,9 @@ static bool tag_address_part_generate
  */
  
 bool sieve_address_stringlist_match
-	(struct sieve_address_part *addrp, struct sieve_coded_stringlist *key_list,
-		struct sieve_comparator *cmp,	const char *data)
+	(const struct sieve_address_part *addrp, 
+		struct sieve_coded_stringlist *key_list,
+		const struct sieve_comparator *cmp,	const char *data)
 {
 	bool matched = FALSE;
 	const struct message_address *addr;
@@ -281,7 +379,7 @@ bool sieve_address_stringlist_match
 			const char *part;
 			
 			i_assert(addr->mailbox != NULL);
-			
+
 			part = addrp->extract_from(addr);
 			
 			if ( sieve_stringlist_match(key_list, part, cmp) )
@@ -299,14 +397,14 @@ bool sieve_address_stringlist_match
 /* 
  * Core address-part modifiers
  */
- 
-const struct sieve_argument address_localpart_tag = 
-	{ "localpart", tag_address_part_validate, tag_address_part_generate };
-const struct sieve_argument address_domain_tag = 
-	{ "domain", tag_address_part_validate, tag_address_part_generate };
-const struct sieve_argument address_all_tag = 
-	{ "all", tag_address_part_validate, tag_address_part_generate };
 
+const struct sieve_argument address_part_tag = { 
+	NULL,
+	tag_address_part_is_instance_of, 
+	tag_address_part_validate, 
+	tag_address_part_generate 
+};
+ 
 static const char *addrp_all_extract_from
 	(const struct message_address *address)
 {
@@ -327,25 +425,25 @@ static const char *addrp_localpart_extract_from
 
 const struct sieve_address_part all_address_part = {
 	"all",
-	&address_all_tag,
 	SIEVE_ADDRESS_PART_ALL,
 	NULL,
+	0,
 	addrp_all_extract_from
 };
 
 const struct sieve_address_part local_address_part = {
 	"localpart",
-	&address_localpart_tag,
 	SIEVE_ADDRESS_PART_LOCAL,
 	NULL,
+	0,
 	addrp_localpart_extract_from
 };
 
 const struct sieve_address_part domain_address_part = {
 	"domain",
-	&address_domain_tag,
 	SIEVE_ADDRESS_PART_DOMAIN,
 	NULL,
+	0,
 	addrp_domain_extract_from
 };
 
@@ -353,6 +451,7 @@ const struct sieve_address_part *sieve_core_address_parts[] = {
 	&all_address_part, &local_address_part, &domain_address_part
 };
 
-const unsigned int sieve_core_address_parts_count = N_ELEMENTS(sieve_core_address_parts);
+const unsigned int sieve_core_address_parts_count = 
+	N_ELEMENTS(sieve_core_address_parts);
 
 
diff --git a/src/lib-sieve/sieve-address-parts.h b/src/lib-sieve/sieve-address-parts.h
index ebc560e0c..a45c3620a 100644
--- a/src/lib-sieve/sieve-address-parts.h
+++ b/src/lib-sieve/sieve-address-parts.h
@@ -14,38 +14,58 @@ enum sieve_address_part_code {
 
 struct sieve_address_part {
 	const char *identifier;
-	const struct sieve_argument *tag;
 	
 	enum sieve_address_part_code code;
+	
 	const struct sieve_extension *extension;
+	unsigned int ext_code;
 
 	const char *(*extract_from)(const struct message_address *address);
 };
 
+struct sieve_address_part_extension {
+	/* Either a single addr-part in this extension ... */
+	const struct sieve_address_part *address_part;
+	
+	/* ... or multiple: then the extension must handle emit/read */
+	const struct sieve_address_part *(*get_part)
+		(unsigned int code);
+};
+
 void sieve_address_parts_link_tags
 	(struct sieve_validator *validator, 
 		struct sieve_command_registration *cmd_reg,
 		unsigned int id_code);
 		
 void sieve_address_part_register
-	(struct sieve_validator *validator, const struct sieve_address_part *addrp); 
+	(struct sieve_validator *validator, 
+	const struct sieve_address_part *addrp, int ext_id);
 const struct sieve_address_part *sieve_address_part_find
-		(struct sieve_validator *validator, const char *addrp_name);
+	(struct sieve_validator *validator, const char *identifier,
+		int *ext_id);
+void sieve_address_part_extension_set
+	(struct sieve_interpreter *interpreter, int ext_id,
+		const struct sieve_address_part_extension *ext);
+
+extern const struct sieve_argument address_part_tag;
 
-const struct sieve_address_part all_address_part;
-const struct sieve_address_part local_address_part;
-const struct sieve_address_part domain_address_part;
+extern const struct sieve_address_part all_address_part;
+extern const struct sieve_address_part local_address_part;
+extern const struct sieve_address_part domain_address_part;
 
 extern const struct sieve_address_part *sieve_core_address_parts[];
 extern const unsigned int sieve_core_address_parts_count;
 
 const struct sieve_address_part *sieve_opr_address_part_read
-  (struct sieve_binary *sbin, sieve_size_t *address);
+  (struct sieve_interpreter *interpreter, 
+  	struct sieve_binary *sbin, sieve_size_t *address);
 bool sieve_opr_address_part_dump
-	(struct sieve_binary *sbin, sieve_size_t *address);
+	(struct sieve_interpreter *interpreter,
+		struct sieve_binary *sbin, sieve_size_t *address);
 
 bool sieve_address_stringlist_match
-	(struct sieve_address_part *addrp, struct sieve_coded_stringlist *key_list,
-		struct sieve_comparator *cmp, const char *data);
+	(const struct sieve_address_part *addrp, 
+		struct sieve_coded_stringlist *key_list,
+		const struct sieve_comparator *cmp, const char *data);
 
 #endif /* __SIEVE_ADDRESS_PARTS_H */
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index 525bd293e..bfc60ec3e 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -87,6 +87,9 @@ struct sieve_ast_argument {
 
 	/* Context data associated with this ast element */
 	void *context;
+	
+	/* Indicates whether this argument is part of which extension */
+	int ext_id;
 };
 
 struct sieve_ast_list {
diff --git a/src/lib-sieve/sieve-binary.c b/src/lib-sieve/sieve-binary.c
index 552edc092..febbc4891 100644
--- a/src/lib-sieve/sieve-binary.c
+++ b/src/lib-sieve/sieve-binary.c
@@ -123,6 +123,12 @@ int sieve_binary_extension_get_index
 	return -1;
 }
 
+int sieve_binary_extensions_count(struct sieve_binary *sbin) 
+{
+	return (int) array_count(&sbin->extensions);
+}
+
+
 /*
  * Emission functions
  */
diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h
index fcea7fe2b..49440d1ce 100644
--- a/src/lib-sieve/sieve-binary.h
+++ b/src/lib-sieve/sieve-binary.h
@@ -24,6 +24,7 @@ const struct sieve_extension *sieve_binary_extension_get_by_index
 	(struct sieve_binary *sbin, int index, int *ext_id);
 int sieve_binary_extension_get_index
 	(struct sieve_binary *sbin, int ext_id);
+int sieve_binary_extensions_count(struct sieve_binary *sbin);
 	
 /* 
  * Code emission 
diff --git a/src/lib-sieve/sieve-commands.c b/src/lib-sieve/sieve-commands.c
index 44b6772c5..aea83eeeb 100644
--- a/src/lib-sieve/sieve-commands.c
+++ b/src/lib-sieve/sieve-commands.c
@@ -19,11 +19,11 @@ static bool arg_string_list_generate(struct sieve_generator *generator, struct s
 	struct sieve_command_context *context);
 
 const struct sieve_argument number_argument =
-	{ "@number", NULL, arg_number_generate };
+	{ "@number", NULL, NULL, arg_number_generate };
 const struct sieve_argument string_argument =
-	{ "@string", NULL, arg_string_generate };
+	{ "@string", NULL, NULL, arg_string_generate };
 const struct sieve_argument string_list_argument =
-	{ "@string-list", NULL, arg_string_list_generate };	
+	{ "@string-list", NULL, NULL, arg_string_list_generate };	
 
 static bool arg_number_generate(struct sieve_generator *generator, struct sieve_ast_argument **arg, 
 	struct sieve_command_context *context ATTR_UNUSED)
diff --git a/src/lib-sieve/sieve-commands.h b/src/lib-sieve/sieve-commands.h
index 128289ba4..0c5111997 100644
--- a/src/lib-sieve/sieve-commands.h
+++ b/src/lib-sieve/sieve-commands.h
@@ -13,8 +13,10 @@
 struct sieve_argument {
 	const char *identifier;
 	
+	bool (*is_instance_of)(struct sieve_validator *validator, const char *tag);
 	bool (*validate)(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
 		struct sieve_command_context *context);
+		
 	bool (*generate)(struct sieve_generator *generator, struct sieve_ast_argument **arg, 
 		struct sieve_command_context *context);
 };
diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c
index 7cd416a76..89f40b932 100644
--- a/src/lib-sieve/sieve-comparators.c
+++ b/src/lib-sieve/sieve-comparators.c
@@ -151,7 +151,7 @@ static bool cmp_interpreter_load(struct sieve_interpreter *interpreter)
 		p_new(pool, struct cmp_interpreter_context, 1);
 	
 	/* Setup comparator registry */
-	p_array_init(&ctx->cmp_extensions, default_pool, 4);
+	p_array_init(&ctx->cmp_extensions, pool, 4);
 
 	sieve_interpreter_extension_set_context(interpreter, ext_my_id, ctx);
 	
@@ -177,7 +177,7 @@ static bool tag_comparator_generate
 	struct sieve_command_context *cmd);
 
 const struct sieve_argument comparator_tag = 
-	{ "comparator", tag_comparator_validate, tag_comparator_generate };
+	{ "comparator", NULL, tag_comparator_validate, tag_comparator_generate };
 
 static bool tag_comparator_validate
 	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index 438d02b54..935ceda12 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -24,10 +24,14 @@ extern const struct sieve_extension envelope_extension;
 /* Plugins (FIXME: make this dynamic) */
 
 extern const struct sieve_extension vacation_extension;
+extern const struct sieve_extension subaddress_extension;
 
 const struct sieve_extension *sieve_core_extensions[] = {
 	&comparator_extension, &address_part_extension, 
-	&fileinto_extension, &reject_extension, &envelope_extension, &vacation_extension
+	&fileinto_extension, &reject_extension, &envelope_extension, 
+	
+	/* 'Plugins' */
+	&vacation_extension, &subaddress_extension
 };
 
 const unsigned int sieve_core_extensions_count =
@@ -78,7 +82,8 @@ static struct hash_table *extension_index;
 static void sieve_extensions_init_registry(void)
 {	
 	p_array_init(&extensions, default_pool, 4);
-	extension_index = hash_create(default_pool, default_pool, 0, NULL, NULL);
+	extension_index = hash_create
+		(default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
 }
 
 int sieve_extension_register(const struct sieve_extension *extension) 
diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h
index 0f1d27fe2..9334747ed 100644
--- a/src/lib-sieve/sieve-extensions.h
+++ b/src/lib-sieve/sieve-extensions.h
@@ -9,7 +9,7 @@ struct sieve_extension {
 	
 	bool (*load)(int ext_id);
 	
-	bool (*validator_load)(struct sieve_validator *validator);
+	bool (*validator_load)(struct sieve_validator *validator);	
 	bool (*generator_load)(struct sieve_generator *generator);
 	bool (*interpreter_load)(struct sieve_interpreter *interpreter);
 
diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index e323bc14f..68035454f 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -22,7 +22,7 @@ struct sieve_interpreter {
 	struct sieve_binary *binary;
 		
 	/* Object registries */
-	ARRAY_DEFINE(ext_contexts, void); 
+	ARRAY_DEFINE(ext_contexts, void *); 
 		
 	/* Execution status */
 	sieve_size_t pc; 
@@ -33,8 +33,12 @@ struct sieve_interpreter {
 	struct mail *mail;	
 };
 
+extern struct sieve_extension comparator_extension;
+extern struct sieve_extension address_part_extension;
+
 struct sieve_interpreter *sieve_interpreter_create(struct sieve_binary *binary) 
 {
+	int i;
 	pool_t pool;
 	struct sieve_interpreter *interp;
 	
@@ -48,8 +52,20 @@ struct sieve_interpreter *sieve_interpreter_create(struct sieve_binary *binary)
 	
 	interp->pc = 0;
 
-	array_create(&interp->ext_contexts, pool, sizeof(void *), 
-		sieve_extensions_get_count());
+	p_array_init(&interp->ext_contexts, pool, 4);
+	//array_create(&interp->ext_contexts, pool, sizeof(void *), 
+	//		sieve_extensions_get_count());
+	
+	(void)comparator_extension.interpreter_load(interp);	
+	(void)address_part_extension.interpreter_load(interp);	
+
+	for ( i = 0; i < sieve_binary_extensions_count(binary); i++ ) {
+		const struct sieve_extension *ext = 
+			sieve_binary_extension_get_by_index(binary, i, NULL);
+		
+		if ( ext->interpreter_load != NULL )
+			ext->interpreter_load(interp);
+	}
 	
 	return interp;
 }
@@ -71,16 +87,20 @@ inline pool_t sieve_interpreter_pool(struct sieve_interpreter *interp)
 inline void sieve_interpreter_extension_set_context
 	(struct sieve_interpreter *interpreter, int ext_id, void *context)
 {
-	array_idx_set(&interpreter->ext_contexts, (unsigned int) ext_id, context);	
+	array_idx_set(&interpreter->ext_contexts, (unsigned int) ext_id, &context);	
 }
 
 inline const void *sieve_interpreter_extension_get_context
 	(struct sieve_interpreter *interpreter, int ext_id) 
 {
-	if  ( ext_id < 0 || ext_id > (int) array_count(&interpreter->ext_contexts) )
+	void * const *ctx;
+
+	if  ( ext_id < 0 || ext_id >= (int) array_count(&interpreter->ext_contexts) )
 		return NULL;
 	
-	return array_idx(&interpreter->ext_contexts, (unsigned int) ext_id);		
+	ctx = array_idx(&interpreter->ext_contexts, (unsigned int) ext_id);		
+
+	return *ctx;
 }
 
 
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index e870f4c24..cada9ae85 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -82,7 +82,6 @@ struct sieve_validator *sieve_validator_create(struct sieve_ast *ast, struct sie
 	sieve_validator_register_core_commands(validator);
 	sieve_validator_register_core_tests(validator);
 
-		
 	return validator;
 }
 
@@ -101,10 +100,13 @@ inline pool_t sieve_validator_pool(struct sieve_validator *validator)
 
 /* Command registry */
 
+struct sieve_tag_registration;
+
 struct sieve_command_registration {
 	const struct sieve_command *command;
 	
 	struct hash_table *tags;
+	ARRAY_DEFINE(instanced_tags, struct sieve_tag_registration *); 
 };
 
 /* Dummy function */
@@ -116,10 +118,10 @@ static bool _cmd_unknown_validate
 	return FALSE;
 }
 
-static const struct sieve_command unknown_command = { "", SCT_COMMAND, NULL, _cmd_unknown_validate, NULL, NULL };
-static const struct sieve_command_registration unknown_command_registration = { &unknown_command, NULL };
-static const struct sieve_command unknown_test = { "", SCT_TEST, NULL, _cmd_unknown_validate, NULL, NULL };
-static const struct sieve_command_registration unknown_test_registration = { &unknown_test, NULL };
+static const struct sieve_command unknown_command = 
+	{ "", SCT_COMMAND, NULL, _cmd_unknown_validate, NULL, NULL };
+static const struct sieve_command unknown_test = 
+	{ "", SCT_TEST, NULL, _cmd_unknown_validate, NULL, NULL };
 
 static void sieve_validator_register_core_tests(struct sieve_validator *validator) 
 {
@@ -139,26 +141,37 @@ static void sieve_validator_register_core_commands(struct sieve_validator *valid
 	}
 }
 
-void sieve_validator_register_command(struct sieve_validator *validator, const struct sieve_command *command) 
+static void _sieve_validator_register_command
+	(struct sieve_validator *validator, const struct sieve_command *command,
+	const char *identifier) 
 {
-	struct sieve_command_registration *record = p_new(validator->pool, struct sieve_command_registration, 1);
+	struct sieve_command_registration *record = 
+		p_new(validator->pool, struct sieve_command_registration, 1);
 	record->command = command;
 	record->tags = NULL;
-	hash_insert(validator->commands, (void *) command->identifier, (void *) record);
+	hash_insert(validator->commands, (void *) identifier, (void *) record);
 	
 	if ( command->registered != NULL ) {
 		command->registered(validator, record);
 	}
 }
 
-static void sieve_validator_register_unknown_command(struct sieve_validator *validator, const char *command) 
-{		
-	hash_insert(validator->commands, (void *) command, (void *) &unknown_command_registration);
+void sieve_validator_register_command
+	(struct sieve_validator *validator, const struct sieve_command *command) 
+{
+	_sieve_validator_register_command(validator, command, command->identifier);
+}
+
+static void sieve_validator_register_unknown_command
+	(struct sieve_validator *validator, const char *command) 
+{
+	_sieve_validator_register_command(validator, &unknown_command, command);		
 }
 
-static void sieve_validator_register_unknown_test(struct sieve_validator *validator, const char *test) 
+static void sieve_validator_register_unknown_test
+	(struct sieve_validator *validator, const char *test) 
 {		
-	hash_insert(validator->commands, (void *) test, (void *) &unknown_test_registration);
+	_sieve_validator_register_command(validator, &unknown_test, test);		
 }
 
 static struct sieve_command_registration *sieve_validator_find_command_registration
@@ -193,7 +206,8 @@ static bool _unknown_tag_validate
 	return FALSE;
 }
 
-static const struct sieve_argument _unknown_tag = { "", _unknown_tag_validate, NULL };
+static const struct sieve_argument _unknown_tag = 
+	{ "", NULL, _unknown_tag_validate, NULL };
 
 static void _sieve_validator_register_tag
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
@@ -217,7 +231,19 @@ void sieve_validator_register_tag
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
 	const struct sieve_argument *tag, unsigned int id_code) 
 {
-	_sieve_validator_register_tag(validator, cmd_reg, tag, tag->identifier, id_code);
+	if ( tag->is_instance_of == NULL )
+		_sieve_validator_register_tag(validator, cmd_reg, tag, tag->identifier, id_code);
+	else {
+		struct sieve_tag_registration *reg = 
+			p_new(validator->pool, struct sieve_tag_registration, 1);
+		reg->tag = tag;
+		reg->id_code = id_code;
+
+		if ( !array_is_created(&cmd_reg->instanced_tags) ) 
+				p_array_init(&cmd_reg->instanced_tags, validator->pool, 1);
+				
+		array_append(&cmd_reg->instanced_tags, &reg, 1);
+	}
 }
 
 static void sieve_validator_register_unknown_tag
@@ -228,21 +254,39 @@ static void sieve_validator_register_unknown_tag
 }
 
 static const struct sieve_argument *sieve_validator_find_tag
-	(struct sieve_command_registration *cmd_reg, const char *tag, unsigned int *id_code) 
+	(struct sieve_validator *validator, 
+		struct sieve_command_registration *cmd_reg, 
+		const char *tag, unsigned int *id_code) 
 {
+	unsigned int i;
 	const struct sieve_tag_registration *reg;
 	
 	*id_code = 0;
 	
-	if ( cmd_reg->tags == NULL ) return NULL;
-	
-	reg = (const struct sieve_tag_registration *) hash_lookup(cmd_reg->tags, tag);
-	
-	if ( reg == NULL )return NULL;
+	if ( cmd_reg->tags != NULL ) {
+		reg = (const struct sieve_tag_registration *) 
+			hash_lookup(cmd_reg->tags, tag);
 	
-	*id_code = reg->id_code;
+		if ( reg != NULL ) {
+			*id_code = reg->id_code;
+  		return reg->tag; 
+  	}
+  }
+  
+  /* Not found so far, try the instanced tags */
+  if ( array_is_created(&cmd_reg->instanced_tags) ) {
+	  for ( i = 0; i < array_count(&cmd_reg->instanced_tags); i++ ) {
+	  	struct sieve_tag_registration * const *reg = 
+	  		array_idx(&cmd_reg->instanced_tags, i);
+  	
+	  	if ( (*reg)->tag != NULL && (*reg)->tag->is_instance_of(validator, tag) ) {
+	  		*id_code = (*reg)->id_code;
+	  		return (*reg)->tag;
+	  	}
+	  }
+	}
 	
-  return reg->tag; 
+	return NULL;
 }
 
 /* Extension support */
@@ -301,11 +345,11 @@ static bool sieve_validate_match_type_tag
 }
 
 static const struct sieve_argument match_is_tag = 
-	{ "is", sieve_validate_match_type_tag, NULL };
+	{ "is", NULL, sieve_validate_match_type_tag, NULL };
 static const struct sieve_argument match_contains_tag = 
-	{ "contains", sieve_validate_match_type_tag, NULL };
+	{ "contains", NULL, sieve_validate_match_type_tag, NULL };
 static const struct sieve_argument match_matches_tag = 
-	{ "matches", sieve_validate_match_type_tag, NULL };
+	{ "matches", NULL, sieve_validate_match_type_tag, NULL };
 
 void sieve_validator_link_match_type_tags
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg,
@@ -338,13 +382,15 @@ bool sieve_validate_command_arguments
 		
 		if ( cmd_reg == NULL ) {
 			sieve_command_validate_error(
-					validator, cmd, 
-					"!!BUG!!: the '%s' %s seemed to be known before, but somehow its registration got lost",
-					cmd->command->identifier, sieve_command_type_name(cmd->command)
-				);
-			i_error("BUG: the '%s' %s seemed to be known before, but somehow its registration got lost",
-					cmd->command->identifier, sieve_command_type_name(cmd->command)
-				);
+				validator, cmd, 
+				"!!BUG!!: the '%s' %s seemed to be known before, "
+				"but somehow its registration got lost",
+				cmd->command->identifier, sieve_command_type_name(cmd->command)
+			);
+			i_error("BUG: the '%s' %s seemed to be known before, "
+				"but somehow its registration got lost",
+				cmd->command->identifier, sieve_command_type_name(cmd->command)
+			);
 			return FALSE; 
 		}
 	}
@@ -353,18 +399,22 @@ bool sieve_validate_command_arguments
 	while ( sieve_ast_argument_type(arg) == SAAT_TAG ) {
 		unsigned int id_code;
 		const struct sieve_argument *tag = 
-			sieve_validator_find_tag(cmd_reg, sieve_ast_argument_tag(arg), &id_code);
+			sieve_validator_find_tag
+				(validator, cmd_reg, sieve_ast_argument_tag(arg), &id_code);
 		
 		if ( tag == NULL ) {
 			sieve_command_validate_error(validator, cmd, 
-				"unknown tagged argument ':%s' for the %s %s (reported only once at first occurence)",
-				sieve_ast_argument_tag(arg), cmd->command->identifier, sieve_command_type_name(cmd->command));
-			sieve_validator_register_unknown_tag(validator, cmd_reg, sieve_ast_argument_tag(arg));
-			return FALSE;				
+				"unknown tagged argument ':%s' for the %s %s "
+				"(reported only once at first occurence)",
+				sieve_ast_argument_tag(arg), cmd->command->identifier, 
+				sieve_command_type_name(cmd->command));
+			sieve_validator_register_unknown_tag
+				(validator, cmd_reg, sieve_ast_argument_tag(arg));
+			return FALSE;					
 		}
 		
 		/* Check whether previously tagged as unknown */
-		if ( *(tag->identifier) == '\0' ) 
+		if ( tag->identifier != NULL && *(tag->identifier) == '\0' ) 
 			return FALSE;
 		
 		/* Assign the tagged argument type to the ast for later reference (in generator) */
@@ -486,12 +536,16 @@ bool sieve_validate_command_block(struct sieve_validator *validator, struct siev
 	if ( block_required ) {
 		if ( !cmd->ast_node->block ) {
 			sieve_command_validate_error
-				( validator, cmd, "the %s command requires a command block, but it is missing", cmd->command->identifier );
+				( validator, cmd, 
+					"the %s command requires a command block, but it is missing", 
+					cmd->command->identifier );
 			return FALSE;
 		}
 	} else if ( !block_allowed && cmd->ast_node->block ) {
 		sieve_command_validate_error
-				( validator, cmd, "the %s command does not accept a command block, but one is specified anyway", cmd->command->identifier );
+				( validator, cmd, 
+					"the %s command does not accept a command block, but one is specified anyway", 
+					cmd->command->identifier );
 		return FALSE;
 	}
 	
@@ -518,7 +572,9 @@ static bool sieve_validate_test(struct sieve_validator *validator, struct sieve_
 		/* Identifier = "" when the command was previously marked as unknown */
 		if ( *(test->identifier) != '\0' ) {
 			if ( test->type != SCT_TEST ) {
-				sieve_validator_error(validator, tst_node, "attempted to use command '%s' as test", tst_node->identifier);
+				sieve_validator_error(
+					validator, tst_node, 
+					"attempted to use command '%s' as test", tst_node->identifier);
 			 	result = FALSE;
 			} else {
 				struct sieve_command_context *ctx = sieve_command_context_create(tst_node, test); 
@@ -540,7 +596,9 @@ static bool sieve_validate_test(struct sieve_validator *validator, struct sieve_
 			result = FALSE;
 			
 	} else {
-		sieve_validator_error(validator, tst_node, "unknown test '%s' (only reported once at first occurence)", tst_node->identifier);
+		sieve_validator_error(validator, tst_node, 
+			"unknown test '%s' (only reported once at first occurence)", 
+			tst_node->identifier);
 		sieve_validator_register_unknown_test(validator, tst_node->identifier);
 		
 		result = FALSE;
@@ -602,7 +660,10 @@ static bool sieve_validate_command(struct sieve_validator *validator, struct sie
 			result = FALSE;
 			
 	} else {
-		sieve_validator_error(validator, cmd_node, "unknown command '%s' (only reported once at first occurence)", cmd_node->identifier);
+		sieve_validator_error(
+			validator, cmd_node, 
+			"unknown command '%s' (only reported once at first occurence)", 
+			cmd_node->identifier);
 		sieve_validator_register_unknown_command(validator, cmd_node->identifier);
 		
 		result = FALSE;
diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c
index 5e828aece..bc0801655 100644
--- a/src/lib-sieve/tst-address.c
+++ b/src/lib-sieve/tst-address.c
@@ -113,7 +113,7 @@ static bool tst_address_opcode_dump
             case OPT_MATCH_TYPE:
                 break;
 			case OPT_ADDRESS_PART:
-				if ( !sieve_opr_address_part_dump(sbin, address) )
+				if ( !sieve_opr_address_part_dump(interp, sbin, address) )
 					return FALSE;
 				break;			
             default:
@@ -155,7 +155,7 @@ static bool tst_address_opcode_execute
             case OPT_MATCH_TYPE:
                 break;
 			case OPT_ADDRESS_PART:
-				if ( (addrp = sieve_opr_address_part_read(sbin, address)) == NULL )
+				if ( (addrp = sieve_opr_address_part_read(interp, sbin, address)) == NULL )
 					return FALSE;
 				break;
             default:
diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c
index 9c6650707..2ddca289c 100644
--- a/src/lib-sieve/tst-size.c
+++ b/src/lib-sieve/tst-size.c
@@ -77,9 +77,9 @@ static bool tst_size_validate_under_tag
 /* Test registration */
 
 static const struct sieve_argument size_over_tag = 
-	{ "over", tst_size_validate_over_tag, NULL };
+	{ "over", NULL, tst_size_validate_over_tag, NULL };
 static const struct sieve_argument size_under_tag = 
-	{ "under", tst_size_validate_under_tag, NULL };
+	{ "under", NULL, tst_size_validate_under_tag, NULL };
 
 bool tst_size_registered(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
 {
diff --git a/src/sieve-bin/Makefile.am b/src/sieve-bin/Makefile.am
index 398055a89..ebb5b7dde 100644
--- a/src/sieve-bin/Makefile.am
+++ b/src/sieve-bin/Makefile.am
@@ -13,8 +13,10 @@ AM_CPPFLAGS = \
 plugin_dir = \
 	$(top_srcdir)/src/lib-sieve/plugins
 
+# These are not actual plugins just yet...
 plugins = \
-	$(plugin_dir)/vacation/lib_ext_vacation.a
+	$(plugin_dir)/vacation/lib_ext_vacation.a \
+	$(plugin_dir)/subaddress/lib_ext_subaddress.a
 
 sievec_LDFLAGS = -export-dynamic -Wl,--start-group 
 sieve_test_LDFLAGS = -export-dynamic -Wl,--start-group
-- 
GitLab