cURL / Mailing Lists / curl-library / Single Mail

curl-library

Using Pop3/Pop3s protocol , curl hangs in some condition

From: nagai h <hideon_at_hotmail.com>
Date: Sat, 18 Dec 2010 12:55:05 +0000

I found curl hangs when using protocol pop3/pop3s version curl-7.21.3.
When I receive data like next, curl hangs.

curl_write_callback receive data end with "\x0d\x0a\x2e"
  "Return-Path:....\x0d\x0a\x2e"
and next curl_write_callback receive data
  "\x0d\x0a"

I think that function Curl_pop3_write in pop3.c, pop3c-eob doesn't work well.
In above case, "memcmp(POP3_EOB, &str[nread - check], check)" never retrun 0.

And send LIST command, when pop3 returns 0 mail result, curl hangs.
In this case, pop3 server returns only 3bytes exclude header.

curl_write_callback receive data
  "\x2e\x0d\x0a"

So when send LIST command pop3c-eob must be 2, before Curl_pop3_write call in pop3.c.

I fixed code like this, it seems work well.
And also I try to modify code that will be able to use TOP/DELE/RSET command.

--- pop3.h.orig    2010-09-18 23:00:22.000000000 +0900
+++ pop3.h    2010-12-17 21:50:13.000000000 +0900
@@ -35,6 +35,9 @@
   POP3_LIST,
   POP3_RETR,
   POP3_QUIT,
+  POP3_TOP,
+  POP3_DELE,
+  POP3_RSET,
   POP3_LAST  /* never used */
 } pop3state;
 

--- pop3.c.orig    2010-12-06 14:41:18.000000000 +0900
+++ pop3.c    2010-12-18 20:36:28.000000000 +0900
@@ -235,6 +235,9 @@
     "LIST",
     "RETR",
     "QUIT",
+    "TOP",
+    "DELE",
+    "RSET",
     /* LAST */
   };
 #endif
@@ -401,6 +404,9 @@
     return CURLE_RECV_ERROR;
   }
 
+  /* pop3 server may return 'mail 0', receive data will be only 3 bytes */
+  pop3c->eob = 2;
+
   /* POP3 download */
   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
                       -1, NULL); /* no upload here */
@@ -423,17 +429,60 @@
   return result;
 }
 
-/* start the DO phase for RETR */
+/* for DELE/RSET responses */
+static CURLcode pop3_state_delerset_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *pop3 = data->state.proto.pop3;
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != 'O') {
+    failf(data, "POP3 Command Failed %c", pop3code);
+    state(conn, POP3_STOP);
+    result = CURLE_FUNCTION_NOT_FOUND;
+  }
+
+  state(conn, POP3_STOP);
+  return result;
+}
+
+/* start the DO phase for RETR and so on*/
 static CURLcode pop3_retr(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
+  pop3state newstate = -1;
 
-  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
+  /* check input command */
+  if(strstr(pop3c->mailbox, "RETR ") == pop3c->mailbox) {
+         newstate = POP3_RETR;
+  }
+  else if(strstr(pop3c->mailbox, "TOP ") == pop3c->mailbox) {
+         newstate = POP3_TOP;
+  }
+  else if(strstr(pop3c->mailbox, "DELE ") == pop3c->mailbox) {
+         newstate = POP3_DELE;
+  }
+  else if(strstr(pop3c->mailbox, "RSET") == pop3c->mailbox) {
+         newstate = POP3_RSET;
+  }
+
+  /* default */
+  if(newstate == -1) {
+         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
+  }
+  else {
+  /* retr top dele rset command ok! */
+         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", pop3c->mailbox);
+  }
   if(result)
     return result;
 
-  state(conn, POP3_RETR);
+  /* send ok then test it */
+  state(conn, newstate);
   return result;
 }
 
@@ -503,6 +552,7 @@
       break;
 
     case POP3_RETR:
+    case POP3_TOP:
       result = pop3_state_retr_resp(conn, pop3code, pop3c->state);
       break;
 
@@ -510,6 +560,11 @@
       result = pop3_state_list_resp(conn, pop3code, pop3c->state);
       break;
 
+    case POP3_DELE:
+    case POP3_RSET:
+      result = pop3_state_delerset_resp(conn, pop3code, pop3c->state);
+      break;
+
     case POP3_QUIT:
       /* fallthrough, just stop! */
     default:
@@ -969,7 +1024,7 @@
 }
 
 /* this is the 5-bytes End-Of-Body marker for POP3 */
-#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
+const char POP3_EOB[] = "\x0d\x0a\x2e\x0d\x0a";
 #define POP3_EOB_LEN 5
 
 /*
@@ -989,11 +1044,33 @@
      0d 0a 2e 0d 0a. This marker can of course be spread out
      over up to 5 different data chunks. Deal with it! */
   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);
+  size_t check = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
 
-  if(!memcmp(POP3_EOB, &str[nread - check], check)) {
+  if(pop3c->eob) {
+    /* continue POP3_EOB_LEN */
+    if(!memcmp(POP3_EOB + pop3c->eob, &str[0], checkleft)) {
+      /* full match */
+      str[checkleft] = '\0';
+      nread -= checkleft;
+      k->keepon &= ~KEEP_RECV;
+      pop3c->eob = 0;
+    }
+    else{
+      /* we matched a piece before so we must now
+         send that part as body first, before we move on and send
+         this buffer */
+      if(pop3c->eob > 2){
+        result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                (char *)(POP3_EOB + 2), pop3c->eob - 2);
+        if(result)
+          return result;
+      }
+      pop3c->eob = 0;
+    }
+  }
+
+  if(!memcmp(POP3_EOB + (POP3_EOB_LEN - check), &str[nread - check], check)) {
     /* substring match */
     pop3c->eob += check;
     if(pop3c->eob == POP3_EOB_LEN) {
@@ -1004,15 +1081,20 @@
       pop3c->eob = 0;
     }
   }
-  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;
+  /* part of POP3_EOB will exist in last string, so check it */
+  else{
+    for(checkleft = POP3_EOB_LEN - 1; checkleft > 0; checkleft--){
+      if((nread >= checkleft) && !memcmp(POP3_EOB, &str[nread - checkleft], checkleft)) {
+        pop3c->eob = checkleft;
+        if(checkleft > 2){
+          /* subtract only if match length > 2 */
+          str[nread - checkleft + 2] = '\0';
+          /* Send data "\x0d\x0a" (\r\n) */
+          nread -= (checkleft - 2);
+        }
+        break;
+      }
+    }
   }
 
   result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);

Thank you for reading this report.
                                               
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2010-12-18