#### 3.5.5 Composing Multipart Messages ¶

Multipart messages (or MIME, for short) can be used to send text in character sets other than ASCII, attach non-text files, send multiple parts in alternative formats, etc.

Technically speaking, the boolean variable `mime` controls this feature. If it is set (see Setting and Unsetting the Variables), `MIME` will create MIME messages by default. The variable can be set in the global or user configuration file (see Personal and System-wide Configuration Files), using the following command:

    set mime

It can also be set from the command line, using the `--mime` option.

GNU `mail` automatically turns on the MIME mode, when it is requested to send a non-plaintext message, or a message in character set other than ASCII, when the encoding is specified, or when attachments are given.

To send a message in another character set, specify it with the `--content-type` option:

    mail --content-type 'text/plain; charset=utf-8'

The `--encoding` specifies the encoding to use:

    mail --content-type 'text/plain; charset=utf-8' --encoding=base64

Its argument is any encoding supported by GNU mailutils. The two most often used encodings are ‘`base64`’ and ‘`quoted-printable`’.

To specify the charset from `mail` interactive section, enable the “edit headers” mode (`set editheaders`) and add the needed `Content-Type` header manually.

GNU `mail` also gives you a possibility to attach files to the message being sent.

The simplest way to attach a file from command line is by using the `--attach` (`-A`) option. Its argument specifies the file to attach. For example, the following will attach the content of the file `archive.tar`:

    $ mail --attach=archive.tar

By default, the content type will be set to ‘`application/octet-stream`’, and the attachment will be encoded using the ‘`base64`’ encoding. To change the content type, use the `--content-type` option. For example, to send an HTML attachment:

    $ mail --content-type=text/html --attach=in.html

The `--content-type` option affects all `--attach` options that follow it, and the message body (if any). To change the content type, simply add another `--content-type` option. For example, to send both the HTML file and the archive:

    $ mail --content-type=text/html --attach=in.html \
           --content-type=application/x-tar --attach=archive.tar

To change the content type of the message body when sending a message with attachments, use the trailing `--content-type` option, i.e. the option not followed by another `--attach` option:

    $ mail --content-type=text/html --attach=in.html \
           --content-type=application/x-tar --attach=archive.tar \
           --content-type=text/plain

This example adds two attachments with different content types and switched back to the ‘`text/plain`’ content type for the message body.

The encoding to use is set up by the `--encoding` option. As well as `--content-type`, this option affects all attachments supplied after it in the command line as well as the message body read from the standard input, until changed by the eventual next instance of the same option. Extending the above example:

    $ mail --content-type=text/html --encoding=quoted-printable \
           --attach=in.html \
           --content-type=application/x-tar --encoding=base64 \
           --attach=archive.tar

A trailing `--encoding` option sets the encoding of the message body.

Each attachment can also be assigned a description and a file name. Normally, these are the same as the file name supplied with the `--attach` option. However, you can change either or both of them using the `--content-name` and `--content-filename`, correspondingly. Both of these options affect only the next `--attach` (or `--attach-fd`, see below) option.

By default, the message will be assigned the content type ‘`multipart/mixed`’. To change it to ‘`multipart/alternative`’, use the `--alternative` command line option. Using this option also sets the ‘`Content-Disposition`’ header of each attached message to ‘`inline`’.

All the examples above will enter the usual interactive shell, allowing you to compose the body of the message. If that’s not needed, the non-interactive use can be forced by redirecting `/dev/null` to the standard input, e.g.:

    $ mail --attach=archive.tar < /dev/null

This will normally produce a message saying:

    mail: Null message body; hope that's ok

To suppress this message, unset the ‘`nullbodymsg`’ variable, as shown in the example below:

    $ mail -E 'set nonullbodymsg' --attach=archive.tar < /dev/null

The option `--attach=-` forces `mail` to read the file to be attached from the standard input stream. This option disables the interactive mode and sets ‘`nonullbodymsg`’ implicitly, so that the above example can be rewritten as:

    $ mail --attach=- < archive.tar

