mime: relax easy/mime structures binding

Deprecation and removal of codeset conversion support from the library
have released the strict need for an early binding of mime structures to
an easy handle (https://github.com/curl/curl/commit/2610142).

This constraint currently forces to create the handle before the mime
structure and the latter cannot be attached to another handle once
created (see https://curl.se/mail/lib-2022-08/0027.html).

This commit removes the handle pointers from the mime structures
allowing more flexibility on their use.

When an easy handle is duplicated, bound mime structures must however
still be duplicated too as their components hold send-time dynamic
information.

Closes #9927
This commit is contained in:
Patrick Monnerat 2022-11-16 17:40:11 +01:00 коммит произвёл Daniel Stenberg
Родитель 383fb29da1
Коммит eb559c8056
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5CC908FDB71E12C2
9 изменённых файлов: 40 добавлений и 40 удалений

Просмотреть файл

@ -31,11 +31,14 @@ curl_mime_init - create a mime handle
curl_mime *curl_mime_init(CURL *easy_handle); curl_mime *curl_mime_init(CURL *easy_handle);
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
\fIcurl_mime_init(3)\fP creates a handle to a new empty mime structure \fIcurl_mime_init(3)\fP creates a handle to a new empty mime structure,
intended to be used with \fIeasy_handle\fP. This mime structure can be This mime structure can be subsequently filled using the mime API, then
subsequently filled using the mime API, then attached to \fIeasy_handle\fP attached to some easy handle using option \fICURLOPT_MIMEPOST(3)\fP within
using option \fICURLOPT_MIMEPOST(3)\fP within a \fIcurl_easy_setopt(3)\fP a \fIcurl_easy_setopt(3)\fP call or added as a multipart in another mime
call. handle's part using \fIcurl_mime_subparts(3)\fP.
\fIeasy_handle\fP is used for part separator randomization and error
reporting. It does not need to be the final target handle.
Using a mime handle is the recommended way to post an HTTP form, format and Using a mime handle is the recommended way to post an HTTP form, format and
send a multi-part email with SMTP or upload such an email to an IMAP server. send a multi-part email with SMTP or upload such an email to an IMAP server.
@ -65,5 +68,6 @@ As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
A mime struct handle, or NULL upon failure. A mime struct handle, or NULL upon failure.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR curl_mime_addpart "(3)," .BR curl_mime_addpart "(3),"
.BR curl_mime_subparts "(3),"
.BR curl_mime_free "(3)," .BR curl_mime_free "(3),"
.BR CURLOPT_MIMEPOST "(3)" .BR CURLOPT_MIMEPOST "(3)"

Просмотреть файл

@ -828,7 +828,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
/* Copy src->set into dst->set first, then deal with the strings /* Copy src->set into dst->set first, then deal with the strings
afterwards */ afterwards */
dst->set = src->set; dst->set = src->set;
Curl_mime_initpart(&dst->set.mimepost, dst); Curl_mime_initpart(&dst->set.mimepost);
/* clear all string pointers first */ /* clear all string pointers first */
memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
@ -862,7 +862,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
} }
/* Duplicate mime data. */ /* Duplicate mime data. */
result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost);
if(src->set.resolve) if(src->set.resolve)
dst->state.resolve = dst->set.resolve; dst->state.resolve = dst->set.resolve;

Просмотреть файл

