Commit 7e05a160 7e05a160e2e135006ce612881080d99ed34d4a77 by Sergey Poznyakoff

New option --mime

* mail/mail.c (mime_option): New variable.
(main): assume --mime if either or both of --content-type and
--content-encoding are set.  Set the mime variable if so.
* mail/mail.h (mailvar_is_true): New prototype.
* mail/send.c (add_attachments): Continue if the mime variable
is set.
(add_body): Rewrite, treating the text read from the stdin as
MIME part in itself.
* mailvar.c (mailvar_tab): New variable "mime"
(mailvar_is_true): New function.

* NEWS: Document --mime
* doc/texinfo/programs.texi: Rewrite the Attachments subsection.
1 parent ff1a8a6e
GNU mailutils NEWS -- history of user-visible changes. 2017-04-13
GNU mailutils NEWS -- history of user-visible changes. 2017-04-19
Copyright (C) 2002-2017 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -90,6 +90,20 @@ defined. Instead, the following constants are defined in config.h:
* mail: sending multipart messages
** New option --mime
This option instructs mail to compose output messages in MIME format.
The options --content-type and --encoding turn this option on. As a
side effect, both --content-type and --encoding now affect the message
body read from the standard input as well.
** New variable 'mime'
The 'mime' variable, if set instructs mail to compose output messages
in MIME format. In fact, the '--mime' option is equivalent to
'-E set mime', except that it takes effect after all options are
processed.
** New option --alternative
When used with --attach or --attach-fd options, this option sets the
......
......@@ -2987,7 +2987,7 @@ Configuration Files}, for a detailed description of their format.
* Invoking Mail:: Command Line Options.
* Specifying Messages:: How to Specify Message Sets.
* Composing Mail:: Composing Mail.
* Attachments:: Attaching Files.
* MIME:: How to Attach Files.
* Reading Mail:: Reading Mail.
* Scripting:: Scripting.
* Mail Variables:: How to Alter the Behavior of @command{mail}.
......@@ -3084,6 +3084,14 @@ Print header summary to stdout and exit.
@itemx --ignore
Ignore interrupts when composing the message.
@item -M
@itemx --mime
@itemx --no-mime
The @option{--mime} option instructs @command{mail} to compose MIME
messages. It is equivalent for @option{-E 'set mime'}, except that it
is processed after all other options. The @option{--no-mime} disables
the MIME compose mode, and is a shortcut for @option{-E 'set nomime'},
@item -N
@itemx --nosum
Do not display initial header summary.
......@@ -3522,8 +3530,53 @@ the old contents of your message.
@c *********************************************************************
@node Attachments
@subsection Sending Attachments
@node MIME
@subsection Composing Multipart Messages
Multipart messages (or MIME, for short) can be used to send text in
character set other than ASCII, attach non-text files, send multiple
parts in alternative formats, etc.
Technically speaking, the boolean variable @code{mime}
controls this feature. If it is set (@pxref{Setting and Unsetting
the Variables}), @command{MIME} will create MIME messages by default.
The variable can be set in the global or user configuration file
(@pxref{Mail Configuration Files}), using the following command:
@example
set mime
@end example
It can also be set from the command line, using the @option{--mime}
option.
GNU @command{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
@option{--content-type} option:
@example
mail --content-type 'text/plain; charset=utf-8'
@end example
The @option{--encoding} specifies the encoding to use:
@example
mail --content-type 'text/plain; charset=utf-8' --encoding=base64
@end example
Its argument is any encoding supported by GNU mailutils. The two most
often used encodings are @samp{base64} and @samp{quoted-printable}.
To specify the charset from @command{mail} interactive section, enable
the ``edit headers'' mode (@code{set editheaders}) and add the
needed @code{Content-Type} header manually.
GNU @command{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
@option{--attach} (@option{-A}) option. Its argument specifies the
......@@ -3545,9 +3598,9 @@ $ mail --content-type=text/html --attach=in.html
@end example
The @option{--content-type} option affects all @option{--attach}
options that follow it. To change the content type, simply add
another @option{--content-type} option. For example, to send both
the HTML file and the archive:
options that follow it, and the message body (if any). To change the
content type, simply add another @option{--content-type} option. For
example, to send both the HTML file and the archive:
@example
$ mail --content-type=text/html --attach=in.html \
......@@ -3556,8 +3609,9 @@ $ mail --content-type=text/html --attach=in.html \
Similarly, the encoding to use is set up by the @option{--encoding}
option. As well as @option{--content-type}, this option affects all
attachments supplied after it in the command line, until changed by
the eventual next appearance of the same option. Extending the above
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:
@example
......@@ -5066,6 +5120,17 @@ set metamail
set metamail="metamail -m mail -p"
@end example
@kwindex mime
@item mime
@*Type: String
@*Default: Unset (false)
@vrindex mime, mail variable
If set, this variable instructs @command{mail} to compose MIME
messages.
It can be set from the command line using @option{--mime} option.
@kwindex mimenoask
@item mimenoask
@*Type: String
......
......@@ -36,6 +36,7 @@ int hint;
char *file;
char *user;
int mime_option;
int skip_empty_attachments;
char *default_encoding;
char *default_content_type;
......@@ -274,6 +275,10 @@ static struct mu_option mail_options[] = {
N_("attach from file descriptor FD"),
mu_c_string, NULL, cli_attach_fd },
{ "mime", 'M', NULL, MU_OPTION_DEFAULT,
N_("compose MIME messages"),
mu_c_bool, &mime_option },
MU_OPTION_END
}, *options[] = { mail_options, NULL };
......@@ -483,6 +488,11 @@ main (int argc, char **argv)
/* argument parsing */
mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv);
if (default_content_type || default_encoding)
mime_option = 1;
if (mime_option)
util_cache_command (&command_list, "set mime");
if (read_recipients)
{
argv += argc;
......
......@@ -380,6 +380,7 @@ extern int util_get_crt (void);
extern struct mailvar_variable *mailvar_find_variable (const char *var, int create);
extern int mailvar_get (void *ptr, const char *variable,
enum mailvar_type type, int warn);
int mailvar_is_true (char const *name);
extern void mailvar_print (int set);
extern void mailvar_variable_format (mu_stream_t,
......
......@@ -276,6 +276,10 @@ struct mailvar_symbol mailvar_tab[] =
{ { "xmailer", },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("add the `X-Mailer' header to the outgoing messages") },
{ { "mime" },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("always compose MIME messages") },
/* These will be implemented later */
{ { "onehop", }, MAILVAR_HIDDEN, NULL },
......@@ -450,6 +454,12 @@ mailvar_get (void *ptr, const char *variable, enum mailvar_type type, int warn)
return 0;
}
int
mailvar_is_true (char const *name)
{
return mailvar_get (NULL, name, mailvar_type_boolean, 0) == 0;
}
/* Initialize mailvar_list entry: clear set indicator and free any memory
associated with the data */
void
......
......@@ -499,98 +499,51 @@ saveatt (void *item, void *data)
}
static int
add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
add_body (mu_message_t inmsg, compose_env_t *env)
{
mu_body_t body;
mu_message_t part;
mu_stream_t str, output;
mu_header_t outhdr;
char *p;
int rc;
mu_body_t body;
mu_stream_t str;
struct atchinfo *aptr;
mu_message_get_body (inmsg, &body);
if (skip_empty_attachments || multipart_alternative)
{
size_t size;
rc = mu_body_size (body, &size);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_body_size", NULL, rc);
return -1;
}
if (size == 0)
return 0;
}
/* Add original message as the first part */
/* 1. Create the part and obtain a reference to its stream */
if ((rc = mu_message_create (&part, NULL)) == 0)
{
mu_body_t pbody;
mu_message_get_body (part, &pbody);
mu_body_get_streamref (pbody, &output);
}
else
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_message_create", NULL, rc);
return -1;
}
/* 2. Get original body stream and copy it out to the part's body */
mu_body_get_streamref (body, &str);
mu_stream_copy (output, str, 0, NULL);
mu_stream_close (output);
mu_stream_destroy (&output);
/* 3. Copy "Content-*" headers from the original message */
mu_message_get_header (part, &outhdr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
const char *name, *value;
if (mu_iterator_current_kv (itr, (const void **)&name,
(void**)&value) == 0)
{
if (mu_c_strncasecmp (name, "Content-", 8) == 0)
mu_header_set_value (outhdr, name, value, 0);
}
}
aptr = mu_alloc (sizeof (*aptr));
aptr->id = NULL;
aptr->encoding = default_encoding ? mu_strdup (default_encoding) : NULL;
aptr->content_type = mu_strdup (default_content_type ?
default_content_type : "text/plain");
aptr->name = NULL;
aptr->filename = NULL;
aptr->source = str;
aptr->skip_empty = skip_empty_attachments || multipart_alternative;
if (!env->attlist)
env->attlist = attlist_new ();
rc = mu_list_prepend (env->attlist, aptr);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_list_prepend", NULL, rc);
return rc;
}
/* 4. Add the content type and content ID headers. */
mu_header_set_value (outhdr, MU_HEADER_CONTENT_TYPE,
default_content_type ? default_content_type : "text/plain",
0);
mu_rfc2822_msg_id (0, &p);
mu_header_set_value (outhdr, MU_HEADER_CONTENT_ID, p, 1);
free (p);
/* 5. Add part to the mime object */
mu_mime_add_part (mime, part);
mu_message_unref (part);
return 0;
}
static int
add_attachments (compose_env_t *env, mu_message_t *pmsg)
{
mu_message_t inmsg, outmsg;
mu_header_t inhdr, outhdr;
mu_iterator_t itr;
mu_mime_t mime;
int rc;
inmsg = *pmsg;
if (mailvar_is_true ("mime") && add_body (inmsg, env))
return 1;
if (mu_list_is_empty (env->attlist))
return 0;
inmsg = *pmsg;
/* Create a mime object */
rc = mu_mime_create (&mime, NULL,
rc = mu_mime_create (&env->mime, NULL,
env->alt ?
MU_MIME_MULTIPART_ALT : MU_MIME_MULTIPART_MIXED);
if (rc)
......@@ -600,39 +553,35 @@ add_attachments (compose_env_t *env, mu_message_t *pmsg)
}
mu_message_get_header (inmsg, &inhdr);
mu_header_get_iterator (inhdr, &itr);
if (add_body (inmsg, itr, mime))
{
mu_mime_destroy (&mime);
mu_iterator_destroy (&itr);
return 1;
}
env->mime = mime;
/* Add the respective attachments */
rc = mu_list_foreach (env->attlist, saveatt, env);
if (rc)
{
mu_mime_destroy (&mime);
mu_iterator_destroy (&itr);
return 1;
}
return 1;
/* Get the resulting message */
rc = mu_mime_get_message (mime, &outmsg);
rc = mu_mime_get_message (env->mime, &outmsg);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc);
mu_mime_destroy (&mime);
mu_iterator_destroy (&itr);
return 1;
}
/* Copy rest of headers from the original message */
mu_message_get_header (outmsg, &outhdr);
rc = mu_message_get_header (outmsg, &outhdr);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", NULL, rc);
return 1;
}
rc = mu_header_get_iterator (inhdr, &itr);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_header_get_iterator", NULL, rc);
return 1;
}
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
......@@ -1356,7 +1305,7 @@ mail_send0 (compose_env_t *env, int save_to)
mu_message_set_header (msg, env->header, NULL);
env->header = NULL;
status = add_attachments (env, &msg);
if (status)
break;
......