Special option is provided to facilitate the use of `mail` in scripts. The `--attach-fd=N` instructs the program to read the data to be attached from the file descriptor N. The above example is equivalent to:

    $ mail --attach-fd=0 < archive.tar

Attachments created with this option have neither filename nor description set, so normally the use of `--content-name` and/or `--content-filename` is advised.

The option `--skip-empty-attachments` instructs `mail` to skip creating attachments that would have zero-size body. This option affects all attachments created by `--attach` and `--attach-fd` options appearing after it in the command line. It also affects the handling of the original message body. To cancel its effect, use the `--no-skip-empty-attachments` option.

Here are some examples illustrating how it works.

First, consider the following command line

    $ mail --attach=archive.tar </dev/null

Assume that `archive.tar` is not empty.

This will create a MIME message of two parts: the first part having ‘`text/html`’ type and empty body, and the second part of type ‘`application/octet-stream`’, with the content copied from the file `archive.tar`.

Now, if you do:

    $ mail --attach=archive.tar --skip-empty-attachments </dev/null

then the created MIME message will contain only one part: that containing `archive.tar`.

If the file `archive.tar` has zero length, the resulting archive will still contain the ‘`application/octet-stream`’ part of zero length. However, if you place the `--skip-empty-attachments` option before `--attach`, then the produced message will be empty.

The following Perl program serves as an example of using `mail` from a script to construct a MIME message on the fly. It scans all mounted file systems for executable files that have setuid or setgid bits set and reports the names of those files in separate attachments. Each attachment is named after the mountpoint it describes.

The script begins with the usual prologue stating the modules that will be used:

    #!/usr/bin/perl

    use strict;
    use autodie;

Then global variables are declared. The ‘`@rcpt`’ array contains the email addresses of the recipients:

    my @rcpt= 'root@example.com';

The ‘`@cmd`’ variable holds the `mail` command line. It will be augmented for each file system. The initial value is set as follows:

    my @cmd = ('mail',
               '-E set nonullbodymsg',
               '--content-type=text/plain');

The `find` utility will be used to locate the files. The script will start as many instances as there are mountpoints. Those instances will be run in parallel and their standard output streams will be connected to file descriptors passed to `mail` invocation in `--attach-fd` options.

The descriptors will be held in ‘`@fds`’ array. This will prevent them from being wiped out by the garbage collector. Furthermore, care should be taken to ensure that the `O_CLOEXEC` flag be not set for these descriptors. This sample script takes a simplistic approach: it instructs Perl not to close first 255 descriptors when executing another programs:

    my @fds;
    $^F = 255;

The following code obtains the list of mount points:

    open(my $in, '-|', 'mount -t nonfs,noproc,nosysfs,notmpfs');
    while (<$in>) {
        chomp;
        if (/^\S+ on (?<mpoint>.+) type (?<fstype>.+) /) {

For each mountpoint, the `find` command line is constructed and launched. The file descriptor is pushed to the ‘`@fds`’ array to prevent it from being collected by the garbage collector:

    	open(my $fd, '-|',
    	     "find $+{mpoint} -xdev -type f"
                 . " \\( -perm -u+x -o -perm -g+x -o -perm -o+x \\)"
                 . " \\( -perm -u+s -o -perm -g+s \\) -print");
    	push @fds, $fd;

Now, the `mail` command is instructed to create next attachment from that file descriptor:

            my $mpname = $+{mpoint};
            $mpname =~ tr{/}{%};
            push @cmd,
                 "--content-name=Set[ug]id files on $+{mpoint} (type $+{fstype})",
                 "--content-filename=$mpname.list",
                 '--attach-fd=' . fileno($fd);
        }
    }
    close $in;

Finally, the emails of the recipients are added to the command line, the standard input is closed to make sure `mail` won’t enter the interactive mode and the constructed command is executed:

    push @cmd, @rcpt;
    close STDIN;
    system(@cmd);