diff --git a/TODO b/TODO
index 2e60742f86e64cd8a2771ea91e7ed9eb308aa227..84fb67717de831b669176ae4d040dfe00d5af777 100644
--- a/TODO
+++ b/TODO
@@ -3,10 +3,10 @@ Next (in order of descending priority/precedence):
 
 * Full standards compliance review for the engine and all fully implemented 
   sieve extensions. Issues discovered so far:
-    - Implementations SHOULD consider unknown envelope parts an error.
     - The null reverse-path is matched against as the empty string, regardless 
       of the ADDRESS-PART argument specified.
     - Header test does not strip trailing whitespace
+	- Fix/Report issues listed in 'doc/RFC Controversy.txt'
 * Code cleanup 
 * Full security review. Enforce limits on number of created objects, script 
   size, execution time, etc...
diff --git a/doc/rfc/RFC Controversy.txt b/doc/rfc/RFC Controversy.txt
index cbd91f723b34cdf61ed0cd7dcb3503e106c23b24..3ad9d520dd721b89d501443c58749305471869a4 100644
--- a/doc/rfc/RFC Controversy.txt	
+++ b/doc/rfc/RFC Controversy.txt	
@@ -16,7 +16,7 @@ these issues by my implementation is displayed inside [ ... ].
     headers simply ignored? [fatal validation error]
   - Given the variables extension, sometimes the header names aren't known until
     runtime. If previous answer was to cause a fatal error, will this abort the
-    script? [not checked (FIXME); will be: ignore header]
+    script? [not checked (FIXME); will first be: ignore header]
     
 * RFC 5228 (Sieve) : 5.4.  Test envelope
 	"The "envelope" test is true if the specified part of the [SMTP] (or
@@ -27,5 +27,5 @@ these issues by my implementation is displayed inside [ ... ].
    error."
    
 	- Are envelope parts required to be addresses? And if not, what becomes the 
-	  meaning of the ADDRESS-PART modifiers? [not handled (FIXME); will be: if 
-	  none specified full string match and test validation error otherwise]
+	  meaning of the ADDRESS-PART modifiers? [not handled (FIXME); will first be: 
+	  if none specified full string match and trigger validation error otherwise]
diff --git a/sieve/errors/envelope-errors.sieve b/sieve/errors/envelope-errors.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..e5b7b1020657981bd25da9f3479befe39a715b3b
--- /dev/null
+++ b/sieve/errors/envelope-errors.sieve
@@ -0,0 +1,13 @@
+require "envelope";
+
+if envelope :is "to" "frop@rename-it.nl" {
+}
+
+if envelope :is "frop" "frop@rename-it.nl" {
+}
+
+if envelope :is ["to","from"] "frop@rename-it.nl" {
+}
+
+if envelope :is ["to","frop"] "frop@rename-it.nl" {
+}
diff --git a/sieve/examples/elvey.sieve b/sieve/examples/elvey.sieve
index 92c30f4f6b501122216eab2b6b80a4d62e5d724e..869f1c90521f3b5bb83e8ae06503b0b7952f1029 100644
--- a/sieve/examples/elvey.sieve
+++ b/sieve/examples/elvey.sieve
@@ -1,5 +1,5 @@
 # Example Sieve Script
-#   Author: Matthew Elvey
+#   Author: Matthew Elvey (Slightly modified to remove syntax and context errors)
 #   URL: http://www.elvey.com/it/sieve/SieveScript.txt
 
 # Initial version completed and put in place 4/1/02 by Matthew Elvey  (firstname@lastname.com ; I've checked and it's not a valid address.); Copyright (C).and.current as of 5/19/2002 
@@ -87,7 +87,7 @@ keep;		#this one is important - don't want to miss any bounce messages!
 # LINE 106.
 
 
