From 8d4f501534443326f15c3b69478c201e86d8415c Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 1 Feb 2009 13:30:11 +0100
Subject: [PATCH] Fixed bug in the :matches match type.

---
 src/lib-sieve/mcht-matches.c     |  29 ++++---
 tests/match-types/matches.svtest | 141 +++++++++++++++----------------
 2 files changed, 85 insertions(+), 85 deletions(-)

diff --git a/src/lib-sieve/mcht-matches.c b/src/lib-sieve/mcht-matches.c
index 86f96970b..25d1a156e 100644
--- a/src/lib-sieve/mcht-matches.c
+++ b/src/lib-sieve/mcht-matches.c
@@ -174,6 +174,7 @@ static int mcht_matches_match
 		debug_printf("  section key:     '%s'\n", t_strdup_until(kp, kend));
 		debug_printf("  section remnant: '%s'\n", t_strdup_until(wp, kend));
 		debug_printf("  value remnant:   '%s'\n", t_strdup_until(vp, vend));
+		debug_printf("  key offset:      %d\n", key_offset);
 		
 		pvp = vp;
 		if ( next_wcard == '\0' ) {			
@@ -210,9 +211,9 @@ static int mcht_matches_match
 			debug_printf("  matched end of value\n");
 			break;
 		} else {
-			const char *prv = NULL;
-			const char *prk = NULL;
-			const char *prw = NULL;
+			const char *prv = NULL; /* Stored value pointer for backtrack */
+			const char *prk = NULL; /* Stored key pointer for backtrack */
+			const char *prw = NULL; /* Stored wildcard pointer for backtrack */
 			const char *chars;
 
 			if ( mvalues != NULL )		
@@ -232,23 +233,31 @@ static int mcht_matches_match
 			} else {
 				const char *qp, *qend;
 
-				debug_printf("wcard != NUL; must find needle at an offset.\n");
-				
-				/* Match may happen at any offset: find substring */
-				if ( !_string_find(cmp, &vp, vend, &needle, nend)	) {
+				debug_printf("wcard != NUL; must find needle at an offset (>= %d).\n",
+					key_offset);
+
+				/* Match may happen at any offset (>= key offset): find substring */				
+				vp += key_offset;
+				if ( (vp >= vend) || !_string_find(cmp, &vp, vend, &needle, nend) ) {
 					debug_printf("  failed to find needle at an offset\n"); 
 					break;
 				}
-			
+
 				prv = vp - str_len(section);
 				prk = kp;
-				prw = wp;
-				
+				prw = wp;		
+	
 				qend = vp - str_len(section);
 				qp = qend - key_offset;
 
+				/* Append match values */
 				if ( mvalues != NULL ) {
+					/* Append '*' match value */
 					str_append_n(mvalue, pvp, qp-pvp);
+
+					/* Append any initial '?' match values (those that caused the key
+					 * offset.
+					 */
 					for ( ; qp < qend; qp++ )
 						str_append_c(mchars, *qp);
 				}
diff --git a/tests/match-types/matches.svtest b/tests/match-types/matches.svtest
index 88c6f9636..a41134c7d 100644
--- a/tests/match-types/matches.svtest
+++ b/tests/match-types/matches.svtest
@@ -12,6 +12,7 @@ X-Spam-Score: **********
 X-Bullshit: 33333???a
 Message-ID: <90a02fe01fc25e131d0e9c4c45975894@example.com>
 Comment:                                            
+X-Subject: Log for successful build of Dovecot.
 
 Het werkt!
 .
@@ -35,179 +36,169 @@ test "Empty string" {
 	}
 }
 
-/*
- * Matching tests
- */
-
-test "MATCH-A" {
+test "Multiple '*'" {
 	if not address :matches "from" "*@d*ksn*ers.com" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "*@d*kn*ers.com" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-B" {
+test "End '*'" {
 	if not address :matches "from" "stephan+sieve@drunksnipers.*" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "stepan+sieve@drunksnipers.*" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-C" {
+test "Begin '*'" {
 	if not address :matches "from" "*+sieve@drunksnipers.com" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "*+sieve@drunksnipers.om" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-D" {
+test "Middle '?'" {
 	if not address :matches "from" "stephan+sieve?drunksnipers.com" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "stephan+sieve?drunksipers.com" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-E" {
+test "Begin '?'" {
 	if not address :matches "from" "?tephan+sieve@drunksnipers.com" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "?tephan+sievedrunksnipers.com" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-F" {
+test "End '?'" {
 	if not address :matches "from" "stephan+sieve@drunksnipers.co?" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "sephan+sieve@drunksnipers.co?" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-G" {
+test "Multiple '?'" {
 	if not address :matches "from" "?t?phan?sieve?drunksnip?rs.co?" {
 		test_fail "should have matched";
 	}
+
+	if address :matches "from" "?t?phan?sieve?dunksnip?rs.co?" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-H" {
+test "Escaped '?'" {
 	if not header :matches "x-bullshit" "33333\\?\\?\\??" {
 		test_fail "should have matched";
 	}
-}
 
+	if header :matches "x-bullshit" "33333\\?\\?\\?" {
+		test_fail "should not have matched";
+	}
+}
 
-test "MATCH-I" {
+test "Escaped '?' following '*'" {
 	if not header :matches "x-bullshit" "33333*\\?\\??" {
 		test_fail "should have matched";
 	}
+
 }
 
-test "MATCH-J" {
+test "Escaped '?' directly following initial '*'" {
 	if not header :matches "X-Bullshit" "*\\?\\?\\?a" {
 		test_fail "should have matched";
 	}
 }
 
-test "MATCH-K" {
+test "Escaped '?' following initial '*'" {
 	if not header :matches "x-bullshit" "*3333\\?\\?\\?a" {
 		test_fail "should have matched";
 	}
 }
 
-test "MATCH-L" {
+test "Escaped '*' with active '*' at the end" {
 	if not header :matches "x-spam-score" "\\*\\*\\*\\*\\**" {
 		test_fail "should have matched";
 	}
 }
 
-test "MATCH-M" {
+test "All escaped '*'" {
 	if not header :matches "x-spam-score" "\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*" {
 		test_fail "should have matched";
 	}
+
+	if header :matches "x-spam-score" "\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*" {
+		test_fail "should not have matched";
+	}
 }
 
-test "MATCH-N" {
+test "Middle not escaped '*'" {
 	if not header :matches "x-spam-score" "\\*\\*\\***\\*\\*" {
 		test_fail "should have matched";
 	}
 }
 
-test "MATCH-O" {
+test "Escaped '*' alternating with '?'" {
 	if not header :matches "x-spam-score" "\\*?\\*?\\*?\\*?\\*?" {
 		test_fail "should have matched";
 	}
-}
-
-# Non-matching tests
-
-test "NO-MATCH-A" {
-	if address :matches "from" "*@d*kn*ers.com" {
-		test_fail "should not have matched";
-	}
-}
-
-test "NO-MATCH-B" {
-	if address :matches "from" "stepan+sieve@drunksnipers.*" {
-		test_fail "should not have matched";
-	}
-}
-
-test "NO-MATCH-C" {
-	if address :matches "from" "*+sieve@drunksnipers.om" {
-		test_fail "should not have matched";
-	}
-}
 
-test "NO-MATCH-D" {
-	if address :matches "from" "stephan+sieve?drunksipers.com" {
-		test_fail "should not have matched";
-	}
-}
-
-test "NO-MATCH-E" {
-	if address :matches "from" "?tephan+sievedrunksnipers.com" {
+	if header :matches "x-spam-score" "\\*?\\*?\\*?\\*?\\*??" {
 		test_fail "should not have matched";
 	}
 }
 
-test "NO-MATCH-F" {
-	if address :matches "from" "sephan+sieve@drunksnipers.co?" {
+test "All escaped" {
+	if header :matches "x-bullshit" "\\*3333\\?\\?\\?a" {
 		test_fail "should not have matched";
 	}
-}
 
-test "NO-MATCH-G" {
-	if address :matches "from" "?t?phan?sieve?dunksnip?rs.co?" {
-		test_fail "should not have matched";
-	}
-}
 
-test "NO-MATCH-H" {
-	if header :matches "x-bullshit" "33333\\?\\?\\?" {
+	if header :matches "x-bullshit" "33333\\?\\?\\?aa" {
 		test_fail "should not have matched";
 	}
-}
 
-test "NO-MATCH-I" {
-	if header :matches "x-bullshit" "33333\\?\\?\\?aa" {
+	if header :matches "x-bullshit" "\\f3333\\?\\?\\?a" {
 		test_fail "should not have matched";
 	}
 }
 
-test "NO-MATCH-J" {
-	if header :matches "x-bullshit" "\\*3333\\?\\?\\?a" {
+test "Put '*' directly before '?'" {
+	if header :matches "x-subject" "Log for *??????????? build of *" {
 		test_fail "should not have matched";
 	}
-}
 
-test "NO-MATCH-K" {
-	if header :matches "x-bullshit" "\\f3333\\?\\?\\?a" {
-		test_fail "should not have matched";
+	if not header :matches "x-subject" "Log for *? build of *" {
+		test_fail "should have matched";
 	}
 }
 
-test "NO-MATCH-L" {
-	if header :matches "x-spam-score" "\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*" {
+test "Put '?' directly before '*'" {
+	if header :matches "x-subject" "Log for ???????????* build of *" {
 		test_fail "should not have matched";
 	}
-}
-
 
-test "NO-MATCH-M" {
-	if header :matches "x-spam-score" "\\*?\\*?\\*?\\*?\\*??" {
-		test_fail "should not have matched";
+	if not header :matches "x-subject" "Log for ?* build of *" {
+		test_fail "should have matched";
 	}
 }
 
-- 
GitLab