1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 16:18:48 -05:00

mime: new MIME API.

Available in HTTP, SMTP and IMAP.
Deprecates the FORM API.
See CURLOPT_MIMEPOST.
Lib code and associated documentation.
This commit is contained in:
Patrick Monnerat 2017-09-02 17:47:10 +01:00
parent 5bae72734b
commit ce0881edee
38 changed files with 2946 additions and 994 deletions

View File

@ -17,4 +17,8 @@ man_MANS = curl_easy_cleanup.3 curl_easy_getinfo.3 curl_easy_init.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 curl_easy_recv.3 curl_easy_send.3 \ curl_easy_pause.3 curl_easy_recv.3 curl_easy_send.3 \
curl_multi_socket_action.3 curl_multi_wait.3 libcurl-symbols.3 \ curl_multi_socket_action.3 curl_multi_wait.3 libcurl-symbols.3 \
libcurl-thread.3 curl_multi_socket_all.3 curl_global_sslset.3 libcurl-thread.3 curl_multi_socket_all.3 curl_global_sslset.3 \
curl_mime_init.3 curl_mime_free.3 curl_mime_addpart.3 curl_mime_name.3 \
curl_mime_data.3 curl_mime_data_cb.3 curl_mime_filedata.3 \
curl_mime_filename.3 curl_mime_subparts.3 \
curl_mime_type.3 curl_mime_headers.3

View File

@ -411,6 +411,8 @@ Size of file to send. \fICURLOPT_INFILESIZE(3)\fP
Size of file to send. \fICURLOPT_INFILESIZE_LARGE(3)\fP Size of file to send. \fICURLOPT_INFILESIZE_LARGE(3)\fP
.IP CURLOPT_UPLOAD .IP CURLOPT_UPLOAD
Upload data. See \fICURLOPT_UPLOAD(3)\fP Upload data. See \fICURLOPT_UPLOAD(3)\fP
.IP CURLOPT_MIMEPOST
Post/send MIME data. See \fICURLOPT_MIMEPOST(3)\fP
.IP CURLOPT_MAXFILESIZE .IP CURLOPT_MAXFILESIZE
Maximum file size to get. See \fICURLOPT_MAXFILESIZE(3)\fP Maximum file size to get. See \fICURLOPT_MAXFILESIZE(3)\fP
.IP CURLOPT_MAXFILESIZE_LARGE .IP CURLOPT_MAXFILESIZE_LARGE

View File

@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___ .\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____| .\" * \___|\___/|_| \_\_____|
.\" * .\" *
.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * .\" *
.\" * This software is licensed as described in the file COPYING, which .\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms .\" * you should have received as part of this distribution. The terms
@ -29,6 +29,8 @@ curl_formadd - add a section to a multipart/formdata HTTP POST
.BI "struct curl_httppost ** " lastitem, " ...);" .BI "struct curl_httppost ** " lastitem, " ...);"
.ad .ad
.SH DESCRIPTION .SH DESCRIPTION
This function is deprecated. Do not use! See \fIcurl_mime_init(3)\fP instead!
curl_formadd() is used to append sections when building a multipart/formdata curl_formadd() is used to append sections when building a multipart/formdata
HTTP POST (sometimes referred to as RFC2388-style posts). Append one section HTTP POST (sometimes referred to as RFC2388-style posts). Append one section
at a time until you've added all the sections you want included and then you at a time until you've added all the sections you want included and then you
@ -169,6 +171,8 @@ the \fICURLOPT_HTTPPOST(3)\fP option), you must not free the list until after
you've called \fIcurl_easy_cleanup(3)\fP for the curl handle. you've called \fIcurl_easy_cleanup(3)\fP for the curl handle.
See example below. See example below.
.SH AVAILABILITY
Deprecated in 7.56.0.
.SH RETURN VALUE .SH RETURN VALUE
0 means everything was ok, non-zero means an error occurred corresponding 0 means everything was ok, non-zero means an error occurred corresponding
to a CURL_FORMADD_* constant defined in to a CURL_FORMADD_* constant defined in
@ -254,5 +258,6 @@ to a CURL_FORMADD_* constant defined in
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post); curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
.SH "SEE ALSO" .SH "SEE ALSO"
.BR curl_easy_setopt "(3), " .BR curl_easy_setopt "(3),"
.BR curl_formfree "(3)" .BR curl_formfree "(3),"
.BR curl_mime_init "(3)"

View File

@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___ .\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____| .\" * \___|\___/|_| \_\_____|
.\" * .\" *
.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * .\" *
.\" * This software is licensed as described in the file COPYING, which .\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms .\" * you should have received as part of this distribution. The terms
@ -28,6 +28,8 @@ curl_formfree - free a previously build multipart/formdata HTTP POST chain
.BI "void curl_formfree(struct curl_httppost *" form); .BI "void curl_formfree(struct curl_httppost *" form);
.ad .ad
.SH DESCRIPTION .SH DESCRIPTION
This function is deprecated. Do not use! See \fIcurl_mime_init(3)\fP instead!
curl_formfree() is used to clean up data previously built/appended with curl_formfree() is used to clean up data previously built/appended with
\fIcurl_formadd(3)\fP. This must be called when the data has been used, which \fIcurl_formadd(3)\fP. This must be called when the data has been used, which
typically means after \fIcurl_easy_perform(3)\fP has been called. typically means after \fIcurl_easy_perform(3)\fP has been called.
@ -38,7 +40,9 @@ the \fIcurl_formadd(3)\fP invoke(s).
\fBform\fP is the pointer as returned from a previous call to \fBform\fP is the pointer as returned from a previous call to
\fIcurl_formadd(3)\fP and may be NULL. \fIcurl_formadd(3)\fP and may be NULL.
.SH AVAILABILITY
Deprecated in 7.56.0.
.SH RETURN VALUE .SH RETURN VALUE
None None
.SH "SEE ALSO" .SH "SEE ALSO"
.BR curl_formadd "(3) " .BR curl_formadd "(3), " curl_mime_init "(3), " curl_mime_free "(3)"

View File

@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___ .\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____| .\" * \___|\___/|_| \_\_____|
.\" * .\" *
.\" * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * .\" *
.\" * This software is licensed as described in the file COPYING, which .\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms .\" * you should have received as part of this distribution. The terms
@ -65,6 +65,7 @@ request as only then will libcurl get the actual read callback to use!
return total_size; return total_size;
} }
.SH AVAILABILITY .SH AVAILABILITY
This function was added in libcurl 7.15.5 This function was added in libcurl 7.15.5. The form API is deprecated in
libcurl 7.56.0.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR curl_formadd "(3) " .BR curl_formadd "(3), " curl_mime_init "(3)"

View File

@ -0,0 +1,52 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_addpart 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_addpart - append a new empty part to a mime structure
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "curl_mimepart * curl_mime_addpart(curl_mime * " mime ");"
.ad
.SH DESCRIPTION
curl_mime_addpart() appends a new empty part to the given mime structure and
returns a handle to it.
The returned part can be subsequently filled using functions from the mime API.
\fImime\fP is the handle of the mime structure in which the new part must be
appended.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
A mime part structure handle, or NULL upon failure.
.SH "SEE ALSO"
.BR curl_mime_init "(3),"
.BR curl_mime_name "(3),"
.BR curl_mime_data "(3),"
.BR curl_mime_data_cb "(3),"
.BR curl_mime_filedata "(3),"
.BR curl_mime_filename "(3),"
.BR curl_mime_subparts "(3),"
.BR curl_mime_type "(3),"
.BR curl_mime_headers "(3)"

View File

@ -0,0 +1,54 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_data 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_data - set a mime part's body data from memory
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_data(curl_mimepart * " part ", const char * " data
.BI ", ssize_t " datasize ");"
.ad
.SH DESCRIPTION
curl_mime_data() sets a mime part's body content from memory data.
\fIdata\fP points to the data bytes: those are copied to the part and their
storage may safely be reused after call.
\fIdatasize\fP is the number of data bytes: it can be set to -1 to indicate
\fIdata\fP is a nul-terminated character string.
\fIpart\fP is the part's to assign contents to.
Setting a part's contents twice is valid: only the value set by the last call
is retained. It is possible to unassign part's contents by setting
\fIdata\fP to NULL.
Setting very large data is memory consuming: one might consider using
\fIcurl_mime_data_cb\fP() in such a case.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3),"
.BR curl_mime_data_cb "(3)"

View File

@ -0,0 +1,154 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_data_cb 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_data_cb - set a callback-based data source for a mime part's body
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
size_t readfunc(char *buffer, size_t size, size_t nitems, void *arg);
.br
int seekfunc(void *arg, curl_off_t offset, int origin);
.br
void freefunc(void *arg);
.sp
.BI "CURLcode curl_mime_data(curl_mimepart * " part ", curl_off_t " datasize ,
.br
.BI " curl_read_callback " readfunc ", curl_seek_callback " seekfunc ,
.br
.BI " curl_free_callback " freefunc ", void * " arg ");"
.ad
.SH DESCRIPTION
curl_mime_data_cb() sets the data source of a mime part's body content from
a data read callback function.
\fIpart\fP is the part's to assign contents to.
\fIreadfunc\fP is a pointer to a data read callback function, with a
signature as shown by the above prototype. It may not be set to NULL.
\fIseekfunc\fP is a pointer to a seek callback function, with a
signature as shown by the above prototype. This function will be used upon
resending data (i.e.: after a redirect); this pointer may be set to NULL,
in which case a resend is not possible.
\fIfreefunc\fP is a pointer to a user resource freeing callback function, with
a signature as shown by the above prototype. If no resource is to be freed,
it may safely be set to NULL. This function will be called upon mime
structure freeing.
\fIarg\fP is a user defined argument to callback functions.
The read callback function gets called by libcurl as soon as it needs to
read data in order to send it to the peer - like if you ask it to upload or
post data to the server. The data area pointed at by the pointer \fIbuffer\fP
should be filled up with at most \fIsize\fP multiplied with \fInmemb\fP number
of bytes by your function.
Your read function must then return the actual number of bytes that it stored
in that memory area. Returning 0 will signal end-of-file to the library and
cause it to stop the current transfer.
If you stop the current transfer by returning 0 "pre-maturely" (i.e before the
server expected it, like when you've said you will upload N bytes and you
upload less than N bytes), you may experience that the server "hangs" waiting
for the rest of the data that won't come.
The read callback may return \fICURL_READFUNC_ABORT\fP to stop the current
operation immediately, resulting in a \fICURLE_ABORTED_BY_CALLBACK\fP error
code from the transfer.
The callback can return \fICURL_READFUNC_PAUSE\fP to cause reading from this
connection to pause. See \fIcurl_easy_pause(3)\fP for further details.
The seek function gets called by libcurl to rewind input stream data or to
seek to a certain position. The function shall work like fseek(3) or lseek(3)
and it gets SEEK_SET, SEEK_CUR or SEEK_END as argument for \fIorigin\fP,
although libcurl currently only passes SEEK_SET.
The callback function must return \fICURL_SEEKFUNC_OK\fP on success,
\fICURL_SEEKFUNC_FAIL\fP to cause the upload operation to fail or
\fICURL_SEEKFUNC_CANTSEEK\fP to indicate that while the seek failed, libcurl
is free to work around the problem if possible. The latter can sometimes be
done by instead reading from the input or similar.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH EXAMPLE
Sending a huge data string will cause the same amount of memory to be
allocated: to avoid overhead resources consumption, one might want to use a
callback source to avoid data duplication. In this case, original data
must be retained until after the transfer terminates.
.nf
char hugedata[512000];
struct ctl {
char *buffer;
curl_off_t size;
curl_off_t position;
};
size_t read_callback(char *buffer, size_t size, size_t nitems, void *arg)
{
struct ctl *p = (struct ctl *) arg;
curl_off_t sz = p->size - p->position;
nitems *= size;
if(sz > nitems)
sz = nitems;
if(sz)
memcpy(buffer, p->buffer + p->position, sz);
p->position += sz;
return sz;
}
int seek_callback(void *arg, curl_off_t offset, int origin)
{
struct ctl *p = (struct ctl *) arg;
switch(origin) {
case SEEK_END:
offset += p->size;
break;
case SEEK_CUR:
offset += p->position;
break;
}
if(offset < 0)
return CURL_SEEKFUNC_FAIL;
p->position = offset;
return CURL_SEEKFUNC_OK;
}
CURL *easy = curl_easy_init();
curl_mime *mime = curl_mime_init(easy);
curl_mimepart *part = curl_mime_addpart(mime);
struct ctl hugectl;
hugectl.buffer = hugedata;
hugectl.size = sizeof hugedata;
hugectl.position = 0;
curl_mime_data_cb(part, hugectl.size, read_callback, seek_callback, NULL,
&hugectl);
.SH "SEE ALSO"
.BR curl_mime_addpart "(3)"