@ -715,10 +715,10 @@ int curl_formget(struct curl_httppost *form, void *arg,
CURLcode result; CURLcode result;
curl_mimepart toppart; curl_mimepart toppart;
Curl_mime_initpart(&toppart, NULL); /* default form is empty */ Curl_mime_initpart(&toppart); /* default form is empty */
result = Curl_getformdata(NULL, &toppart, form, NULL); result = Curl_getformdata(NULL, &toppart, form, NULL);
if(!result) if(!result)
result = Curl_mime_prepare_headers(&toppart, "multipart/form-data", result = Curl_mime_prepare_headers(NULL, &toppart, "multipart/form-data",
NULL, MIMESTRATEGY_FORM); NULL, MIMESTRATEGY_FORM);
while(!result) { while(!result) {

Просмотреть файл

@ -265,7 +265,7 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
if(!http) if(!http)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
Curl_mime_initpart(&http->form, data); Curl_mime_initpart(&http->form);
data->req.p.http = http; data->req.p.http = http;
if(data->state.httpwant == CURL_HTTP_VERSION_3) { if(data->state.httpwant == CURL_HTTP_VERSION_3) {
@ -2303,7 +2303,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
cthdr = "multipart/form-data"; cthdr = "multipart/form-data";
curl_mime_headers(http->sendit, data->set.headers, 0); curl_mime_headers(http->sendit, data->set.headers, 0);
result = Curl_mime_prepare_headers(http->sendit, cthdr, result = Curl_mime_prepare_headers(data, http->sendit, cthdr,
NULL, MIMESTRATEGY_FORM); NULL, MIMESTRATEGY_FORM);
curl_mime_headers(http->sendit, NULL, 0); curl_mime_headers(http->sendit, NULL, 0);
if(!result) if(!result)

Просмотреть файл

@ -777,7 +777,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
/* Add external headers and mime version. */ /* Add external headers and mime version. */
curl_mime_headers(&data->set.mimepost, data->set.headers, 0); curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
NULL, MIMESTRATEGY_MAIL); NULL, MIMESTRATEGY_MAIL);
if(!result) if(!result)

Просмотреть файл

@ -1175,7 +1175,7 @@ void Curl_mime_cleanpart(curl_mimepart *part)
Curl_safefree(part->mimetype); Curl_safefree(part->mimetype);
Curl_safefree(part->name); Curl_safefree(part->name);
Curl_safefree(part->filename); Curl_safefree(part->filename);
Curl_mime_initpart(part, part->easy); Curl_mime_initpart(part);
} }
/* Recursively delete a mime handle and its parts. */ /* Recursively delete a mime handle and its parts. */
@ -1195,7 +1195,8 @@ void curl_mime_free(curl_mime *mime)
} }
} }
CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) CURLcode Curl_mime_duppart(struct Curl_easy *data,
curl_mimepart *dst, const curl_mimepart *src)
{ {
curl_mime *mime; curl_mime *mime;
curl_mimepart *d; curl_mimepart *d;
@ -1224,13 +1225,13 @@ CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
case MIMEKIND_MULTIPART: case MIMEKIND_MULTIPART:
/* No one knows about the cloned subparts, thus always attach ownership /* No one knows about the cloned subparts, thus always attach ownership
to the part. */ to the part. */
mime = curl_mime_init(dst->easy); mime = curl_mime_init(data);
res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY; res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY;
/* Duplicate subparts. */ /* Duplicate subparts. */
for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) { for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) {
d = curl_mime_addpart(mime); d = curl_mime_addpart(mime);
res = d? Curl_mime_duppart(d, s): CURLE_OUT_OF_MEMORY; res = d? Curl_mime_duppart(data, d, s): CURLE_OUT_OF_MEMORY;
} }
break; break;
default: /* Invalid kind: should not occur. */ default: /* Invalid kind: should not occur. */
@ -1282,7 +1283,6 @@ curl_mime *curl_mime_init(struct Curl_easy *easy)
mime = (curl_mime *) malloc(sizeof(*mime)); mime = (curl_mime *) malloc(sizeof(*mime));
if(mime) { if(mime) {
mime->easy = easy;
mime->parent = NULL; mime->parent = NULL;
mime->firstpart = NULL; mime->firstpart = NULL;
mime->lastpart = NULL; mime->lastpart = NULL;
@ -1302,10 +1302,9 @@ curl_mime *curl_mime_init(struct Curl_easy *easy)
} }
/* Initialize a mime part. */ /* Initialize a mime part. */
void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy) void Curl_mime_initpart(curl_mimepart *part)
{ {
memset((char *) part, 0, sizeof(*part)); memset((char *) part, 0, sizeof(*part));
part->easy = easy;
part->lastreadstatus = 1; /* Successful read status. */ part->lastreadstatus = 1; /* Successful read status. */
mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); mimesetstate(&part->state, MIMESTATE_BEGIN, NULL);
} }
@ -1321,7 +1320,7 @@ curl_mimepart *curl_mime_addpart(curl_mime *mime)
part = (curl_mimepart *) malloc(sizeof(*part)); part = (curl_mimepart *) malloc(sizeof(*part));
if(part) { if(part) {
Curl_mime_initpart(part, mime->easy); Curl_mime_initpart(part);
part->parent = mime; part->parent = mime;
if(mime->lastpart) if(mime->lastpart)
@ -1551,10 +1550,6 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part,
cleanup_part_content(part); cleanup_part_content(part);
if(subparts) { if(subparts) {
/* Must belong to the same data handle. */
if(part->easy && subparts->easy && part->easy != subparts->easy)
return CURLE_BAD_FUNCTION_ARGUMENT;
/* Should not have been attached already. */ /* Should not have been attached already. */
if(subparts->parent) if(subparts->parent)
return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_BAD_FUNCTION_ARGUMENT;
@ -1565,8 +1560,7 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part,
while(root->parent && root->parent->parent) while(root->parent && root->parent->parent)
root = root->parent->parent; root = root->parent->parent;
if(subparts == root) { if(subparts == root) {
if(part->easy) /* Can't add as a subpart of itself. */
failf(part->easy, "Can't add itself as a subpart");
return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_BAD_FUNCTION_ARGUMENT;
} }
} }
@ -1766,7 +1760,8 @@ static bool content_type_match(const char *contenttype,
return FALSE; return FALSE;
} }
CURLcode Curl_mime_prepare_headers(curl_mimepart *part, CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
curl_mimepart *part,
const char *contenttype, const char *contenttype,
const char *disposition, const char *disposition,
enum mimestrategy strategy) enum mimestrategy strategy)
@ -1835,12 +1830,12 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
char *filename = NULL; char *filename = NULL;
if(part->name) { if(part->name) {
name = escape_string(part->easy, part->name, strategy); name = escape_string(data, part->name, strategy);
if(!name) if(!name)
ret = CURLE_OUT_OF_MEMORY; ret = CURLE_OUT_OF_MEMORY;
} }
if(!ret && part->filename) { if(!ret && part->filename) {
filename = escape_string(part->easy, part->filename, strategy); filename = escape_string(data, part->filename, strategy);
if(!filename) if(!filename)
ret = CURLE_OUT_OF_MEMORY; ret = CURLE_OUT_OF_MEMORY;
} }
@ -1897,7 +1892,8 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
if(content_type_match(contenttype, STRCONST("multipart/form-data"))) if(content_type_match(contenttype, STRCONST("multipart/form-data")))
disposition = "form-data"; disposition = "form-data";
for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy); ret = Curl_mime_prepare_headers(data, subpart, NULL,
disposition, strategy);
if(ret) if(ret)
return ret; return ret;
} }

