From 88827b6556abdf5863622d73a45aec6511ef643a Mon Sep 17 00:00:00 2001
From: Dan Fandrich <dan@coneharvesters.com>
Date: Tue, 12 Apr 2011 17:14:22 -0700
Subject: [PATCH] First attempt to improve POP3 EOB detection

It still has problems with corner cases, and it still doesn't
fix test811, it's really ugly, but it does let the SLOWDOWN
test810 to work.
---
 lib/pop3.c         |   67 ++++++++++++++++++++++++++++++++++++++++++---------
 tests/data/test810 |    6 ++++-
 tests/data/test811 |    2 +-
 3 files changed, 61 insertions(+), 14 deletions(-)

diff --git a/lib/pop3.c b/lib/pop3.c
index 79af8fc..2435a71 100644
--- a/lib/pop3.c
+++ b/lib/pop3.c
@@ -479,8 +479,11 @@ static CURLcode pop3_list(struct connectdata *conn)
   CURLcode result = CURLE_OK;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
 
-  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST %s", pop3c->mailbox);
-  if(result)
+  if (*pop3c->mailbox)
+    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST %s", pop3c->mailbox);
+  else
+    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST");
+  if (result)
     return result;
 
   if (strlen(pop3c->mailbox))
@@ -1010,7 +1013,8 @@ static CURLcode pop3_setup_connection(struct connectdata * conn)
   return CURLE_OK;
 }
 
-/* this is the 5-bytes End-Of-Body marker for POP3 */
+/* This is the 5-bytes End-Of-Body marker for POP3. In the case of an empty
+   LIST, only the last 3 bytes represent the End-Of-Body. */
 #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
 #define POP3_EOB_LEN 5
 
@@ -1023,41 +1027,80 @@ CURLcode Curl_pop3_write(struct connectdata *conn,
                          size_t nread)
 {
   /* This code could be made into a special function in the handler struct. */
-  CURLcode result;
+  CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct SingleRequest *k = &data->req;
 
   /* Detect the end-of-body marker, which is 5 bytes:
      0d 0a 2e 0d 0a. This marker can of course be spread out
-     over up to 5 different data chunks. Deal with it! */
+     over up to 5 different data chunks, but must always be found at the
+     end of the received buffer. Deal with it! */
+  /* Note that this currently fails to detect end-of-body if data plus
+     the first 1-4 bytes of the EOB arrive in the first segment,  and
+     the remaining byte(s) arrive in the next segment. The memcmp() is then
+     not aligned correctly for the bytes in the first segment. An especially
+     devious case is "datadata\r\n\r\n\r\n." */
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
+  const size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
   size_t checkleft = POP3_EOB_LEN-pop3c->eob;
-  size_t check = (checkmax >= checkleft?checkleft:checkmax);
+  size_t check;
+infof(conn->data, "Curl_pop3_write eob=%d cl=%d nread=%d str=0x%x\n", pop3c->eob, checkleft, nread, str[0]);
 
-  if(!memcmp(POP3_EOB, &str[nread - check], check)) {
+  if (pop3c->eob && (nread > checkleft)) {
+    /* not a match, but we matched a piece before so we must now
+       send that part as body first, before we move on and send
+       this buffer */
+    result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                               (char *)POP3_EOB, pop3c->eob);
+    /* this couldn't possibly be a continuation of a previous match */
+    pop3c->eob = 0;
+    if(result)
+      return result;
+  }
+  checkleft = POP3_EOB_LEN-pop3c->eob;
+  check = (checkmax >= checkleft?checkleft:checkmax);
+  
+  if (!memcmp(&POP3_EOB[pop3c->eob], &str[nread - check], check)) {
     /* substring match */
     pop3c->eob += check;
     if(pop3c->eob == POP3_EOB_LEN) {
       /* full match, the transfer is done! */
       str[nread - check] = '\0';
-      nread -= check;
       k->keepon &= ~KEEP_RECV;
       pop3c->eob = 0;
     }
+    nread -= check; /* don't write the EOB */
   }
-  else if(pop3c->eob) {
+  else if (pop3c->eob) {
     /* not a match, but we matched a piece before so we must now
        send that part as body first, before we move on and send
        this buffer */
     result = Curl_client_write(conn, CLIENTWRITE_BODY,
                                (char *)POP3_EOB, pop3c->eob);
+    /* not a match, so start over searching */
+    pop3c->eob = 0;
     if(result)
       return result;
-    pop3c->eob = 0;
+
+    /* If we eliminated a false match, try again now */
+    checkleft = POP3_EOB_LEN-pop3c->eob;
+    check = (checkmax >= checkleft?checkleft:checkmax);
+    if (!memcmp(&POP3_EOB[pop3c->eob], &str[nread - check], check)) {
+      /* substring match */
+      pop3c->eob += check;
+      if(pop3c->eob == POP3_EOB_LEN) {
+	/* full match, the transfer is done! */
+	str[nread - check] = '\0';
+	k->keepon &= ~KEEP_RECV;
+	pop3c->eob = 0;
+      }
+      nread -= check; /* don't write the EOB */
+    }
+
   }
 
-  result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
+  if (nread)
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
 
   return result;
 }
diff --git a/tests/data/test810 b/tests/data/test810
index d4f7597..469bbb7 100644
--- a/tests/data/test810
+++ b/tests/data/test810
@@ -16,6 +16,10 @@ LIST
 2 4294967400
 4 200
 </datacheck>
+# Test the end-of-body detection by writing one byte at a time
+<servercmd>
+SLOWDOWN
+</servercmd>
 </reply>
 
 #
@@ -38,7 +42,7 @@ pop3://%HOSTIP:%POP3PORT/ -u user:secret
 <protocol>
 USER user
 PASS secret
-LIST 
+LIST
 QUIT
 </protocol>
 </verify>
diff --git a/tests/data/test811 b/tests/data/test811
index 850e5e2..ca107ab 100644
--- a/tests/data/test811
+++ b/tests/data/test811
@@ -36,7 +36,7 @@ pop3://%HOSTIP:%POP3PORT/ -u user:secret
 <protocol>
 USER user
 PASS secret
-LIST 
+LIST
 QUIT
 </protocol>
 </verify>
-- 
1.5.3.2


