gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [gnurl] 126/256: mime: new MIME API.


From: gnunet
Subject: [GNUnet-SVN] [gnurl] 126/256: mime: new MIME API.
Date: Fri, 06 Oct 2017 19:43:37 +0200

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to branch master
in repository gnurl.

commit ce0881edee3c78609eae49665fb70264d8786d29
Author: Patrick Monnerat <address@hidden>
AuthorDate: Sat Sep 2 17:47:10 2017 +0100

    mime: new MIME API.
    
    Available in HTTP, SMTP and IMAP.
    Deprecates the FORM API.
    See CURLOPT_MIMEPOST.
    Lib code and associated documentation.
---
 docs/libcurl/Makefile.inc                          |    6 +-
 docs/libcurl/curl_easy_setopt.3                    |    2 +
 docs/libcurl/curl_formadd.3                        |   11 +-
 docs/libcurl/curl_formfree.3                       |    8 +-
 docs/libcurl/curl_formget.3                        |    7 +-
 .../{curl_formfree.3 => curl_mime_addpart.3}       |   36 +-
 docs/libcurl/curl_mime_data.3                      |   54 +
 docs/libcurl/curl_mime_data_cb.3                   |  154 ++
 docs/libcurl/curl_mime_filedata.3                  |   55 +
 docs/libcurl/curl_mime_filename.3                  |   51 +
 docs/libcurl/{curl_formfree.3 => curl_mime_free.3} |   28 +-
 .../{curl_formfree.3 => curl_mime_headers.3}       |   34 +-
 docs/libcurl/curl_mime_init.3                      |   68 +
 docs/libcurl/{curl_formfree.3 => curl_mime_name.3} |   36 +-
 .../{curl_formfree.3 => curl_mime_subparts.3}      |   36 +-
 docs/libcurl/curl_mime_type.3                      |   62 +
 docs/libcurl/opts/CURLOPT_HTTPPOST.3               |   11 +-
 .../{curl_formfree.3 => opts/CURLOPT_MIMEPOST.3}   |   42 +-
 docs/libcurl/opts/Makefile.inc                     |    1 +
 docs/libcurl/symbols-in-versions                   |    1 +
 include/curl/curl.h                                |  130 ++
 include/curl/typecheck-gcc.h                       |    6 +
 lib/Makefile.inc                                   |    5 +-
 lib/formdata.c                                     |  845 ++---------
 lib/formdata.h                                     |   54 +-
 lib/http.c                                         |  259 ++--
 lib/http.h                                         |    4 +-
 lib/http2.c                                        |    1 +
 lib/imap.c                                         |   48 +-
 lib/mime.c                                         | 1497 ++++++++++++++++++++
 lib/mime.h                                         |  119 ++
 lib/non-ascii.c                                    |   25 -
 lib/non-ascii.h                                    |    4 +-
 lib/smtp.c                                         |   37 +-
 lib/transfer.c                                     |   24 +-
 lib/url.c                                          |   22 +-
 lib/urldata.h                                      |    5 +-
 tests/libtest/mk-lib1521.pl                        |    4 +
 38 files changed, 2740 insertions(+), 1052 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, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, 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, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, 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, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, 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_formfree.3 b/docs/libcurl/curl_mime_addpart.3
similarity index 54%
copy from docs/libcurl/curl_formfree.3
copy to docs/libcurl/curl_mime_addpart.3
index a2536cd9e..4a86f3813 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/curl_mime_addpart.3
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, et al.
 .\" *
 .\" * This software is licensed as described in the file COPYING, which
 .\" * you should have received as part of this distribution. The terms
@@ -19,26 +19,34 @@
 .\" * KIND, either express or implied.
 .\" *
 .\" **************************************************************************
-.TH curl_formfree 3 "6 April 2001" "libcurl 7.7.1" "libcurl Manual"
+.TH curl_mime_addpart 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
 .SH NAME
-curl_formfree - free a previously build multipart/formdata HTTP POST chain
+curl_mime_addpart - append a new empty part to a mime structure
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
-.BI "void curl_formfree(struct curl_httppost *" form);
+.BI "curl_mimepart * curl_mime_addpart(curl_mime * " mime ");"
 .ad
 .SH DESCRIPTION
-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.
+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.
 
-The pointer to free is the same pointer you passed to the
-\fICURLOPT_HTTPPOST(3)\fP option, which is the \fIfirstitem\fP pointer from
-the \fIcurl_formadd(3)\fP invoke(s).
+\fImime\fP is the handle of the mime structure in which the new part must be
+appended.
 
