<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div class="moz-text-plain" wrap="true" style="font-family:
      -moz-fixed; font-size: 12px;" lang="x-western">
      <pre class="moz-quote-pre" wrap=""># HG changeset patch
# User Sander Hoentjen <a class="moz-txt-link-rfc2396E" href="mailto:shoentjen@antagonist.nl"><shoentjen@antagonist.nl></a>
# Date 1586370310 -7200
#      Wed Apr 08 20:25:10 2020 +0200
# Node ID 3d35b19abfa72d1d09e23d02917df7fbdee0970c
# Parent  9e5d38da765152a20098642e37b0afe56312f794
Tests: added sieve tests

diff -r 9e5d38da7651 -r 3d35b19abfa7 lib/Test/Nginx.pm
--- a/lib/Test/Nginx.pm Fri Mar 20 16:32:06 2020 +0300
+++ b/lib/Test/Nginx.pm Wed Apr 08 20:25:10 2020 +0200
@@ -165,6 +165,7 @@
                cache   => '(?s)^(?!.*--without-http-cache)',
                pop3    => '(?s)^(?!.*--without-mail_pop3_module)',
                imap    => '(?s)^(?!.*--without-mail_imap_module)',
+               sieve   => '(?s)^(?!.*--without-mail_sieve_module)',
                smtp    => '(?s)^(?!.*--without-mail_smtp_module)',
                pcre    => '(?s)^(?!.*--without-pcre)',
                split_clients
