From 548ad6690c117a90377f292ba7913e65cadec00a Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 1 Aug 2008 20:40:36 +0200
Subject: [PATCH] Variables: fixed :count issue for the string test.

---
 Makefile.am                                   |  1 +
 TODO                                          |  1 -
 src/lib-sieve/mcht-contains.c                 |  3 ++
 src/lib-sieve/mcht-is.c                       |  9 ++++--
 src/lib-sieve/mcht-matches.c                  | 32 +++++++++++--------
 src/lib-sieve/plugins/regex/mcht-regex.c      |  5 +++
 src/lib-sieve/plugins/relational/mcht-count.c |  3 ++
 src/lib-sieve/plugins/relational/mcht-value.c |  9 +++++-
 src/lib-sieve/plugins/variables/tst-string.c  |  3 +-
 src/lib-sieve/sieve-match-types.h             | 11 +++++++
 src/lib-sieve/sieve-match.c                   |  2 +-
 tests/extensions/variables/string.svtest      | 10 ++++++
 12 files changed, 69 insertions(+), 20 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 3090495a4..523036a24 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,6 +34,7 @@ test_cases = \
 	tests/extensions/variables/match.svtest \
 	tests/extensions/variables/modifiers.svtest \
 	tests/extensions/variables/quoting.svtest \
+	tests/extensions/variables/string.svtest \
 	tests/extensions/include/variables.svtest \
 	tests/extensions/imapflags/basic.svtest \
 	tests/extensions/imapflags/rfc.svtest \
diff --git a/TODO b/TODO
index 535bc73f0..20c6231d4 100644
--- a/TODO
+++ b/TODO
@@ -2,7 +2,6 @@ Next (in order of descending priority/precedence):
 * Full standards compliance review for the engine and all fully implemented 
   sieve extensions. Issues discovered so far:
 	- :matches : match values must only be changed when the test matches.
-	- string test must not :count empty strings
 	- If an address is not syntactically valid, then it will not be matched
 	  by tests specifying ":localpart" or ":domain".
 	- Allow for the existance of dynamic comparators 
diff --git a/src/lib-sieve/mcht-contains.c b/src/lib-sieve/mcht-contains.c
index e10b59967..dc4174d0d 100644
--- a/src/lib-sieve/mcht-contains.c
+++ b/src/lib-sieve/mcht-contains.c
@@ -49,6 +49,9 @@ static int mcht_contains_match
 	const char *vp = val;
 	const char *kp = key;
 
+	if ( val == NULL || val_size == 0 ) 
+		return ( key_size == 0 );
+
 	if ( mctx->comparator->char_match == NULL ) 
 		return FALSE;
 
diff --git a/src/lib-sieve/mcht-is.c b/src/lib-sieve/mcht-is.c
index 0b131615b..dd64df659 100644
--- a/src/lib-sieve/mcht-is.c
+++ b/src/lib-sieve/mcht-is.c
@@ -16,7 +16,7 @@
  */
 
 static int mcht_is_match
