diff --git a/src/lib-sieve/plugins/regex/ext-regex.c b/src/lib-sieve/plugins/regex/ext-regex.c
index 6a379acfae2d257f86c968ea890cdb56a7dcd7a9..e34746bd5235c6bebd6edd67934b90c4cfc79c0f 100644
--- a/src/lib-sieve/plugins/regex/ext-regex.c
+++ b/src/lib-sieve/plugins/regex/ext-regex.c
@@ -57,7 +57,8 @@ const struct sieve_match_type regex_match_type = {
 	"regex",
 	SIEVE_MATCH_TYPE_CUSTOM,
 	&regex_match_extension,
-	0
+	0,
+	NULL
 };
 
 const struct sieve_match_type_extension regex_match_extension = { 
diff --git a/src/lib-sieve/plugins/relational/ext-relational.c b/src/lib-sieve/plugins/relational/ext-relational.c
index 4687f15af19dfbbe6499dda88c48cc4f4bd542b1..5b46af083f8b2a1c552e8ddcae5f4412f9524d91 100644
--- a/src/lib-sieve/plugins/relational/ext-relational.c
+++ b/src/lib-sieve/plugins/relational/ext-relational.c
@@ -3,7 +3,7 @@
  *
  * Author: Stephan Bosch
  * Specification: RFC 3431
- * Implementation: 
+ * Implementation: validation only
  * Status: under development
  * 
  */
@@ -18,6 +18,7 @@
 
 #include "sieve-common.h"
 
+#include "sieve-ast.h"
 #include "sieve-code.h"
 #include "sieve-extensions.h"
 #include "sieve-commands.h"
@@ -33,6 +34,18 @@ static bool ext_relational_validator_load(struct sieve_validator *validator);
 static bool ext_relational_interpreter_load
 	(struct sieve_interpreter *interpreter);
 
+/* Types */
+
+enum relational_match {
+	REL_MATCH_GREATER,
+	REL_MATCH_GREATER_EQUAL,
+	REL_MATCH_LESS,
+	REL_MATCH_LESS_EQUAL,
+	REL_MATCH_EQUAL,
+	REL_MATCH_NOT_EQUAL,
+	REL_MATCH_INVALID
+};
+
 /* Extension definitions */
 
 static int ext_my_id;
@@ -54,6 +67,97 @@ static bool ext_relational_load(int ext_id)
 	return TRUE;
 }
 
+/* Validation */
+
+static bool ext_relational_parameter_validate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+		struct sieve_match_type_context *ctx)
+{	
+	enum relational_match rel_match = REL_MATCH_INVALID;
+	const char *rel_match_id;
+
+	/* Check syntax:
+	 *   relational-match = DQUOTE ( "gt" / "ge" / "lt"	
+ 	 *                             / "le" / "eq" / "ne" ) DQUOTE
+ 	 *
+	 * So, actually this must be a constant string and it is implemented as such 
+	 */
+	 
+	/* Did we get a string in the first place ? */ 
+	if ( (*arg)->type != SAAT_STRING ) {
+		sieve_command_validate_error(validator, ctx->command_ctx, 
+			"the :%s match-type requires a constant string argument containing "
+			"one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
+			"but %s was found", 
+			ctx->match_type->identifier, sieve_ast_argument_name(*arg));
+		return FALSE;
+	}
+	
+	/* Check the relational match id */
+	
+	rel_match_id = sieve_ast_argument_strc(*arg);
+	switch ( rel_match_id[0] ) {
+	/* "gt" or "ge" */
+	case 'g':
+		switch ( rel_match_id[1] ) {
+		case 't': 
+			rel_match = REL_MATCH_GREATER; 
+			break;
+		case 'e': 
+			rel_match = REL_MATCH_GREATER_EQUAL; 
+			break;
+		default: 
+			rel_match = REL_MATCH_INVALID;
+		}
+		break;
+	/* "lt" or "le" */
+	case 'l':
+		switch ( rel_match_id[1] ) {
+		case 't': 
+			rel_match = REL_MATCH_LESS; 
+			break;
+		case 'e': 
+			rel_match = REL_MATCH_LESS_EQUAL; 
+			break;
+		default: 
+			rel_match = REL_MATCH_INVALID;
+		}
+		break;
+	/* "eq" */
+	case 'e':
+		if ( rel_match_id[1] == 'q' )
+			rel_match = REL_MATCH_EQUAL;
+		else	
+			rel_match = REL_MATCH_INVALID;
+			
+		break;
+	/* "ne" */
+	case 'n':
+		if ( rel_match_id[1] == 'e' )
+			rel_match = REL_MATCH_NOT_EQUAL;
+		else	
+			rel_match = REL_MATCH_INVALID;
+		break;
+	/* invalid */
+	default:
+		rel_match = REL_MATCH_INVALID;
+	}
+	
+	if ( rel_match >= REL_MATCH_INVALID ) {
+		sieve_command_validate_error(validator, ctx->command_ctx, 
+			"the :%s match-type requires a constant string argument containing "
+			"one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
+			"but \"%s\" was found", 
+			ctx->match_type->identifier, rel_match_id);
+		return FALSE;
+	}
+	
+	/* Delete argument */
+	*arg = sieve_ast_arguments_delete(*arg, 1);
+
+	return TRUE;
+}
+
 /* Actual extension implementation */
 
 
