diff options
-rw-r--r-- | NEWS | 17 | ||||
-rw-r--r-- | doc/texinfo/programs.texi | 15 | ||||
-rw-r--r-- | libmailutils/mime/mime.c | 17 | ||||
-rw-r--r-- | mail/mail.c | 14 | ||||
-rw-r--r-- | mail/mail.h | 26 | ||||
-rw-r--r-- | mail/send.c | 209 | ||||
-rw-r--r-- | mail/table.c | 1 | ||||
-rw-r--r-- | mail/util.c | 6 |
8 files changed, 222 insertions, 83 deletions
@@ -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); |