]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
mime: add client reader
authorStefan Eissing <stefan@eissing.org>
Thu, 29 Feb 2024 09:12:39 +0000 (10:12 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 5 Mar 2024 23:17:37 +0000 (00:17 +0100)
Add `mime` client reader. Encapsulates reading from mime parts, getting
their length, rewinding and unpausing.

- remove special mime handling from sendf.c and easy.c
- add general "unpause" method to client readers
- use new reader in http/imap/smtp
- make some mime functions static that are now only used internally

In addition:
- remove flag 'forbidchunk' as no longer needed

Closes #13039

12 files changed:
lib/easy.c
lib/http.c
lib/http_chunks.c
lib/imap.c
lib/mime.c
lib/mime.h
lib/request.c
lib/request.h
lib/rtsp.c
lib/sendf.c
lib/sendf.h
lib/smtp.c

index 6c932ad823c6ecbb3851b4608ee070342c8b7221..dbc2e7f1791d3fb7e713240ee45e0b4108a83598 100644 (file)
@@ -1110,9 +1110,10 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
   /* Unpause parts in active mime tree. */
   if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
      (data->mstate == MSTATE_PERFORMING ||
-      data->mstate == MSTATE_RATELIMITING) &&
-     data->state.fread_func == (curl_read_callback) Curl_mime_read) {
-    Curl_mime_unpause(data->state.in);
+      data->mstate == MSTATE_RATELIMITING)) {
+    result = Curl_creader_unpause(data);
+    if(result)
+      return result;
   }
 
   /* put it back in the keepon */
index e6375b92e100033462af56b9a9bbd4af298f3ed6..88746eb579ca210bee8dab6f969094ef0462a4a3 100644 (file)
@@ -2030,47 +2030,41 @@ static CURLcode set_post_reader(struct Curl_easy *data, Curl_HttpReq httpreq)
     break;
   }
 
+  switch(httpreq) {
+  case HTTPREQ_POST_FORM:
+  case HTTPREQ_POST_MIME:
+    /* This is form posting using mime data. */
 #ifndef CURL_DISABLE_MIME
-  if(data->state.mimepost) {
-    const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
+    if(data->state.mimepost) {
+      const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
 
-    /* Read and seek body only. */
-    data->state.mimepost->flags |= MIME_BODY_ONLY;
+      /* Read and seek body only. */
+      data->state.mimepost->flags |= MIME_BODY_ONLY;
 
-    /* Prepare the mime structure headers & set content type. */
+      /* Prepare the mime structure headers & set content type. */
 
-    if(cthdr)
-      for(cthdr += 13; *cthdr == ' '; cthdr++)
-        ;
-    else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
-      cthdr = "multipart/form-data";
+      if(cthdr)
+        for(cthdr += 13; *cthdr == ' '; cthdr++)
+          ;
+      else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
+        cthdr = "multipart/form-data";
 
-    curl_mime_headers(data->state.mimepost, data->set.headers, 0);
-    result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr,
-                                       NULL, MIMESTRATEGY_FORM);
-    curl_mime_headers(data->state.mimepost, NULL, 0);
-    if(!result)
-      result = Curl_mime_rewind(data->state.mimepost);
-    if(result)
-      return result;
-    postsize = Curl_mime_size(data->state.mimepost);
-  }
+      curl_mime_headers(data->state.mimepost, data->set.headers, 0);
+      result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr,
+                                         NULL, MIMESTRATEGY_FORM);
+      if(result)
+        return result;
+      curl_mime_headers(data->state.mimepost, NULL, 0);
+      result = Curl_creader_set_mime(data, data->state.mimepost);
+      if(result)
+        return result;
+    }
+    else
 #endif