@@ -70,14 +174,16 @@ const struct sieve_match_type value_match_type = {
 	"value",
 	SIEVE_MATCH_TYPE_CUSTOM,
 	&relational_match_extension,
-	RELATIONAL_VALUE
+	RELATIONAL_VALUE,
+	ext_relational_parameter_validate
 };
 
 const struct sieve_match_type count_match_type = {
 	"count",
 	SIEVE_MATCH_TYPE_CUSTOM,
 	&relational_match_extension,
-	RELATIONAL_COUNT
+	RELATIONAL_COUNT,
+	ext_relational_parameter_validate
 };
 
 static const struct sieve_match_type *ext_relational_get_match 
diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c
index 48e16c3ca421a90f86c2339b36acc29b86d44bfa..2ef62a893b78c3e0caab1b564f29ecbf91b2a681 100644
--- a/src/lib-sieve/sieve-match-types.c
+++ b/src/lib-sieve/sieve-match-types.c
@@ -22,9 +22,9 @@
  */
  
 static void opr_match_type_emit
-	(struct sieve_binary *sbin, struct sieve_match_type *addrp);
+	(struct sieve_binary *sbin, const struct sieve_match_type *mtch);
 static void opr_match_type_emit_ext
-	(struct sieve_binary *sbin, struct sieve_match_type *addrp, int ext_id);
+	(struct sieve_binary *sbin, const struct sieve_match_type *mtch, int ext_id);
 
 /* 
  * Address-part 'extension' 
@@ -78,25 +78,25 @@ static inline struct mtch_validator_context *
 
 static void _sieve_match_type_register
 	(pool_t pool, struct mtch_validator_context *ctx, 
-	const struct sieve_match_type *addrp, int ext_id) 
+	const struct sieve_match_type *mtch, int ext_id) 
 {
 	struct mtch_validator_registration *reg;
 	
 	reg = p_new(pool, struct mtch_validator_registration, 1);
-	reg->match_type = addrp;
+	reg->match_type = mtch;
 	reg->ext_id = ext_id;
 	
-	hash_insert(ctx->registrations, (void *) addrp->identifier, (void *) reg);
+	hash_insert(ctx->registrations, (void *) mtch->identifier, (void *) reg);
 }
  
 void sieve_match_type_register
 	(struct sieve_validator *validator, 
-	const struct sieve_match_type *addrp, int ext_id) 
+	const struct sieve_match_type *mtch, int ext_id) 
 {
 	pool_t pool = sieve_validator_pool(validator);
 	struct mtch_validator_context *ctx = get_validator_context(validator);
 
-	_sieve_match_type_register(pool, ctx, addrp, ext_id);
+	_sieve_match_type_register(pool, ctx, mtch, ext_id);
 }
 
 const struct sieve_match_type *sieve_match_type_find
@@ -129,9 +129,9 @@ bool mtch_validator_load(struct sieve_validator *validator)
 
 	/* Register core match-types */
 	for ( i = 0; i < sieve_core_match_types_count; i++ ) {
-		const struct sieve_match_type *addrp = sieve_core_match_types[i];
+		const struct sieve_match_type *mtch = sieve_core_match_types[i];
 		
-		_sieve_match_type_register(pool, ctx, addrp, -1);
+		_sieve_match_type_register(pool, ctx, mtch, -1);
 	}
 
 	sieve_validator_extension_set_context(validator, ext_my_id, ctx);
