From 19f3572d81eda0fb52c081b91057fe2c61358d0e Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Tue, 20 Nov 2007 22:18:07 +0100
Subject: [PATCH] Implemented regex match execution.

---
 src/lib-sieve/plugins/regex/ext-regex.c       |  53 ++++++++-
 src/lib-sieve/plugins/regex/regex.sieve       |   2 +-
 .../plugins/relational/ext-relational.c       |   8 +-
 src/lib-sieve/sieve-address-parts.c           |   9 +-
 src/lib-sieve/sieve-address-parts.h           |   7 +-
 src/lib-sieve/sieve-common.h                  |   1 +
 src/lib-sieve/sieve-match-types.c             | 109 ++++++++++++++----
 src/lib-sieve/sieve-match-types.h             |  21 +++-
 src/lib-sieve/tst-address.c                   |   8 +-
 src/lib-sieve/tst-header.c                    |  10 +-
 10 files changed, 183 insertions(+), 45 deletions(-)

diff --git a/src/lib-sieve/plugins/regex/ext-regex.c b/src/lib-sieve/plugins/regex/ext-regex.c
index ab50e599b..e92cbabe3 100644
--- a/src/lib-sieve/plugins/regex/ext-regex.c
+++ b/src/lib-sieve/plugins/regex/ext-regex.c
@@ -36,6 +36,16 @@ static bool ext_regex_validator_load(struct sieve_validator *validator);
 static bool ext_regex_interpreter_load
 	(struct sieve_interpreter *interpreter);
 
+void *mtch_regex_match_init
+	(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
+		const char *key, size_t key_size);
+static bool mtch_regex_match
+	(const struct sieve_match_type *mtch ATTR_UNUSED, 
+		const struct sieve_comparator *cmp, const char *val, size_t val_size, 
+		const char *key, size_t key_size, void *key_context);
+void mtch_regex_match_deinit
+	(const struct sieve_match_type *mtch, void *key_context);
+
 /* Extension definitions */
 
 static int ext_my_id;
@@ -75,7 +85,9 @@ const struct sieve_match_type regex_match_type = {
 	0,
 	NULL,
 	mtch_regex_validate_context,
-	NULL
+	mtch_regex_match_init,
+	mtch_regex_match,
+	mtch_regex_match_deinit,
 };
 
 const struct sieve_match_type_extension regex_match_extension = { 
@@ -183,6 +195,45 @@ bool mtch_regex_validate_context
 	return TRUE;
 }
 
+void *mtch_regex_match_init
+(const struct sieve_match_type *mtch ATTR_UNUSED, 
+	const struct sieve_comparator *cmp, const char *key, 
+	size_t key_size ATTR_UNUSED)
+{
+	int ret;
+	int cflags;
+	regex_t *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;
+	}
+
+	return regexp;
+}
+
+static bool mtch_regex_match
+(const struct sieve_match_type *mtch ATTR_UNUSED, 
+	const struct sieve_comparator *cmp ATTR_UNUSED, const char *val, 
+	size_t val_size ATTR_UNUSED, const char *key ATTR_UNUSED, 
+	size_t key_size ATTR_UNUSED, void *key_context)
+{
+	return ( regexec((regex_t *) key_context, val, 0, NULL, 0) == 0 );
+}
+
+void mtch_regex_match_deinit
+	(const struct sieve_match_type *mtch ATTR_UNUSED, void *key_context)
+{
+	regfree((regex_t *) key_context);
+}
+
 /* Load extension into validator */
 
 static bool ext_regex_validator_load(struct sieve_validator *validator)
diff --git a/src/lib-sieve/plugins/regex/regex.sieve b/src/lib-sieve/plugins/regex/regex.sieve
index 437a77b90..7531742f9 100644
--- a/src/lib-sieve/plugins/regex/regex.sieve
+++ b/src/lib-sieve/plugins/regex/regex.sieve
@@ -1,6 +1,6 @@
 require "regex";
 