-
-  switch(httpreq) {
-  case HTTPREQ_POST_FORM:
-  case HTTPREQ_POST_MIME:
-    /* This is form posting using mime data. */
-    data->state.infilesize = postsize;
-    if(!postsize)
+    {
       result = Curl_creader_set_null(data);
-    else {
-      /* Read from mime structure. We could do a special client reader
-       * for this, but replacing the callback seems to work fine. */
-      data->state.fread_func = (curl_read_callback) Curl_mime_read;
-      data->state.in = (void *) data->state.mimepost;
-      result = Curl_creader_set_fread(data, postsize);
     }
+    data->state.infilesize = Curl_creader_total_length(data);
     return result;
   default:
     if(!postsize)
index dbcc527630ecc6dda90eda795543b8fd8f390488..f9cb0741ea23d2b1924a255d78a4132deb532a5a 100644 (file)
@@ -643,6 +643,7 @@ const struct Curl_crtype Curl_httpchunk_encoder = {
   cr_chunked_total_length,
   Curl_creader_def_resume_from,
   Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
   sizeof(struct chunked_reader)
 };
 
index 43ec83d09c644ca5958adf442907fb205ee2e3c7..0e013e740ae922344bc5a19fa4ce85bea74afcc8 100644 (file)
@@ -786,20 +786,19 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
                                       "Mime-Version: 1.0");
 
-    /* Make sure we will read the entire mime structure. */
     if(!result)
-      result = Curl_mime_rewind(&data->set.mimepost);
-
+      result = Curl_creader_set_mime(data, &data->set.mimepost);
     if(result)
       return result;
-
-    data->state.infilesize = Curl_mime_size(&data->set.mimepost);
-
-    /* Read from mime structure. */
-    data->state.fread_func = (curl_read_callback) Curl_mime_read;
-    data->state.in = (void *) &data->set.mimepost;
+    data->state.infilesize = Curl_creader_client_length(data);
   }
+  else
 #endif
+  {
+    result = Curl_creader_set_fread(data, data->state.infilesize);
+    if(result)
+      return result;
+  }
 
   /* Check we know the size of the upload */
   if(data->state.infilesize < 0) {
index d712331d0405e18fe94f6b7cbb3e1aab76a2e3ae..cccf46f897578979d4fc440b2a2b4eb9f6cc8a85 100644 (file)
@@ -74,6 +74,7 @@ static curl_off_t encoder_base64_size(curl_mimepart *part);
 static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
                               curl_mimepart *part);
 static curl_off_t encoder_qp_size(curl_mimepart *part);
+static curl_off_t mime_size(curl_mimepart *part);
 
 static const struct mime_encoder encoders[] = {
   {"binary", encoder_nop_read, encoder_nop_size},
@@ -1602,7 +1603,7 @@ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
 }
 
 /* Rewind mime stream. */
-CURLcode Curl_mime_rewind(curl_mimepart *part)
+static CURLcode mime_rewind(curl_mimepart *part)
 {
   return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
          CURLE_OK: CURLE_SEND_FAIL_REWIND;
@@ -1634,7 +1635,7 @@ static curl_off_t multipart_size(curl_mime *mime)
   size = boundarysize;  /* Final boundary - CRLF after headers. */
 
   for(part = mime->firstpart; part; part = part->nextpart) {
-    curl_off_t sz = Curl_mime_size(part);
+    curl_off_t sz = mime_size(part);
 
     if(sz < 0)
       size = sz;
@@ -1647,7 +1648,7 @@ static curl_off_t multipart_size(curl_mime *mime)
 }
 
 /* Get/compute mime size. */
-curl_off_t Curl_mime_size(curl_mimepart *part)
+static curl_off_t mime_size(curl_mimepart *part)
 {
   curl_off_t size;
 
@@ -1896,7 +1897,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
 }
 
 /* Recursively reset paused status in the given part. */
-void Curl_mime_unpause(curl_mimepart *part)
+static void mime_unpause(curl_mimepart *part)
 {
   if(part) {
     if(part->lastreadstatus == CURL_READFUNC_PAUSE)
@@ -1908,12 +1909,227 @@ void Curl_mime_unpause(curl_mimepart *part)
         curl_mimepart *subpart;
 
         for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
-          Curl_mime_unpause(subpart);
+          mime_unpause(subpart);
       }
     }
   }
 }
 
