From eff006788183cb6da4d6d6876d649d6e12369c36 Mon Sep 17 00:00:00 2001
From: Jiri Hruska <jirka@fud.cz>
Date: Thu, 3 Jan 2013 14:59:30 +0100
Subject: [PATCH 3/3] IMAP: Add tests for the new functionality, update
 ftpserver.pl

* Enriched IMAP capabilities of ftpserver.pl:
    - Added support for APPEND   - Saves uploaded data to log/upload.$testno
    - Added support for LIST     - Returns hardcoded list of mailboxes
    - Added support for STORE    - Returns hardcoded updated flags
    - Changed handling of SELECT - Returns much more information in the usual
                                   set of untagged responses; uses hardcoded
                                   data from an example in the IMAP RFC
    - Changed handling of FETCH  - Fixed response format

* Changed 2, added 7 tests dealing with IMAP:
    - test801  - IMAP FETCH BODY
    + test816  - IMAP FETCH Section
    + test817  - IMAP SELECT UIDVALIDITY Success
    + test818  - IMAP SELECT UIDVALIDITY Failure
    + test819  - IMAP doesn't do SELECT if reusing the same mailbox
    + test820  - IMAP APPEND
    + test821  - IMAP STORE (CUSTOMREQUEST)
    + test822  - IMAP LIST (CUSTOMREQUEST)
    - test1321 - IMAP FETCH tunneled through HTTP proxy

---
 tests/data/Makefile.am |   3 +-
 tests/data/test1321    |  10 ++--
 tests/data/test801     |  12 ++---
 tests/data/test816     |  44 +++++++++++++++++
 tests/data/test817     |  45 ++++++++++++++++++
 tests/data/test818     |  43 +++++++++++++++++
 tests/data/test819     |  45 ++++++++++++++++++
 tests/data/test820     |  60 +++++++++++++++++++++++
 tests/data/test821     |  42 +++++++++++++++++
 tests/data/test822     |  43 +++++++++++++++++
 tests/ftpserver.pl     | 126 ++++++++++++++++++++++++++++++++++++++++++++-----
 11 files changed, 450 insertions(+), 23 deletions(-)

diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index aaa75e9..0cf4ba4 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -58,7 +58,8 @@ test700 test701 test702	test703 test704 test705 test706 test707 test708 \
 test709 test710 \
 test800 test801 test802	\
 test803 test804 test805 test806 test807 test808 test809 test810 test811	\
-test812 test813 test814 test815 \
+test812 test813 test814 test815 test816 test817 test818 test819 test820 \
+test821 test822 \
 test1000 test1001 test1002 test1003 test1004 test1005	\
 test1006 test1007 test1008 test1009 test1010 test1011 test1012 test1013	\
 test1014 test1015 test1016 test1017 test1018 test1019 test1020 test1021	\
diff --git a/tests/data/test1321 b/tests/data/test1321
index 7d45d96..7a124e4 100644
--- a/tests/data/test1321
+++ b/tests/data/test1321
@@ -45,7 +45,7 @@ http-proxy
 IMAP FETCH tunneled through HTTP proxy
  </name>
  <command>
-imap://%HOSTIP:%IMAPPORT/1321 -u user:secret -p -x %HOSTIP:%PROXYPORT
+'imap://%HOSTIP:%IMAPPORT/1321/;uid=1' -u user:secret -p -x %HOSTIP:%PROXYPORT
 </command>
 </client>
 
@@ -56,10 +56,10 @@ imap://%HOSTIP:%IMAPPORT/1321 -u user:secret -p -x %HOSTIP:%PROXYPORT
 ^User-Agent: curl/.*
 </strip>
 <protocol>
-B LOGIN user secret
-C SELECT 1321
-D FETCH 1 BODY[TEXT]
-A LOGOUT
+A001 LOGIN user secret
+A002 SELECT 1321
+A003 FETCH 1 BODY[]
+A004 LOGOUT
 </protocol>
 <proxy>
 CONNECT %HOSTIP:%IMAPPORT HTTP/1.1
