From 34fd1a7b453e7e22319dec324701cf237298b4dd Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Mon, 28 Jul 2008 12:48:14 +0200 Subject: [PATCH] Fixed bugs in the :matches match type. --- src/lib-sieve/mcht-matches.c | 83 +++++---- src/lib-sieve/sieve-match-types.c | 14 ++ src/lib-sieve/sieve-match-types.h | 2 + .../tests/extensions/variables/match.svtest | 167 ++++++++++++++++++ src/testsuite/tests/lexer.svtest | 14 +- .../tests/match-types/matches.svtest | 2 + 6 files changed, 247 insertions(+), 35 deletions(-) create mode 100644 src/testsuite/tests/extensions/variables/match.svtest diff --git a/src/lib-sieve/mcht-matches.c b/src/lib-sieve/mcht-matches.c index db8863c9e..4d6a441fb 100644 --- a/src/lib-sieve/mcht-matches.c +++ b/src/lib-sieve/mcht-matches.c @@ -40,7 +40,7 @@ const struct sieve_match_type matches_match_type = { /* Quick 'n dirty debug */ //#define MATCH_DEBUG #ifdef MATCH_DEBUG -#define debug_printf(...) printf (__VA_ARGS__) +#define debug_printf(...) printf ("match debug: " __VA_ARGS__) #else #define debug_printf(...) #endif @@ -116,8 +116,9 @@ static int mcht_matches_match * Escape sequences \? and \* need special attention. */ - debug_printf("MATCH key: %s\n", t_strdup_until(key, kend)); - debug_printf("MATCH val: %s\n", t_strdup_until(val, vend)); + debug_printf("=== Start ===\n"); + debug_printf(" key: %s\n", t_strdup_until(key, kend)); + debug_printf(" value: %s\n", t_strdup_until(val, vend)); /* Loop until either key or value ends */ while (kp < kend && vp < vend && j < 40) { @@ -145,29 +146,32 @@ static int mcht_matches_match key_offset++; } - debug_printf("MATCH found wildcard '%c' at pos [%d]\n", + debug_printf("found wildcard '%c' at pos [%d]\n", next_wcard, (int) (wp-key)); str_truncate(mvalue, 0); - } else + } else { + debug_printf("backtracked"); backtrack = FALSE; + } needle = str_c(section); nend = PTR_OFFSET(needle, str_len(section)); - debug_printf("MATCH sneedle: '%s'\n", t_strdup_until(needle, nend)); - debug_printf("MATCH skey: '%s'\n", t_strdup_until(kp, kend)); - debug_printf("MATCH swp: '%s'\n", t_strdup_until(wp, kend)); - debug_printf("MATCH sval: '%s'\n", t_strdup_until(vp, vend)); + debug_printf(" section needle: '%s'\n", t_strdup_until(needle, nend)); + 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)); pvp = vp; if ( next_wcard == '\0' ) { const char *qp, *qend; - debug_printf("MATCH find end.\n"); + debug_printf("next_wcard = NUL; must find needle at end\n"); /* Find substring at the end of string */ if ( vend - str_len(section) < vp ) { + debug_printf(" wont match: value is too short\n"); break; } @@ -177,7 +181,7 @@ static int mcht_matches_match str_append_n(mvalue, pvp, qp-pvp); if ( !cmp->char_match(cmp, &vp, vend, &needle, nend) ) { - debug_printf("MATCH failed end\n"); + debug_printf(" match at end failed\n"); break; } @@ -187,32 +191,35 @@ static int mcht_matches_match kp = kend; vp = vend; + debug_printf(" matched end of value\n"); break; } else { const char *prv = NULL; const char *prk = NULL; const char *prw = NULL; const char *chars; - debug_printf("MATCH find.\n"); str_truncate(mchars, 0); if ( wcard == '\0' ) { /* Match needs to happen right at the beginning */ - debug_printf("MATCH bkey: '%s'\n", t_strdup_until(needle, nend)); - debug_printf("MATCH bval: '%s'\n", t_strdup_until(vp, vend)); + debug_printf("wcard = NUL; needle should be found at the beginning.\n"); + debug_printf(" begin needle: '%s'\n", t_strdup_until(needle, nend)); + debug_printf(" begin value: '%s'\n", t_strdup_until(vp, vend)); if ( !cmp->char_match(cmp, &vp, vend, &needle, nend) ) { - debug_printf("MATCH failed begin\n"); + debug_printf(" failed to find needle at begin\n"); break; } } 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("MATCH failed find\n"); + debug_printf(" failed to find needle at an offset\n"); break; } @@ -225,31 +232,32 @@ static int mcht_matches_match str_append_n(mvalue, pvp, qp-pvp); for ( ; qp < qend; qp++ ) str_append_c(mchars, *qp); - debug_printf("MATCH :: %s\n", str_c(mvalue)); } if ( wp < kend ) wp++; kp = wp; while ( next_wcard == '?' ) { - debug_printf("MATCH ?\n"); + debug_printf("next_wcard = '?'; need to match arbitrary character\n"); /* Add match value */ str_append_c(mchars, *vp); vp++; next_wcard = _scan_key_section(subsection, &wp, kend); - debug_printf("MATCH found next wildcard '%c' at pos [%d]\n", + debug_printf("found next wildcard '%c' at pos [%d] (fixed match)\n", next_wcard, (int) (wp-key)); needle = str_c(subsection); nend = PTR_OFFSET(needle, str_len(subsection)); - debug_printf("MATCH fkey: '%s'\n", t_strdup_until(needle, nend)); - debug_printf("MATCH fval: '%s'\n", t_strdup_until(vp, vend)); + debug_printf(" sub key: '%s'\n", t_strdup_until(needle, nend)); + debug_printf(" value remnant: '%s'\n", vp <= vend ? t_strdup_until(vp, vend) : ""); - if ( (needle == nend && vp < vend ) || + if ( (needle == nend && next_wcard == '\0' && vp < vend ) || !cmp->char_match(cmp, &vp, vend, &needle, nend) ) { + debug_printf(" failed fixed match\n"); + if ( prv != NULL && prv + 1 < vend ) { vp = prv; kp = prk; @@ -262,13 +270,8 @@ static int mcht_matches_match next_wcard = '?'; backtrack = TRUE; - debug_printf("MATCH backtrack\n"); - } else { - /* We are sure to have failed */ - return FALSE; + debug_printf(" BACKTRACK\n"); } - - debug_printf("MATCH failed fixed\n"); break; } @@ -279,8 +282,8 @@ static int mcht_matches_match if ( !backtrack ) { unsigned int i; - if ( next_wcard != '*' && next_wcard != '\0' ) { - debug_printf("MATCH failed '?'\n"); + if ( next_wcard == '?' ) { + debug_printf("failed to match '?'\n"); break; } @@ -290,6 +293,11 @@ static int mcht_matches_match for ( i = 0; i < str_len(mchars); i++ ) { sieve_match_values_add_char(mvalues, chars[i]); } + + if ( next_wcard != '*' ) { + debug_printf("failed to match at end of string\n"); + break; + } } } @@ -302,18 +310,27 @@ static int mcht_matches_match sieve_match_values_add(mvalues, mvalue); kp = kend; vp = vend; + debug_printf("key ends with '*'\n"); break; } - debug_printf("MATCH loop\n"); + debug_printf("== Loop ==\n"); j++; } - debug_printf("MATCH loop ended\n"); + debug_printf("=== Finish ===\n"); + debug_printf(" result: %s\n", (kp == kend && vp == vend) ? "true" : "false"); /* By definition, the match is only successful if both value and key pattern * are exhausted. */ - return (kp == kend && vp == vend); + if (kp == kend && vp == vend) { + string_t *matched = str_new_const(pool_datastack_create(), val, val_size); + sieve_match_values_set(mvalues, 0, matched); + + return TRUE; + } + + return FALSE; } diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c index b79f55e42..7e64310aa 100644 --- a/src/lib-sieve/sieve-match-types.c +++ b/src/lib-sieve/sieve-match-types.c @@ -194,6 +194,20 @@ static string_t *sieve_match_values_add_entry return entry; } + +void sieve_match_values_set +(struct sieve_match_values *mvalues, unsigned int index, string_t *value) +{ + if ( mvalues != NULL ) { + string_t * const *ep = array_idx(&mvalues->values, index); + string_t *entry = *ep; + + if ( entry != NULL && value != NULL ) { + str_truncate(entry, 0); + str_append_str(entry, value); + } + } +} void sieve_match_values_add (struct sieve_match_values *mvalues, string_t *value) diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h index 83894f068..293b6eec7 100644 --- a/src/lib-sieve/sieve-match-types.h +++ b/src/lib-sieve/sieve-match-types.h @@ -70,6 +70,8 @@ bool sieve_match_values_set_enabled (struct sieve_interpreter *interp, bool enable); struct sieve_match_values *sieve_match_values_start (struct sieve_interpreter *interp); +void sieve_match_values_set + (struct sieve_match_values *mvalues, unsigned int index, string_t *value); void sieve_match_values_add (struct sieve_match_values *mvalues, string_t *value); void sieve_match_values_add_char diff --git a/src/testsuite/tests/extensions/variables/match.svtest b/src/testsuite/tests/extensions/variables/match.svtest new file mode 100644 index 000000000..67c734afc --- /dev/null +++ b/src/testsuite/tests/extensions/variables/match.svtest @@ -0,0 +1,167 @@ +require "vnd.dovecot.testsuite"; + +require "variables"; + +set "match1" "Test of general stupidity"; + +test "Begin" { + if not string :matches "${match1}" "Test of *" { + test_fail "should have matched"; + } + + if not string :is "${1}" "general stupidity" { + test_fail "match value incorrect"; + } +} + +test "Begin no match" { + if string :matches "${match1}" "of *" { + test_fail "should not have matched"; + } +} + +set "match2" "toptoptop"; + +test "End" { + if not string :matches "${match2}" "*top" { + test_fail "should have matched"; + } + + if not string :is "${1}" "toptop" { + test_fail "match value incorrect"; + } +} + +set "match3" "ik ben een tukker met grote oren en een lelijke broek."; + +test "Multiple" { + if not string :matches "${match3}" "ik ben * met * en *." { + test_fail "should have matched"; + } + + set "line" "Hij is ${1} met ${2} en ${3}!"; + + if not string :is "${line}" + "Hij is een tukker met grote oren en een lelijke broek!" { + test_fail "match values incorrect: ${line}"; + } +} + +set "match4" "beter van niet?"; + +test "Escape" { + if not string :matches "${match4}" "*\\?" { + test_fail "should have matched"; + } + + if not string :is "${1}" "beter van niet" { + test_fail "match value incorrect: ${1}"; + } +} + +set "match5" "The quick brown fox jumps over the lazy dog."; + +test "Alphabet ?" { + if not string :matches "${match5}" "T?? ????? ????? ?o? ?u??? o?er ?he ???? ?o?." { + test_fail "should have matched"; + } + + set "alphabet" "${22}${8}${6}${25}${2}${13}${26}${1}${5}${15}${7}${21}${16}${12}${10}${17}${3}${9}${18}${20}${4}${19}${11}${14}${24}${23}"; + + if not string :is "${alphabet}" "abcdefghijklmnopqrstuvwxyz" { + test_fail "match values incorrect: ${alphabet}"; + } + + if string :matches "${match5}" "T?? ????? ?w??? ?o? ?u??? o?er ?he ???? ?o?." { + test_fail "should not have matched"; + } +} + +set "match6" "zero:one:zero|three;one;zero/five"; + +test "Words sep ?" { + + if not string :matches "${match6}" "*one?zero?five" { + test_fail "should have matched"; + } + + if not string :is "${1}${2}${3}" "zero:one:zero|three;;/" { + test_fail "incorrect match values: ${1} ${2} ${3}"; + } +} + +set "match7" "frop"; + +test "Letters begin ?" { + if not string :matches "${match7}" "??op" { + test_fail "should have matched"; + } + + set "val" "${0}:${1}:${2}:${3}:"; + + if not string :is "${val}" "frop:f:r::" { + test_fail "incorrect match values: ${val}"; + } +} + +test "Letters end ?" { + if not string :matches "${match7}" "fr??" { + test_fail "should have matched"; + } + + set "val" "${0}:${1}:${2}:${3}:"; + + if not string :is "${val}" "frop:o:p::" { + test_fail "incorrect match values: ${val}"; + } +} + +set "match8" "klopfropstroptop"; + +test "Letters words *? - 1" { + if not string :matches "${match8}" "*fr??*top" { + test_fail "should have matched"; + } + + set "val" ":${0}:${1}:${2}:${3}:${4}:${5}:"; + + if not string :is "${val}" ":klopfropstroptop:klop:o:p:strop::" { + test_fail "incorrect match values: ${val}"; + } +} + +test "Letters words *? - 2" { + if not string :matches "${match8}" "?*fr??*top" { + test_fail "should have matched"; + } + + set "val" ":${0}:${1}:${2}:${3}:${4}:${5}:${6}:"; + + if not string :is "${val}" ":klopfropstroptop:k:lop:o:p:strop::" { + test_fail "incorrect match values: ${val}"; + } +} + +test "Letters words *? backtrack" { + if not string :matches "${match8}" "*?op" { + test_fail "should have matched"; + } + + set "val" ":${0}:${1}:${2}:${3}:${4}:"; + + if not string :is "${val}" ":klopfropstroptop:klopfropstrop:t:::" { + test_fail "incorrect match values: ${val}"; + } +} + +test "Letters words *? first" { + if not string :matches "${match8}" "*?op*" { + test_fail "failed to match"; + } + + set "val" ":${0}:${1}:${2}:${3}:${4}:"; + + if not string :is "${val}" ":klopfropstroptop:k:l:fropstroptop::" { + test_fail "incorrect match values: ${val}"; + } +} diff --git a/src/testsuite/tests/lexer.svtest b/src/testsuite/tests/lexer.svtest index 95e668b6d..491309d0b 100644 --- a/src/testsuite/tests/lexer.svtest +++ b/src/testsuite/tests/lexer.svtest @@ -3,7 +3,7 @@ require "variables"; /* Test conformance to RFC 5228 - 2.4.2. Strings */ -set "text" text: +set "text" text: # Comment Line 1 .Line 2 ..Line 3 @@ -20,10 +20,20 @@ set "quoted" Line 5 "; -test "LEXER-string-literal" { +test "String Literal" { if not string :is "${text}" "${quoted}" { test_fail "lexer messed-up dot stuffing"; } + + if string :is "${text}" "" { + test_fail "variable substitution failed"; + } +} + +test "Unknown Escapes" { + if not string :is "\a\a\a\a\a" "aaaaa" { + test_fail "unknown quoted string escape sequences are handled inappropriately"; + } } diff --git a/src/testsuite/tests/match-types/matches.svtest b/src/testsuite/tests/match-types/matches.svtest index a808520c5..32b4eb510 100644 --- a/src/testsuite/tests/match-types/matches.svtest +++ b/src/testsuite/tests/match-types/matches.svtest @@ -189,3 +189,5 @@ test "NO-MATCH-M" { test_fail "should not have matched"; } } + + -- GitLab