diff --git a/src/lib-sieve/plugins/regex/Makefile.am b/src/lib-sieve/plugins/regex/Makefile.am
index 3b4444768d544eb4522a94ed8bca26bd9d03cd05..d80bbedef641490c36f7f602d902a3319dadbb51 100644
--- a/src/lib-sieve/plugins/regex/Makefile.am
+++ b/src/lib-sieve/plugins/regex/Makefile.am
@@ -8,5 +8,8 @@ AM_CPPFLAGS = \
 	-I$(dovecot_incdir)/src/lib-storage 
 
 libsieve_ext_regex_la_SOURCES = \
+	mcht-regex.c \
 	ext-regex.c
 
+noinst_HEADERS = \
+	ext-regex-common.h
diff --git a/src/lib-sieve/plugins/regex/ext-regex-common.h b/src/lib-sieve/plugins/regex/ext-regex-common.h
new file mode 100644
index 0000000000000000000000000000000000000000..3c50ad1cf423cdda882af440c6ecb1cd37ded630
--- /dev/null
+++ b/src/lib-sieve/plugins/regex/ext-regex-common.h
@@ -0,0 +1,8 @@
+#ifndef __EXT_REGEX_COMMON_H
+#define __EXT_REGEX_COMMON_H
+
+extern const struct sieve_match_type_extension regex_match_extension;
+
+#endif
+
+
diff --git a/src/lib-sieve/plugins/regex/ext-regex.c b/src/lib-sieve/plugins/regex/ext-regex.c
index a9a39b882cd009c35fad88ebea54f80240625be5..ca8b1999f998a5a2e9cd59afa5b0f0a044b10c44 100644
--- a/src/lib-sieve/plugins/regex/ext-regex.c
+++ b/src/lib-sieve/plugins/regex/ext-regex.c
@@ -31,6 +31,8 @@
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
 
+#include "ext-regex-common.h"
+
 #include <sys/types.h>
 #include <ctype.h>
 #include <regex.h>
@@ -41,12 +43,6 @@ static bool ext_regex_load(int ext_id);
 static bool ext_regex_validator_load(struct sieve_validator *validator);
 static bool ext_regex_binary_load(struct sieve_binary *sbin);
 
-void mtch_regex_match_init(struct sieve_match_context *mctx);
-static bool mtch_regex_match
-	(struct sieve_match_context *mctx, const char *val, size_t val_size,
-    	const char *key, size_t key_size, int key_index);
-bool mtch_regex_match_deinit(struct sieve_match_context *mctx);
-
 /* Extension definitions */
 
 static int ext_my_id;
@@ -69,219 +65,20 @@ static bool ext_regex_load(int ext_id)
 	return TRUE;
 }
 
-/* Extension access structures */
-
-extern const struct sieve_match_type_extension regex_match_extension;
-
-bool mtch_regex_validate_context
-(struct sieve_validator *validator, struct sieve_ast_argument *arg,
-    struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg);
+/* 
+ * Extension access structures 
+ */
 
-const struct sieve_match_type regex_match_type = {
-	"regex", TRUE,
-	&regex_match_extension,
-	0,
-	NULL,
-	mtch_regex_validate_context,
-	mtch_regex_match_init,
-	mtch_regex_match,
-	mtch_regex_match_deinit
-};
+extern const struct sieve_match_type regex_match_type;
 
 const struct sieve_match_type_extension regex_match_extension = { 
 	&regex_extension,
 	SIEVE_EXT_DEFINE_MATCH_TYPE(regex_match_type)
 };
 