diff -r 9e5d38da7651 -r 3d35b19abfa7 lib/Test/Nginx/SIEVE.pm
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/Test/Nginx/SIEVE.pm   Wed Apr 08 20:25:10 2020 +0200
@@ -0,0 +1,156 @@
+package <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>;
+
+# (C) Maxim Dounin
+
+# Module for nginx sieve tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use <a class="moz-txt-link-freetext" href="Test::More">Test::More</a> qw//;
+use <a class="moz-txt-link-freetext" href="IO::Select">IO::Select</a>;
+use <a class="moz-txt-link-freetext" href="IO::Socket">IO::Socket</a>;
+use Socket qw/ CRLF /;
+
+use <a class="moz-txt-link-freetext" href="Test::Nginx">Test::Nginx</a>;
+
+sub new {
+       my $self = {};
+       bless $self, shift @_;
+
+       $self->{_socket} = <a class="moz-txt-link-freetext" href="IO::Socket::INET">IO::Socket::INET</a>->new(
+               Proto => "tcp",
+               PeerAddr => "127.0.0.1:" . port(8200),
+               @_
+       )
+               or die "Can't connect to nginx: $!\n";
+
+       if ({@_}->{'SSL'}) {
+               require <a class="moz-txt-link-freetext" href="IO::Socket::SSL">IO::Socket::SSL</a>;
+               <a class="moz-txt-link-freetext" href="IO::Socket::SSL">IO::Socket::SSL</a>->start_SSL($self->{_socket}, @_)
+                       or die $<a class="moz-txt-link-freetext" href="IO::Socket::SSL::SSL_ERROR">IO::Socket::SSL::SSL_ERROR</a> . "\n";
+       }
+
+       $self->{_socket}->autoflush(1);
+
+       return $self;
+}
+
+sub eof {
+       my $self = shift;
+       return $self->{_socket}->eof();
+}
+
+sub print {
+       my ($self, $cmd) = @_;
+       log_out($cmd);
+       $self->{_socket}->print($cmd);
+}
+
+sub send {
+       my ($self, $cmd) = @_;
+       #warn "\n>>>$cmd\n";
+       log_out($cmd);
+       $self->{_socket}->print($cmd . CRLF);
+}
+
+sub read {
+       my ($self) = @_;
+       my $socket = $self->{_socket};
+       eval {
+               local $SIG{ALRM} = sub { die "timeout\n" };
+               alarm(8);
+               while (<$socket>) {
+                       #warn "\n====\n<<<$_\n===\n";
+                       log_in($_);
+                       # XXX
+                       last;
+               }
+               alarm(0);
+       };
+       alarm(0);
+       if ($@) {
+               log_in("died: $@");
+               return undef;
+       }
+       return $_;
+}
+
+sub read_ok {
+       my ($self) = @_;
+       my $socket = $self->{_socket};
+       eval {
+               local $SIG{ALRM} = sub { die "timeout\n" };
+               alarm(8);
+               while (<$socket>) {
+                       #warn "\n====\n<<<$_\n===\n";
+                       log_in($_);
+                       # XXX
+                       next if m/^"/;
+                       last;
+               }
+               alarm(0);
+       };
+       alarm(0);
+       if ($@) {
+               log_in("died: $@");
+               return undef;
+       }
+       return $_;
+}
+
+sub check {
+       my ($self, $regex, $name) = @_;
+       <a class="moz-txt-link-freetext" href="Test::More">Test::More</a>->builder->like($self->read(), $regex, $name);
+}
+
+sub ok {
+       my $self = shift;
+       <a class="moz-txt-link-freetext" href="Test::More">Test::More</a>->builder->like($self->read_ok(), qr/^OK/, @_);
+}
+
+sub can_read {
+       my ($self, $timo) = @_;
+       <a class="moz-txt-link-freetext" href="IO::Select">IO::Select</a>->new($self->{_socket})->can_read($timo || 3);
+}
+
+###############################################################################
+
+sub sieve_test_daemon {
+       my ($port) = @_;
+
+       my $server = <a class="moz-txt-link-freetext" href="IO::Socket::INET">IO::Socket::INET</a>->new(
+               Proto => 'tcp',
+               LocalAddr => '127.0.0.1:' . ($port || port(8201)),
+               Listen => 5,
+               Reuse => 1
+       )
+               or die "Can't create listening socket: $!\n";
+
+       while (my $client = $server->accept()) {
+               $client->autoflush(1);
+               print $client "OK fake sieve server ready" . CRLF;
+
+               while (<$client>) {
+                       if (/^logout/i) {
+                               print $client 'OK logout ok' . CRLF;
+                       } elsif (/^AUTHENTICATE /i) {
+                               print $client 'OK login ok' . CRLF;
+                       } elsif (/^CAPABILITY/i) {
+                               print $client 'OK capabilty ok' . CRLF;
+                       } else {
+                               print $client 'NO unknown command ' . $_ . CRLF;
+                       }
+               }
+
+               close $client;
+       }
+}
+
+###############################################################################
+
+1;
+
+###############################################################################
diff -r 9e5d38da7651 -r 3d35b19abfa7 mail_capability.t
--- a/mail_capability.t Fri Mar 20 16:32:06 2020 +0300
+++ b/mail_capability.t Wed Apr 08 20:25:10 2020 +0200
@@ -3,7 +3,7 @@
 # (C) Sergey Kandaurov
 # (C) Nginx, Inc.
 
-# Tests for imap/pop3/smtp capabilities.
+# Tests for imap/pop3/sieve/smtp capabilities.
 
 ###############################################################################
 
@@ -18,6 +18,7 @@
 use <a class="moz-txt-link-freetext" href="Test::Nginx">Test::Nginx</a>;
 use <a class="moz-txt-link-freetext" href="Test::Nginx::IMAP">Test::Nginx::IMAP</a>;
 use <a class="moz-txt-link-freetext" href="Test::Nginx::POP3">Test::Nginx::POP3</a>;
+use <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>;
 use <a class="moz-txt-link-freetext" href="Test::Nginx::SMTP">Test::Nginx::SMTP</a>;
 
 ###############################################################################
@@ -25,8 +26,8 @@
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-my $t = <a class="moz-txt-link-freetext" href="Test::Nginx">Test::Nginx</a>->new()->has(qw/mail mail_ssl imap pop3 smtp/)
-       ->has_daemon('openssl')->plan(17);
+my $t = <a class="moz-txt-link-freetext" href="Test::Nginx">Test::Nginx</a>->new()->has(qw/mail mail_ssl imap pop3 sieve smtp/)
+       ->has_daemon('openssl')->plan(25);
 
 $t->write_file_expand('nginx.conf', <<'EOF');
 
