Index: docs/libcurl/Makefile.am
===================================================================
RCS file: /cvsroot/curl/curl/docs/libcurl/Makefile.am,v
retrieving revision 1.26
diff -u -r1.26 Makefile.am
--- docs/libcurl/Makefile.am	4 Oct 2007 22:05:25 -0000	1.26
+++ docs/libcurl/Makefile.am	23 Dec 2007 12:40:40 -0000
@@ -18,7 +18,8 @@
  curl_multi_strerror.3 curl_share_strerror.3 curl_global_init_mem.3	 \
  libcurl-tutorial.3 curl_easy_reset.3 curl_easy_escape.3		 \
  curl_easy_unescape.3 curl_multi_setopt.3 curl_multi_socket.3		 \
- curl_multi_timeout.3 curl_formget.3 curl_multi_assign.3
+ curl_multi_timeout.3 curl_formget.3 curl_multi_assign.3		 \
+ curl_easy_pause.3
 
 HTMLPAGES = curl_easy_cleanup.html curl_easy_getinfo.html		  \
  curl_easy_init.html curl_easy_perform.html curl_easy_setopt.html	  \
@@ -36,7 +37,7 @@
  curl_share_strerror.html curl_global_init_mem.html libcurl-tutorial.html \
  curl_easy_reset.html curl_easy_escape.html curl_easy_unescape.html	  \
  curl_multi_setopt.html curl_multi_socket.html curl_multi_timeout.html	  \
- curl_formget.html curl_multi_assign.html
+ curl_formget.html curl_multi_assign.html curl_easy_pause.html
 
 PDFPAGES = curl_easy_cleanup.pdf curl_easy_getinfo.pdf curl_easy_init.pdf \
  curl_easy_perform.pdf curl_easy_setopt.pdf curl_easy_duphandle.pdf	  \
@@ -53,7 +54,7 @@
  curl_share_strerror.pdf curl_global_init_mem.pdf libcurl-tutorial.pdf	  \
  curl_easy_reset.pdf curl_easy_escape.pdf curl_easy_unescape.pdf	  \
  curl_multi_setopt.pdf curl_multi_socket.pdf curl_multi_timeout.pdf	  \
- curl_formget.pdf curl_multi_assign.pdf
+ curl_formget.pdf curl_multi_assign.pdf curl_easy_pause.pdf
 
 CLEANFILES = $(HTMLPAGES) $(PDFPAGES)
 
Index: docs/libcurl/curl_easy_pause.3
===================================================================
RCS file: docs/libcurl/curl_easy_pause.3
diff -N docs/libcurl/curl_easy_pause.3
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ docs/libcurl/curl_easy_pause.3	23 Dec 2007 12:40:41 -0000
@@ -0,0 +1,47 @@
+.\" $Id: curl_easy_pause.3,v 1.9 2006/01/15 23:55:53 bagder Exp $
+.\"
+.TH curl_easy_pause 3 "17 Dec 2007" "libcurl 7.18.0" "libcurl Manual"
+.SH NAME
+curl_easy_pause - pause and unpause a connection
+.SH SYNOPSIS
+.B #include <curl/curl.h>
+
+.BI "CURLcode curl_easy_pause(CURL *"handle ", int "bitmask " );"
+
+.SH DESCRIPTION
+Using this function, you can explicitly mark a running connection to get
+paused, and you can unpause a connection that was previously paused.
+
+A connection can made to pause by using this function or by letting the read
+or the write callbacks return the proper magic return code
+(\fICURL_READFUNC_PAUSE\fP and \fICURL_WRITEFUNC_PAUSE\fP).
+
+NOTE: while it may feel tempting, take care and notice that you cannot call
+this function from another thread.
+
+The \fBhandle\fP argument is of course identifying the handle that operates on
+the connection you want to pause or unpause.
+
+The \fBbitmask\fP argument is a set of bits that sets the new state of the
+connection. The following bits can be used:
+.IP CURLPAUSE_RECV
+Pause receiving data. There will be no data received on this conneciton until
+this function is called again without this bit set. Thus, the write callback
+(\fICURLOPT_WRITEFUNCTION\fP) won't be called.
+.IP CURLPAUSE_SEND
+Pause sending data. There will be no data sent on this connection until this
+function is called again without this bit set. Thus, the read callback
+(\fICURLOPT_READFUNCTION\fP) won't be called.
+.IP CURLPAUSE_ALL
+Convenience define that pauses both directions.
+.IP CURLPAUSE_CONT
+Convenience define that unpauses both directions
+.SH RETURN VALUE
+CURLE_OK (zero) means that the option was set properly, and a non-zero return
+code means something wrong occurred after the new state was set.  See the
+\fIlibcurl-errors(3)\fP man page for the full list with descriptions.
+.SH AVAILABILITY
+This function was added in libcurl 7.18.0. Before this version, there was no
+explicit support for pausing transfers.
+.SH "SEE ALSO"
+.BR curl_easy_cleanup "(3), " curl_easy_reset "(3)"
Index: include/curl/curl.h
===================================================================
RCS file: /cvsroot/curl/curl/include/curl/curl.h,v
retrieving revision 1.335
diff -u -r1.335 curl.h
--- include/curl/curl.h	2 Dec 2007 23:38:24 -0000	1.335
+++ include/curl/curl.h	23 Dec 2007 12:40:44 -0000
@@ -247,7 +247,9 @@
      time for those who feel adventurous. */
 #define CURL_MAX_WRITE_SIZE 16384
 #endif