View File

@ -0,0 +1,55 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_filedata 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_filedata - set a mime part's body data from a file contents
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_filedata(curl_mimepart * " part ,
.BI " const char * " filename ");"
.ad
.SH DESCRIPTION
curl_mime_filedata() sets a mime part's body content from the named file's
contents.
\fIpart\fP is the part's to assign contents to.
\fIfilename\fP points to the nul-terminated file's path name. The pointer can
be NULL to detach previous part contents settings.
If \fIfilename\fP is "-", part contents data will be read from stdin.
Filename storage can be safely be reused after this call.
As a side effect, the part's remote file name is set to the base name of the
given \fIfilename\fP if it is a valid named file. This can be undone or
overriden by a subsequent call to \fIcurl_mime_filename\fP().
Setting a part's contents twice is valid: only the value set by the last call
is retained.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3),"
.BR curl_mime_filename "(3)"

View File

@ -0,0 +1,51 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_filename 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_filename - set a mime part's remote file name
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_filename(curl_mimepart * " part ,
.BI "const char * " filename ");"
.ad
.SH DESCRIPTION
curl_mime_filename() sets a mime part's remote file name. When remote file
name is set, content data is processed as a file, whatever is the part's
content source. A part's remote file name is transmitted to the server in
the associated Content-Disposition generated header.
\fIpart\fP is the part's handle to assign the remote file name to.
\fIfilename\fP points to the nul-terminated file name string; it may be set to
NULL to remove a previously attached remote file name.
The remote file name string is copied into the part, thus the associated
storage may safely be released or reused after call. Setting a part's file
name twice is valid: only the value set by the last call is retained.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3)"

View File

@ -0,0 +1,48 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_free 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_free - free a previously built mime structure
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "void curl_mime_free(curl_mime *" mime);
.ad
.SH DESCRIPTION
curl_mime_free() is used to clean up data previously built/appended with
\fIcurl_mime_addpart(3)\fP and other mime-handling functions.
This must be called when the data has been used, which
typically means after \fIcurl_easy_perform(3)\fP has been called.
The handle to free is the one you passed to
the \fICURLOPT_MIMEPOST(3)\fP option: attached subparts mime structures must
not be explicitly freed as they are by the top structure freeing.
\fBmime\fP is the handle as returned from a previous call to
\fIcurl_mime_init(3)\fP and may be NULL.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
None
.SH "SEE ALSO"
.BR curl_mime_init "(3)"

View File

@ -0,0 +1,50 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_headers 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_headers - set a mime part's custom headers
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_headers(curl_mimepart * " part ,
.BI "struct curl_slist * " headers ", int " take_ownership ");"
.ad
.SH DESCRIPTION
curl_mime_headers() sets a mime part's custom headers.
\fIpart\fP is the part's handle to assign the custom headers list to.
\fIheaders\fP is the head of a list of custom headers; it may be set to
NULL to remove a previously attached custom header list.
\fItake_ownership\fP: when non-zero, causes the list to be freed upon
replacement or mime structure deletion; in this case the list must not be
freed explicitly.
Setting a part's custom headers list twice is valid: only the value set by
the last call is retained.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3)"

View File

@ -0,0 +1,68 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_init 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_init - create a mime handle
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "curl_mime * curl_mime_init(CURL * " easy_handle ");"
.ad
.SH DESCRIPTION
curl_mime_init() creates a handle to a new empty mime structure intended to be
used with \fIeasy_handle\fP. This mime structure can be subsequently filled
using the mime API, then attached to \fIeasy_handle\fP using option
\fICURLOPT_MIMEPOST\fP within a \fIcurl_easy_setopt\fP() call.
Using a mime handle is the recommended way to post an HTTP form, format and
send a multi-part e-mail with SMTP or upload such an e-mail to an IMAP server.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
A mime struct handle, or NULL upon failure.
.SH EXAMPLE
.nf
CURL *easy = curl_easy_init();
struct curl_mime *mime;
struct mimepart *part;
/* Build an HTTP form with a single field named "data", */
mime = curl_mime_init(easy);
part = curl_mime_addpart(mime);
curl_mime_data(part, "This is the field data", -1);
curl_mime_name(part, "data", -1);
/* Post and send it. */
curl_easy_setopt(easy, CURLOPT_MIMEPOST, mime);
curl_easy_setopt(easy, CURLOPT_URL, "http://example.com");
curl_easy_perform(easy);
/* Clean-up. */
curl_easy_cleanup(easy);
curl_mime_free(mime);
.SH "SEE ALSO"
.BR curl_mime_addpart "(3),"
.BR curl_mime_free "(3),"
.BR CURLOPT_MIMEPOST "(3)"

View File

@ -0,0 +1,52 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_name 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_name - set a mime part's name
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_name(curl_mimepart * " part ", const char * " name
.BI ", ssize_t " namesize ");"
.ad
.SH DESCRIPTION
curl_mime_name() sets a mime part's name. This is the way HTTP form fields are
named.
\fIname\fP points to the name byte string; the string may contain nul bytes
unless \fInamesize\fP is -1.
\fInamesize\fP is the name length: it can be set to -1 to indicate
\fIname\fP is a nul-terminated string.
\fIpart\fP is the part's handle to assign a name to.
The name string is copied into the part, thus the associated storage may safely
be released or reused after call. Setting a part's name twice is valid:
only the value set by the last call is retained. It is possible to "unname"
a part by setting \fIname\fP to NULL.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3)"

View File

@ -0,0 +1,52 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_subparts 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_subparts - set subparts of a multipart mime part
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_subparts(curl_mimepart * " part ,
.BI "curl_mime * " subparts ");"
.ad
.SH DESCRIPTION
curl_mime_subparts() sets a multipart mime part's content from a mime
structure.
\fIpart\fP is a handle to the multipart part.
\fIsubparts\fP is a mime structure handle holding the subparts. After
\fIcurl_mime_subparts\fP succeeds, the mime structure handle belongs to the
multipart part and must not be freed explicitly. It may however be updated
by subsequent calls to mime API functions.
Setting a part's contents twice is valid: only the value set by the last call
is retained. It is possible to unassign previous part's contents by setting
\fIsubparts\fP to NULL.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3),"
.BR curl_mime_init "(3)"

View File

@ -0,0 +1,62 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.TH curl_mime_type 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
.SH NAME
curl_mime_type - set a mime part's content type
.SH SYNOPSIS
.B #include <curl/curl.h>
.sp
.BI "CURLcode curl_mime_type(curl_mimepart * " part ,
.BI "const char * " mimetype ");"
.ad
.SH DESCRIPTION
curl_mime_type() sets a mime part's content type.
\fIpart\fP is the part's handle to assign the content type to.
\fImimetype\fP points to the nul-terminated file mime type string; it may be
set to NULL to remove a previously attached mime type.
The mime type string is copied into the part, thus the associated
storage may safely be released or reused after call. Setting a part's type
twice is valid: only the value set by the last call is retained.
In the absence of a mime type and if needed by the protocol specifications,
a default mime type is determined by the context:
.br
- If set as a custom header, use this value.
.br
- application/form-data for a HTTP form post.
.br
- If a remote file name is set, the mime type is taken from the file name
extension, or application/octet-stream by default.
.br
- For a multipart part, multipart/mixed.
.br
- text/plain in other cases.
.SH AVAILABILITY
As long as at least one of HTTP, SMTP or IMAP is enabled. Added in 7.56.0.
.SH RETURN VALUE
CURLE_OK or a CURL error code upon failure.
.SH "SEE ALSO"
.BR curl_mime_addpart "(3)"

View File

@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___ .\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____| .\" * \___|\___/|_| \_\_____|
.\" * .\" *
.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * .\" *
.\" * This software is licensed as described in the file COPYING, which .\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms .\" * you should have received as part of this distribution. The terms
@ -42,6 +42,9 @@ You can disable this header with \fICURLOPT_HTTPHEADER(3)\fP.
When setting \fICURLOPT_HTTPPOST(3)\fP, it will automatically set When setting \fICURLOPT_HTTPPOST(3)\fP, it will automatically set
\fICURLOPT_NOBODY(3)\fP to 0. \fICURLOPT_NOBODY(3)\fP to 0.
This option is deprecated! Do not use it. Use \fICURLOPT_MIMEPOST(3)\fP
instead after having prepared mime data.
.SH DEFAULT .SH DEFAULT
NULL NULL
.SH PROTOCOLS .SH PROTOCOLS
@ -71,9 +74,9 @@ curl_formadd(&formpost,
CURLFORM_END); CURLFORM_END);
.fi .fi
.SH AVAILABILITY .SH AVAILABILITY
As long as HTTP is enabled As long as HTTP is enabled. Deprecated in 7.56.0.
.SH RETURN VALUE .SH RETURN VALUE
Returns CURLE_OK if HTTP is enabled, and CURLE_UNKNOWN_OPTION if not. Returns CURLE_OK if HTTP is enabled, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR CURLOPT_POSTFIELDS "(3), " CURLOPT_POST "(3), " .BR CURLOPT_POSTFIELDS "(3), " CURLOPT_POST "(3), " CURLOPT_MIMEPOST "(3),"
.BR curl_formadd "(3), " curl_formfree "(3), " .BR curl_formadd "(3), " curl_formfree "(3), " curl_mime_init "(3)"