-\fBform\fP is the pointer as returned from a previous call to
-\fIcurl_formadd(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
+A mime part structure handle, or NULL upon failure.
+
 .SH "SEE ALSO"
-.BR curl_formadd "(3) "
+.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, <address@hidden>, 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, <address@hidden>, 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, <address@hidden>, 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, <address@hidden>, 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_formfree.3 b/docs/libcurl/curl_mime_free.3
similarity index 58%
copy from docs/libcurl/curl_formfree.3
copy to docs/libcurl/curl_mime_free.3
index a2536cd9e..87fea127b 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/curl_mime_free.3
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, et al.
 .\" *
 .\" * This software is licensed as described in the file COPYING, which
 .\" * you should have received as part of this distribution. The terms
@@ -19,26 +19,30 @@
 .\" * KIND, either express or implied.
 .\" *
 .\" **************************************************************************
-.TH curl_formfree 3 "6 April 2001" "libcurl 7.7.1" "libcurl Manual"
+.TH curl_mime_free 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
 .SH NAME
-curl_formfree - free a previously build multipart/formdata HTTP POST chain
+curl_mime_free - free a previously built mime structure
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
-.BI "void curl_formfree(struct curl_httppost *" form);
+.BI "void curl_mime_free(curl_mime *" mime);
 .ad
 .SH DESCRIPTION
-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
+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 pointer to free is the same pointer you passed to the
-\fICURLOPT_HTTPPOST(3)\fP option, which is the \fIfirstitem\fP pointer from
-the \fIcurl_formadd(3)\fP invoke(s).
+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.
 
-\fBform\fP is the pointer as returned from a previous call to
-\fIcurl_formadd(3)\fP and may be NULL.
+\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_formadd "(3) "
+.BR curl_mime_init "(3)"
diff --git a/docs/libcurl/curl_formfree.3 b/docs/libcurl/curl_mime_headers.3
similarity index 53%
copy from docs/libcurl/curl_formfree.3
copy to docs/libcurl/curl_mime_headers.3
index a2536cd9e..368c4d26c 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/curl_mime_headers.3
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, et al.
 .\" *
 .\" * This software is licensed as described in the file COPYING, which
 .\" * you should have received as part of this distribution. The terms
@@ -19,26 +19,32 @@
 .\" * KIND, either express or implied.
 .\" *
 .\" **************************************************************************
-.TH curl_formfree 3 "6 April 2001" "libcurl 7.7.1" "libcurl Manual"
+.TH curl_mime_headers 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
 .SH NAME
-curl_formfree - free a previously build multipart/formdata HTTP POST chain
+curl_mime_headers - set a mime part's custom headers
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
-.BI "void curl_formfree(struct curl_httppost *" form);
+.BI "CURLcode curl_mime_headers(curl_mimepart * " part ,
+.BI "struct curl_slist * " headers ", int " take_ownership ");"
 .ad
 .SH DESCRIPTION
-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.
+curl_mime_headers() sets a mime part's custom headers.
 
-The pointer to free is the same pointer you passed to the
-\fICURLOPT_HTTPPOST(3)\fP option, which is the \fIfirstitem\fP pointer from
-the \fIcurl_formadd(3)\fP invoke(s).
+\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.
 
-\fBform\fP is the pointer as returned from a previous call to
-\fIcurl_formadd(3)\fP and may be NULL.
+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
-None
+CURLE_OK or a CURL error code upon failure.
+
 .SH "SEE ALSO"
-.BR curl_formadd "(3) "
+.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, <address@hidden>, 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_formfree.3 b/docs/libcurl/curl_mime_name.3
similarity index 51%
copy from docs/libcurl/curl_formfree.3
copy to docs/libcurl/curl_mime_name.3
index a2536cd9e..5ef7c887f 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/curl_mime_name.3
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, et al.
 .\" *
 .\" * This software is licensed as described in the file COPYING, which
 .\" * you should have received as part of this distribution. The terms
@@ -19,26 +19,34 @@
 .\" * KIND, either express or implied.
 .\" *
 .\" **************************************************************************
-.TH curl_formfree 3 "6 April 2001" "libcurl 7.7.1" "libcurl Manual"
+.TH curl_mime_name 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
 .SH NAME
-curl_formfree - free a previously build multipart/formdata HTTP POST chain
+curl_mime_name - set a mime part's name
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
-.BI "void curl_formfree(struct curl_httppost *" form);
+.BI "CURLcode curl_mime_name(curl_mimepart * " part ", const char * " name
+.BI ", ssize_t " namesize ");"
 .ad
 .SH DESCRIPTION
-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.
+curl_mime_name() sets a mime part's name. This is the way HTTP form fields are
+named.
 