+struct cr_mime_ctx {
+  struct Curl_creader super;
+  curl_mimepart *part;
+  curl_off_t total_len;
+  curl_off_t read_len;
+  CURLcode error_result;
+  BIT(seen_eos);
+  BIT(errored);
+};
+
+static CURLcode cr_mime_init(struct Curl_easy *data,
+                             struct Curl_creader *reader)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+  (void)data;
+  ctx->total_len = -1;
+  ctx->read_len = 0;
+  return CURLE_OK;
+}
+
+/* Real client reader to installed client callbacks. */
+static CURLcode cr_mime_read(struct Curl_easy *data,
+                             struct Curl_creader *reader,
+                             char *buf, size_t blen,
+                             size_t *pnread, bool *peos)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+  size_t nread;
+
+  /* Once we have errored, we will return the same error forever */
+  if(ctx->errored) {
+    *pnread = 0;
+    *peos = FALSE;
+    return ctx->error_result;
+  }
+  if(ctx->seen_eos) {
+    *pnread = 0;
+    *peos = TRUE;
+    return CURLE_OK;
+  }
+  /* respect length limitations */
+  if(ctx->total_len >= 0) {
+    curl_off_t remain = ctx->total_len - ctx->read_len;
+    if(remain <= 0)
+      blen = 0;
+    else if(remain < (curl_off_t)blen)
+      blen = (size_t)remain;
+  }
+  nread = 0;
+  if(blen) {
+    nread = Curl_mime_read(buf, 1, blen, ctx->part);
+  }
+
+  switch(nread) {
+  case 0:
+    if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
+      failf(data, "client mime read EOF fail, only "
+            "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
+            " of needed bytes read", ctx->read_len, ctx->total_len);
+      return CURLE_READ_ERROR;
+    }
+    *pnread = 0;
+    *peos = TRUE;
+    ctx->seen_eos = TRUE;
+    break;
+
+  case CURL_READFUNC_ABORT:
+    failf(data, "operation aborted by callback");
+    *pnread = 0;
+    *peos = FALSE;
+    ctx->errored = TRUE;
+    ctx->error_result = CURLE_ABORTED_BY_CALLBACK;
+    return CURLE_ABORTED_BY_CALLBACK;
+
+  case CURL_READFUNC_PAUSE:
+    /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
+    data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
+    *pnread = 0;
+    *peos = FALSE;
+    break; /* nothing was read */
+
+  default:
+    if(nread > blen) {
+      /* the read function returned a too large value */
+      failf(data, "read function returned funny value");
+      *pnread = 0;
+      *peos = FALSE;
+      ctx->errored = TRUE;
+      ctx->error_result = CURLE_READ_ERROR;
+      return CURLE_READ_ERROR;
+    }
+    ctx->read_len += nread;
+    if(ctx->total_len >= 0)
+      ctx->seen_eos = (ctx->read_len >= ctx->total_len);
+    *pnread = nread;
+    *peos = ctx->seen_eos;
+    break;
+  }
+  DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
+         ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d",
+         blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos));
+  return CURLE_OK;
+}
+
+static bool cr_mime_needs_rewind(struct Curl_easy *data,
+                                 struct Curl_creader *reader)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+  (void)data;
+  return ctx->read_len > 0;
+}
+
+static curl_off_t cr_mime_total_length(struct Curl_easy *data,
+                                       struct Curl_creader *reader)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+  (void)data;
+  return ctx->total_len;
+}
+
+static CURLcode cr_mime_resume_from(struct Curl_easy *data,
+                                    struct Curl_creader *reader,
+                                    curl_off_t offset)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+
+  if(offset > 0) {
+    curl_off_t passed = 0;
+
+    do {
+      char scratch[4*1024];
+      size_t readthisamountnow =
+        (offset - passed > (curl_off_t)sizeof(scratch)) ?
+        sizeof(scratch) :
+        curlx_sotouz(offset - passed);
+      size_t nread;
+
+      nread = Curl_mime_read(scratch, 1, readthisamountnow, ctx->part);
+      passed += (curl_off_t)nread;
+      if((nread == 0) || (nread > readthisamountnow)) {
+        /* this checks for greater-than only to make sure that the
+           CURL_READFUNC_ABORT return code still aborts */
+        failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
+              " bytes from the mime post", passed);
+        return CURLE_READ_ERROR;
+      }
+    } while(passed < offset);
+
+    /* now, decrease the size of the read */
+    if(ctx->total_len > 0) {
+      ctx->total_len -= offset;
+
+      if(ctx->total_len <= 0) {
+        failf(data, "Mime post already completely uploaded");
+        return CURLE_PARTIAL_FILE;
+      }
+    }
+    /* we've passed, proceed as normal */
+  }
+  return CURLE_OK;
+}
+
+static CURLcode cr_mime_rewind(struct Curl_easy *data,
+                               struct Curl_creader *reader)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+  CURLcode result = mime_rewind(ctx->part);
+  if(result)
+    failf(data, "Cannot rewind mime/post data");
+  return result;
+}
+
+static CURLcode cr_mime_unpause(struct Curl_easy *data,
+                                struct Curl_creader *reader)
+{
+  struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+  (void)data;
+  mime_unpause(ctx->part);
+  return CURLE_OK;
+}
+
+static const struct Curl_crtype cr_mime = {
+  "cr-mime",
+  cr_mime_init,
+  cr_mime_read,
+  Curl_creader_def_close,
+  cr_mime_needs_rewind,
+  cr_mime_total_length,
+  cr_mime_resume_from,
+  cr_mime_rewind,
+  cr_mime_unpause,
+  sizeof(struct cr_mime_ctx)
+};
+
+CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part)
+{
+  struct Curl_creader *r;
+  struct cr_mime_ctx *ctx;
+  CURLcode result;
+
+  result = Curl_creader_create(&r, data, &cr_mime, CURL_CR_CLIENT);
+  if(result)
+    return result;
+  ctx = (struct cr_mime_ctx *)r;
+  ctx->part = part;
+  /* Make sure we will read the entire mime structure. */
+  result = mime_rewind(ctx->part);
+  if(result) {
+    Curl_creader_free(data, r);
+    return result;
+  }
+  ctx->total_len = mime_size(ctx->part);
+
+  return Curl_creader_set(data, r);
+}
 
 #else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
                                 !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