Просмотреть файл

@ -99,7 +99,6 @@ struct mime_state {
/* A mime multipart. */ /* A mime multipart. */
struct curl_mime { struct curl_mime {
struct Curl_easy *easy; /* The associated easy handle. */
curl_mimepart *parent; /* Parent part. */ curl_mimepart *parent; /* Parent part. */
curl_mimepart *firstpart; /* First part. */ curl_mimepart *firstpart; /* First part. */
curl_mimepart *lastpart; /* Last part. */ curl_mimepart *lastpart; /* Last part. */
@ -109,7 +108,6 @@ struct curl_mime {
/* A mime part. */ /* A mime part. */
struct curl_mimepart { struct curl_mimepart {
struct Curl_easy *easy; /* The associated easy handle. */
curl_mime *parent; /* Parent mime structure. */ curl_mime *parent; /* Parent mime structure. */
curl_mimepart *nextpart; /* Forward linked list. */ curl_mimepart *nextpart; /* Forward linked list. */
enum mimekind kind; /* The part kind. */ enum mimekind kind; /* The part kind. */
@ -139,14 +137,16 @@ CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
!defined(CURL_DISABLE_IMAP)) !defined(CURL_DISABLE_IMAP))
/* Prototypes. */ /* Prototypes. */
void Curl_mime_initpart(struct curl_mimepart *part, struct Curl_easy *easy); void Curl_mime_initpart(struct curl_mimepart *part);
void Curl_mime_cleanpart(struct curl_mimepart *part); void Curl_mime_cleanpart(struct curl_mimepart *part);
CURLcode Curl_mime_duppart(struct curl_mimepart *dst, CURLcode Curl_mime_duppart(struct Curl_easy *data,
struct curl_mimepart *dst,
const curl_mimepart *src); const curl_mimepart *src);
CURLcode Curl_mime_set_subparts(struct curl_mimepart *part, CURLcode Curl_mime_set_subparts(struct curl_mimepart *part,
struct curl_mime *subparts, struct curl_mime *subparts,
int take_ownership); int take_ownership);
CURLcode Curl_mime_prepare_headers(struct curl_mimepart *part, CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
struct curl_mimepart *part,
const char *contenttype, const char *contenttype,
const char *disposition, const char *disposition,
enum mimestrategy strategy); enum mimestrategy strategy);
@ -159,11 +159,11 @@ void Curl_mime_unpause(struct curl_mimepart *part);
#else #else
/* if disabled */ /* if disabled */
#define Curl_mime_initpart(x,y) #define Curl_mime_initpart(x)
#define Curl_mime_cleanpart(x) #define Curl_mime_cleanpart(x)
#define Curl_mime_duppart(x,y) CURLE_OK /* Nothing to duplicate. Succeed */ #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_set_subparts(a,b,c) CURLE_NOT_BUILT_IN
#define Curl_mime_prepare_headers(a,b,c,d) 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_size(x) (curl_off_t) -1
#define Curl_mime_read NULL #define Curl_mime_read NULL
#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN) #define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)

Просмотреть файл

@ -697,7 +697,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
/* Add external headers and mime version. */ /* Add external headers and mime version. */
curl_mime_headers(&data->set.mimepost, data->set.headers, 0); curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
NULL, MIMESTRATEGY_MAIL); NULL, MIMESTRATEGY_MAIL);
if(!result) if(!result)

Просмотреть файл

@ -560,7 +560,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
/* make libcurl quiet by default: */ /* make libcurl quiet by default: */
set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
Curl_mime_initpart(&set->mimepost, data); Curl_mime_initpart(&set->mimepost);
/* /*
* libcurl 7.10 introduced SSL verification *by default*! This needs to be * libcurl 7.10 introduced SSL verification *by default*! This needs to be