-
+/* This is a magic return code for the write callback that, when returned,
+   will signal libcurl to pause receving on the current transfer. */
+#define CURL_WRITEFUNC_PAUSE 0x10000001
 typedef size_t (*curl_write_callback)(char *buffer,
                                       size_t size,
                                       size_t nitems,
@@ -256,6 +258,9 @@
 /* This is a return code for the read callback that, when returned, will
    signal libcurl to immediately abort the current transfer. */
 #define CURL_READFUNC_ABORT 0x10000000
+/* This is a return code for the read callback that, when returned, will
+   signal libcurl to pause sending data on the current transfer. */
+#define CURL_READFUNC_PAUSE 0x10000001
 typedef size_t (*curl_read_callback)(char *buffer,
                                       size_t size,
                                       size_t nitems,
@@ -1759,6 +1764,26 @@
  */
 CURL_EXTERN const char *curl_share_strerror(CURLSHcode);
 
+/*
+ * NAME curl_easy_pause()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_pause function pauses or unpauses transfers. Select the new
+ * state by setting the bitmask, use the convenience defines below.
+ *
+ */
+CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
+
+#define CURLPAUSE_RECV      (1<<0)
+#define CURLPAUSE_RECV_CONT (0)
+
+#define CURLPAUSE_SEND      (1<<2)
+#define CURLPAUSE_SEND_CONT (0)
+
+#define CURLPAUSE_ALL       (CURLPAUSE_RECV|CURLPAUSE_SEND)
+#define CURLPAUSE_CONT      (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT)
+
 #ifdef  __cplusplus
 }
 #endif
Index: lib/easy.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/easy.c,v
retrieving revision 1.111
diff -u -r1.111 easy.c
--- lib/easy.c	24 Nov 2007 23:16:55 -0000	1.111
+++ lib/easy.c	23 Dec 2007 12:40:45 -0000
@@ -744,6 +744,57 @@
   data->set.new_directory_perms = 0755; /* Default permissions */
 }
 