View File

@ -0,0 +1,52 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_MIMEPOST 3 "22 Aug 2017" "libcurl 7.56.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_MIMEPOST \- set post/send data from mime structure
.SH SYNOPSIS
.nf
#include <curl/curl.h>
curl_mime *mime;
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MIMEPOST, mime);
.SH DESCRIPTION
Pass a mime handle previously obtained from \fIcurl_mime_init()\fP.
This setting is supported by the HTTP protocol to post forms and by the
SMTP and IMAP protocols to provide the e-mail data to send/upload.
This option is the preferred way of posting an HTTP form, replacing
and extending the deprecated \fICURLOPT_HTTPPOST\fP option.
.SH PROTOCOLS
HTTP, SMTP, IMAP.
.SH AVAILABILITY
Since 7.56.0.
.SH RETURN VALUE
This will return CURLE_OK.
.SH EXAMPLE
Using this option implies the use of several mime structure building
functions: see https://curl.haxx.se/libcurl/c/smtp-mime.html for a complete
example.
.SH "SEE ALSO"
.BR curl_mime_init "(3)"

View File

@ -175,6 +175,7 @@ man_MANS = \
CURLOPT_MAXREDIRS.3 \ CURLOPT_MAXREDIRS.3 \
CURLOPT_MAX_RECV_SPEED_LARGE.3 \ CURLOPT_MAX_RECV_SPEED_LARGE.3 \
CURLOPT_MAX_SEND_SPEED_LARGE.3 \ CURLOPT_MAX_SEND_SPEED_LARGE.3 \
CURLOPT_MIMEPOST.3 \
CURLOPT_NETRC.3 \ CURLOPT_NETRC.3 \
CURLOPT_NETRC_FILE.3 \ CURLOPT_NETRC_FILE.3 \
CURLOPT_NEW_DIRECTORY_PERMS.3 \ CURLOPT_NEW_DIRECTORY_PERMS.3 \

View File

@ -441,6 +441,7 @@ CURLOPT_MAXFILESIZE_LARGE 7.11.0
CURLOPT_MAXREDIRS 7.5 CURLOPT_MAXREDIRS 7.5
CURLOPT_MAX_RECV_SPEED_LARGE 7.15.5 CURLOPT_MAX_RECV_SPEED_LARGE 7.15.5
CURLOPT_MAX_SEND_SPEED_LARGE 7.15.5 CURLOPT_MAX_SEND_SPEED_LARGE 7.15.5
CURLOPT_MIMEPOST 7.56.0
CURLOPT_MUTE 7.1 7.8 7.15.5 CURLOPT_MUTE 7.1 7.8 7.15.5
CURLOPT_NETRC 7.1 CURLOPT_NETRC 7.1
CURLOPT_NETRC_FILE 7.11.0 CURLOPT_NETRC_FILE 7.11.0

View File

@ -1791,6 +1791,9 @@ typedef enum {
/* Enable/disable SSH compression */ /* Enable/disable SSH compression */
CINIT(SSH_COMPRESSION, LONG, 268), CINIT(SSH_COMPRESSION, LONG, 268),
/* Post MIME data. */
CINIT(MIMEPOST, OBJECTPOINT, 269),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;
@ -1946,6 +1949,133 @@ typedef enum {
CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2);
CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n);
/* Mime/form handling support. */
typedef struct Curl_mime curl_mime; /* Mime context. */
typedef struct Curl_mimepart curl_mimepart; /* Mime part context. */
/*
* NAME curl_mime_init()
*
* DESCRIPTION
*
* Create a mime context and return its handle. The easy parameter is the
* target handle.
*/
CURL_EXTERN curl_mime *curl_mime_init(CURL *easy);
/*
* NAME curl_mime_free()
*
* DESCRIPTION
*
* release a mime handle and its substructures.
*/
CURL_EXTERN void curl_mime_free(curl_mime *mime);
/*
* NAME curl_mime_addpart()
*
* DESCRIPTION
*
* Append a new empty part to the given mime context and return a handle to
* the created part.
*/
CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime);
/*
* NAME curl_mime_name()
*
* DESCRIPTION
*
* Set mime/form part name.
*/
CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part,
const char *name, ssize_t namesize);
/*
* NAME curl_mime_filename()
*
* DESCRIPTION
*
* Set mime part remote file name.
*/
CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part,
const char *filename);
/*
* NAME curl_mime_type()
*
* DESCRIPTION
*
* Set mime part type.
*/
CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype);
/*
* NAME curl_mime_encoder()
*
* DESCRIPTION
*
* Set mime data transfer encoder.
*/
CURL_EXTERN CURLcode curl_mime_encoder(struct Curl_mimepart *part,
const char *encoding);
/*
* NAME curl_mime_data()
*
* DESCRIPTION
*
* Set mime part data source from memory data,
*/
CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part,
const char *data, ssize_t datasize);
/*
* NAME curl_mime_filedata()
*
* DESCRIPTION
*
* Set mime part data source from named file.
*/
CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part,
const char *filename);
/*
* NAME curl_mime_data_cb()
*
* DESCRIPTION
*
* Set mime part data source from callback function.
*/
CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part,
curl_off_t datasize,
curl_read_callback readfunc,
curl_seek_callback seekfunc,
curl_free_callback freefunc,
void *arg);
/*
* NAME curl_mime_subparts()
*
* DESCRIPTION
*
* Set mime part data source from subparts.
*/
CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part,
curl_mime *subparts);
/*
* NAME curl_mime_headers()
*
* DESCRIPTION
*
* Set mime part headers.
*/
CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part,
struct curl_slist *headers,
int take_ownership);
/* Old form API. */
/* name is uppercase CURLFORM_<name> */ /* name is uppercase CURLFORM_<name> */
#ifdef CFINIT #ifdef CFINIT
#undef CFINIT #undef CFINIT

View File