index a64f41d4b52cc4beeedaad72ac5a961dfdd54dc4..954b3ccf394947ef9155578d132b800454bde354 100644 (file)
@@ -151,12 +151,15 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
                                    const char *contenttype,
                                    const char *disposition,
                                    enum mimestrategy strategy);
-curl_off_t Curl_mime_size(struct curl_mimepart *part);
 size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
                       void *instream);
-CURLcode Curl_mime_rewind(struct curl_mimepart *part);
 const char *Curl_mime_contenttype(const char *filename);
-void Curl_mime_unpause(struct curl_mimepart *part);
+
+/**
+ * Install a client reader as upload source that reads the given
+ * mime part.
+ */
+CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part);
 
 #else
 /* if disabled */
@@ -165,10 +168,8 @@ void Curl_mime_unpause(struct curl_mimepart *part);
 #define Curl_mime_duppart(x,y,z) CURLE_OK /* Nothing to duplicate. Succeed */
 #define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN
 #define Curl_mime_prepare_headers(a,b,c,d,e) CURLE_NOT_BUILT_IN
-#define Curl_mime_size(x) (curl_off_t) -1
 #define Curl_mime_read NULL
-#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)
-#define Curl_mime_unpause(x)
+#define Curl_creader_set_mime(x,y) ((void)x, CURLE_NOT_BUILT_IN)
 #endif
 
 
