Commit dda815b7 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

POP3: fix end of body detection

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.
parent 8d3efb6b
Loading
Loading
Loading
Loading
+53 −19
Original line number Original line Diff line number Diff line
@@ -1032,33 +1032,67 @@ CURLcode Curl_pop3_write(struct connectdata *conn,


  /* Detect the end-of-body marker, which is 5 bytes:
  /* Detect the end-of-body marker, which is 5 bytes:
     0d 0a 2e 0d 0a. This marker can of course be spread out
     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;
  struct pop3_conn *pop3c = &conn->proto.pop3c;
  size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
  unsigned int i;
  size_t checkleft = POP3_EOB_LEN-pop3c->eob;

  size_t check = (checkmax >= checkleft?checkleft:checkmax);
  /* 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)) {
  for(i=checkstart; i<nread; i++) {
    /* substring match */
    size_t prev = pop3c->eob;
    pop3c->eob += check;
    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) {
    if(pop3c->eob == POP3_EOB_LEN) {
      /* full match, the transfer is done! */
      /* full match, the transfer is done! */
      str[nread - check] = '\0';
      nread -= check;
      k->keepon &= ~KEEP_RECV;
      k->keepon &= ~KEEP_RECV;
      pop3c->eob = 0;
      pop3c->eob = 0;
      return CURLE_OK;
    }
    }
  }
    else if(prev && (prev >= pop3c->eob)) {
  else if(pop3c->eob) {
      /* write out the body part that didn't match */
    /* not a match, but we matched a piece before so we must now
      result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
       send that part as body first, before we move on and send
                                 prev);
       this buffer */
    result = Curl_client_write(conn, CLIENTWRITE_BODY,
                               (char *)POP3_EOB, pop3c->eob);
      if(result)
      if(result)
        return 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);
  result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);


+6 −1
Original line number Original line Diff line number Diff line
@@ -9,6 +9,11 @@ LIST
#
#
# Server-side
# Server-side
<reply>
<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
# When doing LIST, we get the default list output hard-coded in the test
# POP3 server
# POP3 server
<datacheck>
<datacheck>
@@ -25,7 +30,7 @@ LIST
pop3
pop3
</server>
</server>
 <name>
 <name>
POP3 LIST messages
POP3 LIST messages from *SLOW* server
 </name>
 </name>
 <command>
 <command>
pop3://%HOSTIP:%POP3PORT/ -u user:secret
pop3://%HOSTIP:%POP3PORT/ -u user:secret