]> granicus.if.org Git - curl/commitdiff
POP3: fix end of body detection
authorDaniel Stenberg <daniel@haxx.se>
Mon, 28 Nov 2011 22:02:35 +0000 (23:02 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 28 Nov 2011 23:25:21 +0000 (00:25 +0100)
Curl_pop3_write() now has a state machine that scans for the end of a
POP3 body so that the CR LF '.' CR LF sequence can come in everything
from one up to five subsequent packets.

Test case 810 is modified to use SLOWDOWN which makes the server pause
between each single byte and thus makes the POP3 body get sent to curl
basically one byte at a time.

lib/pop3.c
tests/data/test810

index 8fd8ab01f517eb0af57b751b10784b62418ae8fa..a47717a71f35e63b69b566f8d26b3d22fb59676c 100644 (file)
@@ -1032,33 +1032,67 @@ CURLcode Curl_pop3_write(struct connectdata *conn,
 
   /* 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.
+  */
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  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);
+  unsigned int i;
+
+  /* since the EOB string must be within the last 5 bytes, get the index
+     position of where to start to scan for it */
+  size_t checkstart = (nread>POP3_EOB_LEN)?nread-POP3_EOB_LEN:0;
+
+  if(checkstart) {
+    /* write out the first piece, if any */
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, str, checkstart);
+    if(result)
+      return result;
+    pop3c->eob=0;
+  }
 
-  if(!memcmp(POP3_EOB, &str[nread - check], check)) {
-    /* substring match */
-    pop3c->eob += check;
+  for(i=checkstart; i<nread; i++) {
+    size_t prev = pop3c->eob;
+    switch(str[i]) {
+    case 0x0d:
+      if((pop3c->eob == 0) || (pop3c->eob == 3))
+        pop3c->eob++;
+      else
+        /* if it wasn't 0 or 3, it restarts the pattern match again */
+        pop3c->eob=1;
+      break;
+    case 0x0a:
+      if((pop3c->eob == 1) || (pop3c->eob == 4))
+        pop3c->eob++;
+      else
+        pop3c->eob=0;
+      break;
+    case 0x2e:
+      if(pop3c->eob == 2)
+        pop3c->eob++;
+      else
+        pop3c->eob=0;
+      break;
+    default:
+      pop3c->eob=0;
+      break;
+    }
     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;
+      return CURLE_OK;
+    }
+    else if(prev && (prev >= pop3c->eob)) {
+      /* write out the body part that didn't match */
+      result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
+                                 prev);
+      if(result)
+        return result;
     }
   }
-  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);
-    if(result)
-      return result;
-    pop3c->eob = 0;
-  }
+
+  if(pop3c->eob)
+    /* while EOB is matching, don't output it! */
+    return CURLE_OK;
 
   result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
 
index 1c31042b69ff47c60243deca18d41e89ee7b118f..09f9562a37393527f88e34300be3292e06844cfa 100644 (file)
@@ -9,6 +9,11 @@ LIST
 #
 # Server-side
 <reply>
+# We use SLOWDOWN to really exercise the end-of-body parsing over multiple
+# packets
+<servercmd>
+SLOWDOWN
+</servercmd>
 # When doing LIST, we get the default list output hard-coded in the test
 # POP3 server
 <datacheck>
@@ -25,7 +30,7 @@ LIST
 pop3
 </server>
  <name>
-POP3 LIST messages
+POP3 LIST messages from *SLOW* server
  </name>
  <command>
 pop3://%HOSTIP:%POP3PORT/ -u user:secret