-/* Validation */
-
-/* Wrapper around the regerror function for easy access */
-static const char *_regexp_error(regex_t *regexp, int errorcode)
-{
-	size_t errsize = regerror(errorcode, regexp, NULL, 0); 
-
-	if ( errsize > 0 ) {
-		char *errbuf;
-
-		buffer_t *error_buf = 
-			buffer_create_dynamic(pool_datastack_create(), errsize);
-		errbuf = buffer_get_space_unsafe(error_buf, 0, errsize);
-
-		errsize = regerror(errorcode, regexp, errbuf, errsize);
-	 
-		/* We don't want the error to start with a capital letter */
-		errbuf[0] = i_tolower(errbuf[0]);
-
-		buffer_append_space_unsafe(error_buf, errsize);
-
-		return str_c(error_buf);
-	}
-
-	return "";
-}
-
-static bool mtch_regex_validate_regexp
-(struct sieve_validator *validator, struct sieve_match_type_context *ctx,
-	struct sieve_ast_argument *key, int cflags) 
-{
-	int ret;
-	regex_t regexp;
-
-	if ( (ret=regcomp(&regexp, sieve_ast_argument_strc(key), cflags)) != 0 ) {
-		sieve_command_validate_error(validator, ctx->command_ctx,
-			"invalid regular expression for regex match: %s", 
-			_regexp_error(&regexp, ret));
-
-		regfree(&regexp);	
-		return FALSE;
-	}
-
-	regfree(&regexp);
-	return TRUE;
-}
-	
-bool mtch_regex_validate_context
-(struct sieve_validator *validator, struct sieve_ast_argument *arg,
-	struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg)
-{
-	int cflags;
-	struct sieve_ast_argument *carg = 
-		sieve_command_first_argument(ctx->command_ctx);
-	
-	cflags =  REG_EXTENDED | REG_NOSUB;
-	while ( carg != NULL ) {
-		if ( carg != arg && carg->argument == &comparator_tag ) {
-			if ( sieve_comparator_tag_is(carg, &i_ascii_casemap_comparator) )
-				cflags =  REG_EXTENDED | REG_NOSUB | REG_ICASE;
-			else if ( sieve_comparator_tag_is(carg, &i_octet_comparator) )
-				cflags =  REG_EXTENDED | REG_NOSUB ;
-			else {
-				sieve_command_validate_error(validator, ctx->command_ctx, 
-					"regex match type only supports "
-					"i;octet and i;ascii-casemap comparators" );
-				return FALSE;	
-			}
-
-			return TRUE;
-		}
-	
-		carg = sieve_ast_argument_next(carg);
-	}
-
-	/* Validate regular expression(s) */
-	if ( sieve_ast_argument_type(key_arg) == SAAT_STRING ) {
-		/* Single string */	
-		if ( !mtch_regex_validate_regexp(validator, ctx, key_arg, cflags) )
-			return FALSE;
-
-	} else if ( sieve_ast_argument_type(key_arg) == SAAT_STRING_LIST ) {
-		/* String list */
-		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(key_arg);
-
-		while ( stritem != NULL ) {
-			if ( !mtch_regex_validate_regexp(validator, ctx, stritem, cflags) )
-				return FALSE;
-
-			stritem = sieve_ast_strlist_next(stritem);
-		}
-	} else {
-		/* ??? */ 
-		sieve_command_validate_error(validator, ctx->command_ctx, 
-			"!!BUG!!: mtch_regex_validate_context: invalid ast argument type(%s)",
-			sieve_ast_argument_type_name(sieve_ast_argument_type(key_arg)) );
-		return FALSE;
-	} 
-
-	return TRUE;
-}
-
-/* Actual extension implementation */
-
-struct mtch_regex_context {
-	ARRAY_DEFINE(reg_expressions, regex_t *);
-	int value_index;
-};
-
-void mtch_regex_match_init
-	(struct sieve_match_context *mctx)
-{
-	struct mtch_regex_context *ctx = 
-		t_new(struct mtch_regex_context, 1);
-	
-	t_array_init(&ctx->reg_expressions, 4);
-
-	ctx->value_index = -1;
-
-	mctx->data = (void *) ctx;
-}
-
-static regex_t *mtch_regex_get
-(struct mtch_regex_context *ctx,
-	const struct sieve_comparator *cmp, 
-	const char *key, unsigned int key_index)
-{
-	regex_t *regexp = NULL;
-	regex_t * const *rxp = &regexp;
-	int ret;
-	int cflags;
-	
-	if ( ctx->value_index <= 0 ) {
-		regexp = p_new(pool_datastack_create(), regex_t, 1);
-
-		if ( cmp == &i_octet_comparator ) 
-			cflags =  REG_EXTENDED | REG_NOSUB;
-		else if ( cmp ==  &i_ascii_casemap_comparator )
-			cflags =  REG_EXTENDED | REG_NOSUB | REG_ICASE;
-		else
-			return NULL;
-
-		if ( (ret=regcomp(regexp, key, cflags)) != 0 ) {
-    		/* FIXME: Do something useful, i.e. report error somewhere */
-			return NULL;
-		}
-
-		array_idx_set(&ctx->reg_expressions, key_index, &regexp);
-		rxp = &regexp;
-	} else {
-		rxp = array_idx(&ctx->reg_expressions, key_index);
-	}
-
-	return *rxp;
-}
-
-static bool mtch_regex_match
-(struct sieve_match_context *mctx, 
-	const char *val, size_t val_size ATTR_UNUSED, 
-	const char *key, size_t key_size ATTR_UNUSED, int key_index)
-{
-	struct mtch_regex_context *ctx = (struct mtch_regex_context *) mctx->data;
-	regex_t *regexp;
-
-	if ( key_index < 0 ) return FALSE;
-
-	if ( key_index == 0 ) ctx->value_index++;
-
-	regexp = mtch_regex_get(ctx, mctx->comparator, key, key_index);
-	 
-	return ( regexec(regexp, val, 0, NULL, 0) == 0 );
-}
-
-bool mtch_regex_match_deinit
-	(struct sieve_match_context *mctx)
-{
-	struct mtch_regex_context *ctx = (struct mtch_regex_context *) mctx->data;
-	unsigned int i;
-
-	for ( i = 0; i < array_count(&ctx->reg_expressions); i++ ) {
-		regex_t * const *regexp = array_idx(&ctx->reg_expressions, i);
-
-		regfree(*regexp);
-	}
-
-	return FALSE;
-}
-
-/* Load extension into validator */
+/* 
+ * Load extension into validator 
+ */
 
 static bool ext_regex_validator_load(struct sieve_validator *validator)
 {
@@ -291,7 +88,9 @@ static bool ext_regex_validator_load(struct sieve_validator *validator)
 	return TRUE;
 }
 