+/*
+ * curl_easy_pause() allows an application to pause or unpause a specific
+ * transfer and direction. This function sets the full new state for the
+ * current connection this easy handle operates on.
+ *
+ * NOTE: if you have the receiving paused and you call this function to remove
+ * the pausing, you may get your write callback called at this point.
+ *
+ * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
+ */
+CURLcode curl_easy_pause(CURL *curl, int action)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+  struct SingleRequest *k = &data->req;
+  CURLcode result = CURLE_OK;
+
+  /* first switch off both pause bits */
+  int newstate = k->keepon &~ (KEEP_READ_PAUSE| KEEP_WRITE_PAUSE);
+
+  /* set the new desired pause bits */
+  newstate |= ((action & CURLPAUSE_RECV)?KEEP_READ_PAUSE:0) |
+    ((action & CURLPAUSE_SEND)?KEEP_WRITE_PAUSE:0);
+
+  /* put it back in the keepon */
+  k->keepon = newstate;
+
+  if(!(newstate & KEEP_READ_PAUSE) && data->state.tempwrite) {
+    /* we have a buffer for writing that we now seem to be able to deliver since
+       the receive pausing is lifted! */
+
+    /* get the pointer in a local copy since the function may return PAUSE
+       again and then we'll get a new copy allocted and stored in the
+       tempwrite variable */
+    char *tofree = data->state.tempwrite;
+
+    /* clear tempwrite here just to make sure it gets cleared if there's no
+       further use of it, and make sure we don't clear it after the function
+       invoke as it may have been set to a new value by then */
+    data->state.tempwrite = NULL;
+
+    result = Curl_client_write(data->state.current_conn,
+                               data->state.tempwritetype,
+                               tofree,
+                               data->state.tempwritesize);
+
+    free(tofree); /* this is unconditionally no longer used */
+  }
+
+  return result;
+}
+
 #ifdef CURL_DOES_CONVERSIONS
 /*
  * Curl_convert_to_network() is an internal function
Index: lib/multi.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/multi.c,v
retrieving revision 1.156
diff -u -r1.156 multi.c
--- lib/multi.c	24 Nov 2007 23:16:55 -0000	1.156
+++ lib/multi.c	23 Dec 2007 12:40:46 -0000
@@ -1255,13 +1255,13 @@
       k = &easy->easy_handle->req;
 
       if(!(k->keepon & KEEP_READ)) {
-          /* We're done reading */
-          easy->easy_conn->readchannel_inuse = FALSE;
+        /* We're done reading */
+        easy->easy_conn->readchannel_inuse = FALSE;
       }
 
       if(!(k->keepon & KEEP_WRITE)) {
-          /* We're done writing */
-          easy->easy_conn->writechannel_inuse = FALSE;
+        /* We're done writing */
+        easy->easy_conn->writechannel_inuse = FALSE;
       }
 
       if(easy->result)  {
Index: lib/sendf.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/sendf.c,v
retrieving revision 1.137
diff -u -r1.137 sendf.c
--- lib/sendf.c	9 Dec 2007 22:31:53 -0000	1.137
+++ lib/sendf.c	23 Dec 2007 12:40:46 -0000
@@ -376,6 +376,36 @@
   return retcode;
 }
 
+static CURLcode pausewrite(struct SessionHandle *data,
+                           int type, /* what type of data */
+                           char *ptr,
+                           size_t len)
+{
+  /* signalled to pause sending on this connection, but since we have data
+     we want to send we need to dup it to save a copy for when the sending
+     is again enabled */
+  struct SingleRequest *k = &data->req;
+  char *dupl = malloc(len);
+  if(!dupl)
+    return CURLE_OUT_OF_MEMORY;
+
+  memcpy(dupl, ptr, len);
+
+  /* store this information in the state struct for later use */
+  data->state.tempwrite = dupl;
+  data->state.tempwritesize = len;
+  data->state.tempwritetype = type;
+
+  /* mark the connection as RECV paused */
+  k->keepon |= KEEP_READ_PAUSE;
+
+  DEBUGF(infof(data, "Pausing with %d bytes in buffer for type %02x\n",
+               (int)len, type));
+
+  return CURLE_OK;
+}
+
+
 /* client_write() sends data to the write callback(s)
 
    The bit pattern defines to what "streams" to write to. Body and/or header.
@@ -390,8 +420,8 @@
   size_t wrote;
 
   if(data->state.cancelled) {
-      /* We just suck everything into a black hole */
-      return CURLE_OK;
+    /* We just suck everything into a black hole */
+    return CURLE_OK;
   }
 
   if(0 == len)
@@ -422,8 +452,11 @@
       wrote = len;
     }
 
+    if(CURL_WRITEFUNC_PAUSE == wrote)
+      return pausewrite(data, type, ptr, len);
+
     if(wrote != len) {
-      failf (data, "Failed writing body");
+      failf(data, "Failed writing body (%d != %d)", (int)wrote, (int)len);
       return CURLE_WRITE_ERROR;
     }
   }
