aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Monnerat <patrick@monnerat.net>2017-09-02 17:47:10 +0100
committerPatrick Monnerat <patrick@monnerat.net>2017-09-02 17:47:10 +0100
commitce0881edee3c78609eae49665fb70264d8786d29 (patch)
treee4f5cda7865f3e73664dca5c4eb6a7a648c1e2d2
parent5bae72734b45a01c6337eb3b2c40020c4e904415 (diff)
mime: new MIME API.
Available in HTTP, SMTP and IMAP. Deprecates the FORM API. See CURLOPT_MIMEPOST. Lib code and associated documentation.
-rw-r--r--docs/libcurl/Makefile.inc6
-rw-r--r--docs/libcurl/curl_easy_setopt.32
-rw-r--r--docs/libcurl/curl_formadd.311
-rw-r--r--docs/libcurl/curl_formfree.38
-rw-r--r--docs/libcurl/curl_formget.37
-rw-r--r--docs/libcurl/curl_mime_addpart.352
-rw-r--r--docs/libcurl/curl_mime_data.354
-rw-r--r--docs/libcurl/curl_mime_data_cb.3154
-rw-r--r--docs/libcurl/curl_mime_filedata.355
-rw-r--r--docs/libcurl/curl_mime_filename.351
-rw-r--r--docs/libcurl/curl_mime_free.348
-rw-r--r--docs/libcurl/curl_mime_headers.350
-rw-r--r--docs/libcurl/curl_mime_init.368
-rw-r--r--docs/libcurl/curl_mime_name.352
-rw-r--r--docs/libcurl/curl_mime_subparts.352
-rw-r--r--docs/libcurl/curl_mime_type.362
-rw-r--r--docs/libcurl/opts/CURLOPT_HTTPPOST.311
-rw-r--r--docs/libcurl/opts/CURLOPT_MIMEPOST.352
-rw-r--r--docs/libcurl/opts/Makefile.inc1
-rw-r--r--docs/libcurl/symbols-in-versions1
-rw-r--r--include/curl/curl.h130
-rw-r--r--include/curl/typecheck-gcc.h6
-rw-r--r--lib/Makefile.inc5
-rw-r--r--lib/formdata.c845
-rw-r--r--lib/formdata.h54
-rw-r--r--lib/http.c259
-rw-r--r--lib/http.h4
-rw-r--r--lib/http2.c1
-rw-r--r--lib/imap.c48
-rw-r--r--lib/mime.c1497
-rw-r--r--lib/mime.h119
-rw-r--r--lib/non-ascii.c25
-rw-r--r--lib/non-ascii.h4
-rw-r--r--lib/smtp.c37
-rw-r--r--lib/transfer.c24
-rw-r--r--lib/url.c22
-rw-r--r--lib/urldata.h5
-rw-r--r--tests/libtest/mk-lib1521.pl4
38 files changed, 2919 insertions, 967 deletions
diff --git a/docs/libcurl/Makefile.inc b/docs/libcurl/Makefile.inc
index 580fe563f..2414ecc12 100644
--- a/docs/libcurl/Makefile.inc
+++ b/docs/libcurl/Makefile.inc
@@ -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_easy_pause.3 curl_easy_recv.3 curl_easy_send.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
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index 01dfa85b6..2982056f0 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -411,6 +411,8 @@ Size of file to send. \fICURLOPT_INFILESIZE(3)\fP
Size of file to send. \fICURLOPT_INFILESIZE_LARGE(3)\fP
.IP CURLOPT_UPLOAD
Upload data. See \fICURLOPT_UPLOAD(3)\fP
+.IP CURLOPT_MIMEPOST
+Post/send MIME data. See \fICURLOPT_MIMEPOST(3)\fP
.IP CURLOPT_MAXFILESIZE
Maximum file size to get. See \fICURLOPT_MAXFILESIZE(3)\fP
.IP CURLOPT_MAXFILESIZE_LARGE
diff --git a/docs/libcurl/curl_formadd.3 b/docs/libcurl/curl_formadd.3
index 5d1faa532..652663b7c 100644
--- a/docs/libcurl/curl_formadd.3
+++ b/docs/libcurl/curl_formadd.3
@@ -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
.\" * 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, " ...);"
.ad
.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
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
@@ -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.
See example below.
+.SH AVAILABILITY
+Deprecated in 7.56.0.
.SH RETURN VALUE
0 means everything was ok, non-zero means an error occurred corresponding
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);
.SH "SEE ALSO"
-.BR curl_easy_setopt "(3), "
-.BR curl_formfree "(3)"
+.BR curl_easy_setopt "(3),"
+.BR curl_formfree "(3),"
+.BR curl_mime_init "(3)"
diff --git a/docs/libcurl/curl_formfree.3 b/docs/libcurl/curl_formfree.3
index a2536cd9e..dd00494be 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/curl_formfree.3
@@ -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
.\" * 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);
.ad
.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
\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.
@@ -38,7 +40,9 @@ the \fIcurl_formadd(3)\fP invoke(s).
\fBform\fP is the pointer as returned from a previous call to
\fIcurl_formadd(3)\fP and may be NULL.
+.SH AVAILABILITY
+Deprecated in 7.56.0.
.SH RETURN VALUE
None
.SH "SEE ALSO"
-.BR curl_formadd "(3) "
+.BR curl_formadd "(3), " curl_mime_init "(3), " curl_mime_free "(3)"
diff --git a/docs/libcurl/curl_formget.3 b/docs/libcurl/curl_formget.3
index 635c487f5..0d947253a 100644
--- a/docs/libcurl/curl_formget.3
+++ b/docs/libcurl/curl_formget.3
@@ -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
.\" * 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;
}
.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"
-.BR curl_formadd "(3) "
+.BR curl_formadd "(3), " curl_mime_init "(3)"
diff --git a/docs/libcurl/curl_mime_addpart.3 b/docs/libcurl/curl_mime_addpart.3
new file mode 100644
index 000000000..4a86f3813
--- /dev/null
+++ b/docs/libcurl/curl_mime_addpart.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_data.3 b/docs/libcurl/curl_mime_data.3
new file mode 100644
index 000000000..4ce41c38a
--- /dev/null
+++ b/docs/libcurl/curl_mime_data.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_data_cb.3 b/docs/libcurl/curl_mime_data_cb.3
new file mode 100644
index 000000000..64e5c7dc0
--- /dev/null
+++ b/docs/libcurl/curl_mime_data_cb.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_filedata.3 b/docs/libcurl/curl_mime_filedata.3
new file mode 100644
index 000000000..fcd86cc18
--- /dev/null
+++ b/docs/libcurl/curl_mime_filedata.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_filename.3 b/docs/libcurl/curl_mime_filename.3
new file mode 100644
index 000000000..4c09402aa
--- /dev/null
+++ b/docs/libcurl/curl_mime_filename.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_free.3 b/docs/libcurl/curl_mime_free.3
new file mode 100644
index 000000000..87fea127b
--- /dev/null
+++ b/docs/libcurl/curl_mime_free.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_headers.3 b/docs/libcurl/curl_mime_headers.3
new file mode 100644
index 000000000..368c4d26c
--- /dev/null
+++ b/docs/libcurl/curl_mime_headers.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_init.3 b/docs/libcurl/curl_mime_init.3
new file mode 100644
index 000000000..601123c41
--- /dev/null
+++ b/docs/libcurl/curl_mime_init.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_name.3 b/docs/libcurl/curl_mime_name.3
new file mode 100644
index 000000000..5ef7c887f
--- /dev/null
+++ b/docs/libcurl/curl_mime_name.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_subparts.3 b/docs/libcurl/curl_mime_subparts.3
new file mode 100644
index 000000000..3c607c586
--- /dev/null
+++ b/docs/libcurl/curl_mime_subparts.3
@@ -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)"
diff --git a/docs/libcurl/curl_mime_type.3 b/docs/libcurl/curl_mime_type.3
new file mode 100644
index 000000000..0c6cd3d6b
--- /dev/null
+++ b/docs/libcurl/curl_mime_type.3
@@ -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)"
diff --git a/docs/libcurl/opts/CURLOPT_HTTPPOST.3 b/docs/libcurl/opts/CURLOPT_HTTPPOST.3
index 974f9f37a..68a15d816 100644
--- a/docs/libcurl/opts/CURLOPT_HTTPPOST.3
+++ b/docs/libcurl/opts/CURLOPT_HTTPPOST.3
@@ -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
.\" * 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
\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
NULL
.SH PROTOCOLS
@@ -71,9 +74,9 @@ curl_formadd(&formpost,
CURLFORM_END);
.fi
.SH AVAILABILITY
-As long as HTTP is enabled
+As long as HTTP is enabled. Deprecated in 7.56.0.
.SH RETURN VALUE
Returns CURLE_OK if HTTP is enabled, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
-.BR CURLOPT_POSTFIELDS "(3), " CURLOPT_POST "(3), "
-.BR curl_formadd "(3), " curl_formfree "(3), "
+.BR CURLOPT_POSTFIELDS "(3), " CURLOPT_POST "(3), " CURLOPT_MIMEPOST "(3),"
+.BR curl_formadd "(3), " curl_formfree "(3), " curl_mime_init "(3)"
diff --git a/docs/libcurl/opts/CURLOPT_MIMEPOST.3 b/docs/libcurl/opts/CURLOPT_MIMEPOST.3
new file mode 100644
index 000000000..a3a964f7f
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_MIMEPOST.3
@@ -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)"
diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc
index ad09dce2d..f710b5eb3 100644
--- a/docs/libcurl/opts/Makefile.inc
+++ b/docs/libcurl/opts/Makefile.inc
@@ -175,6 +175,7 @@ man_MANS = \
CURLOPT_MAXREDIRS.3 \
CURLOPT_MAX_RECV_SPEED_LARGE.3 \
CURLOPT_MAX_SEND_SPEED_LARGE.3 \
+ CURLOPT_MIMEPOST.3 \
CURLOPT_NETRC.3 \
CURLOPT_NETRC_FILE.3 \
CURLOPT_NEW_DIRECTORY_PERMS.3 \
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 1a539d440..9ef7148c8 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -441,6 +441,7 @@ CURLOPT_MAXFILESIZE_LARGE 7.11.0
CURLOPT_MAXREDIRS 7.5
CURLOPT_MAX_RECV_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_NETRC 7.1
CURLOPT_NETRC_FILE 7.11.0
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 63c6e17be..aa94976f3 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1791,6 +1791,9 @@ typedef enum {
/* Enable/disable SSH compression */
CINIT(SSH_COMPRESSION, LONG, 268),
+ /* Post MIME data. */
+ CINIT(MIMEPOST, OBJECTPOINT, 269),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
@@ -1946,6 +1949,133 @@ typedef enum {
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);
+/* 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> */
#ifdef CFINIT
#undef CFINIT
diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h
index 74548241b..10c74c764 100644
--- a/include/curl/typecheck-gcc.h
+++ b/include/curl/typecheck-gcc.h
@@ -96,6 +96,9 @@ __extension__ ({ \
if((_curl_opt) == CURLOPT_HTTPPOST) \
if(!_curl_is_arr((value), struct curl_httppost)) \
_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_arr((value), struct 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_easy_setopt expects a 'struct curl_httppost *' "
"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_easy_setopt expects a 'struct curl_slist *' argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_CURLSH,
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 19f58000a..5c0b930cf 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -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_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 \
- 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 \
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_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 \
- curl_printf.h system_win32.h rand.h
+ curl_printf.h system_win32.h rand.h mime.h
LIB_RCFILES = libcurl.rc
diff --git a/lib/formdata.c b/lib/formdata.c
index e48a6276c..129086dee 100644
--- a/lib/formdata.c
+++ b/lib/formdata.c
@@ -32,6 +32,8 @@
#include "urldata.h" /* for struct Curl_easy */
#include "formdata.h"
+#include "mime.h"
+#include "non-ascii.h"
#include "vtls/vtls.h"
#include "strcase.h"
#include "sendf.h"
@@ -42,13 +44,6 @@
#include "curl_memory.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
extensions. */
@@ -746,211 +741,6 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost,
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()
* Serialize a curl_httppost struct.
@@ -962,42 +752,34 @@ int curl_formget(struct curl_httppost *form, void *arg,
curl_formget_callback append)
{
CURLcode result;
- curl_off_t size;
- struct FormData *data, *ptr;
+ struct Curl_mimepart toppart;
- result = Curl_getformdata(NULL, &data, form, NULL, &size);
- if(result)
- return (int)result;
-
- for(ptr = data; ptr; ptr = ptr->next) {
- if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) {
- char buffer[8192];
- size_t nread;
- struct Form temp;
-
- Curl_FormInit(&temp, ptr);
-
- do {
- nread = readfromfile(&temp, buffer, sizeof(buffer));
- if((nread == (size_t) -1) ||
- (nread > sizeof(buffer)) ||
- (nread != append(arg, buffer, nread))) {
- if(temp.fp)
- fclose(temp.fp);
- Curl_formclean(&data);
- return -1;
- }
- } while(nread);
- }
- else {
- if(ptr->length != append(arg, ptr->line, ptr->length)) {
- Curl_formclean(&data);
- return -1;
- }
+ Curl_mime_initpart(&toppart, NULL); /* default form is empty */
+ result = Curl_getformdata(NULL, &toppart, form, NULL);
+ if(!result)
+ result = Curl_mime_prepare_headers(&toppart, "multipart/form-data",
+ NULL, MIMESTRATEGY_FORM);
+
+ while(!result) {
+ char buffer[8192];
+ size_t nread = Curl_mime_read(buffer, 1, sizeof buffer, &toppart);
+
+ if(!nread)
+ break;
+
+ switch(nread) {
+ default:
+ if(append(arg, buffer, nread) != nread)
+ result = CURLE_READ_ERROR;
+ break;
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ break;
}
}
- 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 */
}
-#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
- * (possibly huge) multipart formdata. The input list is in 'post', while the
- * output resulting linked lists gets stored in '*finalform'. *sizep will get
- * 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'.
+ * Curl_getformdata() converts a linked list of "meta data" into a mime
+ * structure. The input list is in 'post', while the output is stored in
+ * mime part at '*finalform'.
*
* 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
@@ -1150,422 +825,114 @@ static CURLcode formdata_add_filename(const struct curl_httppost *file,
*/
CURLcode Curl_getformdata(struct Curl_easy *data,
- struct FormData **finalform,
+ struct Curl_mimepart *finalform,
struct curl_httppost *post,
- const char *custom_content_type,
- curl_off_t *sizep)
+ curl_read_callback fread_func)
{
- struct FormData *form = NULL;
- struct FormData *firstform;
- struct curl_httppost *file;
CURLcode result = CURLE_OK;
- curl_off_t size = 0; /* support potentially ENORMOUS formposts */
- char fileboundary[42];
- struct curl_slist *curList;
- char boundary[42];
+ struct Curl_mime *form = NULL;
+ struct Curl_mime *multipart;
+ struct Curl_mimepart *part;
+ struct curl_httppost *file;
- *finalform = NULL; /* default form is empty */
+ Curl_mime_cleanpart(finalform); /* default form is empty */
if(!post)
return result; /* no input => no output! */
- result = formboundary(data, boundary, sizeof(boundary));
- if(result)
- return result;
-
- /* 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;
+ form = curl_mime_init(data);
+ if(!form)
+ result = CURLE_OUT_OF_MEMORY;
- result = AddFormDataf(&form, &size, "\"");
- if(result)
- break;
+ if(!result)
+ result = curl_mime_subparts(finalform, form);
+ /* Process each top part. */
+ for(; !result && post; post = post->next) {
+ /* If we have more than a file here, create a mime subpart and fill it. */
+ multipart = form;
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;
+ 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;
}
-
- result = AddFormDataf(&form, &size,
- "\r\nContent-Type: multipart/mixed;"
- " boundary=%s\r\n",
- fileboundary);
- if(result)
- break;
+ if(!result)
+ result = curl_mime_subparts(part, multipart);
}
- 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. */
+ /* 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;
- 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);
- }
+ /* Set the headers. */
+ if(!result)
+ result = curl_mime_headers(part, file->contentheader, 0);
- if(result)
- break;
- }
+ /* Set the content type. */
+ if(!result &&file->contenttype)
+ result = curl_mime_type(part, file->contenttype);
- if(file->contenttype) {
- /* we have a specified type */
- result = AddFormDataf(&form, &size,
- "\r\nContent-Type: %s",
- file->contenttype);
- if(result)
- break;
- }
+ /* Set field name. */
+ if(!result && !post->more)
+ result = curl_mime_name(part, post->name,
+ post->namelength? post->namelength: -1);
- 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;
+ /* Process contents. */
+ if(!result) {
+ curl_off_t clen = post->contentslength;
- result = AddFormDataf(&form, &size, "\r\n\r\n");
- if(result)
- break;
+ if(post->flags & CURL_HTTPPOST_LARGE)
+ clen = post->contentlen;
+ if(!clen)
+ clen = -1;
- 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;
- }
- }
+ 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 {
- if(data)
- failf(data, "couldn't open file \"%s\"", file->contents);
- *finalform = NULL;
- result = CURLE_READ_ERROR;
+ 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
}
}
- 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;
+ /* 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);
}
- post = post->next;
- } while(post); /* for each field */
-
- /* end-boundary for everything */
- if(!result)
- result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary);
-
- if(result) {
- Curl_formclean(&firstform);
- return result;
}
- *sizep = size;
- *finalform = firstform;
+ if(result)
+ Curl_mime_cleanpart(finalform);
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 */
CURLFORMcode curl_formadd(struct curl_httppost **httppost,
struct curl_httppost **last_post,
diff --git a/lib/formdata.h b/lib/formdata.h
index 69629f628..46642fb63 100644
--- a/lib/formdata.h
+++ b/lib/formdata.h
@@ -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
* 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 */
typedef struct FormInfo {
char *name;
@@ -69,31 +43,9 @@ typedef struct FormInfo {
struct FormInfo *more;
} FormInfo;
-int Curl_FormInit(struct Form *form, struct FormData *formdata);
-
CURLcode Curl_getformdata(struct Curl_easy *data,
- struct FormData **,
+ struct Curl_mimepart *,
struct curl_httppost *post,
- const char *custom_contenttype,
- 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 *);
+ curl_read_callback fread_func);
#endif /* HEADER_CURL_FORMDATA_H */
diff --git a/lib/http.c b/lib/http.c
index cf50a7cac..f4a1faac3 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -50,6 +50,7 @@
#include "transfer.h"
#include "sendf.h"
#include "formdata.h"
+#include "mime.h"
#include "progress.h"
#include "curl_base64.h"
#include "cookie.h"
@@ -162,6 +163,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
if(!http)
return CURLE_OUT_OF_MEMORY;
+ Curl_mime_initpart(&http->form, conn->data);
conn->data->req.protop = http;
Curl_http2_setup_conn(conn);
@@ -427,6 +429,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
expectsend = data->state.infilesize;
break;
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
expectsend = http->postsize;
break;
default:
@@ -1470,18 +1473,17 @@ CURLcode Curl_http_done(struct connectdata *conn,
Curl_http2_done(conn, premature);
- if(HTTPREQ_POST_FORM == data->set.httpreq) {
- data->req.bytecount = http->readbytecount + http->writebytecount;
+ Curl_mime_cleanpart(&http->form);
- Curl_formclean(&http->sendit); /* Now free that whole lot */
- if(http->form.fp) {
- /* a file being uploaded was left opened, close it! */
- fclose(http->form.fp);
- http->form.fp = NULL;
- }
- }
- else if(HTTPREQ_PUT == data->set.httpreq)
+ switch(data->set.httpreq) {
+ case HTTPREQ_PUT:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
data->req.bytecount = http->readbytecount + http->writebytecount;
+ break;
+ default:
+ break;
+ }
if(status)
return status;
@@ -1637,6 +1639,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
/* this header (extended by formdata.c) is sent later */
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 &&
/* while doing auth neg, don't allow the custom length since
we will force length zero then */
@@ -1775,7 +1781,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
const char *httpstring;
Curl_send_buffer *req_buffer;
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
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) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
request = "POST";
break;
case HTTPREQ_PUT:
@@ -1942,6 +1949,48 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
#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:");
if(ptr) {
/* 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");
}
else {
- if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
- data->set.upload &&
- (data->state.infilesize == -1)) {
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
+ http->postsize < 0) ||
+ (data->set.upload && data->state.infilesize == -1))) {
if(conn->bits.authneg)
/* don't enable chunked during auth neg */
;
@@ -2123,21 +2173,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
#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";
- if(( (HTTPREQ_POST == httpreq) ||
- (HTTPREQ_POST_FORM == httpreq) ||
- (HTTPREQ_PUT == httpreq) ) &&
+ if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
data->state.resume_from) {
/**********************************************************************
* 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"
* 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.
+ * Resuming mime/form posting at an offset > 0 has no sense and is ignored.
*********************************************************************/
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",
data->state.range);
}
- else if((httpreq != HTTPREQ_GET) &&
+ else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
!Curl_checkheaders(conn, "Content-Range:")) {
/* if a line like this was already allocated, free the previous one */
@@ -2415,117 +2454,79 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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;
+ case HTTPREQ_PUT: /* Let's PUT the data to the server! */
- http->sending = HTTPSEND_BODY;
+ if(conn->bits.authneg)
+ postsize = 0;
+ else
+ postsize = data->state.infilesize;
- if(!data->req.upload_chunky &&
- !Curl_checkheaders(conn, "Content-Length:")) {
+ if((postsize != -1) && !data->req.upload_chunky &&
+ (conn->bits.authneg || !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);
+ "\r\n", 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(postsize != 0) {
+ result = expect100(data, conn, req_buffer);
if(result)
return result;
}
- /* make the request end in a true CRLF */
- result = Curl_add_buffer(req_buffer, "\r\n", 2);
+ result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
if(result)
return result;
- /* set upload size to the progress meter */
- Curl_pgrsSetUploadSize(data, http->postsize);
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, postsize);
- /* fire away the whole request to the server */
+ /* 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");
+ failf(data, "Failed sending PUT request");
else
- /* setup variables for the upcoming transfer */
+ /* prepare for 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 */
+ &http->readbytecount, postsize?FIRSTSOCKET:-1,
+ postsize?&http->writebytecount:NULL);
+ if(result)
return result;
- }
-
break;
- case HTTPREQ_PUT: /* Let's PUT the data to the server! */
+ 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;
- if(conn->bits.authneg)
- postsize = 0;
- else
- postsize = data->state.infilesize;
+ 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((postsize != -1) && !data->req.upload_chunky &&
+ 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:"))) {
- /* only add Content-Length if not uploading chunked */
+ /* 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);
@@ -2533,24 +2534,52 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return result;
}
- if(postsize != 0) {
+ /* 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;
- result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
+ /* 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 PUT request");
+ failf(data, "Failed sending POST request");
else
/* prepare for transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
@@ -2558,6 +2587,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
postsize?&http->writebytecount:NULL);
if(result)
return result;
+
break;
case HTTPREQ_POST:
@@ -3171,6 +3201,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
case HTTPREQ_PUT:
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
/* We got an error response. If this happened before the whole
* request body has been sent we stop sending and mark the
* connection for closure after we've read the entire response.
diff --git a/lib/http.h b/lib/http.h
index a845f564d..04c58522c 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -130,7 +130,7 @@ CURLcode Curl_http_perhapsrewind(struct connectdata *conn);
* HTTP unique setup
***************************************************************************/
struct HTTP {
- struct FormData *sendit;
+ struct Curl_mimepart *sendit;
curl_off_t postsize; /* off_t to handle large file sizes */
const char *postdata;
@@ -140,7 +140,7 @@ struct HTTP {
curl_off_t writebytecount;
/* For FORM posting */
- struct Form form;
+ struct Curl_mimepart form;
struct back {
curl_read_callback fread_func; /* backup storage for fread pointer */
diff --git a/lib/http2.c b/lib/http2.c
index 8beaabf17..d466ccaa0 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -1955,6 +1955,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
switch(conn->data->set.httpreq) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
case HTTPREQ_PUT:
if(conn->data->state.infilesize != -1)
stream->upload_left = conn->data->state.infilesize;
diff --git a/lib/imap.c b/lib/imap.c
index 21fe0870f..d37113aae 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -68,6 +68,7 @@
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h"
#include "imap.h"
+#include "mime.h"
#include "strtoofft.h"
#include "strcase.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)
{
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;
/* Check we have a mailbox */
if(!imap->mailbox) {
- failf(conn->data, "Cannot APPEND without a mailbox.");
+ failf(data, "Cannot APPEND without a mailbox.");
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 */
- if(conn->data->state.infilesize < 0) {
- failf(conn->data, "Cannot APPEND with unknown input file size\n");
+ if(data->state.infilesize < 0) {
+ failf(data, "Cannot APPEND with unknown input file size\n");
return CURLE_UPLOAD_FAILED;
}
@@ -730,7 +761,7 @@ static CURLcode imap_perform_append(struct connectdata *conn)
/* Send the APPEND command */
result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
- mailbox, conn->data->state.infilesize);
+ mailbox, data->state.infilesize);
free(mailbox);
@@ -1423,9 +1454,10 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
result = status; /* use the already set error code */
}
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 */
- if(!data->set.upload)
+ if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
state(conn, IMAP_FETCH_FINAL);
else {
/* 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;
/* 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 */
result = imap_perform_append(conn);
else if(imap->custom && (selected || !imap->mailbox))
diff --git a/lib/mime.c b/lib/mime.c
new file mode 100644
index 000000000..759ade085
--- /dev/null
+++ b/lib/mime.c
@@ -0,0 +1,1497 @@
+/***************************************************************************
+ * _ _ ____ _
+ * 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "mime.h"
+#include "non-ascii.h"
+
+#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_IMAP)
+
+#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
+#include <libgen.h>
+#endif
+
+#include "rand.h"
+#include "slist.h"
+#include "strcase.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef WIN32
+# ifndef R_OK
+# define R_OK 4
+# endif
+#endif
+
+
+#define FILE_CONTENTTYPE_DEFAULT "application/octet-stream"
+#define MULTIPART_CONTENTTYPE_DEFAULT "multipart/mixed"
+#define DISPOSITION_DEFAULT "attachment"
+
+#define READ_ERROR ((size_t) -1)
+
+
+#ifndef __VMS
+#define filesize(name, stat_data) (stat_data.st_size)
+#define fopen_read fopen
+
+#else
+
+#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;
+ }
+}
+
+#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
+
+/*
+ * 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.
+ *
+ */
+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");
+ }
+}
+
+#define fopen_read vmsfopenread
+#endif
+
+
+#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;
+}
+
+#define basename(x) Curl_basename((x))
+#endif
+
+
+/* Set readback state. */
+static void mimesetstate(struct mime_state *state,
+ enum mimestate tok, void *ptr)
+{
+ state->state = tok;
+ state->ptr = ptr;
+ state->offset = 0;
+}
+
+
+/* Escape header string into allocated memory. */
+static char *escape_string(const char *src, ssize_t srclen)
+{
+ size_t len = srclen >= 0? (size_t) srclen: strlen(src);
+ size_t bytecount;
+ size_t i;
+ char *dst;
+
+ bytecount = len;
+ for(i = 0; i < len; i++)
+ if(src[i] == '"' || src[i] == '\\' || !src[i])
+ bytecount++;
+
+ dst = malloc(bytecount + 1);
+ if(!dst)
+ return NULL;
+
+ for(i = 0; len; len--) {
+ char c = *src++;
+
+ if(c == '"' || c == '\\' || !c)
+ dst[i++] = '\\';
+ dst[i++] = c? c: '0';
+ }
+
+ dst[i] = '\0';
+ return dst;
+}
+
+/* Check if header matches. */
+static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len)
+{
+ char *value = NULL;
+
+ if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':')
+ for(value = hdr->data + len + 1; *value == ' '; value++)
+ ;
+ return value;
+}
+
+/* Get a header from an slist. */
+static char *search_header(struct curl_slist *hdrlist, const char *hdr)
+{
+ size_t len = strlen(hdr);
+ char *value = NULL;
+
+ for(; !value && hdrlist; hdrlist = hdrlist->next)
+ value = match_header(hdrlist, hdr, len);
+
+ return value;
+}
+
+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 ! */
+}
+
+
+/* In-memory data callbacks. */
+/* Argument is a pointer to the mime part. */
+static size_t mime_mem_read(char *buffer, size_t size, size_t nitems,
+ void *instream)
+{
+ struct Curl_mimepart *part = (struct Curl_mimepart *) instream;
+ size_t sz = part->datasize - part->state.offset;
+
+ (void) size; /* Always 1.*/
+
+ if(sz > nitems)
+ sz = nitems;
+
+ if(sz)
+ memcpy(buffer, (char *) part->data, sz);
+
+ part->state.offset += sz;
+ return sz;
+}
+
+static int mime_mem_seek(void *instream, curl_off_t offset, int whence)
+{
+ struct Curl_mimepart *part = (struct Curl_mimepart *) instream;
+
+ switch(whence) {
+ case SEEK_CUR:
+ offset += part->state.offset;
+ break;
+ case SEEK_END:
+ offset += part->datasize;
+ break;
+ }
+
+ if(offset < 0 || offset > part->datasize)
+ return CURL_SEEKFUNC_FAIL;
+
+ part->state.offset = offset;
+ return CURL_SEEKFUNC_OK;
+}
+
+static void mime_mem_free(void *ptr)
+{
+ Curl_safefree(((struct Curl_mimepart *) ptr)->data);
+}
+
+
+/* Open file callbacks. */
+/* Argument is the FILE pointer. */
+static size_t mime_file_read(char *buffer, size_t size, size_t nitems,
+ void *instream)
+{
+ return (size_t) fread(buffer, size, nitems, instream);
+}
+
+static int mime_file_seek(void *instream, curl_off_t offset, int whence)
+{
+ FILE *fp = (FILE *) instream;
+
+ return fseek(fp, (long) offset, whence)?
+ CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK;
+}
+
+
+/* Named file callbacks. */
+/* Argument is a pointer to the mime part. */
+static int mime_open_namedfile(struct Curl_mimepart * part)
+{
+ /* Open a MIMEKIND_NAMEDFILE part. */
+
+ if(part->namedfp)
+ return 0;
+ part->namedfp = fopen_read(part->data, "rb");
+ return part->namedfp? 0: -1;
+}
+
+static size_t mime_namedfile_read(char *buffer, size_t size, size_t nitems,
+ void *instream)
+{
+ struct Curl_mimepart *part = (struct Curl_mimepart *) instream;
+
+ if(mime_open_namedfile(part))
+ return READ_ERROR;
+
+ return mime_file_read(buffer, size, nitems, part->namedfp);
+}
+
+static int mime_namedfile_seek(void *instream, curl_off_t offset, int whence)
+{
+ struct Curl_mimepart *part = (struct Curl_mimepart *) instream;
+
+ switch(whence) {
+ case SEEK_CUR:
+ if(part->namedfp)
+ offset += ftell(part->namedfp);
+ break;
+ case SEEK_END:
+ offset += part->datasize;
+ break;
+ }
+
+ if(!offset && !part->namedfp)
+ return CURL_SEEKFUNC_OK;
+
+ if(mime_open_namedfile(part))
+ return CURL_SEEKFUNC_FAIL;
+
+ return mime_file_seek(part->namedfp, offset, SEEK_SET);
+}
+
+static void mime_namedfile_free(void *ptr)
+{
+ struct Curl_mimepart *part = (struct Curl_mimepart *) ptr;
+
+ if(part->namedfp) {
+ fclose(part->namedfp);
+ part->namedfp = NULL;
+ }
+ Curl_safefree(part->data);
+ part->data = NULL;
+}
+
+
+/* Subparts callbacks. */
+/* Argument is a pointer to the mime structure. */
+
+/* Readback a byte string segment. */
+static size_t readback_bytes(struct mime_state *state,
+ char *buffer, size_t bufsize,
+ const char *bytes, size_t numbytes,
+ const char *trail)
+{
+ size_t sz;
+
+ sz = numbytes - state->offset;
+
+ if(numbytes > state->offset) {
+ sz = numbytes - state->offset;
+ bytes += state->offset;
+ }
+ else {
+ size_t tsz = strlen(trail);
+
+ sz = state->offset - numbytes;
+ if(sz >= tsz)
+ return 0;
+ bytes = trail + sz;
+ sz = tsz - sz;
+ }
+
+ if(sz > bufsize)
+ sz = bufsize;
+
+ memcpy(buffer, bytes, sz);
+ state->offset += sz;
+ return sz;
+}
+
+/* Readback a mime part. */
+static size_t readback_part(struct Curl_mimepart *part,
+ char *buffer, size_t bufsize)
+{
+ size_t cursize = 0;
+ size_t sz;
+ struct curl_slist *hdr;
+#ifdef CURL_DOES_CONVERSIONS
+ char *convbuf = buffer;
+#endif
+
+ /* Readback from part. */
+
+ while(bufsize) {
+ sz = 0;
+ hdr = (struct curl_slist *) part->state.ptr;
+ switch(part->state.state) {
+ case MIMESTATE_BEGIN:
+ mimesetstate(&part->state, part->flags & MIME_BODY_ONLY? MIMESTATE_BODY:
+ MIMESTATE_CURLHEADERS, part->curlheaders);
+ break;
+ case MIMESTATE_USERHEADERS:
+ if(!hdr) {
+ mimesetstate(&part->state, MIMESTATE_EOH, NULL);
+ break;
+ }
+ if(match_header(hdr, "Content-Type", 12)) {
+ mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
+ break;
+ }
+ /* FALLTHROUGH */
+ case MIMESTATE_CURLHEADERS:
+ if(!hdr)
+ mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
+ else {
+ sz = readback_bytes(&part->state, buffer, bufsize,
+ hdr->data, strlen(hdr->data), "\r\n");
+ if(!sz)
+ mimesetstate(&part->state, part->state.state, hdr->next);
+ }
+ break;
+ case MIMESTATE_EOH:
+ sz = readback_bytes(&part->state, buffer, bufsize, "\r\n", 2, "");
+ if(!sz)
+ mimesetstate(&part->state, MIMESTATE_BODY, NULL);
+ break;
+ case MIMESTATE_BODY:
+#ifdef CURL_DOES_CONVERSIONS
+ if(part->easy && convbuf < buffer) {
+ CURLcode result = Curl_convert_to_network(part->easy, convbuf,
+ buffer - convbuf);
+ if(result)
+ return READ_ERROR;
+ convbuf = buffer;
+ }
+#endif
+ mimesetstate(&part->state, MIMESTATE_CONTENT, NULL);
+ break;
+ case MIMESTATE_CONTENT:
+ if(part->readfunc)
+ sz = part->readfunc(buffer, 1, bufsize, part->arg);
+ switch(sz) {
+ case 0:
+ mimesetstate(&part->state, MIMESTATE_END, NULL);
+ /* Try sparing open file descriptors. */
+ if(part->kind == MIMEKIND_NAMEDFILE && part->namedfp) {
+ fclose(part->namedfp);
+ part->namedfp = NULL;
+ }
+ /* FALLTHROUGH */
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ return cursize? cursize: sz;
+ }
+ break;
+ case MIMESTATE_END:
+ return cursize;
+ default:
+ break; /* Other values not in part state. */
+ }
+
+ /* Bump buffer and counters according to read size. */
+ cursize += sz;
+ buffer += sz;
+ bufsize -= sz;
+ }
+
+#ifdef CURL_DOES_CONVERSIONS
+ if(part->easy && convbuf < buffer &&
+ part->state.state < MIMESTATE_BODY) {
+ CURLcode result = Curl_convert_to_network(part->easy, convbuf,
+ buffer - convbuf);
+ if(result)
+ return READ_ERROR;
+ }
+#endif
+
+ return cursize;
+}
+
+/* Readback from mime. */
+static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
+ void *instream)
+{
+ struct Curl_mime *mime = (struct Curl_mime *) instream;
+ size_t cursize = 0;
+ size_t sz;
+ struct Curl_mimepart *part;
+#ifdef CURL_DOES_CONVERSIONS
+ char *convbuf = buffer;
+#endif
+
+ (void) size; /* Always 1. */
+
+ while(nitems) {
+ sz = 0;
+ part = mime->state.ptr;
+ switch(mime->state.state) {
+ case MIMESTATE_BEGIN:
+ case MIMESTATE_BODY:
+#ifdef CURL_DOES_CONVERSIONS
+ convbuf = buffer;
+#endif
+ mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart);
+ /* The first boundary always follows the header termination empty line,
+ so is always preceded by a CRLK. We can then spare 2 characters
+ by skipping the leading CRLF in boundary. */
+ mime->state.offset += 2;
+ break;
+ case MIMESTATE_BOUNDARY1:
+ sz = readback_bytes(&mime->state, buffer, nitems, "\r\n--", 4, "");
+ if(!sz)
+ mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part);
+ break;
+ case MIMESTATE_BOUNDARY2:
+ sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
+ strlen(mime->boundary), part? "\r\n": "--\r\n");
+ if(!sz) {
+#ifdef CURL_DOES_CONVERSIONS
+ if(mime->easy && convbuf < buffer) {
+ CURLcode result = Curl_convert_to_network(mime->easy, convbuf,
+ buffer - convbuf);
+ if(result)
+ return READ_ERROR;
+ convbuf = buffer;
+ }
+#endif
+ mimesetstate(&mime->state,
+ part? MIMESTATE_CONTENT: MIMESTATE_END, part);
+ }
+ break;
+ case MIMESTATE_CONTENT:
+ sz = readback_part(part, buffer, nitems);
+ switch(sz) {
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ return cursize? cursize: sz;
+ case 0:
+#ifdef CURL_DOES_CONVERSIONS
+ convbuf = buffer;
+#endif
+ mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart);
+ break;
+ }
+ break;
+ case MIMESTATE_END:
+ return cursize;
+ default:
+ break; /* other values not used in mime state. */
+ }
+
+ /* Bump buffer and counters according to read size. */
+ cursize += sz;
+ buffer += sz;
+ nitems -= sz;
+ }
+
+#ifdef CURL_DOES_CONVERSIONS
+ if(mime->easy && convbuf < buffer &&
+ mime->state.state <= MIMESTATE_CONTENT) {
+ CURLcode result = Curl_convert_to_network(mime->easy, convbuf,
+ buffer - convbuf);
+ if(result)
+ return READ_ERROR;
+ }
+#endif
+
+ return cursize;
+}
+
+static int mime_part_rewind(struct Curl_mimepart *part)
+{
+ int res = CURL_SEEKFUNC_OK;
+ enum mimestate targetstate = MIMESTATE_BEGIN;
+
+ if(part->flags & MIME_BODY_ONLY)
+ targetstate = MIMESTATE_BODY;
+ if(part->state.state > targetstate) {
+ res = CURL_SEEKFUNC_CANTSEEK;
+ if(part->seekfunc)
+ res = part->seekfunc(part->arg, part->origin, SEEK_SET);
+ }
+
+ if(res == CURL_SEEKFUNC_OK)
+ mimesetstate(&part->state, targetstate, NULL);
+
+ return res;
+}
+
+static int mime_subparts_seek(void *instream, curl_off_t offset, int whence)
+{
+ struct Curl_mime *mime = (struct Curl_mime *) instream;
+ struct Curl_mimepart *part;
+ int result = CURL_SEEKFUNC_OK;
+ int res;
+
+ if(whence != SEEK_SET || offset)
+ return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */
+
+ if(mime->state.state == MIMESTATE_BEGIN)
+ return CURL_SEEKFUNC_OK; /* Already rewound. */
+
+ for(part = mime->firstpart; part; part = part->nextpart) {
+ res = mime_part_rewind(part);
+ if(res != CURL_SEEKFUNC_OK)
+ result = res;
+ }
+
+ if(result == CURL_SEEKFUNC_OK)
+ mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
+
+ return result;
+}
+
+static void mime_subparts_free(void *ptr)
+{
+ struct Curl_mime *mime = (struct Curl_mime *) ptr;
+ curl_mime_free(mime);
+}
+
+
+/* Release part content. */
+static void cleanup_part_content(struct Curl_mimepart *part)
+{
+ if(part->freefunc)
+ part->freefunc(part->arg);
+
+ part->readfunc = NULL;
+ part->seekfunc = NULL;
+ part->freefunc = NULL;
+ part->arg = NULL;
+ part->data = NULL;
+ part->namedfp = NULL;
+ part->origin = 0;
+ part->datasize = (curl_off_t) 0; /* No size yet. */
+ part->kind = MIMEKIND_NONE;
+}
+
+void Curl_mime_cleanpart(struct Curl_mimepart *part)
+{
+ cleanup_part_content(part);
+ curl_slist_free_all(part->curlheaders);
+ if(part->flags & MIME_USERHEADERS_OWNER)
+ curl_slist_free_all(part->userheaders);
+ Curl_safefree(part->mimetype);
+ Curl_safefree(part->name);
+ Curl_safefree(part->filename);
+ Curl_mime_initpart(part, part->easy);
+}
+
+/* Recursively delete a mime handle and its parts. */
+void curl_mime_free(struct Curl_mime *mime)
+{
+ struct Curl_mimepart *part;
+
+ if(mime) {
+ while(mime->firstpart) {
+ part = mime->firstpart;
+ mime->firstpart = part->nextpart;
+ Curl_mime_cleanpart(part);
+ free(part);
+ }
+
+ free(mime->boundary);
+ free(mime);
+ }
+}
+
+/*
+ * Mime build functions.
+ */
+
+/* Create a mime handle. */
+struct Curl_mime *curl_mime_init(struct Curl_easy *easy)
+{
+ struct Curl_mime *mime;
+
+ mime = (struct Curl_mime *) malloc(sizeof *mime);
+
+ if(mime) {
+ mime->easy = easy;
+ mime->parent = NULL;
+ mime->firstpart = NULL;
+ mime->lastpart = NULL;
+
+ /* Get a part boundary. */
+ mime->boundary = malloc(24 + MIME_RAND_BOUNDARY_CHARS + 1);
+ if(!mime->boundary) {
+ free(mime);
+ return NULL;
+ }
+
+ memset(mime->boundary, '-', 24);
+ Curl_rand_hex(easy, (unsigned char *) mime->boundary + 24,
+ MIME_RAND_BOUNDARY_CHARS + 1);
+ mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
+ }
+
+ return mime;
+}
+
+/* Initialize a mime part. */
+void Curl_mime_initpart(struct Curl_mimepart *part, struct Curl_easy *easy)
+{
+ memset((char *) part, 0, sizeof *part);
+ part->easy = easy;
+ mimesetstate(&part->state, MIMESTATE_BEGIN, NULL);
+}
+
+/* Create a mime part and append it to a mime handle's part list. */
+struct Curl_mimepart *curl_mime_addpart(struct Curl_mime *mime)
+{
+ struct Curl_mimepart *part;
+
+ if(!mime)
+ return NULL;
+
+ part = (struct Curl_mimepart *) malloc(sizeof *part);
+
+ if(part) {
+ Curl_mime_initpart(part, mime->easy);
+ part->parent = mime;
+
+ if(mime->lastpart)
+ mime->lastpart->nextpart = part;
+ else
+ mime->firstpart = part;
+
+ mime->lastpart = part;
+ }
+
+ return part;
+}
+
+/* Set mime part name. */
+CURLcode curl_mime_name(struct Curl_mimepart *part,
+ const char *name, ssize_t namesize)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ Curl_safefree(part->name);
+ part->name = NULL;
+ part->namesize = 0;
+
+ if(name) {
+ if(namesize < 0)
+ namesize = strlen(name);
+ part->name = malloc(namesize + 1);
+ if(!part->name)
+ return CURLE_OUT_OF_MEMORY;
+ if(namesize)
+ memcpy(part->name, name, namesize);
+ part->name[namesize] = '\0';
+ part->namesize = namesize;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part remote file name. */
+CURLcode curl_mime_filename(struct Curl_mimepart *part, const char *filename)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ Curl_safefree(part->filename);
+ part->filename = NULL;
+
+ if(filename) {
+ part->filename = strdup(filename);
+ if(!part->filename)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part content from memory data. */
+CURLcode curl_mime_data(struct Curl_mimepart *part,
+ const char *data, ssize_t datasize)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ cleanup_part_content(part);
+
+ if(data) {
+ if(datasize < 0)
+ datasize = strlen(data);
+
+ part->data = malloc(datasize + 1);
+ if(!part->data)
+ return CURLE_OUT_OF_MEMORY;
+
+ part->datasize = datasize;
+
+ if(datasize)
+ memcpy(part->data, data, datasize);
+ part->data[datasize] = '\0'; /* Set a nul terminator as sentinel. */
+
+ part->readfunc = mime_mem_read;
+ part->seekfunc = mime_mem_seek;
+ part->freefunc = mime_mem_free;
+ part->arg = part;
+ part->kind = MIMEKIND_DATA;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part content from opened file. */
+CURLcode Curl_mime_file(struct Curl_mimepart *part,
+ FILE *fp, int closewhendone)
+{
+ if(!part || !fp)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ cleanup_part_content(part);
+
+ part->arg = fp;
+ part->readfunc = (curl_read_callback) mime_file_read;
+ if(closewhendone)
+ part->freefunc = (curl_free_callback) fclose;
+ part->origin = ftell(fp);
+ /* Check if file is seekable and get its size. */
+ part->datasize = (curl_off_t) -1; /* Unknown size. */
+ if(!fseek(fp, 0L, SEEK_END)) {
+ part->datasize = ftell(fp);
+ if(part->datasize >= 0) {
+ if(part->datasize < part->origin)
+ part->datasize = 0;
+ else
+ part->datasize -= part->origin;
+ part->seekfunc = mime_file_seek;
+ }
+ fseek(fp, (long) part->origin, SEEK_SET);
+ }
+ part->kind = MIMEKIND_FILE;
+ return CURLE_OK;
+}
+
+/* Set mime part content from named local file. */
+CURLcode curl_mime_filedata(struct Curl_mimepart *part, const char *filename)
+{
+ CURLcode result = CURLE_OK;
+ struct_stat sbuf;
+ char *base;
+
+ if(!part || !filename)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(!strcmp(filename, "-"))
+ return Curl_mime_file(part, stdin, 0);
+
+ if(stat(filename, &sbuf) || access(filename, R_OK))
+ result = CURLE_READ_ERROR;
+
+ cleanup_part_content(part);
+
+ part->data = strdup(filename);
+ if(!part->data)
+ result = CURLE_OUT_OF_MEMORY;
+
+ part->datasize = -1;
+ if(!result && S_ISREG(sbuf.st_mode)) {
+ part->datasize = filesize(filename, sbuf);
+ part->seekfunc = mime_namedfile_seek;
+ }
+
+ part->readfunc = mime_namedfile_read;
+ part->freefunc = mime_namedfile_free;
+ part->arg = part;
+ part->kind = MIMEKIND_NAMEDFILE;
+
+ /* As a side effect, set the filename to the current file's base name.
+ It is possible to withdraw this by explicitly calling curl_mime_filename()
+ with a NULL filename argument after the current call. */
+ base = strippath(filename);
+ if(!base)
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ CURLcode res = curl_mime_filename(part, base);
+
+ if(res)
+ result = res;
+ free(base);
+ }
+ return result;
+}
+
+/* Set mime part type. */
+CURLcode curl_mime_type(struct Curl_mimepart *part, const char *mimetype)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ Curl_safefree(part->mimetype);
+ part->mimetype = NULL;
+
+ if(mimetype) {
+ part->mimetype = strdup(mimetype);
+ if(!part->mimetype)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime data transfer encoder. */
+CURLcode curl_mime_encoder(struct Curl_mimepart *part, const char *encoding)
+{
+ CURLcode result = CURLE_OK;
+
+ /* Encoding feature not yet implemented. */
+
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(encoding)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ return result;
+}
+
+/* Set mime part headers. */
+CURLcode curl_mime_headers(struct Curl_mimepart *part,
+ struct curl_slist *headers, int take_ownership)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(part->flags & MIME_USERHEADERS_OWNER) {
+ curl_slist_free_all(part->userheaders);
+ part->flags &= ~MIME_USERHEADERS_OWNER;
+ }
+ part->userheaders = headers;
+ if(headers && take_ownership)
+ part->flags |= MIME_USERHEADERS_OWNER;
+ return CURLE_OK;
+}
+
+/* Set mime part content from callback. */
+CURLcode curl_mime_data_cb(struct Curl_mimepart *part, curl_off_t datasize,
+ curl_read_callback readfunc,
+ curl_seek_callback seekfunc,
+ curl_free_callback freefunc, void *arg)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ cleanup_part_content(part);
+
+ if(readfunc) {
+ part->readfunc = readfunc;
+ part->seekfunc = seekfunc;
+ part->freefunc = freefunc;
+ part->arg = arg;
+ part->datasize = datasize;
+ part->kind = MIMEKIND_CALLBACK;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part content from subparts. */
+CURLcode curl_mime_subparts(struct Curl_mimepart *part,
+ struct Curl_mime *subparts)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* Accept setting twice the same subparts. */
+ if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts)
+ return CURLE_OK;
+
+ cleanup_part_content(part);
+
+ if(subparts) {
+ /* Must belong to the same data handle. */
+ if(part->easy && subparts->easy && part->easy != subparts->easy)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* Should not have been attached already. */
+ if(subparts->parent)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ subparts->parent = part;
+ part->readfunc = mime_subparts_read;
+ part->seekfunc = mime_subparts_seek;
+ part->freefunc = mime_subparts_free;
+ part->arg = subparts;
+ part->datasize = -1;
+ part->kind = MIMEKIND_MULTIPART;
+ }
+
+ return CURLE_OK;
+}
+
+
+/* Readback from top mime. */
+/* Argument is the dummy top part. */
+size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
+{
+ struct Curl_mimepart *part = (struct Curl_mimepart *) instream;
+
+ (void) size; /* Always 1. */
+ return readback_part(part, buffer, nitems);
+}
+
+/* Rewind mime stream. */
+CURLcode Curl_mime_rewind(struct Curl_mimepart *part)
+{
+ return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
+ CURLE_OK: CURLE_SEND_FAIL_REWIND;
+}
+
+/* Compute header list size. */
+static size_t slist_size(struct curl_slist *s,
+ size_t overhead, const char *skip)
+{
+ size_t size = 0;
+ size_t skiplen = skip? strlen(skip): 0;
+
+ for(; s; s = s->next)
+ if(!skip || !match_header(s, skip, skiplen))
+ size += strlen(s->data) + overhead;
+ return size;
+}
+
+/* Get/compute multipart size. */
+static curl_off_t multipart_size(struct Curl_mime *mime)
+{
+ curl_off_t size;
+ curl_off_t sz;
+ size_t boundarysize;
+ struct Curl_mimepart *part;
+
+ if(!mime)
+ return 0; /* Not present -> empty. */
+
+ boundarysize = 4 + strlen(mime->boundary) + 2;
+ size = boundarysize; /* Final boundary - CRLF after headers. */
+
+ for(part = mime->firstpart; part; part = part->nextpart) {
+ sz = Curl_mime_size(part);
+
+ if(sz < 0)
+ size = sz;
+
+ if(size >= 0)
+ size += boundarysize + sz;
+ }
+
+ return size;
+}
+
+/* Get/compute mime size. */
+curl_off_t Curl_mime_size(struct Curl_mimepart *part)
+{
+ curl_off_t size;
+
+ if(part->datasize < 0 && part->kind == MIMEKIND_MULTIPART)
+ part->datasize = multipart_size(part->arg);
+
+ size = part->datasize;
+ if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) {
+ /* Compute total part size. */
+ size += slist_size(part->curlheaders, 2, NULL);
+ size += slist_size(part->userheaders, 2, "Content-Type");
+ size += 2; /* CRLF after headers. */
+ }
+ return size;
+}
+
+/* Add a header. */
+/* VARARGS2 */
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+{
+ struct curl_slist *hdr = NULL;
+ char *s = NULL;
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = curl_mvaprintf(fmt, ap);
+ va_end(ap);
+
+ if(s) {
+ hdr = Curl_slist_append_nodup(*slp, s);
+ if(hdr)
+ *slp = hdr;
+ else
+ free(s);
+ }
+
+ return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY;
+}
+
+/* Add a content type header. */
+static CURLcode add_content_type(struct curl_slist **slp,
+ const char *type, const char *boundary)
+{
+ return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type,
+ boundary? "; boundary=": "",
+ boundary? boundary: "");
+}
+
+
+static const char *ContentTypeForFilename(const char *filename)
+{
+ unsigned int i;
+
+ /*
+ * If no content type was specified, we scan through a few well-known
+ * extensions and pick the first we match!
+ */
+ struct ContentType {
+ const char *extension;
+ const char *type;
+ };
+ static const struct ContentType ctts[] = {
+ {".gif", "image/gif"},
+ {".jpg", "image/jpeg"},
+ {".jpeg", "image/jpeg"},
+ {".png", "image/png"},
+ {".svg", "image/svg+xml"},
+ {".txt", "text/plain"},
+ {".htm", "text/html"},
+ {".html", "text/html"},
+ {".pdf", "application/pdf"},
+ {".xml", "application/xml"}
+ };
+
+ if(filename) {
+ size_t len1 = strlen(filename);
+ const char *nameend = filename + len1;
+
+ for(i = 0; i < sizeof ctts / sizeof ctts[0]; i++) {
+ size_t len2 = strlen(ctts[i].extension);
+
+ if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
+ return ctts[i].type;
+ }
+ }
+ return NULL;
+}
+
+CURLcode Curl_mime_prepare_headers(struct Curl_mimepart *part,
+ const char *contenttype,
+ const char *disposition,
+ enum mimestrategy strategy)
+{
+ struct Curl_mime *mime = NULL;
+ const char *boundary = NULL;
+ char *s;
+ CURLcode ret = CURLE_OK;
+
+ /* Get rid of previously prepared headers. */
+ curl_slist_free_all(part->curlheaders);
+ part->curlheaders = NULL;
+
+ /* Be sure we won't access old headers later. */
+ if(part->state.state == MIMESTATE_CURLHEADERS)
+ mimesetstate(&mime->state, MIMESTATE_CURLHEADERS, NULL);
+
+ /* Build the content-type header. */
+ s = search_header(part->userheaders, "Content-Type");
+ if(s)
+ contenttype = s;
+ if(part->mimetype)
+ contenttype = part->mimetype;
+ if(!contenttype) {
+ switch(part->kind) {
+ case MIMEKIND_MULTIPART:
+ contenttype = MULTIPART_CONTENTTYPE_DEFAULT;
+ break;
+ case MIMEKIND_NAMEDFILE:
+ contenttype = ContentTypeForFilename(part->filename);
+ if(!contenttype)
+ contenttype = ContentTypeForFilename(part->data);
+ if(!contenttype && part->filename)
+ contenttype = FILE_CONTENTTYPE_DEFAULT;
+ break;
+ case MIMEKIND_FILE:
+ contenttype = ContentTypeForFilename(part->filename);
+ if(!contenttype && part->filename)
+ contenttype = FILE_CONTENTTYPE_DEFAULT;
+ break;
+ default:
+ contenttype = ContentTypeForFilename(part->filename);
+ break;
+ }
+ }
+
+ if(part->kind == MIMEKIND_MULTIPART) {
+ mime = (struct Curl_mime *) part->arg;
+ if(mime)
+ boundary = mime->boundary;
+ }
+ else if(contenttype && strcasecompare(contenttype, "text/plain"))
+ if(strategy == MIMESTRATEGY_MAIL || !part->filename)
+ contenttype = NULL;
+
+ /* Issue content-disposition header only if not already set by caller. */
+ if(!search_header(part->userheaders, "Content-Disposition")) {
+ if(!disposition)
+ if(part->filename || part->name ||
+ (contenttype && !strncasecompare(contenttype, "multipart/", 10)))
+ disposition = DISPOSITION_DEFAULT;
+ if(disposition && curl_strequal(disposition, "attachment") &&
+ !part->name && !part->filename)
+ disposition = NULL;
+ if(disposition) {
+ char *name = NULL;
+ char *filename = NULL;
+
+ if(part->name) {
+ name = escape_string(part->name, part->namesize);
+ if(!name)
+ ret = CURLE_OUT_OF_MEMORY;
+ }
+ if(!ret && part->filename) {
+ filename = escape_string(part->filename, -1);
+ if(!filename)
+ ret = CURLE_OUT_OF_MEMORY;
+ }
+ if(!ret)
+ ret = Curl_mime_add_header(&part->curlheaders,
+ "Content-Disposition: %s%s%s%s%s%s%s",
+ disposition,
+ name? "; name=\"": "",
+ name? name: "",
+ name? "\"": "",
+ filename? "; filename=\"": "",
+ filename? filename: "",
+ filename? "\"": "");
+ Curl_safefree(name);
+ Curl_safefree(filename);
+ if(ret)
+ return ret;
+ }
+ }
+
+ /* Issue Content-Type header. */
+ if(contenttype) {
+ ret = add_content_type(&part->curlheaders, contenttype, boundary);
+ if(ret)
+ return ret;
+ }
+
+ /* Content-Transfer-Encoding header. */
+ if(contenttype && strategy == MIMESTRATEGY_MAIL &&
+ part->kind != MIMEKIND_MULTIPART &&
+ !search_header(part->userheaders, "Content-Transfer-Encoding")) {
+ ret = Curl_mime_add_header(&part->curlheaders,
+ "Content-Transfer-Encoding: 8bit");
+ if(ret)
+ return ret;
+ }
+
+ /* If we were reading curl-generated headers, restart with new ones (this
+ should not occur). */
+ if(part->state.state == MIMESTATE_CURLHEADERS)
+ mimesetstate(&mime->state, MIMESTATE_CURLHEADERS, part->curlheaders);
+
+ /* Process subparts. */
+ if(part->kind == MIMEKIND_MULTIPART && mime) {
+ struct Curl_mimepart *subpart;
+
+ disposition = NULL;
+ if(strcasecompare(contenttype, "multipart/form-data"))
+ disposition = "form-data";
+ for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
+ ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy);
+ if(ret)
+ return ret;
+ }
+ }
+ return ret;
+}
+
+#else /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
+
+/* Mime not compiled in: define stubs for externally-referenced functions. */
+curl_mime *curl_mime_init(CURL *easy)
+{
+ (void) easy;
+ return NULL;
+}
+
+void curl_mime_free(curl_mime *mime)
+{
+ (void) mime;
+}
+
+curl_mimepart *curl_mime_addpart(curl_mime *mime)
+{
+ (void) mime;
+ return NULL;
+}
+
+CURLcode curl_mime_name(curl_mimepart *part,
+ const char *name, ssize_t namesize)
+{
+ (void) part;
+ (void) name;
+ (void) namesize;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
+{
+ (void) part;
+ (void) filename;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
+{
+ (void) part;
+ (void) mimetype;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_encoder(struct Curl_mimepart *part, const char *encoding)
+{
+ (void) part;
+ (void) encoding;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_data(curl_mimepart *part,
+ const char *data, ssize_t datasize)
+{
+ (void) part;
+ (void) data;
+ (void) datasize;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_mime_file(curl_mimepart *part, FILE *fp, int closewhendone)
+{
+ (void) part;
+ (void) fp;
+ (void) closewhendone;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
+{
+ (void) part;
+ (void) filename;
+ return CURLE_NOT_BUILT_IN;
+}
+
+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)
+{
+ (void) part;
+ (void) datasize;
+ (void) readfunc;
+ (void) seekfunc;
+ (void) freefunc;
+ (void) arg;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
+{
+ (void) part;
+ (void) subparts;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_headers(curl_mimepart *part,
+ struct curl_slist *headers, int take_ownership)
+{
+ (void) part;
+ (void) headers;
+ (void) take_ownership;
+ return CURLE_NOT_BUILT_IN;
+}
+
+void Curl_mime_initpart(struct Curl_mimepart *part, struct Curl_easy *easy)
+{
+ (void) part;
+ (void) data;
+}
+
+void Curl_mime_cleanpart(struct Curl_mimepart *part)
+{
+ (void) part;
+}
+
+CURLcode Curl_mime_prepare_headers(struct Curl_mimepart *part,
+ const char *contenttype,
+ const char *disposition,
+ enum mimestrategy strategy)
+{
+ (void) part;
+ (void) contenttype;
+ (void) disposition;
+ (void) strategy;
+ return CURLE_NOT_BUILT_IN;
+}
+
+curl_off_t Curl_mime_size(struct Curl_mimepart *part)
+{
+ (void) part;
+ return (curl_off_t) -1;
+}
+
+size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
+{
+ (void) buffer;
+ (void) size;
+ (void) nitems;
+ (void) instream;
+ return 0;
+}
+
+CURLcode Curl_mime_rewind(struct Curl_mimepart *part)
+{
+ (void) part;
+ return CURLE_NOT_BUILT_IN;
+}
+
+/* VARARGS2 */
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+{
+ (void) slp;
+ (void) fmt;
+ return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
diff --git a/lib/mime.h b/lib/mime.h
new file mode 100644
index 000000000..4d52b72fb
--- /dev/null
+++ b/lib/mime.h
@@ -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 */
diff --git a/lib/non-ascii.c b/lib/non-ascii.c
index 0a768bd91..b8ddb02e5 100644
--- a/lib/non-ascii.c
+++ b/lib/non-ascii.c
@@ -319,29 +319,4 @@ void Curl_convert_close(struct Curl_easy *data)
#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 */
diff --git a/lib/non-ascii.h b/lib/non-ascii.h
index e27f1f41f..5fb5771e0 100644
--- a/lib/non-ascii.h
+++ b/lib/non-ascii.h
@@ -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
* 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);
CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
char *buffer, size_t length);
-CURLcode Curl_convert_form(struct Curl_easy *data, struct FormData *form);
#else
#define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK)
#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_from_network(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 /* HEADER_CURL_NON_ASCII_H */
diff --git a/lib/smtp.c b/lib/smtp.c
index 4e2c3361e..67dc04f28 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -67,6 +67,7 @@
#include "transfer.h"
#include "escape.h"
#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "mime.h"
#include "socks.h"
#include "smtp.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 */
- 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);
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 */
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
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.
@@ -1249,7 +1280,7 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
smtp->rcpt = data->set.mail_rcpt;
/* 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 */
result = smtp_perform_mail(conn);
else
diff --git a/lib/transfer.c b/lib/transfer.c
index e7625ef77..4c160ef7e 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -73,6 +73,7 @@
#include "connect.h"
#include "non-ascii.h"
#include "http2.h"
+#include "mime.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -241,6 +242,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
CURLcode Curl_readrewind(struct connectdata *conn)
{
struct Curl_easy *data = conn->data;
+ struct Curl_mimepart *mimepart = &data->set.mimepost;
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
CURLOPT_HTTPPOST, call app to rewind
*/
- if(data->set.postfields ||
- (data->set.httpreq == HTTPREQ_POST_FORM))
+ if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
+ struct HTTP *http = data->req.protop;
+
+ if(http->sendit)
+ mimepart = http->sendit;
+ }
+ if(data->set.postfields)
; /* 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 {
if(data->set.seek_func) {
int err;
@@ -1826,7 +1840,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
* can be overridden with CURLOPT_POSTREDIR.
*/
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)) {
infof(data, "Switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
@@ -1850,7 +1865,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
* can be overridden with CURLOPT_POSTREDIR.
*/
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)) {
infof(data, "Switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
diff --git a/lib/url.c b/lib/url.c
index 296951059..9799096a4 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -71,6 +71,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "netrc.h"
#include "formdata.h"
+#include "mime.h"
#include "vtls/vtls.h"
#include "hostip.h"
#include "transfer.h"
@@ -479,6 +480,8 @@ CURLcode Curl_close(struct Curl_easy *data)
Curl_http2_cleanup_dependencies(data);
Curl_convert_close(data);
+ Curl_mime_cleanpart(&data->set.mimepost);
+
/* No longer a dirty share, if it exists */
if(data->share) {
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;
}
+ Curl_mime_initpart(&data->set.mimepost, data);
+
data->state.headerbuff = malloc(HEADERSIZE);
if(!data->state.headerbuff) {
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 */
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:
/*
* String to set in the HTTP Referer: field.
@@ -1875,13 +1893,13 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
break;
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);
break;
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);
break;
diff --git a/lib/urldata.h b/lib/urldata.h
index ae73ba66a..3f087a588 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -103,6 +103,7 @@
#include "hash.h"
#include "splay.h"
+#include "mime.h"
#include "imap.h"
#include "pop3.h"
#include "smtp.h"
@@ -1140,6 +1141,7 @@ typedef enum {
HTTPREQ_GET,
HTTPREQ_POST,
HTTPREQ_POST_FORM, /* we make a difference internally */
+ HTTPREQ_POST_MIME, /* we make a difference internally */
HTTPREQ_PUT,
HTTPREQ_HEAD,
HTTPREQ_OPTIONS,
@@ -1557,7 +1559,8 @@ struct UserDefined {
curl_off_t set_resume_from; /* continue [ftp] transfer from here */
struct curl_slist *headers; /* linked list of extra 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 cookiesession; /* new cookie session? */
bool crlf; /* convert crlf on ftp upload(?) */
diff --git a/tests/libtest/mk-lib1521.pl b/tests/libtest/mk-lib1521.pl
index f5c68eb10..fe7e65d7a 100644
--- a/tests/libtest/mk-lib1521.pl
+++ b/tests/libtest/mk-lib1521.pl
@@ -146,6 +146,7 @@ int test(char *URL)
char *stringpointerextra=(char *)"moooo";
struct curl_slist *slist=NULL;
struct curl_httppost *httppost=NULL;
+ curl_mime *mimepost = NULL;
FILE *stream = stderr;
struct data object;
char *charp;
@@ -216,6 +217,9 @@ while(<STDIN>) {
elsif($name eq "HTTPPOST") {
print "${pref} httppost);\n$check";
}
+ elsif($name eq "MIMEPOST") {
+ print "${pref} mimepost);\n$check";
+ }
elsif($name eq "STDERR") {
print "${pref} stream);\n$check";
}