.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
unit of data. Each part has its own name and contents. You can in fact create
and post a multi-part formpost with the regular libcurl POST support described
above, but that would require that you build a formpost yourself and provide
-to libcurl. To make that easier, libcurl provides \fIcurl_formadd(3)\fP. Using
-this function, you add parts to the form. When you're done adding parts, you
-post the whole form.
+to libcurl. To make that easier, libcurl provides a MIME API consisting in
+several functions: using those, you can create and fill a multi-part form.
+Function \fIcurl_mime_init(3)\fP creates a multi-part body; you can then
+append new parts to a multi-part body using \fIcurl_mime_addpart(3)\fP.
+There are three possible data sources for a part: memory using
+\fIcurl_mime_data(3)\fP, file using \fIcurl_mime_filedata(3)\fP and
+user-defined data read callback using \fIcurl_mime_data_cb(3)\fP.
+\fIcurl_mime_name(3)\fP sets a part's (i.e.: form field) name, while
+\fIcurl_mime_filename(3)\fP fills in the remote file name. With
+\fIcurl_mime_type(3)\fP, you can tell the MIME type of a part,
+\fIcurl_mime_headers(3)\fP allows defining the part's headers. When a
+multi-part body is no longer needed, you can destroy it using
+\fIcurl_mime_free(3)\fP.
The following example sets two simple text parts with plain textual contents,
and then a file with binary contents and uploads the whole thing.
+.nf
+ curl_mime *multipart = curl_mime_init(easyhandle);
+ curl_mimepart *part = curl_mime_addpart(mutipart);
+ curl_mime_name(part, "name");
+ curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED);
+ part = curl_mime_addpart(mutipart);
+ curl_mime_name(part, "project");
+ curl_mime_data(part, "curl", CURL_ZERO_TERMINATED);
+ part = curl_mime_addpart(mutipart);
+ curl_mime_name(part, "logotype-image");
+ curl_mime_filedata(part, "curl.png");
+
+ /* Set the form info */
+ curl_easy_setopt(easyhandle, CURLOPT_MIMEPOST, multipart);
+
+ curl_easy_perform(easyhandle); /* post away! */
+
+ /* free the post data again */
+ curl_mime_free(multipart);
+.fi
+
+To post multiple files for a single form field, you must supply each file in
+a separate part, all with the same field name. Although function
+\fIcurl_mime_subparts(3)\fP implements nested muti-parts, this way of
+multiple files posting is deprecated by RFC 7578, chapter 4.3.
+
+To set the data source from an already opened FILE pointer, use:
+
+.nf
+ curl_mime_data_cb(part, filesize, fread, fseek, NULL, filepointer);
+.fi
+
+A deprecated \fIcurl_formadd(3)\fP function is still supported in libcurl.
+It should however not be used anymore for new designs and programs using it
+ought to be converted to the MIME API. It is however described here as an
+aid to conversion.
+
+Using \fIcurl_formadd\fP, you add parts to the form. When you're done adding
+parts, you post the whole form.
+
+The MIME API example above is expressed as follows using this function:
+
.nf
struct curl_httppost *post=NULL;
struct curl_httppost *last=NULL;
Just setting \fICURLOPT_POSTFIELDS(3)\fP to "" or NULL will *not* stop libcurl
from doing a POST. It will just make it POST without any data to send!
+.SH "Converting from deprecated form API to MIME API"
+Four rules have to be respected in building the multi-part:
+.br
+- The easy handle must be created before building the multi-part.
+.br
+- The multi-part is always created by a call to curl_mime_init(easyhandle).
+.br
+- Each part is created by a call to curl_mime_addpart(multipart).
+.br
+- When complete, the multi-part must be bound to the easy handle using
+\fICURLOPT_MIMEPOST(3)\fP instead of \fICURLOPT_HTTPPOST(3)\fP.
+
+Here are some example of \fIcurl_formadd\fP calls to MIME API sequences:
+
+.nf
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "id",
+ CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
+ CURLFORM_CONTENTHEADER, headers,
+ CURLFORM_END);
+.fi
+becomes:
+.nf
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "id");
+ curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED);
+ curl_mime_headers(part, headers, FALSE);
+.fi
+
+Setting the last \fIcurl_mime_headers\fP argument to TRUE would have caused
+the headers to be automatically released upon destroyed the multi-part, thus
+saving a clean-up call to \fPcurl_slist_free_all(3)\fP.
+
+.nf
+ curl_formadd(&post, &last,
+ CURLFORM_PTRNAME, "logotype-image",
+ CURLFORM_FILECONTENT, "-",
+ CURLFORM_END);
+.fi
+becomes:
+.nf
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "logotype-image");
+ curl_mime_data_cb(part, (curl_off_t) -1, fread, fseek, NULL, stdin);
+.fi
+
+\fIcurl_mime_name\fP always copies the field name. The special file name "-"
+is not supported by \fIcurl_mime_file\fP: to read an open file, use
+a callback source using fread(). The transfer will be chunked since the data
+size is unknown.
+
+.nf
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "datafile[]",
+ CURLFORM_FILE, "file1",
+ CURLFORM_FILE, "file2",
+ CURLFORM_END);
+.fi
+becomes:
+.nf
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "datafile[]");
+ curl_mime_filedata(part, "file1");
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "datafile[]");
+ curl_mime_filedata(part, "file2");
+.fi
+
+The deprecated multipart/mixed implementation of multiple files field is
+translated to two distinct parts with the same name.
+
+.nf
+ curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, myreadfunc);
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "stream",
+ CURLFORM_STREAM, arg,
+ CURLFORM_CONTENTLEN, (curl_off_t) datasize,
+ CURLFORM_FILENAME, "archive.zip",
+ CURLFORM_CONTENTTYPE, "application/zip",
+ CURLFORM_END);
+.fi
+becomes:
+.nf
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "stream");
+ curl_mime_data_cb(part, (curl_off_t) datasize,
+ myreadfunc, NULL, NULL, arg);
+ curl_mime_filename(part, "archive.zip");
+ curl_mime_type(part, "application/zip");
+.fi
+
+\fICURLOPT_READFUNCTION\fP callback is not used: it is replace by directly
+setting the part source data from the callback read function.
+
+.nf
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "memfile",
+ CURLFORM_BUFFER, "memfile.bin",
+ CURLFORM_BUFFERPTR, databuffer,
+ CURLFORM_BUFFERLENGTH, (long) sizeof databuffer,
+ CURLFORM_END);
+.fi
+becomes:
+.nf
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "memfile");
+ curl_mime_data(part, databuffer, (curl_off_t) sizeof databuffer);
+ curl_mime_filename(part, "memfile.bin");
+.fi
+
+\fIcurl_mime_data\fP always copies the initial data: data buffer is thus
+free for immediate reuse.
+
+.nf
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "message",
+ CURLFORM_FILECONTENT, "msg.txt",
+ CURLFORM_END);
+.fi
+becomes:
+.nf
+ part = curl_mime_addpart(multipart);
+ curl_mime_name(part, "message");
+ curl_mime_filedata(part, "msg.txt");
+ curl_mime_filename(part, NULL);
+.fi
+
+Use of \fIcurl_mime_filedata\fP sets the remote file name as a side effect: it
+is therefore necessary to clear it for \fICURLFORM_FILECONTENT\fP emulation.
+
.SH "Showing Progress"
For historical and traditional reasons, libcurl has a built-in progress meter
LPRT before trying PORT, as they work with more protocols. You can disable
this behavior by setting \fICURLOPT_FTP_USE_EPRT(3)\fP to zero.
+.SH "MIME API revisited for SMTP and IMAP"
+In addition to support HTTP multi-part form fields, the MIME API can be used
+to build structured e-mail messages and send them via SMTP or append such
+messages to IMAP directories.
+
+A structured e-mail message may contain several parts: some are displayed
+inline by the MUA, some are attachments. Parts can also be structured as
+multi-part, for example to include another e-mail message or to offer several
+text formats alternatives. This can be nested to any level.
+
+To build such a message, you prepare the nth-level multi-part and then include
+it as a source to the parent multi-part using function
+\fIcurl_mime_subparts(3)\fP. Once it has been
+bound to its parent multi-part, a nth-level multi-part belongs to it and
+should not be freed explicitly.
+
+E-mail messages data is not supposed to be non-ascii and line length is
+limited: fortunately, some transfer encodings are defined by the standards
+to support the transmission of such incompatible data. Function
+\fIcurl_mime_encoder(3)\fP tells a part that its source data must be encoded
+before being sent. It also generates the corresponding header for that part.
+If the part data you want to send is already encoded in such a scheme,
+do not use this function (this would over-encode it), but explicitly set the
+corresponding part header.
+
+Upon sending such a message, libcurl prepends it with the header list
+set with \fICURLOPT_HTTPHEADERS(3)\fP, as 0th-level mime part headers.
+
+Here is an example building an e-mail message with an inline plain/html text
+alternative and a file attachment encoded in base64:
+
+.nf
+ curl_mime *message = curl_mime_init(easyhandle);
+
+ /* The inline part is an alterative proposing the html and the text
+ versions of the e-mail. */
+ curl_mime *alt = curl_mime_init(easyhandle);
+
+ /* HTML message. */
+ curl_mimepart *part = curl_mime_addpart(alt);
+ curl_mime_data(part, "<html><body><p>This is HTML</p></body></html>",
+ CURL_ZERO_TERMINATED);
+ curl_mime_type(part, "text/html");
+
+ /* Text message. */
+ part = curl_mime_addpart(alt);
+ curl_mime_data(part, "This is plain text message",
+ CURL_ZERO_TERMINATED);
+
+ /* Create the inline part. */
+ part = curl_mime_addpart(message);
+ curl_mime_subparts(part, alt);
+ curl_mime_type(part, "multipart/alternative");
+ struct curl_slist *headers = curl_slist_append(NULL,
+ "Content-Disposition: inline");
+ curl_mime_headers(part, headers, TRUE);
+
+ /* Add the attachment. */
+ part = curl_mime_addpart(message);
+ curl_mime_filedata(part, "manual.pdf");
+ curl_mime_encoder(part, "base64");
+
+ /* Build the mail headers. */
+ headers = curl_slist_append(NULL, "From: me@example.com");
+ headers = curl_slist_append(headers, "To: you@example.com");
+
+ /* Set these into the easy handle. */
+ curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADERS, headers);
+ curl_easy_setopt(easyhandle, CURLOPT_MIMEPOST, mime);
+.fi
+
+It should be noted that appending a message to an IMAP directory requires
+the message size to be known prior upload. It is therefore not possible to
+include parts with unknown data size in this context.
+
.SH "Headers Equal Fun"
Some protocols provide "headers", meta-data separated from the normal