diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /pigeonhole/tests/extensions | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pigeonhole/tests/extensions')
221 files changed, 16063 insertions, 0 deletions
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 |