summaryrefslogtreecommitdiffstats
path: root/pigeonhole/tests/execute
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
commit0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch)
tree3f3789daa2f6db22da6e55e92bee0062a7d613fe /pigeonhole/tests/execute
parentInitial commit. (diff)
downloaddovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz
dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pigeonhole/tests/execute')
-rw-r--r--pigeonhole/tests/execute/actions.svtest80
-rw-r--r--pigeonhole/tests/execute/actions/fileinto.sieve17
-rw-r--r--pigeonhole/tests/execute/actions/redirect.sieve17
-rw-r--r--pigeonhole/tests/execute/address-normalize.svtest46
-rw-r--r--pigeonhole/tests/execute/errors-cpu-limit.svtest363
-rw-r--r--pigeonhole/tests/execute/errors.svtest152
-rw-r--r--pigeonhole/tests/execute/errors/action-duplicates.sieve4
-rw-r--r--pigeonhole/tests/execute/errors/actions-limit.sieve35
-rw-r--r--pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve5
-rw-r--r--pigeonhole/tests/execute/errors/conflict-reject-keep.sieve4
-rw-r--r--pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve4
-rw-r--r--pigeonhole/tests/execute/errors/cpu-limit.sieve145
-rw-r--r--pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve7
-rw-r--r--pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve5
-rw-r--r--pigeonhole/tests/execute/errors/fileinto.sieve3
-rw-r--r--pigeonhole/tests/execute/errors/redirect-limit.sieve5
-rw-r--r--pigeonhole/tests/execute/examples.svtest115
-rw-r--r--pigeonhole/tests/execute/mailstore.svtest84
-rw-r--r--pigeonhole/tests/execute/smtp.svtest449
19 files changed, 1540 insertions, 0 deletions
diff --git a/pigeonhole/tests/execute/actions.svtest b/pigeonhole/tests/execute/actions.svtest
new file mode 100644
index 0000000..3f517fa
--- /dev/null
+++ b/pigeonhole/tests/execute/actions.svtest
@@ -0,0 +1,80 @@
+require "vnd.dovecot.testsuite";
+require "relational";
+require "comparator-i;ascii-numeric";
+
+test_set "message" text:
+To: nico@frop.example.org
+From: stephan@example.org
+Subject: Test
+
+Test.
+.
+;
+
+test_mailbox_create "INBOX.VB";
+test_mailbox_create "INBOX.backup";
+
+test "Fileinto" {
+ if not test_script_compile "actions/fileinto.sieve" {
+ test_fail "script compile failed";
+ }
+
+ if not test_script_run {
+ test_fail "script run failed";
+ }
+
+ if not test_result_action :count "eq" :comparator "i;ascii-numeric" "3" {
+ test_fail "wrong number of actions in result";
+ }
+
+ if not test_result_action :index 1 "store" {
+ test_fail "first action is not 'store'";
+ }
+
+ if not test_result_action :index 2 "store" {
+ test_fail "second action is not 'store'";
+ }
+
+ if not test_result_action :index 3 "keep" {
+ test_fail "third action is not 'keep'";
+ }
+
+ if not test_result_execute {
+ test_fail "result execute failed";
+ }
+}
+
+test "Redirect" {
+ if not test_script_compile "actions/redirect.sieve" {
+ test_fail "compile failed";
+ }
+
+ if not test_script_run {
+ test_fail "execute failed";
+ }
+
+ if not test_result_action :count "eq" :comparator "i;ascii-numeric" "4" {
+ test_fail "wrong number of actions in result";
+ }
+
+ if not test_result_action :index 1 "redirect" {
+ test_fail "first action is not 'redirect'";
+ }
+
+ if not test_result_action :index 2 "keep" {
+ test_fail "second action is not 'keep'";
+ }
+
+ if not test_result_action :index 3 "redirect" {
+ test_fail "third action is not 'redirect'";
+ }
+
+ if not test_result_action :index 4 "redirect" {
+ test_fail "fourth action is not 'redirect'";
+ }
+
+ if not test_result_execute {
+ test_fail "result execute failed";
+ }
+}
+
diff --git a/pigeonhole/tests/execute/actions/fileinto.sieve b/pigeonhole/tests/execute/actions/fileinto.sieve
new file mode 100644
index 0000000..e9c133b
--- /dev/null
+++ b/pigeonhole/tests/execute/actions/fileinto.sieve
@@ -0,0 +1,17 @@
+require "fileinto";
+
+/* Three store actions */
+
+if address :contains "to" "frop.example" {
+ /* #1 */
+ fileinto "INBOX.VB";
+}
+
+/* #2 */
+fileinto "INBOX.backup";
+
+/* #3 */
+keep;
+
+/* Duplicate of keep */
+fileinto "INBOX";
diff --git a/pigeonhole/tests/execute/actions/redirect.sieve b/pigeonhole/tests/execute/actions/redirect.sieve
new file mode 100644
index 0000000..7adc23e
--- /dev/null
+++ b/pigeonhole/tests/execute/actions/redirect.sieve
@@ -0,0 +1,17 @@
+if address :contains "to" "frop.example" {
+ /* #1 */
+ redirect "stephan@example.com";
+
+ /* #2 */
+ keep;
+}
+
+/* #3 */
+redirect "stephan@example.org";
+
+/* #4 */
+redirect "nico@example.nl";
+
+/* Duplicates */
+redirect "Stephan Bosch <stephan@example.com>";
+keep;
diff --git a/pigeonhole/tests/execute/address-normalize.svtest b/pigeonhole/tests/execute/address-normalize.svtest
new file mode 100644
index 0000000..e826bde
--- /dev/null
+++ b/pigeonhole/tests/execute/address-normalize.svtest
@@ -0,0 +1,46 @@
+require "vnd.dovecot.testsuite";
+require "envelope";
+
+test_set "message" text:
+From: tss@example.net
+To: stephan@example.org
+Subject: Frop!
+
+Frop!
+.
+;
+
+test_set "envelope.from" "timo@example.net";
+test_set "envelope.to" "\"sirius\"@example.org";
+
+/*
+ * Mail address normalization - redirect
+ */
+
+test "Mail address normalization - redirect" {
+ redirect "\"S[r]us\"@example.net";
+ redirect "\"Sirius\"@example.net";
+ redirect "\"Stephan Bosch\" <\"S.Bosch\"@example.net>";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if not envelope :is "to" "\"S[r]us\"@example.net" {
+ test_fail "envelope recipient incorrect";
+ }
+
+ test_message :smtp 1;
+
+ if not envelope :is "to" "Sirius@example.net" {
+ test_fail "envelope recipient incorrect";
+ }
+
+ test_message :smtp 2;
+
+ if not envelope :is "to" "S.Bosch@example.net" {
+ test_fail "envelope recipient incorrect";
+ }
+}
diff --git a/pigeonhole/tests/execute/errors-cpu-limit.svtest b/pigeonhole/tests/execute/errors-cpu-limit.svtest
new file mode 100644
index 0000000..4a045bc
--- /dev/null
+++ b/pigeonhole/tests/execute/errors-cpu-limit.svtest
@@ -0,0 +1,363 @@
+require "vnd.dovecot.testsuite";
+
+test_config_set "sieve_max_cpu_time" "2";
+test_config_reload;
+
+test_set "message" text:
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary=
+
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+--
+.
+;
+
+test "CPU limit" {
+ if not test_script_compile "errors/cpu-limit.sieve" {
+ test_fail "script compile failed";
+ }
+
+ if test_script_run {
+ test_fail "script execute should have failed";
+ }
+}
+
diff --git a/pigeonhole/tests/execute/errors.svtest b/pigeonhole/tests/execute/errors.svtest
new file mode 100644
index 0000000..45bc39c
--- /dev/null
+++ b/pigeonhole/tests/execute/errors.svtest
@@ -0,0 +1,152 @@
+require "vnd.dovecot.testsuite";
+
+require "relational";
+require "comparator-i;ascii-numeric";
+require "fileinto";
+
+test "Action conflicts: reject <-> fileinto" {
+ if not test_script_compile "errors/conflict-reject-fileinto.sieve" {
+ test_fail "compile failed";
+ }
+
+ if test_script_run {
+ test_fail "execution should have failed";
+ }
+
+ if test_error :count "gt" :comparator "i;ascii-numeric" "1" {
+ test_fail "too many runtime errors reported";
+ }
+}
+
+test "Action conflicts: reject <-> keep" {
+ if not test_script_compile "errors/conflict-reject-keep.sieve" {
+ test_fail "compile failed";
+ }
+
+ if test_script_run {
+ test_fail "execution should have failed";
+ }
+
+ if test_error :count "gt" :comparator "i;ascii-numeric" "1" {
+ test_fail "too many runtime errors reported";
+ }
+}
+
+test "Action conflicts: reject <-> redirect" {
+ if not test_script_compile "errors/conflict-reject-redirect.sieve" {
+ test_fail "compile failed";
+ }
+
+ if test_script_run {
+ test_fail "execution should have failed";
+ }
+
+ if test_error :count "gt" :comparator "i;ascii-numeric" "1" {
+ test_fail "too many runtime errors reported";
+ }
+}
+
+test "Action limit" {
+ if not test_script_compile "errors/actions-limit.sieve" {
+ test_fail "compile failed";
+ }
+
+ if test_script_run {
+ test_fail "execution should have failed";
+ }
+
+ if test_error :count "gt" :comparator "i;ascii-numeric" "1" {
+ test_fail "too many runtime errors reported";
+ }
+
+ if not test_error :index 1 :contains "total number of actions exceeds policy limit"{
+ test_fail "unexpected error reported";
+ }
+}
+
+test "Redirect limit" {
+ if not test_script_compile "errors/redirect-limit.sieve" {
+ test_fail "compile failed";
+ }
+
+ if test_script_run {
+ test_fail "execution should have failed";
+ }
+
+ if test_error :count "gt" :comparator "i;ascii-numeric" "1" {
+ test_fail "too many runtime errors reported";
+ }
+
+ if not test_error :index 1 :contains "number of redirect actions exceeds policy limit"{
+ test_fail "unexpected error reported";
+ }
+}
+
+test "Fileinto missing folder" {
+ if not test_script_compile "errors/fileinto.sieve" {
+ test_fail "compile failed";
+ }
+
+ test_mailbox_create "INBOX";
+
+ if not test_script_run {
+ test_fail "execution failed";
+ }
+
+ if test_result_execute {
+ test_fail "execution of result should have failed";
+ }
+
+ if test_error :count "gt" :comparator "i;ascii-numeric" "1" {
+ test_fail "too many runtime errors reported";
+ }
+
+ if not allof (
+ test_error :index 1 :contains "failed to store into mailbox",
+ test_error :index 1 :contains "exist",
+ test_error :index 1 :contains "FROP") {
+ test_fail "unexpected error reported";
+ }
+}
+
+test "Fileinto invalid folder name" {
+ if not test_script_compile "errors/fileinto-invalid-name.sieve" {
+ test_fail "compile failed";
+ }
+
+ if not test_script_run {
+ test_fail "execution failed";
+ }
+
+ if test_result_execute {
+ test_fail "execution of result should have failed";
+ }
+
+ if not test_error :count "eq" :comparator "i;ascii-numeric" "1" {
+ test_fail "wrong number of runtime errors reported";
+ }
+
+ if not allof (
+ test_error :index 1 :contains "failed to store into mailbox",
+ test_error :index 1 :contains "name") {
+ test_fail "unexpected error reported";
+ }
+}
+
+test "Fileinto bad UTF-8 in folder name" {
+ if not test_script_compile "errors/fileinto-bad-utf8.sieve" {
+ test_fail "compile failed";
+ }
+
+ if test_script_run {
+ test_fail "execution should have failed";
+ }
+
+ if not test_error :count "eq" :comparator "i;ascii-numeric" "1" {
+ test_fail "wrong number of runtime errors reported";
+ }
+
+ if not test_error :index 1 :contains "invalid folder name" {
+ test_fail "unexpected error reported";
+ }
+}
diff --git a/pigeonhole/tests/execute/errors/action-duplicates.sieve b/pigeonhole/tests/execute/errors/action-duplicates.sieve
new file mode 100644
index 0000000..6d5370d
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/action-duplicates.sieve
@@ -0,0 +1,4 @@
+require "reject";
+
+reject "Message is not appreciated.";
+reject "No, really, it is not appreciated.";
diff --git a/pigeonhole/tests/execute/errors/actions-limit.sieve b/pigeonhole/tests/execute/errors/actions-limit.sieve
new file mode 100644
index 0000000..3ae33a3
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/actions-limit.sieve
@@ -0,0 +1,35 @@
+require "fileinto";
+
+fileinto "box1";
+fileinto "box2";
+fileinto "box3";
+fileinto "box4";
+fileinto "box5";
+fileinto "box6";
+fileinto "box7";
+fileinto "box8";
+fileinto "box9";
+fileinto "box10";
+fileinto "box11";
+fileinto "box12";
+fileinto "box13";
+fileinto "box14";
+fileinto "box15";
+fileinto "box16";
+fileinto "box17";
+fileinto "box18";
+fileinto "box19";
+fileinto "box20";
+fileinto "box21";
+fileinto "box22";
+fileinto "box23";
+fileinto "box24";
+fileinto "box25";
+fileinto "box26";
+fileinto "box27";
+fileinto "box28";
+redirect "address1@example.com";
+redirect "address2@example.com";
+redirect "address3@example.com";
+redirect "address4@example.com";
+keep;
diff --git a/pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve b/pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve
new file mode 100644
index 0000000..85ef139
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/conflict-reject-fileinto.sieve
@@ -0,0 +1,5 @@
+require "reject";
+require "fileinto";
+
+reject "No nonsense in my mailbox.";
+fileinto "Spam";
diff --git a/pigeonhole/tests/execute/errors/conflict-reject-keep.sieve b/pigeonhole/tests/execute/errors/conflict-reject-keep.sieve
new file mode 100644
index 0000000..569a4ac
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/conflict-reject-keep.sieve
@@ -0,0 +1,4 @@
+require "reject";
+
+reject "I am not interested in your nonsense.";
+keep;
diff --git a/pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve b/pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve
new file mode 100644
index 0000000..d012269
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/conflict-reject-redirect.sieve
@@ -0,0 +1,4 @@
+require "reject";
+
+reject "I am not interested in your nonsense.";
+redirect "frop@example.com";
diff --git a/pigeonhole/tests/execute/errors/cpu-limit.sieve b/pigeonhole/tests/execute/errors/cpu-limit.sieve
new file mode 100644
index 0000000..8532a4b
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/cpu-limit.sieve
@@ -0,0 +1,145 @@
+require ["mime","foreverypart","fileinto", "variables", "regex"];
+
+# Here we create an inefficient regex with long compilation time
+set "my_exp" "^(((A)|(AB)|(ABC)|(ABCD)|(ABCDE)|(ABCDEF)|(ABCDEFG)|(ABCDEFGH)|(ABCDEFGHI)|(ABCDEFGHIJ)|(ABCDEFGHIJK)|(ABCDEFGHIJKL)|(ABCDEFGHIJKLM)|(ABCDEFGHIJKLMN)|(ABCDEFGHIJKLMNO)|(ABCDEFGHIJKLMNOP)|(ABCDEFGHIJKLMNOPQ)|(ABCDEFGHIJKLMNOPQR))?((B)|(BC)|(BCD)|(BCDE)|(BCDEF)|(BCDEFG)|(BCDEFGH)|(BCDEFGHI)|(BCDEFGHIJ)|(BCDEFGHIJK)|(BCDEFGHIJKL)|(BCDEFGHIJKLM)|(BCDEFGHIJKLMN)|(BCDEFGHIJKLMNO)|(BCDEFGHIJKLMNOP)|(BCDEFGHIJKLMNOPQ)|(BCDEFGHIJKLMNOPQR))?((C)|(CD)|(CDE)|(CDEF)|(CDEFG)|(CDEFGH)|(CDEFGHI)|(CDEFGHIJ)|(CDEFGHIJK)|(CDEFGHIJKL)|(CDEFGHIJKLM)|(CDEFGHIJKLMN)|(CDEFGHIJKLMNO)|(CDEFGHIJKLMNOP)|(CDEFGHIJKLMNOPQ)|(CDEFGHIJKLMNOPQR))?((D)|(DE)|(DEF)|(DEFG)|(DEFGH)|(DEFGHI)|(DEFGHIJ)|(DEFGHIJK)|(DEFGHIJKL)|(DEFGHIJKLM)|(DEFGHIJKLMN)|(DEFGHIJKLMNO)|(DEFGHIJKLMNOP)|(DEFGHIJKLMNOPQ)|(DEFGHIJKLMNOPQR))?((E)|(EF)|(EFG)|(EFGH)|(EFGHI)|(EFGHIJ)|(EFGHIJK)|(EFGHIJKL)|(EFGHIJKLM)|(EFGHIJKLMN)|(EFGHIJKLMNO)|(EFGHIJKLMNOP)|(EFGHIJKLMNOPQ)|(EFGHIJKLMNOPQR))?((F)|(FG)|(FGH)|(FGHI)|(FGHIJ)|(FGHIJK)|(FGHIJKL)|(FGHIJKLM)|(FGHIJKLMN)|(FGHIJKLMNO)|(FGHIJKLMNOP)|(FGHIJKLMNOPQ)|(FGHIJKLMNOPQR))?((G)|(GH)|(GHI)|(GHIJ)|(GHIJK)|(GHIJKL)|(GHIJKLM)|(GHIJKLMN)|(GHIJKLMNO)|(GHIJKLMNOP)|(GHIJKLMNOPQ)|(GHIJKLMNOPQR))?((H)|(HI)|(HIJ)|(HIJK)|(HIJKL)|(HIJKLM)|(HIJKLMN)|(HIJKLMNO)|(HIJKLMNOP)|(HIJKLMNOPQ)|(HIJKLMNOPQR))?((I)|(IJ)|(IJK)|(IJKL)|(IJKLM)|(IJKLMN)|(IJKLMNO)|(IJKLMNOP)|(IJKLMNOPQ)|(IJKLMNOPQR))?((J)|(JK)|(JKL)|(JKLM)|(JKLMN)|(JKLMNO)|(JKLMNOP)|(JKLMNOPQ)|(JKLMNOPQR))?((K)|(KL)|(KLM)|(KLMN)|(KLMNO)|(KLMNOP)|(KLMNOPQ)|(KLMNOPQR))?((L)|(LM)|(LMN)|(LMNO)|(LMNOP)|(LMNOPQ)|(LMNOPQR))?((M)|(MN)|(MNO)|(MNOP)|(MNOPQ)|(MNOPQR))?((N)|(NO)|(NOP)|(NOPQ)|(NOPQR))?((O)|(OP)|(OPQ)|(OPQR))?((P)|(PQ)|(PQR))?((Q)|(QR))?((R))?((R)|(RQ)|(RQP)|(RQPO)|(RQPON)|(RQPONM)|(RQPONML)|(RQPONMLK)|(RQPONMLKJ)|(RQPONMLKJI)|(RQPONMLKJIH)|(RQPONMLKJIHG)|(RQPONMLKJIHGF)|(RQPONMLKJIHGFE)|(RQPONMLKJIHGFED)|(RQPONMLKJIHGFEDC)|(RQPONMLKJIHGFEDCB)|(RQPONMLKJIHGFEDCBA))?((Q)|(QP)|(QPO)|(QPON)|(QPONM)|(QPONML)|(QPONMLK)|(QPONMLKJ)|(QPONMLKJI)|(QPONMLKJIH)|(QPONMLKJIHG)|(QPONMLKJIHGF)|(QPONMLKJIHGFE)|(QPONMLKJIHGFED)|(QPONMLKJIHGFEDC)|(QPONMLKJIHGFEDCB)|(QPONMLKJIHGFEDCBA))?((P)|(PO)|(PON)|(PONM)|(PONML)|(PONMLK)|(PONMLKJ)|(PONMLKJI)|(PONMLKJIH)|(PONMLKJIHG)|(PONMLKJIHGF)|(PONMLKJIHGFE)|(PONMLKJIHGFED)|(PONMLKJIHGFEDC)|(PONMLKJIHGFEDCB)|(PONMLKJIHGFEDCBA))?((O)|(ON)|(ONM)|(ONML)|(ONMLK)|(ONMLKJ)|(ONMLKJI)|(ONMLKJIH)|(ONMLKJIHG)|(ONMLKJIHGF)|(ONMLKJIHGFE)|(ONMLKJIHGFED)|(ONMLKJIHGFEDC)|(ONMLKJIHGFEDCB)|(ONMLKJIHGFEDCBA))?((N)|(NM)|(NML)|(NMLK)|(NMLKJ)|(NMLKJI)|(NMLKJIH)|(NMLKJIHG)|(NMLKJIHGF)|(NMLKJIHGFE)|(NMLKJIHGFED)|(NMLKJIHGFEDC)|(NMLKJIHGFEDCB)|(NMLKJIHGFEDCBA))?((M)|(ML)|(MLK)|(MLKJ)|(MLKJI)|(MLKJIH)|(MLKJIHG)|(MLKJIHGF)|(MLKJIHGFE)|(MLKJIHGFED)|(MLKJIHGFEDC)|(MLKJIHGFEDCB)|(MLKJIHGFEDCBA))?((L)|(LK)|(LKJ)|(LKJI)|(LKJIH)|(LKJIHG)|(LKJIHGF)|(LKJIHGFE)|(LKJIHGFED)|(LKJIHGFEDC)|(LKJIHGFEDCB)|(LKJIHGFEDCBA))?((K)|(KJ)|(KJI)|(KJIH)|(KJIHG)|(KJIHGF)|(KJIHGFE)|(KJIHGFED)|(KJIHGFEDC)|(KJIHGFEDCB)|(KJIHGFEDCBA))?((J)|(JI)|(JIH)|(JIHG)|(JIHGF)|(JIHGFE)|(JIHGFED)|(JIHGFEDC)|(JIHGFEDCB)|(JIHGFEDCBA))?((I)|(IH)|(IHG)|(IHGF)|(IHGFE)|(IHGFED)|(IHGFEDC)|(IHGFEDCB)|(IHGFEDCBA))?((H)|(HG)|(HGF)|(HGFE)|(HGFED)|(HGFEDC)|(HGFEDCB)|(HGFEDCBA))?((G)|(GF)|(GFE)|(GFED)|(GFEDC)|(GFEDCB)|(GFEDCBA))?((F)|(FE)|(FED)|(FEDC)|(FEDCB)|(FEDCBA))?((E)|(ED)|(EDC)|(EDCB)|(EDCBA))?((D)|(DC)|(DCB)|(DCBA))?((C)|(CB)|(CBA))?((B)|(BA))?((A))?)+$";
+set "a" "ABCDEFGHIJKLMNOPQR";
+set "b" "RQPONMLKJIHGFEDCBA";
+set "c" "${a}${b}${a}${b}${a}${b}${a}${b}";
+set "e" "${c}${c}${c}${c}${c}${c}${c}${c}";
+set "f" "${e}${e}${e}${e}${e}${e}${e}${e}";
+
+# We create a string on which this regex will spend enough time (around 200 ms)
+set "final" "${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}@";
+
+# We repeat the throttling process for every mime part
+foreverypart {
+ # We use several if statements to multiply the cpu time consumed by one match
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+ if string :regex "${final}" "${my_exp}" { discard; }
+ if string :regex "${final}" "${my_exp}" { keep; }
+}
diff --git a/pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve b/pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve
new file mode 100644
index 0000000..3e57c92
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/fileinto-bad-utf8.sieve
@@ -0,0 +1,7 @@
+require "fileinto";
+require "variables";
+require "encoded-character";
+
+set "mailbox" "${hex:ff}rop";
+fileinto "${mailbox}";
+
diff --git a/pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve b/pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve
new file mode 100644
index 0000000..871323e
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/fileinto-invalid-name.sieve
@@ -0,0 +1,5 @@
+require "fileinto";
+require "mailbox";
+
+fileinto :create "foo//somedomain/org";
+
diff --git a/pigeonhole/tests/execute/errors/fileinto.sieve b/pigeonhole/tests/execute/errors/fileinto.sieve
new file mode 100644
index 0000000..185674c
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/fileinto.sieve
@@ -0,0 +1,3 @@
+require "fileinto";
+
+fileinto "FROP";
diff --git a/pigeonhole/tests/execute/errors/redirect-limit.sieve b/pigeonhole/tests/execute/errors/redirect-limit.sieve
new file mode 100644
index 0000000..86cfda0
--- /dev/null
+++ b/pigeonhole/tests/execute/errors/redirect-limit.sieve
@@ -0,0 +1,5 @@
+redirect "address1@example.com";
+redirect "address2@example.com";
+redirect "address3@example.com";
+redirect "address4@example.com";
+redirect "address5@example.com";
diff --git a/pigeonhole/tests/execute/examples.svtest b/pigeonhole/tests/execute/examples.svtest
new file mode 100644
index 0000000..6143018
--- /dev/null
+++ b/pigeonhole/tests/execute/examples.svtest
@@ -0,0 +1,115 @@
+require "vnd.dovecot.testsuite";
+
+/* Compile and execute all example scripts to trigger
+ * any Segfaults. No message is set and no results are checked.
+ */
+
+test "Elvey example" {
+ if not test_script_compile "../../examples/elvey.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "elvey";
+ test_binary_load "elvey";
+
+ if not test_script_run { }
+}
+
+test "M. Johnson example" {
+ if not test_script_compile "../../examples/mjohnson.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "mjohnson";
+ test_binary_load "mjohnson";
+
+ if not test_script_run { }
+}
+
+test "RFC 3028 example" {
+ if not test_script_compile "../../examples/rfc3028.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "rfc3028";
+ test_binary_load "rfc3028";
+
+ if not test_script_run { }
+}
+
+test "Sieve examples" {
+ if not test_script_compile "../../examples/sieve_examples.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "sieve_examples";
+ test_binary_load "sieve_examples";
+
+ if not test_script_run { }
+}
+
+test "Vivil example" {
+ if not test_script_compile "../../examples/vivil.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "vivil";
+ test_binary_load "vivil";
+
+ if not test_script_run { }
+}
+
+test "Jerry example" {
+ if not test_script_compile "../../examples/jerry.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "jerry";
+ test_binary_load "jerry";
+
+ if not test_script_run { }
+}
+
+test "M. Klose example" {
+ if not test_script_compile "../../examples/mklose.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "mklose";
+ test_binary_load "mklose";
+
+ if not test_script_run { }
+}
+
+test "Sanjay example" {
+ if not test_script_compile "../../examples/sanjay.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "sanjay";
+ test_binary_load "sanjay";
+
+ if not test_script_run { }
+}
+
+test "Relational (RFC5231) example" {
+ if not test_script_compile "../../examples/relational.rfc5231.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "relational";
+ test_binary_load "relational";
+
+ if not test_script_run { }
+}
+
+test "Subaddress (RFC5233) example" {
+ if not test_script_compile "../../examples/subaddress.rfc5233.sieve" {
+ test_fail "could not compile";
+ }
+
+ test_binary_save "subaddress";
+ test_binary_load "subaddress";
+
+ if not test_script_run { }
+}
diff --git a/pigeonhole/tests/execute/mailstore.svtest b/pigeonhole/tests/execute/mailstore.svtest
new file mode 100644
index 0000000..d6cc220
--- /dev/null
+++ b/pigeonhole/tests/execute/mailstore.svtest
@@ -0,0 +1,84 @@
+require "vnd.dovecot.testsuite";
+require "fileinto";
+require "variables";
+require "mailbox";
+
+set "message1" text:
+From: stephan@example.org
+To: nico@frop.example.org
+Subject: First message
+
+Frop
+.
+;
+
+set "message2" text:
+From: stephan@example.org
+To: nico@frop.example.org
+Subject: Second message
+
+Frop
+.
+;
+
+set "message3" text:
+From: stephan@example.org
+To: nico@frop.example.org
+Subject: Third message
+
+Frop
+.
+;
+
+test "Duplicates" {
+ test_set "message" "${message1}";
+
+ fileinto :create "Folder";
+ fileinto :create "Folder";
+
+ if not test_result_execute {
+ test_fail "failed to execute first result";
+ }
+
+ test_result_reset;
+
+ test_set "message" "${message2}";
+
+ fileinto :create "Folder";
+ fileinto :create "Folder";
+
+ if not test_result_execute {
+ test_fail "failed to execute second result";
+ }
+
+ test_result_reset;
+
+ test_set "message" "${message3}";
+
+ fileinto :create "Folder";
+ fileinto :create "Folder";
+
+ if not test_result_execute {
+ test_fail "failed to execute third result";
+ }
+
+ test_message :folder "Folder" 0;
+
+ if not header :is "subject" "First message" {
+ test_fail "first message incorrect";
+ }
+
+ test_message :folder "Folder" 1;
+
+ if not header :is "subject" "Second message" {
+ test_fail "first message incorrect";
+ }
+
+ test_message :folder "Folder" 2;
+
+ if not header :is "subject" "Third message" {
+ test_fail "first message incorrect";
+ }
+}
+
+
diff --git a/pigeonhole/tests/execute/smtp.svtest b/pigeonhole/tests/execute/smtp.svtest
new file mode 100644
index 0000000..2c7d2e6
--- /dev/null
+++ b/pigeonhole/tests/execute/smtp.svtest
@@ -0,0 +1,449 @@
+require "vnd.dovecot.testsuite";
+require "envelope";
+
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+
+test "Redirect" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if not address :is "to" "tss@example.net" {
+ test_fail "to address incorrect (strange forward)";
+ }
+
+ if not address :is "from" "stephan@example.org" {
+ test_fail "from address incorrect (strange forward)";
+ }
+
+ if not envelope :is "to" "cras@example.net" {
+ test_fail "envelope recipient incorrect";
+ }
+
+ if not envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "<>";
+test_set "envelope.to" "timo@example.net";
+
+test "Redirect from <>" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect (not changed)";
+ }
+
+ if not envelope :is "from" "" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+
+test_config_set "sieve_redirect_envelope_from" " recipient ";
+test_config_reload;
+
+test "Redirect from [recipient]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect (not changed)";
+ }
+
+ if not envelope :is "from" "timo@example.net" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "orig_recipient ";
+test_config_reload;
+
+test "Redirect from [original recipient]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect (not changed)";
+ }
+
+ if not envelope :is "from" "tss@example.net" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "<backscatter@example.net> ";
+test_config_reload;
+
+test "Redirect from [<explicit>]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect (not changed)";
+ }
+
+ if not envelope :is "from" "backscatter@example.net" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "<>";
+test_config_reload;
+
+test "Redirect from [<>]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect (not changed)";
+ }
+
+ if not envelope :is "from" "" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "<>";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "<backscatter@example.net>";
+test_config_reload;
+
+test "Redirect from <> with [<explicit>]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "backscatter@example.net" {
+ test_fail "envelope sender incorrect (erroneously changed)";
+ }
+
+ if not envelope :is "from" "" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "user_email";
+test_config_reload;
+
+test "Redirect from [user email - fallback default]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if not envelope :is "from" "timo@example.net" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "timo@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+test_result_reset;
+test_set "message" text:
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "user_email";
+test_config_set "sieve_user_email" "t.sirainen@example.net";
+test_config_reload;
+
+test "Redirect from [user email]" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ test_message :smtp 0;
+
+ if envelope :is "from" "sirius@example.org" {
+ test_fail "envelope sender incorrect (not changed)";
+ }
+
+ if not envelope :is "from" "t.sirainen@example.net" {
+ test_fail "envelope sender incorrect";
+ }
+
+ if not header :contains "x-sieve-redirected-from"
+ "t.sirainen@example.net" {
+ test_fail "x-sieve-redirected-from header is incorrect";
+ }
+}
+
+/*
+ * Redirect mail loop (sieve_user_email)
+ */
+
+test_result_reset;
+test_set "message" text:
+X-Sieve-Redirected-From: t.sirainen@example.net
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "user_email";
+test_config_set "sieve_user_email" "t.sirainen@example.net";
+test_config_reload;
+
+test "Redirect mail loop (sieve_user_email)" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ if test_message :smtp 0 {
+ test_fail "failed to recognize mail loop";
+ }
+}
+
+/*
+ * Redirect mail loop (final recipient)
+ */
+
+test_result_reset;
+test_set "message" text:
+X-Sieve-Redirected-From: timo@example.net
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_reload;
+
+test "Redirect mail loop (final recipient)" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ if test_message :smtp 0 {
+ test_fail "failed to recognize mail loop";
+ }
+}
+
+/*
+ * Redirect mail loop (multiple headers)
+ */
+
+test_result_reset;
+test_set "message" text:
+X-Sieve-Redirected-From: stephan@example.net
+From: stephan@example.org
+To: tss@example.net
+Subject: Frop!
+X-Sieve-Redirected-From: t.sirainen@example.net
+X-Sieve-Redirected-From: t.sirainen@example.com
+
+Frop!
+.
+;
+test_set "envelope.from" "sirius@example.org";
+test_set "envelope.to" "timo@example.net";
+test_set "envelope.orig_to" "tss@example.net";
+
+test_config_set "sieve_redirect_envelope_from" "user_email";
+test_config_set "sieve_user_email" "t.sirainen@example.net";
+test_config_reload;
+
+test "Redirect mail loop (sieve_user_email)" {
+ redirect "cras@example.net";
+
+ if not test_result_execute {
+ test_fail "failed to execute redirect";
+ }
+
+ if test_message :smtp 0 {
+ test_fail "failed to recognize mail loop";
+ }
+}