-if address :regex :comparator "i;ascii-casemap" "from" "sirius(\\+.*)?@drunksnipers\\.com" {
+if address :regex :comparator "i;ascii-casemap" "from" "stephan(\\+.*)?@drunksnipers\\.com" {
 	keep;
 	stop;
 }
diff --git a/src/lib-sieve/plugins/relational/ext-relational.c b/src/lib-sieve/plugins/relational/ext-relational.c
index a116ce41e..d42e9332c 100644
--- a/src/lib-sieve/plugins/relational/ext-relational.c
+++ b/src/lib-sieve/plugins/relational/ext-relational.c
@@ -190,7 +190,7 @@ const struct sieve_match_type value_match_type = {
 	&relational_match_extension,
 	RELATIONAL_VALUE,
 	ext_relational_parameter_validate,
-	NULL, NULL
+	NULL, NULL, NULL, NULL
 };
 
 const struct sieve_match_type count_match_type = {
@@ -199,7 +199,7 @@ const struct sieve_match_type count_match_type = {
 	&relational_match_extension,
 	RELATIONAL_COUNT,
 	ext_relational_parameter_validate,
-	NULL, NULL
+	NULL, NULL, NULL, NULL
 };
 
 /* Per-parameter match type objects, used for generation/interpretation 
@@ -213,7 +213,7 @@ const struct sieve_match_type count_match_type = {
 		SIEVE_MATCH_TYPE_CUSTOM,                          \
 		&relational_match_extension,                      \
 		REL_MATCH_INDEX(RELATIONAL_VALUE, rel_match),     \
-		NULL, NULL, NULL                                  \
+		NULL, NULL, NULL, NULL, NULL                      \
 	}
 
 #define COUNT_MATCH_TYPE(name, rel_match, func) {         \
@@ -221,7 +221,7 @@ const struct sieve_match_type count_match_type = {
 		SIEVE_MATCH_TYPE_CUSTOM,                          \
 		&relational_match_extension,                      \
 		REL_MATCH_INDEX(RELATIONAL_COUNT, rel_match),     \
-		NULL, NULL, NULL                                  \
+		NULL, NULL, NULL, NULL, NULL                      \
 	}
 	
 static const struct sieve_match_type rel_match_types[] = { 
diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c
index 26d14a58e..7a5acd1e3 100644
--- a/src/lib-sieve/sieve-address-parts.c
+++ b/src/lib-sieve/sieve-address-parts.c
@@ -370,10 +370,9 @@ static bool tag_address_part_generate
  * Address Matching
  */
  
-bool sieve_address_match_stringlist
-	(const struct sieve_address_part *addrp, const struct sieve_match_type *mtch, 
-		const struct sieve_comparator *cmp,	struct sieve_coded_stringlist *key_list,
-		const char *data)
+bool sieve_address_match
+(const struct sieve_address_part *addrp, struct sieve_match_context *mctx, 		
+	const char *data)
 {
 	bool matched = FALSE;
 	const struct message_address *addr;
@@ -393,7 +392,7 @@ bool sieve_address_match_stringlist
 
 			part = addrp->extract_from(addr);
 			
-			if ( part != NULL && sieve_match_stringlist(mtch, cmp, key_list, part) )
+			if ( part != NULL && sieve_match_value(mctx, part) )
 				matched = TRUE;				
 		} 
 
diff --git a/src/lib-sieve/sieve-address-parts.h b/src/lib-sieve/sieve-address-parts.h
index aaad6413f..5fe765bfd 100644
--- a/src/lib-sieve/sieve-address-parts.h
+++ b/src/lib-sieve/sieve-address-parts.h
@@ -67,9 +67,8 @@ bool sieve_opr_address_part_dump
 	(struct sieve_interpreter *interpreter,
 		struct sieve_binary *sbin, sieve_size_t *address);
 
-bool sieve_address_match_stringlist
-	(const struct sieve_address_part *addrp, const struct sieve_match_type *mtch, 
-		const struct sieve_comparator *cmp,	struct sieve_coded_stringlist *key_list,
-		const char *data);
+bool sieve_address_match
+(const struct sieve_address_part *addrp, struct sieve_match_context *mctx,
+    const char *data);
 
 #endif /* __SIEVE_ADDRESS_PARTS_H */
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index cb4994c62..eecbf3eec 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -63,6 +63,7 @@ struct sieve_comparator;
 
 /* sieve-match-types.h */
 struct sieve_match_type;
+struct sieve_match_context;
 
 /* sieve-address-parts.h */
 struct sieve_address_part;
diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c
index e69aea0e9..af2739ebe 100644
--- a/src/lib-sieve/sieve-match-types.c
+++ b/src/lib-sieve/sieve-match-types.c
@@ -416,43 +416,107 @@ static bool tag_match_type_generate
 	return TRUE;
 }
 
-/* Stringlist Utility */
+/* Match Utility */
 
-bool sieve_match_stringlist
-	(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
-		struct sieve_coded_stringlist *key_list, const char *value)
+struct sieve_match_context {
+	const struct sieve_match_type *match_type;	
+	const struct sieve_comparator *comparator; 
+	struct sieve_coded_stringlist *key_list;
+
+	ARRAY_DEFINE(key_contexts, void *);
+
+	int value_index;
+};
+
+struct sieve_match_context *sieve_match_begin
+(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp, 
+	struct sieve_coded_stringlist *key_list)
 {
+	struct sieve_match_context *mctx = 
+		p_new(pool_datastack_create(), struct sieve_match_context, 1);  
+	int listlen = sieve_coded_stringlist_get_length(key_list);
+
+	mctx->match_type = mtch;
+	mctx->comparator = cmp;
+	mctx->key_list = key_list;
+	mctx->value_index = 0;
+	
+	p_array_init(&mctx->key_contexts, pool_datastack_create(), listlen);
+	array_idx_clear(&mctx->key_contexts, listlen-1);
+
+	return mctx;
+}
+
+bool sieve_match_value
+	(struct sieve_match_context *mctx, const char *value)
+{
+	const struct sieve_match_type *mtch = mctx->match_type;
+	unsigned int key_index;
 	string_t *key_item;
-	sieve_coded_stringlist_reset(key_list);
+	sieve_coded_stringlist_reset(mctx->key_list);
 				
 	/* Reject unimplemented match-type */
 	if ( mtch->match == NULL )
 		return FALSE;
 				
 	/* Match to all key values */
+	key_index = 0;
 	key_item = NULL;
-	while ( sieve_coded_stringlist_next_item(key_list, &key_item) && 
+	while ( sieve_coded_stringlist_next_item(mctx->key_list, &key_item) && 
 		key_item != NULL ) 
 	{
-		if ( mtch->match(mtch, cmp, value, strlen(value), 
-			str_c(key_item), str_len(key_item)) )
+		void * const *kctx;
+
+		if ( mctx->value_index == 0 && mtch->match_init != NULL ) {
+			void *key_ctx = mtch->match_init(
+				mctx->match_type, mctx->comparator,
+				str_c(key_item), str_len(key_item)); 
+
+			kctx = &key_ctx;
+			array_idx_set(&mctx->key_contexts, key_index, kctx); 
+		} else 
+			kctx = array_idx(&mctx->key_contexts, key_index);
+
+		if ( mtch->match
+			(mctx->match_type, mctx->comparator, value, strlen(value), 
+			str_c(key_item), str_len(key_item), *kctx) )
 			return TRUE;  
+	
+		key_index++;
 	}
-  
+
+	mctx->value_index++;
+
+	return FALSE;
+}
+
+bool sieve_match_end(struct sieve_match_context *mctx)
+{
+	unsigned int i;
+	const struct sieve_match_type *mtch = mctx->match_type;
+
+	if ( mtch->match_deinit != NULL ) {
+		for ( i = 0; i < array_count(&mctx->key_contexts); i++ ) {
+			void * const *kctx = array_idx(&mctx->key_contexts, i);
+
+			mtch->match_deinit(mtch, *kctx);
+ 	 	}
+	}
+
 	return FALSE;
 }
 
 /*
  * Matching
  */
- 
+
 static bool mtch_is_match
 (const struct sieve_match_type *mtch ATTR_UNUSED, 
-	const struct sieve_comparator *cmp,
-	const char *val1, size_t val1_size, const char *val2, size_t val2_size)
+	const struct sieve_comparator *cmp,	const char *val1, size_t val1_size, 
+	const char *key, size_t key_size, void *key_context ATTR_UNUSED)
 {
 	if ( cmp->compare != NULL )
-		return (cmp->compare(cmp, val1, val1_size, val2, val2_size) == 0);
+		return (cmp->compare(cmp, val1, val1_size, key, key_size) == 0);
 
 	return FALSE;
 }
@@ -462,8 +526,8 @@ static bool mtch_is_match
  */
 static bool mtch_contains_match
 (const struct sieve_match_type *mtch ATTR_UNUSED, 
-	const struct sieve_comparator *cmp,
-	const char *val, size_t val_size, const char *key, size_t key_size)
+	const struct sieve_comparator *cmp, const char *val, size_t val_size, 
+	const char *key, size_t key_size, void *key_context ATTR_UNUSED)
 {
 	const char *vend = (const char *) val + val_size;
 	const char *kend = (const char *) key + key_size;
@@ -484,8 +548,8 @@ static bool mtch_contains_match
 
 static bool mtch_matches_match
 (const struct sieve_match_type *mtch ATTR_UNUSED, 
-	const struct sieve_comparator *cmp ATTR_UNUSED,
-	const char *val, size_t val_size, const char *key, size_t key_size)
+	const struct sieve_comparator *cmp,	const char *val, size_t val_size, 
+	const char *key, size_t key_size, void *key_context ATTR_UNUSED)
 {
 	return FALSE;
 }