index d009eddb1c0c683aa58f7aec8e07a703cd929cd5..ed4aa714c12c886452ab4e2a0363bc4140b1e9bc 100644 (file)
@@ -139,7 +139,6 @@ void Curl_req_reset(struct SingleRequest *req, struct Curl_easy *data)
   req->ignore_cl = FALSE;
   req->upload_chunky = FALSE;
   req->getheader = FALSE;
-  req->forbidchunk = FALSE;
   req->no_body = data->set.opt_no_body;
   req->authneg = FALSE;
 }
index fdfdb0bee2367f081b45e3b553a7ce10d027b0a9..2298e79d732204fc4df7f451f584b7774e364b27 100644 (file)
@@ -139,9 +139,6 @@ struct SingleRequest {
   BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
                          on upload */
   BIT(getheader);    /* TRUE if header parsing is wanted */
-  BIT(forbidchunk);  /* used only to explicitly forbid chunk-upload for
-                        specific upload buffers. See readmoredata() in http.c
-                        for details. */
   BIT(no_body);      /* the response has no body */
   BIT(authneg);      /* TRUE when the auth phase has started, which means
                         that we are creating a request with an auth header,
index 7c3dd31a6e84a6ba2466a25efb269e921e41224a..a0b978d92a6af4752317cc61445eaba536fa0a60 100644 (file)
@@ -571,8 +571,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
       goto out;
   }
 
-  /* RTSP never allows chunked transfer */
-  data->req.forbidchunk = TRUE;
   /* Finish the request buffer */
   result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
   if(result)
index 22e2b8592aef42de3f2f112000cc055926de496e..81f2deabdc0b3b4a475d9d1ef59cd138044a4723 100644 (file)
@@ -553,6 +553,14 @@ CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
+                                  struct Curl_creader *reader)
+{
+  (void)data;
+  (void)reader;
+  return CURLE_OK;
+}
+
 struct cr_in_ctx {
   struct Curl_creader super;
   curl_read_callback read_cb;
@@ -753,37 +761,11 @@ static CURLcode cr_in_rewind(struct Curl_easy *data,
                              struct Curl_creader *reader)
 {
   struct cr_in_ctx *ctx = (struct cr_in_ctx *)reader;
-  /* TODO: I wonder if we should rather give mime its own client
-   * reader type. This is messy. */
-#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
-  curl_mimepart *mimepart = &data->set.mimepost;
-#endif
 
   /* If we never invoked the callback, there is noting to rewind */
   if(!ctx->has_used_cb)
     return CURLE_OK;
 
-  /* We have sent away data. If not using CURLOPT_POSTFIELDS or
-     CURLOPT_HTTPPOST, call app to rewind
-  */
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)
-  if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
-    if(data->state.mimepost)
-      mimepart = data->state.mimepost;
-  }
-#endif
-#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
-  if(ctx->read_cb == (curl_read_callback)Curl_mime_read) {
-    CURLcode result = Curl_mime_rewind(mimepart);
-    DEBUGF(infof(data, "cr_in, rewind mime/post data -> %d", result));
-    if(result) {
-      failf(data, "Cannot rewind mime/post data");
-    }
-    return result;
-  }
-#endif
-
-  /* With mime out of the way, handle "normal" fread callbacks */
   if(data->set.seek_func) {
     int err;
 
@@ -839,6 +821,7 @@ static const struct Curl_crtype cr_in = {
   cr_in_total_length,
   cr_in_resume_from,
   cr_in_rewind,
+  Curl_creader_def_unpause,
   sizeof(struct cr_in_ctx)
 };
 
@@ -985,6 +968,7 @@ static const struct Curl_crtype cr_lc = {
   cr_lc_total_length,
   Curl_creader_def_resume_from,
   Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
   sizeof(struct cr_lc_ctx)
 };
 
@@ -1004,18 +988,18 @@ static CURLcode cr_lc_add(struct Curl_easy *data)
 }
 
 static CURLcode do_init_reader_stack(struct Curl_easy *data,
-                                     const struct Curl_crtype *crt,
-                                     struct Curl_creader **preader,
-                                     curl_off_t clen)
+                                     struct Curl_creader *r)
 {
-  CURLcode result;
+  CURLcode result = CURLE_OK;
+  curl_off_t clen;
 
+  DEBUGASSERT(r);
+  DEBUGASSERT(r->crt);
+  DEBUGASSERT(r->phase == CURL_CR_CLIENT);
   DEBUGASSERT(!data->req.reader_stack);
-  result = Curl_creader_create(preader, data, crt, CURL_CR_CLIENT);
-  if(result)
-    return result;
-  data->req.reader_stack = *preader;
 
+  data->req.reader_stack = r;
+  clen = r->crt->total_length(data, r);
   /* if we do not have 0 length init, and crlf conversion is wanted,
    * add the reader for it */
   if(clen && (data->set.crlf
@@ -1035,15 +1019,16 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
 {
   CURLcode result;
   struct Curl_creader *r;
+  struct cr_in_ctx *ctx;
+
+  result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
+  if(result)
+    return result;
+  ctx = (struct cr_in_ctx *)r;
+  ctx->total_len = len;
 
   cl_reset_reader(data);
-  result = do_init_reader_stack(data, &cr_in, &r, len);
-  if(!result && r) {
-    struct cr_in_ctx *ctx = (struct cr_in_ctx *)r;
-    DEBUGASSERT(r->crt == &cr_in);
-    ctx->total_len = len;
-  }
-  return result;
+  return do_init_reader_stack(data, r);
 }
 
 CURLcode Curl_creader_add(struct Curl_easy *data,
@@ -1067,6 +1052,21 @@ CURLcode Curl_creader_add(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
+{
+  CURLcode result;
+
+  DEBUGASSERT(r);
+  DEBUGASSERT(r->crt);
+  DEBUGASSERT(r->phase == CURL_CR_CLIENT);
+
+  cl_reset_reader(data);
+  result = do_init_reader_stack(data, r);
+  if(result)
+    Curl_creader_free(data, r);
+  return result;
+}
+
 CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
                           size_t *nread, bool *eos)
 {
@@ -1132,15 +1132,21 @@ static const struct Curl_crtype cr_null = {
   cr_null_total_length,
   Curl_creader_def_resume_from,
   Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
   sizeof(struct Curl_creader)
 };
 
 CURLcode Curl_creader_set_null(struct Curl_easy *data)
 {
   struct Curl_creader *r;
+  CURLcode result;
+
+  result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT);
+  if(result)
+    return result;
 
   cl_reset_reader(data);
-  return do_init_reader_stack(data, &cr_null, &r, 0);
+  return do_init_reader_stack(data, r);
 }
 
 struct cr_buf_ctx {
@@ -1222,6 +1228,7 @@ static const struct Curl_crtype cr_buf = {
   cr_buf_total_length,
   cr_buf_resume_from,
   Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
   sizeof(struct cr_buf_ctx)
 };
 
@@ -1230,17 +1237,18 @@ CURLcode Curl_creader_set_buf(struct Curl_easy *data,
 {
   CURLcode result;
   struct Curl_creader *r;
+  struct cr_buf_ctx *ctx;
+
+  result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT);
+  if(result)
+    return result;
+  ctx = (struct cr_buf_ctx *)r;
+  ctx->buf = buf;
+  ctx->blen = blen;
+  ctx->index = 0;
 
   cl_reset_reader(data);
-  result = do_init_reader_stack(data, &cr_buf, &r, blen);
-  if(!result && r) {
-    struct cr_buf_ctx *ctx = (struct cr_buf_ctx *)r;
-    DEBUGASSERT(r->crt == &cr_buf);
-    ctx->buf = buf;
-    ctx->blen = blen;
-    ctx->index = 0;
-  }
-  return result;
+  return do_init_reader_stack(data, r);
 }
 
 curl_off_t Curl_creader_total_length(struct Curl_easy *data)
@@ -1264,3 +1272,17 @@ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset)
     r = r->next;
   return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
 }
+
+CURLcode Curl_creader_unpause(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = data->req.reader_stack;
+  CURLcode result = CURLE_OK;
+
+  while(reader) {
+    result = reader->crt->unpause(data, reader);
+    if(result)
+      break;
+    reader = reader->next;
+  }
+  return result;
+}
index efb9628fc1b8082e759306117a81b947f0cd983a..2c5bc362086a1c84bc71a8047c2e2b9be3964e73 100644 (file)
@@ -187,6 +187,7 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
                             struct Curl_cwriter *writer);
 
 
+
 /* Client Reader Type, provides the implementation */
 struct Curl_crtype {
   const char *name;        /* writer name. */
@@ -200,6 +201,7 @@ struct Curl_crtype {
   CURLcode (*resume_from)(struct Curl_easy *data,
                           struct Curl_creader *reader, curl_off_t offset);
   CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader);
+  CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader);
   size_t creader_size;  /* sizeof() allocated struct Curl_creader */
 };
 
@@ -236,6 +238,8 @@ CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
                                       curl_off_t offset);
 CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
                                  struct Curl_creader *reader);
+CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
+                                  struct Curl_creader *reader);
 
 /**
  * Convenience method for calling `reader->do_read()` that
@@ -268,6 +272,14 @@ void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader);
 CURLcode Curl_creader_add(struct Curl_easy *data,
                           struct Curl_creader *reader);
 
+/**
+ * Set the given reader, which needs to be of type CURL_CR_CLIENT,
+ * as the new first reader. Discard any installed readers and init
+ * the reader chain anew.
+ * The function takes ownership of `r`.
+ */
+CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r);
+
 /**
  * Read at most `blen` bytes at `buf` from the client.
  * @param date    the transfer to read client bytes for
@@ -328,6 +340,11 @@ curl_off_t Curl_creader_client_length(struct Curl_easy *data);
  */
 CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset);
 
+/**
+ * Unpause all installed readers.
+ */
+CURLcode Curl_creader_unpause(struct Curl_easy *data);
+
 /**
  * Set the client reader to provide 0 bytes, immediate EOS.
  */
index 43a85c97c177a061b1fd1a39079776adf3cb8fcc..2d8536008316ea907793ed12eaad31ba25533e45 100644 (file)
@@ -705,20 +705,19 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
                                       "Mime-Version: 1.0");
 
-    /* Make sure we will read the entire mime structure. */
     if(!result)
-      result = Curl_mime_rewind(&data->set.mimepost);
-
+      result = Curl_creader_set_mime(data, &data->set.mimepost);
     if(result)
       goto out;
-
-    data->state.infilesize = Curl_mime_size(&data->set.mimepost);
-
-    /* Read from mime structure. */
-    data->state.fread_func = (curl_read_callback) Curl_mime_read;
-    data->state.in = (void *) &data->set.mimepost;
+    data->state.infilesize = Curl_creader_total_length(data);
   }
+  else
 #endif
+  {
+    result = Curl_creader_set_fread(data, data->state.infilesize);
+    if(result)
+      goto out;
+  }
 
   /* Calculate the optional SIZE parameter */
   if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
@@ -747,10 +746,6 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     }
   }
 
-  /* Setup client reader for size and EOB conversion */
-  result = Curl_creader_set_fread(data, data->state.infilesize);
-  if(result)
-    goto out;
   /* Add the client reader doing STMP EOB escaping */
   result = cr_eob_add(data);
   if(result)
@@ -1927,6 +1922,7 @@ static const struct Curl_crtype cr_eob = {
   cr_eob_total_length,
   Curl_creader_def_resume_from,
   Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
   sizeof(struct cr_eob_ctx)
 };