From 0ba47146f7ff3dedcf35f568be57d41d47c5bdfe Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 29 Feb 2024 10:12:39 +0100 Subject: [PATCH] mime: add client reader 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 --- lib/easy.c | 7 +- lib/http.c | 60 ++++++------ lib/http_chunks.c | 1 + lib/imap.c | 17 ++-- lib/mime.c | 226 +++++++++++++++++++++++++++++++++++++++++++++- lib/mime.h | 13 +-- lib/request.c | 1 - lib/request.h | 3 - lib/rtsp.c | 2 - lib/sendf.c | 124 ++++++++++++++----------- lib/sendf.h | 17 ++++ lib/smtp.c | 22 ++--- 12 files changed, 367 insertions(+), 126 deletions(-) diff --git a/lib/easy.c b/lib/easy.c index 6c932ad823..dbc2e7f179 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -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 */ diff --git a/lib/http.c b/lib/http.c index e6375b92e1..88746eb579 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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) diff --git a/lib/http_chunks.c b/lib/http_chunks.c index dbcc527630..f9cb0741ea 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -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) }; diff --git a/lib/imap.c b/lib/imap.c index 43ec83d09c..0e013e740a 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -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) { diff --git a/lib/mime.c b/lib/mime.c index d712331d04..cccf46f897 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -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) */ diff --git a/lib/mime.h b/lib/mime.h index a64f41d4b5..954b3ccf39 100644 --- a/lib/mime.h +++ b/lib/mime.h @@ -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 diff --git a/lib/request.c b/lib/request.c index d009eddb1c..ed4aa714c1 100644 --- a/lib/request.c +++ b/lib/request.c @@ -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; } diff --git a/lib/request.h b/lib/request.h index fdfdb0bee2..2298e79d73 100644 --- a/lib/request.h +++ b/lib/request.h @@ -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, diff --git a/lib/rtsp.c b/lib/rtsp.c index 7c3dd31a6e..a0b978d92a 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -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) diff --git a/lib/sendf.c b/lib/sendf.c index 22e2b8592a..81f2deabdc 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -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; +} diff --git a/lib/sendf.h b/lib/sendf.h index efb9628fc1..2c5bc36208 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -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. */ diff --git a/lib/smtp.c b/lib/smtp.c index 43a85c97c1..2d85360083 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -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) }; -- 2.47.3