-The pointer to free is the same pointer you passed to the
-\fICURLOPT_HTTPPOST(3)\fP option, which is the \fIfirstitem\fP pointer from
-the \fIcurl_formadd(3)\fP invoke(s).
+\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.
 
-\fBform\fP is the pointer as returned from a previous call to
-\fIcurl_formadd(3)\fP and may be NULL.
+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
-None
+CURLE_OK or a CURL error code upon failure.
+
 .SH "SEE ALSO"
-.BR curl_formadd "(3) "
+.BR curl_mime_addpart "(3)"
diff --git a/docs/libcurl/curl_formfree.3 b/docs/libcurl/curl_mime_subparts.3
similarity index 51%
copy from docs/libcurl/curl_formfree.3
copy to docs/libcurl/curl_mime_subparts.3
index a2536cd9e..3c607c586 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/curl_mime_subparts.3
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, et al.
 .\" *
 .\" * This software is licensed as described in the file COPYING, which
 .\" * you should have received as part of this distribution. The terms
@@ -19,26 +19,34 @@
 .\" * KIND, either express or implied.
 .\" *
 .\" **************************************************************************
-.TH curl_formfree 3 "6 April 2001" "libcurl 7.7.1" "libcurl Manual"
+.TH curl_mime_subparts 3 "22 August 2017" "libcurl 7.56.0" "libcurl Manual"
 .SH NAME
-curl_formfree - free a previously build multipart/formdata HTTP POST chain
+curl_mime_subparts - set subparts of a multipart mime part
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
-.BI "void curl_formfree(struct curl_httppost *" form);
+.BI "CURLcode curl_mime_subparts(curl_mimepart * " part ,
+.BI "curl_mime * " subparts ");"
 .ad
 .SH DESCRIPTION
-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.
+curl_mime_subparts() sets a multipart mime part's content from a mime
+structure.
 
-The pointer to free is the same pointer you passed to the
-\fICURLOPT_HTTPPOST(3)\fP option, which is the \fIfirstitem\fP pointer from
-the \fIcurl_formadd(3)\fP invoke(s).
+\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.
 
-\fBform\fP is the pointer as returned from a previous call to
-\fIcurl_formadd(3)\fP and may be NULL.
+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
-None
+CURLE_OK or a CURL error code upon failure.
+
 .SH "SEE ALSO"
-.BR curl_formadd "(3) "
+.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, <address@hidden>, 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, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, 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/curl_formfree.3 b/docs/libcurl/opts/CURLOPT_MIMEPOST.3
similarity index 53%
copy from docs/libcurl/curl_formfree.3
copy to docs/libcurl/opts/CURLOPT_MIMEPOST.3
index a2536cd9e..a3a964f7f 100644
--- a/docs/libcurl/curl_formfree.3
+++ b/docs/libcurl/opts/CURLOPT_MIMEPOST.3
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <address@hidden>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, et al.
 .\" *
 .\" * This software is licensed as described in the file COPYING, which
 .\" * you should have received as part of this distribution. The terms
@@ -19,26 +19,34 @@
 .\" * KIND, either express or implied.
 .\" *
 .\" **************************************************************************
-.TH curl_formfree 3 "6 April 2001" "libcurl 7.7.1" "libcurl Manual"
+.\"
+.TH CURLOPT_MIMEPOST 3 "22 Aug 2017" "libcurl 7.56.0" "curl_easy_setopt 
options"
 .SH NAME
-curl_formfree - free a previously build multipart/formdata HTTP POST chain
+CURLOPT_MIMEPOST \- set post/send data from mime structure
 .SH SYNOPSIS
-.B #include <curl/curl.h>
-.sp
-.BI "void curl_formfree(struct curl_httppost *" form);
-.ad
+.nf
+#include <curl/curl.h>
+
+curl_mime *mime;
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MIMEPOST, mime);
 .SH DESCRIPTION
-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.
+Pass a mime handle previously obtained from \fIcurl_mime_init()\fP.
 
-The pointer to free is the same pointer you passed to the
-\fICURLOPT_HTTPPOST(3)\fP option, which is the \fIfirstitem\fP pointer from
-the \fIcurl_formadd(3)\fP invoke(s).
+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.
 
-\fBform\fP is the pointer as returned from a previous call to
-\fIcurl_formadd(3)\fP and may be NULL.
+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
-None
+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_formadd "(3) "
+.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, <address@hidden>, et al.
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, 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, <address@hidden>, 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, <address@hidden>, 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, <address@hidden>, et al.
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <address@hidden>, 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";
             }

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]