@@ -441,6 +474,12 @@
        regardless of the ftp transfer mode (ASCII/Image) */
 
     wrote = writeit(ptr, 1, len, data->set.writeheader);
+    if(CURL_WRITEFUNC_PAUSE == wrote)
+      /* here we pass in the HEADER bit only since if this was body as well
+         then it was passed already and clearly that didn't trigger the pause,
+         so this is saved for later with the HEADER bit only */
+      return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
+
     if(wrote != len) {
       failf (data, "Failed writing header");
       return CURLE_WRITE_ERROR;
Index: lib/transfer.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/transfer.c,v
retrieving revision 1.375
diff -u -r1.375 transfer.c
--- lib/transfer.c	8 Dec 2007 22:50:55 -0000	1.375
+++ lib/transfer.c	23 Dec 2007 12:40:47 -0000
@@ -134,6 +134,14 @@
     failf(data, "operation aborted by callback\n");
     return CURLE_ABORTED_BY_CALLBACK;
   }
+  else if(nread == CURL_READFUNC_PAUSE) {
+    struct SingleRequest *k = &data->req;
+    k->keepon |= KEEP_READ_PAUSE; /* mark reading as paused */
+    return 0; /* nothing was read */
+  }
+  else if((size_t)nread > buffersize)
+    /* the read function returned a too large value */
+    return CURLE_READ_ERROR;
 
   if(!conn->bits.forbidchunk && conn->bits.upload_chunky) {
     /* if chunked Transfer-Encoding */
@@ -330,7 +338,7 @@
   /* only use the proper socket if the *_HOLD bit is not set simultaneously as
      then we are in rate limiting state in that transfer direction */
 
-  if((k->keepon & (KEEP_READ|KEEP_READ_HOLD)) == KEEP_READ) {
+  if((k->keepon & KEEP_READBITS) == KEEP_READ) {
     fd_read = conn->sockfd;
 #if defined(USE_LIBSSH2)
     if(conn->protocol & (PROT_SCP|PROT_SFTP))
@@ -339,7 +347,7 @@
   } else
     fd_read = CURL_SOCKET_BAD;
 
-  if((k->keepon & (KEEP_WRITE|KEEP_WRITE_HOLD)) == KEEP_WRITE)
+  if((k->keepon & KEEP_WRITEBITS) == KEEP_WRITE)
     fd_write = conn->writesockfd;
   else
     fd_write = CURL_SOCKET_BAD;
@@ -1425,9 +1433,11 @@
           else
             nread = 0; /* we're done uploading/reading */
 
-          /* the signed int typecase of nread of for systems that has
-             unsigned size_t */
-          if(nread<=0) {
+          if(!nread && (k->keepon & KEEP_READ_PAUSE)) {
+            /* this is a paused transfer */
+            break;
+          }
+          else if(nread<=0) {
             /* done */
             k->keepon &= ~KEEP_WRITE; /* we're done writing */
             writedone = TRUE;
@@ -1635,7 +1645,7 @@
   }
 
   /* Now update the "done" boolean we return */
-  *done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE)));
+  *done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE|KEEP_READ_PAUSE|KEEP_WRITE_PAUSE)));
 
   return CURLE_OK;
 }
@@ -1660,7 +1670,8 @@
     /* simple check but we might need two slots */
     return GETSOCK_BLANK;
 