@ -96,6 +96,9 @@ __extension__ ({ \
if((_curl_opt) == CURLOPT_HTTPPOST) \ if((_curl_opt) == CURLOPT_HTTPPOST) \
if(!_curl_is_arr((value), struct curl_httppost)) \ if(!_curl_is_arr((value), struct curl_httppost)) \
_curl_easy_setopt_err_curl_httpost(); \ _curl_easy_setopt_err_curl_httpost(); \
if((_curl_opt) == CURLOPT_MIMEPOST) \
if(!_curl_is_ptr((value), curl_mime)) \
_curl_easy_setopt_err_curl_mimepost(); \
if(_curl_is_slist_option(_curl_opt)) \ if(_curl_is_slist_option(_curl_opt)) \
if(!_curl_is_arr((value), struct curl_slist)) \ if(!_curl_is_arr((value), struct curl_slist)) \
_curl_easy_setopt_err_curl_slist(); \ _curl_easy_setopt_err_curl_slist(); \
@ -200,6 +203,9 @@ _CURL_WARNING(_curl_easy_setopt_err_postfields,
_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, _CURL_WARNING(_curl_easy_setopt_err_curl_httpost,
"curl_easy_setopt expects a 'struct curl_httppost *' " "curl_easy_setopt expects a 'struct curl_httppost *' "
"argument for this option") "argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost,
"curl_easy_setopt expects a 'curl_mime *' "
"argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_curl_slist, _CURL_WARNING(_curl_easy_setopt_err_curl_slist,
"curl_easy_setopt expects a 'struct curl_slist *' argument for this option") "curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_CURLSH, _CURL_WARNING(_curl_easy_setopt_err_CURLSH,

View File

@ -53,7 +53,8 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \ http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \
http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \ http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \
curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \ curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
mime.c
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
@ -72,7 +73,7 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
curl_sasl.h curl_multibyte.h hostcheck.h conncache.h \ curl_sasl.h curl_multibyte.h hostcheck.h conncache.h \
curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \ curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \ x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
curl_printf.h system_win32.h rand.h curl_printf.h system_win32.h rand.h mime.h
LIB_RCFILES = libcurl.rc LIB_RCFILES = libcurl.rc

View File

@ -32,6 +32,8 @@
#include "urldata.h" /* for struct Curl_easy */ #include "urldata.h" /* for struct Curl_easy */
#include "formdata.h" #include "formdata.h"
#include "mime.h"
#include "non-ascii.h"
#include "vtls/vtls.h" #include "vtls/vtls.h"
#include "strcase.h" #include "strcase.h"
#include "sendf.h" #include "sendf.h"
@ -42,13 +44,6 @@
#include "curl_memory.h" #include "curl_memory.h"
#include "memdebug.h" #include "memdebug.h"
#ifndef HAVE_BASENAME
static char *Curl_basename(char *path);
#define basename(x) Curl_basename((x))
#endif
static size_t readfromfile(struct Form *form, char *buffer, size_t size);
static CURLcode formboundary(struct Curl_easy *data, char *buffer, size_t len);
/* What kind of Content-Type to use on un-specified files with unrecognized /* What kind of Content-Type to use on un-specified files with unrecognized
extensions. */ extensions. */
@ -746,211 +741,6 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost,
return result; return result;
} }
#ifdef __VMS
#include <fabdef.h>
/*
* get_vms_file_size does what it takes to get the real size of the file
*
* For fixed files, find out the size of the EOF block and adjust.
*
* For all others, have to read the entire file in, discarding the contents.
* Most posted text files will be small, and binary files like zlib archives
* and CD/DVD images should be either a STREAM_LF format or a fixed format.
*
*/
curl_off_t VmsRealFileSize(const char *name,
const struct_stat *stat_buf)
{
char buffer[8192];
curl_off_t count;
int ret_stat;
FILE * file;
file = fopen(name, FOPEN_READTEXT); /* VMS */
if(file == NULL)
return 0;
count = 0;
ret_stat = 1;
while(ret_stat > 0) {
ret_stat = fread(buffer, 1, sizeof(buffer), file);
if(ret_stat != 0)
count += ret_stat;
}
fclose(file);
return count;
}
/*
*
* VmsSpecialSize checks to see if the stat st_size can be trusted and
* if not to call a routine to get the correct size.
*
*/
static curl_off_t VmsSpecialSize(const char *name,
const struct_stat *stat_buf)
{
switch(stat_buf->st_fab_rfm) {
case FAB$C_VAR:
case FAB$C_VFC:
return VmsRealFileSize(name, stat_buf);
break;
default:
return stat_buf->st_size;
}
}
#endif
#ifndef __VMS
#define filesize(name, stat_data) (stat_data.st_size)
#else
/* Getting the expected file size needs help on VMS */
#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
#endif
/*
* AddFormData() adds a chunk of data to the FormData linked list.
*
* size is incremented by the chunk length, unless it is NULL
*/
static CURLcode AddFormData(struct FormData **formp,
enum formtype type,
const void *line,
curl_off_t length,
curl_off_t *size)
{
struct FormData *newform;
char *alloc2 = NULL;
CURLcode result = CURLE_OK;
if(length < 0 || (size && *size < 0))
return CURLE_BAD_FUNCTION_ARGUMENT;
newform = malloc(sizeof(struct FormData));
if(!newform)
return CURLE_OUT_OF_MEMORY;
newform->next = NULL;
if(type <= FORM_CONTENT) {
/* we make it easier for plain strings: */
if(!length)
length = strlen((char *)line);
#if (SIZEOF_SIZE_T < CURL_SIZEOF_CURL_OFF_T)
else if(length >= (curl_off_t)(size_t)-1) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto error;
}
#endif
if(type != FORM_DATAMEM) {
newform->line = malloc((size_t)length+1);
if(!newform->line) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
alloc2 = newform->line;
memcpy(newform->line, line, (size_t)length);
/* zero terminate for easier debugging */
newform->line[(size_t)length]=0;
}
else {
newform->line = (char *)line;
type = FORM_DATA; /* in all other aspects this is just FORM_DATA */
}
newform->length = (size_t)length;
}
else
/* For callbacks and files we don't have any actual data so we just keep a
pointer to whatever this points to */
newform->line = (char *)line;
newform->type = type;
if(size) {
if(type != FORM_FILE)
/* for static content as well as callback data we add the size given
as input argument */
*size += length;
else {
/* Since this is a file to be uploaded here, add the size of the actual
file */
if(strcmp("-", newform->line)) {
struct_stat file;
if(!stat(newform->line, &file) && !S_ISDIR(file.st_mode))
*size += filesize(newform->line, file);
else {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto error;
}
}
}
}
if(*formp) {
(*formp)->next = newform;
*formp = newform;
}
else
*formp = newform;
return CURLE_OK;
error:
if(newform)
free(newform);
if(alloc2)
free(alloc2);
return result;
}
/*
* AddFormDataf() adds printf()-style formatted data to the formdata chain.
*/
static CURLcode AddFormDataf(struct FormData **formp,
curl_off_t *size,
const char *fmt, ...)
{
char *s;
CURLcode result;
va_list ap;
va_start(ap, fmt);
s = curl_mvaprintf(fmt, ap);
va_end(ap);
if(!s)
return CURLE_OUT_OF_MEMORY;
result = AddFormData(formp, FORM_DATAMEM, s, 0, size);
if(result)
free(s);
return result;
}
/*
* Curl_formclean() is used from http.c, this cleans a built FormData linked
* list
*/
void Curl_formclean(struct FormData **form_ptr)
{
struct FormData *next, *form;
form = *form_ptr;
if(!form)
return;
do {
next=form->next; /* the following form line */
if(form->type <= FORM_CONTENT)
free(form->line); /* free the line */
free(form); /* free the struct */
form = next;
} while(form); /* continue */
*form_ptr = NULL;
}
/* /*
* curl_formget() * curl_formget()
* Serialize a curl_httppost struct. * Serialize a curl_httppost struct.
@ -962,42 +752,34 @@ int curl_formget(struct curl_httppost *form, void *arg,
curl_formget_callback append) curl_formget_callback append)
{ {
CURLcode result; CURLcode result;
curl_off_t size; struct Curl_mimepart toppart;
struct FormData *data, *ptr;
result = Curl_getformdata(NULL, &data, form, NULL, &size); Curl_mime_initpart(&toppart, NULL); /* default form is empty */
if(result) result = Curl_getformdata(NULL, &toppart, form, NULL);
return (int)result; if(!result)
result = Curl_mime_prepare_headers(&toppart, "multipart/form-data",
NULL, MIMESTRATEGY_FORM);
for(ptr = data; ptr; ptr = ptr->next) { while(!result) {
if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) { char buffer[8192];
char buffer[8192]; size_t nread = Curl_mime_read(buffer, 1, sizeof buffer, &toppart);
size_t nread;
struct Form temp;
Curl_FormInit(&temp, ptr); if(!nread)
break;
do { switch(nread) {
nread = readfromfile(&temp, buffer, sizeof(buffer)); default:
if((nread == (size_t) -1) || if(append(arg, buffer, nread) != nread)
(nread > sizeof(buffer)) || result = CURLE_READ_ERROR;
(nread != append(arg, buffer, nread))) { break;
if(temp.fp) case CURL_READFUNC_ABORT:
fclose(temp.fp); case CURL_READFUNC_PAUSE:
Curl_formclean(&data); break;
return -1;
}
} while(nread);
}
else {
if(ptr->length != append(arg, ptr->line, ptr->length)) {
Curl_formclean(&data);
return -1;
}
} }
} }
Curl_formclean(&data);
return 0; Curl_mime_cleanpart(&toppart);
return (int) result;
} }
/* /*
@ -1031,118 +813,11 @@ void curl_formfree(struct curl_httppost *form)
} while(form); /* continue */ } while(form); /* continue */
} }
#ifndef HAVE_BASENAME
/*
(Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
Edition)
The basename() function shall take the pathname pointed to by path and
return a pointer to the final component of the pathname, deleting any
trailing '/' characters.
If the string pointed to by path consists entirely of the '/' character,
basename() shall return a pointer to the string "/". If the string pointed
to by path is exactly "//", it is implementation-defined whether '/' or "//"
is returned.
If path is a null pointer or points to an empty string, basename() shall
return a pointer to the string ".".
The basename() function may modify the string pointed to by path, and may
return a pointer to static storage that may then be overwritten by a
subsequent call to basename().
The basename() function need not be reentrant. A function that is not
required to be reentrant is not required to be thread-safe.
*/
static char *Curl_basename(char *path)
{
/* Ignore all the details above for now and make a quick and simple
implementaion here */
char *s1;
char *s2;
s1=strrchr(path, '/');
s2=strrchr(path, '\\');
if(s1 && s2) {
path = (s1 > s2? s1 : s2)+1;
}
else if(s1)
path = s1 + 1;
else if(s2)
path = s2 + 1;
return path;
}
#endif
static char *strippath(const char *fullfile)
{
char *filename;
char *base;
filename = strdup(fullfile); /* duplicate since basename() may ruin the
buffer it works on */
if(!filename)
return NULL;
base = strdup(basename(filename));
free(filename); /* free temporary buffer */
return base; /* returns an allocated string or NULL ! */
}
static CURLcode formdata_add_filename(const struct curl_httppost *file,
struct FormData **form,
curl_off_t *size)
{
CURLcode result = CURLE_OK;
char *filename = file->showfilename;
char *filebasename = NULL;
char *filename_escaped = NULL;
if(!filename) {
filebasename = strippath(file->contents);
if(!filebasename)
return CURLE_OUT_OF_MEMORY;
filename = filebasename;
}
if(strchr(filename, '\\') || strchr(filename, '"')) {
char *p0, *p1;
/* filename need be escaped */
filename_escaped = malloc(strlen(filename)*2+1);
if(!filename_escaped) {
free(filebasename);
return CURLE_OUT_OF_MEMORY;
}
p0 = filename_escaped;
p1 = filename;
while(*p1) {
if(*p1 == '\\' || *p1 == '"')
*p0++ = '\\';
*p0++ = *p1++;
}
*p0 = '\0';
filename = filename_escaped;
}
result = AddFormDataf(form, size,
"; filename=\"%s\"",
filename);
free(filename_escaped);
free(filebasename);
return result;
}
/* /*
* Curl_getformdata() converts a linked list of "meta data" into a complete * Curl_getformdata() converts a linked list of "meta data" into a mime
* (possibly huge) multipart formdata. The input list is in 'post', while the * structure. The input list is in 'post', while the output is stored in
* output resulting linked lists gets stored in '*finalform'. *sizep will get * mime part at '*finalform'.
* the total size of the whole POST.
* A multipart/form_data content-type is built, unless a custom content-type
* is passed in 'custom_content_type'.
* *
* This function will not do a failf() for the potential memory failures but * This function will not do a failf() for the potential memory failures but
* should for all other errors it spots. Just note that this function MAY get * should for all other errors it spots. Just note that this function MAY get
@ -1150,422 +825,114 @@ static CURLcode formdata_add_filename(const struct curl_httppost *file,
*/ */
CURLcode Curl_getformdata(struct Curl_easy *data, CURLcode Curl_getformdata(struct Curl_easy *data,
struct FormData **finalform, struct Curl_mimepart *finalform,
struct curl_httppost *post, struct curl_httppost *post,
const char *custom_content_type, curl_read_callback fread_func)
curl_off_t *sizep)
{ {
struct FormData *form = NULL;
struct FormData *firstform;
struct curl_httppost *file;
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
curl_off_t size = 0; /* support potentially ENORMOUS formposts */ struct Curl_mime *form = NULL;
char fileboundary[42]; struct Curl_mime *multipart;
struct curl_slist *curList; struct Curl_mimepart *part;
char boundary[42]; struct curl_httppost *file;
*finalform = NULL; /* default form is empty */ Curl_mime_cleanpart(finalform); /* default form is empty */
if(!post) if(!post)
return result; /* no input => no output! */ return result; /* no input => no output! */
result = formboundary(data, boundary, sizeof(boundary)); form = curl_mime_init(data);
if(result) if(!form)
return result; result = CURLE_OUT_OF_MEMORY;
/* Make the first line of the output */
result = AddFormDataf(&form, NULL,
"%s; boundary=%s\r\n",
custom_content_type?custom_content_type:
"Content-Type: multipart/form-data",
boundary);
if(result) {
return result;
}
/* we DO NOT include that line in the total size of the POST, since it'll be
part of the header! */
firstform = form;
do {
if(size) {
result = AddFormDataf(&form, &size, "\r\n");
if(result)
break;
}
/* boundary */
result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
if(result)
break;
/* Maybe later this should be disabled when a custom_content_type is
passed, since Content-Disposition is not meaningful for all multipart
types.
*/
result = AddFormDataf(&form, &size,
"Content-Disposition: form-data; name=\"");
if(result)
break;
result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
&size);
if(result)
break;
result = AddFormDataf(&form, &size, "\"");
if(result)
break;
if(post->more) {
/* If used, this is a link to more file names, we must then do
the magic to include several files with the same field name */
result = formboundary(data, fileboundary, sizeof(fileboundary));
if(result) {
break;
}
result = AddFormDataf(&form, &size,
"\r\nContent-Type: multipart/mixed;"
" boundary=%s\r\n",
fileboundary);
if(result)
break;
}
file = post;
do {
/* If 'showfilename' is set, that is a faked name passed on to us
to use to in the formpost. If that is not set, the actually used
local file name should be added. */
if(post->more) {
/* if multiple-file */
result = AddFormDataf(&form, &size,
"\r\n--%s\r\nContent-Disposition: "
"attachment",
fileboundary);
if(result)
break;
result = formdata_add_filename(file, &form, &size);
if(result)
break;
}
else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
HTTPPOST_CALLBACK)) {
/* it should be noted that for the HTTPPOST_FILENAME and
HTTPPOST_CALLBACK cases the ->showfilename struct member is always
assigned at this point */
if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
result = formdata_add_filename(post, &form, &size);
}
if(result)
break;
}
if(file->contenttype) {
/* we have a specified type */
result = AddFormDataf(&form, &size,
"\r\nContent-Type: %s",
file->contenttype);
if(result)
break;
}
curList = file->contentheader;
while(curList) {
/* Process the additional headers specified for this form */
result = AddFormDataf(&form, &size, "\r\n%s", curList->data);
if(result)
break;
curList = curList->next;
}
if(result)
break;
result = AddFormDataf(&form, &size, "\r\n\r\n");
if(result)
break;
if((post->flags & HTTPPOST_FILENAME) ||
(post->flags & HTTPPOST_READFILE)) {
/* we should include the contents from the specified file */
FILE *fileread;
fileread = !strcmp("-", file->contents)?
stdin:fopen(file->contents, "rb"); /* binary read for win32 */
/*
* VMS: This only allows for stream files on VMS. Stream files are
* OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
* every record needs to have a \n appended & 1 added to SIZE
*/
if(fileread) {
if(fileread != stdin) {
/* close the file */
fclose(fileread);
/* add the file name only - for later reading from this */
result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
}
else {
/* When uploading from stdin, we can't know the size of the file,
* thus must read the full file as before. We *could* use chunked
* transfer-encoding, but that only works for HTTP 1.1 and we
* can't be sure we work with such a server.
*/
size_t nread;
char buffer[512];
while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
if(result || feof(fileread) || ferror(fileread))
break;
}
}
}
else {
if(data)
failf(data, "couldn't open file \"%s\"", file->contents);
*finalform = NULL;
result = CURLE_READ_ERROR;
}
}
else if(post->flags & HTTPPOST_BUFFER)
/* include contents of buffer */
result = AddFormData(&form, FORM_CONTENT, post->buffer,
post->bufferlength, &size);
else if(post->flags & HTTPPOST_CALLBACK)
/* the contents should be read with the callback and the size is set
with the contentslength */
result = AddFormData(&form, FORM_CALLBACK, post->userp,
post->flags&CURL_HTTPPOST_LARGE?
post->contentlen:post->contentslength, &size);
else
/* include the contents we got */
result = AddFormData(&form, FORM_CONTENT, post->contents,
post->flags&CURL_HTTPPOST_LARGE?
post->contentlen:post->contentslength, &size);
file = file->more;
} while(file && !result); /* for each specified file for this field */
if(result)
break;
if(post->more) {
/* this was a multiple-file inclusion, make a termination file
boundary: */
result = AddFormDataf(&form, &size,
"\r\n--%s--",
fileboundary);
if(result)
break;
}
post = post->next;
} while(post); /* for each field */
/* end-boundary for everything */
if(!result) if(!result)
result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary); result = curl_mime_subparts(finalform, form);
if(result) { /* Process each top part. */
Curl_formclean(&firstform); for(; !result && post; post = post->next) {
return result; /* If we have more than a file here, create a mime subpart and fill it. */
multipart = form;
if(post->more) {
part = curl_mime_addpart(form);
if(!part)
result = CURLE_OUT_OF_MEMORY;
if(!result)
result = curl_mime_name(part, post->name,
post->namelength? post->namelength: -1);
if(!result) {
multipart = curl_mime_init(data);
if(!multipart)
result = CURLE_OUT_OF_MEMORY;
}
if(!result)
result = curl_mime_subparts(part, multipart);
}
/* Generate all the part contents. */
for(file = post; !result && file; file = file->more) {
/* Create the part. */
part = curl_mime_addpart(multipart);
if(!part)
result = CURLE_OUT_OF_MEMORY;
/* Set the headers. */
if(!result)
result = curl_mime_headers(part, file->contentheader, 0);
/* Set the content type. */
if(!result &&file->contenttype)
result = curl_mime_type(part, file->contenttype);
/* Set field name. */
if(!result && !post->more)
result = curl_mime_name(part, post->name,
post->namelength? post->namelength: -1);
/* Process contents. */
if(!result) {
curl_off_t clen = post->contentslength;
if(post->flags & CURL_HTTPPOST_LARGE)
clen = post->contentlen;
if(!clen)
clen = -1;
if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) {
result = curl_mime_filedata(part, file->contents);
if(!result && (post->flags & HTTPPOST_READFILE))
result = curl_mime_filename(part, NULL);
}
else if(post->flags & HTTPPOST_BUFFER)
result = curl_mime_data(part, post->buffer,
post->bufferlength? post->bufferlength: -1);
else if(post->flags & HTTPPOST_CALLBACK)
/* the contents should be read with the callback and the size is set
with the contentslength */
result = curl_mime_data_cb(part, clen,
fread_func, NULL, NULL, post->userp);
else {
result = curl_mime_data(part, post->contents, (ssize_t) clen);
#ifdef CURL_DOES_CONVERSIONS
/* Convert textual contents now. */
if(!result && data && part->datasize)
result = Curl_convert_to_network(data, part->data, part->datasize);
#endif
}
}
/* Set fake file name. */
if(!result && post->showfilename)
if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER |
HTTPPOST_CALLBACK)))
result = curl_mime_filename(part, post->showfilename);
}
} }
*sizep = size; if(result)
*finalform = firstform; Curl_mime_cleanpart(finalform);
return result; return result;
} }
/*
* Curl_FormInit() inits the struct 'form' points to with the 'formdata'
* and resets the 'sent' counter.
*/
int Curl_FormInit(struct Form *form, struct FormData *formdata)
{
if(!formdata)
return 1; /* error */
form->data = formdata;
form->sent = 0;
form->fp = NULL;
form->fread_func = ZERO_NULL;
return 0;
}
#ifndef __VMS
# define fopen_read fopen
#else
/*
* vmsfopenread
*
* For upload to work as expected on VMS, different optional
* parameters must be added to the fopen command based on
* record format of the file.
*
*/
# define fopen_read vmsfopenread
static FILE * vmsfopenread(const char *file, const char *mode)
{
struct_stat statbuf;
int result;
result = stat(file, &statbuf);
switch(statbuf.st_fab_rfm) {
case FAB$C_VAR:
case FAB$C_VFC:
case FAB$C_STMCR:
return fopen(file, FOPEN_READTEXT); /* VMS */
break;
default:
return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
}
}
#endif
/*
* readfromfile()
*
* The read callback that this function may use can return a value larger than
* 'size' (which then this function returns) that indicates a problem and it
* must be properly dealt with
*/
static size_t readfromfile(struct Form *form, char *buffer,
size_t size)
{
size_t nread;
bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
if(callback) {
if(form->fread_func == ZERO_NULL)
return 0;
nread = form->fread_func(buffer, 1, size, form->data->line);
}
else {
if(!form->fp) {
/* this file hasn't yet been opened */
form->fp = fopen_read(form->data->line, "rb"); /* b is for binary */
if(!form->fp)
return (size_t)-1; /* failure */
}
nread = fread(buffer, 1, size, form->fp);
}
if(!nread) {
/* this is the last chunk from the file, move on */
if(form->fp) {
fclose(form->fp);
form->fp = NULL;
}
form->data = form->data->next;
}
return nread;
}
/*
* Curl_FormReader() is the fread() emulation function that will be used to
* deliver the formdata to the transfer loop and then sent away to the peer.
*/
size_t Curl_FormReader(char *buffer,
size_t size,
size_t nitems,
FILE *mydata)
{
struct Form *form;
size_t wantedsize;
size_t gotsize = 0;
form=(struct Form *)mydata;
wantedsize = size * nitems;
if(!form->data)
return 0; /* nothing, error, empty */
if((form->data->type == FORM_FILE) ||
(form->data->type == FORM_CALLBACK)) {
gotsize = readfromfile(form, buffer, wantedsize);
if(gotsize)
/* If positive or -1, return. If zero, continue! */
return gotsize;
}
do {
if((form->data->length - form->sent) > wantedsize - gotsize) {
memcpy(buffer + gotsize, form->data->line + form->sent,
wantedsize - gotsize);
form->sent += wantedsize-gotsize;
return wantedsize;
}
memcpy(buffer+gotsize,
form->data->line + form->sent,
(form->data->length - form->sent) );
gotsize += form->data->length - form->sent;
form->sent = 0;
form->data = form->data->next; /* advance */
} while(form->data && (form->data->type < FORM_CALLBACK));
/* If we got an empty line and we have more data, we proceed to the next
line immediately to avoid returning zero before we've reached the end. */
return gotsize;
}
/*
* Curl_formpostheader() returns the first line of the formpost, the
* request-header part (which is not part of the request-body like the rest of
* the post).
*/
char *Curl_formpostheader(void *formp, size_t *len)
{
char *header;
struct Form *form=(struct Form *)formp;
if(!form->data)
return NULL; /* nothing, ERROR! */
header = form->data->line;
*len = form->data->length;
form->data = form->data->next; /* advance */
return header;
}
/*
* formboundary() creates a suitable boundary string and returns an allocated
* one.
*/
static CURLcode formboundary(struct Curl_easy *data,
char *buffer, size_t buflen)
{
/* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615)
combinations */
if(buflen < 41)
return CURLE_BAD_FUNCTION_ARGUMENT;
memset(buffer, '-', 24);
Curl_rand_hex(data, (unsigned char *)&buffer[24], 17);
return CURLE_OK;
}
#else /* CURL_DISABLE_HTTP */ #else /* CURL_DISABLE_HTTP */
CURLFORMcode curl_formadd(struct curl_httppost **httppost, CURLFORMcode curl_formadd(struct curl_httppost **httppost,
struct curl_httppost **last_post, struct curl_httppost **last_post,

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -22,32 +22,6 @@
* *
***************************************************************************/ ***************************************************************************/
enum formtype {
FORM_DATAMEM, /* already allocated FORM_DATA memory */
FORM_DATA, /* form metadata (convert to network encoding if necessary) */
FORM_CONTENT, /* form content (never convert) */
FORM_CALLBACK, /* 'line' points to the custom pointer we pass to the callback
*/
FORM_FILE /* 'line' points to a file name we should read from
to create the form data (never convert) */
};
/* plain and simple linked list with lines to send */
struct FormData {
struct FormData *next;
enum formtype type;
char *line;
size_t length;
};
struct Form {
struct FormData *data; /* current form line to send */
size_t sent; /* number of bytes of the current line that has
already been sent in a previous invoke */
FILE *fp; /* file to read from */
curl_read_callback fread_func; /* fread callback pointer */
};
/* used by FormAdd for temporary storage */ /* used by FormAdd for temporary storage */
typedef struct FormInfo { typedef struct FormInfo {
char *name; char *name;
@ -69,31 +43,9 @@ typedef struct FormInfo {
struct FormInfo *more; struct FormInfo *more;
} FormInfo; } FormInfo;
int Curl_FormInit(struct Form *form, struct FormData *formdata);
CURLcode Curl_getformdata(struct Curl_easy *data, CURLcode Curl_getformdata(struct Curl_easy *data,
struct FormData **, struct Curl_mimepart *,
struct curl_httppost *post, struct curl_httppost *post,
const char *custom_contenttype, curl_read_callback fread_func);
curl_off_t *size);
/* fread() emulation */
size_t Curl_FormReader(char *buffer,
size_t size,
size_t nitems,
FILE *mydata);
/*
* Curl_formpostheader() returns the first line of the formpost, the
* request-header part (which is not part of the request-body like the rest of
* the post).
*/
char *Curl_formpostheader(void *formp, size_t *len);
char *Curl_FormBoundary(void);
void Curl_formclean(struct FormData **);
CURLcode Curl_formconvert(struct Curl_easy *, struct FormData *);
#endif /* HEADER_CURL_FORMDATA_H */ #endif /* HEADER_CURL_FORMDATA_H */

View File

@ -50,6 +50,7 @@
#include "transfer.h" #include "transfer.h"
#include "sendf.h" #include "sendf.h"
#include "formdata.h" #include "formdata.h"
#include "mime.h"
#include "progress.h" #include "progress.h"
#include "curl_base64.h" #include "curl_base64.h"
#include "cookie.h" #include "cookie.h"
@ -162,6 +163,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
if(!http) if(!http)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
Curl_mime_initpart(&http->form, conn->data);
conn->data->req.protop = http; conn->data->req.protop = http;
Curl_http2_setup_conn(conn); Curl_http2_setup_conn(conn);
@ -427,6 +429,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
expectsend = data->state.infilesize; expectsend = data->state.infilesize;
break; break;
case HTTPREQ_POST_FORM: case HTTPREQ_POST_FORM:
case HTTPREQ_POST_MIME:
expectsend = http->postsize; expectsend = http->postsize;
break; break;
default: default:
@ -1470,18 +1473,17 @@ CURLcode Curl_http_done(struct connectdata *conn,
Curl_http2_done(conn, premature); Curl_http2_done(conn, premature);
if(HTTPREQ_POST_FORM == data->set.httpreq) { Curl_mime_cleanpart(&http->form);
data->req.bytecount = http->readbytecount + http->writebytecount;
Curl_formclean(&http->sendit); /* Now free that whole lot */ switch(data->set.httpreq) {
if(http->form.fp) { case HTTPREQ_PUT:
/* a file being uploaded was left opened, close it! */ case HTTPREQ_POST_FORM:
fclose(http->form.fp); case HTTPREQ_POST_MIME:
http->form.fp = NULL;
}
}
else if(HTTPREQ_PUT == data->set.httpreq)
data->req.bytecount = http->readbytecount + http->writebytecount; data->req.bytecount = http->readbytecount + http->writebytecount;
break;
default:
break;
}
if(status) if(status)
return status; return status;
@ -1637,6 +1639,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
/* this header (extended by formdata.c) is sent later */ /* this header (extended by formdata.c) is sent later */
checkprefix("Content-Type:", headers->data)) checkprefix("Content-Type:", headers->data))
; ;
else if(data->set.httpreq == HTTPREQ_POST_MIME &&
/* this header is sent later */
checkprefix("Content-Type:", headers->data))
;
else if(conn->bits.authneg && else if(conn->bits.authneg &&
/* while doing auth neg, don't allow the custom length since /* while doing auth neg, don't allow the custom length since
we will force length zero then */ we will force length zero then */
@ -1775,7 +1781,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
const char *httpstring; const char *httpstring;
Curl_send_buffer *req_buffer; Curl_send_buffer *req_buffer;
curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */ curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
int seekerr = CURL_SEEKFUNC_OK; int seekerr = CURL_SEEKFUNC_CANTSEEK;
/* Always consider the DO phase done after this function call, even if there /* Always consider the DO phase done after this function call, even if there
may be parts of the request that is not yet sent, since we can deal with may be parts of the request that is not yet sent, since we can deal with
@ -1848,6 +1854,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
switch(httpreq) { switch(httpreq) {
case HTTPREQ_POST: case HTTPREQ_POST:
case HTTPREQ_POST_FORM: case HTTPREQ_POST_FORM:
case HTTPREQ_POST_MIME:
request = "POST"; request = "POST";
break; break;
case HTTPREQ_PUT: case HTTPREQ_PUT:
@ -1942,6 +1949,48 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
} }
#endif #endif
switch(httpreq) {
case HTTPREQ_POST_MIME:
http->sendit = &data->set.mimepost;
break;
case HTTPREQ_POST_FORM:
/* Convert the form structure into a mime structure. */
Curl_mime_cleanpart(&http->form);
result = Curl_getformdata(data, &http->form, data->set.httppost,
data->state.fread_func);
if(result)
return result;
http->sendit = &http->form;
break;
default:
http->sendit = NULL;
}
if(http->sendit) {
const char *cthdr = Curl_checkheaders(conn, "Content-Type:");
/* Read and seek body only. */
http->sendit->flags |= MIME_BODY_ONLY;
/* Prepare the mime structure headers & set content type. */
if(cthdr)
for(cthdr += 13; *cthdr == ' '; cthdr++)
;
else if(http->sendit->kind == MIMEKIND_MULTIPART)
cthdr = "multipart/form-data";
curl_mime_headers(http->sendit, data->set.headers, 0);
result = Curl_mime_prepare_headers(http->sendit, cthdr,
NULL, MIMESTRATEGY_FORM);
curl_mime_headers(http->sendit, NULL, 0);
if(!result)
result = Curl_mime_rewind(http->sendit);
if(result)
return result;
http->postsize = Curl_mime_size(http->sendit);
}
ptr = Curl_checkheaders(conn, "Transfer-Encoding:"); ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
if(ptr) { if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */ /* Some kind of TE is requested, check if 'chunked' is chosen */
@ -1949,9 +1998,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
Curl_compareheader(ptr, "Transfer-Encoding:", "chunked"); Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
} }
else { else {
if((conn->handler->protocol&PROTO_FAMILY_HTTP) && if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
data->set.upload && (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
(data->state.infilesize == -1)) { http->postsize < 0) ||
(data->set.upload && data->state.infilesize == -1))) {
if(conn->bits.authneg) if(conn->bits.authneg)
/* don't enable chunked during auth neg */ /* don't enable chunked during auth neg */
; ;
@ -2123,21 +2173,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
} }
#endif /* CURL_DISABLE_PROXY */ #endif /* CURL_DISABLE_PROXY */
if(HTTPREQ_POST_FORM == httpreq) {
/* we must build the whole post sequence first, so that we have a size of
the whole transfer before we start to send it */
result = Curl_getformdata(data, &http->sendit, data->set.httppost,
Curl_checkheaders(conn, "Content-Type:"),
&http->postsize);
if(result)
return result;
}
http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n"; http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n";
if(( (HTTPREQ_POST == httpreq) || if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
(HTTPREQ_POST_FORM == httpreq) ||
(HTTPREQ_PUT == httpreq) ) &&
data->state.resume_from) { data->state.resume_from) {
/********************************************************************** /**********************************************************************
* Resuming upload in HTTP means that we PUT or POST and that we have * Resuming upload in HTTP means that we PUT or POST and that we have
@ -2145,6 +2183,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
* a Range: header that will be passed along. We need to "fast forward" * a Range: header that will be passed along. We need to "fast forward"
* the file the given number of bytes and decrease the assume upload * the file the given number of bytes and decrease the assume upload
* file size before we continue this venture in the dark lands of HTTP. * file size before we continue this venture in the dark lands of HTTP.
* Resuming mime/form posting at an offset > 0 has no sense and is ignored.
*********************************************************************/ *********************************************************************/
if(data->state.resume_from < 0) { if(data->state.resume_from < 0) {
@ -2219,7 +2258,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
data->state.range); data->state.range);
} }
else if((httpreq != HTTPREQ_GET) && else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
!Curl_checkheaders(conn, "Content-Range:")) { !Curl_checkheaders(conn, "Content-Range:")) {
/* if a line like this was already allocated, free the previous one */ /* if a line like this was already allocated, free the previous one */
@ -2415,107 +2454,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
switch(httpreq) { switch(httpreq) {
case HTTPREQ_POST_FORM:
if(!http->sendit || conn->bits.authneg) {
/* nothing to post! */
result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
if(result)
return result;
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
-1, NULL);
break;
}
if(Curl_FormInit(&http->form, http->sendit)) {
failf(data, "Internal HTTP POST error!");
return CURLE_HTTP_POST_ERROR;
}
/* Get the currently set callback function pointer and store that in the
form struct since we might want the actual user-provided callback later
on. The data->set.fread_func pointer itself will be changed for the
multipart case to the function that returns a multipart formatted
stream. */
http->form.fread_func = data->state.fread_func;
/* Set the read function to read from the generated form data */
data->state.fread_func = (curl_read_callback)Curl_FormReader;
data->state.in = &http->form;
http->sending = HTTPSEND_BODY;
if(!data->req.upload_chunky &&
!Curl_checkheaders(conn, "Content-Length:")) {
/* only add Content-Length if not uploading chunked */
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
"\r\n", http->postsize);
if(result)
return result;
}
result = expect100(data, conn, req_buffer);
if(result)
return result;
{
/* Get Content-Type: line from Curl_formpostheader.
*/
char *contentType;
size_t linelength=0;
contentType = Curl_formpostheader((void *)&http->form,
&linelength);
if(!contentType) {
failf(data, "Could not get Content-Type header line!");
return CURLE_HTTP_POST_ERROR;
}
result = Curl_add_buffer(req_buffer, contentType, linelength);
if(result)
return result;
}
/* make the request end in a true CRLF */
result = Curl_add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
/* set upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
/* fire away the whole request to the server */
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount, FIRSTSOCKET,
&http->writebytecount);
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
/* convert the form data */
result = Curl_convert_form(data, http->sendit);
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
break;
case HTTPREQ_PUT: /* Let's PUT the data to the server! */ case HTTPREQ_PUT: /* Let's PUT the data to the server! */
if(conn->bits.authneg) if(conn->bits.authneg)
@ -2560,6 +2498,98 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return result; return result;
break; break;
case HTTPREQ_POST_FORM:
case HTTPREQ_POST_MIME:
/* This is form posting using mime data. */
if(conn->bits.authneg) {
/* nothing to post! */
result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
if(result)
return result;
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
-1, NULL);
break;
}
postsize = http->postsize;
/* We only set Content-Length and allow a custom Content-Length if
we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
if(postsize != -1 && !data->req.upload_chunky &&
(conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
/* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
"\r\n", postsize);
if(result)
return result;
}
/* Output mime-generated headers. */
{
struct curl_slist *hdr;
for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
result = Curl_add_bufferf(req_buffer, "%s\r\n", hdr->data);
if(result)
return result;
}
}
/* For really small posts we don't use Expect: headers at all, and for
the somewhat bigger ones we allow the app to disable it. Just make
sure that the expect100header is always set to the preferred value
here. */
ptr = Curl_checkheaders(conn, "Expect:");
if(ptr) {
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
}
else if(postsize > EXPECT_100_THRESHOLD || postsize < 0) {
result = expect100(data, conn, req_buffer);
if(result)
return result;
}
else
data->state.expect100header = FALSE;
/* make the request end in a true CRLF */
result = Curl_add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize);
/* Read from mime structure. */
data->state.fread_func = (curl_read_callback) Curl_mime_read;
data->state.in = (void *) http->sendit;
http->sending = HTTPSEND_BODY;
/* this sends the buffer and frees all the buffer resources */
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* prepare for transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount, postsize?FIRSTSOCKET:-1,
postsize?&http->writebytecount:NULL);
if(result)
return result;
break;
case HTTPREQ_POST: case HTTPREQ_POST:
/* this is the simple POST, using x-www-form-urlencoded style */ /* this is the simple POST, using x-www-form-urlencoded style */
@ -3171,6 +3201,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
case HTTPREQ_PUT: case HTTPREQ_PUT:
case HTTPREQ_POST: case HTTPREQ_POST:
case HTTPREQ_POST_FORM: case HTTPREQ_POST_FORM:
case HTTPREQ_POST_MIME:
/* We got an error response. If this happened before the whole /* We got an error response. If this happened before the whole
* request body has been sent we stop sending and mark the * request body has been sent we stop sending and mark the
* connection for closure after we've read the entire response. * connection for closure after we've read the entire response.

View File

@ -130,7 +130,7 @@ CURLcode Curl_http_perhapsrewind(struct connectdata *conn);
* HTTP unique setup * HTTP unique setup
***************************************************************************/ ***************************************************************************/
struct HTTP { struct HTTP {
struct FormData *sendit; struct Curl_mimepart *sendit;
curl_off_t postsize; /* off_t to handle large file sizes */ curl_off_t postsize; /* off_t to handle large file sizes */
const char *postdata; const char *postdata;
@ -140,7 +140,7 @@ struct HTTP {
curl_off_t writebytecount; curl_off_t writebytecount;
/* For FORM posting */ /* For FORM posting */
struct Form form; struct Curl_mimepart form;
struct back { struct back {
curl_read_callback fread_func; /* backup storage for fread pointer */ curl_read_callback fread_func; /* backup storage for fread pointer */

View File

@ -1955,6 +1955,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
switch(conn->data->set.httpreq) { switch(conn->data->set.httpreq) {
case HTTPREQ_POST: case HTTPREQ_POST:
case HTTPREQ_POST_FORM: case HTTPREQ_POST_FORM:
case HTTPREQ_POST_MIME:
case HTTPREQ_PUT: case HTTPREQ_PUT:
if(conn->data->state.infilesize != -1) if(conn->data->state.infilesize != -1)
stream->upload_left = conn->data->state.infilesize; stream->upload_left = conn->data->state.infilesize;

View File

@ -68,6 +68,7 @@
#include "http.h" /* for HTTP proxy tunnel stuff */ #include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h" #include "socks.h"
#include "imap.h" #include "imap.h"
#include "mime.h"
#include "strtoofft.h" #include "strtoofft.h"
#include "strcase.h" #include "strcase.h"
#include "vtls/vtls.h" #include "vtls/vtls.h"
@ -708,18 +709,48 @@ static CURLcode imap_perform_fetch(struct connectdata *conn)
static CURLcode imap_perform_append(struct connectdata *conn) static CURLcode imap_perform_append(struct connectdata *conn)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
struct IMAP *imap = conn->data->req.protop; struct Curl_easy *data = conn->data;
struct IMAP *imap = data->req.protop;
char *mailbox; char *mailbox;
/* Check we have a mailbox */ /* Check we have a mailbox */
if(!imap->mailbox) { if(!imap->mailbox) {
failf(conn->data, "Cannot APPEND without a mailbox."); failf(data, "Cannot APPEND without a mailbox.");
return CURLE_URL_MALFORMAT; return CURLE_URL_MALFORMAT;
} }
/* Prepare the mime data if some. */
if(data->set.mimepost.kind != MIMEKIND_NONE) {
/* Use the whole structure as data. */
data->set.mimepost.flags &= ~MIME_BODY_ONLY;
/* Add external headers and mime version. */
curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
NULL, MIMESTRATEGY_MAIL);
if(!result)
if(!Curl_checkheaders(conn, "Mime-Version"))
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);
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;
}
/* Check we know the size of the upload */ /* Check we know the size of the upload */
if(conn->data->state.infilesize < 0) { if(data->state.infilesize < 0) {
failf(conn->data, "Cannot APPEND with unknown input file size\n"); failf(data, "Cannot APPEND with unknown input file size\n");
return CURLE_UPLOAD_FAILED; return CURLE_UPLOAD_FAILED;
} }
@ -730,7 +761,7 @@ static CURLcode imap_perform_append(struct connectdata *conn)
/* Send the APPEND command */ /* Send the APPEND command */
result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
mailbox, conn->data->state.infilesize); mailbox, data->state.infilesize);
free(mailbox); free(mailbox);
@ -1423,9 +1454,10 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
result = status; /* use the already set error code */ result = status; /* use the already set error code */
} }
else if(!data->set.connect_only && !imap->custom && else if(!data->set.connect_only && !imap->custom &&
(imap->uid || data->set.upload)) { (imap->uid || data->set.upload ||
data->set.mimepost.kind != MIMEKIND_NONE)) {
/* Handle responses after FETCH or APPEND transfer has finished */ /* Handle responses after FETCH or APPEND transfer has finished */
if(!data->set.upload) if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
state(conn, IMAP_FETCH_FINAL); state(conn, IMAP_FETCH_FINAL);
else { else {
/* End the APPEND command first by sending an empty line */ /* End the APPEND command first by sending an empty line */
@ -1495,7 +1527,7 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected,
selected = TRUE; selected = TRUE;
/* Start the first command in the DO phase */ /* Start the first command in the DO phase */
if(conn->data->set.upload) if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
/* APPEND can be executed directly */ /* APPEND can be executed directly */
result = imap_perform_append(conn); result = imap_perform_append(conn);
else if(imap->custom && (selected || !imap->mailbox)) else if(imap->custom && (selected || !imap->mailbox))

1497
lib/mime.c Normal file

File diff suppressed because it is too large Load Diff

119
lib/mime.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef HEADER_CURL_MIME_H
#define HEADER_CURL_MIME_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#define MIME_RAND_BOUNDARY_CHARS 16 /* Nb. of random boundary chars. */
/* Part flags. */
#define MIME_USERHEADERS_OWNER (1 << 0)
#define MIME_BODY_ONLY (1 << 1)
/* Part source kinds. */
enum mimekind {
MIMEKIND_NONE = 0, /* Part not set. */
MIMEKIND_DATA, /* Allocated mime data. */
MIMEKIND_NAMEDFILE, /* Data from named file. */
MIMEKIND_FILE, /* Data from file pointer. */
MIMEKIND_CALLBACK, /* Data from `read' callback. */
MIMEKIND_MULTIPART, /* Data is a mime subpart. */
MIMEKIND_LAST
};
/* Readback state tokens. */
enum mimestate {
MIMESTATE_BEGIN, /* Readback has not yet started. */
MIMESTATE_CURLHEADERS, /* In curl-generated headers. */
MIMESTATE_USERHEADERS, /* In caller's supplied headers. */
MIMESTATE_EOH, /* End of headers. */
MIMESTATE_BODY, /* Placeholder. */
MIMESTATE_BOUNDARY1, /* In boundary prefix. */
MIMESTATE_BOUNDARY2, /* In boundary. */
MIMESTATE_CONTENT, /* In content. */
MIMESTATE_END, /* End of part reached. */
MIMESTATE_LAST
};
/* Mime headers strategies. */
enum mimestrategy {
MIMESTRATEGY_MAIL, /* Mime mail. */
MIMESTRATEGY_FORM, /* HTTP post form. */
MIMESTRATEGY_LAST
};
/* Mime readback state. */
struct mime_state {
enum mimestate state; /* Current state token. */
void * ptr; /* State-dependent pointer. */
size_t offset; /* State-dependent offset. */
};
/* A mime context. */
struct Curl_mime {
struct Curl_easy * easy; /* The associated easy handle. */
struct Curl_mimepart * parent; /* Parent part. */
struct Curl_mimepart * firstpart; /* First part. */
struct Curl_mimepart * lastpart; /* Last part. */
char * boundary; /* The part boundary. */
struct mime_state state; /* Current readback state. */
};
/* A mime part. */
struct Curl_mimepart {
struct Curl_easy * easy; /* The associated easy handle. */
struct Curl_mime * parent; /* Parent mime structure. */
struct Curl_mimepart * nextpart; /* Forward linked list. */
enum mimekind kind; /* The part kind. */
char * data; /* Memory data or file name. */
curl_read_callback readfunc; /* Read function. */
curl_seek_callback seekfunc; /* Seek function. */
curl_free_callback freefunc; /* Argument free function. */
void * arg; /* Argument to callback functions. */
FILE * namedfp; /* Named file pointer. */
struct curl_slist * curlheaders; /* Part headers. */
struct curl_slist * userheaders; /* Part headers. */
char * mimetype; /* Part mime type. */
char * filename; /* Remote file name. */
char * name; /* Data name. */
size_t namesize; /* Data name size. */
curl_off_t origin; /* Origin file offset. */
curl_off_t datasize; /* Expected data size. */
unsigned int flags; /* Flags. */
struct mime_state state; /* Current readback state. */
};
/* Prototypes. */
void Curl_mime_initpart(struct Curl_mimepart *part, struct Curl_easy *easy);
void Curl_mime_cleanpart(struct Curl_mimepart *part);
CURLcode Curl_mime_prepare_headers(struct Curl_mimepart *part,
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);
CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
CURLcode Curl_mime_file(curl_mimepart *part, FILE *fp, int closewhendone);
#endif /* HEADER_CURL_MIME_H */

View File

@ -319,29 +319,4 @@ void Curl_convert_close(struct Curl_easy *data)
#endif /* HAVE_ICONV */ #endif /* HAVE_ICONV */
} }
/*
* Curl_convert_form() is used from http.c, this converts any form items that
need to be sent in the network encoding. Returns CURLE_OK on success.
*/
CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form)
{
CURLcode result;
if(!data)
return CURLE_BAD_FUNCTION_ARGUMENT;
while(form) {
if(form->type == FORM_DATA) {
result = Curl_convert_to_network(data, form->line, form->length);
/* Curl_convert_to_network calls failf if unsuccessful */
if(result)
return result;
}
form = form->next;
}
return CURLE_OK;
}
#endif /* CURL_DOES_CONVERSIONS */ #endif /* CURL_DOES_CONVERSIONS */

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -48,7 +48,6 @@ CURLcode Curl_convert_from_network(struct Curl_easy *data,
char *buffer, size_t length); char *buffer, size_t length);
CURLcode Curl_convert_from_utf8(struct Curl_easy *data, CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
char *buffer, size_t length); char *buffer, size_t length);
CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form);
#else #else
#define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK) #define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK)
#define Curl_convert_init(x) Curl_nop_stmt #define Curl_convert_init(x) Curl_nop_stmt
@ -57,7 +56,6 @@ CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form);
#define Curl_convert_to_network(a,b,c) ((void)a, CURLE_OK) #define Curl_convert_to_network(a,b,c) ((void)a, CURLE_OK)
#define Curl_convert_from_network(a,b,c) ((void)a, CURLE_OK) #define Curl_convert_from_network(a,b,c) ((void)a, CURLE_OK)
#define Curl_convert_from_utf8(a,b,c) ((void)a, CURLE_OK) #define Curl_convert_from_utf8(a,b,c) ((void)a, CURLE_OK)
#define Curl_convert_form(a,b) CURLE_OK
#endif #endif
#endif /* HEADER_CURL_NON_ASCII_H */ #endif /* HEADER_CURL_NON_ASCII_H */