@@ -81,6 +82,24 @@
     }
 
     server {
+        listen     127.0.0.1:8200;
+        protocol   sieve;
+        sieve_capabilities '"SEE-THIS"';
+    }
+
+    server {
+        listen     127.0.0.1:8201;
+        protocol   sieve;
+        starttls   on;
+    }
+
+    server {
+        listen     127.0.0.1:8202;
+        protocol   sieve;
+        starttls   only;
+    }
+
+    server {
         listen     127.0.0.1:8025;
         protocol   smtp;
         starttls   off;
@@ -188,6 +207,38 @@
 unlike($caps, qr/SASL/, 'pop3 starttls only - no methods');
 like($caps, qr/STLS/, 'pop3 startls only - stls');
 
+# sieve, custom capabilities
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new(PeerAddr => '127.0.0.1:' . port(8200));
+$s->ok('sieve connection completed');
+
+$s->send('CAPABILITY');
+$s->check(qr/^"SEE-THIS"/, 'sieve capability custom');
+$s->check(qr/^"SASL" "PLAIN"/, 'sieve capability sasl');
+$s->ok('sieve capability completed');
+
+# sieve starttls
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new(PeerAddr => '127.0.0.1:' . port(8201));
+$s->read();
+$s->read();
+$s->read();
+$s->check(qr/^"SASL" "PLAIN"/,
+        'sieve capability starttls has plain');
+$s->check(qr/^"STARTTLS"/,
+       'sieve capability starttls');
+
+# sieve starttls only
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new(PeerAddr => '127.0.0.1:' . port(8202));
+$s->read();
+$s->read();
+$s->read();
+$s->check(qr/^"SASL" ""/,
+        'sieve capability starttls only not has plain');
+$s->check(qr/^"STARTTLS"/,
+       'sieve capability starttls only');
+
 # smtp
 
 $s = <a class="moz-txt-link-freetext" href="Test::Nginx::SMTP">Test::Nginx::SMTP</a>->new(PeerAddr => '127.0.0.1:' . port(8025));
diff -r 9e5d38da7651 -r 3d35b19abfa7 mail_sieve.t
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/mail_sieve.t      Wed Apr 08 20:25:10 2020 +0200
@@ -0,0 +1,176 @@
+#!/usr/bin/perl
+
+# (C) Sander Hoentjen
+# (C) Antagonist B.V.
+
+# Tests for nginx mail sieve module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use <a class="moz-txt-link-freetext" href="Test::More">Test::More</a>;
+
+use <a class="moz-txt-link-freetext" href="MIME::Base64">MIME::Base64</a>;
+
+BEGIN { use FindBin; chdir($<a class="moz-txt-link-freetext" href="FindBin::Bin">FindBin::Bin</a>); }
+
+use lib 'lib';
+use <a class="moz-txt-link-freetext" href="Test::Nginx">Test::Nginx</a>;
+use <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+local $SIG{PIPE} = 'IGNORE';
+
+my $t = <a class="moz-txt-link-freetext" href="Test::Nginx">Test::Nginx</a>->new()->has(qw/mail sieve http rewrite/)
+       ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  <a class="moz-txt-link-freetext" href="http://127.0.0.1:8080/mail/auth">http://127.0.0.1:8080/mail/auth</a>;
+
+    server {
+        listen     127.0.0.1:8200;
+        protocol   sieve;
+        sieve_auth  plain cram-md5 external;
+    }
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            set $reply ERROR;
+            set $passw "";
+
+            if ($http_auth_smtp_to ~ example.com) {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_<a class="moz-txt-link-freetext" href="user:$http_auth_pass">user:$http_auth_pass</a>";
+            if ($userpass ~ '^test@<a class="moz-txt-link-freetext" href="example.com:secret$">example.com:secret$</a>') {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_<a class="moz-txt-link-freetext" href="user:$http_auth_salt:$http_auth_pass">user:$http_auth_salt:$http_auth_pass</a>";
+            if ($userpass ~ '^test@example.com:<a class="moz-txt-link-rfc2396E" href="mailto:.*@.*"><.*@.*></a>:0{32}$') {
+                set $reply OK;
+                set $passw secret;
+            }
+
+            set $userpass "$http_auth_<a class="moz-txt-link-freetext" href="method:$http_auth_user:$http_auth_pass">method:$http_auth_user:$http_auth_pass</a>";
+            if ($userpass ~ '^<a class="moz-txt-link-freetext" href="external:test@example.com:$">external:test@example.com:$</a>') {
+                set $reply OK;
+                set $passw secret;
+            }
+
+            add_header Auth-Status $reply;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port %%PORT_8201%%;
+            add_header Auth-Pass $passw;
+            add_header Auth-Wait 1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&<a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE::sieve_test_daemon">Test::Nginx::SIEVE::sieve_test_daemon</a>);
+$t->run()->plan(15);
+
+$t->waitforsocket('127.0.0.1:' . port(8201));
+
+###############################################################################
+
+my $s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->ok('greeting');
+
+# bad auth
+
+$s->send('AUTHENTICATE');
+$s->check(qr/^NO/, 'auth without arguments');
+
+# auth plain
+
+$s->send('AUTHENTICATE "PLAIN" "' . encode_base64(<a class="moz-txt-link-rfc2396E" href="mailto:\0test\@example.com\0bad">"\0test\@example.com\0bad"</a>, '') . '"');
+$s->check(qr/^BYE/, 'auth plain with bad password');
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->ok('greeting');
+$s->send('AUTHENTICATE "PLAIN" "' . encode_base64(<a class="moz-txt-link-rfc2396E" href="mailto:\0test\@example.com\0secret">"\0test\@example.com\0secret"</a>, '') . '"');
+#$s->check(qr/auth plain/, 'blaat');
+$s->ok('auth plain');
+
+# auth login simple
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->read_ok();
+
+$s->send('AUTHENTICATE "LOGIN"');
+$s->check(qr/"VXNlcm5hbWU6"/, 'auth login username challenge');
+
+$s->send('"' . encode_base64('<a class="moz-txt-link-abbreviated" href="mailto:test@example.com">test@example.com</a>', '') . '"');
+$s->check(qr/"UGFzc3dvcmQ6"/, 'auth login password challenge');
+
+$s->send('"' . encode_base64('secret', '') . '"');
+$s->ok('auth login simple');
+
+# auth login with username
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->read_ok();
+
+$s->send('AUTHENTICATE "LOGIN" "' . encode_base64('<a class="moz-txt-link-abbreviated" href="mailto:test@example.com">test@example.com</a>', '') . '"');
+$s->check(qr/"UGFzc3dvcmQ6"/, 'auth login with username password challenge');
+
+$s->send('"' . encode_base64('secret', '') . '"');
+$s->ok('auth login with username');
+
+# auth cram-md5
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->read_ok();
+
+$s->send('AUTHENTICATE "CRAM-MD5"');
+$s->check(qr/"/, 'auth cram-md5 challenge');
+
+$s->send('"' . encode_base64('<a class="moz-txt-link-abbreviated" href="mailto:test@example.com">test@example.com</a> ' . ('0' x 32), '') . '"');
+$s->ok('auth cram-md5');
+
+# auth external
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->read_ok();
+
+$s->send('AUTHENTICATE "EXTERNAL"');
+$s->check(qr/"VXNlcm5hbWU6"/, 'auth external challenge');
+
+$s->send('"' . encode_base64('<a class="moz-txt-link-abbreviated" href="mailto:test@example.com">test@example.com</a>', '') . '"');
+$s->ok('auth external');
+
+# auth external with username
+
+$s = <a class="moz-txt-link-freetext" href="Test::Nginx::SIEVE">Test::Nginx::SIEVE</a>->new();
+$s->read_ok();
+
+$s->send('AUTHENTICATE "EXTERNAL" "' . encode_base64('<a class="moz-txt-link-abbreviated" href="mailto:test@example.com">test@example.com</a>', '') . '"');
+$s->ok('auth external with username');
+
+###############################################################################

</pre>
    </div>
  </body>
</html>