summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS17
-rw-r--r--doc/texinfo/programs.texi15
-rw-r--r--libmailutils/mime/mime.c17
-rw-r--r--mail/mail.c14
-rw-r--r--mail/mail.h26
-rw-r--r--mail/send.c209
-rw-r--r--mail/table.c1
-rw-r--r--mail/util.c6
8 files changed, 222 insertions, 83 deletions
diff --git a/NEWS b/NEWS
index aed4476b3..f6fef0c7e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU mailutils NEWS -- history of user-visible changes. 2017-04-09
+GNU mailutils NEWS -- history of user-visible changes. 2017-04-13
Copyright (C) 2002-2017 Free Software Foundation, Inc.
See the end of file for copying conditions.
@@ -88,6 +88,21 @@ defined. Instead, the following constants are defined in config.h:
* movemail: new option --progress-meter
+* mail: sending multipart messages
+
+** New option --alternative
+
+When used with --attach or --attach-fd options, this option sets the
+Content-Type of the constructed message to "multipart/alternative".
+In the absense of this option, the type is "multipart/mixed".
+
+** New escape ~/
+
+New escape ~/ toggles the Content-Type of the message being composed
+between "multipart/mixed" and "multipart/alternative".
+
+The actual Content-Type is displayed by the ~l (list attachments) escape.
+
* scheme implementation of the Sieve language discontinued
There's no reason to keep two different implementations of the Sieve
diff --git a/doc/texinfo/programs.texi b/doc/texinfo/programs.texi
index 8e420f9ea..1586df2ab 100644
--- a/doc/texinfo/programs.texi
+++ b/doc/texinfo/programs.texi
@@ -3420,12 +3420,19 @@ To list the files attached so far, use the @samp{~l} escape:
@example
~l
+multipart/mixed
1 myfile.html text/html base64
@end example
-Each line of the listing contains the ordinal number of the attachment,
+The first line of the output shows the content type of the message.
+Each subsequent line contains the ordinal number of the attachment,
the name of the file, content-type and transfer encoding used.
+@cindex ~/, mail escape
+The @samp{~/} escape toggles the content type bewteen
+@samp{multipart/mixed}, and @samp{multipart/alternative}. The new
+value of the content type is displayed on the screen.
+
@cindex ~^, mail escape
The @samp{~^} escape removes attachments. Its argument is the number
of the attachment to remove, e.g.:
@@ -3568,6 +3575,12 @@ either or both of them using the @option{--content-name} and
affect only the next @option{--attach} (or @option{--attach-fd}, see
below) option.
+By default, the message will be assigned the content type
+@samp{multipart/mixed}. To change it to @samp{multipart/alternative},
+use the @option{--alternative} command line option. Using this option
+also sets the @samp{Content-Disposition} header of each attached
+message to @samp{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
diff --git a/libmailutils/mime/mime.c b/libmailutils/mime/mime.c
index 2ba565dab..849efb35a 100644
--- a/libmailutils/mime/mime.c
+++ b/libmailutils/mime/mime.c
@@ -879,6 +879,9 @@ _mime_body_lines (mu_body_t body, size_t *plines)
return 0;
}
+#define MIME_MULTIPART_FLAGS \
+ (MU_MIME_MULTIPART_MIXED|MU_MIME_MULTIPART_ALT)
+
int
mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags)
{
@@ -886,9 +889,19 @@ mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags)
int ret = 0;
size_t size;
mu_body_t body;
-
+
if (pmime == NULL)
return EINVAL;
+
+ switch (flags & MIME_MULTIPART_FLAGS)
+ {
+ case MIME_MULTIPART_FLAGS:
+ return EINVAL;
+
+ case 0:
+ flags |= MU_MIME_MULTIPART_MIXED;
+ }
+
*pmime = NULL;
if ((mime = calloc (1, sizeof (*mime))) == NULL)
return ENOMEM;
@@ -931,7 +944,7 @@ mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags)
}
else
{
- mime->flags |= MIME_NEW_MESSAGE | MU_MIME_MULTIPART_MIXED;
+ mime->flags |= MIME_NEW_MESSAGE;
}
if (ret != 0)
{
diff --git a/mail/mail.c b/mail/mail.c
index 3647586bb..3a3116991 100644
--- a/mail/mail.c
+++ b/mail/mail.c
@@ -146,9 +146,8 @@ cli_attach (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
arg = NULL;
fd = 0;
}
- if (send_attach_file (fd, arg, content_filename, content_name,
- default_content_type, default_encoding))
- exit (1);
+ send_attach_file (fd, arg, content_filename, content_name,
+ default_content_type, default_encoding);
free (content_name);
content_name = NULL;
@@ -169,9 +168,8 @@ cli_attach_fd (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
exit (po->po_exit_error);
}
- if (send_attach_file (fd, NULL, content_filename, content_name,
- default_content_type, default_encoding))
- exit (1);
+ send_attach_file (fd, NULL, content_filename, content_name,
+ default_content_type, default_encoding);
free (content_name);
content_name = NULL;
@@ -240,6 +238,10 @@ static struct mu_option mail_options[] = {
N_("append given header to the message being sent"),
mu_c_string, NULL, cli_append },
+ { "alternative", 0, NULL, MU_OPTION_DEFAULT,
+ N_("force multipart/alternative content type"),
+ mu_c_bool, &multipart_alternative },
+
{ "skip-empty-attachments", 0, NULL, MU_OPTION_DEFAULT,
N_("skip attachments with empty body"),
mu_c_bool, &skip_empty_attachments },
diff --git a/mail/mail.h b/mail/mail.h
index 690409e52..b729eb5b0 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -99,10 +99,13 @@ typedef int function_t (int, char **);
typedef struct compose_env
{
mu_header_t header; /* The message headers */
- mu_stream_t compstr; /* Temporary compose stream */
- char **outfiles; /* Names of the output files. The message is to be
- saved in each of these. */
- int nfiles; /* Number of output files */
+ mu_stream_t compstr; /* Temporary compose stream */
+ char **outfiles; /* Names of the output files. The message is to be
+ saved in each of these. */
+ int nfiles; /* Number of output files */
+ int alt; /* Use multipart/alternative type */
+ mu_list_t attlist; /* Attachments */
+ mu_mime_t mime; /* Associated MIME object */
} compose_env_t;
#define MAIL_COMMAND_COMMON_MEMBERS \
@@ -174,7 +177,8 @@ extern const char *program_version;
extern char *default_encoding;
extern char *default_content_type;
extern int skip_empty_attachments;
-
+extern int multipart_alternative;
+
/* Functions */
extern int mail_alias (int argc, char **argv);
extern int mail_alt (int argc, char **argv); /* command alternates */
@@ -259,11 +263,11 @@ extern char *mail_expand_name (const char *name);
extern void send_append_header (char const *text);
extern void send_append_header2 (char const *name, char const *value, int mode);
-extern int send_attach_file (int fd,
- const char *filename,
- const char *content_filename,
- const char *content_name,
- const char *content_type, const char *encoding);
+extern void send_attach_file (int fd,
+ const char *filename,
+ const char *content_filename,
+ const char *content_name,
+ const char *content_type, const char *encoding);
extern int escape_check_args (int argc, char **argv, int minargs, int maxargs);
@@ -292,6 +296,8 @@ extern int escape_list_attachments (int argc, char **argv,
extern int escape_attach (int argc, char **argv, compose_env_t *env);
extern int escape_remove_attachment (int argc, char **argv,
compose_env_t *env);
+extern int escape_toggle_multipart_type (int argc, char **argv,
+ compose_env_t *env);
enum
{
diff --git a/mail/send.c b/mail/send.c
index 22c238c74..21529a027 100644
--- a/mail/send.c
+++ b/mail/send.c
@@ -24,6 +24,8 @@
static int isfilename (const char *);
static int msg_to_pipe (const char *cmd, mu_message_t msg);
+int multipart_alternative;
+
/* Additional message headers */
struct add_header
@@ -143,8 +145,6 @@ struct atchinfo
int skip_empty;
};
-static mu_list_t attlist;
-
static void
atchinfo_free (void *p)
{
@@ -158,19 +158,63 @@ atchinfo_free (void *p)
free (ap);
}
+static mu_list_t
+attlist_new (void)
+{
+ mu_list_t lst;
+ int rc = mu_list_create (&lst);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
+ exit (1);
+ }
+ mu_list_set_destroy_item (lst, atchinfo_free);
+ return lst;
+}
+
+static void
+attlist_add (mu_list_t attlist, char *id, char const *encoding,
+ char const *content_type, char const *content_name,
+ char const *content_filename,
+ mu_stream_t stream, int skip_empty)
+{
+ struct atchinfo *aptr;
+ int rc;
+
+ aptr = mu_alloc (sizeof (*aptr));
+
+ aptr->id = id ? mu_strdup (id) : id;
+ aptr->encoding = mu_strdup (encoding);
+ aptr->content_type = mu_strdup (content_type ?
+ content_type : "application/octet-stream");
+ aptr->name = content_name ? mu_strdup (content_name) : NULL;
+ aptr->filename = content_filename ? mu_strdup (content_filename) : NULL;
+ aptr->source = stream;
+ if (stream)
+ mu_stream_ref (stream);
+ aptr->skip_empty = skip_empty;
+ rc = mu_list_append (attlist, aptr);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc);
+ exit (1);
+ }
+}
+
int
-send_attach_file (int fd,
- const char *realname,
- const char *content_filename, const char *content_name,
- const char *content_type, const char *encoding)
+attlist_attach_file (mu_list_t *attlist_ptr,
+ int fd,
+ const char *realname,
+ const char *content_filename, const char *content_name,
+ const char *content_type, const char *encoding)
{
int rc;
struct stat st;
- struct atchinfo *aptr;
mu_list_t list;
mu_stream_t stream = NULL;
char *id = NULL;
-
+ mu_list_t attlist;
+
if (fd >= 0)
{
rc = mu_fd_stream_create (&stream, NULL, fd, MU_STREAM_READ);
@@ -238,32 +282,75 @@ send_attach_file (int fd,
return 1;
}
- if (!attlist)
+ if (!*attlist_ptr)
{
- rc = mu_list_create (&attlist);
- if (rc)
- {
- mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
- exit (1);
- }
- mu_list_set_destroy_item (attlist, atchinfo_free);
+ attlist = attlist_new ();
+ *attlist_ptr = attlist;
}
- aptr = mu_alloc (sizeof (*aptr));
+ else
+ attlist = *attlist_ptr;
+
+ attlist_add (attlist, id, encoding, content_type,
+ content_name, content_filename,
+ stream, skip_empty_attachments);
+ if (stream)
+ mu_stream_unref (stream);
+ free (id);
- aptr->id = id;
- aptr->encoding = mu_strdup (encoding);
- aptr->content_type = mu_strdup (content_type ?
- content_type : "application/octet-stream");
- aptr->name = content_name ? mu_strdup (content_name) : NULL;
- aptr->filename = content_filename ? mu_strdup (content_filename) : NULL;
- aptr->source = stream;
- aptr->skip_empty = skip_empty_attachments;
- rc = mu_list_append (attlist, aptr);
- if (rc)
- {
- mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc);
- exit (1);
- }
+ return 0;
+}
+
+static int
+attlist_helper (void *item, void *data)
+{
+ struct atchinfo *aptr = item;
+ mu_list_t list = data;
+ attlist_add (list, aptr->id, aptr->encoding, aptr->content_type,
+ aptr->name, aptr->filename, aptr->source, aptr->skip_empty);
+ return 0;
+}
+
+static mu_list_t
+attlist_copy (mu_list_t src)
+{
+ mu_list_t dst;
+
+ if (!src)
+ return NULL;
+ dst = attlist_new ();
+ mu_list_foreach (src, attlist_helper, dst);
+ return dst;
+}
+
+static mu_list_t attachment_list;
+
+void
+send_attach_file (int fd,
+ const char *realname,
+ const char *content_filename, const char *content_name,
+ const char *content_type, const char *encoding)
+{
+ attlist_attach_file (&attachment_list,
+ fd,
+ realname,
+ content_filename,
+ content_name,
+ content_type,
+ encoding);
+}
+
+static void
+report_multipart_type (compose_env_t *env)
+{
+ mu_printf ("multipart/%s\n", env->alt ? "alternative" : "mixed");
+}
+
+/* ~/ - toggle between multipart/mixed and multipart/alternative */
+int
+escape_toggle_multipart_type (int argc, char **argv, compose_env_t *env)
+{
+ env->alt = !env->alt;
+ report_multipart_type (env);
return 0;
}
@@ -273,8 +360,10 @@ escape_list_attachments (int argc, char **argv, compose_env_t *env)
mu_iterator_t itr;
int i;
- if (mu_list_is_empty (attlist) ||
- mu_list_get_iterator (attlist, &itr))
+ report_multipart_type (env);
+
+ if (mu_list_is_empty (env->attlist) ||
+ mu_list_get_iterator (env->attlist, &itr))
{
mu_printf ("%s\n", _("No attachments"));
return 0;
@@ -308,8 +397,9 @@ escape_attach (int argc, char **argv, compose_env_t *env)
case 3:
content_type = argv[2];
case 2:
- return send_attach_file (-1, argv[1], argv[1], argv[1],
- content_type, encoding);
+ return attlist_attach_file (&env->attlist,
+ -1, argv[1], argv[1], argv[1],
+ content_type, encoding);
default:
return escape_check_args (argc, argv, 2, 4);
}
@@ -332,21 +422,21 @@ escape_remove_attachment (int argc, char **argv, compose_env_t *env)
return 1;
}
- mu_list_count (attlist, &count);
+ mu_list_count (env->attlist, &count);
if (n == 0 || n > count)
{
mu_error (_("index out of range"));
return 1;
}
- return mu_list_remove_nth (attlist, n - 1);
+ return mu_list_remove_nth (env->attlist, n - 1);
}
-
+
static int
saveatt (void *item, void *data)
{
struct atchinfo *aptr = item;
- mu_mime_t mime = data;
+ compose_env_t *env = data;
mu_message_t part;
mu_header_t hdr;
int rc;
@@ -390,13 +480,15 @@ saveatt (void *item, void *data)
return 0;
}
- mu_mime_get_num_parts (mime, &nparts);
+ mu_mime_get_num_parts (env->mime, &nparts);
mu_message_get_header (part, &hdr);
+ if (env->alt)
+ mu_header_set_value (hdr, MU_HEADER_CONTENT_DISPOSITION, "inline", 1);
mu_rfc2822_msg_id (nparts, &p);
mu_header_set_value (hdr, MU_HEADER_CONTENT_ID, p, 1);
free (p);
- rc = mu_mime_add_part (mime, part);
+ rc = mu_mime_add_part (env->mime, part);
mu_message_unref (part);
if (rc)
{
@@ -418,7 +510,7 @@ add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
int rc;
mu_message_get_body (inmsg, &body);
- if (skip_empty_attachments)
+ if (skip_empty_attachments || multipart_alternative)
{
size_t size;
rc = mu_body_size (body, &size);
@@ -483,7 +575,7 @@ add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
}
static int
-add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
+add_attachments (compose_env_t *env, mu_message_t *pmsg)
{
mu_message_t inmsg, outmsg;
mu_header_t inhdr, outhdr;
@@ -491,16 +583,15 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
mu_mime_t mime;
int rc;
- if (mu_list_is_empty (attlist))
- {
- *pmime = NULL;
- return 0;
- }
+ if (mu_list_is_empty (env->attlist))
+ return 0;
inmsg = *pmsg;
/* Create a mime object */
- rc = mu_mime_create (&mime, NULL, 0);
+ rc = mu_mime_create (&mime, NULL,
+ env->alt ?
+ MU_MIME_MULTIPART_ALT : MU_MIME_MULTIPART_MIXED);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc);
@@ -517,8 +608,10 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
return 1;
}
+ env->mime = mime;
+
/* Add the respective attachments */
- rc = mu_list_foreach (attlist, saveatt, mime);
+ rc = mu_list_foreach (env->attlist, saveatt, env);
if (rc)
{
mu_mime_destroy (&mime);
@@ -559,7 +652,6 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
mu_message_unref (inmsg);
*pmsg = outmsg;
- *pmime = mime;
return 0;
}
@@ -785,6 +877,8 @@ void
compose_init (compose_env_t *env)
{
memset (env, 0, sizeof (*env));
+ env->alt = multipart_alternative;
+ env->attlist = attlist_copy (attachment_list);
mu_list_foreach (add_header_list, seed_headers, env);
}
@@ -873,6 +967,9 @@ compose_destroy (compose_env_t *env)
{
mu_header_destroy (&env->header);
free (env->outfiles);
+ mu_mime_destroy (&env->mime);
+ mu_list_destroy (&env->attlist);
+ mu_stream_destroy (&env->compstr);
}
static int
@@ -1112,7 +1209,6 @@ mail_send0 (compose_env_t *env, int save_to)
if (rc)
{
mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc));
- mu_list_destroy (&attlist);
return 1;
}
@@ -1208,8 +1304,6 @@ mail_send0 (compose_env_t *env, int save_to)
if (int_cnt)
{
save_dead_message_env (env);
- mu_stream_destroy (&env->compstr);
- mu_list_destroy (&attlist);
return 1;
}
@@ -1225,7 +1319,6 @@ mail_send0 (compose_env_t *env, int save_to)
if (util_header_expand (&env->header) == 0)
{
- mu_mime_t mime = NULL;
mu_message_t msg = NULL;
int status = 0;
int sendit = (compose_header_get (env, MU_HEADER_TO, NULL) ||
@@ -1246,7 +1339,7 @@ mail_send0 (compose_env_t *env, int save_to)
mu_message_set_header (msg, env->header, NULL);
env->header = NULL;
- status = add_attachments (&msg, &mime);
+ status = add_attachments (env, &msg);
if (status)
break;
@@ -1323,14 +1416,10 @@ mail_send0 (compose_env_t *env, int save_to)
mu_stream_destroy (&env->compstr);
mu_message_destroy (&msg, NULL);
- mu_mime_destroy (&mime);
return status;
}
else
save_dead_message_env (env);
-
- mu_stream_destroy (&env->compstr);
- mu_list_destroy (&attlist);
return 1;
}
diff --git a/mail/table.c b/mail/table.c
index b14f4058f..c50421cb5 100644
--- a/mail/table.c
+++ b/mail/table.c
@@ -228,6 +228,7 @@ static const struct mail_escape_entry mail_escape_table[] = {
{"-", "-", "-[mail-command]", escape_command },
{"+", "+", "+name [content-type [encoding]]", escape_attach },
{"^", "^", "^N", escape_remove_attachment },
+ {"/", "/", "/", escape_toggle_multipart_type },
{"?", "?", "?", escape_help },
{"A", "A", "A", escape_sign },
{"a", "a", "a", escape_sign },
diff --git a/mail/util.c b/mail/util.c
index 3344d064f..46a04ea34 100644
--- a/mail/util.c
+++ b/mail/util.c
@@ -706,7 +706,7 @@ util_save_outgoing (mu_message_t msg, char *savefile)
if (rc)
{
mu_error (_("Cannot create output mailbox `%s': %s"),
- filename, strerror (rc));
+ filename, mu_strerror (rc));
free (filename);
return;
}
@@ -714,13 +714,13 @@ util_save_outgoing (mu_message_t msg, char *savefile)
rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
if (rc)
mu_error (_("Cannot open output mailbox `%s': %s"),
- filename, strerror (rc));
+ filename, mu_strerror (rc));
else
{
rc = mu_mailbox_append_message (outbox, msg);
if (rc)
mu_error (_("Cannot append message to `%s': %s"),
- filename, strerror (rc));
+ filename, mu_strerror (rc));
}
mu_mailbox_close (outbox);

Return to:

Send suggestions and report system problems to the System administrator.