@@ -507,8 +571,9 @@ const struct sieve_match_type is_match_type = {
 	SIEVE_MATCH_TYPE_IS,
 	NULL,
 	0,
-	NULL, NULL,
+	NULL, NULL, NULL,
 	mtch_is_match,
+	NULL
 };
 
 const struct sieve_match_type contains_match_type = {
@@ -516,8 +581,9 @@ const struct sieve_match_type contains_match_type = {
 	SIEVE_MATCH_TYPE_CONTAINS,
 	NULL,
 	0,
-	NULL, NULL,
+	NULL, NULL, NULL,
 	mtch_contains_match,
+	NULL
 };
 
 const struct sieve_match_type matches_match_type = {
@@ -525,8 +591,9 @@ const struct sieve_match_type matches_match_type = {
 	SIEVE_MATCH_TYPE_MATCHES,
 	NULL,
 	0,
-	NULL, NULL,
-	mtch_matches_match
+	NULL, NULL, NULL,
+	mtch_matches_match,
+	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 61716e071..63550d937 100644
--- a/src/lib-sieve/sieve-match-types.h
+++ b/src/lib-sieve/sieve-match-types.h
@@ -10,6 +10,7 @@ enum sieve_match_type_code {
 	SIEVE_MATCH_TYPE_CUSTOM
 };
 
+struct sieve_match_type;
 struct sieve_match_type_extension;
 struct sieve_match_type_context;
 
@@ -28,9 +29,14 @@ struct sieve_match_type {
 		(struct sieve_validator *validator, struct sieve_ast_argument *arg, 
 			struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg);
 			
+	void *(*match_init)
+		(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
+			const char *key, size_t key_size);
 	bool (*match)
 		(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
-			const char *val1, size_t val1_size, const char *val2, size_t val2_size);
+			const char *val, size_t val_size, const char *key, size_t key_size,
+			void *key_context);
+	void (*match_deinit)(const struct sieve_match_type *mtch, void *key_context);
 };
 
 struct sieve_match_type_extension {
@@ -92,10 +98,15 @@ bool sieve_opr_match_type_dump
 	(struct sieve_interpreter *interpreter,
 		struct sieve_binary *sbin, sieve_size_t *address);
 		
-/* Stringlist Utility */
+/* Match Utility */
+
+struct sieve_match_context;
 
-bool sieve_match_stringlist
-	(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
-		struct sieve_coded_stringlist *key_list, const char *value);
+struct sieve_match_context *sieve_match_begin
+(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
+    struct sieve_coded_stringlist *key_list);
+bool sieve_match_value
+    (struct sieve_match_context *mctx, const char *value);
+bool sieve_match_end(struct sieve_match_context *mctx);
 		
 #endif /* __SIEVE_MATCH_TYPES_H */
diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c
index 41b2947d0..409988015 100644
--- a/src/lib-sieve/tst-address.c
+++ b/src/lib-sieve/tst-address.c
@@ -157,6 +157,7 @@ static bool tst_address_opcode_execute
 	const struct sieve_match_type *mtch = &is_match_type;
 	const struct sieve_address_part *addrp = &all_address_part;
 	unsigned int opt_code;
+	struct sieve_match_context *mctx;
 	struct sieve_coded_stringlist *hdr_list;
 	struct sieve_coded_stringlist *key_list;
 	string_t *hdr_item;
@@ -199,6 +200,9 @@ static bool tst_address_opcode_execute
 		t_pop();
 		return FALSE;
 	}
+
+	/* Initialize match context */
+    mctx = sieve_match_begin(mtch, cmp, key_list);
 	
 	/* Iterate through all requested headers to match */
 	hdr_item = NULL;
@@ -210,12 +214,14 @@ static bool tst_address_opcode_execute
 			
 			int i;
 			for ( i = 0; !matched && headers[i] != NULL; i++ ) {
-				if ( sieve_address_match_stringlist(addrp, mtch, cmp, key_list, headers[i]) )
+				if ( sieve_address_match(addrp, mctx, headers[i]) )
 					matched = TRUE;				
 			} 
 		}
 	}
 	