@@ -231,20 +231,21 @@ static bool tag_match_type_validate
 	struct sieve_command_context *cmd)
 {
 	int ext_id;
-	const struct sieve_match_type *addrp;
+	struct sieve_match_type_context *mtctx;
+	const struct sieve_match_type *mtch;
 
 	/* Syntax:   
-	 *   ":localpart" / ":domain" / ":all" (subject to extension)
+	 *   ":is" / ":contains" / ":matches" (subject to extension)
    */
 	
 	/* Get match_type from registry */
-	addrp = sieve_match_type_find
+	mtch = sieve_match_type_find
 		(validator, sieve_ast_argument_tag(*arg), &ext_id);
 	
-	/* In theory, addrp can never be NULL, because we must have found it earlier
+	/* In theory, mtch can never be NULL, because we must have found it earlier
 	 * to get here.
 	 */
-	if ( addrp == NULL ) {
+	if ( mtch == NULL ) {
 		sieve_command_validate_error(validator, cmd, 
 			"unknown match-type modifier '%s' "
 			"(this error should not occur and is probably a bug)", 
@@ -253,35 +254,43 @@ static bool tag_match_type_validate
 		return FALSE;
 	}
 
-	/* Store match-type in context */
-	(*arg)->context = (void *) addrp;
+	/* Create context */
+	mtctx = p_new(sieve_command_pool(cmd), struct sieve_match_type_context, 1);
+	mtctx->match_type = mtch;
+	mtctx->command_ctx = cmd;
+	
+	(*arg)->context = (void *) mtctx;
 	(*arg)->ext_id = ext_id;
 	
 	/* Skip tag */
 	*arg = sieve_ast_argument_next(*arg);
-
+	
+	if ( mtch->validate != NULL ) {
+		return mtch->validate(validator, arg, mtctx);
+	}
+	
 	return TRUE;
 }
 
 /* Code generation */
 
 static void opr_match_type_emit
-	(struct sieve_binary *sbin, struct sieve_match_type *addrp)
+	(struct sieve_binary *sbin, const struct sieve_match_type *mtch)
 { 
 	(void) sieve_operand_emit_code(sbin, SIEVE_OPERAND_MATCH_TYPE);
-	(void) sieve_binary_emit_byte(sbin, addrp->code);
+	(void) sieve_binary_emit_byte(sbin, mtch->code);
 }
 
 static void opr_match_type_emit_ext
-	(struct sieve_binary *sbin, struct sieve_match_type *addrp, int ext_id)
+	(struct sieve_binary *sbin, const struct sieve_match_type *mtch, int ext_id)
 { 
 	unsigned char mtch_code = SIEVE_MATCH_TYPE_CUSTOM + 
 		sieve_binary_extension_get_index(sbin, ext_id);
 	
 	(void) sieve_operand_emit_code(sbin, SIEVE_OPERAND_MATCH_TYPE);	
 	(void) sieve_binary_emit_byte(sbin, mtch_code);
-	if ( addrp->extension->match_type == NULL )
-		(void) sieve_binary_emit_byte(sbin, addrp->ext_code);
+	if ( mtch->extension->match_type == NULL )
+		(void) sieve_binary_emit_byte(sbin, mtch->ext_code);
 }
 
 const struct sieve_match_type *sieve_opr_match_type_read
@@ -332,13 +341,13 @@ bool sieve_opr_match_type_dump
 		struct sieve_binary *sbin, sieve_size_t *address)
 {
 	sieve_size_t pc = *address;
-	const struct sieve_match_type *addrp = 
+	const struct sieve_match_type *mtch = 
 		sieve_opr_match_type_read(interpreter, sbin, address);
 	
-	if ( addrp == NULL )
+	if ( mtch == NULL )
 		return FALSE;
 		
-	printf("%08x:   MATCH-TYPE: %s\n", pc, addrp->identifier);
+	printf("%08x:   MATCH-TYPE: %s\n", pc, mtch->identifier);
 	
 	return TRUE;
 }
@@ -348,16 +357,16 @@ static bool tag_match_type_generate
 	struct sieve_command_context *cmd ATTR_UNUSED)
 {
 	struct sieve_binary *sbin = sieve_generator_get_binary(generator);
-	struct sieve_match_type *addrp =
-		(struct sieve_match_type *) (*arg)->context;
+	struct sieve_match_type_context *mtctx =
+		(struct sieve_match_type_context *) (*arg)->context;
 	
-	if ( addrp->extension == NULL ) {
-		if ( addrp->code < SIEVE_MATCH_TYPE_CUSTOM )
-			opr_match_type_emit(sbin, addrp);
+	if ( mtctx->match_type->extension == NULL ) {
+		if ( mtctx->match_type->code < SIEVE_MATCH_TYPE_CUSTOM )
+			opr_match_type_emit(sbin, mtctx->match_type);
 		else
 			return FALSE;
 	} else {
-		opr_match_type_emit_ext(sbin, addrp, (*arg)->ext_id);
+		opr_match_type_emit_ext(sbin, mtctx->match_type, (*arg)->ext_id);
 	} 
 		
 	*arg = sieve_ast_argument_next(*arg);
@@ -386,6 +395,7 @@ const struct sieve_match_type is_match_type = {
 	SIEVE_MATCH_TYPE_IS,
 	NULL,
 	0,
+	NULL
 };
 
 const struct sieve_match_type contains_match_type = {
@@ -393,6 +403,7 @@ const struct sieve_match_type contains_match_type = {
 	SIEVE_MATCH_TYPE_CONTAINS,
 	NULL,
 	0,
+	NULL
 };
 
 const struct sieve_match_type matches_match_type = {
@@ -400,6 +411,7 @@ const struct sieve_match_type matches_match_type = {
 	SIEVE_MATCH_TYPE_MATCHES,
 	NULL,
 	0,
+	NULL
 };
 
 const struct sieve_match_type *sieve_core_match_types[] = {
diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h
index 7af63242d94df1a0e49a0078144ea94a3a972253..08814f1bd7028f5da1f230898060bec75b4716c4 100644
--- a/src/lib-sieve/sieve-match-types.h
+++ b/src/lib-sieve/sieve-match-types.h
@@ -11,6 +11,7 @@ enum sieve_match_type_code {
 };
 
 struct sieve_match_type_extension;
+struct sieve_match_type_context;
 
 struct sieve_match_type {
 	const char *identifier;
@@ -19,6 +20,10 @@ struct sieve_match_type {
 	
 	const struct sieve_match_type_extension *extension;
 	unsigned int ext_code;
+	
+	bool (*validate)
+		(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+			struct sieve_match_type_context *ctx);
 };
 
 struct sieve_match_type_extension {
@@ -32,6 +37,12 @@ struct sieve_match_type_extension {
 		(unsigned int code);
 };
 
+struct sieve_match_type_context {
+	struct sieve_command_context *command_ctx;
+	const struct sieve_match_type *match_type;
+	void *ctx_data;
+};
+
 void sieve_match_types_link_tags
 	(struct sieve_validator *validator, 
 		struct sieve_command_registration *cmd_reg,