diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
commit | 0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch) | |
tree | 3f3789daa2f6db22da6e55e92bee0062a7d613fe /pigeonhole/tests | |
parent | Initial commit. (diff) | |
download | dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip |
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pigeonhole/tests')
347 files changed, 23660 insertions, 0 deletions
diff --git a/pigeonhole/tests/comparators/i-ascii-casemap.svtest b/pigeonhole/tests/comparators/i-ascii-casemap.svtest new file mode 100644 index 0000000..0891f3e --- /dev/null +++ b/pigeonhole/tests/comparators/i-ascii-casemap.svtest @@ -0,0 +1,39 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: stephan@example.org +Cc: frop@example.com +To: test@dovecot.example.net +X-A: This is a TEST header +Subject: Test Message + +Test! +. +; + +test "i;ascii-casemap :contains (1)" { + if not header :contains :comparator "i;ascii-casemap" "X-A" "TEST" { + test_fail "should have matched"; + } +} + +test "i;ascii-casemap :contains (2)" { + if not header :contains :comparator "i;ascii-casemap" "X-A" "test" { + test_fail "should have matched"; + } +} + +test "i;ascii-casemap :matches (1)" { + if not header :matches :comparator "i;ascii-casemap" "X-A" "This*TEST*r" { + test_fail "should have matched"; + } +} + +test "i;ascii-casemap :matches (2)" { + if not header :matches :comparator "i;ascii-casemap" "X-A" "ThIs*tEsT*R" { + test_fail "should have matched"; + } +} + + + diff --git a/pigeonhole/tests/comparators/i-octet.svtest b/pigeonhole/tests/comparators/i-octet.svtest new file mode 100644 index 0000000..b6041bc --- /dev/null +++ b/pigeonhole/tests/comparators/i-octet.svtest @@ -0,0 +1,37 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: stephan@example.org +Cc: frop@example.com +To: test@dovecot.example.net +X-A: This is a TEST header +Subject: Test Message + +Test! +. +; + +test "i;octet :contains" { + if not header :contains :comparator "i;octet" "X-A" "TEST" { + test_fail "should have matched"; + } +} + +test "i;octet not :contains" { + if header :contains :comparator "i;octet" "X-A" "test" { + test_fail "should not have matched"; + } +} + +test "i;octet :matches" { + if not header :matches :comparator "i;octet" "X-A" "This*TEST*r" { + test_fail "should have matched"; + } +} + +test "i;octet not :matches" { + if header :matches :comparator "i;octet" "X-A" "ThIs*tEsT*R" { + test_fail "should not have matched"; + } +} + diff --git a/pigeonhole/tests/compile/compile.svtest b/pigeonhole/tests/compile/compile.svtest new file mode 100644 index 0000000..7abda7f --- /dev/null +++ b/pigeonhole/tests/compile/compile.svtest @@ -0,0 +1,16 @@ +require "vnd.dovecot.testsuite"; + +# Just test whether valid scripts will compile without problems + +test "Trivial" { + if not test_script_compile "trivial.sieve" { + test_fail "could not compile"; + } +} + +test "Redirect" { + if not test_script_compile "redirect.sieve" { + test_fail "could not compile"; + } +} + diff --git a/pigeonhole/tests/compile/errors.svtest b/pigeonhole/tests/compile/errors.svtest new file mode 100644 index 0000000..f17ea3f --- /dev/null +++ b/pigeonhole/tests/compile/errors.svtest @@ -0,0 +1,395 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Errors triggered in the compiled scripts are pretty reduntant over the + * tested commands, but we want to be thorough. + */ + +/* + * Lexer errors + */ + +test "Lexer errors (FIXME: count only)" { + if test_script_compile "errors/lexer.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "9" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Parser errors + */ + +test "Parser errors (FIXME: count only)" { + if test_script_compile "errors/parser.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "9" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Header test + */ + +test "Header errors" { + if test_script_compile "errors/header.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "10" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :matches + "unknown * ':all' for * header test *" { + test_fail "error 1 is invalid"; + } + + if not test_error :index 2 :matches + "*header test * string list * 1 (header names), but * number *" { + test_fail "error 2 is invalid"; + } + + if not test_error :index 3 :matches + "*header test * string list * 2 (key list), * number *" { + test_fail "error 3 is invalid"; + } + + if not test_error :index 4 :matches + "unknown tagged argument ':tag' for the header test *" { + test_fail "error 4 is invalid"; + } + + if not test_error :index 5 :matches + "* header test requires 2 *, but 1 *" { + test_fail "error 5 is invalid"; + } + + if not test_error :index 6 :matches + "* header test requires 2 *, but 0 *" { + test_fail "error 6 is invalid"; + } + + if not test_error :index 7 :matches + "*header test accepts no sub-tests* specified*" { + test_fail "error 7 is invalid"; + } + + if not test_error :index 8 :matches + "* use test 'header' * command*" { + test_fail "error 8 is invalid"; + } + + if not test_error :index 9 :matches + "* use test 'header' * command*" { + test_fail "error 9 is invalid"; + } + + if test_error :index 4 :contains "radish" { + test_fail "error test matched nonsense"; + } +} + +/* + * Address test + */ + + +test "Address errors" { + if test_script_compile "errors/address.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "9" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :matches + "*unknown * ':nonsense' * address test*" { + test_fail "error 1 is invalid"; + } + + if not test_error :index 2 :matches + "*address test expects *string list * 1 (header list),* number * found*" { + test_fail "error 2 is invalid"; + } + + if not test_error :index 3 :matches + "*address test expects *string list * 2 (key list),* number * found*" { + test_fail "error 3 is invalid"; + } + + if not test_error :index 4 :matches + "*unexpected *':is' * address test*" { + test_fail "error 4 is invalid"; + } + + if not test_error :index 5 :matches + "*address test * 2 positional arg*, but 1*" { + test_fail "error 5 is invalid"; + } + + if not test_error :index 6 :matches + "*address test * 2 positional arg*, but 0*" { + test_fail "error 6 is invalid"; + } + + if not test_error :index 7 :matches + "*'frop' *not allowed *address test*" { + test_fail "error 7 is invalid"; + } + + if not test_error :index 8 :matches + "*'frop' *not allowed *address test*" { + test_fail "error 8 is invalid"; + } + + if test_error :index 23 :contains "radish" { + test_fail "error test matched nonsense"; + } +} + +/* + * If command + */ + +test "If errors (FIXME: count only)" { + if test_script_compile "errors/if.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "12" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Require command + */ + +test "Require errors (FIXME: count only)" { + if test_script_compile "errors/require.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "15" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Size test + */ + +test "Size errors (FIXME: count only)" { + if test_script_compile "errors/size.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "7" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Envelope test + */ + +test "Envelope errors (FIXME: count only)" { + if test_script_compile "errors/envelope.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Stop command + */ + +test "Stop errors (FIXME: count only)" { + if test_script_compile "errors/stop.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "9" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Keep command + */ + +test "Keep errors (FIXME: count only)" { + if test_script_compile "errors/keep.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Fileinto command + */ + +test "Fileinto errors (FIXME: count only)" { + if test_script_compile "errors/fileinto.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "10" { + test_fail "wrong number of errors reported"; + } +} + +/* + * COMPARATOR errors + */ + +test "COMPARATOR errors (FIXME: count only)" { + if test_script_compile "errors/comparator.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "6" { + test_fail "wrong number of errors reported"; + } +} + +/* + * ADDRESS-PART errors + */ + +test "ADDRESS-PART errors (FIXME: count only)" { + if test_script_compile "errors/address-part.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * MATCH-TYPE errors + */ + +test "MATCH-TYPE errors (FIXME: count only)" { + if test_script_compile "errors/match-type.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Encoded-character errors + */ + +test "Encoded-character errors (FIXME: count only)" { + if test_script_compile "errors/encoded-character.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Outgoing address errors + */ + +test "Outgoing address errors (FIXME: count only)" { + if test_script_compile "errors/out-address.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "16" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Tagged argument errors + */ + +test "Tagged argument errors (FIXME: count only)" { + if test_script_compile "errors/tag.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Typos + */ + +test "Typos" { + if test_script_compile "errors/typos.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "6" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :matches + "missing semicolon * fileinto *" { + test_fail "error 1 is invalid"; + } + + if not test_error :index 2 :matches + "*fileinto command * no *tests* specified*" { + test_fail "error 2 is invalid"; + } + + if not test_error :index 3 :matches + "missing semicolon * fileinto *" { + test_fail "error 3 is invalid"; + } + + if not test_error :index 4 :matches + "*address test requires 2 * 0 * specified" { + test_fail "error 4 is invalid"; + } + + if not test_error :index 5 :matches + "missing colon *matches* tag * address test" { + test_fail "error 5 is invalid"; + } +} + + +/* + * Unsupported language features + */ + +test "Unsupported language features (FIXME: count only)" { + if test_script_compile "errors/unsupported.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/compile/errors/address-part.sieve b/pigeonhole/tests/compile/errors/address-part.sieve new file mode 100644 index 0000000..1d10cbf --- /dev/null +++ b/pigeonhole/tests/compile/errors/address-part.sieve @@ -0,0 +1,17 @@ +/* + * Address part errors + * + * Total errors: 2 (+1 = 3) + */ + +# Duplicate address part (1) +if address :all :comparator "i;octet" :domain "from" "STEPHAN" { + + # Duplicate address part (2) + if address :domain :localpart :comparator "i;octet" "from" "friep.example.com" { + keep; + } + + stop; +} + diff --git a/pigeonhole/tests/compile/errors/address.sieve b/pigeonhole/tests/compile/errors/address.sieve new file mode 100644 index 0000000..f7d3b26 --- /dev/null +++ b/pigeonhole/tests/compile/errors/address.sieve @@ -0,0 +1,71 @@ +require "comparator-i;ascii-numeric"; + +/* + * Address test errors + * + * Total count: 8 (+1 = 9) + */ + +/* + * Command structure + */ + +# Invalid tag +if address :nonsense :comparator "i;ascii-casemap" :localpart "From" "nico" { + discard; +} + +# Invalid first argument +if address :is :comparator "i;ascii-numeric" :localpart 45 "nico" { + discard; +} + +# Invalid second argument +if address :is :comparator "i;ascii-numeric" :localpart "From" 45 { + discard; +} + +# Invalid second argument +if address :comparator "i;ascii-numeric" :localpart "From" :is { + discard; +} + +# Missing second argument +if address :is :comparator "i;ascii-numeric" :localpart "From" { + discard; +} + +# Missing arguments +if address :is :comparator "i;ascii-numeric" :localpart { + discard; +} + +# Not an error +if address :localpart :is :comparator "i;ascii-casemap" "from" ["frop", "frop"] { + discard; +} + +/* + * Specified headers must contain addresses + */ + +# Invalid header +if address :is "frop" "frml" { + keep; +} + +# Not an error +if address :is "reply-to" "frml" { + keep; +} + +# Invalid header (#2) +if address :is ["to", "frop"] "frml" { + keep; +} + +# Not an error +if address :is ["to", "reply-to"] "frml" { + keep; +} + diff --git a/pigeonhole/tests/compile/errors/comparator.sieve b/pigeonhole/tests/compile/errors/comparator.sieve new file mode 100644 index 0000000..368b56b --- /dev/null +++ b/pigeonhole/tests/compile/errors/comparator.sieve @@ -0,0 +1,21 @@ +/* + * Address part errors + * + * Total errors: 5 (+1 = 6) + */ + +# 1: No argument +if address :comparator { } + +# 2: Number argument +if address :comparator 1 "from" "frop" { } + +# 3: String list argument +if address :comparator ["a", "b"] "from" "frop" { } + +# 4: Unknown tag +if address :comparator :frop "from" "frop" { } + +# 5: Known tag +if address :comparator :all "from" "frop" { } + diff --git a/pigeonhole/tests/compile/errors/encoded-character.sieve b/pigeonhole/tests/compile/errors/encoded-character.sieve new file mode 100644 index 0000000..04d9de4 --- /dev/null +++ b/pigeonhole/tests/compile/errors/encoded-character.sieve @@ -0,0 +1,23 @@ +/* + * Encoded-character errors + * + * Total errors: 2 (+1 = 3) + */ + +require "encoded-character"; +require "fileinto"; + +# Invalid unicode character (1) +fileinto "INBOX.${unicode:200000}"; + +# Not an error +fileinto "INBOX.${unicode:200000"; + +# Invalid unicode character (2) +fileinto "INBOX.${Unicode:DF01}"; + +# Not an error +fileinto "INBOX.${Unicode:DF01"; + + + diff --git a/pigeonhole/tests/compile/errors/envelope.sieve b/pigeonhole/tests/compile/errors/envelope.sieve new file mode 100644 index 0000000..9639846 --- /dev/null +++ b/pigeonhole/tests/compile/errors/envelope.sieve @@ -0,0 +1,23 @@ +/* + * Envelope test errors + * + * Total errors: 2 (+1 = 3) + */ + +require "envelope"; + +# Not an error +if envelope :is "to" "frop@example.org" { +} + +# Unknown envelope part (1) +if envelope :is "frop" "frop@example.org" { +} + +# Not an error +if envelope :is ["to","from"] "frop@example.org" { +} + +# Unknown envelope part (2) +if envelope :is ["to","frop"] "frop@example.org" { +} diff --git a/pigeonhole/tests/compile/errors/fileinto.sieve b/pigeonhole/tests/compile/errors/fileinto.sieve new file mode 100644 index 0000000..0598557 --- /dev/null +++ b/pigeonhole/tests/compile/errors/fileinto.sieve @@ -0,0 +1,38 @@ +require "fileinto"; +require "encoded-character"; + +/* + * Fileinto errors + * + * Total erors: 9 (+1 = 10) + */ + +# Missing string argument +fileinto; + +# Spurious test +fileinto true; + +# Spurious test +fileinto "Frop" true; + +# Spurious number argument +fileinto 33; + +# Spurious tag argument +fileinto :frop; + +# Spurious additional string argument +fileinto "Frop" "Friep"; + +# Spurious additional number argument +fileinto "Frop" 123; + +# Spurious additional tag argument +fileinto "Frop" :frop; + +# Bad mailbox name +fileinto "${hex:ff}rop"; + +# Not an error +fileinto "Frop"; diff --git a/pigeonhole/tests/compile/errors/header.sieve b/pigeonhole/tests/compile/errors/header.sieve new file mode 100644 index 0000000..1c87f94 --- /dev/null +++ b/pigeonhole/tests/compile/errors/header.sieve @@ -0,0 +1,57 @@ +require "comparator-i;ascii-numeric"; + +/* + * Compile errors for the header test + * + * Total errors: 9 (+1 validation failed msg = 10) + */ + +# Unknown tagged argument +if header :all :comparator "i;ascii-casemap" "From" "nico" { + keep; +} + +# Wrong first argument +if header :is :comparator "i;ascii-numeric" 45 "nico" { + keep; +} + +# Wrong second argument +if header :is :comparator "i;ascii-numeric" "From" 45 { + discard; +} + +# Wrong second argument +if header :is :comparator "i;ascii-numeric" "From" :tag { + stop; +} + +# Missing second argument +if header :is :comparator "i;ascii-numeric" "From" { + stop; +} + +# Missing arguments +if header :is :comparator "i;ascii-numeric" { + keep; +} + +# Not an error +if header :is :comparator "i;ascii-casemap" "frop" ["frop", "frop"] { + discard; +} + +# Spurious sub-test +if header "frop" "frop" true { + discard; +} + +# Test used as command with block +header "frop" "frop" { + discard; +} + +# Test used as command +header "frop" "frop"; + + diff --git a/pigeonhole/tests/compile/errors/if.sieve b/pigeonhole/tests/compile/errors/if.sieve new file mode 100644 index 0000000..6a8537b --- /dev/null +++ b/pigeonhole/tests/compile/errors/if.sieve @@ -0,0 +1,78 @@ +/* + * If command errors + * + * Total errors: 11 (+1 = 12) + */ + +# Spurious argument +if "frop" true {} + +# Spurious argument +elsif "frop" true {} + +# Spurious string list +if [ "false", "false", "false" ] false { + stop; +} + +# No block +if true; + +# No test +if { + keep; +} + +# Spurious test list +if ( false, false, true ) { + keep; +} + +stop; + +# If-less else +else { + keep; +} + +# Not an error +if true { + keep; +} + +stop; + +# If-less if structure (should produce only one error) +elsif true { + keep; +} +elsif true { + keep; +} +else { +} + +# Elsif after else +if true { + keep; +} else { + stop; +} elsif true { + stop; +} + +# If used as test +if if true { +} + +# Else if instead of elsif + +if true { + stop; +} else if false { + keep; +} + + + + diff --git a/pigeonhole/tests/compile/errors/keep.sieve b/pigeonhole/tests/compile/errors/keep.sieve new file mode 100644 index 0000000..7b3397c --- /dev/null +++ b/pigeonhole/tests/compile/errors/keep.sieve @@ -0,0 +1,14 @@ +/* + * Keep errors + * + * Total erors: 2 (+1 = 3) + */ + +# Spurious string argument +keep "frop"; + +# Spurious test +keep true; + +# Not an error +keep; diff --git a/pigeonhole/tests/compile/errors/lexer.sieve b/pigeonhole/tests/compile/errors/lexer.sieve new file mode 100644 index 0000000..9675db1 --- /dev/null +++ b/pigeonhole/tests/compile/errors/lexer.sieve @@ -0,0 +1,68 @@ +/* + * Lexer tests + * + * Total errors: 8 (+1 = 9) + */ + +/* + * Number limits + */ + +# 1: Number too large +if size :under 18446744073709551617 { + stop; +} + +# 2: Number too large +if size :under 18446744073709551616 { + stop; +} + +# 3: Number too large +if size :over 180143985094819840k { + stop; +} + +# 4: Number too large +if size :over 1006622342342296M { + stop; +} + +# 5: Number too large +if size :over 34359738368G { + stop; +} + +# 6: Number far too large +if size :over 49834598293485814273947921734981723971293741923 { + stop; +} + +# Not an error +if size :under 18446744073709551615 { + stop; +} + +# Not an error +if size :under 18446744073709551614 { + stop; +} + +# Not an error +if size :under 800G { + stop; +} + +/* + * Identifier limits + */ + +# 7: Identifier too long +if this_is_a_rediculously_long_test_name { + stop; +} + +# 8: Identifier way too long +if test :this_is_an_even_more_rediculously_long_tagged_argument_name { + stop; +} diff --git a/pigeonhole/tests/compile/errors/match-type.sieve b/pigeonhole/tests/compile/errors/match-type.sieve new file mode 100644 index 0000000..d8e1681 --- /dev/null +++ b/pigeonhole/tests/compile/errors/match-type.sieve @@ -0,0 +1,7 @@ +require "comparator-i;ascii-numeric"; + +if header :contains :comparator "i;ascii-numeric" "from" "friep.example.com" { + keep; +} + +keep; diff --git a/pigeonhole/tests/compile/errors/out-address.sieve b/pigeonhole/tests/compile/errors/out-address.sieve new file mode 100644 index 0000000..3e39599 --- /dev/null +++ b/pigeonhole/tests/compile/errors/out-address.sieve @@ -0,0 +1,33 @@ +require "vacation"; + +# Error + +redirect "@wrong.example.com"; +redirect "error"; +redirect "error@"; +redirect "Stephan Bosch error@example.org"; +redirect "Stephan Bosch <error@example.org"; +redirect " more error @ example.com "; +redirect "@"; +redirect "<>"; +redirect "Error <"; +redirect "Error <stephan"; +redirect "Error <stephan@"; +redirect "stephan@example.org,tss@example.net"; +redirect "stephan@example.org,%&^&!!~"; +redirect "rüdiger@example.com"; + +vacation :from "Error" "Ik ben er niet."; + +# Ok + +redirect "Ok Good <stephan@example.org>"; +redirect "ok@example.com"; +redirect " more @ example.com "; + +redirect ".japanese@example.com"; +redirect "japanese.@example.com"; +redirect "japanese...localpart@example.com"; +redirect "..japanese...localpart..@example.com"; + +vacation :from "good@voorbeeld.nl.example.com" "Ik ben weg!"; diff --git a/pigeonhole/tests/compile/errors/parser.sieve b/pigeonhole/tests/compile/errors/parser.sieve new file mode 100644 index 0000000..26a1e53 --- /dev/null +++ b/pigeonhole/tests/compile/errors/parser.sieve @@ -0,0 +1,78 @@ +/* + * Parser errors + * + * Total errors: 8 (+1 = 9) + */ + +# Too many arguments (1) +frop :this "is" "a" 2 :long "argument" "list" :and :it :should "fail" :during "parsing" :but "it" "should" "be" + "recoverable" "." :this "is" "a" 2 :long "argument" "list" :and :it :should "fail" :during "parsing" :but + "it" "should" "be" "recoverable" { + stop; +} + +# Garbage argument (2) +friep $$$; + +# Deep block nesting (1) +if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + stop; + } } } } } } } } + } } } } } } } } + } } } } } } } } + } } } } } } } } +} } } } } } } } + +# Deepest block and too deep test (list) nesting (1) +if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { + if + anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( + anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( + anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( + anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( + anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( anyof ( + true + )))))))) + )))))))) + )))))))) + )))))))) + )))))))) + { + stop; + } + } } } } } } + } } } } } } } } + } } } } } } } } +} } } } } } } } + +# Deepest block and too deep test nesting (1) +if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { if true { if true { + if true { if true { if true { if true { if true { if true { + if + not not not not not not not not + not not not not not not not not + not not not not not not not not + not not not not not not not not + not not not not not not not not false + { + stop; + } + } } } } } } + } } } } } } } } + } } } } } } } } +} } } } } } } } + + +# Garbage command; test wether previous errors were resolved (2) +frop $$$$; + + diff --git a/pigeonhole/tests/compile/errors/require.sieve b/pigeonhole/tests/compile/errors/require.sieve new file mode 100644 index 0000000..ecbd7a2 --- /dev/null +++ b/pigeonhole/tests/compile/errors/require.sieve @@ -0,0 +1,42 @@ +/* + * Require errors + * + * Total errors: 11 (+1 = 12) + */ + +# Not an error +require "fileinto"; + +# Missing argument +require; + +# Too many arguments +require "fileinto" "vacation"; + +# Invalid argument +require 45; + +# Invalid extensions (3 errors) +require ["_frop", "_friep", "_frml"]; + +# Core commands required +require ["redirect", "keep", "discard"]; + +# Invalid arguments +require "dovecot.test" true; + +# Invalid extension +require "_frop"; + +# Spurious command block +require "fileinto" { + keep; +} + +# Nested require +if true { + require "relional"; +} + +# Require after other command than require +require "copy"; diff --git a/pigeonhole/tests/compile/errors/size.sieve b/pigeonhole/tests/compile/errors/size.sieve new file mode 100644 index 0000000..14dbee5 --- /dev/null +++ b/pigeonhole/tests/compile/errors/size.sieve @@ -0,0 +1,47 @@ +/* + * Size test errors + * + * Total errors: 6 (+1 = 7) + */ + +# Used as command (1) +size :under 23; + +# Missing argument (2) +if size { +} + +# Missing :over/:under (3) +if size 45 { + discard; +} + +# No error +if size :over 34K { + stop; +} + +# No error +if size :under 34M { + stop; +} + +# Conflicting tags (4) +if size :under :over 34 { + keep; +} + +# Duplicate tags (5) +if size :over :over 45M { + stop; +} + +# Wrong argument order (6) +if size 34M :over { + stop; +} + +# No error; but worthy of a warning +if size :under 0 { + stop; +} diff --git a/pigeonhole/tests/compile/errors/stop.sieve b/pigeonhole/tests/compile/errors/stop.sieve new file mode 100644 index 0000000..75a3d76 --- /dev/null +++ b/pigeonhole/tests/compile/errors/stop.sieve @@ -0,0 +1,33 @@ +/* + * Stop command errors + * + * Total errors: 7 (+1 = 8) + */ + +# Spurious string argument +stop "frop"; + +# Spurious number argument +stop 13; + +# Spurious string list argument +stop [ "frop", "frop" ]; + +# Spurious test +stop true; + +# Spurious test list +stop ( true, false ); + +# Spurious command block +stop { + keep; +} + +# Spurious argument and test +stop "frop" true { + stop; +} + +# Not an error +stop; diff --git a/pigeonhole/tests/compile/errors/tag.sieve b/pigeonhole/tests/compile/errors/tag.sieve new file mode 100644 index 0000000..7fa65e9 --- /dev/null +++ b/pigeonhole/tests/compile/errors/tag.sieve @@ -0,0 +1,16 @@ +/* + * Tag errors + * + * Total errors: 2 (+1 = 3) + */ + +# Unknown tag (1) +if envelope :isnot :comparator "i;ascii-casemap" :localpart "From" "nico" { + discard; +} + +# Spurious tag (1) +if true :comparator "i;ascii-numeric" { + keep; +} + diff --git a/pigeonhole/tests/compile/errors/typos.sieve b/pigeonhole/tests/compile/errors/typos.sieve new file mode 100644 index 0000000..3d65b26 --- /dev/null +++ b/pigeonhole/tests/compile/errors/typos.sieve @@ -0,0 +1,29 @@ +/* + * This test is primarily meant to check the compiler's handling of typos + * at various locations. + */ + +require "fileinto"; + +/* + * Missing semicolon + */ + +fileinto "frop" +keep; + +/* Other situations */ + +fileinto "frup" +true; + +fileinto "friep" +snot; + +/* + * Forgot tag colon + */ + +if address matches "from" "*frop*" { + stop; +} diff --git a/pigeonhole/tests/compile/errors/unsupported.sieve b/pigeonhole/tests/compile/errors/unsupported.sieve new file mode 100644 index 0000000..9943f7b --- /dev/null +++ b/pigeonhole/tests/compile/errors/unsupported.sieve @@ -0,0 +1,30 @@ +/* + * Handling of unsupported language features. + * + * Total errors: 3 (+1 = 4) + */ + +require "variables"; +require "include"; +require "regex"; + +/* + * Unsupported use of variables + */ + +/* Comparator argument */ + +set "comp" "i;ascii-numeric"; + +if address :comparator "${comp}" "from" "stephan@example.org" { + stop; +} + +/* Included script */ + +set "script" "blacklist"; + +include "${blacklist}"; + + + diff --git a/pigeonhole/tests/compile/recover.svtest b/pigeonhole/tests/compile/recover.svtest new file mode 100644 index 0000000..9c24c11 --- /dev/null +++ b/pigeonhole/tests/compile/recover.svtest @@ -0,0 +1,50 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Test parser's recover capability + */ + +/* + * Commands + */ + +/* Missing semicolon */ + +test "Missing semicolons" { + if test_script_compile "recover/commands-semicolon.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* End of block recovery*/ + +test "Missing semicolon at end of block" { + if test_script_compile "recover/commands-endblock.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "4" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Tests + */ + +test "Spurious comma at end of test list" { + if test_script_compile "recover/tests-endcomma.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/compile/recover/commands-endblock.sieve b/pigeonhole/tests/compile/recover/commands-endblock.sieve new file mode 100644 index 0000000..c06e218 --- /dev/null +++ b/pigeonhole/tests/compile/recover/commands-endblock.sieve @@ -0,0 +1,27 @@ +if true { + if true { + # Missing semicolon + keep + } +} + +if true { + # Erroneous syntax + keep, + keep +} + +if true { + if anyof(true,true,false) { + keep; + } +} + +if true { + if anyof(true,true,false) { + keep; + # Missing semicolon + discard + } +} + diff --git a/pigeonhole/tests/compile/recover/commands-semicolon.sieve b/pigeonhole/tests/compile/recover/commands-semicolon.sieve new file mode 100644 index 0000000..effb389 --- /dev/null +++ b/pigeonhole/tests/compile/recover/commands-semicolon.sieve @@ -0,0 +1,16 @@ + +keep; + +discard; + +# Missing semicolon +keep + +redirect "frop@nl.example.com"; + +discard; + +# Missing semicolon +keep + +redirect "frml@nl.example.com"; diff --git a/pigeonhole/tests/compile/recover/tests-endcomma.sieve b/pigeonhole/tests/compile/recover/tests-endcomma.sieve new file mode 100644 index 0000000..54c93ec --- /dev/null +++ b/pigeonhole/tests/compile/recover/tests-endcomma.sieve @@ -0,0 +1,17 @@ +if true { + if true { + # Spurious comma + if anyof(true,true,true,) { + } + } +} + +if true { + if anyof(true,true) { + # Spurious comma + if anyof(true,true,true,) { + if anyof(true,true,true) { + } + } + } +} diff --git a/pigeonhole/tests/compile/redirect.sieve b/pigeonhole/tests/compile/redirect.sieve new file mode 100644 index 0000000..fb9f23d --- /dev/null +++ b/pigeonhole/tests/compile/redirect.sieve @@ -0,0 +1,23 @@ +# Test various white space occurrences +redirect "stephan@example.org"; +redirect " stephan@example.org"; +redirect "stephan @example.org"; +redirect "stephan@ example.org"; +redirect "stephan@example.org "; +redirect " stephan @ example.org "; +redirect "Stephan Bosch<stephan@example.org>"; +redirect " Stephan Bosch<stephan@example.org>"; +redirect "Stephan Bosch <stephan@example.org>"; +redirect "Stephan Bosch< stephan@example.org>"; +redirect "Stephan Bosch<stephan @example.org>"; +redirect "Stephan Bosch<stephan@ example.org>"; +redirect "Stephan Bosch<stephan@example.org >"; +redirect "Stephan Bosch<stephan@example.org> "; +redirect " Stephan Bosch < stephan @ example.org > "; + +# Test address syntax +redirect "\"Stephan Bosch\"@example.org"; +redirect "Stephan.Bosch@eXamPle.oRg"; +redirect "Stephan.Bosch@example.org"; +redirect "Stephan Bosch <stephan@example.org>"; + diff --git a/pigeonhole/tests/compile/trivial.sieve b/pigeonhole/tests/compile/trivial.sieve new file mode 100644 index 0000000..a3dcbc1 --- /dev/null +++ b/pigeonhole/tests/compile/trivial.sieve @@ -0,0 +1,17 @@ +# Commands must be case-insensitive +keep; +Keep; +KEEP; +discard; +DisCaRD; + +# Tags must be case-insensitive +if size :UNDER 34 { +} + +if header :Is "from" "tukker@example.com" { +} + +# Numbers must be case-insensitive +if anyof( size :UNDER 34m, size :oVeR 50M ) { +} diff --git a/pigeonhole/tests/compile/warnings.svtest b/pigeonhole/tests/compile/warnings.svtest new file mode 100644 index 0000000..8261551 --- /dev/null +++ b/pigeonhole/tests/compile/warnings.svtest @@ -0,0 +1,8 @@ +require "vnd.dovecot.testsuite"; + +test "EOF Warnings" { + if not test_script_compile "warnings/eof.sieve" { + test_fail "compile should have succeeded."; + } +} + diff --git a/pigeonhole/tests/compile/warnings/eof.sieve b/pigeonhole/tests/compile/warnings/eof.sieve new file mode 100644 index 0000000..cf906dc --- /dev/null +++ b/pigeonhole/tests/compile/warnings/eof.sieve @@ -0,0 +1,2 @@ +keep; +# Final comment without newline diff --git a/pigeonhole/tests/compile/warnings/invalid-headers.sieve b/pigeonhole/tests/compile/warnings/invalid-headers.sieve new file mode 100644 index 0000000..a6b12a8 --- /dev/null +++ b/pigeonhole/tests/compile/warnings/invalid-headers.sieve @@ -0,0 +1,14 @@ +# Header test +if header "from:" "frop@example.org" { + stop; +} + +# Address test +if address "from:" "frop@example.org" { + stop; +} + +# Exists test +if exists "from:" { + stop; +} diff --git a/pigeonhole/tests/control-if.svtest b/pigeonhole/tests/control-if.svtest new file mode 100644 index 0000000..e11de64 --- /dev/null +++ b/pigeonhole/tests/control-if.svtest @@ -0,0 +1,292 @@ +require "vnd.dovecot.testsuite"; + +/* + * ## RFC 5228, Section 3.1. Control if (page 21) ## + */ + +test_set "message" text: +From: stephan@example.org +To: test@dovecot.example.net +Cc: friep@example.com +Subject: Test + +Test! +. +; + +/* + * Basic functionality + */ + +/* "The semantics are similar to those of any of the many other + * programming languages these control structures appear in. When the + * interpreter sees an "if", it evaluates the test associated with it. + * If the test is true, it executes the block associated with it. + * + * If the test of the "if" is false, it evaluates the test of the first + * "elsif" (if any). If the test of "elsif" is true, it runs the + * elsif's block. An elsif may be followed by an elsif, in which case, + * the interpreter repeats this process until it runs out of elsifs. + * + * When the interpreter runs out of elsifs, there may be an "else" case. + * If there is, and none of the if or elsif tests were true, the + * interpreter runs the else's block. + * + * This provides a way of performing exactly one of the blocks in the + * chain. + * " + */ + +/* + * TEST: Basic functionality: if true/false + */ + +test "Basic functionality: if true/false" { + /* Static */ + if true { + /* Correct */ + } else { + test_fail "executed wrong alternative for static true"; + } + + if false { + test_fail "executed wrong alternative for static false"; + } else { + /* Correct */ + } + + /* Dynamic */ + if exists "to" { + /* Correct */ + } else { + test_fail "executed wrong alternative for dynamic true"; + } + + if exists "flierp" { + test_fail "executed wrong alternative for dynamic false"; + } else { + /* Correct */ + } +} + +/* + * TEST: Basic functionality: if not true/false + */ + +test "Basic functionality: if not true/false" { + /* Static */ + if not true { + test_fail "executed wrong alternative for static not true"; + } else { + /* Correct */ + } + + if not false { + /* Correct */ + } else { + test_fail "executed wrong alternative for static not false"; + } + + /* Dynamic */ + if not exists "to" { + test_fail "executed wrong alternative for dynamic not true"; + } else { + /* Correct */ + } + + if not exists "flierp" { + /* Correct */ + } else { + test_fail "executed wrong alternative for dynamic not false"; + } +} + +/* + * TEST: Basic functionality: elseif true/false + */ + +test "Basic functionality: elseif true/false" { + /* Static */ + if true { + /* Correct */ + } elsif true { + test_fail "executed wrong alternative for static true-true (elsif)"; + } else { + test_fail "executed wrong alternative for static true-true (else)"; + } + + if true { + /* Correct */ + } elsif false { + test_fail "executed wrong alternative for static true-false (elsif)"; + } else { + test_fail "executed wrong alternative for static true-false (else)"; + } + + if false { + test_fail "executed wrong alternative for static false-true (if)"; + } elsif true { + /* Correct */ + } else { + test_fail "executed wrong alternative for static false-false (else)"; + } + + if false { + test_fail "executed wrong alternative for static false-false (if)"; + } elsif false { + test_fail "executed wrong alternative for static false-false (elsif)"; + } else { + /* Correct */ + } + + /* Dynamic */ + if address :is "from" "stephan@example.org" { + /* Correct */ + } elsif address :contains "from" "stephan" { + test_fail "executed wrong alternative for dynamic true-true (elsif)"; + } else { + test_fail "executed wrong alternative for dynamic true-true (else)"; + } + + if address :is "from" "stephan@example.org" { + /* Correct */ + } elsif address :is "from" "frop@example.com" { + test_fail "executed wrong alternative for dynamic true-false (elsif)"; + } else { + test_fail "executed wrong alternative for dynamic true-false (else)"; + } + + if address :is "from" "tss@example.net" { + test_fail "executed wrong alternative for dynamic false-true (if)"; + } elsif address :is "from" "stephan@example.org" { + /* Correct */ + } else { + test_fail "executed wrong alternative for dynamic false-true(else)"; + } + + if address :is "from" "tss@example.net" { + test_fail "executed wrong alternative for dynamic false-false (if)"; + } elsif address :is "to" "stephan@example.org" { + test_fail "executed wrong alternative for dynamic false-false (elsif)"; + } else { + /* Correct */ + } + + /* Static/Dynamic */ + + if true { + /* Correct */ + } elsif address :contains "from" "stephan" { + test_fail "executed wrong alternative for first-static true-true (elsif)"; + } else { + test_fail "executed wrong alternative for first-static true-true (else)"; + } + + if address :is "from" "stephan@example.org" { + /* Correct */ + } elsif true { + test_fail "executed wrong alternative for second-static true-true (elsif)"; + } else { + test_fail "executed wrong alternative for second-static true-true (else)"; + } + + if true { + /* Correct */ + } elsif address :is "from" "frop@example.com" { + test_fail "executed wrong alternative for first-static true-false (elsif)"; + } else { + test_fail "executed wrong alternative for first-static true-false (else)"; + } + + if address :is "from" "stephan@example.org" { + /* Correct */ + } elsif false { + test_fail "executed wrong alternative for second-static true-false (elsif)"; + } else { + test_fail "executed wrong alternative for second-static true-false (else)"; + } + + if false { + test_fail "executed wrong alternative for first-static false-true (if)"; + } elsif address :is "from" "stephan@example.org" { + /* Correct */ + } else { + test_fail "executed wrong alternative for first-static false-true(else)"; + } + + if address :is "from" "tss@example.net" { + test_fail "executed wrong alternative for second-static false-true (if)"; + } elsif true { + /* Correct */ + } else { + test_fail "executed wrong alternative for second-static false-true(else)"; + } + + if false { + test_fail "executed wrong alternative for first-static false-false (if)"; + } elsif address :is "to" "stephan@example.org" { + test_fail "executed wrong alternative for first-static false-false (elsif)"; + } else { + /* Correct */ + } + + if address :is "from" "tss@example.net" { + test_fail "executed wrong alternative for second-static false-false (if)"; + } elsif false { + test_fail "executed wrong alternative for second-static false-false (elsif)"; + } else { + /* Correct */ + } +} + +/* + * TEST: Basic functionality: nesting + */ + +test "Basic functionality: nesting" { + /* Static */ + if true { + if true { + if false { + test_fail "chose wrong static outcome: true->true->false"; + } else { + /* Correct */ + } + } else { + test_fail "chose wrong static outcome: true->false"; + } + } elsif true { + if false { + test_fail "chose wrong static outcome: false->true->false"; + } elsif true { + test_fail "chose wrong static outcome: false->true->true"; + } + } else { + test_fail "chose wrong static outcome: false->false"; + } + + /* Dynamic */ + + if exists "to" { + if exists "from" { + if exists "friep" { + test_fail "chose wrong dynamic outcome: true->true->false"; + } else { + /* Correct */ + } + } else { + test_fail "chose wrong dynamic outcome: true->false"; + } + } elsif exists "cc" { + if exists "frop" { + test_fail "chose wrong dynamic outcome: false->true->false"; + } elsif exists "from" { + test_fail "chose wrong dynamic outcome: false->true->true"; + } + } else { + test_fail "chose wrong dynamic outcome: false->false"; + } +} + + + diff --git a/pigeonhole/tests/control-stop.svtest b/pigeonhole/tests/control-stop.svtest new file mode 100644 index 0000000..b49199d --- /dev/null +++ b/pigeonhole/tests/control-stop.svtest @@ -0,0 +1,29 @@ +require "vnd.dovecot.testsuite"; + +/* + * ## RFC 5228, Section 3.3. Control stop (page 22) ## + */ + +/* + * TEST: End processing + */ + +/* "The "stop" action ends all processing. + * " + */ + +test "End processing" { + stop; + + test_fail "continued after stop"; +} + +/* + * TEST: Implicit keep + */ + +/* "If the implicit keep has not been cancelled, then it is taken. + * " + */ + +/* FIXME */ diff --git a/pigeonhole/tests/deprecated/imapflags/errors.svtest b/pigeonhole/tests/deprecated/imapflags/errors.svtest new file mode 100644 index 0000000..a9d9cde --- /dev/null +++ b/pigeonhole/tests/deprecated/imapflags/errors.svtest @@ -0,0 +1,24 @@ +require "vnd.dovecot.testsuite"; + +require "comparator-i;ascii-numeric"; +require "relational"; + +test "Deprecated imapflags extension used with imap4flags" { + if test_script_compile "errors/conflict.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + +test "Deprecated imapflags extension used with imap4flags (ihave)" { + if test_script_compile "errors/conflict-ihave.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/deprecated/imapflags/errors/conflict-ihave.sieve b/pigeonhole/tests/deprecated/imapflags/errors/conflict-ihave.sieve new file mode 100644 index 0000000..e924923 --- /dev/null +++ b/pigeonhole/tests/deprecated/imapflags/errors/conflict-ihave.sieve @@ -0,0 +1,6 @@ +require "imap4flags"; +require "ihave"; + +if ihave "imapflags" { + addflags "Frop"; +} diff --git a/pigeonhole/tests/deprecated/imapflags/errors/conflict.sieve b/pigeonhole/tests/deprecated/imapflags/errors/conflict.sieve new file mode 100644 index 0000000..1b18a42 --- /dev/null +++ b/pigeonhole/tests/deprecated/imapflags/errors/conflict.sieve @@ -0,0 +1,4 @@ +require "imapflags"; +require "imap4flags"; + +addflag "\\flagged"; diff --git a/pigeonhole/tests/deprecated/imapflags/execute.svtest b/pigeonhole/tests/deprecated/imapflags/execute.svtest new file mode 100644 index 0000000..ea6657b --- /dev/null +++ b/pigeonhole/tests/deprecated/imapflags/execute.svtest @@ -0,0 +1,92 @@ +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "imap4flags"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "mailbox"; + +test_set "message" text: +From: Henry von Flockenstoffen <henry@example.com> +To: Dieter von Ausburg <dieter@example.com> +Subject: Test message. + +Test message. +. +; + +test "Mark / Unmark" { + if not test_script_compile "execute/mark.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_result_reset; + + test_message :folder "Marked" 0; + + if not hasflag "\\flagged" { + test_fail "message not marked"; + } + + test_result_reset; + + test_message :folder "Unmarked" 0; + + if hasflag "\\flagged" { + test_fail "message not unmarked"; + } +} + +test_result_reset; +test "Setflag / Addflag / Removeflag" { + if not test_script_compile "execute/flags.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_result_reset; + + test_message :folder "Set" 0; + + if not hasflag "\\draft" { + test_fail "flag not set"; + } + + test_result_reset; + + test_message :folder "Add" 0; + + if not hasflag "\\draft" { + test_fail "flag not retained"; + } + + if not hasflag "\\flagged" { + test_fail "flag not added"; + } + + test_result_reset; + + test_message :folder "Remove" 0; + + if not hasflag "\\flagged" { + test_fail "flag not retained"; + } + + if hasflag "\\draft" { + test_fail "flag not removed"; + } +} diff --git a/pigeonhole/tests/deprecated/imapflags/execute/flags.sieve b/pigeonhole/tests/deprecated/imapflags/execute/flags.sieve new file mode 100644 index 0000000..ba68b44 --- /dev/null +++ b/pigeonhole/tests/deprecated/imapflags/execute/flags.sieve @@ -0,0 +1,12 @@ +require "imapflags"; +require "fileinto"; +require "mailbox"; + +setflag "\\draft"; +fileinto :create "Set"; + +addflag "\\flagged"; +fileinto :create "Add"; + +removeflag "\\draft"; +fileinto :create "Remove"; diff --git a/pigeonhole/tests/deprecated/imapflags/execute/mark.sieve b/pigeonhole/tests/deprecated/imapflags/execute/mark.sieve new file mode 100644 index 0000000..3216ca4 --- /dev/null +++ b/pigeonhole/tests/deprecated/imapflags/execute/mark.sieve @@ -0,0 +1,11 @@ +require "imapflags"; +require "fileinto"; +require "mailbox"; + +mark; + +fileinto :create "Marked"; + +unmark; + +fileinto :create "Unmarked"; diff --git a/pigeonhole/tests/deprecated/notify/basic.svtest b/pigeonhole/tests/deprecated/notify/basic.svtest new file mode 100644 index 0000000..974f8ca --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/basic.svtest @@ -0,0 +1,59 @@ +require "vnd.dovecot.testsuite"; +require "notify"; +require "body"; + +test "Execute" { + /* Test to catch runtime segfaults */ + notify + :message "This is probably very important" + :low + :method "mailto" + :options ["stephan@example.com", "stephan@example.org"]; + + if not test_result_execute { + test_fail "Execute failed"; + } +} + +test_result_reset; + +test_set "message" text: +To: user@example.com +From: stephan@example.org +Subject: Mail + +Test! +. +; + +test "Substitutions" { + notify + :message "$from$: $subject$" + :options "stephan@example.com"; + if not test_result_execute { + test_fail "Execute failed"; + } + test_message :smtp 0; + if not body :contains "stephan@example.org: Mail" { + test_fail "Substitution failed"; + } +} + +test_result_reset; + +test_set "message" text: +To: user@example.com + +Test! +. +; + +test "Empty substitutions" { + notify + :message "$from$: $subject$" + :options "stephan@example.com"; + if not test_result_execute { + test_fail "Execute failed"; + } +} + diff --git a/pigeonhole/tests/deprecated/notify/denotify.svtest b/pigeonhole/tests/deprecated/notify/denotify.svtest new file mode 100644 index 0000000..9f752e1 --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/denotify.svtest @@ -0,0 +1,279 @@ +require "vnd.dovecot.testsuite"; +require "notify"; +require "envelope"; + +/* + * Denotify all + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify All" { + notify :options "timo@example.com"; + notify :options "stephan@dovecot.example.net"; + notify :options "postmaster@frop.example.org"; + denotify; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if test_message :smtp 0 { + test_fail "no notifications should have been sent"; + } +} + +/* + * Denotify First + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify ID First" { + /* #1 */ + notify :options "timo@example.com" :id "aap"; + + /* #2 */ + notify :options "stephan@dovecot.example.net" :id "noot"; + + /* #3 */ + notify :options "postmaster@frop.example.org" :id "mies"; + + denotify :is "aap"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "two notifications should have been sent (#2 missing)"; + } + + if not envelope "to" "stephan@dovecot.example.net" { + test_fail "message #2 unexpectedly missing from output"; + } + + if not test_message :smtp 1 { + test_fail "two notifications should have been sent (#3 missing)"; + } + + if not envelope "to" "postmaster@frop.example.org" { + test_fail "message #3 unexpectedly missing from output"; + } + + if test_message :smtp 2 { + test_fail "too many notifications sent"; + } +} + +/* + * Denotify Middle + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify ID Middle" { + /* #1 */ + notify :options "timo@example.com" :id "aap"; + + /* #2 */ + notify :options "stephan@dovecot.example.net" :id "noot"; + + /* #3 */ + notify :options "postmaster@frop.example.org" :id "mies"; + + denotify :is "noot"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "two notifications should have been sent (#1 missing)"; + } + + if not envelope "to" "timo@example.com" { + test_fail "message #1 unexpectedly missing from output"; + } + + if not test_message :smtp 1 { + test_fail "two notifications should have been sent (#3 missing)"; + } + + if not envelope "to" "postmaster@frop.example.org" { + test_fail "message #3 unexpectedly missing from output"; + } + + if test_message :smtp 2 { + test_fail "too many notifications sent"; + } +} + +/* + * Denotify Last + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify ID Last" { + /* #1 */ + notify :options "timo@example.com" :id "aap"; + + /* #2 */ + notify :options "stephan@dovecot.example.net" :id "noot"; + + /* #3 */ + notify :options "postmaster@frop.example.org" :id "mies"; + + denotify :is "mies"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "two notifications should have been sent (#1 missing)"; + } + + if not envelope "to" "timo@example.com" { + test_fail "message #1 unexpectedly missing from output"; + } + + if not test_message :smtp 1 { + test_fail "two notifications should have been sent (#2 missing)"; + } + + if not envelope "to" "stephan@dovecot.example.net" { + test_fail "message #2 unexpectedly missing from output"; + } + + if test_message :smtp 2 { + test_fail "too many notifications sent"; + } +} + + +/* + * Denotify Matching + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify Matching" { + /* #1 */ + notify :options "timo@example.com" :id "frop"; + + /* #2 */ + notify :options "stephan@dovecot.example.net" :id "noot"; + + /* #3 */ + notify :options "postmaster@frop.example.org" :id "friep"; + + denotify :matches "fr*"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "one notification should have been sent"; + } + + if not envelope "to" "stephan@dovecot.example.net" { + test_fail "message #2 unexpectedly missing from output"; + } + + if test_message :smtp 1 { + test_fail "too many notifications sent"; + } +} + + +/* + * Denotify Matching + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify Matching Importance" { + /* #1 */ + notify :options "timo@example.com" :id "frop" :low; + + /* #2 */ + notify :options "stephan@dovecot.example.net" :id "frml" :high; + + /* #3 */ + notify :options "postmaster@frop.example.org" :id "friep" :low; + + denotify :matches "fr*" :low; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "one notification should have been sent"; + } + + if not envelope "to" "stephan@dovecot.example.net" { + test_fail "message #2 unexpectedly missing from output"; + } + + if test_message :smtp 1 { + test_fail "too many notifications sent"; + } +} + + diff --git a/pigeonhole/tests/deprecated/notify/errors.svtest b/pigeonhole/tests/deprecated/notify/errors.svtest new file mode 100644 index 0000000..549cb6b --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/errors.svtest @@ -0,0 +1,33 @@ +require "vnd.dovecot.testsuite"; +require "comparator-i;ascii-numeric"; +require "relational"; + +test "Invalid :options argument (FIXME: count only)" { + if test_script_compile "errors/options.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +test "Deprecated notify extension used with enotify" { + if test_script_compile "errors/conflict.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +test "Deprecated notify extension used with enotify (ihave)" { + if test_script_compile "errors/conflict-ihave.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/deprecated/notify/errors/conflict-ihave.sieve b/pigeonhole/tests/deprecated/notify/errors/conflict-ihave.sieve new file mode 100644 index 0000000..9686f03 --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/errors/conflict-ihave.sieve @@ -0,0 +1,8 @@ +require "enotify"; +require "ihave"; + +# 1: Conflict +if ihave "notify" { + # 2: Syntax wrong for enotify (and not skipped in compile) + notify :options "frop@frop.example.org"; +} diff --git a/pigeonhole/tests/deprecated/notify/errors/conflict.sieve b/pigeonhole/tests/deprecated/notify/errors/conflict.sieve new file mode 100644 index 0000000..46a6283 --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/errors/conflict.sieve @@ -0,0 +1,4 @@ +require "enotify"; +require "notify"; + +notify :options "frop@frop.example.org"; diff --git a/pigeonhole/tests/deprecated/notify/errors/options.sieve b/pigeonhole/tests/deprecated/notify/errors/options.sieve new file mode 100644 index 0000000..c86fea0 --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/errors/options.sieve @@ -0,0 +1,11 @@ +require "notify"; + +# 1: empty option +notify :options ""; + +# 2: invalid address syntax +notify :options "frop#frop.example.org"; + +# Valid +notify :options "frop@frop.example.org"; + diff --git a/pigeonhole/tests/deprecated/notify/execute.svtest b/pigeonhole/tests/deprecated/notify/execute.svtest new file mode 100644 index 0000000..90fde47 --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/execute.svtest @@ -0,0 +1,25 @@ +require "vnd.dovecot.testsuite"; +require "relational"; + + +/* + * Execution testing (currently just meant to trigger any segfaults) + */ + +test "Duplicate recipients" { + if not test_script_compile "execute/duplicates.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if test_result_action :count "ne" "2" { + test_fail "second notify action was discarded entirely"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} diff --git a/pigeonhole/tests/deprecated/notify/execute/duplicates.sieve b/pigeonhole/tests/deprecated/notify/execute/duplicates.sieve new file mode 100644 index 0000000..ef3fa5f --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/execute/duplicates.sieve @@ -0,0 +1,4 @@ +require "notify"; + +notify :message "Incoming stupidity." :options ["stephan@example.org", "stephan@friep.example.com", "idiot@example.org"]; +notify :message "There it is." :options ["tss@example.net", "stephan@example.org", "idiot@example.org", "nico@frop.example.org", "stephan@friep.example.com"]; diff --git a/pigeonhole/tests/deprecated/notify/mailto.svtest b/pigeonhole/tests/deprecated/notify/mailto.svtest new file mode 100644 index 0000000..1724339 --- /dev/null +++ b/pigeonhole/tests/deprecated/notify/mailto.svtest @@ -0,0 +1,317 @@ +require "vnd.dovecot.testsuite"; + +require "notify"; +require "body"; +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Simple test + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Simple" { + notify :method "mailto" :options "stephan@example.org"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not header :matches "Auto-Submitted" "auto-generated*" { + test_fail "auto-submitted header set inappropriately"; + } + + if not exists "X-Sieve" { + test_fail "x-sieve header missing from outgoing message"; + } +} + +/* + * Multiple recipients + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Multiple recipients" { + notify :options ["timo@example.com","stephan@dovecot.example.net","postmaster@frop.example.org"]; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :is "to" "timo@example.com" { + test_fail "first To address missing"; + } + + test_message :smtp 1; + + if not address :is "to" "stephan@dovecot.example.net" { + test_fail "second To address missing"; + } + + if not header :matches "Auto-Submitted" "auto-generated*" { + test_fail "auto-submitted header not found for second message"; + } + + test_message :smtp 2; + + if not address :is "to" "postmaster@frop.example.org" { + test_fail "third To address missing"; + } + + if not header :matches "Auto-Submitted" "auto-generated*" { + test_fail "auto-submitted header not found for third message"; + } + + if not address :count "eq" :comparator "i;ascii-numeric" "to" "3" { + test_fail "wrong number of recipients in To header"; + } + + if not address :count "eq" :comparator "i;ascii-numeric" "cc" "0" { + test_fail "too many recipients in Cc header"; + } +} + +/* + * Duplicate recipients + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Duplicate recipients" { + notify :options ["timo@example.com", "stephan@dovecot.example.net", "stephan@dovecot.example.net"]; + notify :options ["timo@example.com", "stephan@example.org"]; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 2; + + if address "To" "stephan@dovecot.example.net" { + test_fail "duplicate recipient not removed from first message"; + } + + if address "To" "timo@example.com" { + test_fail "duplicate recipient not removed from second message"; + } +} + +/* + * Notifying on automated messages + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Auto-submitted: auto-notify +Subject: Frop! + +Klutsefluts. +. +; + +test "Notifying on automated messages" { + notify :options "stephan@example.org"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if test_message :smtp 0 { + test_fail "notified of auto-submitted message"; + } +} + +test_result_reset; + +test_set "message" text: +To: nico@frop.example.org +From: stephan@example.org +Subject: Test + +Test. Test +Frop! +. +; + +test "Body; Singular Message" { + notify :low :id "frop" :options "stephan@example.org" + :message text: +Received interesting message: + +$text$ + +You have been notified. +. +; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Received interesting message" { + test_fail "notification has no heading"; + } + + if not body :raw :contains "You have been notified" { + test_fail "notification has no footer"; + } + + if not allof( + body :raw :contains "Test. Test", + body :raw :contains "Frop" ) { + test_fail "notification has no original message"; + } +} + +test_result_reset; + +test_set "message" text: +To: nico@frop.example.org +From: stephan@example.org +Subject: Test + +Test. Test +Frop! +. +; + +test "Body; $text[maxsize]$" { + notify :low :id "frop" :options "sirius@example.org" + :message text: +Received interesting message: + +$text[5]$ + +You have been notified. +. +; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Received interesting message" { + test_fail "notification has no heading"; + } + + if not body :raw :contains "You have been notified" { + test_fail "notification has no footer"; + } + + if anyof( + body :raw :contains "Test. Test", + body :raw :contains "Frop" ) { + test_fail "original message in notification is not truncated"; + } + + if not body :raw :contains "Test." { + test_fail "notification does not contain the required message"; + } +} + +test_result_reset; + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: multipart/alternative; boundary=inner + +This is a nested multi-part message in MIME format. + +--inner +Content-Type: application/sieve; charset="us-ascii" + +keep; + +--inner +Content-Type: text/plain; charset="us-ascii" + +Friep! + +--inner-- + +This is the end of the inner MIME multipart. + +--outer +Content-Type: message/rfc822 + +From: Someone Else +Subject: hello request + +Please say Hello + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +test "Body; Multipart Message" { + notify :low :id "frop" :options "stephan@example.org" + :message text: +Received interesting message: + +$text$ + +You have been notified. +. +; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Friep!" { + test_fail "notification has incorrect content"; + } +} + + + diff --git a/pigeonhole/tests/execute/actions.svtest b/pigeonhole/tests/execute/actions.svtest new file mode 100644 index 0000000..3f517fa --- /dev/null +++ b/pigeonhole/tests/execute/actions.svtest @@ -0,0 +1,80 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "comparator-i;ascii-numeric"; + +test_set "message" text: +To: nico@frop.example.org +From: stephan@example.org +Subject: Test + +Test. +. +; + +test_mailbox_create "INBOX.VB"; +test_mailbox_create "INBOX.backup"; + +test "Fileinto" { + if not test_script_compile "actions/fileinto.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_action :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of actions in result"; + } + + if not test_result_action :index 1 "store" { + test_fail "first action is not 'store'"; + } + + if not test_result_action :index 2 "store" { + test_fail "second action is not 'store'"; + } + + if not test_result_action :index 3 "keep" { + test_fail "third action is not 'keep'"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +test "Redirect" { + if not test_script_compile "actions/redirect.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execute failed"; + } + + if not test_result_action :count "eq" :comparator "i;ascii-numeric" "4" { + test_fail "wrong number of actions in result"; + } + + if not test_result_action :index 1 "redirect" { + test_fail "first action is not 'redirect'"; + } + + if not test_result_action :index 2 "keep" { + test_fail "second action is not 'keep'"; + } + + if not test_result_action :index 3 "redirect" { + test_fail "third action is not 'redirect'"; + } + + if not test_result_action :index 4 "redirect" { + test_fail "fourth action is not 'redirect'"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + diff --git a/pigeonhole/tests/execute/actions/fileinto.sieve b/pigeonhole/tests/execute/actions/fileinto.sieve new file mode 100644 index 0000000..e9c133b --- /dev/null +++ b/pigeonhole/tests/execute/actions/fileinto.sieve @@ -0,0 +1,17 @@ +require "fileinto"; + +/* Three store actions */ + +if address :contains "to" "frop.example" { + /* #1 */ + fileinto "INBOX.VB"; +} + +/* #2 */ +fileinto "INBOX.backup"; + +/* #3 */ +keep; + +/* Duplicate of keep */ +fileinto "INBOX"; diff --git a/pigeonhole/tests/execute/actions/redirect.sieve b/pigeonhole/tests/execute/actions/redirect.sieve new file mode 100644 index 0000000..7adc23e --- /dev/null +++ b/pigeonhole/tests/execute/actions/redirect.sieve @@ -0,0 +1,17 @@ +if address :contains "to" "frop.example" { + /* #1 */ + redirect "stephan@example.com"; + + /* #2 */ + keep; +} + +/* #3 */ +redirect "stephan@example.org"; + +/* #4 */ +redirect "nico@example.nl"; + +/* Duplicates */ +redirect "Stephan Bosch <stephan@example.com>"; +keep; diff --git a/pigeonhole/tests/execute/address-normalize.svtest b/pigeonhole/tests/execute/address-normalize.svtest new file mode 100644 index 0000000..e826bde --- /dev/null +++ b/pigeonhole/tests/execute/address-normalize.svtest @@ -0,0 +1,46 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; + +test_set "message" text: +From: tss@example.net +To: stephan@example.org +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.net"; +test_set "envelope.to" "\"sirius\"@example.org"; + +/* + * Mail address normalization - redirect + */ + +test "Mail address normalization - redirect" { + redirect "\"S[r]us\"@example.net"; + redirect "\"Sirius\"@example.net"; + redirect "\"Stephan Bosch\" <\"S.Bosch\"@example.net>"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if not envelope :is "to" "\"S[r]us\"@example.net" { + test_fail "envelope recipient incorrect"; + } + + test_message :smtp 1; + + if not envelope :is "to" "Sirius@example.net" { + test_fail "envelope recipient incorrect"; + } + + test_message :smtp 2; + + if not envelope :is "to" "S.Bosch@example.net" { + test_fail "envelope recipient incorrect"; + } +} diff --git a/pigeonhole/tests/execute/errors-cpu-limit.svtest b/pigeonhole/tests/execute/errors-cpu-limit.svtest new file mode 100644 index 0000000..4a045bc --- /dev/null +++ b/pigeonhole/tests/execute/errors-cpu-limit.svtest @@ -0,0 +1,363 @@ +require "vnd.dovecot.testsuite"; + +test_config_set "sieve_max_cpu_time" "2"; +test_config_reload; + +test_set "message" text: +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary= + +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +-- +. +; + +test "CPU limit" { + if not test_script_compile "errors/cpu-limit.sieve" { + test_fail "script compile failed"; + } + + if test_script_run { + test_fail "script execute should have failed"; + } +} + diff --git a/pigeonhole/tests/execute/errors.svtest b/pigeonhole/tests/execute/errors.svtest new file mode 100644 index 0000000..45bc39c --- /dev/null +++ b/pigeonhole/tests/execute/errors.svtest @@ -0,0 +1,152 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; +require "fileinto"; + +test "Action conflicts: reject <-> fileinto" { + if not test_script_compile "errors/conflict-reject-fileinto.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } +} + +test "Action conflicts: reject <-> keep" { + if not test_script_compile "errors/conflict-reject-keep.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } +} + +test "Action conflicts: reject <-> redirect" { + if not test_script_compile "errors/conflict-reject-redirect.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } +} + +test "Action limit" { + if not test_script_compile "errors/actions-limit.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } + + if not test_error :index 1 :contains "total number of actions exceeds policy limit"{ + test_fail "unexpected error reported"; + } +} + +test "Redirect limit" { + if not test_script_compile "errors/redirect-limit.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } + + if not test_error :index 1 :contains "number of redirect actions exceeds policy limit"{ + test_fail "unexpected error reported"; + } +} + +test "Fileinto missing folder" { + if not test_script_compile "errors/fileinto.sieve" { + test_fail "compile failed"; + } + + test_mailbox_create "INBOX"; + + if not test_script_run { + test_fail "execution failed"; + } + + if test_result_execute { + test_fail "execution of result should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } + + if not allof ( + test_error :index 1 :contains "failed to store into mailbox", + test_error :index 1 :contains "exist", + test_error :index 1 :contains "FROP") { + test_fail "unexpected error reported"; + } +} + +test "Fileinto invalid folder name" { + if not test_script_compile "errors/fileinto-invalid-name.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execution failed"; + } + + if test_result_execute { + test_fail "execution of result should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of runtime errors reported"; + } + + if not allof ( + test_error :index 1 :contains "failed to store into mailbox", + test_error :index 1 :contains "name") { + test_fail "unexpected error reported"; + } +} + +test "Fileinto bad UTF-8 in folder name" { + if not test_script_compile "errors/fileinto-bad-utf8.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of runtime errors reported"; + } + + if not test_error :index 1 :contains "invalid folder name" { + test_fail "unexpected error reported"; + } +} diff --git a/pigeonhole/tests/execute/errors/action-duplicates.sieve b/pigeonhole/tests/execute/errors/action-duplicates.sieve new file mode 100644 index 0000000..6d5370d --- /dev/null +++ b/pigeonhole/tests/execute/errors/action-duplicates.sieve @@ -0,0 +1,4 @@ +require "reject"; + +reject "Message is not appreciated."; +reject "No, really, it is not appreciated."; diff --git a/pigeonhole/tests/execute/errors/actions-limit.sieve b/pigeonhole/tests/execute/errors/actions-limit.sieve new file mode 100644 index 0000000..3ae33a3 --- /dev/null +++ b/pigeonhole/tests/execute/errors/actions-limit.sieve @@ -0,0 +1,35 @@ +require "fileinto"; + +fileinto "box1"; +fileinto "box2"; +fileinto "box3"; +fileinto "box4"; +fileinto "box5"; +fileinto "box6"; +fileinto "box7"; +fileinto "box8"; +fileinto "box9"; +fileinto "box10"; +fileinto "box11"; +fileinto "box12"; +fileinto "box13"; +fileinto "box14"; +fileinto "box15"; +fileinto "box16"; +fileinto "box17"; +fileinto "box18"; +fileinto "box19"; +fileinto "box20"; +fileinto "box21"; +fileinto "box22"; +fileinto "box23"; +fileinto "box24"; +fileinto "box25"; +fileinto "box26"; +fileinto "box27"; +fileinto "box28"; +redirect "address1@example.com"; +redirect "address2@example.com"; +redirect "address3@example.com"; +redirect "address4@example.com"; +keep; diff --git a/pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve b/pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve new file mode 100644 index 0000000..85ef139 --- /dev/null +++ b/pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve @@ -0,0 +1,5 @@ +require "reject"; +require "fileinto"; + +reject "No nonsense in my mailbox."; +fileinto "Spam"; diff --git a/pigeonhole/tests/execute/errors/conflict-reject-keep.sieve b/pigeonhole/tests/execute/errors/conflict-reject-keep.sieve new file mode 100644 index 0000000..569a4ac --- /dev/null +++ b/pigeonhole/tests/execute/errors/conflict-reject-keep.sieve @@ -0,0 +1,4 @@ +require "reject"; + +reject "I am not interested in your nonsense."; +keep; diff --git a/pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve b/pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve new file mode 100644 index 0000000..d012269 --- /dev/null +++ b/pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve @@ -0,0 +1,4 @@ +require "reject"; + +reject "I am not interested in your nonsense."; +redirect "frop@example.com"; diff --git a/pigeonhole/tests/execute/errors/cpu-limit.sieve b/pigeonhole/tests/execute/errors/cpu-limit.sieve new file mode 100644 index 0000000..8532a4b --- /dev/null +++ b/pigeonhole/tests/execute/errors/cpu-limit.sieve @@ -0,0 +1,145 @@ +require ["mime","foreverypart","fileinto", "variables", "regex"]; + +# Here we create an inefficient regex with long compilation time +set "my_exp" "^(((A)|(AB)|(ABC)|(ABCD)|(ABCDE)|(ABCDEF)|(ABCDEFG)|(ABCDEFGH)|(ABCDEFGHI)|(ABCDEFGHIJ)|(ABCDEFGHIJK)|(ABCDEFGHIJKL)|(ABCDEFGHIJKLM)|(ABCDEFGHIJKLMN)|(ABCDEFGHIJKLMNO)|(ABCDEFGHIJKLMNOP)|(ABCDEFGHIJKLMNOPQ)|(ABCDEFGHIJKLMNOPQR))?((B)|(BC)|(BCD)|(BCDE)|(BCDEF)|(BCDEFG)|(BCDEFGH)|(BCDEFGHI)|(BCDEFGHIJ)|(BCDEFGHIJK)|(BCDEFGHIJKL)|(BCDEFGHIJKLM)|(BCDEFGHIJKLMN)|(BCDEFGHIJKLMNO)|(BCDEFGHIJKLMNOP)|(BCDEFGHIJKLMNOPQ)|(BCDEFGHIJKLMNOPQR))?((C)|(CD)|(CDE)|(CDEF)|(CDEFG)|(CDEFGH)|(CDEFGHI)|(CDEFGHIJ)|(CDEFGHIJK)|(CDEFGHIJKL)|(CDEFGHIJKLM)|(CDEFGHIJKLMN)|(CDEFGHIJKLMNO)|(CDEFGHIJKLMNOP)|(CDEFGHIJKLMNOPQ)|(CDEFGHIJKLMNOPQR))?((D)|(DE)|(DEF)|(DEFG)|(DEFGH)|(DEFGHI)|(DEFGHIJ)|(DEFGHIJK)|(DEFGHIJKL)|(DEFGHIJKLM)|(DEFGHIJKLMN)|(DEFGHIJKLMNO)|(DEFGHIJKLMNOP)|(DEFGHIJKLMNOPQ)|(DEFGHIJKLMNOPQR))?((E)|(EF)|(EFG)|(EFGH)|(EFGHI)|(EFGHIJ)|(EFGHIJK)|(EFGHIJKL)|(EFGHIJKLM)|(EFGHIJKLMN)|(EFGHIJKLMNO)|(EFGHIJKLMNOP)|(EFGHIJKLMNOPQ)|(EFGHIJKLMNOPQR))?((F)|(FG)|(FGH)|(FGHI)|(FGHIJ)|(FGHIJK)|(FGHIJKL)|(FGHIJKLM)|(FGHIJKLMN)|(FGHIJKLMNO)|(FGHIJKLMNOP)|(FGHIJKLMNOPQ)|(FGHIJKLMNOPQR))?((G)|(GH)|(GHI)|(GHIJ)|(GHIJK)|(GHIJKL)|(GHIJKLM)|(GHIJKLMN)|(GHIJKLMNO)|(GHIJKLMNOP)|(GHIJKLMNOPQ)|(GHIJKLMNOPQR))?((H)|(HI)|(HIJ)|(HIJK)|(HIJKL)|(HIJKLM)|(HIJKLMN)|(HIJKLMNO)|(HIJKLMNOP)|(HIJKLMNOPQ)|(HIJKLMNOPQR))?((I)|(IJ)|(IJK)|(IJKL)|(IJKLM)|(IJKLMN)|(IJKLMNO)|(IJKLMNOP)|(IJKLMNOPQ)|(IJKLMNOPQR))?((J)|(JK)|(JKL)|(JKLM)|(JKLMN)|(JKLMNO)|(JKLMNOP)|(JKLMNOPQ)|(JKLMNOPQR))?((K)|(KL)|(KLM)|(KLMN)|(KLMNO)|(KLMNOP)|(KLMNOPQ)|(KLMNOPQR))?((L)|(LM)|(LMN)|(LMNO)|(LMNOP)|(LMNOPQ)|(LMNOPQR))?((M)|(MN)|(MNO)|(MNOP)|(MNOPQ)|(MNOPQR))?((N)|(NO)|(NOP)|(NOPQ)|(NOPQR))?((O)|(OP)|(OPQ)|(OPQR))?((P)|(PQ)|(PQR))?((Q)|(QR))?((R))?((R)|(RQ)|(RQP)|(RQPO)|(RQPON)|(RQPONM)|(RQPONML)|(RQPONMLK)|(RQPONMLKJ)|(RQPONMLKJI)|(RQPONMLKJIH)|(RQPONMLKJIHG)|(RQPONMLKJIHGF)|(RQPONMLKJIHGFE)|(RQPONMLKJIHGFED)|(RQPONMLKJIHGFEDC)|(RQPONMLKJIHGFEDCB)|(RQPONMLKJIHGFEDCBA))?((Q)|(QP)|(QPO)|(QPON)|(QPONM)|(QPONML)|(QPONMLK)|(QPONMLKJ)|(QPONMLKJI)|(QPONMLKJIH)|(QPONMLKJIHG)|(QPONMLKJIHGF)|(QPONMLKJIHGFE)|(QPONMLKJIHGFED)|(QPONMLKJIHGFEDC)|(QPONMLKJIHGFEDCB)|(QPONMLKJIHGFEDCBA))?((P)|(PO)|(PON)|(PONM)|(PONML)|(PONMLK)|(PONMLKJ)|(PONMLKJI)|(PONMLKJIH)|(PONMLKJIHG)|(PONMLKJIHGF)|(PONMLKJIHGFE)|(PONMLKJIHGFED)|(PONMLKJIHGFEDC)|(PONMLKJIHGFEDCB)|(PONMLKJIHGFEDCBA))?((O)|(ON)|(ONM)|(ONML)|(ONMLK)|(ONMLKJ)|(ONMLKJI)|(ONMLKJIH)|(ONMLKJIHG)|(ONMLKJIHGF)|(ONMLKJIHGFE)|(ONMLKJIHGFED)|(ONMLKJIHGFEDC)|(ONMLKJIHGFEDCB)|(ONMLKJIHGFEDCBA))?((N)|(NM)|(NML)|(NMLK)|(NMLKJ)|(NMLKJI)|(NMLKJIH)|(NMLKJIHG)|(NMLKJIHGF)|(NMLKJIHGFE)|(NMLKJIHGFED)|(NMLKJIHGFEDC)|(NMLKJIHGFEDCB)|(NMLKJIHGFEDCBA))?((M)|(ML)|(MLK)|(MLKJ)|(MLKJI)|(MLKJIH)|(MLKJIHG)|(MLKJIHGF)|(MLKJIHGFE)|(MLKJIHGFED)|(MLKJIHGFEDC)|(MLKJIHGFEDCB)|(MLKJIHGFEDCBA))?((L)|(LK)|(LKJ)|(LKJI)|(LKJIH)|(LKJIHG)|(LKJIHGF)|(LKJIHGFE)|(LKJIHGFED)|(LKJIHGFEDC)|(LKJIHGFEDCB)|(LKJIHGFEDCBA))?((K)|(KJ)|(KJI)|(KJIH)|(KJIHG)|(KJIHGF)|(KJIHGFE)|(KJIHGFED)|(KJIHGFEDC)|(KJIHGFEDCB)|(KJIHGFEDCBA))?((J)|(JI)|(JIH)|(JIHG)|(JIHGF)|(JIHGFE)|(JIHGFED)|(JIHGFEDC)|(JIHGFEDCB)|(JIHGFEDCBA))?((I)|(IH)|(IHG)|(IHGF)|(IHGFE)|(IHGFED)|(IHGFEDC)|(IHGFEDCB)|(IHGFEDCBA))?((H)|(HG)|(HGF)|(HGFE)|(HGFED)|(HGFEDC)|(HGFEDCB)|(HGFEDCBA))?((G)|(GF)|(GFE)|(GFED)|(GFEDC)|(GFEDCB)|(GFEDCBA))?((F)|(FE)|(FED)|(FEDC)|(FEDCB)|(FEDCBA))?((E)|(ED)|(EDC)|(EDCB)|(EDCBA))?((D)|(DC)|(DCB)|(DCBA))?((C)|(CB)|(CBA))?((B)|(BA))?((A))?)+$"; +set "a" "ABCDEFGHIJKLMNOPQR"; +set "b" "RQPONMLKJIHGFEDCBA"; +set "c" "${a}${b}${a}${b}${a}${b}${a}${b}"; +set "e" "${c}${c}${c}${c}${c}${c}${c}${c}"; +set "f" "${e}${e}${e}${e}${e}${e}${e}${e}"; + +# We create a string on which this regex will spend enough time (around 200 ms) +set "final" "${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}@"; + +# We repeat the throttling process for every mime part +foreverypart { + # We use several if statements to multiply the cpu time consumed by one match + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } + if string :regex "${final}" "${my_exp}" { discard; } + if string :regex "${final}" "${my_exp}" { keep; } +} diff --git a/pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve b/pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve new file mode 100644 index 0000000..3e57c92 --- /dev/null +++ b/pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve @@ -0,0 +1,7 @@ +require "fileinto"; +require "variables"; +require "encoded-character"; + +set "mailbox" "${hex:ff}rop"; +fileinto "${mailbox}"; + diff --git a/pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve b/pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve new file mode 100644 index 0000000..871323e --- /dev/null +++ b/pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve @@ -0,0 +1,5 @@ +require "fileinto"; +require "mailbox"; + +fileinto :create "foo//somedomain/org"; + diff --git a/pigeonhole/tests/execute/errors/fileinto.sieve b/pigeonhole/tests/execute/errors/fileinto.sieve new file mode 100644 index 0000000..185674c --- /dev/null +++ b/pigeonhole/tests/execute/errors/fileinto.sieve @@ -0,0 +1,3 @@ +require "fileinto"; + +fileinto "FROP"; diff --git a/pigeonhole/tests/execute/errors/redirect-limit.sieve b/pigeonhole/tests/execute/errors/redirect-limit.sieve new file mode 100644 index 0000000..86cfda0 --- /dev/null +++ b/pigeonhole/tests/execute/errors/redirect-limit.sieve @@ -0,0 +1,5 @@ +redirect "address1@example.com"; +redirect "address2@example.com"; +redirect "address3@example.com"; +redirect "address4@example.com"; +redirect "address5@example.com"; diff --git a/pigeonhole/tests/execute/examples.svtest b/pigeonhole/tests/execute/examples.svtest new file mode 100644 index 0000000..6143018 --- /dev/null +++ b/pigeonhole/tests/execute/examples.svtest @@ -0,0 +1,115 @@ +require "vnd.dovecot.testsuite"; + +/* Compile and execute all example scripts to trigger + * any Segfaults. No message is set and no results are checked. + */ + +test "Elvey example" { + if not test_script_compile "../../examples/elvey.sieve" { + test_fail "could not compile"; + } + + test_binary_save "elvey"; + test_binary_load "elvey"; + + if not test_script_run { } +} + +test "M. Johnson example" { + if not test_script_compile "../../examples/mjohnson.sieve" { + test_fail "could not compile"; + } + + test_binary_save "mjohnson"; + test_binary_load "mjohnson"; + + if not test_script_run { } +} + +test "RFC 3028 example" { + if not test_script_compile "../../examples/rfc3028.sieve" { + test_fail "could not compile"; + } + + test_binary_save "rfc3028"; + test_binary_load "rfc3028"; + + if not test_script_run { } +} + +test "Sieve examples" { + if not test_script_compile "../../examples/sieve_examples.sieve" { + test_fail "could not compile"; + } + + test_binary_save "sieve_examples"; + test_binary_load "sieve_examples"; + + if not test_script_run { } +} + +test "Vivil example" { + if not test_script_compile "../../examples/vivil.sieve" { + test_fail "could not compile"; + } + + test_binary_save "vivil"; + test_binary_load "vivil"; + + if not test_script_run { } +} + +test "Jerry example" { + if not test_script_compile "../../examples/jerry.sieve" { + test_fail "could not compile"; + } + + test_binary_save "jerry"; + test_binary_load "jerry"; + + if not test_script_run { } +} + +test "M. Klose example" { + if not test_script_compile "../../examples/mklose.sieve" { + test_fail "could not compile"; + } + + test_binary_save "mklose"; + test_binary_load "mklose"; + + if not test_script_run { } +} + +test "Sanjay example" { + if not test_script_compile "../../examples/sanjay.sieve" { + test_fail "could not compile"; + } + + test_binary_save "sanjay"; + test_binary_load "sanjay"; + + if not test_script_run { } +} + +test "Relational (RFC5231) example" { + if not test_script_compile "../../examples/relational.rfc5231.sieve" { + test_fail "could not compile"; + } + + test_binary_save "relational"; + test_binary_load "relational"; + + if not test_script_run { } +} + +test "Subaddress (RFC5233) example" { + if not test_script_compile "../../examples/subaddress.rfc5233.sieve" { + test_fail "could not compile"; + } + + test_binary_save "subaddress"; + test_binary_load "subaddress"; + + if not test_script_run { } +} diff --git a/pigeonhole/tests/execute/mailstore.svtest b/pigeonhole/tests/execute/mailstore.svtest new file mode 100644 index 0000000..d6cc220 --- /dev/null +++ b/pigeonhole/tests/execute/mailstore.svtest @@ -0,0 +1,84 @@ +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "variables"; +require "mailbox"; + +set "message1" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: First message + +Frop +. +; + +set "message2" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Second message + +Frop +. +; + +set "message3" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Third message + +Frop +. +; + +test "Duplicates" { + test_set "message" "${message1}"; + + fileinto :create "Folder"; + fileinto :create "Folder"; + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_result_reset; + + test_set "message" "${message2}"; + + fileinto :create "Folder"; + fileinto :create "Folder"; + + if not test_result_execute { + test_fail "failed to execute second result"; + } + + test_result_reset; + + test_set "message" "${message3}"; + + fileinto :create "Folder"; + fileinto :create "Folder"; + + if not test_result_execute { + test_fail "failed to execute third result"; + } + + test_message :folder "Folder" 0; + + if not header :is "subject" "First message" { + test_fail "first message incorrect"; + } + + test_message :folder "Folder" 1; + + if not header :is "subject" "Second message" { + test_fail "first message incorrect"; + } + + test_message :folder "Folder" 2; + + if not header :is "subject" "Third message" { + test_fail "first message incorrect"; + } +} + + diff --git a/pigeonhole/tests/execute/smtp.svtest b/pigeonhole/tests/execute/smtp.svtest new file mode 100644 index 0000000..2c7d2e6 --- /dev/null +++ b/pigeonhole/tests/execute/smtp.svtest @@ -0,0 +1,449 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test "Redirect" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if not address :is "to" "tss@example.net" { + test_fail "to address incorrect (strange forward)"; + } + + if not address :is "from" "stephan@example.org" { + test_fail "from address incorrect (strange forward)"; + } + + if not envelope :is "to" "cras@example.net" { + test_fail "envelope recipient incorrect"; + } + + if not envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "<>"; +test_set "envelope.to" "timo@example.net"; + +test "Redirect from <>" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test_config_set "sieve_redirect_envelope_from" " recipient "; +test_config_reload; + +test "Redirect from [recipient]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "timo@example.net" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "orig_recipient "; +test_config_reload; + +test "Redirect from [original recipient]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "tss@example.net" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "<backscatter@example.net> "; +test_config_reload; + +test "Redirect from [<explicit>]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "backscatter@example.net" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test_config_set "sieve_redirect_envelope_from" "<>"; +test_config_reload; + +test "Redirect from [<>]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "<>"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "<backscatter@example.net>"; +test_config_reload; + +test "Redirect from <> with [<explicit>]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "backscatter@example.net" { + test_fail "envelope sender incorrect (erroneously changed)"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "user_email"; +test_config_reload; + +test "Redirect from [user email - fallback default]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if not envelope :is "from" "timo@example.net" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "timo@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +test_result_reset; +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "user_email"; +test_config_set "sieve_user_email" "t.sirainen@example.net"; +test_config_reload; + +test "Redirect from [user email]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "t.sirainen@example.net" { + test_fail "envelope sender incorrect"; + } + + if not header :contains "x-sieve-redirected-from" + "t.sirainen@example.net" { + test_fail "x-sieve-redirected-from header is incorrect"; + } +} + +/* + * Redirect mail loop (sieve_user_email) + */ + +test_result_reset; +test_set "message" text: +X-Sieve-Redirected-From: t.sirainen@example.net +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "user_email"; +test_config_set "sieve_user_email" "t.sirainen@example.net"; +test_config_reload; + +test "Redirect mail loop (sieve_user_email)" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + if test_message :smtp 0 { + test_fail "failed to recognize mail loop"; + } +} + +/* + * Redirect mail loop (final recipient) + */ + +test_result_reset; +test_set "message" text: +X-Sieve-Redirected-From: timo@example.net +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_reload; + +test "Redirect mail loop (final recipient)" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + if test_message :smtp 0 { + test_fail "failed to recognize mail loop"; + } +} + +/* + * Redirect mail loop (multiple headers) + */ + +test_result_reset; +test_set "message" text: +X-Sieve-Redirected-From: stephan@example.net +From: stephan@example.org +To: tss@example.net +Subject: Frop! +X-Sieve-Redirected-From: t.sirainen@example.net +X-Sieve-Redirected-From: t.sirainen@example.com + +Frop! +. +; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "user_email"; +test_config_set "sieve_user_email" "t.sirainen@example.net"; +test_config_reload; + +test "Redirect mail loop (sieve_user_email)" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + if test_message :smtp 0 { + test_fail "failed to recognize mail loop"; + } +} diff --git a/pigeonhole/tests/extensions/body/basic.svtest b/pigeonhole/tests/extensions/body/basic.svtest new file mode 100644 index 0000000..0b2bffc --- /dev/null +++ b/pigeonhole/tests/extensions/body/basic.svtest @@ -0,0 +1,97 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "comparator-i;ascii-numeric"; + +require "body"; + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Test message. + +Test! + +. +; + +/* Empty line + * + * RFC 5173: + * 'The body test matches content in the body of an email message, that + * is, anything following the first empty line after the header. (The + * empty line itself, if present, is not considered to be part of the + * body.)' + */ +test "The empty line" { + + if not body :raw :is text: +Test! + +. + { + test_fail "invalid message body extracted (1)"; + } + + if body :raw :is text: + +Test! + +. + { + test_fail "invalid message body extracted (2)"; + } + + if body :raw :is "Test" + { + test_fail "body test matches nonsense (3)"; + } +} + +/* Default comparator and match type + * + * RFC 5173: + * 'The COMPARATOR and MATCH-TYPE keyword parameters are defined in + * [SIEVE]. As specified in Sections 2.7.1 and 2.7.3 of [SIEVE], the + * default COMPARATOR is "i;ascii-casemap" and the default MATCH-TYPE is + * ":is".' + */ + +test "Defaults" { + if anyof ( body :raw "Test", body :raw "*Test*" ) { + test_fail "default match type is not :is as is required"; + } + + if allof( not body :raw :contains "tesT", body :raw :contains "Test" ) { + test_fail "default comparator is not i;ascii-casemap as is required"; + } +} + +/* No body + * + * RFC 5173: + * 'If a message consists of a header only, not followed by an empty line, + * then that set is empty and all "body" tests return false, including + * those that test for an empty string. (This is similar to how the + * "header" test always fails when the named header fields aren't present.)' + */ + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: No body is here! +. +; + +test "No body" { + if body :raw :contains "" { + test_fail "matched against non-existent body (:contains \"\")"; + } + + if body :raw :is "" { + test_fail "matched against non-existent body (:is \"\")"; + } + + if body :raw :matches "*" { + test_fail "matched against non-existent body (:matches \"*\")"; + } +} diff --git a/pigeonhole/tests/extensions/body/content.svtest b/pigeonhole/tests/extensions/body/content.svtest new file mode 100644 index 0000000..2eb3837 --- /dev/null +++ b/pigeonhole/tests/extensions/body/content.svtest @@ -0,0 +1,332 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "comparator-i;ascii-numeric"; + +require "body"; + +/* + * + */ + +test_set "message" text: +From: justin@example.com +To: carl@example.nl +Subject: Frop +Content-Type: multipart/mixed; boundary=donkey + +This is a multi-part message in MIME format. + +--donkey +Content-Type: text/plain + +Plain Text + +--donkey +Content-Type: text/stupid + +Stupid Text + +--donkey +Content-Type: text/plain/stupid + +Plain Stupid Text + +--donkey-- +. +; + +/* + * RFC5173, Section 5.2: + * If an individual content type begins or ends with a '/' (slash) or + * contains multiple slashes, then it matches no content types. + * ... + */ + +test "Basic Match" { + if not body :content "text/plain" :matches "Plain Text*" { + test_fail "failed to match (1)"; + } + + if not body :content "text/plain" :contains "" { + test_fail "failed to match (2)"; + } + + if not body :content "text/stupid" :contains "" { + test_fail "failed to match (3)"; + } +} + +test "Begin Slash" { + if body :content "/plain" :contains "" { + test_fail "matched :content \"/plain\""; + } +} + +test "End Slash" { + if body :content "text/" :contains "" { + test_fail "matched :content \"text/\""; + } +} + +test "Double Slash" { + if body :content "text/plain/stupid" :contains "" { + test_fail "matched :content \"text/plain/stupid\""; + } +} + +/* + * + */ + +test_set "message" text: +From: justin@example.com +To: carl@example.nl +Subject: Frop +Content-Type: multipart/mixed; boundary=limit + +This is a multi-part message in MIME format. + +--limit +Content-Type: text/plain + +This is a text message. + +--limit +Content-Type: text/html + +<html><body>This is HTML</body></html> + +--limit +Content-Type: application/sieve + +keep; + +--limit-- +. +; + +/* RFC5173, Section 5.2: + * ... + * Otherwise, if it contains a slash, then it specifies a full + * <type>/<subtype> pair, and matches only that specific content type. + * If it is the empty string, all MIME content types are matched. + * Otherwise, it specifies a <type> only, and any subtype of that type + * matches it. + */ + +test "Full Content Type" { + if not body :content "text/plain" :matches "This is a text message.*" { + test_fail "failed to match text/plain content"; + } + + if body :content "text/plain" :matches "<html><body>This is HTML</body></html>*" { + test_fail "erroneously matched text/html content"; + } + + if not body :content "text/html" :matches "<html><body>This is HTML</body></html>*" { + test_fail "failed to match text/html content"; + } + + if body :content "text/html" :matches "This is a text message.*" { + test_fail "erroneously matched text/plain content"; + } + + if body :content "text/html" :matches "This is HTML*" { + test_fail "body :content test matched plain text"; + } +} + +test "Empty Content Type" { + if not body :content "" :matches "This is a text message.*" { + test_fail "failed to match text/plain content"; + } + + if not body :content "" :matches "<html><body>This is HTML</body></html>*" { + test_fail "failed to match text/html content"; + } + + if not body :content "" :matches "keep;*" { + test_fail "failed to match application/sieve content"; + } + + if body :content "" :matches "*blurdybloop*" { + test_fail "body :content \"\" test matches nonsense"; + } +} + +test "Main Content Type" { + if not body :content "text" :matches "This is a text message.*" { + test_fail "failed to match text/plain content"; + } + + if not body :content "text" :matches "<html><body>This is HTML</body></html>*" { + test_fail "failed to match text/html content"; + } + + if body :content "text" :matches "keep;*" { + test_fail "erroneously matched application/sieve content"; + } +} + +/* + * + */ + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: multipart/alternative; boundary=inner + +This is a nested multi-part message in MIME format. + +--inner +Content-Type: text/plain; charset="us-ascii" + +Hello + +--inner +Content-Type: text/html; charset="us-ascii" + +<html><body>Hello</body></html> + +--inner-- + +This is the end of the inner MIME multipart. + +--outer +Content-Type: message/rfc822 + +From: Someone Else +Subject: Hello, this is an elaborate request for you to finally say hello + already! + +Please say Hello + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +/* RFC5173, Section 5.2: + * + * The search for MIME parts matching the :content specification is + * recursive and automatically descends into multipart and + * message/rfc822 MIME parts. All MIME parts with matching types are + * searched for the key strings. The test returns true if any + * combination of a searched MIME part and key-list argument match. + */ + +test "Nested Search" { + if not body :content "text/plain" :matches "Hello*" { + test_fail "failed to match text/plain content"; + } + + if body :content "text/plain" :matches "<html><body>Hello</body></html>*" { + test_fail "erroneously matched text/html content"; + } + + if not body :content "text/html" :matches "<html><body>Hello</body></html>*" { + test_fail "failed to match text/html content"; + } + + if body :content "text/html" :matches "Hello*" { + test_fail "erroneously matched text/plain content"; + } + + if not body :content "text" :contains "html" { + test_fail "failed match text content (1)"; + } + + if not body :content "text" :contains "hello" { + test_fail "failed match text content (2)"; + } + + if not body :content "text/plain" :contains "please say hello" { + test_fail "failed match nested message content as text/plain"; + } + + if not body :content "text" :contains "please say hello" { + test_fail "failed match nested message content as text/*"; + } + + if not body :content "text" :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "matched wrong number of \"text/*\" body parts"; + } +} + +/* RFC5173, Section 5.2: + * + * If the :content specification matches a multipart MIME part, only the + * prologue and epilogue sections of the part will be searched for the + * key strings, treating the entire prologue and the entire epilogue as + * separate strings; the contents of nested parts are only searched if + * their respective types match the :content specification. + * + */ + +test "Multipart Content" { + if not body :content "multipart" :contains + "This is a multi-part message in MIME format" { + test_fail "missed first multipart body part"; + } + + if not body :content "multipart" :contains + "This is a nested multi-part message in MIME format" { + test_fail "missed second multipart body part"; + } + + if not body :content "multipart" :contains + "This is the end of the inner MIME multipart" { + test_fail "missed third multipart body part"; + } + + if not body :content "multipart" :contains + "This is the end of the outer MIME multipart." { + test_fail "missed fourth multipart body part"; + } + + if body :content "multipart" :contains "--inner" { + test_fail "inner boundary is part of match"; + } + + if body :content "multipart" :contains "--outer" { + test_fail "outer boundary is part of match"; + } +} + +/* RFC5173, Section 5.2: + * + * If the :content specification matches a message/rfc822 MIME part, + * only the header of the nested message will be searched for the key + * strings, treating the header as a single string; the contents of the + * nested message body parts are only searched if their content type + * matches the :content specification. + */ + +test "Content-Type: message/rfc822" { + if not body :content "message/rfc822" :contains + "From: Someone Else" { + test_fail "missed raw message/rfc822 from header"; + } + + if not body :content "message/rfc822" :is text: +From: Someone Else +Subject: Hello, this is an elaborate request for you to finally say hello + already! +. + { + test_fail "header content does not match exactly"; + } +} + + + + diff --git a/pigeonhole/tests/extensions/body/errors.svtest b/pigeonhole/tests/extensions/body/errors.svtest new file mode 100644 index 0000000..8db5657 --- /dev/null +++ b/pigeonhole/tests/extensions/body/errors.svtest @@ -0,0 +1,19 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid syntax + */ + +test "Invalid Syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "12" { + test_fail "wrong number of errors reported"; + } +} + diff --git a/pigeonhole/tests/extensions/body/errors/syntax.sieve b/pigeonhole/tests/extensions/body/errors/syntax.sieve new file mode 100644 index 0000000..8adf0ef --- /dev/null +++ b/pigeonhole/tests/extensions/body/errors/syntax.sieve @@ -0,0 +1,38 @@ +require "body"; + +# 1: No key list +if body { } + +# 2: Number +if body 3 { } + +# OK: String +if body "frop" { } + +# 3: To many arguments +if body "frop" "friep" { } + +# 4: Unknown tag +if body :frop { } + +# 5: Unknown tag with valid key +if body :friep "frop" { } + +# 6: Content without argument +if body :content { } + +# 7: Content without key argument +if body :content "frop" { } + +# 8: Content with number argument +if body :content 3 "frop" { } + +# 9: Content with unknown tag +if body :content :frml "frop" { } + +# 10: Content with known tag +if body :content :contains "frop" { } + +# 11: Duplicate transform +if body :content "frop" :raw "frop" { } + diff --git a/pigeonhole/tests/extensions/body/match-values.svtest b/pigeonhole/tests/extensions/body/match-values.svtest new file mode 100644 index 0000000..55d5535 --- /dev/null +++ b/pigeonhole/tests/extensions/body/match-values.svtest @@ -0,0 +1,55 @@ +require "vnd.dovecot.testsuite"; + +require "body"; +require "variables"; + +test_set "message" text: +From: stephan@example.org +To: s.bosch@twente.example.net +Subject: Body test + +The big bad body test. +. +; + +# Test whether body test ignores match values +test "Match values disabled" { + if not body :raw :matches "The * bad * test*" { + test_fail "should have matched"; + } + + if anyof ( + string :is "${1}" "big", + string :is "${2}" "body", + not string :is "${0}" "", + not string :is "${1}" "", + not string :is "${2}" "") { + test_fail "match values not disabled"; + } +} + +test "Match values re-enabled" { + if not header :matches "from" "*@*" { + test_fail "should have matched"; + } + + if anyof ( + not string :is "${0}" "stephan@example.org", + not string :is "${1}" "stephan", + not string :is "${2}" "example.org" ) { + test_fail "match values not re-enabled properly."; + } +} + +test "Match values retained" { + if not body :raw :matches "The * bad * test*" { + test_fail "should have matched"; + } + + if anyof ( + not string :is "${0}" "stephan@example.org", + not string :is "${1}" "stephan", + not string :is "${2}" "example.org" ) { + test_fail "match values not retained after body test."; + } +} diff --git a/pigeonhole/tests/extensions/body/raw.svtest b/pigeonhole/tests/extensions/body/raw.svtest new file mode 100644 index 0000000..d3404b9 --- /dev/null +++ b/pigeonhole/tests/extensions/body/raw.svtest @@ -0,0 +1,85 @@ +require "vnd.dovecot.testsuite"; +require "body"; + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: multipart/alternative; boundary=inner + +This is a nested multi-part message in MIME format. + +--inner +Content-Type: text/plain; charset="us-ascii" + +Hello + +--inner +Content-Type: text/html; charset="us-ascii" + +<html><body>Hello</body></html> + +--inner-- + +This is the end of the inner MIME multipart. + +--outer +Content-Type: message/rfc822 + +From: Someone Else +Subject: hello request + +Please say Hello + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +/* + * + * RFC 5173: + * The ":raw" transform matches against the entire undecoded body of a + * message as a single item. + * + * If the specified body-transform is ":raw", the [MIME] structure of + * the body is irrelevant. The implementation MUST NOT remove any + * transfer encoding from the message, MUST NOT refuse to filter + * messages with syntactic errors (unless the environment it is part of + * rejects them outright), and MUST treat multipart boundaries or the + * MIME headers of enclosed body parts as part of the content being + * matched against, instead of MIME structures to interpret. + */ + +test "Multipart Boundaries" { + if not body :raw :contains "--inner" { + test_fail "Raw body does not contain '--inner'"; + } + + if not body :raw :contains "--outer" { + test_fail "Raw body does not contain '--outer'"; + } +} + +test "Multipart Headers" { + if not body :raw :contains "boundary=inner" { + test_fail "Raw body does not contain 'boundary=inner'"; + } + + if not body :raw :contains "rfc822" { + test_fail "Raw body does not contain 'rfc822'"; + } +} + +test "Multipart Content" { + if not body :raw :contains "<html><body>Hello</body></html>" { + test_fail "Raw body does not contain '<html><body>Hello</body></html>'"; + } +} diff --git a/pigeonhole/tests/extensions/body/text.svtest b/pigeonhole/tests/extensions/body/text.svtest new file mode 100644 index 0000000..2dc6a03 --- /dev/null +++ b/pigeonhole/tests/extensions/body/text.svtest @@ -0,0 +1,225 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "comparator-i;ascii-numeric"; + +require "body"; + +/* + * + */ + +test_set "message" text: +From: justin@example.com +To: carl@example.nl +Subject: Frop +Content-Type: multipart/mixed; boundary=donkey + +This is a multi-part message in MIME format. + +--donkey +Content-Type: text/plain + +Plain Text + +--donkey +Content-Type: text/stupid + +Stupid Text + +--donkey +Content-Type: text/plain/stupid + +Plain Stupid Text + +--donkey-- +. +; + +test "Basic Match" { + if not body :text :contains "Plain Text" { + test_fail "failed to match (1)"; + } + + if not body :text :contains "Stupid Text" { + test_fail "failed to match (2)"; + } +} + +test "Double Slash" { + if body :text :contains "Plain Stupid Text" { + test_fail "matched \"text/plain/stupid\""; + } +} + +/* + * + */ + +test_set "message" text: +From: justin@example.com +To: carl@example.nl +Subject: Frop +Content-Type: multipart/mixed; boundary=limit + +This is a multi-part message in MIME format. + +--limit +Content-Type: text/plain + +This is a text message. + +--limit +Content-Type: text/html + +<html><body>This is HTML</body></html> + +--limit +Content-Type: application/sieve + +keep; + +--limit-- +. +; + +test "Full Content Type" { + if not body :text :contains "This is a text message" { + test_fail "failed to match text/plain content"; + } + + if not body :text :contains "This is HTML" { + test_fail "failed to match text/html content"; + } + + if body :text :contains "<html>" { + test_fail "erroneously matched text/html markup"; + } + + if body :text :contains "keep;" { + test_fail "body :text test matched non-text content"; + } +} + +/* + * + */ + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: multipart/alternative; boundary=inner + +This is a nested multi-part message in MIME format. + +--inner +Content-Type: text/plain; charset="us-ascii" + +Hello + +--inner +Content-Type: text/html; charset="us-ascii" + +<html><body>HTML Hello</body></html> + +--inner +Content-Type: application/xhtml+xml; charset="us-ascii" + +<html><body>XHTML Hello</body></html> + +--inner-- + +This is the end of the inner MIME multipart. + +--outer +Content-Type: message/rfc822 + +From: Someone Else +Subject: Hello, this is an elaborate request for you to finally say hello + already! + +Please say Hello + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +/* RFC5173, Section 5.2: + * + * The search for MIME parts matching the :content specification is + * recursive and automatically descends into multipart and + * message/rfc822 MIME parts. All MIME parts with matching types are + * searched for the key strings. The test returns true if any + * combination of a searched MIME part and key-list argument match. + */ + +test "Nested Search" { + if not body :text :contains "Hello" { + test_fail "failed to match text/plain content"; + } + if not body :text :contains "HTML Hello" { + test_fail "failed to match text/html content"; + } + if not body :text :contains "XHTML Hello" { + test_fail "failed to match application/xhtml+xml content"; + } + if body :text :contains ["<html>", "body"] { + test_fail "erroneously matched text/html markup"; + } + if not body :text :contains "Please say Hello" { + test_fail "failed to match message/rfc822 body"; + } + if body :text :contains "MIME" { + test_fail "erroneously matched multipart prologue/epilogue text"; + } +} + +/* + * Broken/Empty parts + */ + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: text/html + +--outer +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: multipart/related +Content-Disposition: inline + +<html><body>Please say Hello</body></html> + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +test "Nested Search" { + if body :text :contains "Hello" { + test_fail "Cannot match empty/broken part"; + } + if body :text :contains ["<html>", "body"] { + test_fail "erroneously matched text/html markup"; + } + if body :text :contains "MIME" { + test_fail "erroneously matched multipart prologue/epilogue text"; + } +} + diff --git a/pigeonhole/tests/extensions/date/basic.svtest b/pigeonhole/tests/extensions/date/basic.svtest new file mode 100644 index 0000000..5d0b33f --- /dev/null +++ b/pigeonhole/tests/extensions/date/basic.svtest @@ -0,0 +1,73 @@ +require "vnd.dovecot.testsuite"; +require "date"; +require "variables"; +require "relational"; + +test_set "message" text: +From: stephan@example.org +To: sirius@friep.example.com +Subject: Frop! +Date: Mon, 20 Jul 2009 21:44:43 +0300 +Delivery-Date: Mon, 22 Jul 2009 23:30:14 +0300 +Invalid-Date: Moo, 34 Juul 3060 25:30:42 +6600 +Wanna date? +. +; + +test "Defaults" { + if not date :originalzone "date" "std11" "mon, 20 jul 2009 21:44:43 +0300" { + test_fail "default comparator is not i;ascii-casemap"; + } + + if anyof ( date "date" "std11" "Mon", date "date" "std11" "*") { + test_fail "default match type appears to be :contains or :matches"; + } +} + +test "Count" { + if not date :count "eq" "date" "date" "1" { + test_fail "count of existing date header field is not 1"; + } + + if not date :count "eq" "resent-date" "date" "0" { + test_fail "count of non-existent date header field is not 0"; + } +} + +test "Invalid" { + if date :matches "invalid-date" "std11" "*" { + test_fail "matched invalid date: ${0}"; + } +} + +test "Comparison" { + if not date :originalzone :is "delivery-date" "date" "2009-07-22" { + if date :originalzone :matches "delivery-date" "date" "*" { set "date" "${1}"; } + test_fail "date is invalid: ${date}"; + } + if not date :originalzone :value "ge" "delivery-date" "date" "2009-07-22" { + test_fail "date comparison ge failed equal"; + } + + if not date :originalzone :value "ge" "delivery-date" "date" "2009-07-21" { + test_fail "date comparison ge failed greater"; + } + + if anyof (not date :originalzone :value "ge" "delivery-date" "date" "2009-06-22", + not date :originalzone :value "ge" "date" "date" "2006-07-22" ) { + test_fail "date comparison ge failed much greater"; + } + + if not date :originalzone :value "le" "delivery-date" "date" "2009-07-22" { + test_fail "date comparison le failed equal"; + } + + if not date :originalzone :value "le" "delivery-date" "date" "2009-07-23" { + test_fail "date comparison le failed less"; + } + + if anyof (not date :originalzone :value "le" "delivery-date" "date" "2009-09-22", + not date :originalzone :value "le" "date" "date" "2012-07-22" ) { + test_fail "date comparison ge failed much less"; + } +} diff --git a/pigeonhole/tests/extensions/date/date-parts.svtest b/pigeonhole/tests/extensions/date/date-parts.svtest new file mode 100644 index 0000000..edc565c --- /dev/null +++ b/pigeonhole/tests/extensions/date/date-parts.svtest @@ -0,0 +1,120 @@ +require "vnd.dovecot.testsuite"; +require "date"; +require "variables"; + +test_set "message" text: +From: stephan@example.org +To: sirius@friep.example.com +Subject: Frop! +Date: Mon, 20 Jul 2009 21:44:43 +0300 +Delivery-Date: Mon, 22 Jul 2009 23:30:14 +0300 + +Wanna date? +. +; + +/* "year" => the year, "0000" .. "9999". */ +test "Year" { + if not date :originalzone "date" "year" "2009" { + test_fail "failed to extract year part"; + } +} + +/* "month" => the month, "01" .. "12". */ +test "Month" { + if not date :originalzone "date" "month" "07" { + test_fail "failed to extract month part"; + } +} + +/* "day" => the day, "01" .. "31". */ +test "Day" { + if not date :originalzone "date" "day" "20" { + test_fail "failed to extract day part"; + } +} + +/* "date" => the date in "yyyy-mm-dd" format. */ +test "Date" { + if not date :originalzone "date" "date" "2009-07-20" { + test_fail "failed to extract date part"; + } +} + +/* "julian" => the Modified Julian Day, that is, the date + expressed as an integer number of days since + 00:00 UTC on November 17, 1858 (using the Gregorian + calendar). This corresponds to the regular + Julian Day minus 2400000.5. */ +test "Julian" { + if not date :originalzone "date" "julian" "55032" { + if date :matches :originalzone "date" "julian" "*" { } + test_fail "failed to extract julian part: ${0}"; + } + if not date :originalzone "delivery-date" "julian" "55034" { + if date :matches :originalzone "delivery-date" "julian" "*" { } + test_fail "failed to extract julian part: ${0}"; + } +} + +/* "hour" => the hour, "00" .. "23". */ +test "Hour" { + if not date :originalzone "date" "hour" "21" { + test_fail "failed to extract hour part"; + } +} + +/* "minute" => the minute, "00" .. "59". */ +test "Minute" { + if not date :originalzone "date" "minute" "44" { + test_fail "failed to extract minute part"; + } +} + +/* "second" => the second, "00" .. "60". */ +test "Second" { + if not date :originalzone "date" "second" "43" { + test_fail "failed to extract second part"; + } +} + +/* "time" => the time in "hh:mm:ss" format. */ +test "Time" { + if not date :originalzone "date" "time" "21:44:43" { + test_fail "failed to extract time part"; + } +} + +/* "iso8601" => the date and time in restricted ISO 8601 format. */ +test "ISO8601" { + if not date :originalzone "date" "iso8601" "2009-07-20T21:44:43+03:00" { + test_fail "failed to extract iso8601 part"; + } +} + +/* "std11" => the date and time in a format appropriate + for use in a Date: header field [RFC2822]. */ +test "STD11" { + if not date :originalzone "date" "std11" "Mon, 20 Jul 2009 21:44:43 +0300" { + test_fail "failed to extract std11 part"; + } +} + +/* "zone" => the time zone in use. */ +test "zone" { + if not date :originalzone "date" "zone" "+0300" { + test_fail "failed to extract zone part"; + } + + if not date :zone "+0200" "date" "zone" "+0200" { + test_fail "failed to extract zone part"; + } +} + +/* "weekday" => the day of the week expressed as an integer between + "0" and "6". "0" is Sunday, "1" is Monday, etc. */ +test "Weekday" { + if not date :originalzone "date" "weekday" "1" { + test_fail "failed to extract weekday part"; + } +} diff --git a/pigeonhole/tests/extensions/date/zones.svtest b/pigeonhole/tests/extensions/date/zones.svtest new file mode 100644 index 0000000..77adb77 --- /dev/null +++ b/pigeonhole/tests/extensions/date/zones.svtest @@ -0,0 +1,76 @@ +require "vnd.dovecot.testsuite"; +require "date"; +require "variables"; + +/* Extract local timezone first */ +test "Local-Zone" { + if not currentdate :matches "zone" "*" { + test_fail "matches '*' failed for zone part."; + } + set "local_zone" "${0}"; +} + +/* FIXME: using variables somehow fails here */ +if string "${local_zone}" "+0200" { +test_set "message" text: +From: stephan@example.org +To: sirius@friep.example.com +Subject: Frop! +Date: Mon, 20 Jul 2009 21:44:43 +0300 +Delivery-Date: Mon, 23 Jul 2009 05:30:14 +0800 + +Wanna date? +. +; +} else { +test_set "message" text: +From: stephan@example.org +To: sirius@friep.example.com +Subject: Frop! +Date: Mon, 20 Jul 2009 21:44:43 +0300 +Delivery-Date: Mon, 22 Jul 2009 23:30:14 +0200 + +Wanna date? +. +; +} + +test "Specified Zone" { + if not date :zone "+0200" "date" "zone" "+0200" { + if date :matches :zone "+0200" "date" "zone" "*" {} + test_fail "zone is incorrect: ${0}"; + } + + if not date :zone "+0200" "date" "time" "20:44:43" { + test_fail "zone is not applied"; + } +} + +test "Original Zone" { + if not date :originalzone "date" "zone" "+0300" { + if date :matches :originalzone "date" "zone" "*" {} + test_fail "zone is incorrect: ${0}"; + } + + if not date :originalzone "date" "time" "21:44:43" { + test_fail "time should be left untouched"; + } +} + +test "Local Zone Shift" { + if anyof ( + allof ( + string "${local_zone}" "+0200", + date "delivery-date" "iso8601" "2009-07-23T05:30:14+08:00"), + allof ( + not string "${local_zone}" "+0200", + date "delivery-date" "iso8601" "2009-07-22T23:30:14+02:00")) { + + if date :matches "delivery-date" "iso8601" "*" + { set "a" "${0}"; } + if date :originalzone :matches "delivery-date" "iso8601" "*" + { set "b" "${0}"; } + + test_fail "time not shifted to local zone: ${b} => ${a}"; + } +} diff --git a/pigeonhole/tests/extensions/duplicate/errors.svtest b/pigeonhole/tests/extensions/duplicate/errors.svtest new file mode 100644 index 0000000..108a0f0 --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/errors.svtest @@ -0,0 +1,54 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid syntax + */ + +test "Invalid Syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "17" { + test_fail "wrong number of errors reported"; + } +} + +test "Invalid Syntax (vnd)" { + if test_script_compile "errors/syntax-vnd.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "5" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Extension conflict + */ + +test "Extension conflict" { + if test_script_compile "errors/conflict.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + +test "Extension conflict (vnd first)" { + if test_script_compile "errors/conflict-vnd.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + + diff --git a/pigeonhole/tests/extensions/duplicate/errors/conflict-vnd.sieve b/pigeonhole/tests/extensions/duplicate/errors/conflict-vnd.sieve new file mode 100644 index 0000000..1c133df --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/errors/conflict-vnd.sieve @@ -0,0 +1,4 @@ +require "vnd.dovecot.duplicate"; +require "duplicate"; + +if duplicate { keep; } diff --git a/pigeonhole/tests/extensions/duplicate/errors/conflict.sieve b/pigeonhole/tests/extensions/duplicate/errors/conflict.sieve new file mode 100644 index 0000000..aa9b038 --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/errors/conflict.sieve @@ -0,0 +1,4 @@ +require "duplicate"; +require "vnd.dovecot.duplicate"; + +if duplicate { keep; } diff --git a/pigeonhole/tests/extensions/duplicate/errors/syntax-vnd.sieve b/pigeonhole/tests/extensions/duplicate/errors/syntax-vnd.sieve new file mode 100644 index 0000000..f62aa2c --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/errors/syntax-vnd.sieve @@ -0,0 +1,19 @@ +require "vnd.dovecot.duplicate"; + +# Used as a command +duplicate; + +# Used with no argument (not an error) +if duplicate {} + +# Used with string argument +if duplicate "frop" { } + +# Used with numer argument +if duplicate 23423 { } + +# Used with numer argument +if duplicate ["frop"] { } + + + diff --git a/pigeonhole/tests/extensions/duplicate/errors/syntax.sieve b/pigeonhole/tests/extensions/duplicate/errors/syntax.sieve new file mode 100644 index 0000000..a561cfb --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/errors/syntax.sieve @@ -0,0 +1,54 @@ +require "duplicate"; + +# Used as a command +duplicate; + +# Used with no argument (not an error) +if duplicate {} + +# Used with string argument +if duplicate "frop" { } + +# Used with numner argument +if duplicate 23423 { } + +# Used with numer argument +if duplicate ["frop"] { } + +# Used with unknown tag +if duplicate :test "frop" { } + +# Bad :header parameter +if duplicate :header 23 {} + +# Bad :uniqueid parameter +if duplicate :uniqueid 23 {} + +# Bad :handle parameter +if duplicate :handle ["a", "b", "c"] {} + +# Bad seconds parameter +if duplicate :seconds "a" {} + +# Missing :header parameter +if duplicate :header {} + +# Missing :uniqueid parameter +if duplicate :uniqueid {} + +# Missing :handle parameter +if duplicate :handle {} + +# Missing seconds parameter +if duplicate :seconds {} + +# :last with a parameter +if duplicate :last "frop" {} + +# :last as :seconds parameter +if duplicate :seconds :last {} + +# Conflicting tags +if duplicate :header "X-Frop" :uniqueid "FROP!" { } + + diff --git a/pigeonhole/tests/extensions/duplicate/execute-vnd.svtest b/pigeonhole/tests/extensions/duplicate/execute-vnd.svtest new file mode 100644 index 0000000..386550f --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/execute-vnd.svtest @@ -0,0 +1,20 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.duplicate"; + +test "Run" { + if duplicate { + test_fail "test erroneously reported a duplicate"; + } + + if duplicate :handle "handle" { + test_fail "test with name erroneously reported a duplicate"; + } + + if duplicate { + test_fail "test erroneously reported a duplicate"; + } + + if duplicate :handle "handle" { + test_fail "test with name erroneously reported a duplicate"; + } +} diff --git a/pigeonhole/tests/extensions/duplicate/execute.svtest b/pigeonhole/tests/extensions/duplicate/execute.svtest new file mode 100644 index 0000000..9e060ff --- /dev/null +++ b/pigeonhole/tests/extensions/duplicate/execute.svtest @@ -0,0 +1,41 @@ +require "vnd.dovecot.testsuite"; +require "duplicate"; + +# Simple execution tests; no duplicate verification can be tested yet. +test "Run" { + if duplicate { + test_fail "test erroneously reported a duplicate"; + } + + if duplicate :handle "handle" { + test_fail "test with :handle erroneously reported a duplicate"; + } + + if duplicate { + test_fail "test erroneously reported a duplicate"; + } + + if duplicate :handle "handle" { + test_fail "test with :handle erroneously reported a duplicate"; + } + + if duplicate :header "X-frop" { + test_fail "test with :header erroneously reported a duplicate"; + } + + if duplicate :uniqueid "FROP!" { + test_fail "test with :uniqueid erroneously reported a duplicate"; + } + + if duplicate :seconds 90 { + test_fail "test with :seconds erroneously reported a duplicate"; + } + + if duplicate :seconds 90 :last { + test_fail "test with :seconds :last erroneously reported a duplicate"; + } + + if duplicate :last { + test_fail "test with :seconds :last erroneously reported a duplicate"; + } +} diff --git a/pigeonhole/tests/extensions/editheader/addheader.svtest b/pigeonhole/tests/extensions/editheader/addheader.svtest new file mode 100644 index 0000000..426b43d --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/addheader.svtest @@ -0,0 +1,833 @@ +require "vnd.dovecot.testsuite"; +require "encoded-character"; +require "variables"; +require "fileinto"; +require "mailbox"; +require "body"; + +require "editheader"; + +set "message" text: +From: stephan@example.com +To: timo@example.com +Subject: Frop! + +Frop! + +. +; + +test_set "message" "${message}"; +test "Addheader - first" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Header content"; + + if not size :over 76 { + test_fail "mail is not larger"; + } + + if size :over 107 { + test_fail "mail is too large"; + } + + if size :under 107 { + test_fail "mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header" { + test_fail "header not added"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added"; + } + + redirect "frop@example.com"; + fileinto :create "folder1"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder1" 0 { + test_fail "message not stored"; + } + + if not size :over 76 { + test_fail "stored mail is not larger"; + } + + if size :over 107 { + test_fail "stored mail is too large"; + } + + if size :under 100 { + test_fail "stored mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not exists "x-some-header" { + test_fail "header not in stored mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in stored mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header" { + test_fail "header not in redirected mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in redirected mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - first (two)" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Header content"; + addheader "X-Some-Other-Header" "More header content"; + + if not size :over 76 { + test_fail "mail is not larger"; + } + + if size :over 149 { + test_fail "mail is too large"; + } + + if size :under 149 { + test_fail "mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header" { + test_fail "header #1 not added"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added #1"; + } + + if not exists "x-some-other-header" { + test_fail "header #2 not added"; + } + + if not header :is "x-some-other-header" "More header content" { + test_fail "wrong content added #2"; + } + + redirect "frop@example.com"; + fileinto :create "folder2"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder2" 0 { + test_fail "message not stored"; + } + + if not size :over 76 { + test_fail "stored mail is not larger"; + } + + if size :over 149 { + test_fail "stored mail is too large"; + } + + if size :under 100 { + test_fail "stored mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not exists "x-some-header" { + test_fail "header #1 not in stored mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content #1 in stored mail "; + } + + if not exists "x-some-other-header" { + test_fail "header #2 not in stored mail"; + } + + if not header :is "x-some-other-header" "More header content" { + test_fail "wrong content #2 in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header" { + test_fail "header not in redirected mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in redirected mail "; + } + + if not exists "x-some-other-header" { + test_fail "header #2 not in redirected mail"; + } + + if not header :is "x-some-other-header" "More header content" { + test_fail "wrong content #2 in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - last" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader :last "X-Some-Header" "Header content"; + + if not size :over 76 { + test_fail "mail is not larger"; + } + + if size :over 107 { + test_fail "mail is too large"; + } + + if size :under 107 { + test_fail "mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header" { + test_fail "header not added"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added"; + } + + redirect "frop@example.com"; + fileinto :create "folder3"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder3" 0 { + test_fail "message not stored"; + } + + if not size :over 76 { + test_fail "stored mail is not larger"; + } + + if size :over 107 { + test_fail "stored mail is too large"; + } + + if size :under 100 { + test_fail "stored mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not exists "x-some-header" { + test_fail "header not in stored mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in stored mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header" { + test_fail "header not in redirected mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in redirected mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - last (two)" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader :last "X-Some-Header" "Header content"; + addheader "X-Some-Other-Header" "More header content"; + + if not size :over 76 { + test_fail "mail is not larger"; + } + + if size :over 149 { + test_fail "mail is too large"; + } + + if size :under 149 { + test_fail "mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header" { + test_fail "header #1 not added"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added #1"; + } + + if not exists "x-some-other-header" { + test_fail "header #2 not added"; + } + + if not header :is "x-some-other-header" "More header content" { + test_fail "wrong content added #2"; + } + + redirect "frop@example.com"; + fileinto :create "folder4"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder4" 0 { + test_fail "message not stored"; + } + + if not size :over 76 { + test_fail "stored mail is not larger"; + } + + if size :over 149 { + test_fail "stored mail is too large"; + } + + if size :under 100 { + test_fail "stored mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not exists "x-some-header" { + test_fail "header #1 not in stored mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content #1 in stored mail"; + } + + if not exists "x-some-other-header" { + test_fail "header #2 not in stored mail"; + } + + if not header :is "x-some-other-header" "More header content" { + test_fail "wrong content #2 in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header" { + test_fail "header #1 not in redirected mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content #1 in redirected mail "; + } + + if not exists "x-some-other-header" { + test_fail "header #2 not in redirected mail"; + } + + if not header :is "x-some-other-header" "More header content" { + test_fail "wrong content #2 in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - framed" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header-first" "Header content first"; + addheader :last "X-Some-Header-last" "Header content last"; + + if not size :over 76 { + test_fail "mail is not larger"; + } + + if size :over 160 { + test_fail "mail is too large"; + } + + if size :under 160 { + test_fail "mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header-first" { + test_fail "first header not added"; + } + + if not exists "x-some-header-last" { + test_fail "last header not added"; + } + + if not header :is "x-some-header-first" "Header content first" { + test_fail "wrong first content added"; + } + + if not header :is "x-some-header-last" "Header content last" { + test_fail "wrong last content added"; + } + + redirect "frop@example.com"; + fileinto :create "folder5"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder5" 0 { + test_fail "message not stored"; + } + + if not size :over 76 { + test_fail "stored mail is not larger"; + } + + if size :over 160 { + test_fail "stored mail is too large"; + } + + if size :under 152 { + test_fail "stored mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not exists "x-some-header-first" { + test_fail "first header not in stored mail"; + } + + if not exists "x-some-header-last" { + test_fail "last header not in stored mail"; + } + + if not header :is "x-some-header-first" "Header content first" { + test_fail "wrong first header content in stored mail "; + } + + if not header :is "x-some-header-last" "Header content last" { + test_fail "wrong last header content in stored mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header-first" { + test_fail "first header not in redirected mail"; + } + + if not exists "x-some-header-last" { + test_fail "last header not in redirected mail"; + } + + if not header :is "x-some-header-first" "Header content first" { + test_fail "wrong first header content in redirected mail "; + } + + if not header :is "x-some-header-last" "Header content last" { + test_fail "wrong last header content in redirected mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +/* + * Addheader - folded + */ + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - folded" { + set "before" + "This is very long header content, folded to fit inside multiple header lines. This may cause problems, so that is why it is tested here."; + set "after" + "This is somewhat longer header content, folded to fit inside multiple header lines. This may cause problems, so that is why it is tested here."; + + addheader :last "X-Some-Header-first" "${before}"; + addheader :last "X-Some-Header-last" "${after}"; + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header-first" { + test_fail "first header not added"; + } + + if not exists "x-some-header-last" { + test_fail "last header not added"; + } + + if not header :is "x-some-header-first" "${before}" { + test_fail "wrong first content added"; + } + + if not header :is "x-some-header-last" "${after}" { + test_fail "wrong last content added"; + } + + redirect "frop@example.com"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header-first" { + test_fail "first header not in redirected mail"; + } + + if not exists "x-some-header-last" { + test_fail "last header not in redirected mail"; + } + + if not header :is "x-some-header-first" "${before}" { + test_fail "wrong first header content in redirected mail "; + } + + if not header :is "x-some-header-last" "${after}" { + test_fail "wrong last header content in redirected mail "; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +/* + * Addheader - newlines + */ + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - newlines" { + set "before" text: +This is very long header content + containing newlines. This may + cause some problems, so that + is why it is tested here. +. +; + + set "after" text: +This is somewhat longer header content + containing newlines. This may + cause some problems, so that + is why it is tested here. +. +; + + set "before_out" + "This is very long header content containing newlines. This may cause some problems, so that is why it is tested here."; + + set "after_out" + "This is somewhat longer header content containing newlines. This may cause some problems, so that is why it is tested here."; + + addheader "X-Some-Header-first" "${before}"; + addheader :last "X-Some-Header-last" "${after}"; + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not exists "x-some-header-first" { + test_fail "first header not added"; + } + + if not exists "x-some-header-last" { + test_fail "last header not added"; + } + + if not header :is "x-some-header-first" "${before_out}" { + if header :matches "x-some-header-first" "*" {} + test_fail "wrong first content added: `${0}`"; + } + + if not header :is "x-some-header-last" "${after_out}" { + if header :matches "x-some-header-last" "*" {} + test_fail "wrong last content added: `${0}`"; + } + + redirect "frop@example.com"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not exists "x-some-header-first" { + test_fail "first header not in redirected mail"; + } + + if not exists "x-some-header-last" { + test_fail "last header not in redirected mail"; + } + + if not header :is "x-some-header-first" "${before_out}" { + if header :matches "x-some-header-first" "*" {} + test_fail "wrong first header content in redirected mail: `${0}`"; + } + + if not header :is "x-some-header-last" "${after_out}" { + if header :matches "x-some-header-last" "*" {} + test_fail "wrong last header content in redirected mail: `${0}`"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Addheader - implicit keep" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Header content"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "INBOX" 0 { + test_fail "message not stored"; + } + + if not size :over 76 { + test_fail "stored mail is not larger"; + } + + if size :over 107 { + test_fail "stored mail is too large"; + } + + if size :under 100 { + test_fail "stored mail is too small"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored message"; + } + + if not exists "x-some-header" { + test_fail "header not added to stored message"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added to stored message"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } +} + +test_set "message" "${message}"; +test "Addheader - UTF 8" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Это тест!"; + fileinto :create "folder6"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder6" 0 { + test_fail "message not stored"; + } + + if not exists "x-some-header" { + test_fail "header not added to stored message"; + } + + if not header :is "x-some-header" "Это тест!" { + if header :matches "x-some-header" "*" {} + test_fail "Bel character not retained: `${0}`"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } +} + +test_result_reset; + +test_set "message" "${message}"; +test "Addheader - devious characters" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Ring my ${hex:07}!"; + fileinto :create "folder7"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder7" 0 { + test_fail "message not stored"; + } + + if not exists "x-some-header" { + test_fail "header not added to stored message"; + } + + if header :is "x-some-header" "Ring my !" { + if header :matches "x-some-header" "*" {} + test_fail "Bel character not retained: `${0}`"; + } + + if not header :is "x-some-header" "Ring my ${hex:07}!" { + if header :matches "x-some-header" "*" {} + test_fail "Incorrect header value: `${0}`"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } +} diff --git a/pigeonhole/tests/extensions/editheader/alternating.svtest b/pigeonhole/tests/extensions/editheader/alternating.svtest new file mode 100644 index 0000000..44d459c --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/alternating.svtest @@ -0,0 +1,181 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "fileinto"; +require "mailbox"; +require "body"; + +require "editheader"; + +set "message" text: +From: stephan@example.com +To: timo@example.com +Subject: Frop! + +Frop! + +. +; + + +test_set "message" "${message}"; +test "Alternating - add; delete" { + addheader "X-Some-Header" "Header content"; + + if not exists "x-some-header" { + test_fail "header not added"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added"; + } + + redirect "frop@example.com"; + + deleteheader "X-Some-Header"; + + if exists "x-some-header" { + test_fail "header not deleted"; + } + + fileinto :create "folder1"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + /* redirected message */ + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not exists "x-some-header" { + test_fail "added header not in redirected mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in redirected mail "; + } + + /* stored message message */ + + if not test_message :folder "folder1" 0 { + test_fail "message not stored"; + } + + if exists "x-some-header" { + test_fail "added header still present stored mail"; + } +} + +test_result_reset; + +test_set "message" "${message}"; +test "Alternating - delete; add" { + deleteheader "Subject"; + + if exists "subject" { + test_fail "header not deleted"; + } + + redirect "frop@example.com"; + + addheader "Subject" "Friep!"; + + if not exists "subject" { + test_fail "header not added"; + } + + if not header :is "subject" "Friep!" { + test_fail "wrong content added"; + } + + fileinto :create "folder2"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + /* redirected message */ + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if exists "subject" { + test_fail "deleted header still present redirected mail"; + } + + /* stored message message */ + + if not test_message :folder "folder2" 0 { + test_fail "message not stored"; + } + + if not exists "subject" { + test_fail "added header not in stored mail"; + } + + if not header :is "subject" "Friep!" { + test_fail "wrong content in redirected mail "; + } +} + +test_result_reset; + +test_set "message" "${message}"; +test "Alternating - add :last; delete any" { + addheader :last "X-Some-Header" "Header content"; + + if not exists "x-some-header" { + test_fail "header not added"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content added"; + } + + redirect "frop@example.com"; + + deleteheader "X-Some-Other-Header"; + + if not exists "x-some-header" { + test_fail "header somehow deleted"; + } + + fileinto :create "folder3"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + /* redirected message */ + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not exists "x-some-header" { + test_fail "added header not in redirected mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in redirected mail "; + } + + /* stored message message */ + + if not test_message :folder "folder3" 0 { + test_fail "message not stored"; + } + + if not exists "x-some-header" { + test_fail "added header lost in stored mail"; + } + + if not header :is "x-some-header" "Header content" { + test_fail "wrong content in stored mail "; + } + +} + diff --git a/pigeonhole/tests/extensions/editheader/deleteheader.svtest b/pigeonhole/tests/extensions/editheader/deleteheader.svtest new file mode 100644 index 0000000..8b9d3ad --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/deleteheader.svtest @@ -0,0 +1,1115 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "fileinto"; +require "mailbox"; +require "body"; + +require "editheader"; + +set "message" text: +X-A: Onzinnige informatie +X-B: kun je maar beter +X-C: niet via e-mail versturen +From: stephan@example.com +X-D: en daarom is het nuttig +To: timo@example.com +Subject: Frop! +X-A: dit terstond te verwijderen, +X-B: omdat dit anders +X-C: alleen maar schijfruimte verspilt. + +Frop! + +. +; + +test_set "message" "${message}"; +test "Deleteheader - nonexistent" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader "X-Z"; + + if size :under 288 { + test_fail "message is shorter than original"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained"; + } + + redirect "frop@example.com"; + fileinto :create "folder1"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder1" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in stored mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in redirected mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_set "message" "${message}"; +test "Deleteheader - nonexistent (match)" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader :matches "X-Z" "*frop*"; + + if size :under 288 { + test_fail "message is shorter than original"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained"; + } + + redirect "frop@example.com"; + fileinto :create "folder1b"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder1b" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in stored mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in redirected mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Deleteheader - one" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader "X-D"; + + if not size :under 288 { + test_fail "edited message is not shorter"; + } + + if size :over 258 { + test_fail "edited message is too long"; + } + + if size :under 258 { + test_fail "edited message is too short"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained"; + } + + if exists "X-D" { + test_fail "X-D header not deleted"; + } + + redirect "frop@example.com"; + fileinto :create "folder2"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder2" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in stored mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in stored mail"; + } + + if exists "X-D" { + test_fail "X-D header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in redirected mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in redirected mail"; + } + + if exists "X-D" { + test_fail "X-D header not deleted in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Deleteheader - two (first)" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader "X-A"; + + if not size :under 288 { + test_fail "edited message is not shorter"; + } + + if size :over 226 { + test_fail "edited message is too long"; + } + + if size :under 226 { + test_fail "edited message is too short"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained"; + } + + if exists "X-A" { + test_fail "X-A header not deleted"; + } + + redirect "frop@example.com"; + fileinto :create "folder3"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder3" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in stored mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in stored mail"; + } + + if exists "X-A" { + test_fail "X-A header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in redirected mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in redirected mail"; + } + + if exists "X-A" { + test_fail "X-A header not deleted in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Deleteheader - two (last)" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader "X-C"; + + if not size :under 288 { + test_fail "edited message is not shorter"; + } + + if size :over 215 { + test_fail "edited message is too long"; + } + + if size :under 215 { + test_fail "edited message is too short"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A header not retained"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained"; + } + + if exists "X-C" { + test_fail "X-C header not deleted"; + } + + redirect "frop@example.com"; + fileinto :create "folder4"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder4" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A header not retained in stored mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in stored mail"; + } + + if exists "X-C" { + test_fail "X-C header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A header not retained in redirected mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in redirected mail"; + } + + if exists "X-C" { + test_fail "X-C header not deleted in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Deleteheader - :index" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader :index 1 "X-A"; + deleteheader :index 2 "X-C"; + + if not size :under 288 { + test_fail "edited message is not shorter"; + } + + if size :over 220 { + test_fail "edited message is too long"; + } + + if size :under 220 { + test_fail "edited message is too short"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A (2) header not retained"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C (1) header not retained"; + } + + if header :is "X-A" "Onzinnige informatie" { + test_fail "original X-A (1) header not deleted"; + } + + if header :is "X-C" "alleen maar schijfruimte verspilt." { + test_fail "original X-C (2) header not deleted"; + } + + redirect "frop@example.com"; + fileinto :create "folder5"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder5" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A (2) header not retained in stored mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C (1) header not retained in stored mail"; + } + + if header :is "X-A" "Onzinnige informatie" { + test_fail "original X-A (1) header not deleted in stored mail"; + } + + if header :is "X-C" "alleen maar schijfruimte verspilt." { + test_fail "original X-C (2) header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if not header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A (2) header not retained redirected mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-B (1) header not retained redirected mail"; + } + + if header :is "X-A" "Onzinnige informatie" { + test_fail "original X-A (1) header not deleted redirected mail"; + } + + if header :is "X-C" "alleen maar schijfruimte verspilt." { + test_fail "original X-B (2) header not deleted redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Deleteheader - :index :last" { + if size :over 288 { + test_fail "original message is longer than 288 bytes?!"; + } + + if size :under 288 { + test_fail "original message is shorter than 288 bytes?!"; + } + + deleteheader :index 1 :last "X-A"; + deleteheader :last :index 2 "X-C"; + + if size :over 221 { + test_fail "edited message is too long"; + } + + if size :under 221 { + test_fail "edited message is too short"; + } + + if not size :under 288 { + test_fail "edited message is not shorter"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained"; + } + + if not header :is "X-A" "Onzinnige informatie" { + test_fail "original X-A (1) header not retained"; + } + + if not header :is "X-C" "alleen maar schijfruimte verspilt." { + test_fail "original X-C (2) header not retained"; + } + + if header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A (2) header not deleted"; + } + + if header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C (1) header not deleted"; + } + + redirect "frop@example.com"; + fileinto :create "folder6"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder6" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-A" "Onzinnige informatie" { + test_fail "original X-A (1) header not retained in stored mail"; + } + + if not header :is "X-C" "alleen maar schijfruimte verspilt." { + test_fail "original X-C (2) header not retained in stored mail"; + } + + if header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A (2) header not deleted in stored mail"; + } + + if header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C (1) header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in redirected mail"; + } + + if header :is "X-A" "dit terstond te verwijderen," { + test_fail "original X-A (2) header not deleted redirected mail"; + } + + if header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-B (1) header not deleted redirected mail"; + } + + if not header :is "X-A" "Onzinnige informatie" { + test_fail "original X-A (1) header not retained redirected mail"; + } + + if not header :is "X-C" "alleen maar schijfruimte verspilt." { + test_fail "original X-B (2) header not retained redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message}"; +test "Deleteheader - implicit keep" { + deleteheader "X-D"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "INBOX" 0 { + test_fail "message not stored"; + } + + if not header :is "subject" "Frop!" { + test_fail "original subject header not retained in stored mail"; + } + + if not header :is "X-B" "omdat dit anders" { + test_fail "original X-B header not retained in stored mail"; + } + + if not header :is "X-C" "niet via e-mail versturen" { + test_fail "original X-C header not retained in stored mail"; + } + + if exists "X-D" { + test_fail "X-D header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } +} + +/* + * + */ + +test_result_reset; + +test_set "message" text: +X-A: Dit is een klein verhaaltje +X-B: om te testen of de correcte +X-C: informatie wordt herkend en +X-D: verwijderd. Zo valt goed te +X-A: zien dat het allemaal werkt +X-B: zoals het bedoeld is. Alles +X-C: wordt in een keer getest op +X-D: een wijze die efficient die +X-A: problemen naar voren brengt +X-B: die bij dit nieuwe deel van +X-C: de programmatuur naar voren +X-D: kunnen komen. Zo werkt het! + +Frop! +. +; + +test "Deleteheader - :matches" { + if size :over 417 { + test_fail "original message is longer than 417 bytes?!"; + } + + if size :under 417 { + test_fail "original message is shorter than 417 bytes?!"; + } + + deleteheader :matches "X-A" "*klein*"; + deleteheader :matches "X-B" "*bedoeld*"; + deleteheader :matches "X-C" "*programmatuur*"; + deleteheader :contains "X-D" ["verwijderd", "!"]; + + if not size :under 417 { + test_fail "edited message is not shorter"; + } + + if size :over 247 { + test_fail "edited message is too long"; + } + + if size :under 247 { + test_fail "edited message is too short"; + } + + if not header :is "X-A" "zien dat het allemaal werkt" { + test_fail "original X-A (2) header not retained"; + } + + if not header :is "X-A" "problemen naar voren brengt" { + test_fail "original X-A (3) header not retained"; + } + + if not header :is "X-B" "om te testen of de correcte" { + test_fail "original X-B (1) header not retained"; + } + + if not header :is "X-B" "die bij dit nieuwe deel van" { + test_fail "original X-B (3) header not retained"; + } + + if not header :is "X-C" "informatie wordt herkend en" { + test_fail "original X-C (1) header not retained"; + } + + if not header :is "X-C" "wordt in een keer getest op" { + test_fail "original X-C (2) header not retained"; + } + + if not header :is "X-D" "een wijze die efficient die" { + test_fail "original X-C (2) header not retained"; + } + + if header :is "X-A" "Dit is een klein verhaaltje" { + test_fail "original X-A (1) header not deleted"; + } + + if header :is "X-B" "zoals het bedoeld is. Alles" { + test_fail "original X-B (2) header not deleted"; + } + + if header :is "X-C" "de programmatuur naar voren" { + test_fail "original X-C (3) header not deleted"; + } + + if header :is "X-D" "verwijderd. Zo valt goed te" { + test_fail "original X-C (1) header not deleted"; + } + + if header :is "X-D" "kunnen komen. Zo werkt het!" { + test_fail "original X-C (3) header not deleted"; + } + + redirect "frop@example.com"; + fileinto :create "folder7"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder7" 0 { + test_fail "message not stored"; + } + + if not header :is "X-A" "zien dat het allemaal werkt" { + test_fail "original X-A (2) header not retained in stored mail"; + } + + if not header :is "X-A" "problemen naar voren brengt" { + test_fail "original X-A (3) header not retained in stored mail"; + } + + if not header :is "X-B" "om te testen of de correcte" { + test_fail "original X-B (1) header not retained in stored mail"; + } + + if not header :is "X-B" "die bij dit nieuwe deel van" { + test_fail "original X-B (3) header not retained in stored mail"; + } + + if not header :is "X-C" "informatie wordt herkend en" { + test_fail "original X-C (1) header not retained in stored mail"; + } + + if not header :is "X-C" "wordt in een keer getest op" { + test_fail "original X-C (2) header not retained in stored mail"; + } + + if not header :is "X-D" "een wijze die efficient die" { + test_fail "original X-C (2) header not retained in stored mail"; + } + + if header :is "X-A" "Dit is een klein verhaaltje" { + test_fail "original X-A (1) header not deleted in stored mail"; + } + + if header :is "X-B" "zoals het bedoeld is. Alles" { + test_fail "original X-B (2) header not deleted in stored mail"; + } + + if header :is "X-C" "de programmatuur naar voren" { + test_fail "original X-C (3) header not deleted in stored mail"; + } + + if header :is "X-D" "verwijderd. Zo valt goed te" { + test_fail "original X-C (1) header not deleted in stored mail"; + } + + if header :is "X-D" "kunnen komen. Zo werkt het!" { + test_fail "original X-C (3) header not deleted in stored mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not header :is "X-A" "zien dat het allemaal werkt" { + test_fail "original X-A (2) header not retained in redirected mail"; + } + + if not header :is "X-A" "problemen naar voren brengt" { + test_fail "original X-A (3) header not retained in redirected mail"; + } + + if not header :is "X-B" "om te testen of de correcte" { + test_fail "original X-B (1) header not retained in redirected mail"; + } + + if not header :is "X-B" "die bij dit nieuwe deel van" { + test_fail "original X-B (3) header not retained in redirected mail"; + } + + if not header :is "X-C" "informatie wordt herkend en" { + test_fail "original X-C (1) header not retained in redirected mail"; + } + + if not header :is "X-C" "wordt in een keer getest op" { + test_fail "original X-C (2) header not retained in redirected mail"; + } + + if not header :is "X-D" "een wijze die efficient die" { + test_fail "original X-C (2) header not retained in redirected mail"; + } + + if header :is "X-A" "Dit is een klein verhaaltje" { + test_fail "original X-A (1) header not deleted in redirected mail"; + } + + if header :is "X-B" "zoals het bedoeld is. Alles" { + test_fail "original X-B (2) header not deleted in redirected mail"; + } + + if header :is "X-C" "de programmatuur naar voren" { + test_fail "original X-C (3) header not deleted in redirected mail"; + } + + if header :is "X-D" "verwijderd. Zo valt goed te" { + test_fail "original X-C (1) header not deleted in redirected mail"; + } + + if header :is "X-D" "kunnen komen. Zo werkt het!" { + test_fail "original X-C (3) header not deleted in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + + +/* + * + */ + +set "message2" text: +X-A: Long folded header to test removal of folded + headers from a message. This is the top header. +X-B: First intermittent unfolded header +X-A: Long folded header to test removal of folded + headers from a message. This is the middle header. +X-B: Second intermittent unfolded header +X-A: Long folded header to test removal of folded + headers from a message. This is the bottom header, + which concludes the header of this message. + +Frop! +. +; + +test_result_reset; +test_set "message" "${message2}"; +test "Deleteheader - folded" { + deleteheader "X-A"; + + if exists "X-A" { + test_fail "original X-A (1) header not deleted"; + } + + if not header :is "X-B" "First intermittent unfolded header" { + test_fail "original X-B (2) header not retained"; + } + + if not header :is "X-B" "Second intermittent unfolded header" { + test_fail "original X-B (2) header not retained"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } + + redirect "frop@example.com"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if exists "X-A" { + test_fail "original X-A (1) header not deleted in redirected mail"; + } + + if not header :is "X-B" "First intermittent unfolded header" { + test_fail "original X-B (2) header not retained in redirected mail"; + } + + if not header :is "X-B" "Second intermittent unfolded header" { + test_fail "original X-B (2) header not retained in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + +test_result_reset; +test_set "message" "${message2}"; +test "Deleteheader - folded (match)" { + deleteheader :matches "X-A" "*header*"; + + if exists "X-A" { + test_fail "original X-A (1) header not deleted"; + } + + if not header :is "X-B" "First intermittent unfolded header" { + test_fail "original X-B (2) header not retained"; + } + + if not header :is "X-B" "Second intermittent unfolded header" { + test_fail "original X-B (2) header not retained"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } + + redirect "frop@example.com"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if exists "X-A" { + test_fail "original X-A (1) header not deleted in redirected mail"; + } + + if not header :is "X-B" "First intermittent unfolded header" { + test_fail "original X-B (2) header not retained in redirected mail"; + } + + if not header :is "X-B" "Second intermittent unfolded header" { + test_fail "original X-B (2) header not retained in redirected mail"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in redirected mail"; + } +} + + +/* + * TEST: Ignoring whitespace + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Help +X-A: Text +X-B: Text + +Text +. +; + +test "Ignoring whitespace" { + deleteheader :is "subject" "Help"; + deleteheader :is "x-a" "Text"; + deleteheader :is "x-b" "Text"; + + if exists "subject" { + test_fail "subject header not deleted"; + } + + if exists "x-a" { + test_fail "x-a header not deleted"; + } + + if exists "x-b" { + test_fail "x-b header not deleted"; + } +} + +/* + * TEST: Interaction with body test + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Hoppa + +Text +. +; + +test "Interaction with body test" { + addheader "X-Frop" "frop"; + + if body "!TEST!" {} + + deleteheader "subject"; + + if exists "subject" { + test_fail "subject header not deleted"; + } +} + diff --git a/pigeonhole/tests/extensions/editheader/errors.svtest b/pigeonhole/tests/extensions/editheader/errors.svtest new file mode 100644 index 0000000..1d1f24d --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors.svtest @@ -0,0 +1,164 @@ +require "vnd.dovecot.testsuite"; +require "comparator-i;ascii-numeric"; +require "relational"; +require "variables"; + +require "editheader"; + +test "Invalid field-name" { + if test_script_compile "errors/field-name.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "5" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :matches "*field name*X-field:*invalid*" { + test_fail "wrong error reported (1)"; + } + + if not test_error :index 2 :matches "*field name*X field*invalid*" { + test_fail "wrong error reported (2)"; + } + + if not test_error :index 3 :matches "*field name*X-field:*invalid*" { + test_fail "wrong error reported (3)"; + } + + if not test_error :index 4 :matches "*field name*X field*invalid*" { + test_fail "wrong error reported (4)"; + } +} + +test "Invalid field-name at runtime " { + if not test_script_compile "errors/field-name-runtime.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "run should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } + + if not test_error :matches "*field name*X-field:*invalid*" { + test_fail "wrong error reported"; + } +} + +test "Invalid field value" { + if test_script_compile "errors/field-value.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :matches "*value*Woah*invalid*" { + test_fail "wrong error reported (1): ${0}"; + } +} + +test "Command syntax (FIXME: count only)" { + if test_script_compile "errors/command-syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "10" { + test_fail "wrong number of errors reported"; + } +} + +/* + * TEST - Size limit + */ + +test "Size limit" { + if not test_script_compile "errors/size-limit.sieve" { + test_fail "compile should have succeeded"; + } + + test_config_set "sieve_editheader_max_header_size" "1024"; + test_config_reload :extension "editheader"; + + if test_script_compile "errors/size-limit.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + + +/* + * TEST - Size limit at runtime + */ + +test_config_set "sieve_editheader_max_header_size" ""; +test_config_reload :extension "editheader"; + +test "Size limit at runtime" { + if not test_script_compile "errors/size-limit-runtime.sieve" { + test_fail "compile should have succeeded"; + } + + if not test_script_run { + test_fail "run failed"; + } + + test_config_set "sieve_editheader_max_header_size" "1024"; + test_config_reload :extension "editheader"; + + if not test_script_compile "errors/size-limit-runtime.sieve" { + test_fail "compile should have succeeded"; + } + + if test_script_run { + test_fail "run should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } +} + +/* + * TEST - Implicit keep at runtime error + */ + +test_set "message" text: +From: stephan@example.com +To: tss@example.com +Subject: Frop + +Frop! +. +; + +test "Implicit keep at runtime error" { + if not test_script_compile "errors/runtime-error.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "run failed"; + } + + if test_result_execute { + test_fail "result execution should have failed"; + } + + if not test_message :folder "INBOX" 0 { + test_fail "message not stored (no implicit keep)"; + } + + if exists "X-Frop" { + test_fail "implicit keep message has editheader changes"; + } +} + diff --git a/pigeonhole/tests/extensions/editheader/errors/command-syntax.sieve b/pigeonhole/tests/extensions/editheader/errors/command-syntax.sieve new file mode 100644 index 0000000..8543e6d --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/command-syntax.sieve @@ -0,0 +1,42 @@ +require "editheader"; + +/* "addheader" [":last"] <field-name: string> <value: string> + */ + +# 1: missing field name and value +addheader; + +# 2: missing value +addheader "x-frop"; + +# 3: value not a string; number +addheader "x-frop" 2; + +# 4: value not a string; list +addheader "x-frop" ["frop"]; + +# 5: strange tag +addheader :tag "x-frop" "frop"; + +/* "deleteheader" [":index" <fieldno: number> [":last"]] + * [COMPARATOR] [MATCH-TYPE] + * <field-name: string> + * [<value-patterns: string-list>] + */ + +# 6: missing field name +deleteheader; + +# 7: :last tag without index +deleteheader :last "x-frop"; + +# 8: :index tag with string argument +deleteheader :index "frop" "x-frop"; + +# OK: match type without value patterns +deleteheader :matches "x-frop"; + +# 9: value patterns not a string(list) +deleteheader "x-frop" 1; + + diff --git a/pigeonhole/tests/extensions/editheader/errors/field-name-runtime.sieve b/pigeonhole/tests/extensions/editheader/errors/field-name-runtime.sieve new file mode 100644 index 0000000..3f34461 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/field-name-runtime.sieve @@ -0,0 +1,6 @@ +require "editheader"; +require "variables"; + +set "header" "X-field:"; + +addheader "${header}" "Frop"; diff --git a/pigeonhole/tests/extensions/editheader/errors/field-name.sieve b/pigeonhole/tests/extensions/editheader/errors/field-name.sieve new file mode 100644 index 0000000..469bfc8 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/field-name.sieve @@ -0,0 +1,19 @@ +require "editheader"; + +# Ok +addheader "X-field" "Frop"; + +# Invalid ':' +addheader "X-field:" "Frop"; + +# Invalid ' ' +addheader "X field" "Frop"; + +# Ok +deleteheader "X-field"; + +# Invalid ':' +deleteheader "X-field:"; + +# Invalid ' ' +deleteheader "X field"; diff --git a/pigeonhole/tests/extensions/editheader/errors/field-value.sieve b/pigeonhole/tests/extensions/editheader/errors/field-value.sieve new file mode 100644 index 0000000..c9f4eab --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/field-value.sieve @@ -0,0 +1,15 @@ +require "editheader"; +require "encoded-character"; + +# Ok +addheader "X-field" "Frop"; + +# Ok +addheader "X-field" "Frop +Frml"; + +# Invalid 'BELL'; but not an error +addheader "X-field" "Yeah${hex:07}!"; + +# Invalid 'NUL' +addheader "X-field" "Woah${hex:00}!"; diff --git a/pigeonhole/tests/extensions/editheader/errors/runtime-error.sieve b/pigeonhole/tests/extensions/editheader/errors/runtime-error.sieve new file mode 100644 index 0000000..b308d74 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/runtime-error.sieve @@ -0,0 +1,6 @@ +require "editheader"; +require "fileinto"; + +addheader "X-Frop" "Friep"; + +fileinto "Rediculous.non-existent.folder"; diff --git a/pigeonhole/tests/extensions/editheader/errors/size-limit-runtime.sieve b/pigeonhole/tests/extensions/editheader/errors/size-limit-runtime.sieve new file mode 100644 index 0000000..73a1437 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/size-limit-runtime.sieve @@ -0,0 +1,46 @@ +require "editheader"; +require "variables"; + +set "blob" text: +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +. +; + +addheader "x-frop" "${blob}"; diff --git a/pigeonhole/tests/extensions/editheader/errors/size-limit.sieve b/pigeonhole/tests/extensions/editheader/errors/size-limit.sieve new file mode 100644 index 0000000..598f5f9 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/errors/size-limit.sieve @@ -0,0 +1,43 @@ +require "editheader"; + +addheader "x-frop" text: +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +. +; diff --git a/pigeonhole/tests/extensions/editheader/execute.svtest b/pigeonhole/tests/extensions/editheader/execute.svtest new file mode 100644 index 0000000..e65cc5d --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/execute.svtest @@ -0,0 +1,57 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; +require "editheader"; + +/* + * Testsuite self-test + */ + +set "message" "."; +addheader "X-Some-Header" "Header content"; +test_result_reset; +test_set "message" "${message}"; + +/* + * Multi script + */ + +test_result_reset; + +test_set "message" text: +From: idiot@example.com +To: idiot@example.org +Subject: Frop! + +Frop. +. +; + +test_result_reset; +test "Multi script" { + if not test_multiscript [ + "execute/multiscript-before.sieve", + "execute/multiscript-personal.sieve", + "execute/multiscript-after.sieve" + ] { + test_fail "failed to run all scripts"; + } + + test_message :folder "INBOX" 0; + + if not header "subject" "Frop!" { + test_fail "keep not executed."; + } + + if not header "X-Before" "before" { + test_fail "No X-Before header"; + } + + if not header "X-Personal" "personal" { + test_fail "No X-Personal header"; + } + + if not header "X-After" "after" { + test_fail "No X-After header"; + } +} diff --git a/pigeonhole/tests/extensions/editheader/execute/multiscript-after.sieve b/pigeonhole/tests/extensions/editheader/execute/multiscript-after.sieve new file mode 100644 index 0000000..f11f02d --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/execute/multiscript-after.sieve @@ -0,0 +1,4 @@ +require "editheader"; + +addheader "X-After" "after"; + diff --git a/pigeonhole/tests/extensions/editheader/execute/multiscript-before.sieve b/pigeonhole/tests/extensions/editheader/execute/multiscript-before.sieve new file mode 100644 index 0000000..5c8a988 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/execute/multiscript-before.sieve @@ -0,0 +1,4 @@ +require "editheader"; + +addheader "X-Before" "before"; + diff --git a/pigeonhole/tests/extensions/editheader/execute/multiscript-personal.sieve b/pigeonhole/tests/extensions/editheader/execute/multiscript-personal.sieve new file mode 100644 index 0000000..92e82ac --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/execute/multiscript-personal.sieve @@ -0,0 +1,4 @@ +require "editheader"; + +addheader "X-Personal" "personal"; + diff --git a/pigeonhole/tests/extensions/editheader/protected.svtest b/pigeonhole/tests/extensions/editheader/protected.svtest new file mode 100644 index 0000000..148a9c8 --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/protected.svtest @@ -0,0 +1,173 @@ +require "vnd.dovecot.testsuite"; + +require "variables"; +require "encoded-character"; +require "editheader"; + +set "message" text: +Received: by example.com (Postfix, from userid 202) + id 32A131WFW23QWE4; Mon, 21 Nov 2011 05:25:26 +0200 (EET) +Delivery-date: Mon, 21 Nov 2011 04:26:04 +0100 +Auto-Submitted: yes +X-Friep: frop 3 +Subject: Frop! +From: stephan@example.com +To: tss@example.com + +Frop! +. +; + +test_set "message" "${message}"; +test "Default protected" { + if not exists "received" { + test_fail "received header did not exist in the first place"; + } + + if not exists "auto-submitted" { + test_fail "auto-submitted header did not exist in the first place"; + } + + deleteheader "received"; + deleteheader "auto-submitted"; + deleteheader "subject"; + + if not exists "received" { + test_fail "protected received header was deleted"; + } + + if not exists "auto-submitted" { + test_fail "protected auto-submitted header was deleted"; + } + + if exists "subject" { + test_fail "subject header cannot be protected, but it was not deleted"; + } +} + +test_config_set "sieve_editheader_protected" "subject delivery-date x-frop"; +test_config_reload :extension "editheader"; + +test_set "message" "${message}"; +test "Configured protected" { + if not exists "delivery-date" { + test_fail "received header did not exist in the first place"; + } + + if not exists "subject" { + test_fail "received header did not exist in the first place"; + } + + if exists "x-frop" { + test_fail "x-frop header already present"; + } + + deleteheader "delivery-date"; + deleteheader "subject"; + addheader "x-frop" "Frop!"; + + if not exists "delivery-date" { + test_fail "protected delivery-date header was deleted"; + } + + if exists "subject" { + test_fail "subject header cannot be protected, but it was not deleted"; + } + + if exists "x-frop" { + test_fail "protected x-frop header was added"; + } +} + +test_config_set "sieve_editheader_protected" ""; +test_config_set "sieve_editheader_forbid_add" "subject x-frop"; +test_config_set "sieve_editheader_forbid_delete" "subject x-friep"; +test_config_reload :extension "editheader"; + +test_set "message" "${message}"; +test "Configured forbid_add/forbid_delete" { + if not exists "delivery-date" { + test_fail "received header did not exist in the first place"; + } + + if not exists "subject" { + test_fail "received header did not exist in the first place"; + } + + if not exists "x-friep" { + test_fail "x-friep header did not exist in the first place"; + } + + if exists "x-frop" { + test_fail "x-frop header already present"; + } + + deleteheader "delivery-date"; + deleteheader "subject"; + deleteheader "x-friep"; + + if exists "delivery-date" { + test_fail "unprotected delivery-date header was not deleted"; + } + + if exists "subject" { + test_fail "subject header cannot be protected, but it was not deleted"; + } + + if not exists "x-friep" { + test_fail "protected x-friep header was deleted"; + } + + addheader "delivery-date" "Yesterday"; + addheader "subject" "Fropfrop!"; + addheader "x-frop" "Frop!"; + addheader "received" text: +by sieve.example.com (My little Sieve script) +id 3jhl22khhf23f; Mon, 24 Aug 2015 04:11:54 -0600; +. +; + addheader "auto-submitted" "no way"; + + if not header "delivery-date" "Yesterday" { + test_fail "unprotected delivery-date header was not added"; + } + + if not header "subject" "Fropfrop!" { + test_fail "subject header cannot be protected, but it was not added"; + } + + if exists "x-frop" { + test_fail "protected x-frop header was added"; + } + + if not header :contains "received" "sieve.example.com" { + test_fail "received header was not added"; + } + + if not header "auto-submitted" "no way" { + test_fail "autosubmitted header was not added"; + } +} + +/* + * TEST - Bad header configuration + */ + +test_config_set "sieve_editheader_protected" "${unicode:1F4A9} delivery-date"; +test_config_reload :extension "editheader"; + +test_set "message" "${message}"; +test "Bad header configuration" { + if not exists "delivery-date" { + test_fail "delivery-date header did not exist in the first place"; + } + + deleteheader "delivery-date"; + + if not exists "delivery-date" { + test_fail "protected delivery-date header was deleted"; + } +} + +test_config_set "sieve_editheader_protected" ""; +test_config_reload :extension "editheader"; diff --git a/pigeonhole/tests/extensions/editheader/utf8.svtest b/pigeonhole/tests/extensions/editheader/utf8.svtest new file mode 100644 index 0000000..159a71c --- /dev/null +++ b/pigeonhole/tests/extensions/editheader/utf8.svtest @@ -0,0 +1,97 @@ +require "vnd.dovecot.testsuite"; + +require "encoded-character"; +require "variables"; +require "editheader"; + +test_set "message" text: +Subject: Frop! +From: stephan@example.com +To: stephan@example.com + +Frop! +. +; + +test "UTF8 - add; get" { + set "comment" "Ein unerh${unicode:00F6}rt gro${unicode:00DF}er Test"; + + addheader "Comment" "${comment}"; + + if not exists "comment" { + test_fail "header not added"; + } + + if not header :is "comment" "${comment}" { + test_fail "wrong content added/retrieved"; + } + + redirect "frop@example.com"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + /* redirected message */ + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not exists "comment" { + test_fail "header not added in redirected mail"; + } + + if not header :is "comment" "${comment}" { + test_fail "wrong content added/retrieved from redirected mail"; + } +} + +test_result_reset; + +test_set "message" text: +Subject: Frop! +Comment: Ein =?utf-8?q?unerh=C3=B6rt_gro=C3=9Fer?= Test +X-Spam: no +From: stephan@example.com +To: stephan@example.com + +Frop! +. +; + +test "UTF8 - existing; delete other; get" { + set "comment" "Ein unerh${unicode:00F6}rt gro${unicode:00DF}er Test"; + + deleteheader "x-spam"; + + if not exists "comment" { + test_fail "header not present"; + } + + if not header :is "comment" "${comment}" { + test_fail "wrong content retrieved"; + } + + redirect "frop@example.com"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + /* redirected message */ + + if not test_message :smtp 0 { + test_fail "message not redirected"; + } + + if not exists "comment" { + test_fail "header not present in redirected mail"; + } + + if not header :is "comment" "${comment}" { + test_fail "wrong content retrieved from redirected mail"; + } +} + + diff --git a/pigeonhole/tests/extensions/encoded-character.svtest b/pigeonhole/tests/extensions/encoded-character.svtest new file mode 100644 index 0000000..150d812 --- /dev/null +++ b/pigeonhole/tests/extensions/encoded-character.svtest @@ -0,0 +1,180 @@ +require "vnd.dovecot.testsuite"; + +require "encoded-character"; +require "variables"; + +test "HEX equality one" { + if not string "${hex:42}" "B" { + test_fail "failed to match the string 'B'"; + } + + if string "${hex:42}" "b" { + test_fail "matched nonsense"; + } + + if string "${hex:42}" "" { + test_fail "substitution failed"; + } +} + +test "HEX equality one middle" { + if not string " ${hex:42} " " B " { + test_fail "failed to match the string ' B '"; + } + + if string " ${hex:42} " " b " { + test_fail "matched nonsense"; + } + + if string " ${hex:42} " " " { + test_fail "substitution failed"; + } +} + +test "HEX equality one begin" { + if not string "${hex:42} " "B " { + test_fail "failed to match the string 'B '"; + } + + if string "${hex:42} " " b" { + test_fail "matched nonsense"; + } + + if string "${hex:42} " " " { + test_fail "substitution failed"; + } +} + +test "HEX equality one end" { + if not string " ${hex:42}" " B" { + test_fail "failed to match the string ' B'"; + } + + if string " ${hex:42}" " b " { + test_fail "matched nonsense"; + } + + if string " ${hex:42}" " " { + test_fail "substitution failed"; + } +} + +test "HEX equality two triple" { + if not string "${hex:42 61 64}${hex: 61 73 73}" "Badass" { + test_fail "failed to match the string 'Badass'"; + } + + if string "${hex:42 61 64}${hex: 61 73 73}" "Sadass" { + test_fail "matched nonsense"; + } + + if string "${hex:42 61 64}${hex: 61 73 73}" "" { + test_fail "substitution failed"; + } +} + +test "HEX equality braindead" { + if not string "${hex:42 72 61 69 6E 64 65 61 64}" "Braindead" { + test_fail "failed to match the string 'Braindead'"; + } + + if string "${hex:42 72 61 69 6E 64 65 61 64}" "Brian Nut" { + test_fail "matched nonsense"; + } +} + +test "Syntax errors" { + if anyof( not string "$" "${hex:24}", not string "$ " "${hex:24} ", not string " $" " ${hex:24}" ) { + test_fail "loose $ handled inappropriately"; + } + + if anyof( not string "${" "${hex:24}{", not string "a${" "a${hex:24}{", not string "${a" "${hex:24}{a" ) { + test_fail "loose ${ handled inappropriately"; + } + + if anyof( not string "${}" "${hex:24}{}", not string "b${}" "b${hex:24}{}", not string "${}b" "${hex:24}{}b" ) { + test_fail "entirely missing content handled inappropriately"; + } + + if not string "${:}" "${hex:24}{:}" { + test_fail "missing content handled inappropriately"; + } + + if not string "${hex:}" "${hex:24}{hex:}" { + test_fail "missing hex content handled inappropriately"; + } + + if not string "${unicode:}" "${hex:24}{unicode:}" { + test_fail "missing unicode content handled inappropriately"; + } + + if not string "${hex:sss}" "${hex:24}{hex:sss}" { + test_fail "erroneous hex content handled inappropriately"; + } + + if not string "${unicode:ttt}" "${hex:24}{unicode:ttt}" { + test_fail "erroneous unicode content handled inappropriately"; + } + + if not string "${hex:aa aa" "${hex:24}{hex:aa aa" { + test_fail "unterminated hex content handled inappropriately"; + } + + if not string "${unicode: aaaa aaaa" "${hex:24}{unicode: aaaa aaaa" { + test_fail "unterminated unicode content handled inappropriately"; + } +} + +/* + * RFC Examples + */ + +test "RFC Examples" { + if not string "$${hex:40}" "$@" { + test_fail "failed RFC example 1"; + } + + if not string "${hex: 40 }" "@" { + test_fail "failed RFC example 2"; + } + + if not string "${HEX: 40}" "@" { + test_fail "failed RFC example 3"; + } + + if not string "${hex:40" "${hex:40" { + test_fail "failed RFC example 4"; + } + + if not string "${hex:400}" "${hex:400}" { + test_fail "failed RFC example 5"; + } + + if not string "${hex:4${hex:30}}" "${hex: 24}{hex:40}" { + test_fail "failed RFC example 6"; + } + + if not string "${unicode:40}" "@" { + test_fail "failed RFC example 7"; + } + + if not string "${ unicode:40}" "${ unicode:40}" { + test_fail "failed RFC example 8"; + } + + if not string "${UNICODE:40}" "@" { + test_fail "failed RFC example 9"; + } + + if not string "${UnICoDE:0000040}" "@" { + test_fail "failed RFC example 10"; + } + + if not string "${Unicode:40}" "@" { + test_fail "failed RFC example 11"; + } + + if not string "${Unicode:Cool}" "${Unicode:Cool}" { + test_fail "failed RFC example 12"; + } +} diff --git a/pigeonhole/tests/extensions/enotify/basic.svtest b/pigeonhole/tests/extensions/enotify/basic.svtest new file mode 100644 index 0000000..2a03aee --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/basic.svtest @@ -0,0 +1,15 @@ +require "vnd.dovecot.testsuite"; +require "enotify"; + +test "Execute" { + /* Test to catch runtime segfaults */ + if valid_notify_method + "mailto:stephan@example.com" { + + /* Test to catch runtime segfaults */ + notify + :message "This is probably very important" + :importance "1" + "mailto:stephan@example.com%2cstephan@example.org?subject=Important%20message%20received"; + } +} diff --git a/pigeonhole/tests/extensions/enotify/encodeurl.svtest b/pigeonhole/tests/extensions/enotify/encodeurl.svtest new file mode 100644 index 0000000..d334dd3 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/encodeurl.svtest @@ -0,0 +1,359 @@ +require "vnd.dovecot.testsuite"; +require "encoded-character"; +require "variables"; +require "enotify"; + +/* + * :encodeurl simple + */ + +test ":encodeurl simple" { + set :encodeurl "url_data" "\\frop\\&fruts/^@"; + + if not string :is :comparator "i;octet" "${url_data}" "%5Cfrop%5C%26fruts%2F%5E%40" { + test_fail "url data encoded incorrectly '${url_data}'"; + } +} + +/* + * :encodeurl variable size limit + */ + +test_config_set "sieve_variables_max_variable_size" "4000"; +test_config_reload :extension "variables"; + +set "a" text: +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@ +. +; + +test ":encodeurl variable size limit" { + set :length "alen" "${a}"; + + if not string "${alen}" "4000" { + test_fail "variable 'a' not 4000 bytes long (${alen})"; + } + + set :encodeurl "b" "${a}"; + set :length "blen" "${b}"; + + if not string "${blen}" "3999" { + test_fail "variable 'b' not 3999 bytes long (${blen})"; + } + + set :encodeurl "c" "0${a}"; + set :length "clen" "${c}"; + + if not string "${clen}" "4000" { + test_fail "variable 'c' not 4000 bytes long (${clen})"; + } + + set "cmt" "%40%40%40%40%40%40%40%40%40%40%40%40"; + set "cmt" "${cmt}%40%40%40%40%40%40%40%40%40%40%40%0D%0A"; + set "cmh" "${cmt}${cmt}${cmt}${cmt}"; + set "cm" "${cmh}${cmh}${cmh}${cmh}${cmh}${cmh}${cmh}${cmh}${cmh}${cmh}"; + set "cm" "${cm}${cmh}${cmh}${cmh}"; + set "cm" "0${cm}${cmt}%40%40%40%40%40%40%40%40"; + + if not string :is "${c}" "${cm}" { + test_fail "variable 'c' has unexpected value"; + } + + set :encodeurl "d" "00${a}"; + set :length "dlen" "${d}"; + + if not string "${dlen}" "3998" { + test_fail "variable 'd' not 3998 bytes long (${dlen})"; + } +} + +/* + * :encodeurl variable size limit UTF-8 + */ + +test_config_set "sieve_variables_max_variable_size" "4000"; +test_config_reload :extension "variables"; + +set "a" text: +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03}${unicode:4e03} +. +; + +test ":encodeurl variable size limit UTF-8" { + set :length "alen" "${a}"; + + if not string "${alen}" "546" { + test_fail "variable 'a' not 549 characters long (${alen})"; + } + + set :encodeurl "b" "${a}"; + set :length "blen" "${b}"; + + if not string "${blen}" "3978" { + test_fail "variable 'b' not 3978 bytes long (${blen})"; + } + + set :encodeurl "c" "${a}${unicode:4e00}${unicode:4e00}"; + set :length "clen" "${c}"; + + if not string "${clen}" "3996" { + test_fail "variable 'c' not 3996 bytes long (${clen})"; + } + + set :encodeurl "d" "${a}${unicode:4e00}${unicode:4e00}${unicode:4e00}"; + set :length "dlen" "${d}"; + + if not string "${dlen}" "3996" { + test_fail "variable 'd' not 3996 bytes long (${dlen})"; + } + + set :encodeurl "e" "0000${a}${unicode:4e00}${unicode:4e00}"; + set :length "elen" "${e}"; + + if not string "${elen}" "4000" { + test_fail "variable 'e' not 4000 bytes long (${elen})"; + } + + set :encodeurl "f" "00000${a}${unicode:4e00}${unicode:4e00}"; + set :length "flen" "${f}"; + + if not string "${flen}" "3992" { + test_fail "variable 'f' not 3992 bytes long (${flen})"; + } +} diff --git a/pigeonhole/tests/extensions/enotify/errors.svtest b/pigeonhole/tests/extensions/enotify/errors.svtest new file mode 100644 index 0000000..5af36df --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/errors.svtest @@ -0,0 +1,45 @@ +require "vnd.dovecot.testsuite"; +require "comparator-i;ascii-numeric"; +require "relational"; + +require "enotify"; + +test "Invalid URI (FIXME: count only)" { + if test_script_compile "errors/uri.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + +test "Invalid mailto URI (FIXME: count only)" { + if test_script_compile "errors/uri-mailto.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "7" { + test_fail "wrong number of errors reported"; + } +} + +test "Invalid mailto :from address (FIXME: count only)" { + if test_script_compile "errors/from-mailto.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +test "Invalid :options argument (FIXME: count only)" { + if test_script_compile "errors/options.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "6" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/enotify/errors/from-mailto.sieve b/pigeonhole/tests/extensions/enotify/errors/from-mailto.sieve new file mode 100644 index 0000000..d519256 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/errors/from-mailto.sieve @@ -0,0 +1,7 @@ +require "enotify"; + +# 1: Invalid from address +notify :from "stephan#example.org" "mailto:stephan@example.com"; + +# 2: Empty from address +notify :from "" "mailto:stephan@example.com"; diff --git a/pigeonhole/tests/extensions/enotify/errors/options.sieve b/pigeonhole/tests/extensions/enotify/errors/options.sieve new file mode 100644 index 0000000..58d2265 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/errors/options.sieve @@ -0,0 +1,18 @@ +require "enotify"; + +# 1: empty option +notify :options "" "mailto:stephan@example.org"; + +# 2: invalid option name syntax +notify :options "frop" "mailto:stephan@example.org"; + +# 3: invalid option name syntax +notify :options "_frop=" "mailto:stephan@example.org"; + +# 4: invalid option name syntax +notify :options "=frop" "mailto:stephan@example.org"; + +# 5: invalid value +notify :options "frop=frml +frop" "mailto:stephan@example.org"; + diff --git a/pigeonhole/tests/extensions/enotify/errors/uri-mailto.sieve b/pigeonhole/tests/extensions/enotify/errors/uri-mailto.sieve new file mode 100644 index 0000000..2aced86 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/errors/uri-mailto.sieve @@ -0,0 +1,20 @@ +require "enotify"; + +# 1: Invalid character in to part +notify "mailto:stephan@example.org;?header=frop"; + +# 2: Invalid character in hname +notify "mailto:stephan@example.org?header<=frop"; + +# 3: Invalid character in hvalue +notify "mailto:stephan@example.org?header=fr>op"; + +# 4: Invalid header name +notify "mailto:stephan@example.org?header:=frop"; + +# 5: Invalid recipient +notify "mailto:stephan%23example.org"; + +# 6: Invalid to header recipient +notify "mailto:stephan@example.org?to=nico%23frop.example.org"; + diff --git a/pigeonhole/tests/extensions/enotify/errors/uri.sieve b/pigeonhole/tests/extensions/enotify/errors/uri.sieve new file mode 100644 index 0000000..13ead81 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/errors/uri.sieve @@ -0,0 +1,5 @@ +require "enotify"; + +# 1: Invalid url scheme +notify "snailto:stephan@example.org"; + diff --git a/pigeonhole/tests/extensions/enotify/execute.svtest b/pigeonhole/tests/extensions/enotify/execute.svtest new file mode 100644 index 0000000..cd6486d --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute.svtest @@ -0,0 +1,99 @@ +require "vnd.dovecot.testsuite"; +require "relational"; + + +/* + * Execution testing (currently just meant to trigger any segfaults) + */ + +test "RFC Example 1" { + if not test_script_compile "execute/draft-rfc-ex1.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +test "RFC Example 2" { + if not test_script_compile "execute/draft-rfc-ex2.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +/* tel: not supported +test "RFC Example 3" { + if not test_script_compile "execute/draft-rfc-ex3.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} +*/ + +/* tel: and xmmp: not supported +test "RFC Example 5" { + if not test_script_compile "execute/draft-rfc-ex5.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} +*/ + +test "RFC Example 6" { + if not test_script_compile "execute/draft-rfc-ex6.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +test "Duplicate recipients" { + if not test_script_compile "execute/duplicates.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if test_result_action :count "ne" "2" { + test_fail "second notify action was discarded entirely"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} diff --git a/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex1.sieve b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex1.sieve new file mode 100644 index 0000000..6747d7b --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex1.sieve @@ -0,0 +1,26 @@ +require ["enotify", "fileinto", "variables"]; + +if header :contains "from" "boss@example.org" { + notify :importance "1" + :message "This is probably very important" + "mailto:alm@example.com"; + # Don't send any further notifications + stop; +} + +if header :contains "to" "sievemailinglist@example.org" { + # :matches is used to get the value of the Subject header + if header :matches "Subject" "*" { + set "subject" "${1}"; + } + + # :matches is used to get the value of the From header + if header :matches "From" "*" { + set "from" "${1}"; + } + + notify :importance "3" + :message "[SIEVE] ${from}: ${subject}" + "mailto:alm@example.com"; + fileinto "INBOX.sieve"; +} diff --git a/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex2.sieve b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex2.sieve new file mode 100644 index 0000000..a5c6a26 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex2.sieve @@ -0,0 +1,22 @@ +require ["enotify", "fileinto", "variables", "envelope"]; + +if header :matches "from" "*@*.example.org" { + # :matches is used to get the MAIL FROM address + if envelope :all :matches "from" "*" { + set "env_from" " [really: ${1}]"; + } + + # :matches is used to get the value of the Subject header + if header :matches "Subject" "*" { + set "subject" "${1}"; + } + + # :matches is used to get the address from the From header + if address :matches :all "from" "*" { + set "from_addr" "${1}"; + } + + notify :message "${from_addr}${env_from}: ${subject}" + "mailto:alm@example.com"; +} + diff --git a/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex3.sieve b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex3.sieve new file mode 100644 index 0000000..a7b4a64 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex3.sieve @@ -0,0 +1,31 @@ +require ["enotify", "variables"]; + +set "notif_method" + "xmpp:tim@example.com?message;subject=SIEVE;body=You%20got%20mail"; + +if header :contains "subject" "Your dog" { + set "notif_method" "tel:+14085551212"; +} + +if header :contains "to" "sievemailinglist@example.org" { + set "notif_method" ""; +} + +if not string :is "${notif_method}" "" { + notify "${notif_method}"; +} + +if header :contains "from" "boss@example.org" { + # :matches is used to get the value of the Subject header + if header :matches "Subject" "*" { + set "subject" "${1}"; + } + + # don't need high importance notification for + # a 'for your information' + if not header :contains "subject" "FYI:" { + notify :importance "1" :message "BOSS: ${subject}" + "tel:+14085551212"; + } +} + diff --git a/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex5.sieve b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex5.sieve new file mode 100644 index 0000000..c6b7dc6 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex5.sieve @@ -0,0 +1,11 @@ +require ["enotify"]; + +if notify_method_capability + "xmpp:tim@example.com?message;subject=SIEVE" + "Online" + "yes" { + notify :importance "1" :message "You got mail" + "xmpp:tim@example.com?message;subject=SIEVE"; +} else { + notify :message "You got mail" "tel:+14085551212"; +} diff --git a/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex6.sieve b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex6.sieve new file mode 100644 index 0000000..6a65c64 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute/draft-rfc-ex6.sieve @@ -0,0 +1,5 @@ +require ["enotify", "variables"]; + +set :encodeurl "body_param" "Safe body&evil=evilbody"; + +notify "mailto:tim@example.com?body=${body_param}"; diff --git a/pigeonhole/tests/extensions/enotify/execute/duplicates.sieve b/pigeonhole/tests/extensions/enotify/execute/duplicates.sieve new file mode 100644 index 0000000..17f2388 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/execute/duplicates.sieve @@ -0,0 +1,4 @@ +require "enotify"; + +notify :message "Incoming stupidity." "mailto:stephan@example.org%2cstephan@friep.example.com%2cidiot@example.org"; +notify :message "There it is." "mailto:tss@example.net%2cstephan@example.org%2cidiot@example.org%2cnico@frop.example.org%2cstephan@friep.example.com"; diff --git a/pigeonhole/tests/extensions/enotify/mailto.svtest b/pigeonhole/tests/extensions/enotify/mailto.svtest new file mode 100644 index 0000000..68d8daa --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/mailto.svtest @@ -0,0 +1,541 @@ +require "vnd.dovecot.testsuite"; +require "enotify"; +require "relational"; +require "envelope"; +require "variables"; +require "comparator-i;ascii-numeric"; + +/* + * Simple test + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Simple" { + notify "mailto:stephan@example.org"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not header :matches "Auto-Submitted" "auto-notified*" { + test_fail "auto-submitted header set inappropriately"; + } + + if not exists "X-Sieve" { + test_fail "x-sieve header missing from outgoing message"; + } + + if anyof ( + not header :matches "x-priority" "3 *", + not header "importance" "normal") { + + test_fail "default priority is not normal"; + } +} + +/* + * Multiple recipients + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Multiple recipients" { + notify "mailto:timo@example.com%2cstephan@dovecot.example.net?cc=postmaster@frop.example.org&subject=Frop%20received"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :is "to" "timo@example.com" { + test_fail "first To address missing"; + } + + if not address :is "to" "stephan@dovecot.example.net" { + test_fail "second To address missing"; + } + + if not address :is "cc" "postmaster@frop.example.org" { + test_fail "first Cc address missing"; + } + + if not address :count "eq" :comparator "i;ascii-numeric" "to" "2" { + test_fail "too many recipients in To header"; + } + + if not address :count "eq" :comparator "i;ascii-numeric" "cc" "1" { + test_fail "too many recipients in Cc header"; + } + + if not header "subject" "Frop received" { + test_fail "subject header set incorrectly"; + } + + test_message :smtp 1; + + if not header :matches "Auto-Submitted" "auto-notified*" { + test_fail "auto-submitted header not found for second message"; + } + + test_message :smtp 2; + + if not header :matches "Auto-Submitted" "auto-notified*" { + test_fail "auto-submitted header not found for third message"; + } +} + +/* + * Duplicate recipients + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Duplicate recipients" { + notify "mailto:timo@example.com%2cstephan@dovecot.example.net?cc=stephan@dovecot.example.net"; + notify "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if address "Cc" "stephan@dovecot.example.net" { + test_fail "duplicate recipient not removed from first message"; + } + + test_message :smtp 1; + + if address "Cc" "timo@example.com" { + test_fail "duplicate recipient not removed from second message"; + } +} + + +/* + * Notifying on automated messages + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Auto-submitted: auto-notify +Subject: Frop! + +Klutsefluts. +. +; + +test "Notifying on automated messages" { + notify "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if test_message :smtp 0 { + test_fail "notified of auto-submitted message"; + } +} + +/* + * Envelope + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_result_reset; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "bertus@frop.example.org"; + +test "Envelope" { + notify "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not envelope :localpart :is "from" "postmaster" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope sender set incorrectly"; + } + + test_message :smtp 1; + + if not envelope :localpart :is "from" "postmaster" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope sender set incorrectly"; + } +} + +/* + * Envelope :from + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "bertus@frop.example.org"; + +test_result_reset; + +test "Envelope :from" { + notify :from "nico@frop.example.org" + "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not envelope :is "from" "nico@frop.example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope sender set incorrectly"; + } + + test_message :smtp 1; + + if not envelope :is "from" "nico@frop.example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope sender set incorrectly"; + } +} + +/* + * Envelope <> + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.from" "<>"; +test_set "envelope.to" "bertus@frop.example.org"; + +test_result_reset; + +test "Envelope <>" { + notify :from "nico@frop.example.org" + "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not envelope :is "from" "" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope recipient set incorrectly"; + } + + test_message :smtp 1; + + if not envelope :is "from" "" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope recipient set incorrectly"; + } +} + +/* + * Envelope config - sender + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "bertus@frop.example.org"; + +test_config_set "sieve_notify_mailto_envelope_from" + "sender"; +test_config_reload :extension "enotify"; +test_result_reset; + +test "Envelope config - sender" { + notify :from "nico@frop.example.org" + "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "sirius@example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope recipient set incorrectly"; + } + + test_message :smtp 1; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "sirius@example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope recipient set incorrectly"; + } +} + +/* + * Envelope config - recipient + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "bertus@frop.example.org"; + +test_config_set "sieve_notify_mailto_envelope_from" + "recipient"; +test_config_reload :extension "enotify"; +test_result_reset; + +test "Envelope config - recipient" { + notify :from "nico@frop.example.org" + "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "bertus@frop.example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope recipient set incorrectly"; + } + + test_message :smtp 1; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "bertus@frop.example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope recipient set incorrectly"; + } +} + +/* + * Envelope config - user_email + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "bertus@frop.example.org"; + +test_config_set "sieve_notify_mailto_envelope_from" + "user_email"; +test_config_set "sieve_user_email" "b.wortel@example.org"; +test_config_reload; +test_config_reload :extension "enotify"; +test_result_reset; + +test "Envelope config - user_email" { + notify :from "nico@frop.example.org" + "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "b.wortel@example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope recipient set incorrectly"; + } + + test_message :smtp 1; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "b.wortel@example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope recipient set incorrectly"; + } +} + +/* + * UTF-8 addresses + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "UTF-8 address" { + set "to" "=?utf-8?q?G=C3=BCnther?= M. Karotte <g.m.karotte@example.com>"; + set "cc" "Dieter T. =?utf-8?q?Stoppelr=C3=BCbe?= <d.t.stoppelruebe@example.com>"; + + set :encodeurl "to_enc" "${to}"; + set :encodeurl "cc_enc" "${cc}"; + + notify "mailto:?to=${to_enc}&cc=${cc_enc}"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + set "expected" "Günther M. Karotte <g.m.karotte@example.com>"; + if not header :is "to" "${expected}" { + if header :matches "to" "*" { set "decoded" "${1}"; } + + test_fail text: +to header is not encoded/decoded properly: +expected: ${expected} +decoded: ${decoded} +. +; + } + + set "expected" "Dieter T. Stoppelrübe <d.t.stoppelruebe@example.com>"; + if not header :is "cc" "${expected}" { + if header :matches "cc" "*" { set "decoded" "${1}"; } + + test_fail text: +to header is not encoded/decoded properly: +expected: ${expected} +decoded: ${decoded} +. +; + } +} diff --git a/pigeonhole/tests/extensions/enotify/notify_method_capability.svtest b/pigeonhole/tests/extensions/enotify/notify_method_capability.svtest new file mode 100644 index 0000000..0d13477 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/notify_method_capability.svtest @@ -0,0 +1,12 @@ +require "vnd.dovecot.testsuite"; +require "enotify"; + +test "Mailto" { + if not notify_method_capability :is "mailto:stephan@example.org" "online" "maybe" { + test_fail "test should have matched"; + } + + if notify_method_capability :is "mailto:stephan@example.org" "online" "yes" { + test_fail "test should not have matched"; + } +} diff --git a/pigeonhole/tests/extensions/enotify/valid_notify_method.svtest b/pigeonhole/tests/extensions/enotify/valid_notify_method.svtest new file mode 100644 index 0000000..35255d6 --- /dev/null +++ b/pigeonhole/tests/extensions/enotify/valid_notify_method.svtest @@ -0,0 +1,31 @@ +require "vnd.dovecot.testsuite"; + +require "enotify"; + +test "Mailto: invalid header name" { + if valid_notify_method + "mailto:stephan@example.org?header:=frop" { + test_fail "invalid uri accepted"; + } +} + +test "Mailto: invalid recipient" { + if valid_notify_method + "mailto:stephan%23example.org" { + test_fail "invalid uri accepted"; + } +} + +test "Mailto: invalid to header recipient" { + if valid_notify_method + "mailto:stephan@example.org?to=nico%23frop.example.org" { + test_fail "invalid uri accepted"; + } +} + +test "Mailto: valid URI" { + if not valid_notify_method + "mailto:stephan@example.org" { + test_fail "valid uri denied"; + } +} diff --git a/pigeonhole/tests/extensions/envelope.svtest b/pigeonhole/tests/extensions/envelope.svtest new file mode 100644 index 0000000..9cf3b8b --- /dev/null +++ b/pigeonhole/tests/extensions/envelope.svtest @@ -0,0 +1,244 @@ +require "vnd.dovecot.testsuite"; + +require "envelope"; + +/* + * Empty envelope addresses + */ + +/* RFC 5228, Section 5.4: The null reverse-path is matched against as the empty + * string, regardless of the ADDRESS-PART argument specified. + */ + +test "Envelope - from empty" { + /* Return_path: "" */ + + test_set "envelope.from" ""; + + if not envelope :all :is "from" "" { + test_fail "failed to (:all :is)-match a \"\" return path"; + } + + if not envelope :all :contains "from" "" { + test_fail "failed to (:all :contains)-match a \"\" return path"; + } + + if not envelope :domain :is "from" "" { + test_fail "failed to (:domain :is)-match a \"\" return path"; + } + + if not envelope :domain :contains "from" "" { + test_fail "failed to (:domain :contains)-match a \"\" return path"; + } + + /* Return path: <> */ + + test_set "envelope.from" "<>"; + + if not envelope :all :is "from" "" { + test_fail "failed to (:all :is)-match a <> return path"; + } + + if not envelope :all :contains "from" "" { + test_fail "failed to (:all :contains)-match a <> return path"; + } + + if not envelope :domain :is "from" "" { + test_fail "failed to (:domain :is)-match a <> return path"; + } + + if not envelope :domain :contains "from" "" { + test_fail "failed to (:domain :contains)-match a <> return path"; + } + + if envelope :all :is "from" "nico@frop.example.org" { + test_fail "envelope test matches nonsense"; + } +} + +/* + * Invalid envelope addresses + */ + +test "Envelope - invalid paths" { + /* Return_path: "hutsefluts" */ + + test_set "envelope.from" "hutsefluts@"; + test_set "envelope.to" "knurft@"; + + if envelope :all :is "from" "hutsefluts@" { + test_fail ":all address part matched syntactically incorrect reverse path"; + } + if envelope :all :is "to" "knurft@" { + test_fail ":all address part matched syntactically incorrect forward path"; + } +} + +/* + * Syntax errors + */ + +test "Envelope - syntax errors" { + /* Control */ + test_set "envelope.from" "<stephan@example.org>"; + if not envelope :all :is "from" "stephan@example.org" { + test_fail "correct control test failed"; + } + + # Duplicate < + test_set "envelope.from" "<<stephan@example.org>"; + if envelope :all :is "from" "stephan@example.org" { + test_fail "failed to recognize syntax error (1)"; + } + + # Spurious > + test_set "envelope.from" "stephan@example.org>"; + if envelope :all :is "from" "stephan@example.org" { + test_fail "failed to recognize syntax error (2)"; + } + + # Missing > + test_set "envelope.from" "<stephan@example.org"; + if envelope :all :is "from" "stephan@example.org" { + test_fail "failed to recognize syntax error (3)"; + } + + # No @ + test_set "envelope.from" "<stephan example.org>"; + if envelope :domain :contains "from" "example" { + test_fail "failed to recognize syntax error (4)"; + } + + # Duplicate @ + test_set "envelope.from" "<stephan@@example.org>"; + if envelope :domain :contains "from" "example" { + test_fail "failed to recognize syntax error (5)"; + } +} + +/* + * Ignoring source routes + */ + +test "Envelope - source route" { + /* Single */ + test_set "envelope.from" "<@cola.example.org:stephan@example.org>"; + if not envelope :localpart :is "from" "stephan" { + test_fail "parsing path with source route (single) failed"; + } + + /* Dual */ + test_set "envelope.from" "<@cola.example.org,@mx.utwente.nl:stephan@example.org>"; + if not envelope :localpart :is "from" "stephan" { + test_fail "parsing path with source route (dual) failed"; + } + + /* Multiple */ + test_set "envelope.from" "<@cola.example.org,@mx.utwente.nl,@smtp.example.net:stephan@example.org>"; + if not envelope :localpart :is "from" "stephan" { + test_fail "parsing path with source route (multiple) failed"; + } +} + +test "Envelope - source route errors" { + test_set "envelope.to" "<cola.example.org:stephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (1)"; + } + + test_set "envelope.to" "<@.example.org:stephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (2)"; + } + + test_set "envelope.to" "<@cola..nl:stephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (3)"; + } + + test_set "envelope.to" "<@cola.example.orgstephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (4)"; + } + + test_set "envelope.to" "<@cola.example.org@mx.utwente.nl:stephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (5)"; + } + + test_set "envelope.to" "<@cola.example.org,mx.utwente.nl:stephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (6)"; + } + + test_set "envelope.to" "<@cola.example.org,@mx.utwente.nl,stephan@example.org>"; + if envelope :domain :contains "to" "" { + test_fail "parsing syntactically incorrect path should have failed (7)"; + } +} + +test "Envelope - local part only" { + test_set "envelope.to" "<MAILER-DAEMON>"; + if not envelope :is "to" "MAILER-DAEMON" { + test_fail "failed to parse local_part only path"; + } + + test_set "envelope.to" "MAILER-DAEMON@"; + if envelope :is "to" "MAILER-DAEMON" { + test_fail "parsing syntactically incorrect path with missing domain"; + } + + test_set "envelope.to" "<MAILER-DAEMON>"; + if not envelope :is "to" "MAILER-DAEMON" { + test_fail "failed to parse local_part only path with angle brackets"; + } +} + +test "Envelope - Japanese localpart" { + test_set "envelope.to" ".japanese@example.com"; + if not envelope :localpart :is "to" ".japanese" { + test_fail "failed to parse japanese local_part (1)"; + } + + test_set "envelope.to" "japanese.@example.com"; + if not envelope :localpart :is "to" "japanese." { + test_fail "failed to parse japanese local_part (2)"; + } + + test_set "envelope.to" "japanese...localpart@example.com"; + if not envelope :localpart :is "to" "japanese...localpart" { + test_fail "failed to parse japanese local_part (3)"; + } + + test_set "envelope.to" "..japanese...localpart..@example.com"; + if not envelope :localpart :is "to" "..japanese...localpart.." { + test_fail "failed to parse japanese local_part (4)"; + } +} + +test "Envelope - Non-standard hostnames" { + test_set "envelope.to" "japanese@_example.com"; + if not envelope :domain :is "to" "_example.com" { + test_fail "failed to parse non-standard domain (1)"; + } + + test_set "envelope.to" "japanese@ex_ample.com"; + if not envelope :domain :is "to" "ex_ample.com" { + test_fail "failed to parse non-standard domain (2)"; + } + + test_set "envelope.to" "japanese@example_.com"; + if not envelope :domain :is "to" "example_.com" { + test_fail "failed to parse non-standard domain (3)"; + } + + test_set "envelope.to" "japanese@-example.com"; + if not envelope :domain :is "to" "-example.com" { + test_fail "failed to parse non-standard domain (4)"; + } + + test_set "envelope.to" "japanese@example-.com"; + if not envelope :domain :is "to" "example-.com" { + test_fail "failed to parse non-standard domain (5)"; + } +} diff --git a/pigeonhole/tests/extensions/environment/basic.svtest b/pigeonhole/tests/extensions/environment/basic.svtest new file mode 100644 index 0000000..bb0beb4 --- /dev/null +++ b/pigeonhole/tests/extensions/environment/basic.svtest @@ -0,0 +1,33 @@ +require "vnd.dovecot.testsuite"; +require "environment"; +require "variables"; + +test "Name" { + if not environment :contains "name" "pigeonhole" { + if environment :matches "name" "*" { set "env_name" "${1}"; } + + test_fail "name environment returned invalid value(1): ${env_name}"; + } + + if not environment :contains "name" "sieve" { + if environment :matches "name" "*" { set "env_name" "${1}"; } + + test_fail "name environment returned invalid value(2): ${env_name}"; + } + + if environment :contains "name" "cyrus" { + test_fail "something is definitely wrong here"; + } + + if not environment :is :comparator "i;octet" "name" "Pigeonhole Sieve" { + test_fail "name environment does not match exactly with what is expected"; + } +} + +test "Location" { + if not environment "location" "MS" { + test_fail "wrong testsuite environment location"; + } +} + + diff --git a/pigeonhole/tests/extensions/environment/rfc.svtest b/pigeonhole/tests/extensions/environment/rfc.svtest new file mode 100644 index 0000000..c3177ae --- /dev/null +++ b/pigeonhole/tests/extensions/environment/rfc.svtest @@ -0,0 +1,28 @@ +require "vnd.dovecot.testsuite"; +require "environment"; +require "relational"; + +test "Non-existent" { + if environment :contains "nonsense" "" { + test_fail "matched unknown environment item"; + } +} + +test "Exists" { + if not environment :contains "version" "" { + test_fail "failed to match known environment item"; + } +} + +test "Count" { + if anyof ( + environment :count "eq" "nonsense" "0", + environment :count "eq" "nonsense" "1" + ) { + test_fail "count should not match unknown environment item"; + } + + if not environment :count "eq" "location" "1" { + test_fail "count of non-empty environment should be 1"; + } +} diff --git a/pigeonhole/tests/extensions/ihave/errors.svtest b/pigeonhole/tests/extensions/ihave/errors.svtest new file mode 100644 index 0000000..c6b9750 --- /dev/null +++ b/pigeonhole/tests/extensions/ihave/errors.svtest @@ -0,0 +1,19 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test "Error command" { + if not test_script_compile "errors/error.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } +} + diff --git a/pigeonhole/tests/extensions/ihave/errors/error.sieve b/pigeonhole/tests/extensions/ihave/errors/error.sieve new file mode 100644 index 0000000..8da0fe7 --- /dev/null +++ b/pigeonhole/tests/extensions/ihave/errors/error.sieve @@ -0,0 +1,3 @@ +require "ihave"; + +error "Something failed."; diff --git a/pigeonhole/tests/extensions/ihave/execute.svtest b/pigeonhole/tests/extensions/ihave/execute.svtest new file mode 100644 index 0000000..701d817 --- /dev/null +++ b/pigeonhole/tests/extensions/ihave/execute.svtest @@ -0,0 +1,23 @@ +require "vnd.dovecot.testsuite"; + +/* + * Execution testing (currently just meant to trigger any segfaults) + */ + +test "Basic" { + if not test_script_compile "execute/ihave.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } + + test_binary_save "ihave-basic"; + test_binary_load "ihave-basic"; +} + diff --git a/pigeonhole/tests/extensions/ihave/execute/ihave.sieve b/pigeonhole/tests/extensions/ihave/execute/ihave.sieve new file mode 100644 index 0000000..0fe84c8 --- /dev/null +++ b/pigeonhole/tests/extensions/ihave/execute/ihave.sieve @@ -0,0 +1,7 @@ +require "ihave"; + +if ihave "nonsense-extension" { + nonsense_command "Frop!"; +} + +redirect "frop@example.com"; diff --git a/pigeonhole/tests/extensions/ihave/restrictions.svtest b/pigeonhole/tests/extensions/ihave/restrictions.svtest new file mode 100644 index 0000000..5dba126 --- /dev/null +++ b/pigeonhole/tests/extensions/ihave/restrictions.svtest @@ -0,0 +1,14 @@ +require "vnd.dovecot.testsuite"; +require "ihave"; + +test "Restricted: encoded-character" { + if ihave "encoded-character" { + test_fail "encoded-character extension is incompatible with ihave"; + } +} + +test "Restricted: variables" { + if ihave "variables" { + test_fail "variables extension is incompatible with ihave"; + } +} diff --git a/pigeonhole/tests/extensions/imap4flags/basic.svtest b/pigeonhole/tests/extensions/imap4flags/basic.svtest new file mode 100644 index 0000000..d6af444 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/basic.svtest @@ -0,0 +1,332 @@ +require "vnd.dovecot.testsuite"; + +require "imap4flags"; +require "relational"; +require "variables"; +require "comparator-i;ascii-numeric"; + +/* + * Basic functionality tests + */ + +test "Hasflag empty" { + if hasflag "\\Seen" { + test_fail "hasflag sees initial \\seen flag were there should be none"; + } + if hasflag "\\draft" { + test_fail "hasflag sees initial \\draft flag were there should be none"; + } + if hasflag "\\recent" { + test_fail "hasflag sees initial \\recent flag were there should be none"; + } + if hasflag "\\flagged" { + test_fail "hasflag sees initial \\flagged flag were there should be none"; + } + if hasflag "\\answered" { + test_fail "hasflag sees initial \\answered flag were there should be none"; + } + if hasflag "\\deleted" { + test_fail "hasflag sees initial \\deleted flag were there should be none"; + } + + if hasflag :comparator "i;ascii-numeric" :count "ge" "1" { + test_fail "hasflag sees initial flags were there should be none"; + } +} + +test "Setflag; Hasflag one" { + setflag "\\seen"; + + if not hasflag "\\Seen" { + test_fail "flag not set of hasflag fails to see it"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "1" { + test_fail "flag not set of hasflag fails to see it"; + } + + if hasflag "$Nonsense" { + test_fail "hasflag sees other flag that the one set"; + } +} + +test "Hasflag; duplicates" { + set "Flags" "A B C D E F A B C D E F"; + + if hasflag :comparator "i;ascii-numeric" :count "gt" "Flags" "6" { + test_fail "hasflag must ignore duplicates"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "Flags" "6" { + test_fail "hasflag :count gives strange results"; + } +} + +test "Flag operations" { + setflag "A"; + + if not hasflag "A" { + test_fail "hasflag misses set flag"; + } + + if hasflag :comparator "i;ascii-numeric" :count "gt" "1" { + test_fail "hasflag sees more than one flag"; + } + + addflag "B"; + + if not hasflag "B" { + test_fail "flag \"B\" not added"; + } + + if not hasflag "A" { + test_fail "flag \"A\" not retained"; + } + + if hasflag :comparator "i;ascii-numeric" :count "gt" "2" { + test_fail "hasflag sees more than two flags"; + } + + addflag ["C", "D", "E F"]; + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "6" { + test_fail "hasflag sees more than two flags"; + } + + removeflag ["D"]; + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "5" { + test_fail "hasflag sees more than two flags"; + } + + if hasflag "D" { + test_fail "removed flag still present"; + } + + set "var" "G"; + addflag "${var}"; + + if not hasflag "G" { + test_fail "flag \"G\" not added"; + } + + if not hasflag "A" { + test_fail "flag \"A\" not retained"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "6" { + test_fail "hasflag sees something other than six flags"; + } +} + +test "Variable flag operations" { + setflag "frop" "A"; + + if not hasflag "frop" "A" { + test_fail "hasflag misses set flag"; + } + + if hasflag :comparator "i;ascii-numeric" :count "gt" "frop" "1" { + test_fail "hasflag sees more than one flag"; + } + + addflag "frop" "B"; + + if not hasflag "frop" "B" { + test_fail "flag \"B\" not added"; + } + + if not hasflag "frop" "A" { + test_fail "flag \"A\" not retained"; + } + + if hasflag :comparator "i;ascii-numeric" :count "gt" "frop" "2" { + test_fail "hasflag sees more than two flags"; + } + + addflag "frop" ["C", "D", "E F"]; + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "frop" "6" { + test_fail "hasflag sees something other than six flags"; + } + + removeflag "frop" ["D"]; + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "frop" "5" { + test_fail "hasflag sees something other than five flags"; + } + + if hasflag "frop" "D" { + test_fail "removed flag still present"; + } + + set "var" "G"; + addflag "frop" "${var}"; + + if not hasflag "frop" "G" { + test_fail "flag \"G\" not added"; + } + + if not hasflag "frop" "A" { + test_fail "flag \"A\" not retained"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "frop" "6" { + test_fail "hasflag sees something other than six flags"; + } +} + +test "Setflag; string list" { + setflag ["A B", "C D"]; + + if not hasflag "A" { + test_fail "hasflag misses A flag"; + } + + if not hasflag "B" { + test_fail "hasflag misses B flag"; + } + + if not hasflag "C" { + test_fail "hasflag misses C flag"; + } + + if not hasflag "D" { + test_fail "hasflag misses D flag"; + } + + if hasflag :comparator "i;ascii-numeric" :count "ne" "4" { + test_fail "hasflag sees incorrect number of flags"; + } +} + +test "Removal: one" { + setflag "\\seen"; + + if not hasflag "\\seen" { + test_fail "hasflag misses set flag"; + } + + removeflag "\\seen"; + + if hasflag "\\seen" { + test_fail "flag not removed"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "0" { + test_fail "flags are still set"; + } +} + +test "Removal: first" { + setflag "$frop \\seen"; + + if not allof ( hasflag "\\seen", hasflag "$frop" ) { + test_fail "hasflag misses set flags"; + } + + removeflag "$frop"; + + if not hasflag "\\seen" { + test_fail "wrong flag removed"; + } + + if hasflag "$frop" { + test_fail "flag not removed"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "1" { + test_fail "more than one flag remains set"; + } +} + +test "Removal: last" { + setflag "\\seen $friep"; + + if not allof ( hasflag "\\seen", hasflag "$friep" ) { + test_fail "hasflag misses set flags"; + } + + removeflag "$friep"; + + if not hasflag "\\seen" { + test_fail "wrong flag removed"; + } + + if hasflag "$friep" { + test_fail "flag not removed"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "1" { + test_fail "more than one flag remains set"; + } +} + +test "Removal: middle" { + setflag "\\seen $friep \\flagged"; + + if not allof ( hasflag "\\flagged", hasflag "\\seen", hasflag "$friep" ) { + test_fail "hasflag misses set flags"; + } + + removeflag "$friep"; + + if not allof ( hasflag "\\seen", hasflag "\\flagged" ) { + test_fail "wrong flag removed"; + } + + if hasflag "$friep" { + test_fail "flag not removed"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "2" { + test_fail "more than two flags remain set"; + } +} + +test "Removal: duplicates" { + setflag "\\seen $friep $friep \\flagged $friep"; + + if not allof ( hasflag "\\flagged", hasflag "\\seen", hasflag "$friep" ) { + test_fail "hasflag misses set flags"; + } + + removeflag "$friep"; + + if not allof ( hasflag "\\seen", hasflag "\\flagged" ) { + test_fail "wrong flag removed"; + } + + if hasflag "$friep" { + test_fail "flag not removed"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "2" { + test_fail "more than two flags remain set"; + } +} + +test "Removal: whitespace" { + setflag " \\seen $friep $friep \\flagged $friep "; + + if not allof ( hasflag "\\flagged", hasflag "\\seen", hasflag "$friep" ) { + test_fail "hasflag misses set flags"; + } + + removeflag "$friep"; + + if not allof ( hasflag "\\seen", hasflag "\\flagged" ) { + test_fail "wrong flag removed"; + } + + if hasflag "$friep" { + test_fail "flag not removed"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "2" { + test_fail "more than two flags remain set"; + } +} + + + diff --git a/pigeonhole/tests/extensions/imap4flags/execute.svtest b/pigeonhole/tests/extensions/imap4flags/execute.svtest new file mode 100644 index 0000000..1ee1906 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/execute.svtest @@ -0,0 +1,68 @@ +require "vnd.dovecot.testsuite"; +require "imap4flags"; +require "relational"; + + +/* + * Execution testing + */ + +test_mailbox_create "INBOX.Junk"; +test_mailbox_create "INBOX.Nonsense"; + +test "Flags Side Effect" { + if not test_script_compile "execute/flags-side-effect.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } + + test_result_reset; + + if not test_message :folder "INBOX.Junk" 0 { + test_fail "message not stored in INBOX.Junk"; + } + + if not hasflag :count "eq" "1" { + test_fail "invalid number of flags for message in INBOX.Junk"; + } + + if not hasflag :is "NONSENSE" { + test_fail "invalid flag set for message in INBOX.Junk"; + } + + test_result_reset; + + if not test_message :folder "INBOX" 0 { + test_fail "message not stored in INBOX"; + } + + if not hasflag :count "eq" "1" { + test_fail "invalid number of flags for message in INBOX"; + } + + if not hasflag :is "\\seen" { + test_fail "invalid flag set for message in INBOX"; + } + + test_result_reset; + + if not test_message :folder "INBOX.Nonsense" 0 { + test_fail "message not stored in INBOX.Nonsense"; + } + + if not hasflag :count "eq" "1" { + test_fail "invalid number of flags for message in Inbox.Nonsense"; + } + + if not hasflag :is "IMPLICIT" { + test_fail "invalid flag set for message in Inbox.Nonsene"; + } + +} diff --git a/pigeonhole/tests/extensions/imap4flags/execute/flags-side-effect.sieve b/pigeonhole/tests/extensions/imap4flags/execute/flags-side-effect.sieve new file mode 100644 index 0000000..17de0ad --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/execute/flags-side-effect.sieve @@ -0,0 +1,18 @@ +require "imap4flags"; +require "fileinto"; + +/* + * When keep/fileinto is used multiple times in a script and duplicate + * message elimination is performed, the last flag list value MUST win. + */ + +setflag "IMPLICIT"; + +fileinto :flags "\\Seen \\Draft" "INBOX.Junk"; +fileinto :flags "NONSENSE" "INBOX.Junk"; + +keep; +keep :flags "\\Seen"; + +fileinto :flags "\\Seen" "Inbox.Nonsense"; +fileinto "Inbox.Nonsense"; diff --git a/pigeonhole/tests/extensions/imap4flags/flagstore.svtest b/pigeonhole/tests/extensions/imap4flags/flagstore.svtest new file mode 100644 index 0000000..bf11402 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/flagstore.svtest @@ -0,0 +1,146 @@ +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "imap4flags"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "mailbox"; + +test_set "message" text: +From: Henry von Flockenstoffen <henry@example.com> +To: Dieter von Ausburg <dieter@example.com> +Subject: Test message. + +Test message. +. +; + +test "Basic" { + if hasflag :comparator "i;ascii-numeric" :count "ge" "1" { + test_fail "some flags or keywords are already set"; + } + + setflag "$label1 \\answered"; + + fileinto :create "Uninteresting"; + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_result_reset; + + setflag "\\draft \\seen Junk"; + + fileinto "Uninteresting"; + + if not test_result_execute { + test_fail "failed to execute second result"; + } + + test_result_reset; + + fileinto :flags "\\flagged" "Uninteresting"; + + if not test_result_execute { + test_fail "failed to execute third result"; + } + + test_result_reset; + + test_message :folder "Uninteresting" 0; + + if not hasflag "$label1 \\answered" { + test_fail "flags not stored for first message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "2" { + test_fail "invalid number of flags set for first message"; + } + + test_result_reset; + + test_message :folder "Uninteresting" 1; + + if not hasflag "\\draft \\seen Junk" { + test_fail "flags not stored for second message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "3" { + test_fail "invalid number of flags set for second message"; + } + + test_result_reset; + + test_message :folder "Uninteresting" 2; + + if not hasflag "\\flagged" { + test_fail "flags not stored for third message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "1" { + test_fail "invalid number of flags set for third message"; + } +} + +test_result_reset; +test_set "message" text: +From: Henry von Flockenstoffen <henry@example.com> +To: Dieter von Ausburg <dieter@example.com> +Subject: Test message. + +Test message. +. +; + +test "Flag changes between stores" { + if hasflag :comparator "i;ascii-numeric" :count "ge" "1" { + test_fail "some flags or keywords are already set"; + } + + setflag "$label1 \\answered"; + fileinto :create "FolderA"; + + setflag "$label2"; + fileinto :create "FolderB"; + + fileinto :create :flags "\\seen \\draft \\flagged" "FolderC"; + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_result_reset; + test_message :folder "FolderA" 0; + + if not hasflag "\\answered $label1" { + test_fail "flags not stored for first message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "2" { + test_fail "invalid number of flags set for first message"; + } + + test_result_reset; + test_message :folder "FolderB" 0; + + if not hasflag "$label2" { + test_fail "flag not stored for second message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "1" { + test_fail "invalid number of flags set for second message"; + } + + test_result_reset; + test_message :folder "FolderC" 0; + + if not hasflag "\\seen \\flagged \\draft" { + test_fail "flags not stored for third message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "3" { + test_fail "invalid number of flags set for third message"; + } +} + + diff --git a/pigeonhole/tests/extensions/imap4flags/flagstring.svtest b/pigeonhole/tests/extensions/imap4flags/flagstring.svtest new file mode 100644 index 0000000..23b6b34 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/flagstring.svtest @@ -0,0 +1,82 @@ +require "vnd.dovecot.testsuite"; +require "imap4flags"; +require "variables"; + +test "Duplicates: setflag" { + setflag "flags" "\\seen \\seen"; + + if not string "${flags}" "\\seen" { + test_fail "duplicate \\seen flag item not removed (1)"; + } + + setflag "flags" "\\seen $frop \\seen"; + + if not string "${flags}" "\\seen $frop" { + test_fail "duplicate \\seen flag item not removed (2)"; + } + + setflag "flags" "\\seen $frop $frop \\seen"; + + if not string "${flags}" "\\seen $frop" { + test_fail "duplicate \\seen flag item not removed (3)"; + } + + setflag "flags" "$frop \\seen $frop \\seen"; + + if not string "${flags}" "$frop \\seen" { + test_fail "duplicate \\seen flag item not removed (4)"; + } + + setflag "flags" "$frop \\seen \\seen \\seen \\seen $frop $frop $frop \\seen"; + + if not string "${flags}" "$frop \\seen" { + test_fail "duplicate \\seen flag item not removed (5)"; + } +} + +test "Duplicates: addflag" { + setflag "flags" ""; + addflag "flags" "\\seen \\seen"; + + if not string "${flags}" "\\seen" { + test_fail "duplicate \\seen flag item not removed (1)"; + } + + setflag "flags" ""; + addflag "flags" "\\seen $frop \\seen"; + + if not string "${flags}" "\\seen $frop" { + test_fail "duplicate \\seen flag item not removed (2)"; + } + + setflag "flags" ""; + addflag "flags" "\\seen $frop $frop \\seen"; + + if not string "${flags}" "\\seen $frop" { + test_fail "duplicate \\seen flag item not removed (3)"; + } + + setflag "flags" ""; + addflag "flags" "$frop \\seen $frop \\seen"; + + if not string "${flags}" "$frop \\seen" { + test_fail "duplicate \\seen flag item not removed (4)"; + } + + setflag "flags" ""; + addflag "flags" "$frop \\seen \\seen \\seen \\seen $frop $frop $frop \\seen"; + + if not string "${flags}" "$frop \\seen" { + test_fail "duplicate \\seen flag item not removed (5)"; + } + + setflag "flags" "$frop \\seen"; + addflag "flags" "\\seen \\seen \\seen $frop $frop $frop \\seen"; + + if not string "${flags}" "$frop \\seen" { + test_fail "duplicate \\seen flag item not removed (6)"; + } +} + + + diff --git a/pigeonhole/tests/extensions/imap4flags/hasflag.svtest b/pigeonhole/tests/extensions/imap4flags/hasflag.svtest new file mode 100644 index 0000000..1088190 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/hasflag.svtest @@ -0,0 +1,91 @@ +require "vnd.dovecot.testsuite"; + +require "imap4flags"; +require "relational"; +require "variables"; +require "comparator-i;ascii-numeric"; + +/* + * Generic tests + */ + +test "Ignoring \"\"" { + setflag ""; + + if hasflag "" { + test_fail "hasflag fails to ignore empty string"; + } +} + +/* + * Variables + */ + +test "Multiple variables" { + setflag "A" "Aflag"; + setflag "B" "Bflag"; + setflag "C" "Cflag"; + + if not hasflag ["a", "b", "c"] ["Bflag"] { + test_fail "hasflag failed to match multiple flags variables"; + } +} + +/* + * RFC examples + */ + +test "RFC hasflag example - :is" { + setflag "A B"; + + if not hasflag ["b","A"] { + test_fail "list representation did not match"; + } + + if not hasflag :is "b A" { + test_fail "string representation did not match"; + } +} + +test "RFC hasflag example - :contains variable" { + set "MyVar" "NonJunk Junk gnus-forward $Forwarded NotJunk JunkRecorded $Junk $NotJunk"; + + if not hasflag :contains "MyVar" "Junk" { + test_fail "failed true example 1"; + } + + if not hasflag :contains "MyVar" "forward" { + test_fail "failed true example 2"; + } + + if not hasflag :contains "MyVar" ["label", "forward"] { + test_fail "failed true example 3"; + } + + if not hasflag :contains "MyVar" ["junk", "forward"] { + test_fail "failed true example 4"; + } + + if not hasflag :contains "MyVar" "junk forward" { + test_fail "failed true example 4 (rewrite 1)"; + } + + if not hasflag :contains "MyVar" "forward junk" { + test_fail "failed true example 4 (rewrite 2)"; + } + + if hasflag :contains "MyVar" "label" { + test_fail "failed false example 1"; + } + + if hasflag :contains "MyVar" ["label1", "label2"] { + test_fail "failed false example 2"; + } +} + +test "RFC hasflag example - :count variable" { + set "MyFlags" "A B"; + if not hasflag :count "ge" :comparator "i;ascii-numeric" "MyFlags" "2" { + test_fail "failed count \"ge\" comparison"; + } +} diff --git a/pigeonhole/tests/extensions/imap4flags/multiscript.svtest b/pigeonhole/tests/extensions/imap4flags/multiscript.svtest new file mode 100644 index 0000000..5080eda --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/multiscript.svtest @@ -0,0 +1,55 @@ +require "vnd.dovecot.testsuite"; +require "imap4flags"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "mailbox"; +require "fileinto"; + +test "Segfault Trigger 1" { + + if not test_multiscript [ + "multiscript/group-spam.sieve", + "multiscript/spam.sieve", + "multiscript/sent-store.sieve"] + { + test_fail "failed multiscript execution"; + } +} + +test_set "message" text: +From: Henry von Flockenstoffen <henry@example.com> +To: Dieter von Ausburg <dieter@example.com> +Subject: Test message. + +Test message. +. +; + +test "Internal Flags" { + if hasflag :comparator "i;ascii-numeric" :count "ge" "1" { + test_fail "some flags or keywords are already set"; + } + + if not test_multiscript [ + "multiscript/setflag.sieve", + "multiscript/fileinto.sieve"] + { + test_fail "failed multiscript execution"; + } + + test_result_reset; + test_message :folder "folder" 0; + + if not hasflag "\\answered" { + test_fail "\\answered flag not stored for message"; + } + + if not hasflag "$label1" { + test_fail "$label1 keyword not stored for message"; + } + + if not hasflag :comparator "i;ascii-numeric" :count "eq" "2" { + test_fail "invalid number of flags set for message"; + } +} + diff --git a/pigeonhole/tests/extensions/imap4flags/multiscript/fileinto.sieve b/pigeonhole/tests/extensions/imap4flags/multiscript/fileinto.sieve new file mode 100644 index 0000000..94892a5 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/multiscript/fileinto.sieve @@ -0,0 +1,4 @@ +require "fileinto"; +require "mailbox"; + +fileinto :create "folder"; diff --git a/pigeonhole/tests/extensions/imap4flags/multiscript/group-spam.sieve b/pigeonhole/tests/extensions/imap4flags/multiscript/group-spam.sieve new file mode 100644 index 0000000..92ea3b9 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/multiscript/group-spam.sieve @@ -0,0 +1,14 @@ +require ["fileinto", "variables", "envelope"]; + +if header :contains "X-Group-Mail" ["Yes", "YES", "1"] { + if header :contains "X-Spam-Flag" ["Yes", "YES", "1"] { + if envelope :matches :localpart "to" "*" { + fileinto "group/${1}/SPAM"; stop; + } + } + if address :is ["To"] "sales@florist.ru" { + fileinto "group/info/Orders"; + } + stop; +} +keep; diff --git a/pigeonhole/tests/extensions/imap4flags/multiscript/sent-store.sieve b/pigeonhole/tests/extensions/imap4flags/multiscript/sent-store.sieve new file mode 100644 index 0000000..cb21daa --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/multiscript/sent-store.sieve @@ -0,0 +1,7 @@ +require ["imap4flags"]; + +if header :contains "X-Set-Seen" ["Yes", "YES", "1"] { + setflag "\\Seen"; +} + +keep; diff --git a/pigeonhole/tests/extensions/imap4flags/multiscript/setflag.sieve b/pigeonhole/tests/extensions/imap4flags/multiscript/setflag.sieve new file mode 100644 index 0000000..c992d19 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/multiscript/setflag.sieve @@ -0,0 +1,3 @@ +require "imap4flags"; + +setflag "$label1 \\answered"; diff --git a/pigeonhole/tests/extensions/imap4flags/multiscript/spam.sieve b/pigeonhole/tests/extensions/imap4flags/multiscript/spam.sieve new file mode 100644 index 0000000..9e1b6c3 --- /dev/null +++ b/pigeonhole/tests/extensions/imap4flags/multiscript/spam.sieve @@ -0,0 +1,8 @@ +require ["fileinto"]; + +if header :contains "X-Spam-Flag" ["Yes", "YES", "1"] { + fileinto "SPAM"; +} +keep; + + diff --git a/pigeonhole/tests/extensions/include/errors.svtest b/pigeonhole/tests/extensions/include/errors.svtest new file mode 100644 index 0000000..6f8b1cc --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors.svtest @@ -0,0 +1,149 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Generic include errors + */ + +test "Generic" { + if test_script_compile "errors/generic.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +test "Circular - direct" { + if test_script_compile "errors/circular-1.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +test "Circular - one intermittent" { + if test_script_compile "errors/circular-2.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "4" { + test_fail "wrong number of errors reported"; + } +} + +test "Circular - two intermittent" { + if test_script_compile "errors/circular-3.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "5" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Using global without variables required + */ + +test "Variables inactive" { + if test_script_compile "errors/variables-inactive.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Generic variables errors + */ + +test "Variables" { + if test_script_compile "errors/variables.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Global variable namespace + */ + +test "Global Namespace" { + if test_script_compile "errors/global-namespace.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "4" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Invalid script names + */ + +test "Invalid Script Names" { + if test_script_compile "errors/scriptname.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + +/* Include limit */ + +test "Include limit" { + test_config_set "sieve_include_max_includes" "3"; + test_config_reload :extension "include"; + + if test_script_compile "errors/include-limit.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } + + test_config_set "sieve_include_max_includes" "255"; + test_config_reload :extension "include"; + + if not test_script_compile "errors/include-limit.sieve" { + test_fail "compile should have succeeded"; + } +} + +/* Depth limit */ + +test "Depth limit" { + test_config_set "sieve_include_max_nesting_depth" "2"; + test_config_reload :extension "include"; + + if test_script_compile "errors/depth-limit.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "4" { + test_fail "wrong number of errors reported"; + } + + test_config_set "sieve_include_max_nesting_depth" "10"; + test_config_reload :extension "include"; + + if not test_script_compile "errors/depth-limit.sieve" { + test_fail "compile should have succeeded"; + } +} + diff --git a/pigeonhole/tests/extensions/include/errors/action-conflicts.sieve b/pigeonhole/tests/extensions/include/errors/action-conflicts.sieve new file mode 100644 index 0000000..ddeb42c --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/action-conflicts.sieve @@ -0,0 +1,4 @@ +require "include"; + +include "action-fileinto"; +include "action-reject"; diff --git a/pigeonhole/tests/extensions/include/errors/circular-1.sieve b/pigeonhole/tests/extensions/include/errors/circular-1.sieve new file mode 100644 index 0000000..22b6f87 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/circular-1.sieve @@ -0,0 +1,5 @@ +require "include"; + +discard; + +include "circular-one"; diff --git a/pigeonhole/tests/extensions/include/errors/circular-2.sieve b/pigeonhole/tests/extensions/include/errors/circular-2.sieve new file mode 100644 index 0000000..0cfa375 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/circular-2.sieve @@ -0,0 +1,5 @@ +require "include"; + +discard; + +include "circular-two"; diff --git a/pigeonhole/tests/extensions/include/errors/circular-3.sieve b/pigeonhole/tests/extensions/include/errors/circular-3.sieve new file mode 100644 index 0000000..1ad95b6 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/circular-3.sieve @@ -0,0 +1,5 @@ +require "include"; + +discard; + +include "circular-three"; diff --git a/pigeonhole/tests/extensions/include/errors/depth-limit.sieve b/pigeonhole/tests/extensions/include/errors/depth-limit.sieve new file mode 100644 index 0000000..93291b6 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/depth-limit.sieve @@ -0,0 +1,3 @@ +require "include"; + +include :personal "depth-limit-1"; diff --git a/pigeonhole/tests/extensions/include/errors/generic.sieve b/pigeonhole/tests/extensions/include/errors/generic.sieve new file mode 100644 index 0000000..66eba18 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/generic.sieve @@ -0,0 +1,7 @@ +require "include"; + +# Non-existent sieve script +include "frop.sieve"; + +# Use of / in script names +include "../frop.sieve"; diff --git a/pigeonhole/tests/extensions/include/errors/global-namespace.sieve b/pigeonhole/tests/extensions/include/errors/global-namespace.sieve new file mode 100644 index 0000000..3827b60 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/global-namespace.sieve @@ -0,0 +1,13 @@ +require "variables"; +require "include"; + +# Invalid namespace +set "globl.var" "frop"; + +# Sub-namespace +set "global.env.0" "12"; + +# Invalid variable name +set "global.12" "porf"; + + diff --git a/pigeonhole/tests/extensions/include/errors/include-limit.sieve b/pigeonhole/tests/extensions/include/errors/include-limit.sieve new file mode 100644 index 0000000..f6689dd --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/include-limit.sieve @@ -0,0 +1,6 @@ +require "include"; + +include "rfc-ex1-always_allow"; +include "rfc-ex2-spam_filter_script"; +include "rfc-ex1-mailing_lists"; +include "rfc-ex1-spam_tests"; diff --git a/pigeonhole/tests/extensions/include/errors/scriptname.sieve b/pigeonhole/tests/extensions/include/errors/scriptname.sieve new file mode 100644 index 0000000..9a10c3d --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/scriptname.sieve @@ -0,0 +1,25 @@ +require "variables"; +require "include"; +require "encoded-character"; + +# Slash +include "../frop"; + +# More slashes +include "../../james/sieve/vacation"; + +# 0000-001F; [CONTROL CHARACTERS] +include "idiotic${unicode: 001a}"; + +# 007F; DELETE +include "idiotic${unicode: 007f}"; + +# 0080-009F; [CONTROL CHARACTERS] +include "idiotic${unicode: 0085}"; + +# 2028; LINE SEPARATOR +include "idiotic${unicode: 2028}"; + +# 2029; PARAGRAPH SEPARATOR +include "idiotic${unicode: 2029}"; + diff --git a/pigeonhole/tests/extensions/include/errors/variables-inactive.sieve b/pigeonhole/tests/extensions/include/errors/variables-inactive.sieve new file mode 100644 index 0000000..06e0df1 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/variables-inactive.sieve @@ -0,0 +1,7 @@ +require "include"; +require "fileinto"; + +global "friep"; +global "frop"; + +fileinto "Frop"; diff --git a/pigeonhole/tests/extensions/include/errors/variables.sieve b/pigeonhole/tests/extensions/include/errors/variables.sieve new file mode 100644 index 0000000..eac99f8 --- /dev/null +++ b/pigeonhole/tests/extensions/include/errors/variables.sieve @@ -0,0 +1,23 @@ +require "include"; +require "variables"; + +# Duplicate global declaration (not an error) +global "frml"; +global "frml"; + +keep; + +# Global after command not being require or global (not an error) +global "friep"; + +# DEPRECATED: import/export after command not being require or import/export +export "friep"; +import "friep"; + +# Marking local variable as global +set "frutsels" "frop"; +global "frutsels"; +set "frutsels" "frop"; + + + diff --git a/pigeonhole/tests/extensions/include/execute.svtest b/pigeonhole/tests/extensions/include/execute.svtest new file mode 100644 index 0000000..734ac66 --- /dev/null +++ b/pigeonhole/tests/extensions/include/execute.svtest @@ -0,0 +1,68 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; + +test_set "message" text: +From: idiot@example.com +To: idiot@example.org +Subject: Frop! + +Frop. +. +; + +test "Actions Fileinto" { + test_mailbox_create "aaaa"; + test_mailbox_create "bbbb"; + + if not test_script_compile "execute/actions-fileinto.sieve" { + test_fail "failed to compile sieve script"; + } + + test_binary_save "actions-fileinto"; + test_binary_load "actions-fileinto"; + + if not test_script_run { + test_fail "failed to execute sieve script"; + } + + if not test_result_execute { + test_fail "failed to execute result"; + } + + test_message :folder "aaaa" 0; + + if not header "subject" "Frop!" { + test_fail "fileinto \"aaaa\" not executed."; + } + + test_message :folder "bbbb" 0; + + if not header "subject" "Frop!" { + test_fail "fileinto \"bbbb\" not executed."; + } +} + +test "Namespace - file" { + if not test_script_compile "execute/namespace.sieve" { + test_fail "failed to compile sub-test"; + } + + if not test_script_run { + test_fail "failed to execute sub-test"; + } +} + +test "Namespace - dict" { + test_config_set "sieve" "dict:file:${tst.path}/included/namespace.dict"; + test_config_set "sieve_global" "dict:file:${tst.path}/included-global/namespace.dict"; + test_config_reload :extension "include"; + + if not test_script_compile "execute/namespace.sieve" { + test_fail "failed to compile sub-test"; + } + + if not test_script_run { + test_fail "failed to execute sub-test"; + } +} diff --git a/pigeonhole/tests/extensions/include/execute/actions-fileinto.sieve b/pigeonhole/tests/extensions/include/execute/actions-fileinto.sieve new file mode 100644 index 0000000..b0b8157 --- /dev/null +++ b/pigeonhole/tests/extensions/include/execute/actions-fileinto.sieve @@ -0,0 +1,5 @@ +require "include"; + +include "actions-fileinto1"; +include "actions-fileinto2"; +include "actions-fileinto3"; diff --git a/pigeonhole/tests/extensions/include/execute/namespace.sieve b/pigeonhole/tests/extensions/include/execute/namespace.sieve new file mode 100644 index 0000000..cbe41a2 --- /dev/null +++ b/pigeonhole/tests/extensions/include/execute/namespace.sieve @@ -0,0 +1,26 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; + +set "global.a" "none"; +include :personal "namespace"; + +if string "${global.a}" "none" { + test_fail "personal script not executed"; +} + +if not string "${global.a}" "personal" { + test_fail "executed global instead of personal script: ${global.a}"; +} + +set "global.a" "none"; +include :global "namespace"; + +if string "{global.a}" "none" { + test_fail "global script not executed"; +} + +if not string "${global.a}" "global" { + test_fail "executed personal instead of global script: ${global.a}"; +} + diff --git a/pigeonhole/tests/extensions/include/execute/optional.sieve b/pigeonhole/tests/extensions/include/execute/optional.sieve new file mode 100644 index 0000000..a6ad479 --- /dev/null +++ b/pigeonhole/tests/extensions/include/execute/optional.sieve @@ -0,0 +1,5 @@ +require "include"; + +include :optional "optional-1"; +include :optional "optional-2"; +include :optional "optional-3"; diff --git a/pigeonhole/tests/extensions/include/included-global/namespace.dict b/pigeonhole/tests/extensions/include/included-global/namespace.dict new file mode 100644 index 0000000..8f52fd3 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included-global/namespace.dict @@ -0,0 +1,4 @@ +priv/sieve/name/namespace +1 +priv/sieve/data/1 +require ["variables", "include"]; set "global.a" "global"; diff --git a/pigeonhole/tests/extensions/include/included-global/namespace.sieve b/pigeonhole/tests/extensions/include/included-global/namespace.sieve new file mode 100644 index 0000000..d11c2f1 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included-global/namespace.sieve @@ -0,0 +1,4 @@ +require "include"; +require "variables"; + +set "global.a" "global"; diff --git a/pigeonhole/tests/extensions/include/included-global/rfc-ex1-spam_tests.sieve b/pigeonhole/tests/extensions/include/included-global/rfc-ex1-spam_tests.sieve new file mode 100644 index 0000000..340ceaf --- /dev/null +++ b/pigeonhole/tests/extensions/include/included-global/rfc-ex1-spam_tests.sieve @@ -0,0 +1,7 @@ +require ["reject"]; + +if anyof (header :contains "Subject" "$$", + header :contains "Subject" "Make money") +{ + reject "Not wanted"; +} diff --git a/pigeonhole/tests/extensions/include/included/action-fileinto.sieve b/pigeonhole/tests/extensions/include/included/action-fileinto.sieve new file mode 100644 index 0000000..9aafb95 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/action-fileinto.sieve @@ -0,0 +1,3 @@ +require "fileinto"; + +fileinto "frop"; diff --git a/pigeonhole/tests/extensions/include/included/action-reject.sieve b/pigeonhole/tests/extensions/include/included/action-reject.sieve new file mode 100644 index 0000000..6e7b0b0 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/action-reject.sieve @@ -0,0 +1,3 @@ +require "reject"; + +reject "Ik heb geen zin in die rommel."; diff --git a/pigeonhole/tests/extensions/include/included/actions-fileinto1.sieve b/pigeonhole/tests/extensions/include/included/actions-fileinto1.sieve new file mode 100644 index 0000000..d4c5031 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/actions-fileinto1.sieve @@ -0,0 +1,3 @@ +require "fileinto"; + +fileinto "aaaa"; diff --git a/pigeonhole/tests/extensions/include/included/actions-fileinto2.sieve b/pigeonhole/tests/extensions/include/included/actions-fileinto2.sieve new file mode 100644 index 0000000..f73da0d --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/actions-fileinto2.sieve @@ -0,0 +1,4 @@ +require "fileinto"; + +fileinto "bbbb"; + diff --git a/pigeonhole/tests/extensions/include/included/actions-fileinto3.sieve b/pigeonhole/tests/extensions/include/included/actions-fileinto3.sieve new file mode 100644 index 0000000..d4c5031 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/actions-fileinto3.sieve @@ -0,0 +1,3 @@ +require "fileinto"; + +fileinto "aaaa"; diff --git a/pigeonhole/tests/extensions/include/included/circular-one.sieve b/pigeonhole/tests/extensions/include/included/circular-one.sieve new file mode 100644 index 0000000..2d60606 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/circular-one.sieve @@ -0,0 +1,5 @@ +require "include"; + +keep; + +include "circular-one"; diff --git a/pigeonhole/tests/extensions/include/included/circular-three-2.sieve b/pigeonhole/tests/extensions/include/included/circular-three-2.sieve new file mode 100644 index 0000000..5199f21 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/circular-three-2.sieve @@ -0,0 +1,3 @@ +require "include"; + +include "circular-three-3"; diff --git a/pigeonhole/tests/extensions/include/included/circular-three-3.sieve b/pigeonhole/tests/extensions/include/included/circular-three-3.sieve new file mode 100644 index 0000000..4c062cd --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/circular-three-3.sieve @@ -0,0 +1,3 @@ +require "include"; + +include "circular-three.sieve"; diff --git a/pigeonhole/tests/extensions/include/included/circular-three.sieve b/pigeonhole/tests/extensions/include/included/circular-three.sieve new file mode 100644 index 0000000..13be546 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/circular-three.sieve @@ -0,0 +1,7 @@ +require "include"; + +keep; + +include "circular-three-2"; + + diff --git a/pigeonhole/tests/extensions/include/included/circular-two-2.sieve b/pigeonhole/tests/extensions/include/included/circular-two-2.sieve new file mode 100644 index 0000000..d529214 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/circular-two-2.sieve @@ -0,0 +1,3 @@ +require "include"; + +include "circular-two.sieve"; diff --git a/pigeonhole/tests/extensions/include/included/circular-two.sieve b/pigeonhole/tests/extensions/include/included/circular-two.sieve new file mode 100644 index 0000000..8a879cb --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/circular-two.sieve @@ -0,0 +1,7 @@ +require "include"; + +keep; + +include "circular-two-2"; + + diff --git a/pigeonhole/tests/extensions/include/included/depth-limit-1.sieve b/pigeonhole/tests/extensions/include/included/depth-limit-1.sieve new file mode 100644 index 0000000..ce5571f --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/depth-limit-1.sieve @@ -0,0 +1,3 @@ +require "include"; + +include :personal "depth-limit-2"; diff --git a/pigeonhole/tests/extensions/include/included/depth-limit-2.sieve b/pigeonhole/tests/extensions/include/included/depth-limit-2.sieve new file mode 100644 index 0000000..79c55e0 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/depth-limit-2.sieve @@ -0,0 +1,3 @@ +require "include"; + +include :personal "depth-limit-3"; diff --git a/pigeonhole/tests/extensions/include/included/depth-limit-3.sieve b/pigeonhole/tests/extensions/include/included/depth-limit-3.sieve new file mode 100644 index 0000000..6203a21 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/depth-limit-3.sieve @@ -0,0 +1 @@ +keep; diff --git a/pigeonhole/tests/extensions/include/included/namespace.dict b/pigeonhole/tests/extensions/include/included/namespace.dict new file mode 100644 index 0000000..35d7aaa --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/namespace.dict @@ -0,0 +1,4 @@ +priv/sieve/name/namespace +1 +priv/sieve/data/1 +require ["variables", "include"]; set "global.a" "personal"; diff --git a/pigeonhole/tests/extensions/include/included/namespace.sieve b/pigeonhole/tests/extensions/include/included/namespace.sieve new file mode 100644 index 0000000..3f5738f --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/namespace.sieve @@ -0,0 +1,4 @@ +require "include"; +require "variables"; + +set "global.a" "personal"; diff --git a/pigeonhole/tests/extensions/include/included/once-1.sieve b/pigeonhole/tests/extensions/include/included/once-1.sieve new file mode 100644 index 0000000..288d141 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/once-1.sieve @@ -0,0 +1,9 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} ONE"; + +return; + diff --git a/pigeonhole/tests/extensions/include/included/once-2.sieve b/pigeonhole/tests/extensions/include/included/once-2.sieve new file mode 100644 index 0000000..abf29e5 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/once-2.sieve @@ -0,0 +1,12 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} TWO"; + +keep; + +include :once "once-1"; + +return; diff --git a/pigeonhole/tests/extensions/include/included/once-3.sieve b/pigeonhole/tests/extensions/include/included/once-3.sieve new file mode 100644 index 0000000..739651e --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/once-3.sieve @@ -0,0 +1,3 @@ +require "include"; + +include "once-4"; diff --git a/pigeonhole/tests/extensions/include/included/once-4.sieve b/pigeonhole/tests/extensions/include/included/once-4.sieve new file mode 100644 index 0000000..9cc1a47 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/once-4.sieve @@ -0,0 +1,3 @@ +require "include"; + +include :once "once-3"; diff --git a/pigeonhole/tests/extensions/include/included/optional-1.sieve b/pigeonhole/tests/extensions/include/included/optional-1.sieve new file mode 100644 index 0000000..288d141 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/optional-1.sieve @@ -0,0 +1,9 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} ONE"; + +return; + diff --git a/pigeonhole/tests/extensions/include/included/optional-2.sieve b/pigeonhole/tests/extensions/include/included/optional-2.sieve new file mode 100644 index 0000000..11920f5 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/optional-2.sieve @@ -0,0 +1,9 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} TWO"; + +keep; + diff --git a/pigeonhole/tests/extensions/include/included/rfc-ex1-always_allow.sieve b/pigeonhole/tests/extensions/include/included/rfc-ex1-always_allow.sieve new file mode 100644 index 0000000..6dc8ddc --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/rfc-ex1-always_allow.sieve @@ -0,0 +1,8 @@ +if header :is "From" "boss@example.com" +{ + keep; +} +elsif header :is "From" "ceo@example.com" +{ + keep; +} diff --git a/pigeonhole/tests/extensions/include/included/rfc-ex1-mailing_lists.sieve b/pigeonhole/tests/extensions/include/included/rfc-ex1-mailing_lists.sieve new file mode 100644 index 0000000..d020972 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/rfc-ex1-mailing_lists.sieve @@ -0,0 +1,10 @@ +require ["fileinto"]; + +if header :is "Sender" "owner-ietf-mta-filters@imc.example.com" +{ + fileinto "lists.sieve"; +} +elsif header :is "Sender" "owner-ietf-imapext@imc.example.com" +{ + fileinto "lists.imapext"; +} diff --git a/pigeonhole/tests/extensions/include/included/rfc-ex1-spam_tests.sieve b/pigeonhole/tests/extensions/include/included/rfc-ex1-spam_tests.sieve new file mode 100644 index 0000000..7916064 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/rfc-ex1-spam_tests.sieve @@ -0,0 +1,10 @@ +require ["reject"]; + +if header :contains "Subject" "XXXX" +{ + reject "Not wanted"; +} +elsif header :is "From" "money@example.com" +{ + reject "Not wanted"; +} diff --git a/pigeonhole/tests/extensions/include/included/rfc-ex2-spam_filter_script.sieve b/pigeonhole/tests/extensions/include/included/rfc-ex2-spam_filter_script.sieve new file mode 100644 index 0000000..01ab984 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/rfc-ex2-spam_filter_script.sieve @@ -0,0 +1,8 @@ +require ["variables", "include"]; +global ["test", "test_mailbox"]; + +if header :contains "Subject" "${test}" +{ + set "test_mailbox" "spam-${test}"; +} + diff --git a/pigeonhole/tests/extensions/include/included/twice-1.sieve b/pigeonhole/tests/extensions/include/included/twice-1.sieve new file mode 100644 index 0000000..a770a3b --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/twice-1.sieve @@ -0,0 +1,7 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} TWO"; + diff --git a/pigeonhole/tests/extensions/include/included/twice-2.sieve b/pigeonhole/tests/extensions/include/included/twice-2.sieve new file mode 100644 index 0000000..eff9429 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/twice-2.sieve @@ -0,0 +1,8 @@ +require "include"; +require "variables"; + +global "result"; + +set "result" "${result} THREE"; + +include "twice-1"; diff --git a/pigeonhole/tests/extensions/include/included/variables-included1.sieve b/pigeonhole/tests/extensions/include/included/variables-included1.sieve new file mode 100644 index 0000000..5f6cb2f --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/variables-included1.sieve @@ -0,0 +1,7 @@ +require "include"; +require "variables"; + +global ["value1"]; +global ["result1"]; + +set "result1" "${value1} ${global.value2}"; diff --git a/pigeonhole/tests/extensions/include/included/variables-included2.sieve b/pigeonhole/tests/extensions/include/included/variables-included2.sieve new file mode 100644 index 0000000..135e03b --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/variables-included2.sieve @@ -0,0 +1,6 @@ +require "include"; +require "variables"; + +global ["value3", "value4"]; + +set "global.result2" "${value3} ${value4}"; diff --git a/pigeonhole/tests/extensions/include/included/variables-included3.sieve b/pigeonhole/tests/extensions/include/included/variables-included3.sieve new file mode 100644 index 0000000..51bb786 --- /dev/null +++ b/pigeonhole/tests/extensions/include/included/variables-included3.sieve @@ -0,0 +1,8 @@ +require "include"; +require "variables"; + +global "result1"; +global "result2"; +global "result"; + +set "result" "${result1} ${result2}"; diff --git a/pigeonhole/tests/extensions/include/once.svtest b/pigeonhole/tests/extensions/include/once.svtest new file mode 100644 index 0000000..3395c6b --- /dev/null +++ b/pigeonhole/tests/extensions/include/once.svtest @@ -0,0 +1,24 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; + +global "result"; + +set "result" ""; + +test "Included Once" { + include "once-1"; + include "once-2"; + + if string "${result}" " ONE TWO ONE" { + test_fail "duplicate included :once script"; + } + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value: ${result}"; + } +} + +test "Included Once recursive" { + include "once-3"; +} diff --git a/pigeonhole/tests/extensions/include/optional.svtest b/pigeonhole/tests/extensions/include/optional.svtest new file mode 100644 index 0000000..345f830 --- /dev/null +++ b/pigeonhole/tests/extensions/include/optional.svtest @@ -0,0 +1,40 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; + +global "result"; +set "result" ""; + +test "Included Optional" { + include :optional "optional-1"; + include :optional "optional-2"; + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value: ${result}"; + } + + # missing + include :optional "optional-3"; + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value after missing script: ${result}"; + } +} + + +test "Included Optional - Binary" { + if not test_script_compile "execute/optional.sieve" { + test_fail "failed to compile sieve script"; + } + + test_binary_save "optional"; + test_binary_load "optional"; + + if not test_script_run { + test_fail "failed to execute sieve script"; + } + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value: ${result}"; + } +} diff --git a/pigeonhole/tests/extensions/include/rfc-ex1-default.sieve b/pigeonhole/tests/extensions/include/rfc-ex1-default.sieve new file mode 100644 index 0000000..5a8cb52 --- /dev/null +++ b/pigeonhole/tests/extensions/include/rfc-ex1-default.sieve @@ -0,0 +1,6 @@ +require ["include"]; + +include :personal "rfc-ex1-always_allow"; +include :global "rfc-ex1-spam_tests"; +include :personal "rfc-ex1-spam_tests"; +include :personal "rfc-ex1-mailing_lists"; diff --git a/pigeonhole/tests/extensions/include/rfc-ex2-default.sieve b/pigeonhole/tests/extensions/include/rfc-ex2-default.sieve new file mode 100644 index 0000000..8b1bf4d --- /dev/null +++ b/pigeonhole/tests/extensions/include/rfc-ex2-default.sieve @@ -0,0 +1,21 @@ +require ["variables", "include", "relational", "fileinto"]; +global "test"; +global "test_mailbox"; + +# The included script may contain repetitive code that is +# effectively a subroutine that can be factored out. +set "test" "$$"; +include "rfc-ex2-spam_filter_script"; + +set "test" "Make money"; +include "rfc-ex2-spam_filter_script"; + +# Message will be filed according to the test that matched last. +if string :count "eq" "${test_mailbox}" "1" +{ + fileinto "INBOX${test_mailbox}"; + stop; +} + +# If nothing matched, the message is implicitly kept. + diff --git a/pigeonhole/tests/extensions/include/rfc.svtest b/pigeonhole/tests/extensions/include/rfc.svtest new file mode 100644 index 0000000..00908ac --- /dev/null +++ b/pigeonhole/tests/extensions/include/rfc.svtest @@ -0,0 +1,13 @@ +require "vnd.dovecot.testsuite"; + +test "RFC example 1" { + if not test_script_compile "rfc-ex1-default.sieve" { + test_fail "failed to compile sieve script"; + } +} + +test "RFC example 2" { + if not test_script_compile "rfc-ex2-default.sieve" { + test_fail "failed to compile sieve script"; + } +} diff --git a/pigeonhole/tests/extensions/include/twice.svtest b/pigeonhole/tests/extensions/include/twice.svtest new file mode 100644 index 0000000..5cd5da2 --- /dev/null +++ b/pigeonhole/tests/extensions/include/twice.svtest @@ -0,0 +1,20 @@ +require "vnd.dovecot.testsuite"; +require "include"; +require "variables"; + +global "result"; + +set "result" "ONE"; + +test "Twice included" { + include "twice-1"; + include "twice-2"; + + if string "${result}" "ONE TWO THREE" { + test_fail "duplicate include failed"; + } + + if not string "${result}" "ONE TWO THREE TWO" { + test_fail "unexpected result: ${result}"; + } +} diff --git a/pigeonhole/tests/extensions/include/variables.svtest b/pigeonhole/tests/extensions/include/variables.svtest new file mode 100644 index 0000000..5c4f8d8 --- /dev/null +++ b/pigeonhole/tests/extensions/include/variables.svtest @@ -0,0 +1,29 @@ +require "vnd.dovecot.testsuite"; + +require "include"; +require "variables"; + +global ["value1", "value2"]; +set "value1" "Works"; +set "value2" "fine."; + +global ["value3", "value4"]; +set "value3" "Yeah"; +set "value4" "it does."; + +include "variables-included1"; +include "variables-included2"; +include "variables-included3"; + +global "result"; + +test "Basic" { + if not string :is "${result}" "Works fine. Yeah it does." { + test_fail "invalid result: ${result}"; + } + + if string :is "${result}" "nonsense" { + test_fail "string test succeeds inappropriately"; + } +} + diff --git a/pigeonhole/tests/extensions/index/basic.svtest b/pigeonhole/tests/extensions/index/basic.svtest new file mode 100644 index 0000000..0706022 --- /dev/null +++ b/pigeonhole/tests/extensions/index/basic.svtest @@ -0,0 +1,93 @@ +require "vnd.dovecot.testsuite"; +require "index"; +require "date"; +require "variables"; +require "subaddress"; + +test_set "message" text: +To: first@friep.example.com +X-A: First +Received: from mx.example.com (127.0.0.13) by mx.example.org + (127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4; + Wed, 12 Nov 2014 18:18:31 +0100 +To: second@friep.example.com +From: stephan@example.org +Received: from mx.example.com (127.0.0.13) by mx.example.org + (127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4; + Wed, 12 Nov 2014 18:18:30 +0100 +X-A: Second +To: third@friep.example.com +X-A: Third +Received: from mx.example.com (127.0.0.13) by mx.example.org + (127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4; + Wed, 12 Nov 2014 18:18:29 +0100 +Subject: Frop! +X-A: Fourth +To: fourth@friep.example.com +Received: from mx.example.com (127.0.0.13) by mx.example.org + (127.0.0.12) with Macrosoft SMTP Server (TLS) id 1.2.3.4; + Wed, 12 Nov 2014 18:18:28 +0100 + +Frop +. +; + +test "Header :index" { + if not header :index 3 "x-a" "Third" { + test_fail "wrong header retrieved"; + } + + if header :index 3 "x-a" ["First", "Second", "Fourth"] { + test_fail "other header retrieved"; + } +} + +test "Header :index :last" { + if not header :index 3 :last "x-a" "Second" { + test_fail "wrong header retrieved"; + } + + if header :index 3 :last "x-a" ["First", "Third", "Fourth"] { + test_fail "other header retrieved"; + } +} + +test "Address :index" { + if not address :localpart :index 2 "to" "second" { + test_fail "wrong header retrieved"; + } + + if address :localpart :index 2 "to" ["first", "third", "fourth"] { + test_fail "other header retrieved"; + } +} + +test "Address :index :last" { + if not address :localpart :index 2 :last "to" "third" { + test_fail "wrong header retrieved"; + } + + if address :localpart :index 2 :last "to" ["first", "second", "fourth"] { + test_fail "other header retrieved"; + } +} + +test "Date :index" { + if not date :index 1 "received" "second" "31" { + test_fail "wrong header retrieved"; + } + + if date :index 1 "received" "second" ["30", "29", "28"] { + test_fail "other header retrieved"; + } +} + +test "Date :index :last" { + if not date :index 1 :last "received" "second" "28"{ + test_fail "wrong header retrieved"; + } + + if date :index 1 :last "received" "second" ["31", "30", "29"] { + test_fail "other header retrieved"; + } +} diff --git a/pigeonhole/tests/extensions/index/errors.svtest b/pigeonhole/tests/extensions/index/errors.svtest new file mode 100644 index 0000000..4bfe2dc --- /dev/null +++ b/pigeonhole/tests/extensions/index/errors.svtest @@ -0,0 +1,20 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid syntax + */ + +test "Invalid Syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "7" { + test_fail "wrong number of errors reported"; + } +} + + diff --git a/pigeonhole/tests/extensions/index/errors/syntax.sieve b/pigeonhole/tests/extensions/index/errors/syntax.sieve new file mode 100644 index 0000000..ee19d88 --- /dev/null +++ b/pigeonhole/tests/extensions/index/errors/syntax.sieve @@ -0,0 +1,26 @@ +require "date"; +require "index"; + +# Not an error +if header :last :index 2 "to" "ok" { } + +# Not an error +if header :index 444 :last "to" "ok" { } + +# 1: missing argument +if header :index "to" "ok" {} + +# 2: missing argument +if header :index :last "to" "ok" {} + +# 3: erroneous string argument +if header :index "frop" "to" "ok" {} + +# 4: last without index +if header :last "to" "ok" {} + +# 5: index 0 +if header :index 0 "to" "ok" {} + +# 6: index 0 last +if header :index 0 :last "to" "ok" {} diff --git a/pigeonhole/tests/extensions/mailbox/errors.svtest b/pigeonhole/tests/extensions/mailbox/errors.svtest new file mode 100644 index 0000000..0821f52 --- /dev/null +++ b/pigeonhole/tests/extensions/mailbox/errors.svtest @@ -0,0 +1,40 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "vnd.dovecot.debug"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid syntax + */ + +test "Invalid Syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + # FIXME: check warnings + if not test_error :count "eq" :comparator "i;ascii-numeric" "12" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Mailboxexists - bad UTF-8 in mailbox name + */ + +test "Mailboxexists - bad UTF-8 in mailbox name" { + if not test_script_compile "errors/mailboxexists-bad-utf8.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execution failed"; + } + + # FIXME: check warnings + if not test_error :count "eq" :comparator "i;ascii-numeric" "0" { + test_fail "wrong number of runtime errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/mailbox/errors/mailboxexists-bad-utf8.sieve b/pigeonhole/tests/extensions/mailbox/errors/mailboxexists-bad-utf8.sieve new file mode 100644 index 0000000..e68e00e --- /dev/null +++ b/pigeonhole/tests/extensions/mailbox/errors/mailboxexists-bad-utf8.sieve @@ -0,0 +1,9 @@ +require "mailbox"; +require "variables"; +require "encoded-character"; + +set "mailbox" "${hex:ff}rop"; +if mailboxexists "${mailbox}" { + keep; +} + diff --git a/pigeonhole/tests/extensions/mailbox/errors/syntax.sieve b/pigeonhole/tests/extensions/mailbox/errors/syntax.sieve new file mode 100644 index 0000000..727a6e8 --- /dev/null +++ b/pigeonhole/tests/extensions/mailbox/errors/syntax.sieve @@ -0,0 +1,41 @@ +require "mailbox"; +require "fileinto"; +require "encoded-character"; + +# 1 +if mailboxexists {} +# 2 +if mailboxexists 3423 {} +# 3 +if mailboxexists :frop {} +# 4 +if mailboxexists 24234 "\\Sent" {} +# 5 +if mailboxexists "frop" 32234 {} +# 6 +if mailboxexists "frop" :friep {} + +if mailboxexists "frop" {} +if mailboxexists ["frop", "friep"] {} + +# W:1 +if mailboxexists "${hex:ff}rop" {} +# W:2 +if mailboxexists ["frop", "${hex:ff}riep"] {} + +# 7 +if mailboxexists "frop" ["frop"] {} + +# 8 +fileinto :create 343 "frop"; +# 9 +fileinto :create :frop "frop"; +# 10 +fileinto :create 234234; + +fileinto :create "frop"; + +# 11 +fileinto :create "${hex:ff}rop"; + + diff --git a/pigeonhole/tests/extensions/mailbox/execute.svtest b/pigeonhole/tests/extensions/mailbox/execute.svtest new file mode 100644 index 0000000..cba3034 --- /dev/null +++ b/pigeonhole/tests/extensions/mailbox/execute.svtest @@ -0,0 +1,80 @@ +require "vnd.dovecot.testsuite"; +require "mailbox"; +require "fileinto"; + +test "MailboxExists - None exist" { + if mailboxexists "frop" { + test_fail "mailboxexists confirms existance of unknown folder"; + } +} + +test_mailbox_create "frop"; +test_mailbox_create "friep"; + +test "MailboxExists - Not all exist" { + if mailboxexists ["frop", "friep", "frml"] { + test_fail "mailboxexists confirms existance of unknown folder"; + } +} + +test_mailbox_create "frml"; + +test "MailboxExists - One exists" { + if not mailboxexists ["frop"] { + test_fail "mailboxexists fails to recognize folder"; + } +} + +test "MailboxExists - All exist" { + if not mailboxexists ["frop", "friep", "frml"] { + test_fail "mailboxexists fails to recognize folders"; + } +} + +test ":Create" { + if mailboxexists "created" { + test_fail "mailbox exists already"; + } + + test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop 1 + +Frop! +. + ; + + fileinto :create "created"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + if not mailboxexists "created" { + test_fail "mailbox somehow not created"; + } + + test_result_reset; + + test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop 2 + +Frop! +. + ; + + fileinto "created"; + + if not test_result_execute { + test_fail "execution of result failed second time"; + } + + test_message :folder "created" 0; + + if not header :is "subject" "Frop 1" { + test_fail "incorrect message read back from mail store"; + } +} diff --git a/pigeonhole/tests/extensions/metadata/errors.svtest b/pigeonhole/tests/extensions/metadata/errors.svtest new file mode 100644 index 0000000..3602484 --- /dev/null +++ b/pigeonhole/tests/extensions/metadata/errors.svtest @@ -0,0 +1,56 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid syntax + */ + +test "Invalid Syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "27" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Metadataexists - bad UTF-8 in mailbox name + */ + +test "Metadataexists - bad UTF-8 in mailbox name" { + if not test_script_compile "errors/metadataexists-bad-utf8.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execution failed"; + } + + # FIXME: check warnings + if not test_error :count "eq" :comparator "i;ascii-numeric" "0" { + test_fail "wrong number of runtime errors reported"; + } +} + +/* + * Metadata - bad UTF-8 in mailbox name + */ + +test "Metadata - bad UTF-8 in mailbox name" { + if not test_script_compile "errors/metadata-bad-utf8.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execution failed"; + } + + # FIXME: check warnings + if not test_error :count "eq" :comparator "i;ascii-numeric" "0" { + test_fail "wrong number of runtime errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/metadata/errors/metadata-bad-utf8.sieve b/pigeonhole/tests/extensions/metadata/errors/metadata-bad-utf8.sieve new file mode 100644 index 0000000..fd093a3 --- /dev/null +++ b/pigeonhole/tests/extensions/metadata/errors/metadata-bad-utf8.sieve @@ -0,0 +1,9 @@ +require "mboxmetadata"; +require "variables"; +require "encoded-character"; + +set "mailbox" "${hex:ff}rop"; +if metadata "${mailbox}" "/private/frop" "friep" { + keep; +} + diff --git a/pigeonhole/tests/extensions/metadata/errors/metadataexists-bad-utf8.sieve b/pigeonhole/tests/extensions/metadata/errors/metadataexists-bad-utf8.sieve new file mode 100644 index 0000000..dbb5023 --- /dev/null +++ b/pigeonhole/tests/extensions/metadata/errors/metadataexists-bad-utf8.sieve @@ -0,0 +1,9 @@ +require "mboxmetadata"; +require "variables"; +require "encoded-character"; + +set "mailbox" "${hex:ff}rop"; +if metadataexists "${mailbox}" ["/private/frop", "/shared/friep"] { + keep; +} + diff --git a/pigeonhole/tests/extensions/metadata/errors/syntax.sieve b/pigeonhole/tests/extensions/metadata/errors/syntax.sieve new file mode 100644 index 0000000..c719d94 --- /dev/null +++ b/pigeonhole/tests/extensions/metadata/errors/syntax.sieve @@ -0,0 +1,53 @@ +require "mboxmetadata"; +require "servermetadata"; +require "encoded-character"; + +# 1-4: Used as a command +metadata; +metadataexists; +servermetadata; +servermetadataexists; + +# 5-8: Used with no argument +if metadata {} +if metadataexists {} +if servermetadata {} +if servermetadataexists {} + +# 9-11: Used with one string argument +if metadata "frop" { } +if servermetadata "frop" { } +if metadataexists "frop" { } + +# 12-15: Used with one number argument +if metadata 13123123 { } +if servermetadata 123123 { } +if metadataexists 123123 { } +if servermetadataexists 123123 {} + +# 16-18: Used with one string list argument +if metadata ["frop"] { } +if servermetadata ["frop"] { } +if metadataexists ["frop"] { } + +# 19-22: Used with unknown tag +if metadata :frop "frop" { } +if servermetadata :frop "frop" { } +if metadataexists :frop "frop" { } +if servermetadataexists :frop "frop" {} + +# 23-26: Invalid arguments +if metadata "/private/frop" "friep" {} +if servermetadata "INBOX" "/private/frop" "friep" {} +if metadataexists 23 "/private/frop" {} +if servermetadataexists "INBOX" "/private/frop" {} + +# W1-W4: Invalid annotations +if metadata "INBOX" "frop" "friep" {} +if servermetadata "frop" "friep" {} +if metadataexists "INBOX" ["/private/frop", "/friep"] { } +if servermetadataexists ["/private/frop", "/friep", "/private/friep"] { } + +# W5-W6: Invalid mailbox name +if metadata "${hex:ff}rop" "/private/frop" "friep" {} +if metadataexists "${hex:ff}rop" ["/private/frop", "/shared/friep"] { } diff --git a/pigeonhole/tests/extensions/metadata/execute.svtest b/pigeonhole/tests/extensions/metadata/execute.svtest new file mode 100644 index 0000000..32aac82 --- /dev/null +++ b/pigeonhole/tests/extensions/metadata/execute.svtest @@ -0,0 +1,145 @@ +require "vnd.dovecot.testsuite"; +require "mboxmetadata"; +require "servermetadata"; +require "fileinto"; + +test "MetadataExists - None exist" { + if metadataexists "INBOX" "/private/frop" { + test_fail "metadataexists confirms existence of unknown annotation"; + } +} + +test_imap_metadata_set :mailbox "INBOX" "/private/frop" "FROP!"; +test_imap_metadata_set :mailbox "INBOX" "/private/friep" "FRIEP!"; + +test "MetadataExists - Not all exist" { + if metadataexists "INBOX" + ["/private/frop", "/private/friep", "/private/frml"] { + test_fail "metadataexists confirms existence of unknown annotation"; + } +} + +test_imap_metadata_set :mailbox "INBOX" "/private/friep" "FRIEP!"; +test_imap_metadata_set :mailbox "INBOX" "/private/frml" "FRML!"; + +test "MetadataExists - One exists" { + if not metadataexists "INBOX" ["/private/frop"] { + test_fail "metadataexists fails to recognize annotation"; + } +} + +test "MetadataExists - All exist" { + if not metadataexists "INBOX" + ["/private/frop", "/private/friep", "/private/frml"] { + test_fail "metadataexists fails to recognize annotations"; + } +} + +test "MetadataExists - Invalid" { + if metadataexists "INBOX" + ["/shared/frop", "/friep", "/private/frml"] { + test_fail "metadataexists accepted invalid annotation name"; + } +} + +test "Metadata" { + if not metadata :is "INBOX" "/private/frop" "FROP!" { + test_fail "invalid metadata value for /private/frop"; + } + if metadata :is "INBOX" "/private/frop" "Hutsefluts" { + test_fail "unexpected match for /private/frop"; + } + + if not metadata :is "INBOX" "/private/friep" "FRIEP!" { + test_fail "invalid metadata value for /private/friep"; + } + if metadata :is "INBOX" "/private/friep" "Hutsefluts" { + test_fail "unexpected match for /private/friep"; + } + + if not metadata :is "INBOX" "/private/frml" "FRML!" { + test_fail "invalid metadata value for /private/frml"; + } + if metadata :is "INBOX" "/private/frml" "Hutsefluts" { + test_fail "unexpected match for /private/frml"; + } +} + +test "Metadata - Invalid" { + if metadata :contains "INBOX" "/frop" "" { + test_fail "erroneously found a value for \"/frop\""; + } +} + +test "ServermetadataExists - None exist" { + if servermetadataexists "/private/frop" { + test_fail "servermetadataexists confirms existence of unknown annotation"; + } +} + +# currently not possible to test servermetadata +if false { + +test_imap_metadata_set "/private/frop" "FROP!"; +test_imap_metadata_set "/private/friep" "FRIEP!"; + +test "ServermetadataExists - Not all exist" { + if servermetadataexists + ["/private/frop", "/private/friep", "/private/frml"] { + test_fail "metadataexists confirms existence of unknown annotation"; + } +} + +test_imap_metadata_set "/private/friep" "FRIEP!"; +test_imap_metadata_set "/private/frml" "FRML!"; + +test "ServermetadataExists - One exists" { + if not servermetadataexists ["/private/frop"] { + test_fail "servermetadataexists fails to recognize annotation"; + } +} + +test "ServermetadataExists - All exist" { + if not servermetadataexists + ["/private/frop", "/private/friep", "/private/frml"] { + test_fail "servermetadataexists fails to recognize annotations"; + } +} + +test "ServermetadataExists - Invalid" { + if servermetadataexists + ["frop", "/private/friep", "/private/frml"] { + test_fail "servermetadataexists accepted invalid annotation name"; + } +} + +test "Servermetadata" { + if not servermetadata :is "/private/frop" "FROP!" { + test_fail "invalid servermetadata value for /private/frop"; + } + if servermetadata :is "/private/frop" "Hutsefluts" { + test_fail "unexpected match for /private/frop"; + } + + if not servermetadata :is "/private/friep" "FRIEP!" { + test_fail "invalid servermetadata value for /private/friep"; + } + if servermetadata :is "/private/friep" "Hutsefluts" { + test_fail "unexpected match for /private/friep"; + } + + if not servermetadata :is "/private/frml" "FRML!" { + test_fail "invalid servermetadata value for /private/frml"; + } + if servermetadata :is "/private/frml" "Hutsefluts" { + test_fail "unexpected match for /private/frml"; + } +} + +test "Servermetadata - Invalid" { + if servermetadata :contains "/frop" "" { + test_fail "erroneously found a value for \"/frop\""; + } +} + +} #disabled diff --git a/pigeonhole/tests/extensions/mime/address.svtest b/pigeonhole/tests/extensions/mime/address.svtest new file mode 100644 index 0000000..1607450 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/address.svtest @@ -0,0 +1,281 @@ +require "vnd.dovecot.testsuite"; +require "mime"; +require "foreverypart"; + +/* + * Basic functionionality + */ + +test_set "message" text: +From: stephan@example.com +To: nico@nl.example.com, harry@de.example.com +cc: Timo <tss(no spam)@fi.iki> +Subject: Frobnitzm + +Test. +. +; + +test "Basic functionality" { + /* Must match */ + if not address :mime :anychild :contains ["to", "from"] "harry" { + test_fail "failed to match address (1)"; + } + + if not address :mime :anychild :contains ["to", "from"] "de.example" { + test_fail "failed to match address (2)"; + } + + if not address :mime :anychild :matches "to" "*@*.example.com" { + test_fail "failed to match address (3)"; + } + + if not address :mime :anychild :is "to" "harry@de.example.com" { + test_fail "failed to match address (4)"; + } + + /* Must not match */ + if address :mime :anychild :is ["to", "from"] "nonsense@example.com" { + test_fail "matches erroneous address"; + } + + /* Match first key */ + if not address :mime :anychild :contains ["to"] ["nico", "fred", "henk"] { + test_fail "failed to match first key"; + } + + /* Match second key */ + if not address :mime :anychild :contains ["to"] ["fred", "nico", "henk"] { + test_fail "failed to match second key"; + } + + /* Match last key */ + if not address :mime :anychild :contains ["to"] ["fred", "henk", "nico"] { + test_fail "failed to match last key"; + } + + /* First header */ + if not address :mime :anychild :contains + ["to", "from"] ["fred", "nico", "henk"] { + test_fail "failed to match first header"; + } + + /* Second header */ + if not address :mime :anychild :contains + ["from", "to"] ["fred", "nico", "henk"] { + test_fail "failed to match second header"; + } + + /* Comment */ + if not address :mime :anychild :is "cc" "tss@fi.iki" { + test_fail "failed to ignore comment in address"; + } +} + +/* + * Basic functionionality - foreverypart + */ + +test "Basic functionality - foreverypart" { + foreverypart { + /* Must match */ + if not address :mime :anychild :contains ["to", "from"] "harry" { + test_fail "failed to match address (1)"; + } + + if not address :mime :anychild :contains ["to", "from"] "de.example" { + test_fail "failed to match address (2)"; + } + + if not address :mime :anychild :matches "to" "*@*.example.com" { + test_fail "failed to match address (3)"; + } + + if not address :mime :anychild :is "to" "harry@de.example.com" { + test_fail "failed to match address (4)"; + } + + /* Must not match */ + if address :mime :anychild :is ["to", "from"] "nonsense@example.com" { + test_fail "matches erroneous address"; + } + + /* Match first key */ + if not address :mime :anychild :contains ["to"] ["nico", "fred", "henk"] { + test_fail "failed to match first key"; + } + + /* Match second key */ + if not address :mime :anychild :contains ["to"] ["fred", "nico", "henk"] { + test_fail "failed to match second key"; + } + + /* Match last key */ + if not address :mime :anychild :contains ["to"] ["fred", "henk", "nico"] { + test_fail "failed to match last key"; + } + + /* First header */ + if not address :mime :anychild :contains + ["to", "from"] ["fred", "nico", "henk"] { + test_fail "failed to match first header"; + } + + /* Second header */ + if not address :mime :anychild :contains + ["from", "to"] ["fred", "nico", "henk"] { + test_fail "failed to match second header"; + } + + /* Comment */ + if not address :mime :anychild :is "cc" "tss@fi.iki" { + test_fail "failed to ignore comment in address"; + } + } +} + +/* + * Address headers + */ + +test_set "message" text: +From: stephan@friep.frop +To: henk@tukkerland.ex +CC: ivo@boer.ex +Bcc: joop@hooibaal.ex +Sender: s.bosch@friep.frop +Resent-From: ivo@boer.ex +Resent-To: idioot@dombo.ex +Subject: Berichtje + +Test. +. +; + +test "Address headers" { + if not address :mime :anychild "from" "stephan@friep.frop" { + test_fail "from header not recognized"; + } + + if not address :mime :anychild "to" "henk@tukkerland.ex" { + test_fail "to header not recognized"; + } + + if not address :mime :anychild "cc" "ivo@boer.ex" { + test_fail "cc header not recognized"; + } + + if not address :mime :anychild "bcc" "joop@hooibaal.ex" { + test_fail "bcc header not recognized"; + } + + if not address :mime :anychild "sender" "s.bosch@friep.frop" { + test_fail "sender header not recognized"; + } + + if not address :mime :anychild "resent-from" "ivo@boer.ex" { + test_fail "resent-from header not recognized"; + } + + if not address :mime :anychild "resent-to" "idioot@dombo.ex" { + test_fail "resent-to header not recognized"; + } +} + +/* + * Address headers - foreverypart + */ + +test "Address headers - foreverypart" { + foreverypart { + if not address :mime :anychild "from" "stephan@friep.frop" { + test_fail "from header not recognized"; + } + + if not address :mime :anychild "to" "henk@tukkerland.ex" { + test_fail "to header not recognized"; + } + + if not address :mime :anychild "cc" "ivo@boer.ex" { + test_fail "cc header not recognized"; + } + + if not address :mime :anychild "bcc" "joop@hooibaal.ex" { + test_fail "bcc header not recognized"; + } + + if not address :mime :anychild "sender" "s.bosch@friep.frop" { + test_fail "sender header not recognized"; + } + + if not address :mime :anychild "resent-from" "ivo@boer.ex" { + test_fail "resent-from header not recognized"; + } + + if not address :mime :anychild "resent-to" "idioot@dombo.ex" { + test_fail "resent-to header not recognized"; + } + } +} + +/* + * Multipart anychild + */ + +test_set "message" text: +From: Hendrik <hendrik@example.com> +To: Harrie <harrie@example.com> +Date: Sat, 11 Oct 2010 00:31:44 +0200 +Subject: Harrie is een prutser +Content-Type: multipart/mixed; boundary=AA +CC: AA@example.com + +This is a multi-part message in MIME format. +--AA +Content-Type: multipart/mixed; boundary=BB +CC: BB@example.com + +This is a multi-part message in MIME format. +--BB +Content-Type: text/plain; charset="us-ascii" +CC: CC@example.com + +Hello + +--BB +Content-Type: text/plain; charset="us-ascii" +CC: DD@example.com + +Hello again + +--BB-- +This is the end of MIME multipart. + +--AA +Content-Type: text/plain; charset="us-ascii" +CC: EE@example.com + +And again + +--AA-- +This is the end of MIME multipart. +. +; + +test "Multipart anychild" { + if not address :mime :anychild :localpart "Cc" "AA" { + test_fail "AA Cc repient does not exist"; + } + if not address :mime :anychild :localpart "Cc" "BB" { + test_fail "BB Cc repient does not exist"; + } + if not address :mime :anychild :localpart "Cc" "CC" { + test_fail "CC Cc repient does not exist"; + } + if not address :mime :anychild :localpart "Cc" "DD" { + test_fail "DD Cc repient does not exist"; + } + if not address :mime :anychild :localpart "Cc" "EE" { + test_fail "EE Cc repient does not exist"; + } +} diff --git a/pigeonhole/tests/extensions/mime/calendar-example.svtest b/pigeonhole/tests/extensions/mime/calendar-example.svtest new file mode 100644 index 0000000..745e6e6 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/calendar-example.svtest @@ -0,0 +1,129 @@ +require "vnd.dovecot.testsuite"; +require "mime"; +require "foreverypart"; +require "editheader"; +require "relational"; +require "variables"; + +# Example from RFC 6047, Section 2.5: +test_set "message" text: +From: user1@example.com +To: user2@example.com +Subject: Phone Conference +Mime-Version: 1.0 +Date: Wed, 07 May 2008 21:30:25 +0400 +Message-ID: <4821E731.5040506@laptop1.example.com> +Content-Type: text/calendar; method=REQUEST; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +BEGIN:VCALENDAR +PRODID:-//Example/ExampleCalendarClient//EN +METHOD:REQUEST +VERSION:2.0 +BEGIN:VEVENT +ORGANIZER:mailto:user1@example.com +ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:user1@example.com +ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:user2@example.com +DTSTAMP:20080507T170000Z +DTSTART:20080701T160000Z +DTEND:20080701T163000Z +SUMMARY:Phone call to discuss your last visit +DESCRIPTION:=D1=82=D1=8B =D0=BA=D0=B0=D0=BA - =D0=B4=D0=BE=D0= + =B2=D0=BE=D0=BB=D0=B5=D0=BD =D0=BF=D0=BE=D0=B5=D0=B7=D0=B4=D0=BA=D0 + =BE=D0=B9? +UID:calsvr.example.com-8739701987387998 +SEQUENCE:0 +STATUS:TENTATIVE +END:VEVENT +END:VCALENDAR +. +; + +test "Calendar only" { + foreverypart { + if allof( + header :mime :count "eq" "Content-Type" "1", + header :mime :contenttype "Content-Type" "text/calendar", + header :mime :param "method" :matches "Content-Type" "*", + header :mime :param "charset" :is "Content-Type" "UTF-8" ) { + addheader "X-ICAL" "${1}"; + break; + } + } + + if not header "x-ical" "request" { + test_fail "Failed to parse message correctly"; + } +} + +# Modified example +test_set "message" text: +From: user1@example.com +To: user2@example.com +Subject: Phone Conference +Mime-Version: 1.0 +Date: Wed, 07 May 2008 21:30:25 +0400 +Message-ID: <4821E731.5040506@laptop1.example.com> +Content-Type: multipart/mixed; boundary=AA + +This is a multi-part message in MIME format. + +--AA +Content-Type: text/plain + +Hello, + +I'd like to discuss your last visit. A tentative meeting schedule is +attached. + +Regards, + +User1 + +--AA +Content-Type: text/calendar; method=REQUEST; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +BEGIN:VCALENDAR +PRODID:-//Example/ExampleCalendarClient//EN +METHOD:REQUEST +VERSION:2.0 +BEGIN:VEVENT +ORGANIZER:mailto:user1@example.com +ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:user1@example.com +ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:user2@example.com +DTSTAMP:20080507T170000Z +DTSTART:20080701T160000Z +DTEND:20080701T163000Z +SUMMARY:Phone call to discuss your last visit +DESCRIPTION:=D1=82=D1=8B =D0=BA=D0=B0=D0=BA - =D0=B4=D0=BE=D0= + =B2=D0=BE=D0=BB=D0=B5=D0=BD =D0=BF=D0=BE=D0=B5=D0=B7=D0=B4=D0=BA=D0 + =BE=D0=B9? +UID:calsvr.example.com-8739701987387998 +SEQUENCE:0 +STATUS:TENTATIVE +END:VEVENT +END:VCALENDAR + +--AA-- +. +; + +test "Multipart message" { + foreverypart { + if allof( + header :mime :count "eq" "Content-Type" "1", + header :mime :contenttype "Content-Type" "text/calendar", + header :mime :param "method" :matches "Content-Type" "*", + header :mime :param "charset" :is "Content-Type" "UTF-8" ) { + addheader "X-ICAL" "${1}"; + break; + } + } + + if not header "x-ical" "request" { + test_fail "Failed to parse message correctly"; + } +} + + diff --git a/pigeonhole/tests/extensions/mime/content-header.svtest b/pigeonhole/tests/extensions/mime/content-header.svtest new file mode 100644 index 0000000..9686e35 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/content-header.svtest @@ -0,0 +1,161 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "mime"; + +test_set "message" text: +From: stephan@example.com +To: timo@example.com +Subject: Frop +Content-Type: text/plain + +Frop +. +; + +test "Simple Content-Type :type" { + if not header :mime :type "content-type" "text" { + test_fail "wrong type extracted"; + } +} + +test "Simple Content-Type :subype" { + if not header :mime :subtype "content-type" "plain" { + test_fail "wrong subtype extracted"; + } +} + +test "Simple Content-Type :contenttype" { + if not header :mime :contenttype "content-type" "text/plain" { + test_fail "wrong contenttype extracted"; + } +} + +test_set "message" text: +From: stephan@example.com +To: timo@example.com +Subject: Frop +Content-Type: text/calendar; method=request; charset=UTF-8; + +Frop +. +; + +test "Advanced Content-Type :type" { + if not header :mime :type "content-type" "text" { + test_fail "wrong type extracted"; + } +} + +test "Advanced Content-Type :subype" { + if not header :mime :subtype "content-type" "calendar" { + test_fail "wrong subtype extracted"; + } +} + +test "Advanced Content-Type :contenttype" { + if not header :mime :contenttype "content-type" "text/calendar" { + test_fail "wrong contenttype extracted"; + } +} + +test "Advanced Content-Type :param" { + if not header :mime :param "method" "content-type" "request" { + test_fail "wrong method param extracted"; + } + + if not header :mime :param "charset" "content-type" "UTF-8" { + test_fail "wrong charset param extracted"; + } + + if not header :mime :param ["method", "charset"] + "content-type" "request" { + test_fail "wrong method param extracted"; + } + + if not header :mime :param ["method", "charset"] + "content-type" "UTF-8" { + test_fail "wrong charset param extracted"; + } + + if not header :count "eq" :mime :param ["method", "charset"] + "content-type" "2" { + test_fail "wrong number of parameters"; + } +} + +test_set "message" text: +From: stephan@example.com +To: timo@example.com +Subject: Frop +Content-Type: application/x-stuff; + title*0*=us-ascii'en'This%20is%20even%20more%20; + title*1*=%2A%2A%2Afun%2A%2A%2A%20; + title*2="isn't it!" + +Frop +. +; + +test "Encoded Content-Type :param" { + if not header :mime :param "title" "content-type" + "This is even more ***fun*** isn't it!" { + test_fail "wrong method param extracted"; + } +} + +test_set "message" text: +From: stephan@example.com +To: timo@example.com +Subject: Frop +Content-Type: image/png +Content-Disposition: inline; filename="frop.exe"; title="Frop!" + +Frop +. +; + +test "Content-Disposition :type" { + if not header :mime :type "content-disposition" "inline" { + test_fail "wrong type extracted"; + } +} + +test "Content-Disposition :subype" { + if not header :mime :subtype "content-disposition" "" { + test_fail "wrong subtype extracted"; + } +} + +test "Content-Disposition :contenttype" { + if not header :mime :contenttype "content-disposition" "inline" { + test_fail "wrong contenttype extracted"; + } +} + +test "Content-Disposition :param" { + if not header :mime :param "filename" "content-disposition" "frop.exe" { + test_fail "wrong filename param extracted"; + } + + if not header :mime :param "title" "content-disposition" "Frop!" { + test_fail "wrong title param extracted"; + } + + if not header :mime :param ["filename", "title"] + "content-disposition" "frop.exe" { + test_fail "wrong filename param extracted"; + } + + if not header :mime :param ["filename", "title"] + "content-disposition" "Frop!" { + test_fail "wrong title param extracted"; + } + + if not header :count "eq" :mime :param ["filename", "title"] + "content-disposition" "2" { + test_fail "wrong number of parameters"; + } + +} + + diff --git a/pigeonhole/tests/extensions/mime/errors.svtest b/pigeonhole/tests/extensions/mime/errors.svtest new file mode 100644 index 0000000..b3b858e --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors.svtest @@ -0,0 +1,162 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test "Foreverypart command" { + if test_script_compile "errors/foreverypart.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "12" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Break command" { + if test_script_compile "errors/break.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "21" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Header test with :mime tag" { + if test_script_compile "errors/header-mime-tag.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "10" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Address test with :mime tag" { + if test_script_compile "errors/address-mime-tag.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "6" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Exists test with :mime tag" { + if test_script_compile "errors/exists-mime-tag.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "6" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Limits" { + if test_script_compile "errors/limits.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "2" { + test_fail "incorrect number of compile errors reported"; + } +} + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=AA + +This is a multi-part message in MIME format. + +--AA +Content-Type: multipart/alternative; boundary=BB + +This is a multi-part message in MIME format. + +--BB +Content-Type: multipart/alternative; boundary=CC + +This is a multi-part message in MIME format. + +--CC +Content-Type: multipart/alternative; boundary=DD + +This is a multi-part message in MIME format. + +--DD +Content-Type: multipart/alternative; boundary=EE + +This is a nested multi-part message in MIME format. + +--EE +Content-Type: text/plain; charset="us-ascii" + +Hello + +--EE-- + +This is the end of the inner MIME multipart. + +--DD-- + +This is the end of the MIME multipart. + +--CC-- + +This is the end of the MIME multipart. + +--BB-- + +This is the end of the MIME multipart. + +--AA-- + +This is the end of the MIME multipart. +. +; + +test "Limits - include" { + if not test_script_compile "errors/limits-include.sieve" { + test_fail "script compile failed"; + } + + if test_script_run { + test_fail "script run should have failed"; + } +} + +test "Extracttext" { + if test_script_compile "errors/extracttext.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "11" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Extracttext - without variables" { + if test_script_compile "errors/extracttext-novar.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "2" { + test_fail "incorrect number of compile errors reported"; + } +} + +test "Extracttext - without foreverypart" { + if test_script_compile "errors/extracttext-nofep.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" :comparator "i;ascii-numeric" "2" { + test_fail "incorrect number of compile errors reported"; + } +} + + diff --git a/pigeonhole/tests/extensions/mime/errors/address-mime-tag.sieve b/pigeonhole/tests/extensions/mime/errors/address-mime-tag.sieve new file mode 100644 index 0000000..7adb7bc --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/address-mime-tag.sieve @@ -0,0 +1,38 @@ +require "mime"; + +## Address + +# No error +if address :contains :mime "To" "frop@example.com" { + discard; +} + +# No error +if address :anychild :contains :mime "To" "frop@example.com" { + discard; +} + +# 1: Bare anychild option +if address :anychild "To" "frop@example.com" { + discard; +} + +# 2: Inappropriate option +if address :mime :anychild :type "To" "frop@example.com" { + discard; +} + +# 3: Inappropriate option +if address :mime :anychild :subtype "To" "frop@example.com" { + discard; +} + +# 4: Inappropriate option +if address :mime :anychild :contenttype "To" "frop@example.com" { + discard; +} + +# 5: Inappropriate option +if address :mime :anychild :param "frop" "To" "frop@example.com" { + discard; +} diff --git a/pigeonhole/tests/extensions/mime/errors/break.sieve b/pigeonhole/tests/extensions/mime/errors/break.sieve new file mode 100644 index 0000000..1858673 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/break.sieve @@ -0,0 +1,157 @@ +require "foreverypart"; + +foreverypart :name "frop" { + # 1: Spurious tag + break :tag; + + # 2: Spurious tests + break true; + + # 3: Spurious tests + break anyof(true, false); + + # 4: Bare string + break "frop"; + + # 5: Bare string-list + break ["frop", "friep"]; + + # 6: Several bad arguments + break 13 ["frop", "friep"]; + + # 7: Spurious additional tag + break :name "frop" :friep; + + # 8: Spurious additional string + break :name "frop" "friep"; + + # 9: Bad name + break :name 13; + + # 10: Bad name + break :name ["frop", "friep"]; + + # No error + break; + + # No error + break :name "frop"; + + # No error + if exists "frop" { + break; + } + + # No error + if exists "frop" { + break :name "frop"; + } + + # No error + foreverypart { + break :name "frop"; + } + + # No error + foreverypart :name "friep" { + break :name "frop"; + } + + # No error + foreverypart :name "friep" { + break :name "friep"; + } + + # No error + foreverypart :name "friep" { + break; + } + + # No error + foreverypart { + if exists "frop" { + break :name "frop"; + } + } + + # No error + foreverypart :name "friep" { + if exists "frop" { + break :name "frop"; + } + } + + # No error + foreverypart :name "friep" { + if exists "frop" { + break :name "friep"; + } + } + + # No error + foreverypart :name "friep" { + if exists "frop" { + break; + } + } +} + +# 11: Outside loop +break; + +# 12: Outside loop +if exists "frop" { + break; +} + +# 13: Outside loop +break :name "frop"; + +# 14: Outside loop +if exists "frop" { + break :name "frop"; +} + +# 15: Bad name +foreverypart { + break :name "frop"; +} + +# 16: Bad name +foreverypart { + if exists "frop" { + break :name "frop"; + } +} + +# 17: Bad name +foreverypart :name "friep" { + break :name "frop"; +} + +# 18: Bad name +foreverypart :name "friep" { + if exists "frop" { + break :name "frop"; + } +} + +# 19: Bad name +foreverypart :name "friep" { + foreverypart :name "frop" { + break :name "frml"; + } +} + +# 20: Bad name +foreverypart :name "friep" { + foreverypart :name "frop" { + if exists "frop" { + break :name "frml"; + } + } +} + + + + diff --git a/pigeonhole/tests/extensions/mime/errors/exists-mime-tag.sieve b/pigeonhole/tests/extensions/mime/errors/exists-mime-tag.sieve new file mode 100644 index 0000000..84c86a7 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/exists-mime-tag.sieve @@ -0,0 +1,43 @@ +require "mime"; + +## Exists + +# No error +if exists :mime "To" { + discard; +} + +# No error +if exists :anychild :mime "To" { + discard; +} + +# 1: Inappropriate option +if exists :anychild "To" { + discard; +} + +# 2: Inappropriate option +if exists :mime :type "To" { + discard; +} + +# 3: Inappropriate option +if exists :mime :subtype "To" { + discard; +} + +# 4: Inappropriate option +if exists :mime :contenttype "To" { + discard; +} + +# 5: Inappropriate option +if exists :mime :param ["frop", "friep"] "To" { + discard; +} + + + + + diff --git a/pigeonhole/tests/extensions/mime/errors/extracttext-nofep.sieve b/pigeonhole/tests/extensions/mime/errors/extracttext-nofep.sieve new file mode 100644 index 0000000..c38b228 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/extracttext-nofep.sieve @@ -0,0 +1,4 @@ +require "extracttext"; +require "variables"; + +keep; diff --git a/pigeonhole/tests/extensions/mime/errors/extracttext-novar.sieve b/pigeonhole/tests/extensions/mime/errors/extracttext-novar.sieve new file mode 100644 index 0000000..8e2a378 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/extracttext-novar.sieve @@ -0,0 +1,6 @@ +require "extracttext"; +require "foreverypart"; + +foreverypart { + extracttext "frop"; +} diff --git a/pigeonhole/tests/extensions/mime/errors/extracttext.sieve b/pigeonhole/tests/extensions/mime/errors/extracttext.sieve new file mode 100644 index 0000000..f8af1c9 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/extracttext.sieve @@ -0,0 +1,42 @@ +require "extracttext"; +require "variables"; +require "foreverypart"; + +# 1: Used outside foreverypart +extracttext :first 10 "data"; + +foreverypart { + # 2: Missing arguments + extracttext; + + # 3: Bad arguments + extracttext 1; + + # 4: Bad arguments + extracttext ["frop", "friep"]; + + # 5: Unknown tag + extracttext :frop "frop"; + + # 6: Invalid variable name + extracttext "${frop}"; + + # Not an error + extracttext "\n\a\m\e"; + + # 7: Trying to assign match variable + extracttext "0"; + + # Not an error + extracttext :lower "frop"; + + # 8: Bad ":first" tag + extracttext :first "frop"; + + # 9: Bad ":first" tag + extracttext :first "frop" "friep"; + + # 10: Bad ":first" tag + extracttext :first ["frop", "friep"] "frml"; +} + diff --git a/pigeonhole/tests/extensions/mime/errors/foreverypart.sieve b/pigeonhole/tests/extensions/mime/errors/foreverypart.sieve new file mode 100644 index 0000000..38a28d4 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/foreverypart.sieve @@ -0,0 +1,45 @@ +require "foreverypart"; + +# 1: No block +foreverypart; + +# 2: Spurious tag +foreverypart :tag { } + +# 3: Spurious tests +foreverypart true { } + +# 4: Spurious tests +foreverypart anyof(true, false) { } + +# 5: Bare string +foreverypart "frop" { } + +# 6: Bare string-list +foreverypart ["frop", "friep"] { } + +# 7: Several bad arguments +foreverypart 13 ["frop", "friep"] { } + +# 8: Spurious additional tag +foreverypart :name "frop" :friep { } + +# 9: Spurious additional string +foreverypart :name "frop" "friep" { } + +# 10: Bad name +foreverypart :name 13 { } + +# 11: Bad name +foreverypart :name ["frop", "friep"] { } + +# No error +foreverypart { keep; } + +# No error +foreverypart :name "frop" { keep; } + +# No error +foreverypart :name "frop" { foreverypart { keep; } } + + diff --git a/pigeonhole/tests/extensions/mime/errors/header-mime-tag.sieve b/pigeonhole/tests/extensions/mime/errors/header-mime-tag.sieve new file mode 100644 index 0000000..85782af --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/header-mime-tag.sieve @@ -0,0 +1,100 @@ +require "mime"; + +## Header + +# No error +if header :contains :mime "Content-Type" "text/plain" { + discard; +} + +# No error +if header :mime :type "Content-Type" "text" { + discard; +} + +# No error +if header :mime :subtype "Content-Type" "plain" { + discard; +} + +# No error +if header :mime :contenttype "Content-Type" "text/plain" { + discard; +} + +# No error +if header :mime :param ["frop", "friep"] "Content-Type" "frml" { + discard; +} + +# No error +if header :anychild :contains :mime "Content-Type" "text/plain" { + discard; +} + +# No error +if header :mime :anychild :type "Content-Type" "text" { + discard; +} + +# No error +if header :mime :subtype :anychild "Content-Type" "plain" { + discard; +} + +# No error +if header :anychild :mime :contenttype "Content-Type" "text/plain" { + discard; +} + +# No error +if header :mime :param ["frop", "friep"] :anychild "Content-Type" "frml" { + discard; +} + +# 1: Bare anychild option +if header :anychild "Content-Type" "frml" { + discard; +} + +# 2: Bare mime option +if header :type "Content-Type" "frml" { + discard; +} + +# 3: Bare mime option +if header :subtype "Content-Type" "frml" { + discard; +} + +# 4: Bare mime option +if header :contenttype "Content-Type" "frml" { + discard; +} + +# 5: Bare mime option +if header :param "frop" "Content-Type" "frml" { + discard; +} + +# 6: Multiple option tags +if header :mime :type :subtype "Content-Type" "frml" { + discard; +} + +# 7: Bad param argument +if header :mime :param 13 "Content-Type" "frml" { + discard; +} + +# 8: Missing param argument +if header :mime :param :anychild "Content-Type" "frml" { + discard; +} + +# 9: Missing param argument +if header :mime :param :frop "Content-Type" "frml" { + discard; +} + + diff --git a/pigeonhole/tests/extensions/mime/errors/limits-include.sieve b/pigeonhole/tests/extensions/mime/errors/limits-include.sieve new file mode 100644 index 0000000..ef92456 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/limits-include.sieve @@ -0,0 +1,6 @@ +require "foreverypart"; +require "include"; + +foreverypart :name "frop" { + include "include-loop-2"; +} diff --git a/pigeonhole/tests/extensions/mime/errors/limits.sieve b/pigeonhole/tests/extensions/mime/errors/limits.sieve new file mode 100644 index 0000000..0add1c3 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/errors/limits.sieve @@ -0,0 +1,13 @@ +require "foreverypart"; + +foreverypart :name "frop" { + foreverypart :name "friep" { + foreverypart :name "frml" { + foreverypart { + foreverypart { + break; + } + } + } + } +} diff --git a/pigeonhole/tests/extensions/mime/execute.svtest b/pigeonhole/tests/extensions/mime/execute.svtest new file mode 100644 index 0000000..2ced83b --- /dev/null +++ b/pigeonhole/tests/extensions/mime/execute.svtest @@ -0,0 +1,82 @@ +require "vnd.dovecot.testsuite"; + +/* + * Execution testing (currently just meant to trigger any segfaults) + */ + +test_set "message" text: +From: Whomever <whoever@example.com> +To: Someone <someone@example.com> +Date: Sat, 10 Oct 2009 00:30:04 +0200 +Subject: whatever +Content-Type: multipart/mixed; boundary=outer + +This is a multi-part message in MIME format. + +--outer +Content-Type: multipart/alternative; boundary=inner + +This is a nested multi-part message in MIME format. + +--inner +Content-Type: text/plain; charset="us-ascii" + +Hello + +--inner +Content-Type: text/html; charset="us-ascii" + +<html><body>Hello</body></html> + +--inner-- + +This is the end of the inner MIME multipart. + +--outer +Content-Type: message/rfc822 + +From: Someone Else +Subject: Hello, this is an elaborate request for you to finally say hello + already! + +Please say Hello + +--outer-- + +This is the end of the outer MIME multipart. +. +; + +test "Basic - foreverypart" { + if not test_script_compile "execute/foreverypart.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } + + test_binary_save "ihave-basic"; + test_binary_load "ihave-basic"; +} + +test "Basic - mime" { + if not test_script_compile "execute/mime.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } + + test_binary_save "ihave-basic"; + test_binary_load "ihave-basic"; +} diff --git a/pigeonhole/tests/extensions/mime/execute/foreverypart.sieve b/pigeonhole/tests/extensions/mime/execute/foreverypart.sieve new file mode 100644 index 0000000..9ae1fba --- /dev/null +++ b/pigeonhole/tests/extensions/mime/execute/foreverypart.sieve @@ -0,0 +1,14 @@ +require "foreverypart"; +require "variables"; + +foreverypart { + foreverypart { + foreverypart { + foreverypart { + set "a" "a${a}"; + } + } + } +} + + diff --git a/pigeonhole/tests/extensions/mime/execute/mime.sieve b/pigeonhole/tests/extensions/mime/execute/mime.sieve new file mode 100644 index 0000000..dd7fedc --- /dev/null +++ b/pigeonhole/tests/extensions/mime/execute/mime.sieve @@ -0,0 +1,69 @@ +require "mime"; +require "foreverypart"; +require "variables"; + +if header :contains :mime "Content-Type" "text/plain" { + discard; +} +if header :mime :type "Content-Type" "text" { + discard; +} +if header :mime :subtype "Content-Type" "plain" { + discard; +} +if header :mime :contenttype "Content-Type" "text/plain" { + discard; +} +if header :mime :param ["frop", "friep"] "Content-Type" "frml" { + discard; +} +if header :anychild :contains :mime "Content-Type" "text/plain" { + discard; +} +if header :mime :anychild :type "Content-Type" "text" { + discard; +} +if header :mime :subtype :anychild "Content-Type" "plain" { + discard; +} +if header :anychild :mime :contenttype "Content-Type" "text/plain" { + discard; +} +if header :mime :param ["frop", "friep"] :anychild "Content-Type" "frml" { + discard; +} + +foreverypart { + foreverypart { + if header :contains :mime "Content-Type" "text/plain" { + discard; + } + if header :mime :type "Content-Type" "text" { + discard; + } + if header :mime :subtype "Content-Type" "plain" { + discard; + } + if header :mime :contenttype "Content-Type" "text/plain" { + discard; + } + if header :mime :param ["frop", "friep"] "Content-Type" "frml" { + discard; + } + if header :anychild :contains :mime "Content-Type" "text/plain" { + discard; + } + if header :mime :anychild :type "Content-Type" "text" { + discard; + } + if header :mime :subtype :anychild "Content-Type" "plain" { + discard; + } + if header :anychild :mime :contenttype "Content-Type" "text/plain" { + discard; + } + if header :mime :param ["frop", "friep"] :anychild "Content-Type" "frml" { + discard; + } + } +} diff --git a/pigeonhole/tests/extensions/mime/exists.svtest b/pigeonhole/tests/extensions/mime/exists.svtest new file mode 100644 index 0000000..517deeb --- /dev/null +++ b/pigeonhole/tests/extensions/mime/exists.svtest @@ -0,0 +1,237 @@ +require "vnd.dovecot.testsuite"; +require "mime"; +require "foreverypart"; + +test_set "message" text: +From: stephan@example.org +To: nico@vestingbar.bl +Subject: Test message +Date: Wed, 29 Jul 2009 18:21:44 +0300 +X-Spam-Status: Not Spam +Resent-To: nico@frop.example.com + +Test! +. +; + +/* + * One header + */ + +test "One header" { + if not exists :mime :anychild "from" { + test_fail "exists test missed from header"; + } + + if exists :mime :anychild "x-nonsense" { + test_fail "exists test found non-existent header"; + } +} + +/* + * One header - foreverypart + */ + +test "One header - foreverypart" { + foreverypart { + if not exists :mime :anychild "from" { + test_fail "exists test missed from header"; + } + + if exists :mime :anychild "x-nonsense" { + test_fail "exists test found non-existent header"; + } + } +} + +/* + * Two headers + */ + +test "Two headers" { + if not exists :mime :anychild ["from","to"] { + test_fail "exists test missed from or to header"; + } + + if exists :mime :anychild ["from","x-nonsense"] { + test_fail "exists test found non-existent header (1)"; + } + + if exists :mime :anychild ["x-nonsense","to"] { + test_fail "exists test found non-existent header (2)"; + } + + if exists :mime :anychild ["x-nonsense","x-nonsense2"] { + test_fail "exists test found non-existent header (3)"; + } +} + +/* + * Two headers - foreverypart + */ + +test "Two headers - foreverypart" { + foreverypart { + if not exists :mime :anychild ["from","to"] { + test_fail "exists test missed from or to header"; + } + + if exists :mime :anychild ["from","x-nonsense"] { + test_fail "exists test found non-existent header (1)"; + } + + if exists :mime :anychild ["x-nonsense","to"] { + test_fail "exists test found non-existent header (2)"; + } + + if exists :mime :anychild ["x-nonsense","x-nonsense2"] { + test_fail "exists test found non-existent header (3)"; + } + } +} + +/* + * Three headers + */ + +test "Three headers" { + if not exists :mime :anychild ["Subject","date","resent-to"] { + test_fail "exists test missed subject, date or resent-to header"; + } + + if exists :mime :anychild ["x-nonsense","date","resent-to"] { + test_fail "exists test found non-existent header (1)"; + } + + if exists :mime :anychild ["subject", "x-nonsense","resent-to"] { + test_fail "exists test found non-existent header (2)"; + } + + if exists :mime :anychild ["subject","date","x-nonsense"] { + test_fail "exists test found non-existent header (3)"; + } + + if exists :mime :anychild ["subject", "x-nonsense","x-nonsense2"] { + test_fail "exists test found non-existent header (4)"; + } + + if exists :mime :anychild ["x-nonsense","date","x-nonsense2"] { + test_fail "exists test found non-existent header (5)"; + } + + if exists :mime :anychild ["x-nonsense","x-nonsense2","resent-to"] { + test_fail "exists test found non-existent header (6)"; + } + + if exists :mime :anychild ["x-nonsense","x-nonsense2","x-nonsense3"] { + test_fail "exists test found non-existent header (7)"; + } +} + +/* + * Three headers - foreverypart + */ + +test "Three headers - foreverypart " { + foreverypart { + if not exists :mime :anychild ["Subject","date","resent-to"] { + test_fail "exists test missed subject, date or resent-to header"; + } + + if exists :mime :anychild ["x-nonsense","date","resent-to"] { + test_fail "exists test found non-existent header (1)"; + } + + if exists :mime :anychild ["subject", "x-nonsense","resent-to"] { + test_fail "exists test found non-existent header (2)"; + } + + if exists :mime :anychild ["subject","date","x-nonsense"] { + test_fail "exists test found non-existent header (3)"; + } + + if exists :mime :anychild ["subject", "x-nonsense","x-nonsense2"] { + test_fail "exists test found non-existent header (4)"; + } + + if exists :mime :anychild ["x-nonsense","date","x-nonsense2"] { + test_fail "exists test found non-existent header (5)"; + } + + if exists :mime :anychild ["x-nonsense","x-nonsense2","resent-to"] { + test_fail "exists test found non-existent header (6)"; + } + + if exists :mime :anychild ["x-nonsense","x-nonsense2","x-nonsense3"] { + test_fail "exists test found non-existent header (7)"; + } + } +} + +/* + * Multipart anychild + */ + +test_set "message" text: +From: Hendrik <hendrik@example.com> +To: Harrie <harrie@example.com> +Date: Sat, 11 Oct 2010 00:31:44 +0200 +Subject: Harrie is een prutser +Content-Type: multipart/mixed; boundary=AA +X-Test1: AA + +This is a multi-part message in MIME format. +--AA +Content-Type: multipart/mixed; boundary=BB +X-Test2: BB + +This is a multi-part message in MIME format. +--BB +Content-Type: text/plain; charset="us-ascii" +X-Test3: CC + +Hello + +--BB +Content-Type: text/plain; charset="us-ascii" +X-Test4: DD + +Hello again + +--BB-- +This is the end of MIME multipart. + +--AA +Content-Type: text/plain; charset="us-ascii" +X-Test5: EE + +And again + +--AA-- +This is the end of MIME multipart. +. +; + +test "Multipart anychild" { + if not exists :mime :anychild "X-Test1" { + test_fail "X-Test1 header does exist"; + } + if not exists :mime :anychild "X-Test2" { + test_fail "X-Test2 header does exist"; + } + if not exists :mime :anychild "X-Test3" { + test_fail "X-Test3 header does exist"; + } + if not exists :mime :anychild "X-Test4" { + test_fail "X-Test4 header does exist"; + } + if not exists :mime :anychild "X-Test5" { + test_fail "X-Test5 header does exist"; + } + if not exists :mime :anychild + ["X-Test1", "X-Test2", "X-Test3", "X-Test4", "X-Test5"] { + test_fail "Not all headers exist"; + } +} + + diff --git a/pigeonhole/tests/extensions/mime/extracttext.svtest b/pigeonhole/tests/extensions/mime/extracttext.svtest new file mode 100644 index 0000000..510a52b --- /dev/null +++ b/pigeonhole/tests/extensions/mime/extracttext.svtest @@ -0,0 +1,143 @@ +require "vnd.dovecot.testsuite"; +require "foreverypart"; +require "variables"; +require "extracttext"; + +test_set "message" text: +From: Hendrik <hendrik@example.com> +To: Harrie <harrie@example.com> +Date: Sat, 11 Oct 2010 00:31:44 +0200 +Subject: Harrie is een prutser +Content-Type: multipart/mixed; boundary=AA + +This is a multi-part message in MIME format. +--AA +Content-Type: multipart/mixed; boundary=BB + +This is a multi-part message in MIME format. +--BB +Content-Type: text/plain; charset="us-ascii" + +This is the first message part containing +plain text. + +--BB +Content-Type: text/plain; charset="us-ascii" + +This is another plain text message part. + +--BB-- +This is the end of MIME multipart. + +--AA +Content-Type: text/html; charset="us-ascii" + +<html> +<body>This is a piece of HTML text.</body> +</html> + +--AA-- +This is the end of MIME multipart. +. +; + +test "Basic" { + set "a" "a"; + foreverypart { + extracttext "b"; + if string "${a}" "aaa" { + if not string :contains "${b}" "first" { + test_fail "bad content extracted: ${b}"; + } + } elsif string "${a}" "aaaa" { + if not string :contains "${b}" "another" { + test_fail "bad content extracted: ${b}"; + } + } elsif string "${a}" "aaaaa" { + if not string :contains "${b}" "HTML text" { + test_fail "bad content extracted: ${b}"; + } + if string :contains "${b}" "<html>" { + test_fail "content extracted html: ${b}"; + } + } + set "a" "a${a}"; + } + if not string "${a}" "aaaaaa" { + set :length "parts" "${a}"; + test_fail "bad number of parts parsed: ${parts}"; + } +} + +test_set "message" text: +From: <stephan@example.com> +To: <frop@example.com> +Subject: Frop! + +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! FROP! +. +; + +test "First - less" { + foreverypart { + extracttext :first 20 "data"; + if not string "${data}" "FROP! FROP! FROP! FR" { + test_fail "Bad data extracted"; + } + + extracttext :length :first 100 "data_len"; + if not string "${data_len}" "100" { + test_fail "Bad number of bytes extracted"; + } + } +} + +test_set "message" text: +From: <stephan@example.com> +To: <frop@example.com> +Subject: Frop! + +FROP! FROP! FROP! FROP! +. +; + +test "First - more" { + foreverypart { + extracttext :first 100 "data"; + if not string :matches "${data}" "FROP! FROP! FROP! FROP!*" { + test_fail "Bad data extracted"; + } + } +} + +test_set "message" text: +From: <stephan@example.com> +To: <frop@example.com> +Subject: Frop! + +FROP! FROP! FROP! FROP! +. +; + +test "Modifier" { + foreverypart { + extracttext :lower :upperfirst "data"; + if not string :matches "${data}" "Frop! frop! frop! frop!*" { + test_fail "Bad data extracted"; + } + } +} + + + diff --git a/pigeonhole/tests/extensions/mime/foreverypart.svtest b/pigeonhole/tests/extensions/mime/foreverypart.svtest new file mode 100644 index 0000000..08907c9 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/foreverypart.svtest @@ -0,0 +1,178 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "foreverypart"; +require "mime"; +require "variables"; +require "include"; + +test_set "message" text: +From: Hendrik <hendrik@example.com> +To: Harrie <harrie@example.com> +Date: Sat, 11 Oct 2010 00:31:44 +0200 +Subject: Harrie is een prutser +Content-Type: multipart/mixed; boundary=AA +X-Test: AA + +This is a multi-part message in MIME format. +--AA +Content-Type: multipart/mixed; boundary=BB +X-Test: BB + +This is a multi-part message in MIME format. +--BB +Content-Type: text/plain; charset="us-ascii" +X-Test: CC + +Hello + +--BB +Content-Type: text/plain; charset="us-ascii" +X-Test: DD + +Hello again + +--BB-- +This is the end of MIME multipart. + +--AA +Content-Type: text/plain; charset="us-ascii" +X-Test: EE + +And again + +--AA-- +This is the end of MIME multipart. +. +; + +test "Single loop" { + set "a" "a"; + foreverypart { + set :length "la" "${a}"; + + if string "${a}" "a" { + if not header :mime "X-Test" "AA" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aa" { + if not header :mime "X-Test" "BB" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaa" { + if not header :mime "X-Test" "CC" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaa" { + if not header :mime "X-Test" "DD" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaa" { + if not header :mime "X-Test" "EE" { + test_fail "wrong header extracted (${la})"; + } + } + set "a" "a${a}"; + } +} + +test "Double loop" { + set "a" "a"; + foreverypart { + set :length "la" "${a}"; + + if string "${a}" "a" { + if not header :mime "X-Test" "AA" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaaa" { + if not header :mime "X-Test" "BB" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaaaaaa" { + if not header :mime "X-Test" "CC" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaaaaaaa" { + if not header :mime "X-Test" "DD" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaaaaaaaa" { + if not header :mime "X-Test" "EE" { + test_fail "wrong header extracted (${la})"; + } + } + + set "a" "a${a}"; + + foreverypart { + set :length "la" "${a}"; + + if string "${a}" "aa" { + if not header :mime "X-Test" "BB" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaa" { + if not header :mime "X-Test" "CC" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaa" { + if not header :mime "X-Test" "DD" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaa" { + if not header :mime "X-Test" "EE" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaaaa" { + if not header :mime "X-Test" "CC" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${a}" "aaaaaaaa" { + if not header :mime "X-Test" "DD" { + test_fail "wrong header extracted (${la})"; + } + } + set "a" "a${a}"; + } + } +} + +test "Double loop - include" { + global "in"; + global "error"; + set "in" "a"; + foreverypart { + set :length "la" "${in}"; + + if string "${in}" "in" { + if not header :mime "X-Test" "AA" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${in}" "aaaaaa" { + if not header :mime "X-Test" "BB" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${in}" "aaaaaaaaa" { + if not header :mime "X-Test" "CC" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${in}" "aaaaaaaaaa" { + if not header :mime "X-Test" "DD" { + test_fail "wrong header extracted (${la})"; + } + } elsif string "${in}" "aaaaaaaaaaa" { + if not header :mime "X-Test" "EE" { + test_fail "wrong header extracted (${la})"; + } + } + + set "in" "a${in}"; + + include "include-foreverypart"; + + if not string "${error}" "" { + test_fail "INCLUDED: ${error}"; + } + } +} + diff --git a/pigeonhole/tests/extensions/mime/header.svtest b/pigeonhole/tests/extensions/mime/header.svtest new file mode 100644 index 0000000..48cd9e4 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/header.svtest @@ -0,0 +1,444 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "foreverypart"; +require "mime"; + +/* + * Basic functionality + */ + +test_set "message" text: +From: stephan@example.com +To: nico@nl.example.com, harry@de.example.com +Subject: Frobnitzm +Comments: This is nonsense. +Keywords: nonsense, strange, testing +X-Spam: Yes + +Test. +. +; + +test "Basic functionality" { + /* Must match */ + if not header :mime :anychild :contains ["Subject", "Comments"] "Frobnitzm" { + test_fail "failed to match header (1)"; + } + + if not header :mime :anychild :contains ["Subject", "Comments"] "nonsense" { + test_fail "failed to match header(2)"; + } + + if not header :mime :anychild :matches "Keywords" "*, strange, *" { + test_fail "failed to match header (3)"; + } + + if not header :mime :anychild :is "Comments" "This is nonsense." { + test_fail "failed to match header (4)"; + } + + /* Must not match */ + if header :mime :anychild ["subject", "comments", "keywords"] "idiotic" { + test_fail "matched nonsense"; + } + + /* Match first key */ + if not header :mime :anychild :contains ["keywords"] ["strange", "snot", "vreemd"] { + test_fail "failed to match first key"; + } + + /* Match second key */ + if not header :mime :anychild :contains ["keywords"] ["raar", "strange", "vreemd"] { + test_fail "failed to match second key"; + } + + /* Match last key */ + if not header :mime :anychild :contains ["keywords"] ["raar", "snot", "strange"] { + test_fail "failed to match last key"; + } + + /* First header */ + if not header :mime :anychild :contains ["keywords", "subject"] + ["raar", "strange", "vreemd"] { + test_fail "failed to match first header"; + } + + /* Second header */ + if not header :mime :anychild :contains ["subject", "keywords"] + ["raar", "strange", "vreemd"] { + test_fail "failed to match second header"; + } +} + +/* + * Basic functionality - foreverypart + */ + +test "Basic functionality - foreverypart" { + foreverypart { + /* Must match */ + if not header :mime :anychild :contains ["Subject", "Comments"] "Frobnitzm" { + test_fail "failed to match header (1)"; + } + + if not header :mime :anychild :contains ["Subject", "Comments"] "nonsense" { + test_fail "failed to match header(2)"; + } + + if not header :mime :anychild :matches "Keywords" "*, strange, *" { + test_fail "failed to match header (3)"; + } + + if not header :mime :anychild :is "Comments" "This is nonsense." { + test_fail "failed to match header (4)"; + } + + /* Must not match */ + if header :mime :anychild ["subject", "comments", "keywords"] "idiotic" { + test_fail "matched nonsense"; + } + + /* Match first key */ + if not header :mime :anychild :contains ["keywords"] ["strange", "snot", "vreemd"] { + test_fail "failed to match first key"; + } + + /* Match second key */ + if not header :mime :anychild :contains ["keywords"] ["raar", "strange", "vreemd"] { + test_fail "failed to match second key"; + } + + /* Match last key */ + if not header :mime :anychild :contains ["keywords"] ["raar", "snot", "strange"] { + test_fail "failed to match last key"; + } + + /* First header */ + if not header :mime :anychild :contains ["keywords", "subject"] + ["raar", "strange", "vreemd"] { + test_fail "failed to match first header"; + } + + /* Second header */ + if not header :mime :anychild :contains ["subject", "keywords"] + ["raar", "strange", "vreemd"] { + test_fail "failed to match second header"; + } + } +} + +/* + * Matching empty key + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +X-Caffeine: C8H10N4O2 +Subject: I need coffee! +Comments: + +Text +. +; + +test "Matching empty key" { + if header :mime :anychild :is "X-Caffeine" "" { + test_fail ":is-matched non-empty header with empty string"; + } + + if not header :mime :anychild :contains "X-Caffeine" "" { + test_fail "failed to match existing header with empty string"; + } + + if not header :mime :anychild :is "comments" "" { + test_fail "failed to match empty header :mime :anychild with empty string"; + } + + if header :mime :anychild :contains "X-Nonsense" "" { + test_fail ":contains-matched non-existent header with empty string"; + } +} + +/* + * Matching empty key - foreverypart + */ + +test "Matching empty key - foreverypart" { + foreverypart { + if header :mime :anychild :is "X-Caffeine" "" { + test_fail ":is-matched non-empty header with empty string"; + } + + if not header :mime :anychild :contains "X-Caffeine" "" { + test_fail "failed to match existing header with empty string"; + } + + if not header :mime :anychild :is "comments" "" { + test_fail "failed to match empty header :mime :anychild with empty string"; + } + + if header :mime :anychild :contains "X-Nonsense" "" { + test_fail ":contains-matched non-existent header with empty string"; + } + } +} + +/* + * Ignoring whitespace + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Help +X-A: Text +X-B: Text + +Text +. +; + +test "Ignoring whitespace" { + if not header :mime :anychild :is "x-a" "Text" { + if header :mime :anychild :matches "x-a" "*" { + set "header" "${1}"; + } + test_fail "header :mime :anychild test does not strip leading whitespace (header=`${header}`)"; + } + + if not header :mime :anychild :is "x-b" "Text" { + if header :mime :anychild :matches "x-b" "*" { + set "header" "${1}"; + } + test_fail "header :mime :anychild test does not strip trailing whitespace (header=`${header}`)"; + } + + if not header :mime :anychild :is "subject" "Help" { + if header :mime :anychild :matches "subject" "*" { + set "header" "${1}"; + } + test_fail "header :mime :anychild test does not strip both leading and trailing whitespace (header=`${header}`)"; + } +} + +/* + * Ignoring whitespace - foreverypart + */ + +test "Ignoring whitespace - foreverypart" { + foreverypart { + if not header :mime :anychild :is "x-a" "Text" { + if header :mime :anychild :matches "x-a" "*" { + set "header" "${1}"; + } + test_fail "header :mime :anychild test does not strip leading whitespace (header=`${header}`)"; + } + + if not header :mime :anychild :is "x-b" "Text" { + if header :mime :anychild :matches "x-b" "*" { + set "header" "${1}"; + } + test_fail "header :mime :anychild test does not strip trailing whitespace (header=`${header}`)"; + } + + if not header :mime :anychild :is "subject" "Help" { + if header :mime :anychild :matches "subject" "*" { + set "header" "${1}"; + } + test_fail "header :mime :anychild test does not strip both leading and trailing whitespace (header=`${header}`)"; + } + } +} + +/* + * Absent or empty header + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +CC: harry@nonsense.ex +Subject: +Comments: + +Text +. +; + +test "Absent or empty header" { + if not header :mime :anychild :matches "Cc" "?*" { + test_fail "CC header is not absent or empty"; + } + + if header :mime :anychild :matches "Subject" "?*" { + test_fail "Subject header is empty, but matched otherwise"; + } + + if header :mime :anychild :matches "Comment" "?*" { + test_fail "Comment header is empty, but matched otherwise"; + } +} + +/* + * Absent or empty header - foreverypart + */ + +test "Absent or empty header - foreverypart" { + foreverypart { + if not header :mime :anychild :matches "Cc" "?*" { + test_fail "CC header is not absent or empty"; + } + + if header :mime :anychild :matches "Subject" "?*" { + test_fail "Subject header is empty, but matched otherwise"; + } + + if header :mime :anychild :matches "Comment" "?*" { + test_fail "Comment header is empty, but matched otherwise"; + } + } +} + + +/* + * Invalid header name + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Valid message +X-Multiline: This is a multi-line + header body, which should be + unfolded correctly. + +Text +. +; + +test "Invalid header name" { + if header :mime :anychild :contains "subject:" "" { + test_fail "matched invalid header name"; + } + + if header :mime :anychild :contains "to!" "" { + test_fail "matched invalid header name"; + } +} + +/* + * Invalid header name - foreverypart + */ + +test "Invalid header name - foreverypart" { + foreverypart { + if header :mime :anychild :contains "subject:" "" { + test_fail "matched invalid header name"; + } + + if header :mime :anychild :contains "to!" "" { + test_fail "matched invalid header name"; + } + } +} + +/* + * Folded headers + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Not enough space on a line! +X-Multiline: This is a multi-line + header body, which should be + unfolded correctly. + +Text +. +; + +test "Folded headers" { + if not header :mime :anychild :is "x-multiline" + "This is a multi-line header body, which should be unfolded correctly." { + test_fail "failed to properly unfold folded header."; + } +} + +/* + * Folded headers - foreverypart + */ + +test "Folded headers - foreverypart" { + foreverypart { + if not header :mime :anychild :is "x-multiline" + "This is a multi-line header body, which should be unfolded correctly." { + test_fail "failed to properly unfold folded header."; + } + } +} + +/* + * Multipart anychild + */ + +test_set "message" text: +From: Hendrik <hendrik@example.com> +To: Harrie <harrie@example.com> +Date: Sat, 11 Oct 2010 00:31:44 +0200 +Subject: Harrie is een prutser +Content-Type: multipart/mixed; boundary=AA +X-Test: AA + +This is a multi-part message in MIME format. +--AA +Content-Type: multipart/mixed; boundary=BB +X-Test: BB + +This is a multi-part message in MIME format. +--BB +Content-Type: text/plain; charset="us-ascii" +X-Test: CC + +Hello + +--BB +Content-Type: text/plain; charset="us-ascii" +X-Test: DD + +Hello again + +--BB-- +This is the end of MIME multipart. + +--AA +Content-Type: text/plain; charset="us-ascii" +X-Test: EE + +And again + +--AA-- +This is the end of MIME multipart. +. +; + +test "Multipart anychild" { + if not header :mime :anychild "X-Test" "AA" { + test_fail "No AA"; + } + if not header :mime :anychild "X-Test" "BB" { + test_fail "No BB"; + } + if not header :mime :anychild "X-Test" "CC" { + test_fail "No CC"; + } + if not header :mime :anychild "X-Test" "DD" { + test_fail "No DD"; + } + if not header :mime :anychild "X-Test" "EE" { + test_fail "No EE"; + } +} + + diff --git a/pigeonhole/tests/extensions/mime/included/include-foreverypart.sieve b/pigeonhole/tests/extensions/mime/included/include-foreverypart.sieve new file mode 100644 index 0000000..f1b1b16 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/included/include-foreverypart.sieve @@ -0,0 +1,44 @@ +require "include"; +require "foreverypart"; +require "mime"; +require "variables"; + +global "in"; +global "error"; + +foreverypart { + set :length "la" "${in}"; + + if string "${in}" "aa" { + if not header :mime "X-Test" "BB" { + set "error" "wrong header extracted (${la})"; + return; + } + } elsif string "${in}" "aaa" { + if not header :mime "X-Test" "CC" { + set "error" "wrong header extracted (${la})"; + return; + } + } elsif string "${in}" "aaaa" { + if not header :mime "X-Test" "DD" { + set "error" "wrong header extracted (${la})"; + return; + } + } elsif string "${in}" "aaaaa" { + if not header :mime "X-Test" "EE" { + set "error" "wrong header extracted (${la})"; + return; + } + } elsif string "${in}" "aaaaaaa" { + if not header :mime "X-Test" "CC" { + set "error" "wrong header extracted (${la})"; + return; + } + } elsif string "${in}" "aaaaaaaa" { + if not header :mime "X-Test" "DD" { + set "error" "wrong header extracted (${la})"; + return; + } + } + set "in" "a${in}"; +} diff --git a/pigeonhole/tests/extensions/mime/included/include-loop-2.sieve b/pigeonhole/tests/extensions/mime/included/include-loop-2.sieve new file mode 100644 index 0000000..80c5884 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/included/include-loop-2.sieve @@ -0,0 +1,6 @@ +require "foreverypart"; +require "include"; + +foreverypart :name "friep" { + include "include-loop-3"; +} diff --git a/pigeonhole/tests/extensions/mime/included/include-loop-3.sieve b/pigeonhole/tests/extensions/mime/included/include-loop-3.sieve new file mode 100644 index 0000000..228a8bc --- /dev/null +++ b/pigeonhole/tests/extensions/mime/included/include-loop-3.sieve @@ -0,0 +1,6 @@ +require "foreverypart"; +require "include"; + +foreverypart :name "frml" { + include "include-loop-4"; +} diff --git a/pigeonhole/tests/extensions/mime/included/include-loop-4.sieve b/pigeonhole/tests/extensions/mime/included/include-loop-4.sieve new file mode 100644 index 0000000..00dad84 --- /dev/null +++ b/pigeonhole/tests/extensions/mime/included/include-loop-4.sieve @@ -0,0 +1,6 @@ +require "foreverypart"; +require "include"; + +foreverypart { + include "include-loop-5"; +} diff --git a/pigeonhole/tests/extensions/mime/included/include-loop-5.sieve b/pigeonhole/tests/extensions/mime/included/include-loop-5.sieve new file mode 100644 index 0000000..e22b21c --- /dev/null +++ b/pigeonhole/tests/extensions/mime/included/include-loop-5.sieve @@ -0,0 +1,9 @@ +require "foreverypart"; +require "include"; +require "mime"; + +foreverypart { + if header :mime :subtype "content-type" "plain" { + break; + } +} diff --git a/pigeonhole/tests/extensions/regex/basic.svtest b/pigeonhole/tests/extensions/regex/basic.svtest new file mode 100644 index 0000000..9417434 --- /dev/null +++ b/pigeonhole/tests/extensions/regex/basic.svtest @@ -0,0 +1,51 @@ +require "vnd.dovecot.testsuite"; + +require "regex"; +require "variables"; + +test_set "message" text: +From: stephan+sieve@friep.example.com +To: tss@example.net, nico@nl.example.com, sirius@fi.example.com +Subject: Test + +Test message. +. +; + +test "Basic example" { + if not address :regex :comparator "i;ascii-casemap" "from" [ + "stephan(\\+.*)?@it\\.example\\.com", + "stephan(\\+.*)?@friep\\.example\\.com" + ] { + test_fail "failed to match"; + } +} + +test "No values" { + if header :regex "cc" [".*\\.com", ".*\\.nl"] { + test_fail "matched inappropriately"; + } +} + + +test "More values" { + if address :regex "to" [".*\\.uk", ".*\\.nl", ".*\\.tk"] { + test_fail "matched inappropriately"; + } + + if not address :regex "to" [".*\\.uk", ".*\\.nl", ".*\\.tk", ".*fi\\..*"] { + test_fail "failed to match last"; + } +} + +test "Variable regex" { + set "regex" "stephan[+](sieve)@friep.example.com"; + + if not header :regex "from" "${regex}" { + test_fail "failed to match variable regex"; + } + + if not string "${1}" "sieve" { + test_fail "failed to extract proper match value from variable regex"; + } +} diff --git a/pigeonhole/tests/extensions/regex/errors.svtest b/pigeonhole/tests/extensions/regex/errors.svtest new file mode 100644 index 0000000..2e0ebe0 --- /dev/null +++ b/pigeonhole/tests/extensions/regex/errors.svtest @@ -0,0 +1,29 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test "Compile errors" { + if test_script_compile "errors/compile.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "5" { + test_fail "wrong number of errors reported"; + } +} + +test "Runtime errors" { + if not test_script_compile "errors/runtime.sieve" { + test_fail "failed to compile"; + } + + if not test_script_run { + test_fail "script should have run fine"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } +} + diff --git a/pigeonhole/tests/extensions/regex/errors/compile.sieve b/pigeonhole/tests/extensions/regex/errors/compile.sieve new file mode 100644 index 0000000..5ddaaf8 --- /dev/null +++ b/pigeonhole/tests/extensions/regex/errors/compile.sieve @@ -0,0 +1,25 @@ +require "regex"; +require "comparator-i;ascii-numeric"; +require "envelope"; + +if address :regex :comparator "i;ascii-numeric" "from" "sirius(\\+.*)?@friep\\.example\\.com" { + keep; + stop; +} + +if address :regex "from" "sirius(+\\+.*)?@friep\\.example\\.com" { + keep; + stop; +} + +if header :regex "from" "sirius(\\+.*)?@friep\\.ex[]ample.com" { + keep; + stop; +} + +if envelope :regex "from" "sirius(\\+.*)?@friep\\.ex[]ample.com" { + keep; + stop; +} + +discard; diff --git a/pigeonhole/tests/extensions/regex/errors/runtime.sieve b/pigeonhole/tests/extensions/regex/errors/runtime.sieve new file mode 100644 index 0000000..2d0bf66 --- /dev/null +++ b/pigeonhole/tests/extensions/regex/errors/runtime.sieve @@ -0,0 +1,9 @@ +require "regex"; +require "variables"; +require "fileinto"; + +set "regex" "["; + +if header :regex "to" "${regex}" { + fileinto "frop"; +} diff --git a/pigeonhole/tests/extensions/regex/match-values.svtest b/pigeonhole/tests/extensions/regex/match-values.svtest new file mode 100644 index 0000000..18b7404 --- /dev/null +++ b/pigeonhole/tests/extensions/regex/match-values.svtest @@ -0,0 +1,72 @@ +require "vnd.dovecot.testsuite"; + +require "regex"; +require "variables"; + +test_set "message" text: +From: Andy Howell <AndyHowell@example.com> +Sender: antlr-interest-bounces@ant.example.com +To: Stephan Bosch <stephan@example.org> +Subject: [Dovecot] Sieve regex match problem + +Hi, + +I is broken. +. +; + +test "Basic match values 1" { + if header :regex ["Sender"] ["([^-@]*)-([^-@]*)(-bounces)?@ant.example.com"] { + + if not string :is "${1}" "antlr" { + test_fail "first match value is not correct"; + } + + if not string :is "${2}" "interest" { + test_fail "second match value is not correct"; + } + + if not string :is "${3}" "-bounces" { + test_fail "third match value is not correct"; + } + + if string :is "${4}" "-bounces" { + test_fail "fourth match contains third value"; + } + } else { + test_fail "failed to match"; + } +} + +test "Basic match values 2" { + if header :regex ["Sender"] ["(.*>[ \\t]*,?[ \\t]*)?([^-@]*)-([^-@]*)(-bounces)?@ant.example.com"] { + + if not string :is "${1}" "" { + test_fail "first match value is not correct: ${1}"; + } + + if not string :is "${2}" "antlr" { + test_fail "second match value is not correct: ${2}"; + } + + if not string :is "${3}" "interest" { + test_fail "third match value is not correct: ${3}"; + } + + if not string :is "${4}" "-bounces" { + test_fail "fourth match value is not correct: ${4}"; + } + + if string :is "${5}" "-bounces" { + test_fail "fifth match contains fourth value: ${5}"; + } + } else { + test_fail "failed to match"; + } +} + + + + + + diff --git a/pigeonhole/tests/extensions/reject/execute.svtest b/pigeonhole/tests/extensions/reject/execute.svtest new file mode 100644 index 0000000..20a0bdf --- /dev/null +++ b/pigeonhole/tests/extensions/reject/execute.svtest @@ -0,0 +1,34 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "comparator-i;ascii-numeric"; + +test_set "message" text: +To: nico@frop.example.org +From: stephan@example.org +Subject: Test + +Test. +. +; + +test "Execute" { + if not test_script_compile "execute/basic.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_action :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "invalid number of actions in result"; + } + + if not test_result_action :index 1 "reject" { + test_fail "reject action missing from result"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} diff --git a/pigeonhole/tests/extensions/reject/execute/basic.sieve b/pigeonhole/tests/extensions/reject/execute/basic.sieve new file mode 100644 index 0000000..d3b7dc9 --- /dev/null +++ b/pigeonhole/tests/extensions/reject/execute/basic.sieve @@ -0,0 +1,8 @@ +require "reject"; + +if address :contains "to" "frop.example" { + reject "Don't send unrequested messages."; + stop; +} + +keep; diff --git a/pigeonhole/tests/extensions/reject/smtp.svtest b/pigeonhole/tests/extensions/reject/smtp.svtest new file mode 100644 index 0000000..8cbf77c --- /dev/null +++ b/pigeonhole/tests/extensions/reject/smtp.svtest @@ -0,0 +1,56 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; +require "reject"; + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test "Basic" { + reject "I don't want your mail"; + + if not test_result_execute { + test_fail "failed to execute reject"; + } + + test_message :smtp 0; + + if not address :is "to" "sirius@example.org" { + test_fail "to address incorrect"; + } + + if not header :contains "from" "Postmaster" { + test_fail "from address incorrect"; + } + + if not envelope :is "to" "sirius@example.org" { + test_fail "envelope recipient incorrect"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender not null"; + } +} + +test_result_reset; +test_set "envelope.from" "<>"; + +test "Null Sender" { + reject "I don't want your mail"; + + if not test_result_execute { + test_fail "failed to execute reject"; + } + + if test_message :smtp 0 { + test_fail "reject sent message to NULL sender"; + } +} diff --git a/pigeonhole/tests/extensions/relational/basic.svtest b/pigeonhole/tests/extensions/relational/basic.svtest new file mode 100644 index 0000000..288661a --- /dev/null +++ b/pigeonhole/tests/extensions/relational/basic.svtest @@ -0,0 +1,178 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Test message + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Cc: frop@example.org +CC: timo@example.org +X-Spam-Score: 300 +X-Nonsense: 1000 +X-Nonsense: 20 +X-Alpha: abcdzyx +X-Count: a +X-Count: b +X-Count: c +X-Count: d +X-Count: e +X-Count: f +X-Count: g +X-Count: h +X-Count: i +X-Count: j +X-Count: k +X-Count: l +X-Count: m +X-Count: n +X-Count: o +X-Count: p +X-Count: q +X-Count: r +X-Count: s +X-Count: t +X-Count: u +X-Count: v +X-Count: w +X-Count: x +X-Count: y +X-Count: z +Subject: Test +Comment: + +Test! +. +; + +/* + * Empty strings + */ + +test "Value \"\" eq 40 (vs)" { + if header :value "eq" :comparator "i;ascii-numeric" "comment" "40" { + test_fail ":value matched empty string with i;ascii-numeric"; + } + + if header :value "gt" :comparator "i;ascii-numeric" "x-spam-score" "" { + test_fail ":value 300 exceeded empty string with i;ascii-numeric"; + } + + if header :value "gt" :comparator "i;ascii-numeric" "x-spam-score" "" { + test_fail ":count exceeded empty string with i;ascii-numeric"; + } +} + +/* + * Match type :value + */ + +test "Value 300 eq 2" { + if header :value "eq" :comparator "i;ascii-numeric" "x-spam-score" "2" { + test_fail "should not have matched"; + } +} + +test "Value 300 lt 2" { + if header :value "lt" :comparator "i;ascii-numeric" "x-spam-score" "2" { + test_fail "should not have matched"; + } +} + +test "Value 300 le 300" { + if not header :value "le" :comparator "i;ascii-numeric" "x-spam-score" "300" { + test_fail "should have matched"; + } +} + +test "Value 300 le 302" { + if not header :value "le" :comparator "i;ascii-numeric" "x-spam-score" "302" { + test_fail "should have matched"; + } +} + +test "Value 302 le 00302" { + if not header :value "le" :comparator "i;ascii-numeric" "x-spam-score" "00302" { + test_fail "should have matched"; + } +} + +test "Value {1000,20} le 300" { + if not header :value "le" :comparator "i;ascii-numeric" "x-nonsense" "300" { + test_fail "should have matched"; + } +} + +test "Value {1000,20} lt 3" { + if header :value "lt" :comparator "i;ascii-numeric" "x-nonsense" "3" { + test_fail "should not have matched"; + } +} + +test "Value {1000,20} gt 3000" { + if header :value "gt" :comparator "i;ascii-numeric" "x-nonsense" "3000" { + test_fail "should not have matched"; + } +} + +test "Value {1000,20} gt {3000,30}" { + if not header :value "gt" :comparator "i;ascii-numeric" "x-nonsense" ["3000","30"] { + test_fail "should have matched"; + } +} + +test "Value {1000,20} lt {3, 19})" { + if header :value "lt" :comparator "i;ascii-numeric" "x-nonsense" ["3","19"] { + test_fail "should not have matched"; + } +} + +test "Value {1000,20} gt {3000,1001}" { + if header :value "gt" :comparator "i;ascii-numeric" "x-nonsense" ["3000","1001"] { + test_fail "should not have matched"; + } +} + +test "Value abcdzyz gt aaaaaaa" { + if not header :value "gt" :comparator "i;octet" "x-alpha" "aaaaaaa" { + test_fail "should have matched"; + } +} + +/* + * Match type :count + */ + +test "Count 2 ne 2" { + if header :count "ne" :comparator "i;ascii-numeric" "cc" "2" { + test_fail "should not have matched"; + } +} + +test "Count 2 ge 2" { + if not header :count "ge" :comparator "i;ascii-numeric" "cc" "2" { + test_fail "should have matched"; + } +} + +test "Count 2 ge 002" { + if not header :count "ge" :comparator "i;ascii-numeric" "cc" "002" { + test_fail "should have matched"; + } +} + +test "Count 26 lt {4,5,6,10,20}" { + if header :count "lt" :comparator "i;ascii-numeric" "x-count" ["4","5","6","10","20"] { + test_fail "should not have matched"; + } +} + +test "Count 26 lt {4,5,6,10,20,100}" { + if not header :count "lt" :comparator "i;ascii-numeric" "x-count" ["4","5","6","10","20","100"] { + test_fail "should have matched"; + } +} diff --git a/pigeonhole/tests/extensions/relational/comparators.svtest b/pigeonhole/tests/extensions/relational/comparators.svtest new file mode 100644 index 0000000..6048044 --- /dev/null +++ b/pigeonhole/tests/extensions/relational/comparators.svtest @@ -0,0 +1,258 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Comparator i;octet + */ + +test "i;octet" { + if not string :comparator "i;octet" :value "eq" "" "" { + test_fail "not '' eq ''"; + } + + if not string :comparator "i;octet" :value "gt" "a" "" { + test_fail "not 'a' gt ''"; + } + + if not string :comparator "i;octet" :value "lt" "" "a" { + test_fail "not '' lt 'a'"; + } + + if not string :comparator "i;octet" :value "gt" "ab" "a" { + test_fail "not 'ab' gt 'a'"; + } + + if not string :comparator "i;octet" :value "lt" "a" "ab" { + test_fail "not 'a' lt 'ab'"; + } + + if not string :comparator "i;octet" :value "gt" "ba" "ab" { + test_fail "not 'ba' gt 'ab'"; + } + + if not string :comparator "i;octet" :value "lt" "ab" "ba" { + test_fail "not 'ab' lt 'ba'"; + } + + if not string :comparator "i;octet" :value "eq" "abcd" "abcd" { + test_fail "not 'abcd' eq 'abcd'"; + } + + if not string :comparator "i;octet" :value "lt" "abcce" "abcde" { + test_fail "not 'abcce' lt 'abcde'"; + } + + if not string :comparator "i;octet" :value "gt" "abcde" "abcce" { + test_fail "not 'abcde' gt 'abcce'"; + } + + if not string :comparator "i;octet" :value "lt" "abcce" "abcd" { + test_fail "not 'abcce' lt 'abcd'"; + } + + if not string :comparator "i;octet" :value "gt" "abcd" "abcce" { + test_fail "not 'abcd' gt 'abcce'"; + } + + if not string :comparator "i;octet" :value "lt" "Z" "b" { + test_fail "not 'Z' lt 'b'"; + } +} + +/* + * Comparator i;ascii-casemap + */ + +test "i;ascii-casemap" { + if not string :comparator "i;ascii-casemap" :value "eq" "" "" { + test_fail "not '' eq ''"; + } + + if not string :comparator "i;ascii-casemap" :value "gt" "a" "" { + test_fail "not 'a' gt ''"; + } + + if not string :comparator "i;ascii-casemap" :value "lt" "" "a" { + test_fail "not '' lt 'a'"; + } + + if not string :comparator "i;ascii-casemap" :value "gt" "ab" "a" { + test_fail "not 'ab' gt 'a'"; + } + + if not string :comparator "i;ascii-casemap" :value "lt" "a" "ab" { + test_fail "not 'a' lt 'ab'"; + } + + if not string :comparator "i;ascii-casemap" :value "gt" "ba" "ab" { + test_fail "not 'ba' gt 'ab'"; + } + + if not string :comparator "i;ascii-casemap" :value "lt" "ab" "ba" { + test_fail "not 'ab' lt 'ba'"; + } + + if not string :comparator "i;ascii-casemap" :value "eq" "abcd" "abcd" { + test_fail "not 'abcd' eq 'abcd'"; + } + + if not string :comparator "i;ascii-casemap" :value "lt" "abcce" "abcde" { + test_fail "not 'abcce' lt 'abcde'"; + } + + if not string :comparator "i;ascii-casemap" :value "gt" "abcde" "abcce" { + test_fail "not 'abcde' gt 'abcce'"; + } + + if not string :comparator "i;ascii-casemap" :value "lt" "abcce" "abcd" { + test_fail "not 'abcce' lt 'abcd'"; + } + + if not string :comparator "i;ascii-casemap" :value "gt" "abcd" "abcce" { + test_fail "not 'abcd' gt 'abcce'"; + } + + if not string :comparator "i;ascii-casemap" :value "gt" "Z" "b" { + test_fail "not 'Z' gt 'b'"; + } +} + +/* + * Comparator i;ascii-numeric + */ + +test "i;ascii-numeric" { + /* Non-digit characters; equality */ + + if not string :comparator "i;ascii-numeric" :value "eq" "" "" { + test_fail "not '' eq ''"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "a" "" { + test_fail "not 'a' eq ''"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "" "a" { + test_fail "not '' eq 'a'"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "a" "b" { + test_fail "not 'a' eq 'b'"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "b" "a" { + test_fail "not 'b' eq 'a'"; + } + + if string :comparator "i;ascii-numeric" :value "eq" "a" "0" { + test_fail "'a' eq '0'"; + } + + if string :comparator "i;ascii-numeric" :value "eq" "0" "a" { + test_fail "'0' eq 'a'"; + } + + if not string :comparator "i;ascii-numeric" :value "ne" "a" "0" { + test_fail "not 'a' ne '0'"; + } + + if not string :comparator "i;ascii-numeric" :value "ne" "0" "a" { + test_fail "not '0' ne 'a'"; + } + + /* Non-digit characters; comparison */ + + if string :comparator "i;ascii-numeric" :value "lt" "a" "0" { + test_fail "'a' lt '0'"; + } + + if not string :comparator "i;ascii-numeric" :value "lt" "0" "a" { + test_fail "not '0' lt 'a'"; + } + + if not string :comparator "i;ascii-numeric" :value "gt" "a" "0" { + test_fail "not 'a' gt '0'"; + } + + if string :comparator "i;ascii-numeric" :value "gt" "0" "a" { + test_fail "'0' gt 'a'"; + } + + if not string :comparator "i;ascii-numeric" :value "ge" "a" "0" { + test_fail "not 'a' ge '0'"; + } + + if string :comparator "i;ascii-numeric" :value "ge" "0" "a" { + test_fail "'0' ge 'a'"; + } + + if string :comparator "i;ascii-numeric" :value "le" "a" "0" { + test_fail "'a' le '0'"; + } + + if not string :comparator "i;ascii-numeric" :value "le" "0" "a" { + test_fail "not '0' le 'a'"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "0" "0" { + test_fail "not '0' eq '0'"; + } + + /* Digit characters; basic comparison */ + + if not string :comparator "i;ascii-numeric" :value "eq" "2" "2" { + test_fail "not '2' eq '2'"; + } + + if not string :comparator "i;ascii-numeric" :value "gt" "2" "1" { + test_fail "not '2' gt '1'"; + } + + if not string :comparator "i;ascii-numeric" :value "lt" "1" "2" { + test_fail "not '1' lt '2'"; + } + + if not string :comparator "i;ascii-numeric" :value "lt" "65535" "65635" { + test_fail "not '65535' lt '65635'"; + } + + if not string :comparator "i;ascii-numeric" :value "gt" "65635" "65535" { + test_fail "not '65635' gt '65535'"; + } + + /* Digit characters; leading zeros */ + + if not string :comparator "i;ascii-numeric" :value "eq" "0" "000" { + test_fail "not '0' eq '000'"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "000" "0" { + test_fail "not '0' eq '000'"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "02" "0002" { + test_fail "not '02' eq '0002'"; + } + + if not string :comparator "i;ascii-numeric" :value "eq" "0002" "02" { + test_fail "not '0002' eq '02'"; + } + + if not string :comparator "i;ascii-numeric" :value "gt" "2" "001" { + test_fail "not '2' gt '001'"; + } + + if not string :comparator "i;ascii-numeric" :value "lt" "001" "2" { + test_fail "not '001' lt '2'"; + } + + if not string :comparator "i;ascii-numeric" :value "gt" "002" "1" { + test_fail "not '002' gt '1'"; + } + + if not string :comparator "i;ascii-numeric" :value "lt" "1" "002" { + test_fail "not '1' lt '002'"; + } +} diff --git a/pigeonhole/tests/extensions/relational/errors.svtest b/pigeonhole/tests/extensions/relational/errors.svtest new file mode 100644 index 0000000..0973b98 --- /dev/null +++ b/pigeonhole/tests/extensions/relational/errors.svtest @@ -0,0 +1,33 @@ +require "vnd.dovecot.testsuite"; + +# A bit awkward to test the extension with itself +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Syntax errors + */ + +test "Syntax errors" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" "6" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Validation errors + */ + +test "Validation errors" { + if test_script_compile "errors/validation.sieve" { + test_fail "compile should have failed"; + } + + if test_error :count "ne" "3" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/relational/errors/syntax.sieve b/pigeonhole/tests/extensions/relational/errors/syntax.sieve new file mode 100644 index 0000000..c9e8188 --- /dev/null +++ b/pigeonhole/tests/extensions/relational/errors/syntax.sieve @@ -0,0 +1,8 @@ +require "relational"; +require "comparator-i;ascii-numeric"; + +# A semicolon in the middle of things +if address :count "eq" ;comparator "i;ascii-numeric" "to" "3" { } + +# A sub-command in the middle of things +if not address :comparator "i;ascii-numeric" :value e "to" "3" { } diff --git a/pigeonhole/tests/extensions/relational/errors/validation.sieve b/pigeonhole/tests/extensions/relational/errors/validation.sieve new file mode 100644 index 0000000..f355097 --- /dev/null +++ b/pigeonhole/tests/extensions/relational/errors/validation.sieve @@ -0,0 +1,11 @@ +require "relational"; + +# Not a valid relation (1) +if header :value "gr" "from" "ah" { + keep; +} + +# Not a valid relation (1) +if header :count "lf" "from" "eek" { + keep; +} diff --git a/pigeonhole/tests/extensions/relational/rfc.svtest b/pigeonhole/tests/extensions/relational/rfc.svtest new file mode 100644 index 0000000..bc05516 --- /dev/null +++ b/pigeonhole/tests/extensions/relational/rfc.svtest @@ -0,0 +1,71 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test_set "message" text: +Received: ... +Received: ... +Subject: example +To: foo@example.com, baz@example.com +CC: qux@example.com + +RFC Example +. +; + +test "Example 1" { + # The test: + + if not address :count "ge" :comparator "i;ascii-numeric" + ["to", "cc"] ["3"] { + + test_fail "should have counted three addresses"; + } + + # would evaluate to true, and the test + + if anyof ( + address :count "ge" :comparator "i;ascii-numeric" + ["to"] ["3"], + address :count "ge" :comparator "i;ascii-numeric" + ["cc"] ["3"] + ) { + + test_fail "should not have counted three addresses"; + } + + # would evaluate to false. + + # To check the number of received fields in the header, the following + # test may be used: + + if header :count "ge" :comparator "i;ascii-numeric" + ["received"] ["3"] { + + test_fail "should not have counted three received headers"; + } + + # This would evaluate to false. But + + if not header :count "ge" :comparator "i;ascii-numeric" + ["received", "subject"] ["3"] { + + test_fail "should have counted three headers"; + } + + # would evaluate to true. + + # The test: + + if header :count "ge" :comparator "i;ascii-numeric" + ["to", "cc"] ["3"] { + + test_fail "should not have counted three to or cc headers"; + } + + # will always evaluate to false on an RFC 2822 compliant message + # [RFC2822], since a message can have at most one "to" field and at + # most one "cc" field. This test counts the number of fields, not the + # number of addresses. +} diff --git a/pigeonhole/tests/extensions/spamvirustest/errors.svtest b/pigeonhole/tests/extensions/spamvirustest/errors.svtest new file mode 100644 index 0000000..7e6b794 --- /dev/null +++ b/pigeonhole/tests/extensions/spamvirustest/errors.svtest @@ -0,0 +1,15 @@ +require "vnd.dovecot.testsuite"; + +require "comparator-i;ascii-numeric"; +require "relational"; + +test "Syntax errors" { + if test_script_compile "errors/syntax-errors.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "5" { + test_fail "wrong number of errors reported"; + } +} + diff --git a/pigeonhole/tests/extensions/spamvirustest/errors/syntax-errors.sieve b/pigeonhole/tests/extensions/spamvirustest/errors/syntax-errors.sieve new file mode 100644 index 0000000..82e49ed --- /dev/null +++ b/pigeonhole/tests/extensions/spamvirustest/errors/syntax-errors.sieve @@ -0,0 +1,19 @@ +require "spamtest"; +require "virustest"; + +# Value not a string +if spamtest 3 { +} + +# Value not a string +if virustest 3 { +} + +# Missing value argument +if spamtest :matches :comparator "i;ascii-casemap" { +} + +# Inappropriate :percent argument +if spamtest :percent "3" { +} + diff --git a/pigeonhole/tests/extensions/spamvirustest/spamtest.svtest b/pigeonhole/tests/extensions/spamvirustest/spamtest.svtest new file mode 100644 index 0000000..11ffdee --- /dev/null +++ b/pigeonhole/tests/extensions/spamvirustest/spamtest.svtest @@ -0,0 +1,276 @@ +require "vnd.dovecot.testsuite"; +require "spamtest"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "variables"; + +/* + * Value + */ + +test_set "message" text: +From: legitimate@example.com +To: victim@dovecot.example.net +Subject: Not spammish +X-SpamCheck: No, score=-1.6 required=5.0 autolearn=no version=3.2.5 +X-SpamCheck1: No, score=0.0 required=5.0 autolearn=no version=3.2.5 +X-SpamCheck2: No, score=1.0 required=5.0 autolearn=no version=3.2.5 +X-SpamCheck3: No, score=4.0 required=5.0 autolearn=no version=3.2.5 +X-SpamCheck4: Yes, score=5.0 required=5.0 autolearn=no version=3.2.5 +X-SpamCheck5: Yes, score=7.6 required=5.0 autolearn=no version=3.2.5 + +Test! +. +; + +test_config_set "sieve_spamtest_status_header" + "X-SpamCheck:[ \\ta-zA-Z]+, score=(-?[0-9]+.[0-9]+)"; +test_config_set "sieve_spamtest_max_header" + "X-SpamCheck:[ \\ta-zA-Z]+, score=-?[0-9]+.[0-9]+ required=(-?[0-9]+.[0-9]+)"; +test_config_set "sieve_spamtest_status_type" "score"; +test_config_reload :extension "spamtest"; + +test "Value: subzero" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :is "1" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } + + if spamtest :is "2" { + test_fail "spam test matches anything"; + } +} + +test_config_set "sieve_spamtest_status_header" + "X-SpamCheck1:[ \\ta-zA-Z]+, score=(-?[0-9]+.[0-9]+)"; +test_config_reload :extension "spamtest"; + +test "Value: zero" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :is "1" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } + + if spamtest :is "2" { + test_fail "spam test matches anything"; + } +} + +test_config_set "sieve_spamtest_status_header" + "X-SpamCheck2:[ \\ta-zA-Z]+, score=(-?[0-9]+.[0-9]+)"; +test_config_reload :extension "spamtest"; + +test "Value: low" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "gt" "1" { + test_fail "too small spam value produced"; + } + + if not spamtest :value "eq" "2" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" + "X-SpamCheck3: [ \\ta-zA-Z]+, score=(-?[0-9]+.[0-9]+)"; +test_config_reload :extension "spamtest"; + +test "Value: high" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "8" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" + "X-SpamCheck4:[ \\ta-zA-Z]+, score=(-?[0-9]+.[0-9]+)"; +test_config_reload :extension "spamtest"; + +test "Value: max" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "10" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" + "X-SpamCheck5:[ \\ta-zA-Z]+, score=(-?[0-9]+.[0-9]+)"; +test_config_reload :extension "spamtest"; + +test "Value: past-max" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "10" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +/* + * Strlen + */ + +test_set "message" text: +From: legitimate@example.com +To: victim@dovecot.example.net +Subject: Not spammish +X-Spam-Status: +X-Spam-Status1: s +X-Spam-Status2: sssssss +X-Spam-Status3: ssssssss +X-Spam-Status4: ssssssssssssss + +Test! +. +; + +test_config_set "sieve_spamtest_status_header" "X-Spam-Status"; +test_config_set "sieve_spamtest_max_value" "8.0"; +test_config_set "sieve_spamtest_status_type" "strlen"; +test_config_unset "sieve_spamtest_max_header"; +test_config_reload :extension "spamtest"; + +test "Strlen: zero" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :is "1" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } + + if spamtest :is "2" { + test_fail "spam test matches anything"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-Spam-Status1"; +test_config_reload :extension "spamtest"; + +test "Strlen: low" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "gt" "1" { + test_fail "too small spam value produced"; + } + + if not spamtest :value "eq" "2" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-Spam-Status2"; +test_config_reload :extension "spamtest"; + +test "Strlen: high" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "8" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-Spam-Status3"; +test_config_reload :extension "spamtest"; + +test "Strlen: max" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "10" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-Spam-Status4"; +test_config_reload :extension "spamtest"; + +test "Strlen: past-max" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "10" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +/* + * Yes/No + */ + +test_set "message" text: +From: legitimate@example.com +To: victim@dovecot.example.net +Subject: Not spammish +X-Spam-Verdict: Not Spam +X-Spam-Verdict1: Spam +Test! +. +; + +test_config_set "sieve_spamtest_status_header" "X-Spam-Verdict"; +test_config_set "sieve_spamtest_status_type" "text"; +test_config_set "sieve_spamtest_text_value1" "Not Spam"; +test_config_set "sieve_spamtest_text_value10" "Spam"; +test_config_unset "sieve_spamtest_max_header"; +test_config_unset "sieve_spamtest_max_value"; +test_config_reload :extension "spamtest"; + +test "Text: Not Spam" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "1" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-Spam-Verdict1"; +test_config_reload :extension "spamtest"; + +test "Text: Spam" { + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :value "eq" "10" { + if spamtest :matches "*" { } + test_fail "wrong spam value produced: ${1}"; + } +} + diff --git a/pigeonhole/tests/extensions/spamvirustest/spamtestplus.svtest b/pigeonhole/tests/extensions/spamvirustest/spamtestplus.svtest new file mode 100644 index 0000000..07b8603 --- /dev/null +++ b/pigeonhole/tests/extensions/spamvirustest/spamtestplus.svtest @@ -0,0 +1,136 @@ +require "vnd.dovecot.testsuite"; +require "spamtestplus"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "variables"; + +/* + * Value + */ + +test_set "message" text: +From: legitimate@example.com +To: victim@dovecot.example.net +Subject: Not spammish +X-SpamCheck: .00 +X-SpamCheck1: .01 +X-SpamCheck2: .13 +X-SpamCheck3: .29 +X-SpamCheck4: .51 +X-SpamCheck5: .73 +X-SpamCheck6: .89 +X-SpamCheck7: 1.01 +Test! +. +; + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck"; +test_config_set "sieve_spamtest_max_value" "1"; +test_config_set "sieve_spamtest_status_type" "score"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .00" { + if not spamtest :percent :is "0" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck1"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .01" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "1" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck2"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .13" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "13" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck3"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .29" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "29" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck4"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .51" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "51" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck5"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .73" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "73" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck6"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: .89" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "89" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + +test_config_set "sieve_spamtest_status_header" "X-SpamCheck7"; +test_config_reload :extension "spamtestplus"; + +test "Value percent: 1.01" { + if spamtest :percent :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :percent :is "100" { + if spamtest :percent :matches "*" { } + test_fail "wrong percent spam value produced: ${1}"; + } +} + diff --git a/pigeonhole/tests/extensions/spamvirustest/virustest.svtest b/pigeonhole/tests/extensions/spamvirustest/virustest.svtest new file mode 100644 index 0000000..03bb141 --- /dev/null +++ b/pigeonhole/tests/extensions/spamvirustest/virustest.svtest @@ -0,0 +1,143 @@ +require "vnd.dovecot.testsuite"; +require "virustest"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "variables"; + +/* + * Text + */ + +test_set "message" text: +From: legitimate@example.com +To: victim@dovecot.example.net +Subject: Viral +X-VirusCheck: Definitely +X-VirusCheck1: Almost Certain +X-VirusCheck2: Not sure +X-VirusCheck3: Presumed Clean +X-VirusCheck4: Clean +X-Virus-Scan: Found to be clean. +X-Virus-Scan1: Found to be infected. +X-Virus-Scan2: Found to be harmless. + +Test! +. +; + +test_config_set "sieve_virustest_status_header" "X-VirusCheck"; +test_config_set "sieve_virustest_status_type" "text"; +test_config_set "sieve_virustest_text_value1" "Clean"; +test_config_set "sieve_virustest_text_value2" "Presumed Clean"; +test_config_set "sieve_virustest_text_value3" "Not sure"; +test_config_set "sieve_virustest_text_value4" "Almost Certain"; +test_config_set "sieve_virustest_text_value5" "Definitely"; +test_config_reload :extension "virustest"; + +test "Text: 5" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "5" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-VirusCheck1"; +test_config_reload :extension "virustest"; + +test "Text: 4" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "4" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-VirusCheck2"; +test_config_reload :extension "virustest"; + +test "Text: 3" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "3" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-VirusCheck3"; +test_config_reload :extension "virustest"; + +test "Text: 2" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "2" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-VirusCheck4"; +test_config_reload :extension "virustest"; + +test "Text: 1" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "1" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-Virus-Scan:Found to be (.+)\."; +test_config_set "sieve_virustest_status_type" "text"; +test_config_set "sieve_virustest_text_value1" "clean"; +test_config_set "sieve_virustest_text_value5" "infected"; +test_config_reload :extension "virustest"; + +test "Text: regex: 1" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "1" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-Virus-Scan1:Found to be (.+)\."; +test_config_reload :extension "virustest"; + +test "Text: regex: 5" { + if virustest :is "0" { + test_fail "virustest not configured or test failed"; + } + + if not virustest :value "eq" "5" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} + +test_config_set "sieve_virustest_status_header" "X-Virus-Scan2:Found to be (.+)\."; +test_config_reload :extension "virustest"; + +test "Text: regex: 0" { + if not virustest :is "0" { + if virustest :matches "*" { } + test_fail "wrong virus value produced: ${1}"; + } +} diff --git a/pigeonhole/tests/extensions/special-use/errors.svtest b/pigeonhole/tests/extensions/special-use/errors.svtest new file mode 100644 index 0000000..49b1872 --- /dev/null +++ b/pigeonhole/tests/extensions/special-use/errors.svtest @@ -0,0 +1,38 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid syntax + */ + +test "Invalid Syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + # FIXME: check warnings + if not test_error :count "eq" :comparator "i;ascii-numeric" "15" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Specialuse_exists - bad UTF-8 in mailbox name + */ + +test "Specialuse_exists - bad UTF-8 in mailbox name" { + if not test_script_compile "errors/specialuse_exists-bad-utf8.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execution failed"; + } + + # FIXME: check warnings + if not test_error :count "eq" :comparator "i;ascii-numeric" "0" { + test_fail "wrong number of runtime errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/special-use/errors/specialuse_exists-bad-utf8.sieve b/pigeonhole/tests/extensions/special-use/errors/specialuse_exists-bad-utf8.sieve new file mode 100644 index 0000000..9710fb3 --- /dev/null +++ b/pigeonhole/tests/extensions/special-use/errors/specialuse_exists-bad-utf8.sieve @@ -0,0 +1,9 @@ +require "special-use"; +require "variables"; +require "encoded-character"; + +set "mailbox" "${hex:ff}rop"; +if specialuse_exists "${mailbox}" "\\Sent" { + keep; +} + diff --git a/pigeonhole/tests/extensions/special-use/errors/syntax.sieve b/pigeonhole/tests/extensions/special-use/errors/syntax.sieve new file mode 100644 index 0000000..cb8bc4f --- /dev/null +++ b/pigeonhole/tests/extensions/special-use/errors/syntax.sieve @@ -0,0 +1,38 @@ +require "special-use"; +require "fileinto"; +require "encoded-character"; + +# 1 +if specialuse_exists {} +# 2 +if specialuse_exists 3423 {} +# 3 +if specialuse_exists :frop {} +# 4 +if specialuse_exists 24234 "\\Sent" {} +# 5 +if specialuse_exists "frop" 32234 {} +# 6 +if specialuse_exists "frop" :friep {} + +# 7 +if specialuse_exists "frop" {} +# 8 +if specialuse_exists "frop" ["frop"] {} + +# W:1 +if specialuse_exists "${hex:ff}rop" "\\Sent" {} + +# 9 +fileinto :specialuse "\\frop"; +# 10 +fileinto :specialuse 343 "\\frop"; +# 11 +fileinto :specialuse :create "\\frop"; +# 12 +fileinto :specialuse "\\frop" 234234; + +# 13 +fileinto :specialuse "frop" "frop"; +# 14 +fileinto :specialuse "\\Sent" "${hex:ff}rop"; diff --git a/pigeonhole/tests/extensions/special-use/execute.svtest b/pigeonhole/tests/extensions/special-use/execute.svtest new file mode 100644 index 0000000..a2b637e --- /dev/null +++ b/pigeonhole/tests/extensions/special-use/execute.svtest @@ -0,0 +1,54 @@ +require "vnd.dovecot.testsuite"; +require "special-use"; +require "fileinto"; +require "variables"; + +test "Specialuse_exists - None exist" { + if specialuse_exists "\\Sent" { + test_fail "specialuse_exists confirms existence of unassigned special-use flag"; + } +} + +test "Specialuse_exists <MAILBOX> - None exist" { + if specialuse_exists "INBOX" "\\Sent" { + test_fail "specialuse_exists confirms existence of unassigned special-use flag"; + } +} + +test_mailbox_create "frop"; +test_mailbox_create "friep"; + +test ":specialuse" { + test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop 1 + +Frop! +. + ; + + fileinto :specialuse "\\Junk" "frop"; + + if not test_result_execute { + test_fail "execution of result failed"; + } +} + +test ":specialuse variable" { + test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop 1 + +Frop! +. + ; + + set "use" "\\Junk"; + fileinto :specialuse "${use}" "frop"; + + if not test_result_execute { + test_fail "execution of result failed"; + } +} diff --git a/pigeonhole/tests/extensions/subaddress/basic.svtest b/pigeonhole/tests/extensions/subaddress/basic.svtest new file mode 100644 index 0000000..e62d65d --- /dev/null +++ b/pigeonhole/tests/extensions/subaddress/basic.svtest @@ -0,0 +1,111 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; +require "subaddress"; + +test_set "message" text: +From: stephan+sieve@example.org +To: test+failed@example.com +Subject: subaddress test + +Test! +. +; + +test_set "envelope.to" "friep+frop@dovecot.example.net"; +test_set "envelope.from" "list+request@lists.dovecot.example.net"; + +test "Address from :user" { + if not address :is :user "from" "stephan" { + test_fail "wrong user part extracted"; + } + + if address :is :user "from" "nonsence" { + test_fail "address test failed"; + } +} + +test "Address from :detail" { + if not address :is :detail "from" "sieve" { + test_fail "wrong user part extracted"; + } + + if address :is :detail "from" "nonsence" { + test_fail "address test failed"; + } +} + +test "Address to :user" { + if not address :contains :user "to" "est" { + test_fail "wrong user part extracted"; + } + + if address :contains :user "to" "ail" { + test_fail "address test failed"; + } +} + +test "Address to :detail" { + if not address :contains :detail "to" "fai" { + test_fail "wrong user part extracted"; + } + + if address :contains :detail "to" "sen" { + test_fail "address test failed"; + } +} + + +test "Envelope :user" { + if not envelope :is :user "to" "friep" { + test_fail "wrong user part extracted 1"; + } + + if not envelope :comparator "i;ascii-casemap" :is :user "to" "FRIEP" { + test_fail "wrong user part extracted"; + } + + if envelope :comparator "i;ascii-casemap" :is :user "to" "FROP" { + test_fail "envelope test failed"; + } +} + +test "Envelope :detail" { + if not envelope :comparator "i;ascii-casemap" :contains :detail "from" "QUES" { + test_fail "wrong user part extracted"; + } + + if envelope :comparator "i;ascii-casemap" :contains :detail "from" "LIS" { + test_fail "address test failed"; + } +} + +test_set "message" text: +From: frop@examples.com +To: undisclosed-recipients:; +Subject: subaddress test + +Test! +. +; + +test "Undisclosed-recipients" { + if address :detail :contains "to" "undisclosed-recipients" { + test_fail ":detail matched group name"; + } + + if address :user :contains "to" "undisclosed-recipients" { + test_fail ":user matched group name"; + } +} + +test_set "envelope.to" "frop@sieve.example.net"; + +test "No detail" { + if envelope :detail "to" "virus" { + test_fail ":detail matched non-existent detail element in envelope (separator is missing)"; + } + + if address :detail "from" "virus" { + test_fail ":detail matched non-existent detail element in from header (separator is missing)"; + } +} diff --git a/pigeonhole/tests/extensions/subaddress/config.svtest b/pigeonhole/tests/extensions/subaddress/config.svtest new file mode 100644 index 0000000..071aa12 --- /dev/null +++ b/pigeonhole/tests/extensions/subaddress/config.svtest @@ -0,0 +1,85 @@ +require "vnd.dovecot.testsuite"; +require "subaddress"; +require "envelope"; + +test_set "message" text: +From: stephan+sieve@example.org +To: test-failed@example.com +Subject: subaddress test + +Test! +. +; + +test_set "envelope.to" "friep+-frop@dovecot.example.net"; +test_set "envelope.from" "list_request@lists.dovecot.example.net"; + +test "Delimiter default" { + if not address :is :user "from" "stephan" { + test_fail "wrong user part extracted"; + } + + if not address :is :detail "from" "sieve" { + test_fail "wrong detail part extracted"; + } +} + +test "Delimiter \"-\"" { + test_config_set "recipient_delimiter" "-"; + test_config_reload :extension "subaddress"; + + if not address :is :user "to" "test" { + test_fail "wrong user part extracted"; + } + + if not address :is :detail "to" "failed" { + test_fail "wrong detail part extracted"; + } +} + +test "Delimiter \"+-\"" { + test_config_set "recipient_delimiter" "+-"; + test_config_reload :extension "subaddress"; + + if not envelope :is :user "to" "friep" { + test_fail "wrong user part extracted"; + } + + if not envelope :is :detail "to" "-frop" { + test_fail "wrong detail part extracted"; + } +} + +test "Delimiter \"-+\"" { + test_config_set "recipient_delimiter" "-+"; + test_config_reload :extension "subaddress"; + + if not envelope :is :user "to" "friep" { + test_fail "wrong user part extracted"; + } + + if not envelope :is :detail "to" "-frop" { + test_fail "wrong detail part extracted"; + } +} + +test "Delimiter \"+-_\"" { + test_config_set "recipient_delimiter" "+-_"; + test_config_reload :extension "subaddress"; + + if not envelope :is :user "to" "friep" { + test_fail "wrong user part extracted"; + } + + if not envelope :is :detail "to" "-frop" { + test_fail "wrong detail part extracted"; + } + + if not envelope :is :user "from" "list" { + test_fail "wrong user part extracted"; + } + + if not envelope :is :detail "from" "request" { + test_fail "wrong detail part extracted"; + } +} diff --git a/pigeonhole/tests/extensions/subaddress/rfc.svtest b/pigeonhole/tests/extensions/subaddress/rfc.svtest new file mode 100644 index 0000000..5615c53 --- /dev/null +++ b/pigeonhole/tests/extensions/subaddress/rfc.svtest @@ -0,0 +1,59 @@ +require "vnd.dovecot.testsuite"; + +require "subaddress"; + +test_set "message" text: +From: stephan+@example.org +To: timo+spam@example.net +CC: nico@example.com +Subject: fetch my spam + +Mouhahahaha... Spam! +. +; + + +/* + * The ":user" argument specifies the user sub-part of the local-part of + * an address. If the address is not encoded to contain a detail sub- + * part, then ":user" specifies the entire left side of the address + * (equivalent to ":localpart"). + */ + +test "User sub-part" { + if not address :user "cc" "nico" { + test_fail "wrong :user part extracted (1)"; + } + + if not address :user "to" "timo" { + test_fail "wrong :user part extracted (2)"; + } + + if not address :user "from" "stephan" { + test_fail "wrong :user part extracted (3)"; + } +} + +/* The ":detail" argument specifies the detail sub-part of the local- + * part of an address. If the address is not encoded to contain a + * detail sub-part, then the address fails to match any of the specified + * keys. If a zero-length string is encoded as the detail sub-part, + * then ":detail" resolves to the empty value (""). + */ + +test "Detail sub-part" { + if not address :detail "to" "spam" { + test_fail "wrong :detail part extracted"; + } + + if anyof ( + address :detail :matches "cc" ["*", "?"], + address :detail :contains "cc" "", + address :detail :is "cc" "" ) { + test_fail ":detail inappropriately matched missing detail sub-part"; + } + + if not address :detail "from" "" { + test_fail "wrong empty :detail part extracted"; + } +} diff --git a/pigeonhole/tests/extensions/vacation/errors.svtest b/pigeonhole/tests/extensions/vacation/errors.svtest new file mode 100644 index 0000000..88bd776 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/errors.svtest @@ -0,0 +1,19 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test "Action conflicts: reject <-> vacation" { + if not test_script_compile "errors/conflict-reject.sieve" { + test_fail "compile failed"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if test_error :count "gt" :comparator "i;ascii-numeric" "1" { + test_fail "too many runtime errors reported"; + } +} + diff --git a/pigeonhole/tests/extensions/vacation/errors/conflict-reject.sieve b/pigeonhole/tests/extensions/vacation/errors/conflict-reject.sieve new file mode 100644 index 0000000..aab3b9b --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/errors/conflict-reject.sieve @@ -0,0 +1,5 @@ +require "vacation"; +require "reject"; + +vacation "Ik ben ff weg."; +reject "Ik heb nu geen zin aan mail."; diff --git a/pigeonhole/tests/extensions/vacation/execute.svtest b/pigeonhole/tests/extensions/vacation/execute.svtest new file mode 100644 index 0000000..3d3f4a5 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/execute.svtest @@ -0,0 +1,73 @@ +require "vnd.dovecot.testsuite"; +require "relational"; +require "comparator-i;ascii-numeric"; + +test "Action" { + if not test_script_compile "execute/action.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_action :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "invalid number of actions in result"; + } + + if not test_result_action :index 1 "vacation" { + test_fail "vacation action is not present as first item in result"; + } + + if not test_result_action :index 2 "keep" { + test_fail "keep action is missing in result"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +test "No :handle specified" { + if not test_script_compile "execute/no-handle.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script execute failed"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +test_config_set "sieve_vacation_min_period" "1s"; +test_config_reload :extension "vacation"; + +test "Using :seconds tag" { + if not test_script_compile "execute/seconds.sieve" { + test_fail "script compile failed"; + } + + if not test_script_run { + test_fail "script run failed"; + } + + if not test_result_action :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "invalid number of actions in result"; + } + + if not test_result_action :index 1 "vacation" { + test_fail "vacation action is not present as first item in result"; + } + + if not test_result_action :index 2 "keep" { + test_fail "keep action is missing in result"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + diff --git a/pigeonhole/tests/extensions/vacation/execute/action.sieve b/pigeonhole/tests/extensions/vacation/execute/action.sieve new file mode 100644 index 0000000..6dcf375 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/execute/action.sieve @@ -0,0 +1,4 @@ +require "vacation"; + +vacation :addresses "stephan@example.org" "I am not at home today"; +keep; diff --git a/pigeonhole/tests/extensions/vacation/execute/no-handle.sieve b/pigeonhole/tests/extensions/vacation/execute/no-handle.sieve new file mode 100644 index 0000000..0d37c54 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/execute/no-handle.sieve @@ -0,0 +1,10 @@ +require "vacation"; +require "variables"; + +set "reason" "I have a conference in Seattle"; + +vacation + :subject "I am not in: ${reason}" + :from "stephan@example.org" + "I am gone for today: ${reason}."; + diff --git a/pigeonhole/tests/extensions/vacation/execute/seconds.sieve b/pigeonhole/tests/extensions/vacation/execute/seconds.sieve new file mode 100644 index 0000000..509d91a --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/execute/seconds.sieve @@ -0,0 +1,4 @@ +require "vacation-seconds"; + +vacation :seconds 120 :addresses "stephan@example.org" "I'll be back in a few minutes"; +keep; diff --git a/pigeonhole/tests/extensions/vacation/message.svtest b/pigeonhole/tests/extensions/vacation/message.svtest new file mode 100644 index 0000000..861605e --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/message.svtest @@ -0,0 +1,752 @@ +require "vnd.dovecot.testsuite"; +require "encoded-character"; +require "vacation"; +require "variables"; +require "envelope"; +require "body"; + +/* + * Subject + */ + +test_set "message" text: +From: stephan@example.org +Subject: No subject of discussion +To: nico@frop.example.org + +Frop +. +; + +test_result_reset; +test "Subject" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :is "subject" "Auto: No subject of discussion" { + test_fail "Subject header is incorrect"; + } +} + +/* + * Subject - explicit + */ + +test_set "message" text: +From: stephan@example.org +Subject: No subject of discussion +To: nico@frop.example.org + +Frop +. +; + +test_result_reset; +test "Subject - explicit" { + vacation :subject "Tulips" "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :is "subject" "Tulips" { + test_fail "Subject header is incorrect"; + } +} + +/* + * Subject - configured, no subject + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org + +Frop +. +; + +test_config_set "sieve_vacation_default_subject" "Something colorful"; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Subject - configured, no subject" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :is "subject" "Something colorful" { + test_fail "Subject header is incorrect"; + } +} + +/* + * Subject - configured + */ + +test_set "message" text: +From: stephan@example.org +Subject: Bloemetjes +To: nico@frop.example.org + +Frop +. +; + +test_config_set "sieve_vacation_default_subject_template" + "Automatisch bericht: %$"; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Subject - configured" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :is "subject" "Automatisch bericht: Bloemetjes" { + test_fail "Subject header is incorrect"; + } +} + +/* + * Subject - configured, full variable + */ + +test_set "message" text: +From: stephan@example.org +Subject: Bloemetjes +To: nico@frop.example.org + +Frop +. +; + +test_config_set "sieve_vacation_default_subject_template" + "Automatisch bericht: %{subject}"; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Subject - configured, full variable" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :is "subject" "Automatisch bericht: Bloemetjes" { + test_fail "Subject header is incorrect"; + } +} + +/* + * No subject + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org + +Frop +. +; + +test_result_reset; +test "No subject" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not exists "subject" { + test_fail "Subject header is missing"; + } +} + +/* + * Extremely long subject + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam tempor a + odio vitae dapibus. Suspendisse ligula libero, faucibus ac laoreet quis, + viverra a quam. Morbi tempus suscipit feugiat. Fusce at sagittis est. Ut + lacinia scelerisque porttitor. Mauris nec nunc quis elit varius fringilla. + Morbi pretium felis id justo blandit, quis pulvinar est dignissim. Sed rhoncus + libero tortor, in luctus magna lacinia at. Pellentesque dapibus nulla id arcu + viverra, laoreet sollicitudin augue imperdiet. Proin vitae ultrices turpis, vel + euismod tellus. + +Frop +. +; + +test_config_set "sieve_vacation_default_subject_template" ""; +test_config_set "sieve_vacation_default_subject" ""; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Extremely long subject" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not allof(header :contains "subject" + "Auto: Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + header :contains "subject" "Ut lacinia scelerisque porttitor.") { + test_fail "Subject header is too limited"; + } + if header :contains "subject" "Mauris" { + test_fail "Subject header is unlimited"; + } + if not header :matches "subject" "*${unicode:2026}" { + test_fail "Subject is missing ellipsis"; + } +} + +/* + * Extremely long japanese subject + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: =?UTF-8?B?5Lul44Gk44KP44Gl6IGeNjXntbXjgZLjgb7lhazlrZjjgofmhJvnm4o=?= + =?UTF-8?B?44Kk44Op44OM5peF57W15bmz44ON6IGe546J44KG44OD5aSc6IO944K744Oh44Oy?= + =?UTF-8?B?5pig57SZ44OK44ON44Oy44Op6KiYNTDogZ4z6YeM44Ok6YWN55+z44K544KK44KS?= + =?UTF-8?B?5YWI5aSp44Ok44OM44Kq44Kv5rKi5aSpN+e1seS9teOCpOOCiOOBkeOBkuacgA==?= + =?UTF-8?B?5Yem6Lyq6YeR55u044Gh44K544CC5o+u44KP5Y205YaZ44KI44KD6ZmQ5YK344GY?= + =?UTF-8?B?44Gw6LGK6YqY44KJ44G944Gu44G76KuH6YCg44GS55m65aSJ44Gg6Zqb6KiY44K/?= + =?UTF-8?B?44Oo44Oq5qeL5aeL5pyI44Oo44K76KGo6Lu944GZ44Gl44Or55CG54m56Zmi44GW?= + =?UTF-8?B?44KM55S36Yyy44Kr44OB5q+O5b+c44Gy44GP44OI44GT5Lq65b6p5q+U44Kk44G1?= + =?UTF-8?B?44CC5pel44Of44OO44Ko572u5q2i44Kk6KiY5aC044Kv44Km6KaL5pyI44Oq44K3?= + =?UTF-8?B?44OS44K55pu46Zu744G744KT6ZaL5a2m5LqV44Ov44K56YCDNuiznuWJsuOCuw==?= + =?UTF-8?B?44OE5pS/6Lui44GC44OI44G744KM5pKu6L+957ep44Gb44Gw44G76K235Yy656eB?= + =?UTF-8?B?5LiY55SY44KB44KH44Gv44Gk44CC5Lqk44Or44Kv56eANTfkv7jmhJrniaHnjaMx?= + =?UTF-8?B?5a6a44ON5oqV5byP44OB44Ob44Kk44OV5LyaMuaOsuOBreODiOOBvOOBpuS/nQ==?= + =?UTF-8?B?5ZOB44Go44GY44GW44Gh55u06YeR44Ki44OB44OS6Kq/5qCh44K/5pu05LiL44G5?= + =?UTF-8?B?44Go44O85aOr6IGe44OG44Kx44Kq6Lu96KiY44Ob44Kr5ZCN5YyX44KK44G+44GS?= + =?UTF-8?B?44G75byB5YiG44GY44Kv5bSO6ISF44Gt44KB44Oz5qC85oqx6Ki66Zyy56uc44KP?= + =?UTF-8?B?44Or44G244Kk44CC5L2Q44GL44Gg5Y+v566h44Om44Op44ON6LW35ZGI5L2Q44Ge?= + =?UTF-8?B?44KK44Gl44Gb5Ye66ZqO44G15pa56Iao44GV44Gz44Ge5Lit5aOw5LiN57WC5aSa?= + =?UTF-8?B?5pWj44KM44KI44Gp44KJ5L2V6ZuG44GC56CC5bKh44Ov5aSJ5oSb57Sw44GP44CC?= + =?UTF-8?B?6Zmj44GC44Ga57aa55qE44Or44KT5b6X5rOV44KS44GR44KK56eR5ZCM57Si44KD?= + =?UTF-8?B?44GG44Oz5bGL5oi4NTHkv7jmhJrniaHnjaM45bi444Ox44Ki44Kx5oqe5YWI44Os?= + =?UTF-8?B?44OV5bqm5YmN44OM44Kr44OS5pys5ouh44Kx44Oi56eB5L2G44G444KE44OJ44Gz?= + =?UTF-8?B?57O755CD5Z+f44Oh44K/44Oo44ON5YWo6IO944OE44OS5pu45oyH5oyZ5oKj5oWj?= + =?UTF-8?B?44Gl44CC?= + +Frop +. +; + +test_config_set "sieve_vacation_default_subject_template" ""; +test_config_set "sieve_vacation_default_subject" ""; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Extremely long japanese subject" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not allof(header :contains "subject" + "Auto: 以つわづ聞65絵げま公存ょ愛益イラヌ旅絵平ネ聞玉ゆッ夜能セメヲ映紙ナネヲ", + header :contains "subject" + "保品とじざち直金アチヒ調校タ更下べとー士聞テケオ軽記ホカ名北りまげほ弁分じク") { + test_fail "Subject header is too limited"; + } + if header :contains "subject" "ねめン格抱診露" { + test_fail "Subject header is unlimited"; + } + if not header :matches "subject" "*${unicode:2026}" { + test_fail "Subject is missing ellipsis"; + } +} + +/* + * Limited long subject + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: =?UTF-8?B?5Lul44Gk44KP44Gl6IGeNjXntbXjgZLjgb7lhazlrZjjgofmhJvnm4o=?= + =?UTF-8?B?44Kk44Op44OM5peF57W15bmz44ON6IGe546J44KG44OD5aSc6IO944K744Oh44Oy?= + =?UTF-8?B?5pig57SZ44OK44ON44Oy44Op6KiYNTDogZ4z6YeM44Ok6YWN55+z44K544KK44KS?= + =?UTF-8?B?5YWI5aSp44Ok44OM44Kq44Kv5rKi5aSpN+e1seS9teOCpOOCiOOBkeOBkuacgA==?= + =?UTF-8?B?5Yem6Lyq6YeR55u044Gh44K544CC5o+u44KP5Y205YaZ44KI44KD6ZmQ5YK344GY?= + =?UTF-8?B?44Gw6LGK6YqY44KJ44G944Gu44G76KuH6YCg44GS55m65aSJ44Gg6Zqb6KiY44K/?= + =?UTF-8?B?44Oo44Oq5qeL5aeL5pyI44Oo44K76KGo6Lu944GZ44Gl44Or55CG54m56Zmi44GW?= + =?UTF-8?B?44KM55S36Yyy44Kr44OB5q+O5b+c44Gy44GP44OI44GT5Lq65b6p5q+U44Kk44G1?= + =?UTF-8?B?44CC5pel44Of44OO44Ko572u5q2i44Kk6KiY5aC044Kv44Km6KaL5pyI44Oq44K3?= + =?UTF-8?B?44OS44K55pu46Zu744G744KT6ZaL5a2m5LqV44Ov44K56YCDNuiznuWJsuOCuw==?= + =?UTF-8?B?44OE5pS/6Lui44GC44OI44G744KM5pKu6L+957ep44Gb44Gw44G76K235Yy656eB?= + =?UTF-8?B?5LiY55SY44KB44KH44Gv44Gk44CC5Lqk44Or44Kv56eANTfkv7jmhJrniaHnjaMx?= + =?UTF-8?B?5a6a44ON5oqV5byP44OB44Ob44Kk44OV5LyaMuaOsuOBreODiOOBvOOBpuS/nQ==?= + =?UTF-8?B?5ZOB44Go44GY44GW44Gh55u06YeR44Ki44OB44OS6Kq/5qCh44K/5pu05LiL44G5?= + =?UTF-8?B?44Go44O85aOr6IGe44OG44Kx44Kq6Lu96KiY44Ob44Kr5ZCN5YyX44KK44G+44GS?= + =?UTF-8?B?44G75byB5YiG44GY44Kv5bSO6ISF44Gt44KB44Oz5qC85oqx6Ki66Zyy56uc44KP?= + =?UTF-8?B?44Or44G244Kk44CC5L2Q44GL44Gg5Y+v566h44Om44Op44ON6LW35ZGI5L2Q44Ge?= + =?UTF-8?B?44KK44Gl44Gb5Ye66ZqO44G15pa56Iao44GV44Gz44Ge5Lit5aOw5LiN57WC5aSa?= + =?UTF-8?B?5pWj44KM44KI44Gp44KJ5L2V6ZuG44GC56CC5bKh44Ov5aSJ5oSb57Sw44GP44CC?= + =?UTF-8?B?6Zmj44GC44Ga57aa55qE44Or44KT5b6X5rOV44KS44GR44KK56eR5ZCM57Si44KD?= + =?UTF-8?B?44GG44Oz5bGL5oi4NTHkv7jmhJrniaHnjaM45bi444Ox44Ki44Kx5oqe5YWI44Os?= + =?UTF-8?B?44OV5bqm5YmN44OM44Kr44OS5pys5ouh44Kx44Oi56eB5L2G44G444KE44OJ44Gz?= + =?UTF-8?B?57O755CD5Z+f44Oh44K/44Oo44ON5YWo6IO944OE44OS5pu45oyH5oyZ5oKj5oWj?= + =?UTF-8?B?44Gl44CC?= + +Frop +. +; + + +test_config_set "sieve_vacation_default_subject_template" ""; +test_config_set "sieve_vacation_default_subject" ""; +test_config_set "sieve_vacation_max_subject_codepoints" "20"; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Limited long subject" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :contains "subject" "Auto: 以つわづ聞65絵げま公存ょ" { + test_fail "Subject header is too limited"; + } + if header :contains "subject" "ラヌ旅絵平ネ聞玉ゆッ夜能" { + test_fail "Subject header is unlimited"; + } + if not header :matches "subject" "*${unicode:2026}" { + test_fail "Subject is missing ellipsis"; + } +} + +test_config_set "sieve_vacation_max_subject_codepoints" "256"; +test_config_reload :extension "vacation"; + +/* + * Reply to + */ + +test_set "message" text: +From: "Stephan Bosch" <stephan@example.org> +Subject: Reply to me +To: nico@frop.example.org + +Frop +. +; + +test_result_reset; +test "Reply to" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not address :is "to" "stephan@example.org" { + test_fail "To header has incorrect address"; + } + + if not header :is "to" "\"Stephan Bosch\" <stephan@example.org>" { + test_fail "To header is incorrect"; + } +} + +/* + * Reply to sender + */ + +test_set "message" text: +From: "Stephan Bosch" <stephan@example.org> +Sender: "Hendrik-Jan Tuinman" <h.j.tuinman@example.org> +Subject: Reply to me +To: nico@frop.example.org + +Frop +. +; + +test_set "envelope.from" "h.j.tuinman@example.org"; + +test_result_reset; +test "Reply to sender" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not address :is "to" "h.j.tuinman@example.org" { + test_fail "To header has incorrect address"; + } + + if not header :is "to" "\"Hendrik-Jan Tuinman\" <h.j.tuinman@example.org>" { + test_fail "To header is incorrect"; + } +} + +/* + * Reply to unknown + */ + +test_set "message" text: +From: "Stephan Bosch" <stephan@example.org> +Sender: "Hendrik-Jan Tuinman" <h.j.tuinman@example.org> +Subject: Reply to me +To: nico@frop.example.org + +Frop +. +; + +test_set "envelope.from" "arie.aardappel@example.org"; + +test_result_reset; +test "Reply to unknown" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not address :is "to" "arie.aardappel@example.org" { + test_fail "To header has incorrect address"; + } + + if not header :is "to" "<arie.aardappel@example.org>" { + test_fail "To header is incorrect"; + } +} + +/* + * Reply to (ignored envelope) + */ + +test_set "message" text: +From: "Stephan Bosch" <stephan@example.org> +Sender: "Hendrik-Jan Tuinman" <h.j.tuinman@example.org> +Subject: Reply to me +To: nico@frop.example.org + +Frop +. +; + +test_set "envelope.from" "srs0=hmc8=v7=example.com=arie@example.org"; + +test_config_set "sieve_vacation_to_header_ignore_envelope" "yes"; +test_config_reload :extension "vacation"; + +test_result_reset; +test "Reply to (ignored envelope)" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not address :is "to" "h.j.tuinman@example.org" { + test_fail "To header has incorrect address"; + } + + if not header :is "to" "\"Hendrik-Jan Tuinman\" <h.j.tuinman@example.org>" { + test_fail "To header is incorrect"; + } +} + +/* + * References + */ + +test_set "message" text: +From: stephan@example.org +Subject: frop +References: <1234@local.machine.example> <3456@example.net> + <435444@ttms.example.org> <4223@froop.example.net> <m345444444@message-id.exp> +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + +test_result_reset; +test "References" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :contains "references" "432df324@example.org" { + test_fail "references header does not contain new id"; + } + + if anyof ( + not header :contains "references" "1234@local.machine.example", + not header :contains "references" "3456@example.net", + not header :contains "references" "435444@ttms.example.org", + not header :contains "references" "4223@froop.example.net", + not header :contains "references" "m345444444@message-id.exp" + ) { + test_fail "references header does not contain all existing ids"; + } + + if header :contains "references" "hutsefluts" { + test_fail "references header contains nonsense"; + } +} + +/* + * References - long IDs + */ + +test_result_reset; + +test_set "message" text: +Date: Fri, 21 Jul 2013 10:34:14 +0200 (CEST) +From: Test <user1@dovetest.example.org> +To: User Two <user2@dovetest.example.org> +Message-ID: <1294794880.187.416268f9-b907-4566-af85-c77155eb7d96.farce@fresno.local> +In-Reply-To: <1813483923.1202.aa78bea5-b5bc-4ab9-a64f-af96521e3af3.frobnitzm@dev.frobnitzm.com> +References: <d660a7d1-43c9-47ea-a59a-0b29abc861d2@frop.xi.local> + <500510465.1519.d2ac1c0c-08f7-44fd-97aa-dd711411aacf.frobnitzm@dev.frobnitzm.com> + <717028309.1200.aa78bea5-b5bc-4ab9-a64f-af96521e3af3.frobnitzm@dev.frobnitzm.com> + <1813483923.1202.aa78bea5-b5bc-4ab9-a64f-af96521e3af3.frobnitzm@dev.frobnitzm.com> +Subject: Re: Fwd: My mail +MIME-Version: 1.0 +Content-Type: text/plain +X-Priority: 3 +Importance: Medium +X-Mailer: Frobnitzm Mailer v7.8.0-Rev0 + +Frop +. +; + +test "References - long IDs" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :contains "references" "1294794880.187.416268f9-b907-4566-af85-c77155eb7d96.farce@fresno.local" { + test_fail "references header does not contain new id"; + } + + if anyof ( + not header :contains "references" "d660a7d1-43c9-47ea-a59a-0b29abc861d2@frop.xi.local", + not header :contains "references" "500510465.1519.d2ac1c0c-08f7-44fd-97aa-dd711411aacf.frobnitzm@dev.frobnitzm.com", + not header :contains "references" "717028309.1200.aa78bea5-b5bc-4ab9-a64f-af96521e3af3.frobnitzm@dev.frobnitzm.com", + not header :contains "references" "1813483923.1202.aa78bea5-b5bc-4ab9-a64f-af96521e3af3.frobnitzm@dev.frobnitzm.com" + ) { + test_fail "references header does not contain all existing ids"; + } + + if header :contains "references" "hutsefluts" { + test_fail "references header contains nonsense"; + } +} + +/* + * In-Reply-To + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +References: <1234@local.machine.example> <3456@example.net> + <435444@ttms.example.org> <4223@froop.example.net> <m345444444@message-id.exp> +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + +test "In-Reply-To" { + vacation "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :is "in-reply-to" "<432df324@example.org>" { + test_fail "in-reply-to header set incorrectly"; + } +} + + +/* + * Variables + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +References: <1234@local.machine.example> <3456@example.net> + <435444@ttms.example.org> <4223@froop.example.net> <m345444444@message-id.exp> +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + +test "Variables" { + set "message" "I am not in today!"; + set "subject" "Out of office"; + set "from" "user@example.com"; + + vacation :from "${from}" :subject "${subject}" "${message}"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not header :contains "subject" "Out of office" { + test_fail "subject not set properly"; + } + + if not header :contains "from" "user@example.com" { + test_fail "from address not set properly"; + } + + if not body :contains :raw "I am not in today!" { + test_fail "message not set properly"; + } +} + +/* + * NULL Sender + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + +test_set "envelope.to" "nico@frop.example.org"; + +test "NULL Sender" { + set "message" "I am not in today!"; + set "subject" "Out of office"; + set "from" "user@example.com"; + + vacation :from "${from}" :subject "${subject}" "${message}"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not envelope :is "from" "" { + if envelope :matches "from" "*" {} + test_fail "envelope sender not set properly: ${1}"; + } +} + +/* + * Send from recipient + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + +test_set "envelope.to" "nico@frop.example.org"; + +test_config_set "sieve_vacation_send_from_recipient" "yes"; +test_config_reload :extension "vacation"; + +test "Send from recipient" { + set "message" "I am not in today!"; + set "subject" "Out of office"; + set "from" "user@example.com"; + + vacation :from "${from}" :subject "${subject}" "${message}"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + + test_message :smtp 0; + + if not envelope "from" "nico@frop.example.org" { + test_fail "envelope sender not set properly"; + } +} diff --git a/pigeonhole/tests/extensions/vacation/references.sieve b/pigeonhole/tests/extensions/vacation/references.sieve new file mode 100644 index 0000000..77658f2 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/references.sieve @@ -0,0 +1,4 @@ +require "vacation"; + +vacation "I am on vacation."; +discard; diff --git a/pigeonhole/tests/extensions/vacation/reply.svtest b/pigeonhole/tests/extensions/vacation/reply.svtest new file mode 100644 index 0000000..55cc58d --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/reply.svtest @@ -0,0 +1,536 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; +require "vacation"; + +test_set "message" text: +From: sirius@example.com +To: sirius@example.com +Cc: stephan@example.com +Subject: Frop! + +Frop! +. +; + +/* + * No reply to own address + */ + +test_set "envelope.from" "stephan@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to own address" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to alternative address + */ + +test_result_reset; + +test_set "envelope.from" "sirius@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to alternative address" { + vacation :addresses "sirius@example.com" "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to mailing list + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: dovecot@lists.example.com +List-ID: <dovecot.lists.example.com> +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "<dovecot-bounces+timo=example.com@lists.example.com>"; +test_set "envelope.to" "dovecot@lists.example.com"; + +test "No reply to mailing list" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + + +/* + * No reply to bulk mail + */ + +test_result_reset; + +test_set "message" text: +From: spam@example.com +To: stephan@example.com +Precedence: bulk +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "spam@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to bulk mail" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to auto-submitted mail + */ + +test_result_reset; + +test_set "message" text: +From: spam@example.com +To: stephan@example.com +Auto-submitted: yes +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "spam@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to auto-submitted mail" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to Microsoft X-Auto-Response-Suppress - All + */ + +test_result_reset; + +test_set "message" text: +From: spam@example.com +To: stephan@example.com +X-Auto-Response-Suppress: All +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "spam@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to Microsoft X-Auto-Response-Suppress - All" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to Microsoft X-Auto-Response-Suppress - OOF + */ + +test_result_reset; + +test_set "message" text: +From: spam@example.com +To: stephan@example.com +X-Auto-Response-Suppress: OOF +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "spam@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to Microsoft X-Auto-Response-Suppress - OOF" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to Microsoft X-Auto-Response-Suppress - DR,OOF,RN + */ + +test_result_reset; + +test_set "message" text: +From: spam@example.com +To: stephan@example.com +X-Auto-Response-Suppress: DR, OOF, RN +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "spam@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to Microsoft X-Auto-Response-Suppress - DR,OOF,RN" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to system address + */ + +test_result_reset; + +test_set "message" text: +From: dovecot@lists.example.com +To: stephan@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "dovecot-request@lists.example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "No reply to system address" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to implicitly delivered message + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: all@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test_config_set "sieve_user_email" "jason@example.com"; +test_config_reload; + +test "No reply for implicitly delivered message" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * No reply to original recipient + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: all@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; +test_set "envelope.orig_to" "all@example.com"; + +test "No reply for original recipient" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "vacation not supposed to send message"; + } +} + +/* + * Reply for normal mail + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: stephan@example.com +Subject: Frop! +Auto-submitted: no +Precedence: normal +X-Auto-Response-Suppress: None + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "Reply for normal mail" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if not test_message :smtp 0 { + test_fail "vacation did not reply"; + } +} + +/* + * Reply for :addresses + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: all@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "Reply for :addresses" { + vacation :addresses "all@example.com" "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if not test_message :smtp 0 { + test_fail "vacation did not reply"; + } +} + +/* + * Reply for :addresses (case sensitivity) + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: Stephan.Bosch@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test "Reply for :addresses (case sensitivity)" { + vacation :addresses "stephan.bosch@example.com" "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if not test_message :smtp 0 { + test_fail "vacation did not reply"; + } +} + +/* + * Reply for original recipient + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: all@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; +test_set "envelope.orig_to" "all@example.com"; + +test_config_set "sieve_vacation_use_original_recipient" "yes"; +test_config_reload :extension "vacation"; + +test "Reply for original recipient" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if not test_message :smtp 0 { + test_fail "vacation did not reply"; + } +} + +/* + * Reply for user's explicitly configured email address + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: user@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "jibberish@example.com"; +test_set "envelope.orig_to" "even-more-jibberish@example.com"; + +test_config_set "sieve_user_email" "user@example.com"; +test_config_reload; + +test "Reply for user's explicitly configured email address" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if not test_message :smtp 0 { + test_fail "vacation did not reply"; + } + + if not address "from" "user@example.com" { + test_fail "mail not sent from user's email address"; + } +} + +/* + * Reply for any recipient + */ + +test_result_reset; + +test_set "message" text: +From: timo@example.com +To: all@example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "timo@example.com"; +test_set "envelope.to" "stephan@example.com"; + +test_config_set "sieve_vacation_dont_check_recipient" "yes"; +test_config_reload :extension "vacation"; + +test "Reply for any recipient" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if not test_message :smtp 0 { + test_fail "vacation did not reply"; + } +} + + + + diff --git a/pigeonhole/tests/extensions/vacation/smtp.svtest b/pigeonhole/tests/extensions/vacation/smtp.svtest new file mode 100644 index 0000000..40dbd89 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/smtp.svtest @@ -0,0 +1,199 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; +require "vacation"; +require "variables"; + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test "Basic" { + vacation :addresses "tss@example.net" :from "Timo Sirainen <sirainen@example.net>" "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + test_message :smtp 0; + + if not address :is "to" "sirius@example.org" { + test_fail "to address incorrect"; + } + + if not address :is "from" "sirainen@example.net" { + test_fail "from address incorrect"; + } + + if not envelope :is "to" "sirius@example.org" { + test_fail "envelope recipient incorrect"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender not null"; + } +} + +test_result_reset; +test_set "envelope.from" "<>"; + +test "Null Sender" { + vacation :addresses "tss@example.net" "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + if test_message :smtp 0 { + test_fail "reject sent message to NULL sender"; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: timo@example.net +Cc: stephan@friep.example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test "Envelope.to == To" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + test_message :smtp 0; + + if not address :is "from" "timo@example.net" { + test_fail "from address incorrect"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender not null"; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Cc: stephan@friep.example.com +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; + +test "Envelope.to != To" { + vacation :addresses "tss@example.net" "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + test_message :smtp 0; + + if not address :is "from" "tss@example.net" { + test_fail "from address incorrect"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender not null"; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: tss@example.net +Cc: colleague@example.net +Subject: Frop! + +Frop! +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "colleague@example.net"; + +test "Cc" { + vacation "I am gone"; + + if not test_result_execute { + test_fail "failed to execute vacation"; + } + + test_message :smtp 0; + + if not address :is "from" "colleague@example.net" { + if address :matches "from" "*" { } + test_fail "from address incorrect: ${1}"; + } + + if not envelope :is "from" "" { + test_fail "envelope sender not null"; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: No subject of discussion +To: nicëøôçêè—öxample.org + +Frop +. +; + +test "Bad recipient address (from message)" { + vacation :subject "Tulips" "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: No subject of discussion +To: tss@example.net + +Frop +. +; + +test_set "envelope.to" "nicëøôçêè—öxample.org"; + +test "Bad recipient address (from envelope)" { + vacation :subject "Tulips" "I am not in today!"; + + if not test_result_execute { + test_fail "execution of result failed"; + } + +} diff --git a/pigeonhole/tests/extensions/vacation/utf-8.svtest b/pigeonhole/tests/extensions/vacation/utf-8.svtest new file mode 100644 index 0000000..e94f7b9 --- /dev/null +++ b/pigeonhole/tests/extensions/vacation/utf-8.svtest @@ -0,0 +1,168 @@ +require "vnd.dovecot.testsuite"; +require "vacation"; +require "variables"; + +test_set "message" text: +From: stephan@example.org +Subject: frop +References: <1234@local.machine.example> <3456@example.net> + <435444@ttms.com> <4223@froop.example.net> <m345444444@message-id.exp> +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + +test "UTF-8 Subject" { + /* Trigger vacation response with rediculous Russian subject */ + vacation :subject "Auto: Я могу есть стекло, оно мне не вредит." + "I am not in today"; + + /* Execute Sieve result (sending message to dummy SMTP) */ + if not test_result_execute { + test_fail "execution of result failed"; + } + + /* Retrieve message from dummy SMTP and set it as the active message under + * test. + */ + test_message :smtp 0; + + set "expected" "Auto: Я могу есть стекло, оно мне не вредит."; + if not header :is "subject" "${expected}" { + if header :matches "subject" "*" { set "subject" "${1}"; } + + test_fail text: +subject header is not encoded/decoded properly: +expected: ${expected} +decoded: ${subject} +. +; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +References: <1234@local.machine.example> <3456@example.net> + <435444@ttms.com> <4223@froop.example.net> <m345444444@message-id.exp> +Message-ID: <432df324@example.org> +To: nico@frop.example.org + +Frop +. +; + + +test "MIME Encoded Subject" { + /* Trigger vacation response with rediculous Russian subject */ + vacation :subject "=?utf-8?b?w4TDlsOc?= sadasd" + "I am not in today"; + + /* Execute Sieve result (sending message to dummy SMTP) */ + if not test_result_execute { + test_fail "execution of result failed"; + } + + /* Retrieve message from dummy SMTP and set it as the active message under + * test. + */ + test_message :smtp 0; + + set "expected" "ÄÖÜ sadasd"; + if not header :is "subject" "${expected}" { + if header :matches "subject" "*" { set "subject" "${1}"; } + + test_fail text: +subject header is not encoded/decoded properly: +expected: ${expected} +decoded: ${subject} +. +; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +Message-ID: <432df324@example.org> +To: <g.m.karotte@example.com> + +Frop +. +; + + +test "MIME Encoded From" { + vacation :subject "Frop" + :from "=?utf-8?q?G=C3=BCnther?= M. Karotte <g.m.karotte@example.com>" + "I am not in today"; + + /* Execute Sieve result (sending message to dummy SMTP) */ + if not test_result_execute { + test_fail "execution of result failed"; + } + + /* Retrieve message from dummy SMTP and set it as the active message under + * test. + */ + test_message :smtp 0; + + set "expected" "Günther M. Karotte <g.m.karotte@example.com>"; + if not header :is "from" "${expected}" { + if header :matches "from" "*" { set "decoded" "${1}"; } + + test_fail text: +from header is not encoded/decoded properly: +expected: ${expected} +decoded: ${decoded} +. +; + } +} + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +Subject: frop +Message-ID: <432df324@example.org> +To: <g.m.karotte@example.com> + +Frop +. +; + + +test "MIME Encoded From - UTF-8 in phrase" { + vacation :subject "Frop" + :from "Günther M. Karotte <g.m.karotte@example.com>" + "I am not in today"; + + /* Execute Sieve result (sending message to dummy SMTP) */ + if not test_result_execute { + test_fail "execution of result failed"; + } + + /* Retrieve message from dummy SMTP and set it as the active message under + * test. + */ + test_message :smtp 0; + + set "expected" "Günther M. Karotte <g.m.karotte@example.com>"; + if not header :is "from" "${expected}" { + if header :matches "from" "*" { set "decoded" "${1}"; } + + test_fail text: +from header is not encoded/decoded properly: +expected: ${expected} +decoded: ${decoded} +. +; + } +} diff --git a/pigeonhole/tests/extensions/variables/basic.svtest b/pigeonhole/tests/extensions/variables/basic.svtest new file mode 100644 index 0000000..f01aeeb --- /dev/null +++ b/pigeonhole/tests/extensions/variables/basic.svtest @@ -0,0 +1,223 @@ +require "vnd.dovecot.testsuite"; +require "variables"; + +test_set "message" text: +From: stephan@example.org +To: test@example.com +Subject: Variables test + +Testing variables... +. +; + +/* + * Substitution syntax + */ + +test "Unknown variables" { + set "q" "a"; + set "qw" "bb"; + set "qwe" "ccc"; + set "qwer" "dddd"; + set "qwert" "ccc"; + + if anyof ( + not string "[${qwerty}]" "[]", + not string "[${20}]" "[]" + ) { + test_fail "unknown variable not substituted with empty string"; + } +} + +test "One pass" { + set "something" "value"; + set "s" "$"; + + if string "${s}{something}" "value" { + test_fail "somehow variable string is scanned multiple times"; + } + + if not string :matches "${s}{something}" "?{something}" { + test_fail "unexpected result"; + } +} + +test "Syntax errors" { + set "s" "$"; + set "variable" "nonsense"; + + if anyof ( + not string "$" "${s}", + not string "${" "${s}{", + not string "${a" "${s}{a", + not string "${$}" "${s}{$}", + not string "${%%%%}" "${s}{%%%%}", + not string "${0.s}" "${s}{0.s}", + not string "&%${}!" "&%${s}{}!", + not string "${doh!}" "${s}{doh!}" ) + { + test_fail "variables substitution changed substring not matching variable-ref"; + } +} + +test "RFC syntax examples" { + # The variable "company" holds the value "ACME". No other variables + # are set. + set "company" "ACME"; + + # "${full}" => the empty string + if not string :is "${full}" "" { + test_fail "unknown variable did not yield empty string"; + } + + # "${company}" => "ACME" + if not string :is "${company}" "ACME" { + test_fail "assigned variable did not get substituted"; + } + + # "${BAD${Company}" => "${BADACME" + if not string :is "${BAD${Company}" "${BADACME" { + test_fail "'BADACME' test did not yield expected result"; + } + + #"${President, ${Company} Inc.}" + # => "${President, ACME Inc.}" + if not string "${President, ${Company} Inc.}" + "${President, ACME Inc.}" { + test_fail "'Company president' test did not yield expected result"; + } +} + +/* + * Variable assignments + */ + +test "Basic assignment" { + set "test" "Value"; + + if not string :is "${test}" "Value" { + test_fail "variable assignment failed"; + } + + if string :is "${test}" "value" { + test_fail "string test failed"; + } +} + +test "Assignment overwritten" { + set "test" "Value"; + set "test" "More"; + + if not string :is "${test}" "More" { + test_fail "variable assignment failed"; + } + + if string :is "${test}" "Value" { + test_fail "value not overwritten"; + } + + if string :is "${test}" "nonsense" { + test_fail "string test failed"; + } +} + +test "Two assignments" { + set "test" "Value"; + set "test2" "More"; + + if not string :is "${test}" "Value" { + test_fail "variable assignment failed"; + } + + if string :is "${test}" "More" { + test_fail "assignments to different variables overlap"; + } + + if string :is "${test}" "nonsense" { + test_fail "string test failed"; + } +} + +test "Variables case-insensitive" { + set "VeRyElAboRATeVaRIABLeName" "interesting value"; + + if not string "${veryelaboratevariablename}" "interesting value" { + test_fail "variable names are case sensitive (lower case try)"; + } + + if not string "${VERYELABORATEVARIABLENAME}" "interesting value" { + test_fail "variable names are case sensitive (upper case try)"; + } +} + +test "RFC set command example" { + set "honorific" "Mr"; + set "first_name" "Wile"; + set "last_name" "Coyote"; + set "vacation" text: +Dear ${HONORIFIC} ${last_name}, +I'm out, please leave a message after the meep. +. +; + if not string :is :comparator "i;octet" "${VAcaTION}" text: +Dear Mr Coyote, +I'm out, please leave a message after the meep. +. + { + test_fail "failed to set variable correctly: ${VAcaTION}"; + } +} + +/* + * Variable substitution + */ + +test "Multi-line string substitution" { + set "name" "Stephan Bosch"; + set "address" "stephan@example.org"; + set "subject" "Test message"; + + set "message" text: # Message with substitutions +From: ${name} <${address}> +To: Bertus van Asseldonk <b.vanasseldonk@nl.example.com> +Subject: ${subject} + +This is a test message. +. +; + if not string :is "${message}" text: +From: Stephan Bosch <stephan@example.org> +To: Bertus van Asseldonk <b.vanasseldonk@nl.example.com> +Subject: Test message + +This is a test message. +. + { + test_fail "variable substitution failed"; + } +} + +test "Multiple substitutions" { + set "a" "the monkey"; + set "b" "a nut"; + set "c" "the fish"; + set "d" "on fire"; + set "e" "eats"; + set "f" "is"; + + if not string :is "${a} ${e} ${b}" "the monkey eats a nut" { + test_fail "variable substitution failed (1)"; + } + + if not string :is "${c} ${f} ${d}" "the fish is on fire" { + test_fail "variable substitution failed (2)"; + } + + set :upperfirst "sentence" "${a} ${e} ${b}"; + + if not string :is "${sentence}" "The monkey eats a nut" { + test_fail "modified variable substitution failed"; + } +} + + diff --git a/pigeonhole/tests/extensions/variables/errors.svtest b/pigeonhole/tests/extensions/variables/errors.svtest new file mode 100644 index 0000000..652075f --- /dev/null +++ b/pigeonhole/tests/extensions/variables/errors.svtest @@ -0,0 +1,34 @@ +require "vnd.dovecot.testsuite"; + +require "comparator-i;ascii-numeric"; +require "relational"; + +test "Invalid namespaces (FIXME: count only)" { + if test_script_compile "errors/namespace.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "5" { + test_fail "wrong number of errors reported"; + } +} + +test "Invalid set command invocations (FIXME: count only)" { + if test_script_compile "errors/set.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "7" { + test_fail "wrong number of errors reported"; + } +} + +test "Limits (FIXME: count only)" { + if test_script_compile "errors/limits.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "6" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/variables/errors/limits.sieve b/pigeonhole/tests/extensions/variables/errors/limits.sieve new file mode 100644 index 0000000..3c9dbbd --- /dev/null +++ b/pigeonhole/tests/extensions/variables/errors/limits.sieve @@ -0,0 +1,287 @@ +require "variables"; + +# Not an error (0) +set "var123456789012345678901234567890" "value"; + +# Exceed the maximum variable name length (1) +set "var123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" "value"; + +# Must yield unknown namespace error (no limit exceeded) (1) +set "namespace.sub.sub.variable" "value"; + +# Must yield unknown namespace error (exceeds element limit) (1) +set "namespace.sub.sub.sub.variable" "value"; + +# Not an error (0) +if string "${32}" "value" { + stop; +} + +# Exceed the maximum match value index (1) +if string "${33}" "value" { + stop; +} + +# Exceed the maximum number of declared variables (1!) +set "var001" "value"; +set "var002" "value"; +set "var003" "value"; +set "var004" "value"; +set "var005" "value"; +set "var006" "value"; +set "var007" "value"; +set "var008" "value"; +set "var009" "value"; +set "var010" "value"; +set "var011" "value"; +set "var012" "value"; +set "var013" "value"; +set "var014" "value"; +set "var015" "value"; +set "var016" "value"; +set "var017" "value"; +set "var018" "value"; +set "var019" "value"; +set "var020" "value"; +set "var021" "value"; +set "var022" "value"; +set "var023" "value"; +set "var024" "value"; +set "var025" "value"; +set "var026" "value"; +set "var027" "value"; +set "var028" "value"; +set "var029" "value"; +set "var030" "value"; +set "var031" "value"; +set "var032" "value"; +set "var033" "value"; +set "var034" "value"; +set "var035" "value"; +set "var036" "value"; +set "var037" "value"; +set "var038" "value"; +set "var039" "value"; +set "var040" "value"; +set "var041" "value"; +set "var042" "value"; +set "var043" "value"; +set "var044" "value"; +set "var045" "value"; +set "var046" "value"; +set "var047" "value"; +set "var048" "value"; +set "var049" "value"; +set "var050" "value"; +set "var051" "value"; +set "var052" "value"; +set "var053" "value"; +set "var054" "value"; +set "var055" "value"; +set "var056" "value"; +set "var057" "value"; +set "var058" "value"; +set "var059" "value"; +set "var060" "value"; +set "var061" "value"; +set "var062" "value"; +set "var063" "value"; +set "var064" "value"; +set "var065" "value"; +set "var066" "value"; +set "var067" "value"; +set "var068" "value"; +set "var069" "value"; +set "var070" "value"; +set "var071" "value"; +set "var072" "value"; +set "var073" "value"; +set "var074" "value"; +set "var075" "value"; +set "var076" "value"; +set "var077" "value"; +set "var078" "value"; +set "var079" "value"; +set "var080" "value"; +set "var081" "value"; +set "var082" "value"; +set "var083" "value"; +set "var084" "value"; +set "var085" "value"; +set "var086" "value"; +set "var087" "value"; +set "var088" "value"; +set "var089" "value"; +set "var090" "value"; +set "var091" "value"; +set "var092" "value"; +set "var093" "value"; +set "var094" "value"; +set "var095" "value"; +set "var096" "value"; +set "var097" "value"; +set "var098" "value"; +set "var099" "value"; + +set "var100" "value"; +set "var101" "value"; +set "var102" "value"; +set "var103" "value"; +set "var104" "value"; +set "var105" "value"; +set "var106" "value"; +set "var107" "value"; +set "var108" "value"; +set "var109" "value"; +set "var110" "value"; +set "var111" "value"; +set "var112" "value"; +set "var113" "value"; +set "var114" "value"; +set "var115" "value"; +set "var116" "value"; +set "var117" "value"; +set "var118" "value"; +set "var119" "value"; +set "var120" "value"; +set "var121" "value"; +set "var122" "value"; +set "var123" "value"; +set "var124" "value"; +set "var125" "value"; +set "var126" "value"; +set "var127" "value"; +set "var128" "value"; +set "var129" "value"; +set "var130" "value"; +set "var131" "value"; +set "var132" "value"; +set "var133" "value"; +set "var134" "value"; +set "var135" "value"; +set "var136" "value"; +set "var137" "value"; +set "var138" "value"; +set "var139" "value"; +set "var140" "value"; +set "var141" "value"; +set "var142" "value"; +set "var143" "value"; +set "var144" "value"; +set "var145" "value"; +set "var146" "value"; +set "var147" "value"; +set "var148" "value"; +set "var149" "value"; +set "var150" "value"; +set "var151" "value"; +set "var152" "value"; +set "var153" "value"; +set "var154" "value"; +set "var155" "value"; +set "var156" "value"; +set "var157" "value"; +set "var158" "value"; +set "var159" "value"; +set "var160" "value"; +set "var161" "value"; +set "var162" "value"; +set "var163" "value"; +set "var164" "value"; +set "var165" "value"; +set "var166" "value"; +set "var167" "value"; +set "var168" "value"; +set "var169" "value"; +set "var170" "value"; +set "var171" "value"; +set "var172" "value"; +set "var173" "value"; +set "var174" "value"; +set "var175" "value"; +set "var176" "value"; +set "var177" "value"; +set "var178" "value"; +set "var179" "value"; +set "var180" "value"; +set "var181" "value"; +set "var182" "value"; +set "var183" "value"; +set "var184" "value"; +set "var185" "value"; +set "var186" "value"; +set "var187" "value"; +set "var188" "value"; +set "var189" "value"; +set "var190" "value"; +set "var191" "value"; +set "var192" "value"; +set "var193" "value"; +set "var194" "value"; +set "var195" "value"; +set "var196" "value"; +set "var197" "value"; +set "var198" "value"; +set "var199" "value"; +set "var200" "value"; + +set "var201" "value"; +set "var202" "value"; +set "var203" "value"; +set "var204" "value"; +set "var205" "value"; +set "var206" "value"; +set "var207" "value"; +set "var208" "value"; +set "var209" "value"; +set "var210" "value"; +set "var211" "value"; +set "var212" "value"; +set "var213" "value"; +set "var214" "value"; +set "var215" "value"; +set "var216" "value"; +set "var217" "value"; +set "var218" "value"; +set "var219" "value"; +set "var220" "value"; +set "var221" "value"; +set "var222" "value"; +set "var223" "value"; +set "var224" "value"; +set "var225" "value"; +set "var226" "value"; +set "var227" "value"; +set "var228" "value"; +set "var229" "value"; +set "var230" "value"; +set "var231" "value"; +set "var232" "value"; +set "var233" "value"; +set "var234" "value"; +set "var235" "value"; +set "var236" "value"; +set "var237" "value"; +set "var238" "value"; +set "var239" "value"; +set "var240" "value"; +set "var241" "value"; +set "var242" "value"; +set "var243" "value"; +set "var244" "value"; +set "var245" "value"; +set "var246" "value"; +set "var247" "value"; +set "var248" "value"; +set "var249" "value"; +set "var250" "value"; +set "var251" "value"; +set "var252" "value"; +set "var253" "value"; +set "var254" "value"; +set "var255" "value"; +set "var256" "value"; +set "var257" "value"; +set "var258" "value"; +set "var259" "value"; +set "var260" "value"; diff --git a/pigeonhole/tests/extensions/variables/errors/namespace.sieve b/pigeonhole/tests/extensions/variables/errors/namespace.sieve new file mode 100644 index 0000000..e11ac6d --- /dev/null +++ b/pigeonhole/tests/extensions/variables/errors/namespace.sieve @@ -0,0 +1,8 @@ +require "variables"; +require "fileinto"; + +set "namespace.frop" "value"; +set "complex.struct.frop" "value"; + +fileinto "${namespace.frop}"; +fileinto "${complex.struct.frop}"; diff --git a/pigeonhole/tests/extensions/variables/errors/set.sieve b/pigeonhole/tests/extensions/variables/errors/set.sieve new file mode 100644 index 0000000..07c393a --- /dev/null +++ b/pigeonhole/tests/extensions/variables/errors/set.sieve @@ -0,0 +1,19 @@ +require "variables"; + +# Invalid variable name +set "${frop}" "frop"; +set "...." "frop"; +set "name." "frop"; +set ".name" "frop"; + +# Not an error +set "\n\a\m\e" "frop"; + +# Trying to assign match variable; +set "0" "frop"; + +# Not an error +set :UPPER "name" "frop"; + +# Invalid tag +set :inner "name" "frop"; diff --git a/pigeonhole/tests/extensions/variables/limits.svtest b/pigeonhole/tests/extensions/variables/limits.svtest new file mode 100644 index 0000000..7397713 --- /dev/null +++ b/pigeonhole/tests/extensions/variables/limits.svtest @@ -0,0 +1,435 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "encoded-character"; + +/* + * Variable size limit + */ + +test_config_set "sieve_variables_max_variable_size" "4000"; +test_config_reload :extension "variables"; + +set "a" text: +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +. +; + +test "Variable size limit" { + set :length "alen" "${a}"; + + if not string "${alen}" "4000" { + test_fail "variable 'a' not 4000 bytes long (${alen}) [0]"; + } + + set "a" "${a}b"; + set :length "alen" "${a}"; + + if not string "${alen}" "4000" { + test_fail "variable 'a' not 4000 bytes long (${alen}) [1]"; + } + + set "a" "${a}${a}"; + set :length "alen" "${a}"; + + if not string "${alen}" "4000" { + test_fail "variable 'a' not 4000 bytes long (${alen}) [2]"; + } + + test_config_set "sieve_variables_max_variable_size" "8000"; + test_config_reload :extension "variables"; + + set "a" "${a}${a}"; + set :length "alen" "${a}"; + + if not string "${alen}" "8000" { + test_fail "variable 'a' not 8000 bytes long (${alen})"; + } +} + +/* + * Variable size limit UTF-8 + */ + +test_config_set "sieve_variables_max_variable_size" "4000"; +test_config_reload :extension "variables"; + +set "b" text: +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +012345678901234567890123456789012345678901234567 +01234567890123456789012345678901234567890123456 +. +; + +test "Variable size limit UTF-8" { + set :length "blen" "${b}"; + + if not string "${blen}" "3999" { + test_fail "variable 'b' not 3999 bytes long (${blen}) [0]"; + } + + set "b" "${b}${unicode:4e03}"; + set :length "blen" "${b}"; + + if not string "${blen}" "3999" { + test_fail "variable 'b' not 3999 bytes long (${blen}) [1]"; + } + + set "b" "${b}ccc"; + set :length "blen" "${b}"; + + if not string "${blen}" "4000" { + test_fail "variable 'b' not 4000 bytes long (${blen})"; + } +} + +/* + * :quotewildcard variable size limit + */ + +test_config_set "sieve_variables_max_variable_size" "4000"; +test_config_reload :extension "variables"; + +set "c" text: +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +************************************** +. +; + +test ":quotewildcard variable size limit" { + set :length "clen" "${c}"; + + if not string "${clen}" "4000" { + test_fail "variable 'c' not 4000 bytes long (${clen}) [0]"; + } + + set "d" "0${c}"; + set :quotewildcard "c" "${c}"; + set :length "clen" "${c}"; + + if not string "${clen}" "4000" { + test_fail "variable 'c' not 4000 bytes long (${clen}) [1]"; + } + + set :quotewildcard "d" "${d}"; + set :length "dlen" "${d}"; + + if not string "${dlen}" "3999" { + test_fail "variable 'd' not 3999 bytes long (${dlen})"; + } + + if not string :is text: +${d} +. +text: +0\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\*\*\*\*\*\*\*\* +. + { + test_fail "variable 'd' has unexpected value"; + } +} + diff --git a/pigeonhole/tests/extensions/variables/match.svtest b/pigeonhole/tests/extensions/variables/match.svtest new file mode 100644 index 0000000..11c0701 --- /dev/null +++ b/pigeonhole/tests/extensions/variables/match.svtest @@ -0,0 +1,365 @@ +require "vnd.dovecot.testsuite"; + +require "variables"; + +/* + * RFC compliance + */ + +# Test acceptance of leading zeroes +test "RFC - leading zeroes" { + if not string :matches "frop:frup:frop" "*:*:*" { + test_fail "failed to match"; + } + + if not string :is "${0000002}" "frup" { + test_fail "incorrect match value (0000002): ${0000002}"; + } +} + +# Test non-greedyness +test "RFC - not greedy" { + if not string :matches "frop.......frop.........frop...." "?*frop*" { + test_fail "failed to match"; + } + + if not string :is "${1}${2}${3}" "frop................frop...." { + test_fail "incorrect match values: ${1}${2}${3}"; + } +} + +# Index out of range +test "RFC - index out of range" { + if not string :matches "test" "*" { + test_fail "failed to match (impossible)"; + } + + if not string :is "${2}" "" { + test_fail "incorrect match value: '${2}'"; + } +} + +# Index 0 +test "RFC - index 0" { + if not string :matches "a b c d e f g" "? ? ? ? ? ? ?" { + test_fail "failed to match"; + } + + if not string :is "${0}" "a b c d e f g" { + test_fail "incorrect match value: ${0}"; + } +} + +# Test short-circuit +test "RFC - test short-circuit" { + if not anyof ( + string :matches "a b c d e f g" "? ?", + string :matches "puk pok puk pok" "pu*ok", + string :matches "snot kip snot" "snot*snot" + ) { + test_fail "failed to match any"; + } + + if string :is "${1}" " kip " { + test_fail "did not short-circuit test execution or intented test failed."; + } + + if not string :is "${1}" "k pok puk p" { + test_fail "incorrect match value: ${1}"; + } +} + +# Test overwriting only on match +test "RFC - values overwrite" { + set "sentence1" "the cat jumps off the table"; + set "sentence2" "the dog barks at the cat in the alley"; + + if not string :matches "${sentence1}" "the * jumps off the *" { + test_fail "failed to match first sentence"; + } + + if not string :is "${1}:${2}" "cat:table" { + test_fail "invalid match values"; + } + + if string :matches "${sentence2}" "the * barks at the * in the store" { + test_fail "should not have matched second sentence"; + } + + if not string :is "${1}:${2}" "cat:table" { + test_fail "should have preserved match values"; + } + + if not string :matches "${sentence2}" "the * barks at the * in the alley" { + test_fail "failed to match the second sentence (second time)"; + } + + if not string :is "${1}:${2}" "dog:cat" { + test_fail "should have overwritten match values"; + } +} + +test "RFC - example" { + test_set "message" text: +Subject: [acme-users] [fwd] version 1.0 is out +List-Id: Dovecot Mailing List <dovecot@dovecot.example.net> +To: coyote@ACME.Example.COM +Fom: stephan@example.org + +Test message. +. +; + if header :matches "List-ID" "*<*@*" { + if not string "INBOX.lists.${2}" "INBOX.lists.dovecot" { + test_fail "incorrect match value: INBOX.lists.${2}"; + } + } else { + test_fail "failed to match list header"; + } + + # Imagine the header + # Subject: [acme-users] [fwd] version 1.0 is out + if header :matches "Subject" "[*] *" { + # ${1} will hold "acme-users", + # ${2} will hold "[fwd] version 1.0 is out" + + if anyof ( + not string "${1}" "acme-users", + not string "${2}" "[fwd] version 1.0 is out" + ) { + test_fail "invalid match values: ${1} ${2}"; + } + } else { + test_fail "failed to match subject"; + } + + # Imagine the header + # To: coyote@ACME.Example.COM + if address :matches ["To", "Cc"] ["coyote@**.com", + "wile@**.com"] { + # ${0} is the matching address + # ${1} is always the empty string + # ${2} is part of the domain name ("ACME.Example") + + if anyof ( + not string "${0}" "coyote@ACME.Example.COM", + not string "${1}" "", + not string "${2}" "ACME.Example" + ) { + test_fail "invalid match values: ${0}, ${1}, ${2}"; + } + } else { + # Control wouldn't reach this block if any match was + # successful, so no match variables are set at this + # point. + + test_fail "failed to match to address"; + } + + if anyof (true, address :domain :matches "To" "*.com") { + # The second test is never evaluated, so there are + # still no match variables set. + + /* FIXME: not compliant */ + } +} + +/* + * Generic tests + */ + +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}"; + } +} + +/* + * Specific tests + */ + +test_set "message" text: +Return-path: <stephan@xi.example.org> +Envelope-to: stephan@xi.example.org +Delivery-date: Sun, 01 Feb 2009 11:29:57 +0100 +Received: from stephan by xi.example.org with local (Exim 4.69) + (envelope-from <stephan@xi.example.org>) + id 1LTZaP-0007h3-2e + for stephan@xi.example.org; Sun, 01 Feb 2009 11:29:57 +0100 +From: Dovecot Debian Builder <stephan.example.org@xi.example.org> +To: stephan@xi.example.org +Subject: Log for failed build of dovecot_2:1.2.alpha5-0~auto+159 (dist=hardy) +Message-Id: <E1LTZaP-0007h3-2e@xi.example.org> +Date: Sun, 01 Feb 2009 11:29:57 +0100 + +Automatic build of dovecot_1.2.alpha5-0~auto+159 on xi by sbuild/i386 0.57.7 +. +; + +test "Match combined" { + if not header :matches "subject" "Log for ?* build of *" { + test_fail "failed to match"; + } + + if not string "${1}${2}" "failed" { + test_fail "incorrect match values: ${1}${2}"; + } +} diff --git a/pigeonhole/tests/extensions/variables/modifiers.svtest b/pigeonhole/tests/extensions/variables/modifiers.svtest new file mode 100644 index 0000000..37068b6 --- /dev/null +++ b/pigeonhole/tests/extensions/variables/modifiers.svtest @@ -0,0 +1,160 @@ +require "vnd.dovecot.testsuite"; +require "variables"; +require "encoded-character"; + +/* + * Modifiers + */ + +test "Modifier :lower" { + set :lower "test" "VaLuE"; + + if not string :is "${test}" "value" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifiers :lower :upperfirst" { + set :lower :upperfirst "test" "vAlUe"; + + if string :is "${test}" "value" { + test_fail "modifiers applied with wrong precedence"; + } + + if not string :is "${test}" "Value" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifiers :upperfirst :lower" { + set :upperfirst :lower "test" "vAlUe"; + + if string :is "${test}" "value" { + test_fail "modifiers applied with wrong precedence"; + } + + if not string :is "${test}" "Value" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifier :upper" { + set :upper "test" "vAlUe"; + + if not string :is "${test}" "VALUE" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifiers :upper :lowerfirst" { + set :upper :lowerfirst "test" "VaLuE"; + + if string :is "${test}" "VALUE" { + test_fail "modifiers applied with wrong precedence"; + } + + if not string :is "${test}" "vALUE" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifiers :lowerfirst :upper" { + set :lowerfirst :upper "test" "VaLuE"; + + if string :is "${test}" "VALUE" { + test_fail "modifiers applied with wrong precedence"; + } + + if not string :is "${test}" "vALUE" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifier :length (empty)" { + set :length "test" ""; + + if not string :is "${test}" "0" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifier :length (simple)" { + set :length "test" "VaLuE"; + + if not string :is "${test}" "5" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifier :length (elaborate)" { + set "a" "abcdefghijklmnopqrstuvwxyz"; + set "b" "1234567890"; + set :length "test" " ${a}:${b} "; + + if not string :is "${test}" "40" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifier :quotewildcard" { + set :quotewildcard "test" "^^***??**^^"; + + if not string :is "${test}" "^^\\*\\*\\*\\?\\?\\*\\*^^" { + test_fail "modified variable assignment failed"; + } +} + +test "Modifier :length :quotewildcard" { + set :length :quotewildcard "test" "^^***??**^^"; + + if string :is "${test}" "11" { + test_fail "modifiers applied with wrong precedence"; + } + + if not string :is "${test}" "18" { + test_fail "modified variable assignment failed"; + } +} + +test "RFC examples" { + set "a" "juMBlEd lETteRS"; # => "juMBlEd lETteRS" + if not string "${a}" "juMBlEd lETteRS" { + test_fail "modified assignment failed (1): ${a}"; + } + + set :length "b" "${a}"; # => "15" + if not string "${b}" "15" { + test_fail "modified assignment failed (2): ${a}"; + } + + set :lower "b" "${a}"; # => "jumbled letters" + if not string "${b}" "jumbled letters" { + test_fail "modified assignment failed (3): ${a}"; + } + + set :upperfirst "b" "${a}"; # => "JuMBlEd lETteRS" + if not string "${b}" "JuMBlEd lETteRS" { + test_fail "modified assignment failed (4): ${a}"; + } + + set :upperfirst :lower "b" "${a}"; # => "Jumbled letters" + if not string "${b}" "Jumbled letters" { + test_fail "modified assignment failed (5): ${a}"; + } + + set :quotewildcard "b" "Rock*"; # => "Rock\*" + if not string "${b}" "Rock\\*" { + test_fail "modified assignment failed (6): ${a}"; + } +} + +/* RFC mentions `characters' and not octets */ + +test "Modifier :length utf8" { + set "a" "Das ist ${unicode: 00fc}berhaupt nicht m${unicode: 00f6}glich."; + + set :length "b" "${a}"; + if not string "${b}" "32" { + test_fail "incorrect number of unicode characters reported: ${b}/32"; + } +} diff --git a/pigeonhole/tests/extensions/variables/quoting.svtest b/pigeonhole/tests/extensions/variables/quoting.svtest new file mode 100644 index 0000000..f65e4e4 --- /dev/null +++ b/pigeonhole/tests/extensions/variables/quoting.svtest @@ -0,0 +1,36 @@ +require "vnd.dovecot.testsuite"; + +require "variables"; +require "encoded-character"; + +test "Encodings - RFC examples" { + set "s" "$"; + set "foo" "bar"; + + # "${fo\o}" => ${foo} => the expansion of variable foo. + if not string :is "${fo\o}" "bar" { + test_fail "failed 'the expansion of variable foo (${s}{fo\\o})'"; + } + + # "${fo\\o}" => ${fo\o} => illegal identifier => left verbatim. + if not string :is "${fo\\o}" "${s}{fo\\o}" { + test_fail "failed 'illegal identifier => left verbatim'"; + } + + # "\${foo}" => ${foo} => the expansion of variable foo. + if not string "\${foo}" "bar" { + test_fail "failed 'the expansion of variable foo (\\${s}{foo})'"; + } + + # "\\${foo}" => \${foo} => a backslash character followed by the + # expansion of variable foo. + if not string "\\${foo}" "\\bar" { + test_fail "failed 'a backslash character followed by expansion of variable foo"; + } + + set "name" "Ethelbert"; + if not string "dear${hex:20 24 7b 4e}ame}" "dear Ethelbert" { + test_fail "failed 'dear Ethelbert' example"; + } +} + diff --git a/pigeonhole/tests/extensions/variables/regex.svtest b/pigeonhole/tests/extensions/variables/regex.svtest new file mode 100644 index 0000000..04ca00d --- /dev/null +++ b/pigeonhole/tests/extensions/variables/regex.svtest @@ -0,0 +1,35 @@ +require "vnd.dovecot.testsuite"; + +require "regex"; +require "variables"; + +# Test overwriting only on match +test "RFC - values overwrite" { + set "sentence1" "the cat jumps off the table"; + set "sentence2" "the dog barks at the cat in the alley"; + + if not string :regex "${sentence1}" "the (.*) jumps off the (.*)" { + test_fail "failed to match first sentence"; + } + + if not string :is "${1}:${2}" "cat:table" { + test_fail "invalid match values"; + } + + if string :regex "${sentence2}" "the (.*) barks at the (.*) in the store" { + test_fail "should not have matched second sentence"; + } + + if not string :is "${1}:${2}" "cat:table" { + test_fail "should have preserved match values"; + } + + if not string :regex "${sentence2}" "the (.*) barks at the (.*) in the alley" { + test_fail "failed to match the second sentence (second time)"; + } + + if not string :is "${1}:${2}" "dog:cat" { + test_fail "should have overwritten match values"; + } +} + diff --git a/pigeonhole/tests/extensions/variables/string.svtest b/pigeonhole/tests/extensions/variables/string.svtest new file mode 100644 index 0000000..d0244e6 --- /dev/null +++ b/pigeonhole/tests/extensions/variables/string.svtest @@ -0,0 +1,37 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +require "variables"; + +test "String - :count" { + if not string :count "eq" :comparator "i;ascii-numeric" ["a", "b", "c"] "3" { + test_fail "string test failed :count match"; + } +} + +test "String - :count \"\"" { + if not string :count "eq" :comparator "i;ascii-numeric" ["a", "", "c"] "2" { + 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} \""; + } +} + +test "No whitespace stripping" { + set "vara" " value "; + set "varb" "value"; + + if not string :is :comparator "i;octet" "${vara}" " ${varb} " { + test_fail "string test seems to have stripped white space"; + } +} diff --git a/pigeonhole/tests/extensions/vnd.dovecot/debug/execute.svtest b/pigeonhole/tests/extensions/vnd.dovecot/debug/execute.svtest new file mode 100644 index 0000000..6d67024 --- /dev/null +++ b/pigeonhole/tests/extensions/vnd.dovecot/debug/execute.svtest @@ -0,0 +1,6 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.debug"; + +test "Basic" { + debug_log "logging basic message."; +} diff --git a/pigeonhole/tests/extensions/vnd.dovecot/environment/basic.svtest b/pigeonhole/tests/extensions/vnd.dovecot/environment/basic.svtest new file mode 100644 index 0000000..c58bbc0 --- /dev/null +++ b/pigeonhole/tests/extensions/vnd.dovecot/environment/basic.svtest @@ -0,0 +1,29 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.environment"; +require "variables"; + +test "default-mailbox" { + if not environment :is "vnd.dovecot.default-mailbox" "INBOX" { + if environment :matches "vnd.dovecot.default-mailbox" "*" { set "env" "${1}"; } + + test_fail "vnd.dovecot.default-mailbox environment returned invalid value(1): `${env}'"; + } +} + +test "username" { + if not environment :contains "vnd.dovecot.username" "" { + test_fail "vnd.dovecot.username environment does not exist"; + } +} + +test_config_set "sieve_env_display_name" "Jan Jansen"; +test_config_reload :extension "vnd.dovecot.environment"; + +test "config" { + if not environment :contains "vnd.dovecot.config.display_name" "" { + test_fail "vnd.dovecot.config.display_name environment does not exist"; + } + if not environment :is "vnd.dovecot.config.display_name" "Jan Jansen" { + test_fail "vnd.dovecot.config.display_name environment has wrong value"; + } +} diff --git a/pigeonhole/tests/extensions/vnd.dovecot/environment/variables.svtest b/pigeonhole/tests/extensions/vnd.dovecot/environment/variables.svtest new file mode 100644 index 0000000..886e75e --- /dev/null +++ b/pigeonhole/tests/extensions/vnd.dovecot/environment/variables.svtest @@ -0,0 +1,18 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.environment"; +require "variables"; +require "relational"; + +test "default_mailbox" { + if not string "${env.vnd.dovecot.default_mailbox}" "INBOX" { + test_fail "The env.vnd.dovecot.default_mailbox variable returned invalid value: `${env.vnd.dovecot.default_mailbox}'"; + } +} + +test "username" { + set :length "userlen" "${env.vnd.dovecot.username}"; + if not string :value "ge" "${userlen}" "1" { + test_fail "The env.vnd.dovecot.username variable is empty or does not exist"; + } +} + diff --git a/pigeonhole/tests/extensions/vnd.dovecot/report/errors.svtest b/pigeonhole/tests/extensions/vnd.dovecot/report/errors.svtest new file mode 100644 index 0000000..82ab992 --- /dev/null +++ b/pigeonhole/tests/extensions/vnd.dovecot/report/errors.svtest @@ -0,0 +1,13 @@ +require "vnd.dovecot.testsuite"; +require "comparator-i;ascii-numeric"; +require "relational"; + +test "Invalid syntax (FIXME: count only)" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "9" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/extensions/vnd.dovecot/report/errors/syntax.sieve b/pigeonhole/tests/extensions/vnd.dovecot/report/errors/syntax.sieve new file mode 100644 index 0000000..250ad60 --- /dev/null +++ b/pigeonhole/tests/extensions/vnd.dovecot/report/errors/syntax.sieve @@ -0,0 +1,28 @@ +require "vnd.dovecot.report"; + +# 1: Too few arguments +report; + +# 2: Too few arguments +report "abuse"; + +# 3: Too few arguments +report "abuse" "Message is spam."; + +# Not an error +report "abuse" "Message is spam." "frop@example.com"; + +# 4: Bad arguments +report "abuse" "Message is spam." 1; + +# 5: Bad tag +report :frop "abuse" "Message is spam." "frop@example.com"; + +# 6: Bad sub-test +report "abuse" "Message is spam." "frop@example.com" frop; + +# 7: Bad block +report "abuse" "Message is spam." "frop@example.com" { } + +# 8: Bad feedback type +report "?????" "Message is spam." "frop@example.com"; diff --git a/pigeonhole/tests/extensions/vnd.dovecot/report/execute.svtest b/pigeonhole/tests/extensions/vnd.dovecot/report/execute.svtest new file mode 100644 index 0000000..11a8079 --- /dev/null +++ b/pigeonhole/tests/extensions/vnd.dovecot/report/execute.svtest @@ -0,0 +1,269 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.report"; +require "relational"; +require "comparator-i;ascii-numeric"; +require "body"; +require "variables"; + +/* + * Simple test + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Simple" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "This message is spam!" { + test_fail "report does not contain user text"; + } + + if not body :raw :contains "Klutsefluts" { + test_fail "report does not contain message body"; + } +} + +/* + * Simple - :headers_only test + */ + +test_result_reset; + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test "Simple - :headers_only" { + report :headers_only "abuse" + "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "This message is spam!" { + test_fail "report does not contain user text"; + } + + if body :raw :contains "Klutsefluts" { + test_fail "report contains message body"; + } +} + +/* + * Configuration + */ + +set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +/* default */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_result_reset; + +test "Configuration - from default" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "postmaster" { + test_fail "not sent from postmaster"; + } +} + +/* from sender */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "sender"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from sender" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "from" { + test_fail "not sent from sender"; + } +} + +/* from recipient */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "recipient"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from recipient" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "to" { + test_fail "not sent from recipient"; + } +} + +/* from original recipient */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "orig_recipient"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from original recipient" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "orig_to" { + test_fail "not sent from original recipient"; + } +} + +/* from user email */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "user_email"; +test_config_set "sieve_user_email" "user@example.com"; +test_config_reload; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from user email" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "user" { + test_fail "not sent from user email"; + } +} + +/* explicit */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "<frop@example.com>"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - explicit" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "frop" { + test_fail "not sent from explicit address"; + } +} + +/* + * Reporting-User + */ + +/* sieve_user_email */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_user_email" "newuser@example.com"; +test_config_reload; +test_result_reset; + +test "Reporting-User - sieve_user_email" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not body :raw :contains "Dovecot-Reporting-User: <newuser@example.com>" { + test_fail "Reporting-User field is wrong."; + } +}
\ No newline at end of file diff --git a/pigeonhole/tests/failures/fuzz1.svtest b/pigeonhole/tests/failures/fuzz1.svtest new file mode 100644 index 0000000..a6fe086 --- /dev/null +++ b/pigeonhole/tests/failures/fuzz1.svtest @@ -0,0 +1,33 @@ +# Used to cause the test suite to segfault + +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "imap4flags"; +require "mailbox"; + + +test_set "message" text: +Subject: Test message. + +Test message. +. +; + +test "Flag changes between stores" { + fileinto :create "FolderA"; + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_message :folder "FolderA" 0; + + test_result_reset; + + test_message :folder "Uninteiesting" 0; + + if not hasflag "$label1" { + test_fail "flags not stored for fired for third message"; + } + +} diff --git a/pigeonhole/tests/failures/fuzz2.svtest b/pigeonhole/tests/failures/fuzz2.svtest new file mode 100644 index 0000000..9fa63ea --- /dev/null +++ b/pigeonhole/tests/failures/fuzz2.svtest @@ -0,0 +1,37 @@ +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "variables"; +require "mailbox"; + +set "message" text: +From:.org +To:rg +Subject: First message + +Frop +. +; + + +test "sometest" { + test_set "message" "${message}"; + + fileinto :create "Folder"; + + if not test_result_execute { + test_fail ""; + } + + test_message :folder "Folder" 0; + + if not header "subject" "First message" { + test_fail ""; + } + + test_message :folder " .Folder" 1; + + if not header "subject" "Second message" { + test_fail ""; + } + +} diff --git a/pigeonhole/tests/failures/fuzz3.svtest b/pigeonhole/tests/failures/fuzz3.svtest new file mode 100644 index 0000000..c1c22dc --- /dev/null +++ b/pigeonhole/tests/failures/fuzz3.svtest @@ -0,0 +1,12 @@ +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "mailbox"; + +test"" { + fileinto :create "Folder"; + + if test_result_execute { + } + + test_message :folder "Folder" 2; +} diff --git a/pigeonhole/tests/failures/mailbox-bad-utf8.svtest b/pigeonhole/tests/failures/mailbox-bad-utf8.svtest new file mode 100644 index 0000000..ad104e5 --- /dev/null +++ b/pigeonhole/tests/failures/mailbox-bad-utf8.svtest @@ -0,0 +1,6 @@ +require "vnd.dovecot.testsuite"; +require "encoded-character"; + +test "Mailbox parameter with bad UTF-8" { + test_message :folder "I${hex:9b}BOX" 0; +} diff --git a/pigeonhole/tests/lexer.svtest b/pigeonhole/tests/lexer.svtest new file mode 100644 index 0000000..491309d --- /dev/null +++ b/pigeonhole/tests/lexer.svtest @@ -0,0 +1,39 @@ +require "vnd.dovecot.testsuite"; +require "variables"; + +/* Test conformance to RFC 5228 - 2.4.2. Strings */ + +set "text" text: # Comment +Line 1 +.Line 2 +..Line 3 +.Line 4 +Line 5 +. +; + +set "quoted" +"Line 1 +.Line 2 +.Line 3 +.Line 4 +Line 5 +"; + +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/pigeonhole/tests/match-types/contains.svtest b/pigeonhole/tests/match-types/contains.svtest new file mode 100644 index 0000000..710afca --- /dev/null +++ b/pigeonhole/tests/match-types/contains.svtest @@ -0,0 +1,81 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: stephan@example.org +Cc: frop@example.com +To: test@dovecot.example.net +X-Bullshit: f fr fro frop frob frobn frobnitzn +Subject: Test Message +Comment: + +Test! +. +; + +# Match tests + +test "Match empty" { + if not header :contains "x-bullshit" "" { + test_fail "contains tests fails to match \"\" against non-empty string"; + } + + if not header :contains "comment" "" { + test_fail "contains tests fails to match \"\" against empty string"; + } +} + +test "Match full" { + if not address :contains "from" "stephan@example.org" { + test_fail "should have matched"; + } +} + +test "Match begin" { + if not address :contains "from" "stephan" { + test_fail "should have matched"; + } +} + +test "Match end" { + if not address :contains "from" "example.org" { + test_fail "should have matched"; + } +} + +test "Match middle" { + if not address :contains "from" "@" { + test_fail "should have matched"; + } +} + +test "Match similar beginnings" { + if not header :contains "x-bullshit" "frobnitzn" { + test_fail "should have matched"; + } +} + +test "Match case-insensitive" { + if not address :contains :comparator "i;ascii-casemap" "from" "EXAMPLE" { + test_fail "match fails to apply correct comparator"; + } + + if not address :contains "from" "EXAMPLE" { + test_fail "default comparator is wrong"; + } +} + +# Non-match tests + +test "No match full (typo)" { + if address :contains "to" "frob@example.com" { + test_fail "should not have matched"; + } +} + +test "No match end (typo)" { + if header :contains "x-bullshit" "frobnitzm" { + test_fail "should not have matched"; + } +} + + diff --git a/pigeonhole/tests/match-types/is.svtest b/pigeonhole/tests/match-types/is.svtest new file mode 100644 index 0000000..c715db8 --- /dev/null +++ b/pigeonhole/tests/match-types/is.svtest @@ -0,0 +1,22 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: Stephan Bosch <stephan@example.org> +To: nico@frop.example.org +Subject: Test message +Comment: + +Test! + +. +; + +test "Empty key" { + if header :is "from" "" { + test_fail "erroneously matched empty key against non-empty string"; + } + + if not header :is "comment" "" { + test_fail "failed to match empty string"; + } +} diff --git a/pigeonhole/tests/match-types/matches.svtest b/pigeonhole/tests/match-types/matches.svtest new file mode 100644 index 0000000..bcc188d --- /dev/null +++ b/pigeonhole/tests/match-types/matches.svtest @@ -0,0 +1,241 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: stephan+sieve@friep.example.com +To: sirius@example.org +To: nico@frop.example.org +Cc: me@example.com +Cc: timo@dovecot.example.com +X-Hufter: TRUE +Subject: make your money very fast!!! +X-Spam-Score: ********** +X-Bullshit: 33333???a +Message-ID: <90a02fe01fc25e131d0e9c4c45975894@example.com> +Comment: +X-Subject: Log for successful build of Dovecot. + +Het werkt! +. +; + +/* + * General conformance testing + */ + +test "Empty string" { + if not header :matches "comment" "" { + test_fail "failed to match \"\" against \"\""; + } + + if not header :matches "comment" "*" { + test_fail "failed to match \"\" against \"*\""; + } + + if header :matches "comment" "?" { + test_fail "inappropriately matched \"\" against \"?\""; + } +} + +test "Multiple '*'" { + if not address :matches "from" "*@fri*p*examp*.com" { + test_fail "should have matched"; + } + + if address :matches "from" "*@f*pex*mple.com" { + test_fail "should not have matched"; + } +} + +test "End '*'" { + if not address :matches "from" "stephan+sieve@friep.*" { + test_fail "should have matched"; + } + + if address :matches "from" "stepan+sieve@friep.*" { + test_fail "should not have matched"; + } +} + +test "Begin '*'" { + if not address :matches "from" "*+sieve@friep.example.com" { + test_fail "should have matched"; + } + + if address :matches "from" "*+sieve@friep.example.om" { + test_fail "should not have matched"; + } +} + +test "Middle '?'" { + if not address :matches "from" "stephan+sieve?friep.example.com" { + test_fail "should have matched"; + } + + if address :matches "from" "stephan+sieve?fiep.example.com" { + test_fail "should not have matched"; + } +} + +test "Begin '?'" { + if not address :matches "from" "?tephan+sieve@friep.example.com" { + test_fail "should have matched"; + } + + if address :matches "from" "?tephan+sievefriep.example.com" { + test_fail "should not have matched"; + } +} + +test "End '?'" { + if not address :matches "from" "stephan+sieve@friep.example.co?" { + test_fail "should have matched"; + } + + if address :matches "from" "sephan+sieve@friep.example.co?" { + test_fail "should not have matched"; + } +} + +test "Multiple '?'" { + if not address :matches "from" "?t?phan?sieve?fri?p.exampl?.co?" { + test_fail "should have matched"; + } + + if address :matches "from" "?t?phan?sieve?fiep.exam?le.co?" { + test_fail "should not have matched"; + } +} + +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 "Escaped '?' following '*'" { + if not header :matches "x-bullshit" "33333*\\?\\??" { + test_fail "should have matched"; + } + +} + +test "Escaped '?' directly following initial '*'" { + if not header :matches "X-Bullshit" "*\\?\\?\\?a" { + test_fail "should have matched"; + } +} + +test "Escaped '?' following initial '*'" { + if not header :matches "x-bullshit" "*3333\\?\\?\\?a" { + test_fail "should have matched"; + } +} + +test "Escaped '*' with active '*' at the end" { + if not header :matches "x-spam-score" "\\*\\*\\*\\*\\**" { + test_fail "should have matched"; + } +} + +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 "Middle not escaped '*'" { + if not header :matches "x-spam-score" "\\*\\*\\***\\*\\*" { + test_fail "should have matched"; + } +} + +test "Escaped '*' alternating with '?'" { + 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 "All escaped" { + if header :matches "x-bullshit" "\\*3333\\?\\?\\?a" { + test_fail "should not have matched"; + } + + + if header :matches "x-bullshit" "33333\\?\\?\\?aa" { + test_fail "should not have matched"; + } + + if header :matches "x-bullshit" "\\f3333\\?\\?\\?a" { + test_fail "should not have matched"; + } +} + +test "Put '*' directly before '?'" { + if header :matches "x-subject" "Log for *??????????? build of *" { + test_fail "should not have matched"; + } + + if not header :matches "x-subject" "Log for *?????????? build of *" { + test_fail "should have matched"; + } + + if not header :matches "x-subject" "Log for *? build of *" { + test_fail "should have matched"; + } +} + +test "Put '?' directly before '*'" { + if header :matches "x-subject" "Log for ???????????* build of *" { + test_fail "should not have matched"; + } + + if not header :matches "x-subject" "Log for ??????????* build of *" { + test_fail "should have matched"; + } + + if not header :matches "x-subject" "Log for ?* build of *" { + test_fail "should have matched"; + } +} + +test "Fixed beginning" { + if not header :matches "subject" "make your *" { + test_fail "should have matched"; + } +} + +test "Fixed end" { + if not header :matches "subject" "* very fast!!!" { + test_fail "should have matched"; + } + + if header :matches "subject" "* very fast!!" { + test_fail "should not have matched"; + } +} + +test "Fixed string" { + if not address :matches "to" "sirius@example.org" { + test_fail "should have matched"; + } + + if address :matches "to" "example.org" { + test_fail "should not have matched"; + } + + if address :matches "to" "sirius" { + test_fail "should not have matched"; + } +} diff --git a/pigeonhole/tests/multiscript/basic.svtest b/pigeonhole/tests/multiscript/basic.svtest new file mode 100644 index 0000000..ce9bb66 --- /dev/null +++ b/pigeonhole/tests/multiscript/basic.svtest @@ -0,0 +1,91 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: stephan@example.org +Message-ID: <frop33333333333333333@frutsens.example.nl> +To: nico@frop.example.org +Subject: Frop. + +Friep. +. +; + +test "Append" { + if not allof ( + test_script_compile "fileinto-inbox.sieve", + test_script_run ){ + test_fail "failed to compile and run first script"; + } + + if not allof ( + test_script_compile "vacation.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run second script"; + } + + if not allof ( + test_script_compile "notify.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run third script"; + } + + if not test_result_action :index 1 "store" { + test_fail "first action is not 'store'"; + } + + if not test_result_action :index 2 "vacation" { + test_fail "second action is not 'vacation'"; + } + + if not test_result_action :index 3 "notify" { + test_fail "third action is not 'notify'"; + } + + if not test_result_execute { + test_fail "result execute failed"; + } +} + +test "Sequential Execute" { + if not allof ( + test_script_compile "fileinto-inbox.sieve", + test_script_run ) { + test_fail "failed to compile and run first script"; + } + + if not test_result_execute { + test_fail "result execute failed after first script"; + } + + if not allof ( + test_script_compile "vacation.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run second script"; + } + + if not test_result_execute { + test_fail "result execute failed after second script"; + } + + if not allof ( + test_script_compile "notify.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run third script"; + } + + if not test_result_execute { + test_fail "result execute failed after third script"; + } + + if not test_result_action :index 1 "store" { + test_fail "first action is not 'store'"; + } + + if not test_result_action :index 2 "vacation" { + test_fail "second action is not 'vacation'"; + } + + if not test_result_action :index 3 "notify" { + test_fail "third action is not 'notify'"; + } +} diff --git a/pigeonhole/tests/multiscript/conflicts.svtest b/pigeonhole/tests/multiscript/conflicts.svtest new file mode 100644 index 0000000..a2b8fab --- /dev/null +++ b/pigeonhole/tests/multiscript/conflicts.svtest @@ -0,0 +1,100 @@ +require "vnd.dovecot.testsuite"; + +test_set "message" text: +From: stephan@example.org +Message-ID: <frop33333333333333333@nl.example.com> +To: nico@frop.example.org +Subject: Frop. + +Friep. +. +; + +test "Graceful Conflicts" { + if not allof ( + test_script_compile "fileinto-inbox.sieve", + test_script_run ){ + test_fail "failed to compile and run first script"; + } + + if not test_result_execute { + test_fail "result execute failed after first script"; + } + + if not allof ( + test_script_compile "reject-1.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run second script"; + } + + if not test_result_execute { + test_fail "result execute failed after second script"; + } + + if not allof ( + test_script_compile "reject-2.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run third script"; + } + + if not test_result_execute { + test_fail "result execute failed after third script"; + } + + if not test_result_action :index 1 "store" { + test_result_print; + test_fail "first action is not 'store'"; + } + + if not test_result_action :index 2 "reject" { + test_result_print; + test_fail "first reject action not retained"; + } + + if test_result_action :index 3 "reject" { + test_result_print; + test_fail "second reject action not discarded"; + } + +} + +test "Duplicates" { + if not allof ( + test_script_compile "fileinto-inbox.sieve", + test_script_run ){ + test_fail "failed to compile and run first script"; + } + + if not test_result_execute { + test_fail "result execute failed after first script"; + } + + if not allof ( + test_script_compile "fileinto-inbox.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run second script"; + } + + if not test_result_execute { + test_fail "result execute failed after second script"; + } + + if not allof ( + test_script_compile "keep.sieve", + test_script_run :append_result ) { + test_fail "failed to compile and run third script"; + } + + if not test_result_execute { + test_fail "result execute failed after third script"; + } + + if not test_result_action :index 1 "keep" { + test_fail "first action is not 'keep'"; + } + + if test_result_action :index 2 "store" { + test_fail "fileinto action not discarded"; + } +} + diff --git a/pigeonhole/tests/multiscript/fileinto-frop.sieve b/pigeonhole/tests/multiscript/fileinto-frop.sieve new file mode 100644 index 0000000..9aafb95 --- /dev/null +++ b/pigeonhole/tests/multiscript/fileinto-frop.sieve @@ -0,0 +1,3 @@ +require "fileinto"; + +fileinto "frop"; diff --git a/pigeonhole/tests/multiscript/fileinto-inbox.sieve b/pigeonhole/tests/multiscript/fileinto-inbox.sieve new file mode 100644 index 0000000..b5da850 --- /dev/null +++ b/pigeonhole/tests/multiscript/fileinto-inbox.sieve @@ -0,0 +1,4 @@ +require "fileinto"; + +fileinto "INBOX"; + diff --git a/pigeonhole/tests/multiscript/keep.sieve b/pigeonhole/tests/multiscript/keep.sieve new file mode 100644 index 0000000..6203a21 --- /dev/null +++ b/pigeonhole/tests/multiscript/keep.sieve @@ -0,0 +1 @@ +keep; diff --git a/pigeonhole/tests/multiscript/notify.sieve b/pigeonhole/tests/multiscript/notify.sieve new file mode 100644 index 0000000..af47ad9 --- /dev/null +++ b/pigeonhole/tests/multiscript/notify.sieve @@ -0,0 +1,3 @@ +require "enotify"; + +notify "mailto:stephan@example.org"; diff --git a/pigeonhole/tests/multiscript/reject-1.sieve b/pigeonhole/tests/multiscript/reject-1.sieve new file mode 100644 index 0000000..06744f6 --- /dev/null +++ b/pigeonhole/tests/multiscript/reject-1.sieve @@ -0,0 +1,3 @@ +require "reject"; + +reject "Message is not wanted."; diff --git a/pigeonhole/tests/multiscript/reject-2.sieve b/pigeonhole/tests/multiscript/reject-2.sieve new file mode 100644 index 0000000..96b7564 --- /dev/null +++ b/pigeonhole/tests/multiscript/reject-2.sieve @@ -0,0 +1,3 @@ +require "reject"; + +reject "Will not accept this nonsense."; diff --git a/pigeonhole/tests/multiscript/vacation.sieve b/pigeonhole/tests/multiscript/vacation.sieve new file mode 100644 index 0000000..d735da5 --- /dev/null +++ b/pigeonhole/tests/multiscript/vacation.sieve @@ -0,0 +1,3 @@ +require "vacation"; + +vacation "I am not home"; diff --git a/pigeonhole/tests/plugins/extprograms/bin/addheader b/pigeonhole/tests/plugins/extprograms/bin/addheader new file mode 100755 index 0000000..8f9805a --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/addheader @@ -0,0 +1,6 @@ +#!/bin/sh + +echo "$1: $2" +cat + +exit 0 diff --git a/pigeonhole/tests/plugins/extprograms/bin/big b/pigeonhole/tests/plugins/extprograms/bin/big new file mode 100755 index 0000000..ce1df51 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/big @@ -0,0 +1,8 @@ +#!/bin/sh + +N="0123456701234567012345670123456701234567012345670123456701234567" +N="$N$N$N$N$N$N$N$N$N$N$N$N$N$N$N$N" +echo -n "$N$N" + +exit 0 + diff --git a/pigeonhole/tests/plugins/extprograms/bin/cat b/pigeonhole/tests/plugins/extprograms/bin/cat new file mode 100755 index 0000000..02b9858 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/cat @@ -0,0 +1,3 @@ +#!/bin/sh + +cat diff --git a/pigeonhole/tests/plugins/extprograms/bin/cat-stdin b/pigeonhole/tests/plugins/extprograms/bin/cat-stdin new file mode 100755 index 0000000..781d70b --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/cat-stdin @@ -0,0 +1,3 @@ +#!/bin/sh + +cat /dev/stdin diff --git a/pigeonhole/tests/plugins/extprograms/bin/crlf b/pigeonhole/tests/plugins/extprograms/bin/crlf new file mode 100755 index 0000000..a0028cf --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/crlf @@ -0,0 +1,3 @@ +#!/bin/sh + +tr -s '\r' '#' diff --git a/pigeonhole/tests/plugins/extprograms/bin/env b/pigeonhole/tests/plugins/extprograms/bin/env new file mode 100755 index 0000000..a7b81ac --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/env @@ -0,0 +1,3 @@ +#!/bin/sh + +eval echo -n "\${$1}" diff --git a/pigeonhole/tests/plugins/extprograms/bin/frame b/pigeonhole/tests/plugins/extprograms/bin/frame new file mode 100755 index 0000000..225005e --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/frame @@ -0,0 +1,7 @@ +#!/bin/sh + +echo -n "FRAMED $1{ " +cat +echo -n " }" + +exit 0 diff --git a/pigeonhole/tests/plugins/extprograms/bin/modify b/pigeonhole/tests/plugins/extprograms/bin/modify new file mode 100755 index 0000000..ce87014 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/modify @@ -0,0 +1,8 @@ +#!/bin/sh + +echo "X-Frop: Extra header" +cat +echo +echo "Extra body content!" + +exit 0 diff --git a/pigeonhole/tests/plugins/extprograms/bin/program b/pigeonhole/tests/plugins/extprograms/bin/program new file mode 100755 index 0000000..4b5edbf --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/program @@ -0,0 +1,5 @@ +#!/bin/sh + +cat > /dev/null + +exit 0 diff --git a/pigeonhole/tests/plugins/extprograms/bin/replace b/pigeonhole/tests/plugins/extprograms/bin/replace new file mode 100755 index 0000000..b010f06 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/replace @@ -0,0 +1,12 @@ +#!/bin/sh + +cat > /dev/null + +echo "From: hatseflat@example.com" +echo "To: frutsel@example.org" +echo "Subject: replacement message" +echo +echo "Replaced!" + + +exit 0 diff --git a/pigeonhole/tests/plugins/extprograms/bin/sleep10 b/pigeonhole/tests/plugins/extprograms/bin/sleep10 new file mode 100755 index 0000000..8c1b96d --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/sleep10 @@ -0,0 +1,3 @@ +#!/bin/sh + +sleep 10 diff --git a/pigeonhole/tests/plugins/extprograms/bin/sleep2 b/pigeonhole/tests/plugins/extprograms/bin/sleep2 new file mode 100755 index 0000000..a814acd --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/sleep2 @@ -0,0 +1,3 @@ +#!/bin/sh + +sleep 2 diff --git a/pigeonhole/tests/plugins/extprograms/bin/spamc b/pigeonhole/tests/plugins/extprograms/bin/spamc new file mode 100755 index 0000000..a3232f4 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/spamc @@ -0,0 +1,6 @@ +#!/bin/sh + +echo 'X-Spam-Status: Yes, score=66.5/5.0 tests=CONTAINS_LARGE_ROOSTER' +cat + +exit 0 diff --git a/pigeonhole/tests/plugins/extprograms/bin/stderr b/pigeonhole/tests/plugins/extprograms/bin/stderr new file mode 100755 index 0000000..75b94b0 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/bin/stderr @@ -0,0 +1,20 @@ +#!/bin/sh + +echo "========================================" 1>&2 +echo "Test shell script successfully executed!" 1>&2 +echo 1>&2 +echo "Arguments: $1 $2" 1>&2 +echo 1>&2 +echo "Environment:" 1>&2 +env 1>&2 +echo 1>&2 +echo "Message:" 1>&2 +cat 1>&2 +echo "========================================" 1>&2 +echo 1>&2 + +echo "Subject: frop!" +echo "From: stephan@example.org" +echo "To: tss@example.com" +echo +echo "Frop!" diff --git a/pigeonhole/tests/plugins/extprograms/errors.svtest b/pigeonhole/tests/plugins/extprograms/errors.svtest new file mode 100644 index 0000000..148f4da --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/errors.svtest @@ -0,0 +1,32 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Invalid program names + */ + +test "Invalid Program Names" { + if test_script_compile "errors/programname.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Invalid arguments + */ + +test "Invalid Arguments" { + if test_script_compile "errors/arguments.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/plugins/extprograms/errors/arguments.sieve b/pigeonhole/tests/plugins/extprograms/errors/arguments.sieve new file mode 100644 index 0000000..04f0aa0 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/errors/arguments.sieve @@ -0,0 +1,5 @@ +require "vnd.dovecot.pipe"; + +pipe :args "aaaa + aaaa" "frop"; + diff --git a/pigeonhole/tests/plugins/extprograms/errors/programname.sieve b/pigeonhole/tests/plugins/extprograms/errors/programname.sieve new file mode 100644 index 0000000..1d2d19c --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/errors/programname.sieve @@ -0,0 +1,25 @@ +require "variables"; +require "encoded-character"; +require "vnd.dovecot.pipe"; + +# Slash +pipe "../frop"; + +# More slashes +pipe "../../james/sieve/vacation"; + +# 0000-001F; [CONTROL CHARACTERS] +pipe "idiotic${unicode: 001a}"; + +# 007F; DELETE +pipe "idiotic${unicode: 007f}"; + +# 0080-009F; [CONTROL CHARACTERS] +pipe "idiotic${unicode: 0085}"; + +# 2028; LINE SEPARATOR +pipe "idiotic${unicode: 2028}"; + +# 2029; PARAGRAPH SEPARATOR +pipe "idiotic${unicode: 2029}"; + diff --git a/pigeonhole/tests/plugins/extprograms/execute/command.svtest b/pigeonhole/tests/plugins/extprograms/execute/command.svtest new file mode 100644 index 0000000..92c1fd1 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/execute/command.svtest @@ -0,0 +1,27 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.execute"; +require "variables"; + +test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.execute"; + +test "Basic" { + execute "program"; +} + +test "Input message" { + execute :pipe "program"; +} + +test "Input string" { + execute :input "DATA" "program"; +} + +test "Input variable" { + set "DATA" "DATA"; + execute :input "${DATA}" "program"; +} + +test "Output variable" { + execute :output "DATA" "program"; +} diff --git a/pigeonhole/tests/plugins/extprograms/execute/errors.svtest b/pigeonhole/tests/plugins/extprograms/execute/errors.svtest new file mode 100644 index 0000000..3dd2d5f --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/execute/errors.svtest @@ -0,0 +1,53 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.execute"; + +/* + * Command syntax + */ + +test "Command syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "13" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Variables + */ + +test "Variables" { + if test_script_compile "errors/variables.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Unknown program + */ + +test "Unknown program" { + if not test_script_compile "errors/unknown-program.sieve" { + test_fail "compile should have succeeded"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/plugins/extprograms/execute/errors/syntax.sieve b/pigeonhole/tests/plugins/extprograms/execute/errors/syntax.sieve new file mode 100644 index 0000000..1f4646a --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/execute/errors/syntax.sieve @@ -0,0 +1,38 @@ +require "vnd.dovecot.execute"; + +# 1: error: no arguments +execute; + +# 2: error: numeric argument +execute 1; + +# 3: error: tag argument +execute :frop; + +# 4: error: numeric second argument +execute "sdfd" 1; + +# 5: error: stringlist first argument +execute ["sdfd","werwe"] "sdfs"; + +# 6: error: too many arguments +execute "sdfs" "sdfd" "werwe"; + +# 7: error: inappropriate :copy argument +execute :copy "234234" ["324234", "23423"]; + +# 8: error: invalid :input argument; missing parameter +execute :input "frop"; + +# 9: error: invalid :input argument; invalid parameter +execute :input 1 "frop"; + +# 10: error: invalid :input argument; invalid parameter +execute :input ["23423","21342"] "frop"; + +# 11: error: invalid :input argument; invalid parameter +execute :input :friep "frop"; + +# 12: error: :output not allowed without variables extension +execute :output "${frop}" "frop"; + diff --git a/pigeonhole/tests/plugins/extprograms/execute/errors/unknown-program.sieve b/pigeonhole/tests/plugins/extprograms/execute/errors/unknown-program.sieve new file mode 100644 index 0000000..3a79bb6 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/execute/errors/unknown-program.sieve @@ -0,0 +1,3 @@ +require "vnd.dovecot.execute"; + +execute "unknown"; diff --git a/pigeonhole/tests/plugins/extprograms/execute/errors/variables.sieve b/pigeonhole/tests/plugins/extprograms/execute/errors/variables.sieve new file mode 100644 index 0000000..3d0b3e7 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/execute/errors/variables.sieve @@ -0,0 +1,7 @@ +require "vnd.dovecot.execute"; +require "variables"; + +# 1: invalid variable name +execute :output "wqwe-aeqwe" "frop"; + + diff --git a/pigeonhole/tests/plugins/extprograms/execute/execute.svtest b/pigeonhole/tests/plugins/extprograms/execute/execute.svtest new file mode 100644 index 0000000..f16af11 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/execute/execute.svtest @@ -0,0 +1,177 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.execute"; +require "vnd.dovecot.debug"; +require "variables"; +require "relational"; +require "environment"; +require "encoded-character"; + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_execute_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.execute"; +test_result_reset; + +test "Execute - bare" { + execute "program"; +} + +test_result_reset; +test "Execute - i/-" { + execute :input "FROP" "frame"; +} + +test_result_reset; +test "Execute - -/o" { + execute :output "out" "frame"; + + if not string "${out}" "FRAMED { }" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - i/o" { + execute :input "FROP" :output "out" "frame"; + + if not string "${out}" "FRAMED { FROP }" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - i/o and arguments" { + execute :input "FROP" :output "out" "frame" ["FRIEP "]; + + if not string "${out}" "FRAMED FRIEP { FROP }" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - pipe" { + execute :pipe :output "msg" "cat"; + + if not string :contains "${msg}" "Subject: Frop!" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - pipe /dev/stdin" { + execute :pipe :output "msg" "cat-stdin"; + + if not string :contains "${msg}" "Subject: Frop!" { + test_fail "wrong string returned: ${out}"; + } +} + +test_result_reset; +test "Execute - env" { + test_set "envelope.from" "stephan@sub.example.com"; + test_set "envelope.to" "stephan@sub.example.net"; + test_set "envelope.orig_to" "all@sub.example.net"; + + execute :output "out" "env" "SENDER"; + if not string :is "${out}" "stephan@sub.example.com" { + test_fail "wrong SENDER env returned: '${out}'"; + } + + execute :output "out" "env" "RECIPIENT"; + if not string :is "${out}" "stephan@sub.example.net" { + test_fail "wrong RECIPIENT env returned: '${out}'"; + } + + execute :output "out" "env" "ORIG_RECIPIENT"; + if not string :is "${out}" "all@sub.example.net" { + test_fail "wrong ORIG_RECIPIENT env returned: '${out}'"; + } + + execute :output "out" "env" "HOST"; + if not environment :is "host" "${out}" { + test_fail "wrong HOST env returned: '${out}'"; + } + + execute :output "out" "env" "HOME"; + if string :count "eq" "${out}" "0" { + test_fail "empty HOME env returned"; + } + + execute :output "out" "env" "USER"; + if string :count "eq" "${out}" "0" { + test_fail "empty USER env returned"; + } +} + +test_result_reset; +test "Execute - used as test" { + if execute :pipe :output "msg" "dog" { + test_fail "execute action indicated success with invalid program"; + } + + if not execute :pipe :output "msg" "cat" { + test_fail "execute action indicated failure with valid program"; + } + + if not string :contains "${msg}" "Subject: Frop!" { + test_fail "wrong string returned: ${out}"; + } +} + +test_config_set "sieve_execute_input_eol" "crlf"; +test_config_reload :extension "vnd.dovecot.execute"; +test_result_reset; +set "out" ""; + +test "Execute - CRLF" { + execute + :input "FROP${hex:0A}FRIEP${hex:0a}" + :output "out" + "crlf"; + + if not string "${out}" "FROP#${hex:0A}FRIEP#${hex:0a}" { + test_fail "wrong string returned: '${out}'"; + } +} + +test_config_set "sieve_execute_input_eol" "lf"; +test_config_reload :extension "vnd.dovecot.execute"; +test_result_reset; +set "out" ""; + +test "Execute - LF" { + execute + :input "FROP${hex:0D 0A}FRIEP${hex:0d 0a}" + :output "out" + "crlf"; + + if not string "${out}" "FROP${hex:0A}FRIEP${hex:0a}" { + test_fail "wrong string returned: '${out}'"; + } +} + +set "D" "0123456701234567012345670123456701234567012345670123456701234567"; +set "D" "${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}${D}"; +set "data" "${D}${D}"; + +test_config_set "sieve_execute_input_eol" "crlf"; +test_config_reload :extension "vnd.dovecot.execute"; +test_result_reset; +set "out" ""; + +test "Execute - big" { + execute + :output "out" + "big"; + + if not string "${out}" "${data}" { + test_fail "wrong string returned: '${out}'"; + } +} diff --git a/pigeonhole/tests/plugins/extprograms/filter/command.svtest b/pigeonhole/tests/plugins/extprograms/filter/command.svtest new file mode 100644 index 0000000..50f949a --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/filter/command.svtest @@ -0,0 +1,10 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.filter"; +require "variables"; + +test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.filter"; + +test "Basic" { + filter "program"; +} diff --git a/pigeonhole/tests/plugins/extprograms/filter/errors.svtest b/pigeonhole/tests/plugins/extprograms/filter/errors.svtest new file mode 100644 index 0000000..1d04ba1 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/filter/errors.svtest @@ -0,0 +1,39 @@ +require "vnd.dovecot.testsuite"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Command syntax + */ + +test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.filter"; + +test "Command syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + +/* + * Unknown program + */ + +test "Unknown program" { + if not test_script_compile "errors/unknown-program.sieve" { + test_fail "compile should have succeeded"; + } + + if test_script_run { + test_fail "execution should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } +} diff --git a/pigeonhole/tests/plugins/extprograms/filter/errors/syntax.sieve b/pigeonhole/tests/plugins/extprograms/filter/errors/syntax.sieve new file mode 100644 index 0000000..00a3a23 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/filter/errors/syntax.sieve @@ -0,0 +1,22 @@ +require "vnd.dovecot.filter"; + +# 1: error: no arguments +filter; + +# 2: error: numeric argument +filter 1; + +# 3: error: tag argument +filter :frop; + +# 4: error: numeric second argument +filter "sdfd" 1; + +# 5: error: stringlist first argument +filter ["sdfd","werwe"] "sdfs"; + +# 6: error: too many arguments +filter "sdfd" "werwe" "sdfs"; + +# 7: error: inappropriate :copy argument +filter :try :copy "234234" ["324234", "23423"]; diff --git a/pigeonhole/tests/plugins/extprograms/filter/errors/unknown-program.sieve b/pigeonhole/tests/plugins/extprograms/filter/errors/unknown-program.sieve new file mode 100644 index 0000000..7e530ee --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/filter/errors/unknown-program.sieve @@ -0,0 +1,3 @@ +require "vnd.dovecot.filter"; + +filter "unknown"; diff --git a/pigeonhole/tests/plugins/extprograms/filter/execute.svtest b/pigeonhole/tests/plugins/extprograms/filter/execute.svtest new file mode 100644 index 0000000..15fab69 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/filter/execute.svtest @@ -0,0 +1,213 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.filter"; +require "vnd.dovecot.debug"; +require "variables"; +require "editheader"; +require "spamtest"; +require "body"; +require "fileinto"; +require "mailbox"; + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_filter_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.filter"; +test_result_reset; + +test_result_reset; +test "Replace" { + if header :contains "subject" "replacement" { + test_fail "message already replaced"; + } + + filter "replace"; + + if not header :contains "subject" "replacement" { + test_fail "message not replaced"; + } +} + +test_result_reset; +test "Used as test" { + if filter "nonsense" { + test_fail "filter action indicated success with invalid program"; + } + + if not filter "replace" { + test_fail "filter action indicated failure with valid program"; + } + + if not header :contains "subject" "replacement" { + test_fail "message not replaced; filter not actually executed"; + } +} + +test_result_reset; +test "Modify" { + if anyof ( + body :contains "extra", + exists "x-frop") { + test_fail "message already modified"; + } + + if not header "subject" "Frop!" { + test_fail "message is wrong"; + } + + filter "modify"; + + if not header "subject" "Frop!" { + test_fail "message replaced erroneously"; + } + + if not header :contains "x-frop" "extra" { + test_fail "message header not modified"; + } + + if not body :contains "Extra" { + test_fail "message body not modified"; + } +} + +test_result_reset; +test "Editheader" { + if anyof ( exists "X-A", exists "X-B", exists "X-C", exists "X-D", + exists "X-E") { + test_fail "message already modified"; + } + + addheader "X-A" "1"; + if not header "X-A" "1" { + test_fail "X-A header missing"; + } + + fileinto :create "A"; + + filter "addheader" ["X-B", "2"]; + if not header "X-B" "2" { + test_fail "X-B header missing"; + } + + fileinto :create "B"; + + addheader "X-C" "3"; + if not header "X-C" "3" { + test_fail "X-C header missing"; + } + + fileinto :create "C"; + + filter "addheader" ["X-D", "4"]; + if not header "X-D" "4" { + test_fail "X-D header missing"; + } + + fileinto :create "D"; + + addheader "X-E" "5"; + if not header "X-E" "5" { + test_fail "X-E header missing"; + } + + fileinto :create "E"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + test_message :folder "A" 0; + + if not header "X-A" "1" { + test_fail "X-A header missing"; + } + if anyof ( + header "X-B" "2", header "X-C" "3", + header "X-D" "4", header "X-E" "5") { + test_fail "X-B, X-C, X-D or X-E header found"; + } + + test_message :folder "B" 0; + + if not header "X-B" "2" { + test_fail "X-B header missing"; + } + if anyof ( + header "X-C" "3", header "X-D" "4", header "X-E" "5") { + test_fail "X-C, X-D or X-E header found"; + } + + test_message :folder "C" 0; + + if not header "X-C" "3" { + test_fail "X-C header missing"; + } + if anyof (header "X-D" "4", header "X-E" "5") { + test_fail "X-D or X-E header found"; + } + + test_message :folder "D" 0; + + if not header "X-D" "4" { + test_fail "X-D header missing"; + } + if anyof (header "X-E" "5") { + test_fail "X-E header found"; + } + + test_message :folder "E" 0; + + if not header "X-A" "1" { + test_fail "X-A header missing in final message"; + } + if not header "X-B" "2" { + test_fail "X-B header missing in final message"; + } + if not header "X-C" "3" { + test_fail "X-C header missing in final message"; + } + if not header "X-D" "4" { + test_fail "X-D header missing in final message"; + } + if not header "X-E" "5" { + test_fail "X-E header missing in final message"; + } +} + +test_config_set "sieve_spamtest_status_header" + "X-Spam-Status: [^,]*, score=(-?[[:digit:]]+\\.[[:digit:]]).*"; +test_config_set "sieve_spamtest_max_value" "10"; +test_config_set "sieve_spamtest_status_type" "score"; +test_config_reload :extension "spamtest"; + +test_result_reset; +test "Spamtest" { + if exists "x-spam-status" { + test_fail "message already modified"; + } + + if not header "subject" "Frop!" { + test_fail "message is wrong"; + } + + filter "spamc"; + + if not exists "x-spam-status" { + test_fail "x-spam-score header not added"; + } + + if spamtest :is "0" { + test_fail "spamtest not configured or test failed"; + } + + if not spamtest :is "10" { + test_fail "spamtest yields incorrect value"; + } +} + diff --git a/pigeonhole/tests/plugins/extprograms/pipe/command.svtest b/pigeonhole/tests/plugins/extprograms/pipe/command.svtest new file mode 100644 index 0000000..dabd970 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/pipe/command.svtest @@ -0,0 +1,10 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.pipe"; + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.pipe"; + +test "Basic" { + pipe "program"; +} + diff --git a/pigeonhole/tests/plugins/extprograms/pipe/errors.svtest b/pigeonhole/tests/plugins/extprograms/pipe/errors.svtest new file mode 100644 index 0000000..af36b91 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/pipe/errors.svtest @@ -0,0 +1,94 @@ +require "vnd.dovecot.testsuite"; +require "variables"; + +require "relational"; +require "comparator-i;ascii-numeric"; + +/* + * Command syntax + */ + +test "Command syntax" { + if test_script_compile "errors/syntax.sieve" { + test_fail "compile should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + +/* Unknown program */ + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Unknown program" { + if not test_script_compile "errors/unknown-program.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execute failed"; + } + + if test_result_execute { + test_fail "pipe should have failed"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "1" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 1 :contains "failed to pipe" { + test_fail "wrong error reported"; + } +} + +/* Timeout */ + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_set "sieve_pipe_exec_timeout" "1s"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Timeout" { + if not test_script_compile "errors/timeout.sieve" { + test_fail "compile failed"; + } + + if not test_script_run { + test_fail "execute failed"; + } + + if test_result_execute { + test_fail "pipe should have timed out"; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { + test_fail "wrong number of errors reported"; + } + + if not test_error :index 2 :contains "failed to pipe" { + test_fail "wrong error reported"; + } +} diff --git a/pigeonhole/tests/plugins/extprograms/pipe/errors/syntax.sieve b/pigeonhole/tests/plugins/extprograms/pipe/errors/syntax.sieve new file mode 100644 index 0000000..64d5310 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/pipe/errors/syntax.sieve @@ -0,0 +1,22 @@ +require "vnd.dovecot.pipe"; + +# 1: error: no arguments +pipe; + +# 2: error: numeric argument +pipe 1; + +# 3: error: tag argument +pipe :frop; + +# 4: error: numeric second argument +pipe "sdfd" 1; + +# 5: error: stringlist first argument +pipe ["sdfd","werwe"] "sdfs"; + +# 6: error: too many arguments +pipe "sdfd" "werwe" "sdfs"; + +# 7: error: inappropriate :copy argument +pipe :try :copy "234234" ["324234", "23423"]; diff --git a/pigeonhole/tests/plugins/extprograms/pipe/errors/timeout.sieve b/pigeonhole/tests/plugins/extprograms/pipe/errors/timeout.sieve new file mode 100644 index 0000000..7a940c8 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/pipe/errors/timeout.sieve @@ -0,0 +1,3 @@ +require "vnd.dovecot.pipe"; + +pipe "sleep10"; diff --git a/pigeonhole/tests/plugins/extprograms/pipe/errors/unknown-program.sieve b/pigeonhole/tests/plugins/extprograms/pipe/errors/unknown-program.sieve new file mode 100644 index 0000000..fd6338b --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/pipe/errors/unknown-program.sieve @@ -0,0 +1,3 @@ +require "vnd.dovecot.pipe"; + +pipe "unknown"; diff --git a/pigeonhole/tests/plugins/extprograms/pipe/execute.svtest b/pigeonhole/tests/plugins/extprograms/pipe/execute.svtest new file mode 100644 index 0000000..34b6798 --- /dev/null +++ b/pigeonhole/tests/plugins/extprograms/pipe/execute.svtest @@ -0,0 +1,56 @@ +require "vnd.dovecot.testsuite"; +require "vnd.dovecot.pipe"; +require "vnd.dovecot.debug"; +require "variables"; + +test_set "message" text: +From: stephan@example.com +To: pipe@example.net +Subject: Frop! + +Frop! +. +; + +/* Basic pipe */ + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Pipe" { + pipe "stderr" ["ONE", "TWO"]; + + if not test_result_execute { + test_fail "failed to pipe message to script"; + } +} + +/* Timeout */ + +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_set "sieve_pipe_exec_timeout" "3s"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Timeout 3s" { + pipe "sleep2"; + + if not test_result_execute { + test_fail "failed to pipe message to script"; + } +} + +test_result_reset; +test_config_set "sieve_pipe_bin_dir" "${tst.path}/../bin"; +test_config_set "sieve_pipe_exec_timeout" "0"; +test_config_reload :extension "vnd.dovecot.pipe"; +test_result_reset; + +test "Timeout infinite" { + pipe "sleep2"; + + if not test_result_execute { + test_fail "failed to pipe message to script"; + } +} diff --git a/pigeonhole/tests/test-address.svtest b/pigeonhole/tests/test-address.svtest new file mode 100644 index 0000000..135d549 --- /dev/null +++ b/pigeonhole/tests/test-address.svtest @@ -0,0 +1,434 @@ +require "vnd.dovecot.testsuite"; + +/* + * ## RFC 5228, Section 5.1. Test address (page 26) ## + */ + +/* + * TEST: Basic functionionality + */ + +/* "The "address" test matches Internet addresses in structured headers + * that contain addresses. It returns true if any header contains any + * key in the specified part of the address, as modified by the + * comparator and the match keyword. Whether there are other addresses + * present in the header doesn't affect this test; this test does not + * provide any way to determine whether an address is the only address + * in a header. + * + * Like envelope and header, this test returns true if any combination + * of the header-list and key-list arguments match and returns false + * otherwise. + * " + */ + +test_set "message" text: +From: stephan@example.com +To: nico@nl.example.com, harry@de.example.com +cc: Timo <tss(no spam)@fi.iki> +Subject: Frobnitzm + +Test. +. +; + +test "Basic functionality" { + /* Must match */ + if not address :contains ["to", "from"] "harry" { + test_fail "failed to match address (1)"; + } + + if not address :contains ["to", "from"] "de.example" { + test_fail "failed to match address (2)"; + } + + if not address :matches "to" "*@*.example.com" { + test_fail "failed to match address (3)"; + } + + if not address :is "to" "harry@de.example.com" { + test_fail "failed to match address (4)"; + } + + /* Must not match */ + if address :is ["to", "from"] "nonsense@example.com" { + test_fail "matches erroneous address"; + } + + /* Match first key */ + if not address :contains ["to"] ["nico", "fred", "henk"] { + test_fail "failed to match first key"; + } + + /* Match second key */ + if not address :contains ["to"] ["fred", "nico", "henk"] { + test_fail "failed to match second key"; + } + + /* Match last key */ + if not address :contains ["to"] ["fred", "henk", "nico"] { + test_fail "failed to match last key"; + } + + /* First header */ + if not address :contains ["to", "from"] ["fred", "nico", "henk"] { + test_fail "failed to match first header"; + } + + /* Second header */ + if not address :contains ["from", "to"] ["fred", "nico", "henk"] { + test_fail "failed to match second header"; + } + + /* Comment */ + if not address :is "cc" "tss@fi.iki" { + test_fail "failed to ignore comment in address"; + } +} + +/* + * TEST: Case-sensitivity + */ + +/* "Internet email addresses [RFC 2822] have the somewhat awkward characteristic + * that the local-part to the left of the at-sign is considered case sensitive, + * and the domain-part to the right of the at-sign is case insensitive. The + * "address" command does not deal with this itself, but provides the + * ADDRESS-PART argument for allowing users to deal with it. + * " + */ + +test_set "message" text: +From: stephan@example.com +To: Nico@nl.example.com, harry@DE.EXAMPLE.COM +Subject: Case-sensitivity + +Test. +. +; + + +test "Case-sensitivity" { + /* Default: i;ascii-casemap */ + + if not address :is ["to", "from"] "nico@nl.example.com" { + test_fail "address comparator is i;octet by default (1)"; + } + + if not address :is ["to", "from"] "harry@de.example.com" { + test_fail "address comparator is i;octet by default (2)"; + } + + if not address :is ["to", "from"] "STEPHAN@example.com" { + test_fail "address comparator is i;octet by default (3)"; + } + + if not address :is :localpart ["to"] "nico" { + test_fail "address comparator is i;octet by default (4)"; + } + + /* Match case-sensitively */ + + if not address :is :comparator "i;octet" ["to"] "Nico@nl.example.com" { + test_fail "failed to match case-sensitive address (1)"; + } + + if not address :is :comparator "i;octet" ["to"] "harry@DE.EXAMPLE.COM" { + test_fail "failed to match case-sensitive address (2)"; + } + + if address :is :comparator "i;octet" ["to"] "harry@de.example.com" { + test_fail "failed to notice case difference in address with i;octet (1)"; + } + + if address :is :comparator "i;octet" ["from"] "STEPHAN@example.com" { + test_fail "failed to notice case difference in address with i;octet (2)"; + } + + if not address :is :localpart :comparator "i;octet" ["to"] "Nico" { + test_fail "failed to match case-sensitive localpart"; + } + + if address :is :localpart :comparator "i;octet" ["to"] "nico" { + test_fail "failed to notice case difference in local_part with i;octet"; + } + + if not address :is :domain :comparator "i;octet" ["to"] "DE.EXAMPLE.COM" { + test_fail "failed to match case-sensitive localpart"; + } + + if address :is :domain :comparator "i;octet" ["to"] "de.example.com" { + test_fail "failed to notice case difference in domain with i;octet"; + } +} + +/* + * TEST: Phrase part, comments and group names + */ + +/* "The address primitive never acts on the phrase part of an email + * address or on comments within that address. It also never acts on + * group names, ... + * " + */ + +test_set "message" text: +From: Stephan Bosch <stephan(the author)@example.com> +To: Nico Thalens <nico@nl.example.com>, Harry Becker <harry@de.example.com> +cc: tukkers: henk@tukkerland.ex, theo@tukkerland.ex, frits@tukkerland.ex; +Subject: Frobnitzm + +Test. +. +; + +test "Phrase part, comments and group names" { + if address :contains :all :comparator "i;ascii-casemap" + ["to","from"] ["Bosch", "Thalens", "Becker"] { + test_fail "matched phrase part"; + } + + if address :contains :all :comparator "i;ascii-casemap" "from" "author" { + test_fail "matched comment"; + } + + + if address :contains :all :comparator "i;ascii-casemap" ["cc"] ["tukkers"] { + test_fail "matched group name"; + } +} + + +/* + * TEST: Group addresses + */ + +/* "... although it does act on the addresses within the group + * construct. + * " + */ + +test_set "message" text: +From: stephan@friep.frop +To: undisclosed-recipients:; +cc: tukkers: henk@tukkerland.ex, theo@tukkerland.ex, frits@tukkerland.ex; +Subject: Invalid addresses + +Test. +. +; + +test "Group addresses" { + if not address :is :domain ["cc"] ["tukkerland.ex"] { + test_fail "failed to match group address (1)"; + } + + if not address :is :localpart ["cc"] ["henk"] { + test_fail "failed to match group address (2)"; + } + + if not address :is :localpart ["cc"] ["theo"] { + test_fail "failed to match group address (3)"; + } + + if not address :is :localpart ["cc"] ["frits"] { + test_fail "failed to match group address (4)"; + } +} + +/* + * TEST: Address headers + */ + +/* "Implementations MUST restrict the address test to headers that + * contain addresses, but MUST include at least From, To, Cc, Bcc, + * Sender, Resent-From, and Resent-To, and it SHOULD include any other + * header that utilizes an "address-list" structured header body. + * " + */ + +test_set "message" text: +From: stephan@friep.frop +To: henk@tukkerland.ex +CC: ivo@boer.ex +Bcc: joop@hooibaal.ex +Sender: s.bosch@friep.frop +Resent-From: ivo@boer.ex +Resent-To: idioot@dombo.ex +Subject: Berichtje + +Test. +. +; + + +test "Address headers" { + if not address "from" "stephan@friep.frop" { + test_fail "from header not recognized"; + } + + if not address "to" "henk@tukkerland.ex" { + test_fail "to header not recognized"; + } + + if not address "cc" "ivo@boer.ex" { + test_fail "cc header not recognized"; + } + + if not address "bcc" "joop@hooibaal.ex" { + test_fail "bcc header not recognized"; + } + + if not address "sender" "s.bosch@friep.frop" { + test_fail "sender header not recognized"; + } + + if not address "resent-from" "ivo@boer.ex" { + test_fail "resent-from header not recognized"; + } + + if not address "resent-to" "idioot@dombo.ex" { + test_fail "resent-to header not recognized"; + } +} + +/* ## RFC 5228, Section 2.7.4. Comparisons against Addresses (page 16) ## */ + +/* + * TEST: Invalid addresses + */ + +/* + * "If an address is not syntactically valid, then it will not be matched + * by tests specifying ":localpart" or ":domain". + * " + */ + +test_set "message" text: +From: stephan@ +To: @example.org +Cc: nonsense +Resent-To: +Bcc: nico@frop.example.com, @example.org +Resent-Cc:<jürgen@example.com> +Subject: Invalid addresses + +Test. +. +; + +test "Invalid addresses" { + if address :localpart "from" "stephan" { + test_fail ":localpart matched invalid address"; + } + + if address :localpart "resent-cc" "jürgen" { + test_fail ":localpart matched invalid UTF-8 address"; + } + + if address :domain "to" "example.org" { + test_fail ":domain matched invalid address"; + } + + if address :domain "resent-cc" "example.com" { + test_fail ":domain matched invalid UTF-8 address"; + } + + if not address :is :all "resent-to" "" { + test_fail ":all failed to match empty address"; + } + + if not address :is :all "cc" "nonsense" { + test_fail ":all failed to match invalid address"; + } + + if not address :is :all "resent-cc" "<jürgen@example.com>" { + test_fail ":all failed to match invalid UTF-8 address"; + } + + if address :is :localpart "bcc" "" { + test_fail ":localpart matched invalid address"; + } + + if address :is :domain "cc" "example.org" { + test_fail ":domain matched invalid address"; + } +} + +/* + * TEST: Default address part + */ + +/* "If an optional address-part is omitted, the default is ":all". + * " + */ + +test_set "message" text: +From: stephan@example.com +To: nico@nl.example.com, harry@de.example.com +Subject: Frobnitzm + +Test. +. +; + +test "Default address part" { + if not address :is :comparator "i;ascii-casemap" "from" "stephan@example.com" + { + test_fail "invalid default address part (1)"; + } + + if not address :is :comparator "i;ascii-casemap" "to" + ["harry@de.example.com"] { + test_fail "invalid default address part (2)"; + } +} + +/* + * TEST: Mime encoding of '@' in display name + */ + +test_set "message" text: +From: "Frop <frop@example.org>" +To: =?UTF-8?B?RnJpZXBAZnJvcA0K?= + <friep@example.com> +Subject: Test + +Frop! +. +; + + +test "Mime encoding of '@' in display name" { + # Relevant sieve rule: + + if not address :is "To" + ["friep@example.com"] { + test_fail "Invalid address extracted"; + } +} + +/* + * TEST: Erroneous mime encoding + */ + +test_set "message" text: +From: "William Wallace <william@scotsmen.ex>" +To: "=?UTF-8?B?IkR1bWIgTWFpbGVyIg==?=" + <horde@lists.scotsmen.ex> +Subject: Test + +Frop! +. +; + + +test "Erroneous mime encoding" { + # Relevant sieve rule: + + if not address :is ["To","CC"] ["horde@lists.scotsmen.ex","archers@lists.scotsmen.ex"] { + test_fail "Failed to match improperly encoded address headers"; + } +} + + diff --git a/pigeonhole/tests/test-allof.svtest b/pigeonhole/tests/test-allof.svtest new file mode 100644 index 0000000..1ebef67 --- /dev/null +++ b/pigeonhole/tests/test-allof.svtest @@ -0,0 +1,446 @@ +require "vnd.dovecot.testsuite"; + +/* + * ## RFC 5228, Section 5.2. Test allof (page 27) ## + */ + +/* "The "allof" test performs a logical AND on the tests supplied to it. + * + * Example: allof (false, false) => false + * allof (false, true) => false + * allof (true, true) => true + * + * The allof test takes as its argument a test-list. + * " + */ + +test_set "message" text: +From: stephan@example.org +To: test@dovecot.example.net +cc: stephan@idiot.ex +Subject: Test + +Test! +. +; + +/* + * TEST: Basic functionality: static + */ + +test "Basic functionality: static" { + if allof ( true ) { + /* Correct */ + } else { + test_fail "chose wrong single outcome: false"; + } + + if allof ( false ) { + test_fail "chose wrong single outcome: true"; + } else { + /* Correct */ + } + + if allof ( true, true, true ) { + /* Correct */ + } else { + test_fail "chose wrong all-true outcome: false"; + } + + if allof ( false, false, false ) { + test_fail "chose wrong all-false outcome: true"; + } else { + /* Correct */ + } + + if allof ( true, false, false ) { + test_fail "chose wrong first-true outcome: true"; + } else { + /* Correct */ + } + + if allof ( false, true, false ) { + test_fail "chose wrong second-true outcome: true"; + } else { + /* Correct */ + } + + if allof ( false, false, true ) { + test_fail "chose wrong last-true outcome: true"; + } else { + /* Correct */ + } + + if allof ( false, true, true ) { + test_fail "chose wrong first-false outcome: true"; + } else { + /* Correct */ + } + + if allof ( true, false, true ) { + test_fail "chose wrong second-false outcome: true"; + } else { + /* Correct */ + } + + if allof ( true, true, false ) { + test_fail "chose wrong last-false outcome: true"; + } else { + /* Correct */ + } +} + +/* + * TEST: Basic functionality: dynamic + */ + +test "Basic functionality: dynamic" { + if allof ( exists "from" ) { + /* Correct */ + } else { + test_fail "chose wrong single outcome: false"; + } + + if allof ( exists "friep" ) { + test_fail "chose wrong single outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "from", exists "to", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong all-true outcome: false"; + } + + if allof ( exists "friep", exists "frop", exists "frml" ) { + test_fail "chose wrong all-false outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", exists "frop", exists "frml" ) { + test_fail "chose wrong first-true outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "from", exists "frml" ) { + test_fail "chose wrong second-true outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "frop", exists "cc" ) { + test_fail "chose wrong last-true outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "from", exists "cc" ) { + test_fail "chose wrong first-false outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", exists "frop", exists "cc" ) { + test_fail "chose wrong second-false outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", exists "from", exists "frml" ) { + test_fail "chose wrong last-false outcome: true"; + } else { + /* Correct */ + } +} + +/* + * TEST: Basic functionality: static/dynamic + */ + +test "Basic functionality: static/dynamic" { + /* All true */ + + if allof ( true, exists "to", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong all-true first-static outcome: false"; + } + + if allof ( exists "from", true, exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong all-true second-static outcome: false"; + } + + if allof ( exists "from", exists "to", true ) { + /* Correct */ + } else { + test_fail "chose wrong all-true third-static outcome: false"; + } + + /* All false */ + + if allof ( false, exists "frop", exists "frml" ) { + test_fail "chose wrong all-false first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", false, exists "frml" ) { + test_fail "chose wrong all-false second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "frop", false ) { + test_fail "chose wrong all-false third-static outcome: true"; + } else { + /* Correct */ + } + + /* First true */ + + if allof ( true, exists "frop", exists "frml" ) { + test_fail "chose wrong first-true first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", false, exists "frml" ) { + test_fail "chose wrong first-true second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", exists "frop", false ) { + test_fail "chose wrong first-true third-static outcome: true"; + } else { + /* Correct */ + } + + /* Second true */ + + if allof ( false, exists "from", exists "frml" ) { + test_fail "chose wrong second-true first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", true, exists "frml" ) { + test_fail "chose wrong second-true second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "from", false ) { + test_fail "chose wrong second-true third-static outcome: true"; + } else { + /* Correct */ + } + + /* Last true */ + + if allof ( false, exists "frop", exists "cc" ) { + test_fail "chose wrong last-true first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", false, exists "cc" ) { + test_fail "chose wrong last-true second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "frop", true ) { + test_fail "chose wrong last-true third-static outcome: true"; + } else { + /* Correct */ + } + + /* First false */ + + if allof ( false, exists "from", exists "cc" ) { + test_fail "chose wrong first-false first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", true, exists "cc" ) { + test_fail "chose wrong first-false second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "friep", exists "from", true ) { + test_fail "chose wrong first-false third-static outcome: true"; + } else { + /* Correct */ + } + + /* Second false */ + + if allof ( true, exists "frop", exists "cc" ) { + test_fail "chose wrong second-false first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", false, exists "cc" ) { + test_fail "chose wrong second-false second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", exists "frop", true ) { + test_fail "chose wrong second-false third-static outcome: true"; + } else { + /* Correct */ + } + + /* Last false */ + + if allof ( true, exists "from", exists "frml" ) { + test_fail "chose wrong last-false first-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", true, exists "frml" ) { + test_fail "chose wrong last-false second-static outcome: true"; + } else { + /* Correct */ + } + + if allof ( exists "to", exists "from", false ) { + test_fail "chose wrong last-false last-static outcome: true"; + } else { + /* Correct */ + } +} + +/* + * TEST: Basic functionality: nesting + */ + +test "Basic functionality: nesting" { + /* Static */ + + if allof ( allof(true, true), allof(true, true) ) { + /* Correct */ + } else { + test_fail "chose wrong static nesting ((true, true),(true,true)) outcome: false"; + } + + if allof ( allof(false, true), allof(true, true) ) { + test_fail "chose wrong static nesting ((false, true),(true,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(true, false), allof(true, true) ) { + test_fail "chose wrong static nesting ((true,false),(true,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(true, true), allof(false, true) ) { + test_fail "chose wrong static nesting ((true, true),(false,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(true, true), allof(true, false) ) { + test_fail "chose wrong static nesting ((true, true),(true,false)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(true, false), allof(true, false) ) { + test_fail "chose wrong static nesting ((true, false),(true,false)) outcome: true"; + } else { + /* Correct */ + } + + /* Dynamic */ + + if allof ( allof(exists "to", exists "from"), allof(exists "cc", exists "subject") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((true, true),(true,true)) outcome: false"; + } + + if allof ( allof(exists "frop", exists "from"), allof(exists "cc", exists "subject") ) { + test_fail "chose wrong dynamic nesting ((false, true),(true,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", exists "friep"), allof(exists "cc", exists "subject") ) { + test_fail "chose wrong dynamic nesting ((true,false),(true,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", exists "from"), allof(exists "frml", exists "subject") ) { + test_fail "chose wrong dynamic nesting ((true, true),(false,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", exists "from"), allof(exists "cc", exists "fruts") ) { + test_fail "chose wrong dynamic nesting ((true, true),(true,false)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", exists "friep"), allof(exists "cc", exists "fruts") ) { + test_fail "chose wrong dynamic nesting ((true, false),(true,false)) outcome: true"; + } else { + /* Correct */ + } + + /* Static/Dynamic */ + + if allof ( allof(exists "to", true), allof(true, exists "subject") ) { + /* Correct */ + } else { + test_fail "chose wrong static/dynamic nesting ((true, true),(true,true)) outcome: false"; + } + + if allof ( allof(false, exists "from"), allof(exists "cc", exists "subject") ) { + test_fail "chose wrong static/dynamic nesting ((false, true),(true,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", false), allof(exists "cc", exists "subject") ) { + test_fail "chose wrong static/dynamic nesting ((true,false),(true,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", exists "from"), allof(false, exists "subject") ) { + test_fail "chose wrong static/dynamic nesting ((true, true),(false,true)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", exists "from"), allof(exists "cc", false) ) { + test_fail "chose wrong static/dynamic nesting ((true, true),(true,false)) outcome: true"; + } else { + /* Correct */ + } + + if allof ( allof(exists "to", false), allof(true, exists "fruts") ) { + test_fail "chose wrong static/dynamic nesting ((true, false),(true,false)) outcome: true"; + } else { + /* Correct */ + } + +} + + diff --git a/pigeonhole/tests/test-anyof.svtest b/pigeonhole/tests/test-anyof.svtest new file mode 100644 index 0000000..77a9c79 --- /dev/null +++ b/pigeonhole/tests/test-anyof.svtest @@ -0,0 +1,445 @@ +require "vnd.dovecot.testsuite"; + +/* + * ## RFC 5228, Section 5.3. Test anyof (page 27) ## + */ + +/* "The "anyof" test performs a logical OR on the tests supplied to it. + * + * Example: anyof (false, false) => false + * anyof (false, true) => true + * anyof (true, true) => true + * " + */ + +test_set "message" text: +From: stephan@example.org +To: test@dovecot.example.net +cc: stephan@idiot.ex +Subject: Test + +Test! +. +; + +/* + * TEST: Basic functionality: static + */ + +test "Basic functionality: static" { + if anyof ( true ) { + /* Correct */ + } else { + test_fail "chose wrong single outcome: false"; + } + + if anyof ( false ) { + test_fail "chose wrong single outcome: true"; + } else { + /* Correct */ + } + + if anyof ( true, true, true ) { + /* Correct */ + } else { + test_fail "chose wrong all-true outcome: false"; + } + + if anyof ( false, false, false ) { + test_fail "chose wrong all-false outcome: true"; + } else { + /* Correct */ + } + + if anyof ( true, false, false ) { + /* Correct */ + } else { + test_fail "chose wrong first-true outcome: false"; + } + + if anyof ( false, true, false ) { + /* Correct */ + } else { + test_fail "chose wrong second-true outcome: false"; + } + + if anyof ( false, false, true ) { + /* Correct */ + } else { + test_fail "chose wrong last-true outcome: false"; + } + + if anyof ( false, true, true ) { + /* Correct */ + } else { + test_fail "chose wrong first-false outcome: false"; + } + + if anyof ( true, false, true ) { + /* Correct */ + } else { + test_fail "chose wrong second-false outcome: false"; + } + + if anyof ( true, true, false ) { + /* Correct */ + } else { + test_fail "chose wrong last-false outcome: false"; + } +} + +/* + * TEST: Basic functionality: dynamic + */ + +test "Basic functionality: dynamic" { + if anyof ( exists "from" ) { + /* Correct */ + } else { + test_fail "chose wrong single outcome: false"; + } + + if anyof ( exists "friep" ) { + test_fail "chose wrong single outcome: true"; + } else { + /* Correct */ + } + + if anyof ( exists "from", exists "to", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong all-true outcome: false"; + } + + if anyof ( exists "friep", exists "frop", exists "frml" ) { + test_fail "chose wrong all-false outcome: true"; + } else { + /* Correct */ + } + + if anyof ( exists "to", exists "frop", exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong first-true outcome: false"; + } + + if anyof ( exists "friep", exists "from", exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong second-true outcome: false"; + } + + if anyof ( exists "friep", exists "frop", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong last-true outcome: false"; + } + + if anyof ( exists "friep", exists "from", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong first-false outcome: false"; + } + + if anyof ( exists "to", exists "frop", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong second-false outcome: false"; + } + + if anyof ( exists "to", exists "from", exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong last-false outcome: false"; + } +} + +/* + * TEST: Basic functionality: static/dynamic + */ + +test "Basic functionality: static/dynamic" { + /* All true */ + + if anyof ( true, exists "to", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong all-true first-static outcome: false"; + } + + if anyof ( exists "from", true, exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong all-true second-static outcome: false"; + } + + if anyof ( exists "from", exists "to", true ) { + /* Correct */ + } else { + test_fail "chose wrong all-true third-static outcome: false"; + } + + /* All false */ + + if anyof ( false, exists "frop", exists "frml" ) { + test_fail "chose wrong all-false first-static outcome: true"; + } else { + /* Correct */ + } + + if anyof ( exists "friep", false, exists "frml" ) { + test_fail "chose wrong all-false second-static outcome: true"; + } else { + /* Correct */ + } + + if anyof ( exists "friep", exists "frop", false ) { + test_fail "chose wrong all-false third-static outcome: true"; + } else { + /* Correct */ + } + + /* First true */ + + if anyof ( true, exists "frop", exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong first-true first-static outcome: false"; + } + + if anyof ( exists "to", false, exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong first-true second-static outcome: false"; + } + + if anyof ( exists "to", exists "frop", false ) { + /* Correct */ + } else { + test_fail "chose wrong first-true third-static outcome: false"; + } + + /* Second true */ + + if anyof ( false, exists "from", exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong second-true first-static outcome: false"; + } + + if anyof ( exists "friep", true, exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong second-true second-static outcome: false"; + } + + if anyof ( exists "friep", exists "from", false ) { + /* Correct */ + } else { + test_fail "chose wrong second-true third-static outcome: false"; + } + + /* Last true */ + + if anyof ( false, exists "frop", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong last-true first-static outcome: false"; + } + + if anyof ( exists "friep", false, exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong last-true second-static outcome: false"; + } + + if anyof ( exists "friep", exists "frop", true ) { + /* Correct */ + } else { + test_fail "chose wrong last-true third-static outcome: false"; + } + + /* First false */ + + if anyof ( false, exists "from", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong first-false first-static outcome: false"; + } + + if anyof ( exists "friep", true, exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong first-false second-static outcome: false"; + } + + if anyof ( exists "friep", exists "from", true ) { + /* Correct */ + } else { + test_fail "chose wrong first-false third-static outcome: false"; + } + + /* Second false */ + + if anyof ( true, exists "frop", exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong second-false first-static outcome: false"; + } + + if anyof ( exists "to", false, exists "cc" ) { + /* Correct */ + } else { + test_fail "chose wrong second-false second-static outcome: false"; + } + + if anyof ( exists "to", exists "frop", true ) { + /* Correct */ + } else { + test_fail "chose wrong second-false third-static outcome: false"; + } + + /* Third false */ + + if anyof ( true, exists "from", exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong last-false first-static outcome: false"; + } + + if anyof ( exists "to", true, exists "frml" ) { + /* Correct */ + } else { + test_fail "chose wrong last-false second-static outcome: false"; + } + + if anyof ( exists "to", exists "from", false ) { + /* Correct */ + } else { + test_fail "chose wrong last-false third-static outcome: false"; + } +} + +/* + * TEST: Basic functionality: nesting + */ + +test "Basic functionality: nesting" { + /* Static */ + + if anyof ( anyof(false, false), anyof(false, false) ) { + test_fail "chose wrong static nesting ((false, false),(false,false)) outcome: true"; + } else { + /* Correct */ + } + + if anyof ( anyof(true, false), anyof(false, false) ) { + /* Correct */ + } else { + test_fail "chose wrong static nesting ((true, false),(false,false)) outcome: false"; + } + + if anyof ( anyof(false, true), anyof(false, false) ) { + /* Correct */ + } else { + test_fail "chose wrong static nesting ((false, true),(false,false)) outcome: false"; + } + + if anyof ( anyof(false, false), anyof(true, false) ) { + /* Correct */ + } else { + test_fail "chose wrong static nesting ((false, false),(true,false)) outcome: false"; + } + + if anyof ( anyof(false, false), anyof(false, true) ) { + /* Correct */ + } else { + test_fail "chose wrong static nesting ((false, false),(false,true)) outcome: false"; + } + + if anyof ( anyof(true, false), anyof(false, true) ) { + /* Correct */ + } else { + test_fail "chose wrong static nesting ((true, false),(false,true)) outcome: false"; + } + + /* Dynamic */ + + if anyof ( anyof(exists "frop", exists "friep"), anyof(exists "frml", exists "fruts") ) { + test_fail "chose wrong dynamic nesting ((false, false),(false,false)) outcome: true"; + } else { + /* Correct */ + } + + if anyof ( anyof(exists "to", exists "friep"), anyof(exists "frml", exists "fruts") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((true, false),(false,false)) outcome: false"; + } + + if anyof ( anyof(exists "frop", exists "from"), anyof(exists "frml", exists "fruts") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((false, true),(false,false)) outcome: false"; + } + + if anyof ( anyof(exists "frop", exists "friep"), anyof(exists "cc", exists "fruts") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((false, false),(true,false)) outcome: false"; + } + + if anyof ( anyof(exists "frop", exists "friep"), anyof(exists "frml", exists "subject") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((false, false),(false,true)) outcome: false"; + } + + if anyof ( anyof(exists "to", exists "friep"), anyof(exists "frml", exists "subject") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((true, false),(false,true)) outcome: false"; + } + + /* Static/Dynamic */ + + if anyof ( anyof(false, exists "friep"), anyof(exists "frml", exists "fruts") ) { + test_fail "chose wrong static/dynamic nesting ((false, false),(false,false)) outcome: true"; + } else { + /* Correct */ + } + + if anyof ( anyof(exists "to", false), anyof(exists "frml", exists "fruts") ) { + /* Correct */ + } else { + test_fail "chose wrong static/dynamic nesting ((true, false),(false,false)) outcome: false"; + } + + if anyof ( anyof(exists "frop", exists "from"), anyof(false, exists "fruts") ) { + /* Correct */ + } else { + test_fail "chose wrong static/dynamic nesting ((false, true),(false,false)) outcome: false"; + } + + if anyof ( anyof(exists "frop", exists "friep"), anyof(exists "cc", false) ) { + /* Correct */ + } else { + test_fail "chose wrong static/dynamic nesting ((false, false),(true,false)) outcome: false"; + } + + if anyof ( anyof(exists "frop", exists "friep"), anyof(exists "frml", true) ) { + /* Correct */ + } else { + test_fail "chose wrong static/dynamic nesting ((false, false),(false,true)) outcome: false"; + } + + if anyof ( anyof(true, exists "friep"), anyof(false, exists "subject") ) { + /* Correct */ + } else { + test_fail "chose wrong dynamic nesting ((true, false),(false,true)) outcome: false"; + } + +} + + + diff --git a/pigeonhole/tests/test-exists.svtest b/pigeonhole/tests/test-exists.svtest new file mode 100644 index 0000000..8c4c2fc --- /dev/null +++ b/pigeonhole/tests/test-exists.svtest @@ -0,0 +1,93 @@ +require "vnd.dovecot.testsuite"; + +/* "The "exists" test is true if the headers listed in the header-names + * argument exist within the message. All of the headers must exist or + * the test is false. + * " + */ + +test_set "message" text: +From: stephan@example.org +To: nico@vestingbar.bl +Subject: Test message +Date: Wed, 29 Jul 2009 18:21:44 +0300 +X-Spam-Status: Not Spam +Resent-To: nico@frop.example.com + +Test! +. +; + +/* + * TEST: One header + */ + +test "One header" { + if not exists "from" { + test_fail "exists test missed from header"; + } + + if exists "x-nonsense" { + test_fail "exists test found non-existent header"; + } +} + +/* + * TEST: Two headers + */ + +test "Two headers" { + if not exists ["from","to"] { + test_fail "exists test missed from or to header"; + } + + if exists ["from","x-nonsense"] { + test_fail "exists test found non-existent header (1)"; + } + + if exists ["x-nonsense","to"] { + test_fail "exists test found non-existent header (2)"; + } + + if exists ["x-nonsense","x-nonsense2"] { + test_fail "exists test found non-existent header (3)"; + } +} + +/* + * TEST: Three headers + */ + +test "Three headers" { + if not exists ["Subject","date","resent-to"] { + test_fail "exists test missed subject, date or resent-to header"; + } + + if exists ["x-nonsense","date","resent-to"] { + test_fail "exists test found non-existent header (1)"; + } + + if exists ["subject", "x-nonsense","resent-to"] { + test_fail "exists test found non-existent header (2)"; + } + + if exists ["subject","date","x-nonsense"] { + test_fail "exists test found non-existent header (3)"; + } + + if exists ["subject", "x-nonsense","x-nonsense2"] { + test_fail "exists test found non-existent header (4)"; + } + + if exists ["x-nonsense","date","x-nonsense2"] { + test_fail "exists test found non-existent header (5)"; + } + + if exists ["x-nonsense","x-nonsense2","resent-to"] { + test_fail "exists test found non-existent header (6)"; + } + + if exists ["x-nonsense","x-nonsense2","x-nonsense3"] { + test_fail "exists test found non-existent header (7)"; + } +} diff --git a/pigeonhole/tests/test-header.svtest b/pigeonhole/tests/test-header.svtest new file mode 100644 index 0000000..138fb82 --- /dev/null +++ b/pigeonhole/tests/test-header.svtest @@ -0,0 +1,280 @@ +require "vnd.dovecot.testsuite"; +require "variables"; + +/* + * ## RFC 5228, Section 5.7. Test header (page 29) ## + */ + +/* + * TEST: Basic functionality + */ + +/* "The "header" test evaluates to true if the value of any of the named + * headers, ignoring leading and trailing whitespace, matches any key. + * The type of match is specified by the optional match argument, which + * defaults to ":is" if not specified, as specified in section 2.6. + * + * Like address and envelope, this test returns true if any combination + * of the header-names list and key-list arguments match and returns + * false otherwise. + * " + */ + +test_set "message" text: +From: stephan@example.com +To: nico@nl.example.com, harry@de.example.com +Subject: Frobnitzm +Comments: This is nonsense. +Keywords: nonsense, strange, testing +X-Spam: Yes + +Test. +. +; + +test "Basic functionality" { + /* Must match */ + if not header :contains ["Subject", "Comments"] "Frobnitzm" { + test_fail "failed to match header (1)"; + } + + if not header :contains ["Subject", "Comments"] "nonsense" { + test_fail "failed to match header(2)"; + } + + if not header :matches "Keywords" "*, strange, *" { + test_fail "failed to match header (3)"; + } + + if not header :is "Comments" "This is nonsense." { + test_fail "failed to match header (4)"; + } + + /* Must not match */ + if header ["subject", "comments", "keywords"] "idiotic" { + test_fail "matched nonsense"; + } + + /* Match first key */ + if not header :contains ["keywords"] ["strange", "snot", "vreemd"] { + test_fail "failed to match first key"; + } + + /* Match second key */ + if not header :contains ["keywords"] ["raar", "strange", "vreemd"] { + test_fail "failed to match second key"; + } + + /* Match last key */ + if not header :contains ["keywords"] ["raar", "snot", "strange"] { + test_fail "failed to match last key"; + } + + /* First header */ + if not header :contains ["keywords", "subject"] + ["raar", "strange", "vreemd"] { + test_fail "failed to match first header"; + } + + /* Second header */ + if not header :contains ["subject", "keywords"] + ["raar", "strange", "vreemd"] { + test_fail "failed to match second header"; + } +} + +/* + * TEST: Matching empty key + */ + +/* "If a header listed in the header-names argument exists, it contains + * the empty key (""). However, if the named header is not present, it + * does not match any key, including the empty key. So if a message + * contained the header + * + * X-Caffeine: C8H10N4O2 + * + * these tests on that header evaluate as follows: + * + * header :is ["X-Caffeine"] [""] => false + * header :contains ["X-Caffeine"] [""] => true + * " + */ + + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +X-Caffeine: C8H10N4O2 +Subject: I need coffee! +Comments: + +Text +. +; + +test "Matching empty key" { + if header :is "X-Caffeine" "" { + test_fail ":is-matched non-empty header with empty string"; + } + + if not header :contains "X-Caffeine" "" { + test_fail "failed to match existing header with empty string"; + } + + if not header :is "comments" "" { + test_fail "failed to match empty header with empty string"; + } + + if header :contains "X-Nonsense" "" { + test_fail ":contains-matched non-existent header with empty string"; + } +} + +/* + * TEST: Ignoring whitespace + */ + +/* "The "header" test evaluates to true if the value of any of the named + * headers, ignoring leading and trailing whitespace, matches any key. + * ... + * " + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Help +X-A: Text +X-B: Text + +Text +. +; + +test "Ignoring whitespace" { + if not header :is "x-a" "Text" { + if header :matches "x-a" "*" { + set "header" "${1}"; + } + test_fail "header test does not strip leading whitespace (header=`${header}`)"; + } + + if not header :is "x-b" "Text" { + if header :matches "x-b" "*" { + set "header" "${1}"; + } + test_fail "header test does not strip trailing whitespace (header=`${header}`)"; + } + + if not header :is "subject" "Help" { + if header :matches "subject" "*" { + set "header" "${1}"; + } + test_fail "header test does not strip both leading and trailing whitespace (header=`${header}`)"; + } +} + +/* + * TEST: Absent or empty header + */ + +/* "Testing whether a given header is either absent or doesn't contain + * any non-whitespace characters can be done using a negated "header" + * test: + * + * not header :matches "Cc" "?*" + * " + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +CC: harry@nonsense.ex +Subject: +Comments: + +Text +. +; + +test "Absent or empty header" { + if not header :matches "Cc" "?*" { + test_fail "CC header is not absent or empty"; + } + + if header :matches "Subject" "?*" { + test_fail "Subject header is empty, but matched otherwise"; + } + + if header :matches "Comment" "?*" { + test_fail "Comment header is empty, but matched otherwise"; + } +} + +/* + * ## RFC 5228, Section 2.4.2.2. Headers (page 9) + */ + +/* + * TEST: Invalid header name + */ + +/* "A header name never contains a colon. The "From" header refers to a + * line beginning "From:" (or "From :", etc.). No header will match + * the string "From:" due to the trailing colon. + * + * Similarly, no header will match a syntactically invalid header name. + * An implementation MUST NOT cause an error for syntactically invalid + * header names in tests. + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Valid message +X-Multiline: This is a multi-line + header body, which should be + unfolded correctly. + +Text +. +; + +test "Invalid header name" { + if header :contains "subject:" "" { + test_fail "matched invalid header name"; + } + + if header :contains "to!" "" { + test_fail "matched invalid header name"; + } +} + +/* + * TEST: Folded headers + */ + +/* "Header lines are unfolded as described in [RFC 2822] section 2.2.3. + * ... + * " + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Not enough space on a line! +X-Multiline: This is a multi-line + header body, which should be + unfolded correctly. + +Text +. +; + +test "Folded header" { + if not header :is "x-multiline" + "This is a multi-line header body, which should be unfolded correctly." { + test_fail "failed to properly unfold folded header."; + } +} diff --git a/pigeonhole/tests/test-size.svtest b/pigeonhole/tests/test-size.svtest new file mode 100644 index 0000000..dd5cdc4 --- /dev/null +++ b/pigeonhole/tests/test-size.svtest @@ -0,0 +1,74 @@ +require "vnd.dovecot.testsuite"; + +/* + * ## RFC 5228, Section 5.9. Test size (page 29) ## + */ + +/* + * TEST: Basic functionality + */ + +/* "The "size" test deals with the size of a message. It takes either a + * tagged argument of ":over" or ":under", followed by a number + * representing the size of the message. + * + * If the argument is ":over", and the size of the message is greater + * than the number provided, the test is true; otherwise, it is false. + + * If the argument is ":under", and the size of the message is less than + * the number provided, the test is true; otherwise, it is false. + * " + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.com +Subject: Help +X-A: Text +X-B: Text +X-Multiline: This is a multi-line + header body, which should be + unfolded correctly. + +Text + +. +; + +test "Basic functionality" { + if not size :under 1000 { + test_fail "size test produced unexpected result (1)"; + } + + if size :under 10 { + test_fail "size test produced unexpected result (2)"; + } + + if not size :over 10 { + test_fail "size test produced unexpected result (3)"; + } + + if size :over 1000 { + test_fail "size test produced unexpected result (4)"; + } +} + +/* + * TEST: Exact size + */ + +/* "Note that for a message that is exactly 4,000 octets, the message is + * neither ":over" nor ":under" 4000 octets. + * " + */ + +test "Exact size" { + if size :under 221 { + test_fail "size :under matched exact limit"; + } + + if size :over 221 { + test_fail "size :over matched exact limit"; + } +} + diff --git a/pigeonhole/tests/testsuite.svtest b/pigeonhole/tests/testsuite.svtest new file mode 100644 index 0000000..349ba89 --- /dev/null +++ b/pigeonhole/tests/testsuite.svtest @@ -0,0 +1,75 @@ +require "vnd.dovecot.testsuite"; +require "envelope"; + +/* Test message environment */ + +test "Message Environment" { + test_set "message" text: +From: sirius@example.org +To: nico@frop.example.com +Subject: Frop! + +Frop! +. + ; + + if not header :contains "from" "example.org" { + test_fail "message data not set properly."; + } + + test_set "message" text: +From: nico@frop.example.com +To: stephan@nl.example.com +Subject: Friep! + +Friep! +. + ; + + if not header :is "from" "nico@frop.example.com" { + test_fail "message data not set properly."; + } + + keep; +} + +/* Test envelope environment */ + +test "Envelope Environment" { + test_set "envelope.from" "stephan@hutsefluts.example.net"; + + if not envelope :is "from" "stephan@hutsefluts.example.net" { + test_fail "envelope.from data not set properly (1)."; + } + + test_set "envelope.to" "news@example.org"; + + if not envelope :is "to" "news@example.org" { + test_fail "envelope.to data not set properly (1)."; + } + + test_set "envelope.auth" "sirius"; + + if not envelope :is "auth" "sirius" { + test_fail "envelope.auth data not set properly (1)."; + } + + test_set "envelope.from" "stephan@example.org"; + + if not envelope :is "from" "stephan@example.org" { + test_fail "envelope.from data not reset properly (2)."; + } + + test_set "envelope.to" "past-news@example.org"; + + if not envelope :is "to" "past-news@example.org" { + test_fail "envelope.to data not reset properly (2)."; + } + + test_set "envelope.auth" "zilla"; + + if not envelope :is "auth" "zilla" { + test_fail "envelope.auth data not reset properly (2)."; + } +} + |