diff --git a/tests/data/test801 b/tests/data/test801
index 271d8d1..71118cc 100644
--- a/tests/data/test801
+++ b/tests/data/test801
@@ -27,10 +27,10 @@ body
 imap
 </server>
  <name>
-IMAP FETCH
+IMAP FETCH BODY
  </name>
  <command>
-imap://%HOSTIP:%IMAPPORT/801 -u user:secret
+'imap://%HOSTIP:%IMAPPORT/801/;uid=123' -u user:secret
 </command>
 </client>
 
@@ -38,10 +38,10 @@ imap://%HOSTIP:%IMAPPORT/801 -u user:secret
 # Verify data after the test has been "shot"
 <verify>
 <protocol>
-B LOGIN user secret
-C SELECT 801
-D FETCH 1 BODY[TEXT]
-A LOGOUT
+A001 LOGIN user secret
+A002 SELECT 801
+A003 FETCH 123 BODY[]
+A004 LOGOUT
 </protocol>
 </verify>
 </testcase>
diff --git a/tests/data/test816 b/tests/data/test816
new file mode 100644
index 0000000..288262d
--- /dev/null
+++ b/tests/data/test816
@@ -0,0 +1,44 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+FETCH
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+body
+
+--
+  yours sincerely
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP FETCH Section
+ </name>
+ <command>
+-u user:secret 'imap://%HOSTIP:%IMAPPORT/816/;uid=123/;section=1'
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 LOGIN user secret
+A002 SELECT 816
+A003 FETCH 123 BODY[1]
+A004 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test817 b/tests/data/test817
new file mode 100644
index 0000000..b999ecf
--- /dev/null
+++ b/tests/data/test817
@@ -0,0 +1,45 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+SELECT
+UIDVALIDITY
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+body
+
+--
+  yours sincerely
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP SELECT UIDVALIDITY Success
+ </name>
+ <command>
+-u user:secret 'imap://%HOSTIP:%IMAPPORT/817;uidvalidity=3857529045/;uid=123/;section=1'
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 LOGIN user secret
+A002 SELECT 817
+A003 FETCH 123 BODY[1]
+A004 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test818 b/tests/data/test818
new file mode 100644
index 0000000..0555ed6
--- /dev/null
+++ b/tests/data/test818
@@ -0,0 +1,43 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+SELECT
+UIDVALIDITY
+FAILURE
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP SELECT UIDVALIDITY Failure
+ </name>
+ <command>
+-u user:secret 'imap://%HOSTIP:%IMAPPORT/818;uidvalidity=12345/;uid=123'
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+# CURLE_REMOTE_FILE_NOT_FOUND - UIDVALIDITY mismatch
+<errorcode>
+78
+</errorcode>
+<protocol>
+A001 LOGIN user secret
+A002 SELECT 818
+A003 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test819 b/tests/data/test819
new file mode 100644
index 0000000..b6de7ce
--- /dev/null
+++ b/tests/data/test819
@@ -0,0 +1,45 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+SELECT
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+body
+
+--
+  yours sincerely
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP doesn't do SELECT if reusing the same mailbox
+ </name>
+ <command>
+-u user:secret 'imap://%HOSTIP:%IMAPPORT/819/;uid=123/;section=1' 'imap://%HOSTIP:%IMAPPORT/819/;uid=456/;section=2.3'
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 LOGIN user secret
+A002 SELECT 819
+A003 FETCH 123 BODY[1]
+A004 FETCH 456 BODY[2.3]
+A005 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test820 b/tests/data/test820
new file mode 100644
index 0000000..9bcc02a
--- /dev/null
+++ b/tests/data/test820
@@ -0,0 +1,60 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+APPEND
+UPLOAD
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP APPEND
+ </name>
+ <command>
+-u user:secret -T log/upload820 'imap://%HOSTIP:%IMAPPORT/820'
+</command>
+<file name="log/upload820">
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+From: Fred Foobar <foobar@example.COM>
+Subject: afternoon meeting
+To: joe@example.com
+Message-Id: <B27397-0100000@example.COM>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+
+Hello Joe, do you think we can meet at 3:30 tomorrow?
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 LOGIN user secret
+A002 APPEND 820 (\Seen) {295}
+A003 LOGOUT
+</protocol>
+<upload>
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+From: Fred Foobar <foobar@example.COM>
+Subject: afternoon meeting
+To: joe@example.com
+Message-Id: <B27397-0100000@example.COM>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+
+Hello Joe, do you think we can meet at 3:30 tomorrow?
+</upload>
+</verify>
+</testcase>
diff --git a/tests/data/test821 b/tests/data/test821
new file mode 100644
index 0000000..a9c6fb0
--- /dev/null
+++ b/tests/data/test821
@@ -0,0 +1,42 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+STORE
+CUSTOMREQUEST
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+* 123 FETCH (FLAGS (\Seen \Deleted))
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP STORE (CUSTOMREQUEST)
+ </name>
+ <command>
+-u user:secret -X 'STORE 123 +Flags \Deleted' 'imap://%HOSTIP:%IMAPPORT/821'
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 LOGIN user secret
+A002 SELECT 821
+A003 STORE 123 +Flags \Deleted
+A004 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test822 b/tests/data/test822
new file mode 100644
index 0000000..633236d
--- /dev/null
+++ b/tests/data/test822
@@ -0,0 +1,43 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+LIST
+CUSTOMREQUEST
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+* LIST () "/" blurdybloop
+* LIST (\Noselect) "/" foo
+* LIST () "/" foo/bar
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP LIST (CUSTOMREQUEST)
+ </name>
+ <command>
+-u user:secret -X 'LIST "" *' 'imap://%HOSTIP:%IMAPPORT/'
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 LOGIN user secret
+A002 LIST "" *
+A003 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl
index 45b3436..09200d9 100755
--- a/tests/ftpserver.pl
+++ b/tests/ftpserver.pl
@@ -579,12 +579,14 @@ sub protocolsetup {
     }
     elsif($proto eq 'imap') {
         %commandfunc = (
+            'APPEND' => \&APPEND_imap,
             'FETCH'  => \&FETCH_imap,
+            'LIST'   => \&LIST_imap,
             'SELECT' => \&SELECT_imap,
+            'STORE'  => \&STORE_imap
         );
         %displaytext = (
             'LOGIN'  => ' OK We are happy you popped in!',
-            'SELECT' => ' OK selection done',
             'LOGOUT' => ' OK thanks for the fish',
         );
         @welcome = (
@@ -761,27 +763,32 @@ my $selected;
 
 sub SELECT_imap {
     my ($testno) = @_;
-    my @data;
-    my $size;
 
     logmsg "SELECT_imap got test $testno\n";
 
+    # Example from RFC 3501, 6.3.1. SELECT Command
+    sendcontrol "* 172 EXISTS\r\n";
+    sendcontrol "* 1 RECENT\r\n";
+    sendcontrol "* OK [UNSEEN 12] Message 12 is first unseen\r\n";
+    sendcontrol "* OK [UIDVALIDITY 3857529045] UIDs valid\r\n";
+    sendcontrol "* OK [UIDNEXT 4392] Predicted next UID\r\n";
+    sendcontrol "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n";
+    sendcontrol "* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited\r\n";
+    sendcontrol "$cmdid OK [READ-WRITE] SELECT completed\r\n";
+
     $selected = $testno;
 
     return 0;
 }
 
-
 sub FETCH_imap {
-     my ($testno) = @_;
+     my ($uid, $how) = split(/ +/, $_[0]);
      my @data;
      my $size;
 
-     logmsg "FETCH_imap got test $testno\n";
+     logmsg "FETCH_imap got $_[0]\n";
 
-     $testno = $selected;
-
-     if($testno =~ /^verifiedserver$/) {
+     if($selected =~ /^verifiedserver$/) {
          # this is the secret command that verifies that this actually is
          # the curl test server
          my $response = "WE ROOLZ: $$\r\n";
@@ -794,7 +801,7 @@ sub FETCH_imap {
      else {
          logmsg "retrieve a mail\n";
 
-         $testno =~ s/^([^0-9]*)//;
+         my $testno = $selected;
          my $testpart = "";
          if ($testno > 10000) {
              $testpart = $testno % 10000;
@@ -811,17 +818,114 @@ sub FETCH_imap {
          $size += length($_);
      }
 
-     sendcontrol "* FETCH starts {$size}\r\n";
+     sendcontrol "* $uid FETCH ($how {$size}\r\n";
 
      for my $d (@data) {
          sendcontrol $d;
      }
 
+     sendcontrol ")\r\n";
      sendcontrol "$cmdid OK FETCH completed\r\n";
 
      return 0;
 }
 
+sub APPEND_imap {
+    my ($args) = @_;
+
+    logmsg "APPEND_imap got $_[0]\r\n";
+
+    $args =~ /^([^ ]+) [^{]*\{(\d+)\}$/;
+    my ($folder, $size) = ($1, $2);
+
+    sendcontrol "+ Ready for literal data\r\n";
+
+    my $testno = $folder;
+    my $filename = "log/upload.$testno";
+
+    logmsg "Store test number $testno in $filename\n";
+
+    open(FILE, ">$filename") ||
+        return 0; # failed to open output
+
+    my $received = 0;
+    my $line;
+    while(5 == (sysread \*SFREAD, $line, 5)) {
+        if($line eq "DATA\n") {
+            sysread \*SFREAD, $line, 5;
+
+            my $chunksize = 0;
+            if($line =~ /^([0-9a-fA-F]{4})\n/) {
+                $chunksize = hex($1);
+            }
+
+            read_mainsockf(\$line, $chunksize);
+
+            my $left = $size - $received;
+            my $datasize = ($left > $chunksize) ? $chunksize : $left;
+
+            if($datasize > 0) {
+              logmsg "> Appending $datasize bytes to file\n";
+              print FILE substr($line, 0, $datasize) if(!$nosave);
+              $line = substr($line, $datasize);
+
+              $received += $datasize;
+              if($received == $size) {
+                logmsg "Received all data, waiting for final CRLF.\n";
+              }
+            }
+
+            if($received == $size && $line eq "\r\n") {
+              last;
+            }
+        }
+        elsif($line eq "DISC\n") {
+            logmsg "Unexpected disconnect!\n";
+            last;
+        }
+        else {
+            logmsg "No support for: $line";
+            last;
+        }
+    }
+
+    if($nosave) {
+        print FILE "$size bytes would've been stored here\n";
+    }
+    close(FILE);
+
+    logmsg "received $size bytes upload\n";
+
+    sendcontrol "$cmdid OK APPEND completed\r\n";
+
+    return 0;
+}
+
+sub STORE_imap {
+    my ($uid, $what) = split(/ +/, $_[0]);
+
+    logmsg "STORE_imap got $_[0]\n";
+
+    sendcontrol "* $uid FETCH (FLAGS (\\Seen \\Deleted))\r\n";
+    sendcontrol "$cmdid OK STORE completed\r\n";
+
+    return 0;
+}
+
+sub LIST_imap {
+    my ($reference, $mailbox) = split(/ +/, $_[0]);
+
+    logmsg "LIST_imap got $_[0]\n";
+
+    sendcontrol '* LIST () "/" blurdybloop'."\r\n";
+    sendcontrol '* LIST (\Noselect) "/" foo'."\r\n";
+    sendcontrol '* LIST () "/" foo/bar'."\r\n";
+    sendcontrol "$cmdid OK LIST Completed\r\n";
+
+    return 0;
+}
+
+
 ################
 ################ POP3 commands
 ################
-- 
1.8.1

