Chapter 74. Encoding MIME documents


Content transfer encoding
Constructing multipart MIME entities
Retrieving the encoded content

examples/mimeencoder.C is an example of creating encoded MIME entities. mimeencoder.C takes a list of filenames as parameters, and creates a list of MIME entities, one entity per file, with its contents; then creates a multipart MIME entity consisting of all the individual MIME entities.

MIME entities get constructed from a x::headersimpl<x::headersbase::lf_endl> or a x::headersimpl<x::headersbase::crlf_endl> object, and additional parameters that define the content of the entity. The selection of the x::headersimpl specialization determines whether MIME encoding uses LF or CRLF newline sequence for the encoded header and body portion of the MIME entity.

#include <x/mime/structured_content_header.H>
#include <x/mime/encoder.H>

std::string filename;

x::headersimpl<x::headersbase::lf_endl> headers;

x::mime::structured_content_header content_type(x::mime::filetype(x::mime::from_file(filename));

if (content_type.mime_content_type() == "text")
    content_type("charset", x::mime::filecharset(filename));

content_type.append(headers, x::mime::content_type);

    .rfc2231("filename", filename, "UTF-8")
    .append(headers, x::mime::structured_content_header::content_disposition);

x::mime::encoder section=
    x::mime::make_section(headers, x::mime::from_file(filename)));

Encoding a MIME entity begins by constructing structured Content-Type, and, perhaps, Content-Disposition MIME headers, with the help of x::mime::filetype() and x::mime::filecharset().

x::mime::filetype() takes a filename argument, and searches for a registered MIME type for that filename extension in mime.types. If the extension is not found, the MIME type gets heuristically derived from the contents of the file using libmagic, a.k.a. the file(1) command. Alternatively, x::mime::filetype() also takes a file descriptor in place of a filename, which does only the content file lookup.

For text MIME content, if the character set is not known otherwise, x::mime::filecharset() also derives it heuristically via libmagic.

x::mime::make_section() takes a constructed header parameter, that specifies a completed set of MIME headers, and a container that specifies the raw, unencoded contents of the MIME entity, and returns a x::mime::encoder. This is a reference to a reference-counted object that represents an encoded MIME entity.

x::mime::make_section()'s second parameter is any container that implements begin() and end() that define the beginning and the iterator for an input, forward, or a random access sequence of chars that give the unencoded contents of the MIME entity.

The container must be copy-constructible. x::mime::fromfile() supplies a suitable container that reads the contents of the MIME section from a file, specified as a filename or a file descriptor object.

std::copy(section->begin(), section->end(), std::ostreambuf_iterator<char>(std::cout));

x::mime::encoder's referenced object's begin() and end() return a x::mime::encoder::base::iterator, defining a beginning and an ending iterator for an input sequence of chars containing the headers followed by the encoded contents of the MIME section.

x::mime::make_section() does not read the entire contents of the MIME entity and encodes it, and gives back a x::mime::encoder that returns the iterators to the encoded sequence. x::mime::make_section() copies the headers parameter and the container object itself, into the the x::mime::encoder object. begin() and end() call the container's begin() and end(), and encode the object on the fly (together with preceding MIME headers).

In other words, a x::mime::fromfile referencing a ten megabyte file does not load the entire contents of the file into memory. The file gets opened by begin() and end(), and gets encoded on the fly.

A x::mime::fromfile specifying a file descriptor to a non-regular file, such as a pipe, results in x::mime::fromfile reading its entire contents into a temporary file. Specifying a filename simply opens the file, directly.

Content transfer encoding

x::mime::encoder section=
	  x::mime::make_section(headers, x::mime::from_file(filename), false, 255));

x::mime::make_section() selects the transfer encoding heuristically, by reading the container and selecting 7bit, 8bit, quoted-printable, or base64. x::mime::make_section() takes two optional parameters.

  • An optional bool parameter has a default value of false. Setting it to true disables 8bit, and forces either quoted-printable or base64, if 7bit does not work.

  • A maximum size of each text line has a default value of 76. Even if the MIME entity does not need quoted-printable or base64 otherwise, they get used if the MIME entity contains lines that exceed the text line size parameter, and the parameter specifies the maximum size of the lines in the encoded content, as well.

x::mime::encoder section=
	  x::mime::make_base64_section(headers, x::mime::from_file(filename)));

x::mime::encoder section=
	  x::mime::make_qp_section(headers, x::mime::from_file(filename)));

x::mime::make_base64_section() and x::mime::make_qp_section() explicitly construct a base64 or a quoted-printable-encoded MIME entity. An optional parameter gives the maximum line size of the encoded entity, defaulting to 76 characters per line.

x::mime::make_section(), x::mime::make_base64_section(), and x::mime::make_qp_section() supply the appropriate Content-Transfer-Encoding header, and this header should not be present in the x::headersimpl parameter. There's also a x::mime::make_plain_section() that constructs a x::mime::encoder that does not perform any MIME encoding, presuming that 7bit or 8bit works for the MIME entity. x::mime::make_plain_section() does not supply a Content-Transfer-Encoding header. If required, it needs to be added to the headers parameter.

x::headersimpl<x::headersbase::lf_endl> headers;

headers.append("Mime-Version", "1.0");
headers.append("Subject", "test");

auto section1=x::mime::make_section(headers, std::string("Hello world!\n"));


auto section2=x::mime::make_message_rfc822_section(headers, section1);

x::mime::make_message_rfc822_section() takes a previously-created encoder of an entire message, and constructs a message/rfc822 entity from it. x::mime::make_message_rfc822_section adds Content-Type: message/rfc822 header to any headers present in its headers parameter.