View File

@ -67,6 +67,7 @@
#include "transfer.h" #include "transfer.h"
#include "escape.h" #include "escape.h"
#include "http.h" /* for HTTP proxy tunnel stuff */ #include "http.h" /* for HTTP proxy tunnel stuff */
#include "mime.h"
#include "socks.h" #include "socks.h"
#include "smtp.h" #include "smtp.h"
#include "strtoofft.h" #include "strtoofft.h"
@ -530,8 +531,37 @@ static CURLcode smtp_perform_mail(struct connectdata *conn)
} }
} }
/* Prepare the mime data if some. */
if(data->set.mimepost.kind != MIMEKIND_NONE) {
/* Use the whole structure as data. */
data->set.mimepost.flags &= ~MIME_BODY_ONLY;
/* Add external headers and mime version. */
curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
NULL, MIMESTRATEGY_MAIL);
if(!result)
if(!Curl_checkheaders(conn, "Mime-Version"))
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);
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;
}
/* Calculate the optional SIZE parameter */ /* Calculate the optional SIZE parameter */
if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) { if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
if(!size) { if(!size) {
@ -1159,7 +1189,8 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
connclose(conn, "SMTP done with bad status"); /* marked for closure */ connclose(conn, "SMTP done with bad status"); /* marked for closure */
result = status; /* use the already set error code */ result = status; /* use the already set error code */
} }
else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) { else if(!data->set.connect_only && data->set.mail_rcpt &&
(data->set.upload || data->set.mimepost.kind)) {
/* Calculate the EOB taking into account any terminating CRLF from the /* Calculate the EOB taking into account any terminating CRLF from the
previous line of the email or the CRLF of the DATA command when there previous line of the email or the CRLF of the DATA command when there
is "no mail data". RFC-5321, sect. 4.1.1.4. is "no mail data". RFC-5321, sect. 4.1.1.4.
@ -1249,7 +1280,7 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
smtp->rcpt = data->set.mail_rcpt; smtp->rcpt = data->set.mail_rcpt;
/* Start the first command in the DO phase */ /* Start the first command in the DO phase */
if(data->set.upload && data->set.mail_rcpt) if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
/* MAIL transfer */ /* MAIL transfer */
result = smtp_perform_mail(conn); result = smtp_perform_mail(conn);
else else

View File

@ -73,6 +73,7 @@
#include "connect.h" #include "connect.h"
#include "non-ascii.h" #include "non-ascii.h"
#include "http2.h" #include "http2.h"
#include "mime.h"
/* The last 3 #include files should be in this order */ /* The last 3 #include files should be in this order */
#include "curl_printf.h" #include "curl_printf.h"
@ -241,6 +242,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
CURLcode Curl_readrewind(struct connectdata *conn) CURLcode Curl_readrewind(struct connectdata *conn)
{ {
struct Curl_easy *data = conn->data; struct Curl_easy *data = conn->data;
struct Curl_mimepart *mimepart = &data->set.mimepost;
conn->bits.rewindaftersend = FALSE; /* we rewind now */ conn->bits.rewindaftersend = FALSE; /* we rewind now */
@ -253,9 +255,21 @@ CURLcode Curl_readrewind(struct connectdata *conn)
/* We have sent away data. If not using CURLOPT_POSTFIELDS or /* We have sent away data. If not using CURLOPT_POSTFIELDS or
CURLOPT_HTTPPOST, call app to rewind CURLOPT_HTTPPOST, call app to rewind
*/ */
if(data->set.postfields || if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
(data->set.httpreq == HTTPREQ_POST_FORM)) struct HTTP *http = data->req.protop;
if(http->sendit)
mimepart = http->sendit;
}
if(data->set.postfields)
; /* do nothing */ ; /* do nothing */
else if(data->set.httpreq == HTTPREQ_POST_MIME ||
data->set.httpreq == HTTPREQ_POST_FORM) {
if(Curl_mime_rewind(mimepart)) {
failf(data, "Cannot rewind mime/post data");
return CURLE_SEND_FAIL_REWIND;
}
}
else { else {
if(data->set.seek_func) { if(data->set.seek_func) {
int err; int err;
@ -1826,7 +1840,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
* can be overridden with CURLOPT_POSTREDIR. * can be overridden with CURLOPT_POSTREDIR.
*/ */
if((data->set.httpreq == HTTPREQ_POST if((data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM) || data->set.httpreq == HTTPREQ_POST_FORM
|| data->set.httpreq == HTTPREQ_POST_MIME)
&& !(data->set.keep_post & CURL_REDIR_POST_301)) { && !(data->set.keep_post & CURL_REDIR_POST_301)) {
infof(data, "Switch from POST to GET\n"); infof(data, "Switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET; data->set.httpreq = HTTPREQ_GET;
@ -1850,7 +1865,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
* can be overridden with CURLOPT_POSTREDIR. * can be overridden with CURLOPT_POSTREDIR.
*/ */
if((data->set.httpreq == HTTPREQ_POST if((data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM) || data->set.httpreq == HTTPREQ_POST_FORM
|| data->set.httpreq == HTTPREQ_POST_MIME)
&& !(data->set.keep_post & CURL_REDIR_POST_302)) { && !(data->set.keep_post & CURL_REDIR_POST_302)) {
infof(data, "Switch from POST to GET\n"); infof(data, "Switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET; data->set.httpreq = HTTPREQ_GET;

View File

@ -71,6 +71,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "netrc.h" #include "netrc.h"
#include "formdata.h" #include "formdata.h"
#include "mime.h"
#include "vtls/vtls.h" #include "vtls/vtls.h"
#include "hostip.h" #include "hostip.h"
#include "transfer.h" #include "transfer.h"
@ -479,6 +480,8 @@ CURLcode Curl_close(struct Curl_easy *data)
Curl_http2_cleanup_dependencies(data); Curl_http2_cleanup_dependencies(data);
Curl_convert_close(data); Curl_convert_close(data);
Curl_mime_cleanpart(&data->set.mimepost);
/* No longer a dirty share, if it exists */ /* No longer a dirty share, if it exists */
if(data->share) { if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
@ -668,6 +671,8 @@ CURLcode Curl_open(struct Curl_easy **curl)
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
} }
Curl_mime_initpart(&data->set.mimepost, data);
data->state.headerbuff = malloc(HEADERSIZE); data->state.headerbuff = malloc(HEADERSIZE);
if(!data->state.headerbuff) { if(!data->state.headerbuff) {
DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n"));
@ -1148,6 +1153,19 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
data->set.opt_no_body = FALSE; /* this is implied */ data->set.opt_no_body = FALSE; /* this is implied */
break; break;
case CURLOPT_MIMEPOST:
/*
* Set to make us do MIME/form POST
*/
result = curl_mime_subparts(&data->set.mimepost,
va_arg(param, curl_mime *));
if(!result) {
data->set.mimepost.freefunc = NULL; /* Avoid free upon easy cleanup. */
data->set.httpreq = HTTPREQ_POST_MIME;
data->set.opt_no_body = FALSE; /* this is implied */
}
break;
case CURLOPT_REFERER: case CURLOPT_REFERER:
/* /*
* String to set in the HTTP Referer: field. * String to set in the HTTP Referer: field.
@ -1875,13 +1893,13 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
break; break;
case CURLOPT_RESUME_FROM: case CURLOPT_RESUME_FROM:
/* /*
* Resume transfer at the give file position * Resume transfer at the given file position
*/ */
data->set.set_resume_from = va_arg(param, long); data->set.set_resume_from = va_arg(param, long);
break; break;
case CURLOPT_RESUME_FROM_LARGE: case CURLOPT_RESUME_FROM_LARGE:
/* /*
* Resume transfer at the give file position * Resume transfer at the given file position
*/ */
data->set.set_resume_from = va_arg(param, curl_off_t); data->set.set_resume_from = va_arg(param, curl_off_t);
break; break;

View File

@ -103,6 +103,7 @@
#include "hash.h" #include "hash.h"
#include "splay.h" #include "splay.h"
#include "mime.h"
#include "imap.h" #include "imap.h"
#include "pop3.h" #include "pop3.h"
#include "smtp.h" #include "smtp.h"
@ -1140,6 +1141,7 @@ typedef enum {
HTTPREQ_GET, HTTPREQ_GET,
HTTPREQ_POST, HTTPREQ_POST,
HTTPREQ_POST_FORM, /* we make a difference internally */ HTTPREQ_POST_FORM, /* we make a difference internally */
HTTPREQ_POST_MIME, /* we make a difference internally */
HTTPREQ_PUT, HTTPREQ_PUT,
HTTPREQ_HEAD, HTTPREQ_HEAD,
HTTPREQ_OPTIONS, HTTPREQ_OPTIONS,
@ -1557,7 +1559,8 @@ struct UserDefined {
curl_off_t set_resume_from; /* continue [ftp] transfer from here */ curl_off_t set_resume_from; /* continue [ftp] transfer from here */
struct curl_slist *headers; /* linked list of extra headers */ struct curl_slist *headers; /* linked list of extra headers */
struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
struct curl_httppost *httppost; /* linked list of POST data */ struct curl_httppost *httppost; /* linked list of old POST data */
struct Curl_mimepart mimepost; /* MIME/POST data. */
bool sep_headers; /* handle host and proxy headers separately */ bool sep_headers; /* handle host and proxy headers separately */
bool cookiesession; /* new cookie session? */ bool cookiesession; /* new cookie session? */
bool crlf; /* convert crlf on ftp upload(?) */ bool crlf; /* convert crlf on ftp upload(?) */

View File

@ -146,6 +146,7 @@ int test(char *URL)
char *stringpointerextra=(char *)"moooo"; char *stringpointerextra=(char *)"moooo";
struct curl_slist *slist=NULL; struct curl_slist *slist=NULL;
struct curl_httppost *httppost=NULL; struct curl_httppost *httppost=NULL;
curl_mime *mimepost = NULL;
FILE *stream = stderr; FILE *stream = stderr;
struct data object; struct data object;
char *charp; char *charp;
@ -216,6 +217,9 @@ while(<STDIN>) {
elsif($name eq "HTTPPOST") { elsif($name eq "HTTPPOST") {
print "${pref} httppost);\n$check"; print "${pref} httppost);\n$check";
} }
elsif($name eq "MIMEPOST") {
print "${pref} mimepost);\n$check";
}
elsif($name eq "STDERR") { elsif($name eq "STDERR") {
print "${pref} stream);\n$check"; print "${pref} stream);\n$check";
} }