-} elsif anyof (envelope :all :is ["To", "CC", "BCC"] "Firstname.lastname@fastmail.fm",    #a couple people send to this, but I have have all their addrs in whitelist so OK.
+} elsif anyof (address :all :is ["To", "CC", "BCC"] "Firstname.lastname@fastmail.fm",    #a couple people send to this, but I have have all their addrs in whitelist so OK.
            header :matches "X-Spam-score"  ["9.?" , "10.?", "9", "10", "11.?", "12.?" ,"13.?", "14.?", "11", "12","13", "14", "15.?", "16.?", "17.?" ,"18.?", "19.?", "15", "16", "17" ,"18", "19", "2?.?", "2?", "3?.?" , "3?", "40"]) { 		 #"5.?", "6.?", "5", "6" "7.?" , "8.?" , "7", "8"
   reject text: 
   Hello.  The server content filter/spam detector I use has bounced your message. It appears to be spam. 
diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c
index fd1fd45b8c9f8754362eb62948d34dc8b078b08e..efaafeb11b63f3fa8bd9c5f8362823a23095d52a 100644
--- a/src/lib-sieve/ext-envelope.c
+++ b/src/lib-sieve/ext-envelope.c
@@ -132,10 +132,41 @@ static bool tst_envelope_registered
  * Validation 
  */
  
+static const char * const _supported_envelope_parts[] = {
+	/* Required */
+	"from", "to", 
+	
+	/* Non-standard */
+	"auth", 
+	
+	NULL  
+};
+
+static int _envelope_part_is_supported
+(void *context ATTR_UNUSED, struct sieve_ast_argument *arg)
+{
+	if ( sieve_argument_is_string_literal(arg) ) {
+		const char *epart = sieve_ast_strlist_strc(arg);
+
+		const char * const *epsp = _supported_envelope_parts;
+		while ( *epsp != NULL ) {
+			if ( strcasecmp( *epsp, epart ) == 0 ) 
+				return TRUE;
+
+			epsp++;
+		}
+		
+		return FALSE;
+	} 
+	
+	return TRUE;
+}
+
 static bool tst_envelope_validate
 (struct sieve_validator *validator, struct sieve_command_context *tst) 
 { 		
 	struct sieve_ast_argument *arg = tst->first_positional;
+	struct sieve_ast_argument *epart;
 				
 	if ( !sieve_validate_positional_argument
 		(validator, tst, arg, "envelope part", 1, SAAT_STRING_LIST) ) {
@@ -144,6 +175,14 @@ static bool tst_envelope_validate
 	
 	if ( !sieve_validator_argument_activate(validator, tst, arg, FALSE) )
 		return FALSE;
+		
+	epart = arg;
+	if ( !sieve_ast_stringlist_map(&epart, NULL, _envelope_part_is_supported) ) {		
+		sieve_command_validate_error(validator, tst, 
+			"specified envelope part '%s' is not supported by the envelope test", 
+				sieve_ast_strlist_strc(epart));
+		return FALSE;
+	}
 	
 	arg = sieve_ast_argument_next(arg);
 	
diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c
index cdb86f82feafd41135114e4704f1ccb5050e64d4..e1fb2850be8cda9e4148be5ca10b9d40f2c1aded 100644
--- a/src/lib-sieve/sieve-ast.c
+++ b/src/lib-sieve/sieve-ast.c
@@ -564,6 +564,39 @@ struct sieve_ast_node *sieve_ast_command_create
 	return command;
 }
 
+/*
+ * Utility
+ */
+
+int sieve_ast_stringlist_map
+(struct sieve_ast_argument **listitem, void *context,
+	int (*map_function)(void *context, struct sieve_ast_argument *arg))
+{
+	if ( sieve_ast_argument_type(*listitem) == SAAT_STRING ) {
+		/* Single string */
+		return map_function(context, *listitem);
+	} else if ( sieve_ast_argument_type(*listitem) == SAAT_STRING_LIST ) {
+		int ret = 0; 
+		
+		/* String list */
+		*listitem = sieve_ast_strlist_first(*listitem);
+		
+		while ( *listitem != NULL ) {
+			
+			if ( (ret=map_function(context, *listitem)) <= 0 )
+				return ret;
+			
+			*listitem = sieve_ast_strlist_next(*listitem);
+		}
+		
+		return ret;
+	} 
+	
+	i_unreached();
+	return -1;
+}
+
+
 
 /* Debug */
 
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index 67eeb579cc4209d662ba0a8e6749f7bc419d8047..55e1b1112dc6c93f27421961932b70fdf21f7dc9 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -237,11 +237,24 @@ struct sieve_ast_node *sieve_ast_test_create
 struct sieve_ast_node *sieve_ast_command_create
 	(struct sieve_ast_node *parent, const char *identifier, 
 		unsigned int source_line);
+		
+/* 
+ * Utility
+ */
+
+int sieve_ast_stringlist_map
+	(struct sieve_ast_argument **listitem, void *context,
+		int (*map_function)(void *context, struct sieve_ast_argument *arg));
 	
-/* Debug */
+/* 
+ * Debug 
+ */
+
 void sieve_ast_unparse(struct sieve_ast *ast);
 
-/* AST access macros */
+/* 
+ * AST access macros 
+ */
 
 /* Generic list access macros */
 #define __AST_LIST_FIRST(list) \
diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c
index 55a01cb9c12d139e85c565dc24624045f52599aa..7c693b0926e0d3c6ece86fba76225251172d542f 100644
--- a/src/lib-sieve/tst-address.c
+++ b/src/lib-sieve/tst-address.c
@@ -107,24 +107,31 @@ static const char * const _allowed_headers[] = {
 	NULL  
 };
 
-static bool _header_is_allowed(const char *header)
+static int _header_is_allowed
+(void *context ATTR_UNUSED, struct sieve_ast_argument *arg)
 {
-	const char * const *hdsp = _allowed_headers;
-	while ( *hdsp != NULL ) {
-		if ( strcasecmp( *hdsp, header ) == 0 ) 
-			return TRUE;
+	if ( sieve_argument_is_string_literal(arg) ) {
+		const char *header = sieve_ast_strlist_strc(arg);
+
+		const char * const *hdsp = _allowed_headers;
+		while ( *hdsp != NULL ) {
+			if ( strcasecmp( *hdsp, header ) == 0 ) 
+				return TRUE;
 
-		hdsp++;
+			hdsp++;
+		}
+		
+		return FALSE;
 	}
 	
-	return FALSE;
+	return TRUE;
 }
 
 static bool tst_address_validate
 	(struct sieve_validator *validator, struct sieve_command_context *tst) 
 {
 	struct sieve_ast_argument *arg = tst->first_positional;
-	const char *not_allowed = NULL;
+	struct sieve_ast_argument *header;
 		
 	if ( !sieve_validate_positional_argument
 		(validator, tst, arg, "header list", 1, SAAT_STRING_LIST) ) {
@@ -138,33 +145,11 @@ static bool tst_address_validate
 	/* Check if supplied header names are allowed
 	 *   FIXME: verify dynamic header names at runtime 
 	 */
-	 
-	if ( sieve_argument_is_string_literal(arg) ) {
-		const char *header = sieve_ast_argument_strc(arg);
-	
-		/* Single string */
-		if ( !_header_is_allowed(header) )
-			not_allowed = header;
-			
-	} else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
-		/* String list */
-		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
-			
-		while ( not_allowed == NULL && stritem != NULL ) {
-			if ( sieve_argument_is_string_literal(stritem) ) {
-				const char *header = sieve_ast_strlist_strc(stritem);
-				
-				if ( !_header_is_allowed(header) )
-					not_allowed = header;
-					
-				stritem = sieve_ast_strlist_next(stritem);
-			}
-		}
-	}
-	
-	if ( not_allowed != NULL ) {
+	header = arg;
+	if ( !sieve_ast_stringlist_map(&header, NULL, _header_is_allowed) ) {		
 		sieve_command_validate_error(validator, tst, 
-			"specified header '%s' is not allowed for the address test", not_allowed);
+			"specified header '%s' is not allowed for the address test", 
+			sieve_ast_strlist_strc(header));
 		return FALSE;
 	}