-/* Load extension into binary */
+/* 
+ * Load extension into binary 
+ */
 
 static bool ext_regex_binary_load(struct sieve_binary *sbin)
 {
diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c
new file mode 100644
index 0000000000000000000000000000000000000000..c3156f36ce1945f9fb27644ce7fb088b2170fc91
--- /dev/null
+++ b/src/lib-sieve/plugins/regex/mcht-regex.c
@@ -0,0 +1,239 @@
+/* Match-type ':regex'
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "buffer.h"
+#include "array.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+
+#include "ext-regex-common.h"
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <regex.h>
+
+/* 
+ * Forward declarations 
+ */
+
+void mcht_regex_match_init(struct sieve_match_context *mctx);
+static bool mcht_regex_match
+	(struct sieve_match_context *mctx, const char *val, size_t val_size,
+    	const char *key, size_t key_size, int key_index);
+bool mcht_regex_match_deinit(struct sieve_match_context *mctx);
+
+bool mcht_regex_validate_context
+(struct sieve_validator *validator, struct sieve_ast_argument *arg,
+    struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg);
+
+const struct sieve_match_type regex_match_type = {
+	"regex", TRUE,
+	&regex_match_extension,
+	0,
+	NULL,
+	mcht_regex_validate_context,
+	mcht_regex_match_init,
+	mcht_regex_match,
+	mcht_regex_match_deinit
+};
+
+/* 
+ * Match-type validation 
+ */
+
+/* Wrapper around the regerror function for easy access */
+static const char *_regexp_error(regex_t *regexp, int errorcode)
+{
+	size_t errsize = regerror(errorcode, regexp, NULL, 0); 
+
+	if ( errsize > 0 ) {
+		char *errbuf;
+
+		buffer_t *error_buf = 
+			buffer_create_dynamic(pool_datastack_create(), errsize);
+		errbuf = buffer_get_space_unsafe(error_buf, 0, errsize);
+
+		errsize = regerror(errorcode, regexp, errbuf, errsize);
+	 
+		/* We don't want the error to start with a capital letter */
+		errbuf[0] = i_tolower(errbuf[0]);
+
+		buffer_append_space_unsafe(error_buf, errsize);
+
+		return str_c(error_buf);
+	}
+
+	return "";
+}
+
+static bool mcht_regex_validate_regexp
+(struct sieve_validator *validator, struct sieve_match_type_context *ctx,
+	struct sieve_ast_argument *key, int cflags) 
+{
+	int ret;
+	regex_t regexp;
+
+	if ( (ret=regcomp(&regexp, sieve_ast_argument_strc(key), cflags)) != 0 ) {
+		sieve_command_validate_error(validator, ctx->command_ctx,
+			"invalid regular expression for regex match: %s", 
+			_regexp_error(&regexp, ret));
+
+		regfree(&regexp);	
+		return FALSE;
+	}
+
+	regfree(&regexp);
+	return TRUE;
+}
+	
+bool mcht_regex_validate_context
+(struct sieve_validator *validator, struct sieve_ast_argument *arg,
+	struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg)
+{
+	int cflags;
+	struct sieve_ast_argument *carg = 
+		sieve_command_first_argument(ctx->command_ctx);
+	
+	cflags =  REG_EXTENDED | REG_NOSUB;
+	while ( carg != NULL ) {
+		if ( carg != arg && carg->argument == &comparator_tag ) {
+			if ( sieve_comparator_tag_is(carg, &i_ascii_casemap_comparator) )
+				cflags =  REG_EXTENDED | REG_NOSUB | REG_ICASE;
+			else if ( sieve_comparator_tag_is(carg, &i_octet_comparator) )
+				cflags =  REG_EXTENDED | REG_NOSUB ;
+			else {
+				sieve_command_validate_error(validator, ctx->command_ctx, 
+					"regex match type only supports "
+					"i;octet and i;ascii-casemap comparators" );
+				return FALSE;	
+			}
+
+			return TRUE;
+		}
+	
+		carg = sieve_ast_argument_next(carg);
+	}
+
+	/* Validate regular expression(s) */
+	if ( sieve_ast_argument_type(key_arg) == SAAT_STRING ) {
+		/* Single string */	
+		if ( !mcht_regex_validate_regexp(validator, ctx, key_arg, cflags) )
+			return FALSE;
+
+	} else if ( sieve_ast_argument_type(key_arg) == SAAT_STRING_LIST ) {
+		/* String list */
+		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(key_arg);
+
+		while ( stritem != NULL ) {
+			if ( !mcht_regex_validate_regexp(validator, ctx, stritem, cflags) )
+				return FALSE;
+
+			stritem = sieve_ast_strlist_next(stritem);
+		}
+	} else {
+		/* ??? */ 
+		sieve_command_validate_error(validator, ctx->command_ctx, 
+			"!!BUG!!: mcht_regex_validate_context: invalid ast argument type(%s)",
+			sieve_ast_argument_type_name(sieve_ast_argument_type(key_arg)) );
+		return FALSE;
+	} 
+
+	return TRUE;
+}
+
+/* 
+ * Match-type implementation 
+ */
+
+struct mcht_regex_context {
+	ARRAY_DEFINE(reg_expressions, regex_t *);
+	int value_index;
+};
+
+void mcht_regex_match_init
+	(struct sieve_match_context *mctx)
+{
+	struct mcht_regex_context *ctx = 
+		t_new(struct mcht_regex_context, 1);
+	
+	t_array_init(&ctx->reg_expressions, 4);
+
+	ctx->value_index = -1;
+
+	mctx->data = (void *) ctx;
+}
+
+static regex_t *mcht_regex_get
+(struct mcht_regex_context *ctx,
+	const struct sieve_comparator *cmp, 
+	const char *key, unsigned int key_index)
+{
+	regex_t *regexp = NULL;
+	regex_t * const *rxp = &regexp;
+	int ret;
+	int cflags;
+	
+	if ( ctx->value_index <= 0 ) {
+		regexp = p_new(pool_datastack_create(), regex_t, 1);
+
+		if ( cmp == &i_octet_comparator ) 
+			cflags =  REG_EXTENDED | REG_NOSUB;
+		else if ( cmp ==  &i_ascii_casemap_comparator )
+			cflags =  REG_EXTENDED | REG_NOSUB | REG_ICASE;
+		else
+			return NULL;
+
+		if ( (ret=regcomp(regexp, key, cflags)) != 0 ) {
+    		/* FIXME: Do something useful, i.e. report error somewhere */
+			return NULL;
+		}
+
+		array_idx_set(&ctx->reg_expressions, key_index, &regexp);
+		rxp = &regexp;
+	} else {
+		rxp = array_idx(&ctx->reg_expressions, key_index);
+	}
+
+	return *rxp;
+}
+
+static bool mcht_regex_match
+(struct sieve_match_context *mctx, 
+	const char *val, size_t val_size ATTR_UNUSED, 
+	const char *key, size_t key_size ATTR_UNUSED, int key_index)
+{
+	struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
+	regex_t *regexp;
+
+	if ( key_index < 0 ) return FALSE;
+
+	if ( key_index == 0 ) ctx->value_index++;
+
+	regexp = mcht_regex_get(ctx, mctx->comparator, key, key_index);
+	 
+	return ( regexec(regexp, val, 0, NULL, 0) == 0 );
+}
+
+bool mcht_regex_match_deinit
+	(struct sieve_match_context *mctx)
+{
+	struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
+	unsigned int i;
+
+	for ( i = 0; i < array_count(&ctx->reg_expressions); i++ ) {
+		regex_t * const *regexp = array_idx(&ctx->reg_expressions, i);
+
+		regfree(*regexp);
+	}
+
+	return FALSE;
+}
+