-	(struct sieve_match_context *mctx, const char *val1, size_t val1_size, 
+	(struct sieve_match_context *mctx, const char *val, size_t val_size, 
 		const char *key, size_t key_size, int key_index);
 
 /* 
@@ -37,12 +37,15 @@ const struct sieve_match_type is_match_type = {
 
 static int mcht_is_match
 (struct sieve_match_context *mctx ATTR_UNUSED, 
-	const char *val1, size_t val1_size, 
+	const char *val, size_t val_size, 
 	const char *key, size_t key_size, int key_index ATTR_UNUSED)
 {
+	if ( (val == NULL || val_size == 0) ) 
+		return ( key_size == 0 );
+	
 	if ( mctx->comparator->compare != NULL )
 		return (mctx->comparator->compare(mctx->comparator, 
-			val1, val1_size, key, key_size) == 0);
+			val, val_size, key, key_size) == 0);
 
 	return FALSE;
 }
diff --git a/src/lib-sieve/mcht-matches.c b/src/lib-sieve/mcht-matches.c
index 4d6a441fb..00b57a2c5 100644
--- a/src/lib-sieve/mcht-matches.c
+++ b/src/lib-sieve/mcht-matches.c
@@ -86,24 +86,30 @@ static int mcht_matches_match
 {
 	const struct sieve_comparator *cmp = mctx->comparator;
 	struct sieve_match_values *mvalues;
-	
-	string_t *mvalue = t_str_new(32);     /* Match value (*) */
-	string_t *mchars = t_str_new(32);     /* Match characters (.?..?.??) */
-	string_t *section = t_str_new(32);    /* Section (after beginning or *) */
-	string_t *subsection = t_str_new(32); /* Sub-section (after ?) */
-	
-	const char *vend = (const char *) val + val_size;
-	const char *kend = (const char *) key + key_size;
-	const char *vp = val;   /* Value pointer */
-	const char *kp = key;   /* Key pointer */
-	const char *wp = key;   /* Wildcard (key) pointer */
-	const char *pvp = val;  /* Previous value Pointer */
-	
+	string_t *mvalue, *mchars, *section, *subsection;
+	const char *vend, *kend, *vp, *kp, *wp, *pvp;
 	bool backtrack = FALSE; /* TRUE: match of '?'-connected sections failed */
 	char wcard = '\0';      /* Current wildcard */
 	char next_wcard = '\0'; /* Next  widlcard */
 	unsigned int key_offset = 0;
 	unsigned int j = 0;
+
+	if ( val == NULL ) {
+		val = "";
+		val_size = 0;
+	}
+	
+	mvalue = t_str_new(32);     /* Match value (*) */
+	mchars = t_str_new(32);     /* Match characters (.?..?.??) */
+	section = t_str_new(32);    /* Section (after beginning or *) */
+	subsection = t_str_new(32); /* Sub-section (after ?) */
+	
+	vend = (const char *) val + val_size;
+	kend = (const char *) key + key_size;
+	vp = val;                   /* Value pointer */
+	kp = key;                   /* Key pointer */
+	wp = key;                   /* Wildcard (key) pointer */
+	pvp = val;                  /* Previous value Pointer */
 	
 	/* Reset match values list */
 	mvalues = sieve_match_values_start(mctx->interp);
diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c
index 439637b32..dc4c3ec11 100644
--- a/src/lib-sieve/plugins/regex/mcht-regex.c
+++ b/src/lib-sieve/plugins/regex/mcht-regex.c
@@ -231,6 +231,11 @@ static int mcht_regex_match
 	struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
 	regex_t *regexp;
 
+	if ( val == NULL ) {
+		val = "";
+		val_size = 0;
+	}
+
 	if ( key_index < 0 ) return FALSE;
 
 	if ( key_index == 0 ) ctx->value_index++;
diff --git a/src/lib-sieve/plugins/relational/mcht-count.c b/src/lib-sieve/plugins/relational/mcht-count.c
index 874f22b01..fe7bcc775 100644
--- a/src/lib-sieve/plugins/relational/mcht-count.c
+++ b/src/lib-sieve/plugins/relational/mcht-count.c
@@ -74,6 +74,9 @@ static int mcht_count_match
 	const char *key ATTR_UNUSED, size_t key_size ATTR_UNUSED,
 	int key_index) 
 {
+	if ( val == NULL )
+		return FALSE;
+
 	/* Count values */
 	if ( key_index == -1 ) {
 		mctx->data = (void *) (((int) mctx->data) + 1);
diff --git a/src/lib-sieve/plugins/relational/mcht-value.c b/src/lib-sieve/plugins/relational/mcht-value.c
index 0ccc26864..d9c121233 100644
--- a/src/lib-sieve/plugins/relational/mcht-value.c
+++ b/src/lib-sieve/plugins/relational/mcht-value.c
@@ -55,7 +55,14 @@ int mcht_value_match
 {
 	const struct sieve_match_type *mtch = mctx->match_type;
 	unsigned int rel_match = REL_MATCH(mtch->object.code);	
-	int cmp_result = mctx->comparator->
+	int cmp_result;
+
+	if ( val == NULL ) {
+		val = "";
+		val_size = 0;
+	}
+
+	cmp_result = mctx->comparator->
 		compare(mctx->comparator, val, val_size, key, key_size);
 
 	switch ( rel_match ) {
diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c
index add6d9403..a80eabd3c 100644
--- a/src/lib-sieve/plugins/variables/tst-string.c
+++ b/src/lib-sieve/plugins/variables/tst-string.c
@@ -214,9 +214,10 @@ static int tst_string_operation_execute
 	while ( result && !matched && 
 		(result=sieve_coded_stringlist_next_item(source, &src_item)) 
 		&& src_item != NULL ) {
+		const char *src = str_len(src_item) > 0 ? str_c(src_item) : NULL;
 
 		if ( (mret=sieve_match_value
-			(mctx, str_c(src_item), str_len(src_item))) < 0 ) {
+			(mctx, src, str_len(src_item))) < 0 ) {
 			result = FALSE;
 			break;
 		}
diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h
index 293b6eec7..309d8bd25 100644
--- a/src/lib-sieve/sieve-match-types.h
+++ b/src/lib-sieve/sieve-match-types.h
@@ -44,7 +44,18 @@ 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);
 			
+	/*
+	 * Matching
+ 	 */
+
 	void (*match_init)(struct sieve_match_context *mctx);
+
+	/* WARNING: some tests may pass a val == NULL parameter indicating that the passed
+	 * value has no significance. For string-type matches this should map to the empty
+	 * string "", but for match types that consider the passed values as objects rather
+	 * than strings (e.g. :count) this means that the passed value should be skipped.
+	 * This is currently only used by string test of the variables extension.
+	 */
 	int (*match)
 		(struct sieve_match_context *mctx, const char *val, size_t val_size, 
 			const char *key, size_t key_size, int key_index);
diff --git a/src/lib-sieve/sieve-match.c b/src/lib-sieve/sieve-match.c
index 816da07d5..37615566e 100644
--- a/src/lib-sieve/sieve-match.c
+++ b/src/lib-sieve/sieve-match.c
@@ -97,7 +97,7 @@ int sieve_match_value
 			return -1;
 
 	} else {
-		return mtch->match(mctx, value, strlen(value), NULL, 0, -1);
+		return mtch->match(mctx, value, val_size, NULL, 0, -1);
 	}
 
 	return FALSE;
diff --git a/tests/extensions/variables/string.svtest b/tests/extensions/variables/string.svtest
index 0763aae28..a2d4aa6ec 100644
--- a/tests/extensions/variables/string.svtest
+++ b/tests/extensions/variables/string.svtest
@@ -16,3 +16,13 @@ test "String - :count \"\"" {
 		test_fail "string test failed :count match";
 	}
 }
+
+test "RFC example" {
+	set "state" "${state} pending";
+
+	if not string :matches " ${state} " "* pending *" {
+    	# the above test always succeeds
+		
+		test_fail "test should have matched: \" ${state} \"";
+	}
+}
-- 
GitLab