From a2c2b23dbd96978bc36776b227764393003bf22c Mon Sep 17 00:00:00 2001
From: Jiri Hruska <jirka@fud.cz>
Date: Thu, 3 Jan 2013 14:58:32 +0100
Subject: [PATCH 1/3] IMAP: Introduce Curl_pp_extractbody(), fix PP with
 buffered response

* Refactored the code which extracts up to N bytes from the PP buffer (cache)
  to a library-global function Curl_pp_extractbody(), as meddling with internal
  fields of struct pingpong IMHO better belongs to the pingpong.c and it can be
  useful in other modules too.

* Fixed the way Curl_pp_multi_statemach() and Curl_pp_easy_statemach() behave
  when there is still something in the PP buffer (cache). Currently it does
  block waiting till next "data ready" event is signalled on the input socket.
  Changed it so that if reading, there is something in cache and it has not
  been processed yet, it just returns telling the PP user that there is another
  response available right away.

---
 lib/curl_imap.c     | 37 ++++++--------------------------
 lib/curl_pingpong.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++------
 lib/curl_pingpong.h | 10 +++++++++
 3 files changed, 73 insertions(+), 36 deletions(-)

diff --git a/lib/curl_imap.c b/lib/curl_imap.c
index 8175daa..e1b25da 100644
--- a/lib/curl_imap.c
+++ b/lib/curl_imap.c
@@ -608,36 +608,13 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn,
 
     infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
 
-    if(pp->cache) {
-      /* At this point there is a bunch of data in the header "cache" that is
-         actually body content, send it as body and then skip it. Do note
-         that there may even be additional "headers" after the body. */
-      size_t chunk = pp->cache_size;
-
-      if(chunk > (size_t)filesize)
-        /* the conversion from curl_off_t to size_t is always fine here */
-        chunk = (size_t)filesize;
-
-      result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
-      if(result)
-        return result;
-
-      filesize -= chunk;
-
-      /* we've now used parts of or the entire cache */
-      if(pp->cache_size > chunk) {
-        /* part of, move the trailing data to the start and reduce the size */
-        memmove(pp->cache, pp->cache+chunk,
-                pp->cache_size - chunk);
-        pp->cache_size -= chunk;
-      }
-      else {
-        /* cache is drained */
-        Curl_safefree(pp->cache);
-        pp->cache = NULL;
-        pp->cache_size = 0;
-      }
-    }
+    /* At this point there might be a bunch of data in the header "cache" that
+       is actually body content. Call the PP function which sends it to the
+       library user as body and skips it. Do note that there may even be
+       additional responses ("headers") after the body. */
+    result = Curl_pp_extractbody(pp, &filesize);
+    if(result)
+      return result;
 
     infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
 
diff --git a/lib/curl_pingpong.c b/lib/curl_pingpong.c
index d28e78a..ad53c44 100644
--- a/lib/curl_pingpong.c
+++ b/lib/curl_pingpong.c
@@ -96,9 +96,15 @@ CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
     return CURLE_OPERATION_TIMEDOUT;
   }
 
-  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
-                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
-                         0);
+  if(!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size)
+    /* If we are reading, there is something in cache and it has not been
+       processed yet, just tell the PP user that something new has come.
+       Waiting for new data in this case would just block till timeout. */
+    rc = 1;
+  else
+    rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+                           pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+                           0);
 
   if(rc == -1) {
     failf(data, "select/poll error");
@@ -136,9 +142,15 @@ CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
   if(timeout_ms < interval_ms)
     interval_ms = timeout_ms;
 
-  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
-                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
-                         interval_ms);
+  if(!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size)
+    /* If we are reading, there is something in cache and it has not been
+       processed yet, just tell the PP user that something new has come.
+       Waiting for new data in this case would just block till timeout. */
+    rc = 1;
+  else
+    rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+                           pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+                           interval_ms);
 
   if(Curl_pgrsUpdate(conn))
     result = CURLE_ABORTED_BY_CALLBACK;
@@ -524,6 +536,44 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp)
   return CURLE_OK;
 }
 
+/* Used to extract data of specified size from the header "cache" and send it
+ * to the library user by Curl_client_write(CLIENTWRITE_BODY). The passed size
+ * variable is updated. If not 0 when the function returns, the caller should
+ * set up a classic download transfer to take care of the rest. */
+CURLcode Curl_pp_extractbody(struct pingpong *pp, curl_off_t *size)
+{
+  CURLcode result = CURLE_OK;
+
+  if(pp->cache) {
+    size_t chunk = pp->cache_size;
+
+    if(chunk > (size_t)*size)
+      /* the conversion from curl_off_t to size_t is always fine here */
+      chunk = (size_t)*size;
+
+    result = Curl_client_write(pp->conn, CLIENTWRITE_BODY, pp->cache, chunk);
+    if(result)
+      return result;
+
+    *size -= chunk;
+
+    /* we've now used parts of or the entire cache */
+    if(pp->cache_size > chunk) {
+      /* part of, move the trailing data to the start and reduce the size */
+      memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
+      pp->cache_size -= chunk;
+    }
+    else {
+      /* cache is drained */
+      free(pp->cache);
+      pp->cache = NULL;
+      pp->cache_size = 0;
+    }
+  }
+
+  return result;
+}
+
 CURLcode Curl_pp_disconnect(struct pingpong *pp)
 {
   if(pp->cache) {
diff --git a/lib/curl_pingpong.h b/lib/curl_pingpong.h
index 7706078..312df72 100644
--- a/lib/curl_pingpong.h
+++ b/lib/curl_pingpong.h
@@ -128,6 +128,16 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
                           size_t *size); /* size of the response */
 
 
+/*
+ * Curl_pp_extractbody()
+ *
+ * Used to extract data of specified size from the header "cache" and send it
+ * to the library user by Curl_client_write(CLIENTWRITE_BODY). The passed size
+ * variable is updated. If not 0 when the function returns, the caller should
+ * set up a classic download transfer to take care of the rest.
+ */
+CURLcode Curl_pp_extractbody(struct pingpong *pp, curl_off_t *size);
+
 CURLcode Curl_pp_flushsend(struct pingpong *pp);
 
 /* call this when a pingpong connection is disconnected */
-- 
1.8.1