+    sieve_match_end(mctx);
+
 	t_pop();
 	
 	sieve_interpreter_set_test_result(interp, matched);
diff --git a/src/lib-sieve/tst-header.c b/src/lib-sieve/tst-header.c
index ec1ac4c17..43767a2a7 100644
--- a/src/lib-sieve/tst-header.c
+++ b/src/lib-sieve/tst-header.c
@@ -141,10 +141,10 @@ static bool tst_header_opcode_execute
 	(struct sieve_interpreter *interp, struct sieve_binary *sbin, sieve_size_t *address)
 {
 	struct mail *mail = sieve_interpreter_get_mail(interp);
-
 	unsigned int opt_code;
 	const struct sieve_comparator *cmp = &i_octet_comparator;
 	const struct sieve_match_type *mtch = &is_match_type;
+	struct sieve_match_context *mctx;
 	struct sieve_coded_stringlist *hdr_list;
 	struct sieve_coded_stringlist *key_list;
 	string_t *hdr_item;
@@ -181,7 +181,9 @@ static bool tst_header_opcode_execute
 		t_pop();
 		return FALSE;
 	}
-	
+
+	mctx = sieve_match_begin(mtch, cmp, key_list); 	
+
 	/* Iterate through all requested headers to match */
 	hdr_item = NULL;
 	matched = FALSE;
@@ -192,11 +194,13 @@ static bool tst_header_opcode_execute
 			
 			int i;
 			for ( i = 0; !matched && headers[i] != NULL; i++ ) {
-				if ( sieve_match_stringlist(mtch, cmp, key_list, headers[i]) )
+				if ( sieve_match_value(mctx, headers[i]) )
 					matched = TRUE;				
 			} 
 		}
 	}
+
+	sieve_match_end(mctx); 	
 	
 	t_pop();
 	
-- 
GitLab