-  if(data->req.keepon & KEEP_READ) {
+  /* don't include HOLD and PAUSE connections */
+  if((data->req.keepon & KEEP_READBITS) == KEEP_READ) {
 
     DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
 
@@ -1668,7 +1679,8 @@
     sock[sockindex] = conn->sockfd;
   }
 
-  if(data->req.keepon & KEEP_WRITE) {
+  /* don't include HOLD and PAUSE connections */
+  if((data->req.keepon & KEEP_WRITEBITS) == KEEP_WRITE) {
 
     if((conn->sockfd != conn->writesockfd) ||
        !(data->req.keepon & KEEP_READ)) {
@@ -1751,10 +1763,17 @@
         k->keepon |= KEEP_READ_HOLD; /* hold it */
     }
 
-    /* The *_HOLD logic is necessary since even though there might be no
-       traffic during the select interval, we still call Curl_readwrite() for
-       the timeout case and if we limit transfer speed we must make sure that
-       this function doesn't transfer anything while in HOLD status. */
+    /* pause logic. Don't check descriptors for paused connections */
+    if(k->keepon & KEEP_READ_PAUSE)
+      fd_read = CURL_SOCKET_BAD;
+    if(k->keepon & KEEP_WRITE_PAUSE)
+      fd_write = CURL_SOCKET_BAD;
+
+    /* The *_HOLD and *_PAUSE logic is necessary since even though there might
+       be no traffic during the select interval, we still call
+       Curl_readwrite() for the timeout case and if we limit transfer speed we
+       must make sure that this function doesn't transfer anything while in
+       HOLD status. */
 
     switch (Curl_socket_ready(fd_read, fd_write, 1000)) {
     case -1: /* select() error, stop reading */
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.683
diff -u -r1.683 url.c
--- lib/url.c	8 Dec 2007 22:50:55 -0000	1.683
+++ lib/url.c	23 Dec 2007 12:40:48 -0000
@@ -4427,6 +4427,13 @@
 
   Curl_pgrsDone(conn); /* done with the operation */
 
+  /* if the transfer was completed in a paused state there can be buffered
+     data left to write and then kill */
+  if(data->state.tempwrite) {
+    free(data->state.tempwrite);
+    data->state.tempwrite = NULL;
+  }
+
   /* for ares-using, make sure all possible outstanding requests are properly
      cancelled before we proceed */
   ares_cancel(data->state.areschannel);
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.363
diff -u -r1.363 urldata.h
--- lib/urldata.h	2 Dec 2007 23:38:24 -0000	1.363
+++ lib/urldata.h	23 Dec 2007 12:40:49 -0000
@@ -629,12 +629,18 @@
  */
 
 #define KEEP_NONE  0
-#define KEEP_READ  1      /* there is or may be data to read */
-#define KEEP_WRITE 2      /* there is or may be data to write */
-#define KEEP_READ_HOLD 4  /* when set, no reading should be done but there
-                             might still be data to read */
-#define KEEP_WRITE_HOLD 8 /* when set, no writing should be done but there
-                             might still be data to write */
+#define KEEP_READ  (1<<0)     /* there is or may be data to read */
+#define KEEP_WRITE (1<<1)     /* there is or may be data to write */
+#define KEEP_READ_HOLD (1<<2) /* when set, no reading should be done but there
+                                 might still be data to read */
+#define KEEP_WRITE_HOLD (1<<3) /* when set, no writing should be done but there
+                                  might still be data to write */
+#define KEEP_READ_PAUSE (1<<4) /* reading is paused */
+#define KEEP_WRITE_PAUSE (1<<5) /* writing is paused */
+
+#define KEEP_READBITS (KEEP_READ | KEEP_READ_HOLD | KEEP_READ_PAUSE)
+#define KEEP_WRITEBITS (KEEP_WRITE | KEEP_WRITE_HOLD | KEEP_WRITE_PAUSE)
+
 
 #ifdef HAVE_LIBZ
 typedef enum {
@@ -1126,10 +1132,13 @@
                        following not keep sending user+password... This is
                        strdup() data.
                     */
-
   struct curl_ssl_session *session; /* array of 'numsessions' size */
   long sessionage;                  /* number of the most recent session */
-
+  char *tempwrite;      /* allocated buffer to keep data in when a write
+                           callback returns to make the connection paused */
+  size_t tempwritesize; /* size of the 'tempwrite' allocated buffer */
+  int tempwritetype;    /* type of the 'tempwrite' buffer as a bitmask that is
+                           used with Curl_client_write() */
   char *scratch; /* huge buffer[BUFSIZE*2] when doing upload CRLF replacing */
   bool errorbuf; /* Set to TRUE if the error buffer is already filled in.
                     This must be set to FALSE every time _easy_perform() is
