Commit 5a4ca213 5a4ca2135f5a57d2a7ab18bf7d4881b4caefec35 by Sergey Poznyakoff

Send multipart/alternative messages using mail.

The new option --alternative is provided to change the content type of
the composed message to multipart/alternative.  The ~/ escape toggles
the type between multipart/mixed and multipart/alternative.  In messages
of multipart/alternative type, the Content-Disposition header of all
attachments is reset to "inline".

* libmailutils/mime/mime.c (mu_mime_create): Bugfix: honour the
MU_MIME_MULTIPART_ALT flag.
* mail/mail.c: New option --alternative.
* mail/mail.h (compose_env) <attlist,mime>: New members.
(multipart_alternative): New global.
(send_attach_file): Change return value.
(escape_toggle_multipart_type): New proto.
* mail/send.c (multipart_alternative): New global.
Keep the list of attachments in compose_env_t.  Make sure it is freed
when no longer used.
Implement the ~/ escape.
* mail/table.c: New escape ~/
* mail/util.c: Use mu_strerror instead of strerror.

* NEWS: Document changes to the mail utility
* doc/texinfo/programs.texi: Likewise.
1 parent 444d20f3
1 GNU mailutils NEWS -- history of user-visible changes. 2017-04-09 1 GNU mailutils NEWS -- history of user-visible changes. 2017-04-13
2 Copyright (C) 2002-2017 Free Software Foundation, Inc. 2 Copyright (C) 2002-2017 Free Software Foundation, Inc.
3 See the end of file for copying conditions. 3 See the end of file for copying conditions.
4 4
...@@ -88,6 +88,21 @@ defined. Instead, the following constants are defined in config.h: ...@@ -88,6 +88,21 @@ defined. Instead, the following constants are defined in config.h:
88 88
89 * movemail: new option --progress-meter 89 * movemail: new option --progress-meter
90 90
91 * mail: sending multipart messages
92
93 ** New option --alternative
94
95 When used with --attach or --attach-fd options, this option sets the
96 Content-Type of the constructed message to "multipart/alternative".
97 In the absense of this option, the type is "multipart/mixed".
98
99 ** New escape ~/
100
101 New escape ~/ toggles the Content-Type of the message being composed
102 between "multipart/mixed" and "multipart/alternative".
103
104 The actual Content-Type is displayed by the ~l (list attachments) escape.
105
91 * scheme implementation of the Sieve language discontinued 106 * scheme implementation of the Sieve language discontinued
92 107
93 There's no reason to keep two different implementations of the Sieve 108 There's no reason to keep two different implementations of the Sieve
......
...@@ -3420,12 +3420,19 @@ To list the files attached so far, use the @samp{~l} escape: ...@@ -3420,12 +3420,19 @@ To list the files attached so far, use the @samp{~l} escape:
3420 3420
3421 @example 3421 @example
3422 ~l 3422 ~l
3423 multipart/mixed
3423 1 myfile.html text/html base64 3424 1 myfile.html text/html base64
3424 @end example 3425 @end example
3425 3426
3426 Each line of the listing contains the ordinal number of the attachment, 3427 The first line of the output shows the content type of the message.
3428 Each subsequent line contains the ordinal number of the attachment,
3427 the name of the file, content-type and transfer encoding used. 3429 the name of the file, content-type and transfer encoding used.
3428 3430
3431 @cindex ~/, mail escape
3432 The @samp{~/} escape toggles the content type bewteen
3433 @samp{multipart/mixed}, and @samp{multipart/alternative}. The new
3434 value of the content type is displayed on the screen.
3435
3429 @cindex ~^, mail escape 3436 @cindex ~^, mail escape
3430 The @samp{~^} escape removes attachments. Its argument is the number 3437 The @samp{~^} escape removes attachments. Its argument is the number
3431 of the attachment to remove, e.g.: 3438 of the attachment to remove, e.g.:
...@@ -3568,6 +3575,12 @@ either or both of them using the @option{--content-name} and ...@@ -3568,6 +3575,12 @@ either or both of them using the @option{--content-name} and
3568 affect only the next @option{--attach} (or @option{--attach-fd}, see 3575 affect only the next @option{--attach} (or @option{--attach-fd}, see
3569 below) option. 3576 below) option.
3570 3577
3578 By default, the message will be assigned the content type
3579 @samp{multipart/mixed}. To change it to @samp{multipart/alternative},
3580 use the @option{--alternative} command line option. Using this option
3581 also sets the @samp{Content-Disposition} header of each attached
3582 message to @samp{inline}.
3583
3571 All the examples above will enter the usual interactive shell, 3584 All the examples above will enter the usual interactive shell,
3572 allowing you to compose the body of the message. If that's not 3585 allowing you to compose the body of the message. If that's not
3573 needed, the non-interactive use can be forced by redirecting 3586 needed, the non-interactive use can be forced by redirecting
......
...@@ -879,6 +879,9 @@ _mime_body_lines (mu_body_t body, size_t *plines) ...@@ -879,6 +879,9 @@ _mime_body_lines (mu_body_t body, size_t *plines)
879 return 0; 879 return 0;
880 } 880 }
881 881
882 #define MIME_MULTIPART_FLAGS \
883 (MU_MIME_MULTIPART_MIXED|MU_MIME_MULTIPART_ALT)
884
882 int 885 int
883 mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags) 886 mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags)
884 { 887 {
...@@ -886,9 +889,19 @@ 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)
886 int ret = 0; 889 int ret = 0;
887 size_t size; 890 size_t size;
888 mu_body_t body; 891 mu_body_t body;
889 892
890 if (pmime == NULL) 893 if (pmime == NULL)
891 return EINVAL; 894 return EINVAL;
895
896 switch (flags & MIME_MULTIPART_FLAGS)
897 {
898 case MIME_MULTIPART_FLAGS:
899 return EINVAL;
900
901 case 0:
902 flags |= MU_MIME_MULTIPART_MIXED;
903 }
904
892 *pmime = NULL; 905 *pmime = NULL;
893 if ((mime = calloc (1, sizeof (*mime))) == NULL) 906 if ((mime = calloc (1, sizeof (*mime))) == NULL)
894 return ENOMEM; 907 return ENOMEM;
...@@ -931,7 +944,7 @@ mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags) ...@@ -931,7 +944,7 @@ mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags)
931 } 944 }
932 else 945 else
933 { 946 {
934 mime->flags |= MIME_NEW_MESSAGE | MU_MIME_MULTIPART_MIXED; 947 mime->flags |= MIME_NEW_MESSAGE;
935 } 948 }
936 if (ret != 0) 949 if (ret != 0)
937 { 950 {
......
...@@ -146,9 +146,8 @@ cli_attach (struct mu_parseopt *po, struct mu_option *opt, char const *arg) ...@@ -146,9 +146,8 @@ cli_attach (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
146 arg = NULL; 146 arg = NULL;
147 fd = 0; 147 fd = 0;
148 } 148 }
149 if (send_attach_file (fd, arg, content_filename, content_name, 149 send_attach_file (fd, arg, content_filename, content_name,
150 default_content_type, default_encoding)) 150 default_content_type, default_encoding);
151 exit (1);
152 151
153 free (content_name); 152 free (content_name);
154 content_name = NULL; 153 content_name = NULL;
...@@ -169,9 +168,8 @@ cli_attach_fd (struct mu_parseopt *po, struct mu_option *opt, char const *arg) ...@@ -169,9 +168,8 @@ cli_attach_fd (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
169 exit (po->po_exit_error); 168 exit (po->po_exit_error);
170 } 169 }
171 170
172 if (send_attach_file (fd, NULL, content_filename, content_name, 171 send_attach_file (fd, NULL, content_filename, content_name,
173 default_content_type, default_encoding)) 172 default_content_type, default_encoding);
174 exit (1);
175 173
176 free (content_name); 174 free (content_name);
177 content_name = NULL; 175 content_name = NULL;
...@@ -240,6 +238,10 @@ static struct mu_option mail_options[] = { ...@@ -240,6 +238,10 @@ static struct mu_option mail_options[] = {
240 N_("append given header to the message being sent"), 238 N_("append given header to the message being sent"),
241 mu_c_string, NULL, cli_append }, 239 mu_c_string, NULL, cli_append },
242 240
241 { "alternative", 0, NULL, MU_OPTION_DEFAULT,
242 N_("force multipart/alternative content type"),
243 mu_c_bool, &multipart_alternative },
244
243 { "skip-empty-attachments", 0, NULL, MU_OPTION_DEFAULT, 245 { "skip-empty-attachments", 0, NULL, MU_OPTION_DEFAULT,
244 N_("skip attachments with empty body"), 246 N_("skip attachments with empty body"),
245 mu_c_bool, &skip_empty_attachments }, 247 mu_c_bool, &skip_empty_attachments },
......
...@@ -99,10 +99,13 @@ typedef int function_t (int, char **); ...@@ -99,10 +99,13 @@ typedef int function_t (int, char **);
99 typedef struct compose_env 99 typedef struct compose_env
100 { 100 {
101 mu_header_t header; /* The message headers */ 101 mu_header_t header; /* The message headers */
102 mu_stream_t compstr; /* Temporary compose stream */ 102 mu_stream_t compstr; /* Temporary compose stream */
103 char **outfiles; /* Names of the output files. The message is to be 103 char **outfiles; /* Names of the output files. The message is to be
104 saved in each of these. */ 104 saved in each of these. */
105 int nfiles; /* Number of output files */ 105 int nfiles; /* Number of output files */
106 int alt; /* Use multipart/alternative type */
107 mu_list_t attlist; /* Attachments */
108 mu_mime_t mime; /* Associated MIME object */
106 } compose_env_t; 109 } compose_env_t;
107 110
108 #define MAIL_COMMAND_COMMON_MEMBERS \ 111 #define MAIL_COMMAND_COMMON_MEMBERS \
...@@ -174,7 +177,8 @@ extern const char *program_version; ...@@ -174,7 +177,8 @@ extern const char *program_version;
174 extern char *default_encoding; 177 extern char *default_encoding;
175 extern char *default_content_type; 178 extern char *default_content_type;
176 extern int skip_empty_attachments; 179 extern int skip_empty_attachments;
177 180 extern int multipart_alternative;
181
178 /* Functions */ 182 /* Functions */
179 extern int mail_alias (int argc, char **argv); 183 extern int mail_alias (int argc, char **argv);
180 extern int mail_alt (int argc, char **argv); /* command alternates */ 184 extern int mail_alt (int argc, char **argv); /* command alternates */
...@@ -259,11 +263,11 @@ extern char *mail_expand_name (const char *name); ...@@ -259,11 +263,11 @@ extern char *mail_expand_name (const char *name);
259 263
260 extern void send_append_header (char const *text); 264 extern void send_append_header (char const *text);
261 extern void send_append_header2 (char const *name, char const *value, int mode); 265 extern void send_append_header2 (char const *name, char const *value, int mode);
262 extern int send_attach_file (int fd, 266 extern void send_attach_file (int fd,
263 const char *filename, 267 const char *filename,
264 const char *content_filename, 268 const char *content_filename,
265 const char *content_name, 269 const char *content_name,
266 const char *content_type, const char *encoding); 270 const char *content_type, const char *encoding);
267 271
268 extern int escape_check_args (int argc, char **argv, int minargs, int maxargs); 272 extern int escape_check_args (int argc, char **argv, int minargs, int maxargs);
269 273
...@@ -292,6 +296,8 @@ extern int escape_list_attachments (int argc, char **argv, ...@@ -292,6 +296,8 @@ extern int escape_list_attachments (int argc, char **argv,
292 extern int escape_attach (int argc, char **argv, compose_env_t *env); 296 extern int escape_attach (int argc, char **argv, compose_env_t *env);
293 extern int escape_remove_attachment (int argc, char **argv, 297 extern int escape_remove_attachment (int argc, char **argv,
294 compose_env_t *env); 298 compose_env_t *env);
299 extern int escape_toggle_multipart_type (int argc, char **argv,
300 compose_env_t *env);
295 301
296 enum 302 enum
297 { 303 {
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
24 static int isfilename (const char *); 24 static int isfilename (const char *);
25 static int msg_to_pipe (const char *cmd, mu_message_t msg); 25 static int msg_to_pipe (const char *cmd, mu_message_t msg);
26 26
27 int multipart_alternative;
28
27 29
28 /* Additional message headers */ 30 /* Additional message headers */
29 struct add_header 31 struct add_header
...@@ -143,8 +145,6 @@ struct atchinfo ...@@ -143,8 +145,6 @@ struct atchinfo
143 int skip_empty; 145 int skip_empty;
144 }; 146 };
145 147
146 static mu_list_t attlist;
147
148 static void 148 static void
149 atchinfo_free (void *p) 149 atchinfo_free (void *p)
150 { 150 {
...@@ -158,19 +158,63 @@ atchinfo_free (void *p) ...@@ -158,19 +158,63 @@ atchinfo_free (void *p)
158 free (ap); 158 free (ap);
159 } 159 }
160 160
161 static mu_list_t
162 attlist_new (void)
163 {
164 mu_list_t lst;
165 int rc = mu_list_create (&lst);
166 if (rc)
167 {
168 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
169 exit (1);
170 }
171 mu_list_set_destroy_item (lst, atchinfo_free);
172 return lst;
173 }
174
175 static void
176 attlist_add (mu_list_t attlist, char *id, char const *encoding,
177 char const *content_type, char const *content_name,
178 char const *content_filename,
179 mu_stream_t stream, int skip_empty)
180 {
181 struct atchinfo *aptr;
182 int rc;
183
184 aptr = mu_alloc (sizeof (*aptr));
185
186 aptr->id = id ? mu_strdup (id) : id;
187 aptr->encoding = mu_strdup (encoding);
188 aptr->content_type = mu_strdup (content_type ?
189 content_type : "application/octet-stream");
190 aptr->name = content_name ? mu_strdup (content_name) : NULL;
191 aptr->filename = content_filename ? mu_strdup (content_filename) : NULL;
192 aptr->source = stream;
193 if (stream)
194 mu_stream_ref (stream);
195 aptr->skip_empty = skip_empty;
196 rc = mu_list_append (attlist, aptr);
197 if (rc)
198 {
199 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc);
200 exit (1);
201 }
202 }
203
161 int 204 int
162 send_attach_file (int fd, 205 attlist_attach_file (mu_list_t *attlist_ptr,
163 const char *realname, 206 int fd,
164 const char *content_filename, const char *content_name, 207 const char *realname,
165 const char *content_type, const char *encoding) 208 const char *content_filename, const char *content_name,
209 const char *content_type, const char *encoding)
166 { 210 {
167 int rc; 211 int rc;
168 struct stat st; 212 struct stat st;
169 struct atchinfo *aptr;
170 mu_list_t list; 213 mu_list_t list;
171 mu_stream_t stream = NULL; 214 mu_stream_t stream = NULL;
172 char *id = NULL; 215 char *id = NULL;
173 216 mu_list_t attlist;
217
174 if (fd >= 0) 218 if (fd >= 0)
175 { 219 {
176 rc = mu_fd_stream_create (&stream, NULL, fd, MU_STREAM_READ); 220 rc = mu_fd_stream_create (&stream, NULL, fd, MU_STREAM_READ);
...@@ -238,32 +282,75 @@ send_attach_file (int fd, ...@@ -238,32 +282,75 @@ send_attach_file (int fd,
238 return 1; 282 return 1;
239 } 283 }
240 284
241 if (!attlist) 285 if (!*attlist_ptr)
242 { 286 {
243 rc = mu_list_create (&attlist); 287 attlist = attlist_new ();
244 if (rc) 288 *attlist_ptr = attlist;
245 {
246 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
247 exit (1);
248 }
249 mu_list_set_destroy_item (attlist, atchinfo_free);
250 } 289 }
251 aptr = mu_alloc (sizeof (*aptr)); 290 else
291 attlist = *attlist_ptr;
292
293 attlist_add (attlist, id, encoding, content_type,
294 content_name, content_filename,
295 stream, skip_empty_attachments);
296 if (stream)
297 mu_stream_unref (stream);
298 free (id);
252 299
253 aptr->id = id; 300 return 0;
254 aptr->encoding = mu_strdup (encoding); 301 }
255 aptr->content_type = mu_strdup (content_type ? 302
256 content_type : "application/octet-stream"); 303 static int
257 aptr->name = content_name ? mu_strdup (content_name) : NULL; 304 attlist_helper (void *item, void *data)
258 aptr->filename = content_filename ? mu_strdup (content_filename) : NULL; 305 {
259 aptr->source = stream; 306 struct atchinfo *aptr = item;
260 aptr->skip_empty = skip_empty_attachments; 307 mu_list_t list = data;
261 rc = mu_list_append (attlist, aptr); 308 attlist_add (list, aptr->id, aptr->encoding, aptr->content_type,
262 if (rc) 309 aptr->name, aptr->filename, aptr->source, aptr->skip_empty);
263 { 310 return 0;
264 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc); 311 }
265 exit (1); 312
266 } 313 static mu_list_t
314 attlist_copy (mu_list_t src)
315 {
316 mu_list_t dst;
317
318 if (!src)
319 return NULL;
320 dst = attlist_new ();
321 mu_list_foreach (src, attlist_helper, dst);
322 return dst;
323 }
324
325 static mu_list_t attachment_list;
326
327 void
328 send_attach_file (int fd,
329 const char *realname,
330 const char *content_filename, const char *content_name,
331 const char *content_type, const char *encoding)
332 {
333 attlist_attach_file (&attachment_list,
334 fd,
335 realname,
336 content_filename,
337 content_name,
338 content_type,
339 encoding);
340 }
341
342 static void
343 report_multipart_type (compose_env_t *env)
344 {
345 mu_printf ("multipart/%s\n", env->alt ? "alternative" : "mixed");
346 }
347
348 /* ~/ - toggle between multipart/mixed and multipart/alternative */
349 int
350 escape_toggle_multipart_type (int argc, char **argv, compose_env_t *env)
351 {
352 env->alt = !env->alt;
353 report_multipart_type (env);
267 return 0; 354 return 0;
268 } 355 }
269 356
...@@ -273,8 +360,10 @@ escape_list_attachments (int argc, char **argv, compose_env_t *env) ...@@ -273,8 +360,10 @@ escape_list_attachments (int argc, char **argv, compose_env_t *env)
273 mu_iterator_t itr; 360 mu_iterator_t itr;
274 int i; 361 int i;
275 362
276 if (mu_list_is_empty (attlist) || 363 report_multipart_type (env);
277 mu_list_get_iterator (attlist, &itr)) 364
365 if (mu_list_is_empty (env->attlist) ||
366 mu_list_get_iterator (env->attlist, &itr))
278 { 367 {
279 mu_printf ("%s\n", _("No attachments")); 368 mu_printf ("%s\n", _("No attachments"));
280 return 0; 369 return 0;
...@@ -308,8 +397,9 @@ escape_attach (int argc, char **argv, compose_env_t *env) ...@@ -308,8 +397,9 @@ escape_attach (int argc, char **argv, compose_env_t *env)
308 case 3: 397 case 3:
309 content_type = argv[2]; 398 content_type = argv[2];
310 case 2: 399 case 2:
311 return send_attach_file (-1, argv[1], argv[1], argv[1], 400 return attlist_attach_file (&env->attlist,
312 content_type, encoding); 401 -1, argv[1], argv[1], argv[1],
402 content_type, encoding);
313 default: 403 default:
314 return escape_check_args (argc, argv, 2, 4); 404 return escape_check_args (argc, argv, 2, 4);
315 } 405 }
...@@ -332,21 +422,21 @@ escape_remove_attachment (int argc, char **argv, compose_env_t *env) ...@@ -332,21 +422,21 @@ escape_remove_attachment (int argc, char **argv, compose_env_t *env)
332 return 1; 422 return 1;
333 } 423 }
334 424
335 mu_list_count (attlist, &count); 425 mu_list_count (env->attlist, &count);
336 if (n == 0 || n > count) 426 if (n == 0 || n > count)
337 { 427 {
338 mu_error (_("index out of range")); 428 mu_error (_("index out of range"));
339 return 1; 429 return 1;
340 } 430 }
341 431
342 return mu_list_remove_nth (attlist, n - 1); 432 return mu_list_remove_nth (env->attlist, n - 1);
343 } 433 }
344 434
345 static int 435 static int
346 saveatt (void *item, void *data) 436 saveatt (void *item, void *data)
347 { 437 {
348 struct atchinfo *aptr = item; 438 struct atchinfo *aptr = item;
349 mu_mime_t mime = data; 439 compose_env_t *env = data;
350 mu_message_t part; 440 mu_message_t part;
351 mu_header_t hdr; 441 mu_header_t hdr;
352 int rc; 442 int rc;
...@@ -390,13 +480,15 @@ saveatt (void *item, void *data) ...@@ -390,13 +480,15 @@ saveatt (void *item, void *data)
390 return 0; 480 return 0;
391 } 481 }
392 482
393 mu_mime_get_num_parts (mime, &nparts); 483 mu_mime_get_num_parts (env->mime, &nparts);
394 mu_message_get_header (part, &hdr); 484 mu_message_get_header (part, &hdr);
485 if (env->alt)
486 mu_header_set_value (hdr, MU_HEADER_CONTENT_DISPOSITION, "inline", 1);
395 mu_rfc2822_msg_id (nparts, &p); 487 mu_rfc2822_msg_id (nparts, &p);
396 mu_header_set_value (hdr, MU_HEADER_CONTENT_ID, p, 1); 488 mu_header_set_value (hdr, MU_HEADER_CONTENT_ID, p, 1);
397 free (p); 489 free (p);
398 490
399 rc = mu_mime_add_part (mime, part); 491 rc = mu_mime_add_part (env->mime, part);
400 mu_message_unref (part); 492 mu_message_unref (part);
401 if (rc) 493 if (rc)
402 { 494 {
...@@ -418,7 +510,7 @@ add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime) ...@@ -418,7 +510,7 @@ add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
418 int rc; 510 int rc;
419 511
420 mu_message_get_body (inmsg, &body); 512 mu_message_get_body (inmsg, &body);
421 if (skip_empty_attachments) 513 if (skip_empty_attachments || multipart_alternative)
422 { 514 {
423 size_t size; 515 size_t size;
424 rc = mu_body_size (body, &size); 516 rc = mu_body_size (body, &size);
...@@ -483,7 +575,7 @@ add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime) ...@@ -483,7 +575,7 @@ add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
483 } 575 }
484 576
485 static int 577 static int
486 add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) 578 add_attachments (compose_env_t *env, mu_message_t *pmsg)
487 { 579 {
488 mu_message_t inmsg, outmsg; 580 mu_message_t inmsg, outmsg;
489 mu_header_t inhdr, outhdr; 581 mu_header_t inhdr, outhdr;
...@@ -491,16 +583,15 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) ...@@ -491,16 +583,15 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
491 mu_mime_t mime; 583 mu_mime_t mime;
492 int rc; 584 int rc;
493 585
494 if (mu_list_is_empty (attlist)) 586 if (mu_list_is_empty (env->attlist))
495 { 587 return 0;
496 *pmime = NULL;
497 return 0;
498 }
499 588
500 inmsg = *pmsg; 589 inmsg = *pmsg;
501 590
502 /* Create a mime object */ 591 /* Create a mime object */
503 rc = mu_mime_create (&mime, NULL, 0); 592 rc = mu_mime_create (&mime, NULL,
593 env->alt ?
594 MU_MIME_MULTIPART_ALT : MU_MIME_MULTIPART_MIXED);
504 if (rc) 595 if (rc)
505 { 596 {
506 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc); 597 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) ...@@ -517,8 +608,10 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
517 return 1; 608 return 1;
518 } 609 }
519 610
611 env->mime = mime;
612
520 /* Add the respective attachments */ 613 /* Add the respective attachments */
521 rc = mu_list_foreach (attlist, saveatt, mime); 614 rc = mu_list_foreach (env->attlist, saveatt, env);
522 if (rc) 615 if (rc)
523 { 616 {
524 mu_mime_destroy (&mime); 617 mu_mime_destroy (&mime);
...@@ -559,7 +652,6 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) ...@@ -559,7 +652,6 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
559 mu_message_unref (inmsg); 652 mu_message_unref (inmsg);
560 653
561 *pmsg = outmsg; 654 *pmsg = outmsg;
562 *pmime = mime;
563 return 0; 655 return 0;
564 } 656 }
565 657
...@@ -785,6 +877,8 @@ void ...@@ -785,6 +877,8 @@ void
785 compose_init (compose_env_t *env) 877 compose_init (compose_env_t *env)
786 { 878 {
787 memset (env, 0, sizeof (*env)); 879 memset (env, 0, sizeof (*env));
880 env->alt = multipart_alternative;
881 env->attlist = attlist_copy (attachment_list);
788 mu_list_foreach (add_header_list, seed_headers, env); 882 mu_list_foreach (add_header_list, seed_headers, env);
789 } 883 }
790 884
...@@ -873,6 +967,9 @@ compose_destroy (compose_env_t *env) ...@@ -873,6 +967,9 @@ compose_destroy (compose_env_t *env)
873 { 967 {
874 mu_header_destroy (&env->header); 968 mu_header_destroy (&env->header);
875 free (env->outfiles); 969 free (env->outfiles);
970 mu_mime_destroy (&env->mime);
971 mu_list_destroy (&env->attlist);
972 mu_stream_destroy (&env->compstr);
876 } 973 }
877 974
878 static int 975 static int
...@@ -1112,7 +1209,6 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1112,7 +1209,6 @@ mail_send0 (compose_env_t *env, int save_to)
1112 if (rc) 1209 if (rc)
1113 { 1210 {
1114 mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc)); 1211 mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc));
1115 mu_list_destroy (&attlist);
1116 return 1; 1212 return 1;
1117 } 1213 }
1118 1214
...@@ -1208,8 +1304,6 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1208,8 +1304,6 @@ mail_send0 (compose_env_t *env, int save_to)
1208 if (int_cnt) 1304 if (int_cnt)
1209 { 1305 {
1210 save_dead_message_env (env); 1306 save_dead_message_env (env);
1211 mu_stream_destroy (&env->compstr);
1212 mu_list_destroy (&attlist);
1213 return 1; 1307 return 1;
1214 } 1308 }
1215 1309
...@@ -1225,7 +1319,6 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1225,7 +1319,6 @@ mail_send0 (compose_env_t *env, int save_to)
1225 1319
1226 if (util_header_expand (&env->header) == 0) 1320 if (util_header_expand (&env->header) == 0)
1227 { 1321 {
1228 mu_mime_t mime = NULL;
1229 mu_message_t msg = NULL; 1322 mu_message_t msg = NULL;
1230 int status = 0; 1323 int status = 0;
1231 int sendit = (compose_header_get (env, MU_HEADER_TO, NULL) || 1324 int sendit = (compose_header_get (env, MU_HEADER_TO, NULL) ||
...@@ -1246,7 +1339,7 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1246,7 +1339,7 @@ mail_send0 (compose_env_t *env, int save_to)
1246 mu_message_set_header (msg, env->header, NULL); 1339 mu_message_set_header (msg, env->header, NULL);
1247 env->header = NULL; 1340 env->header = NULL;
1248 1341
1249 status = add_attachments (&msg, &mime); 1342 status = add_attachments (env, &msg);
1250 if (status) 1343 if (status)
1251 break; 1344 break;
1252 1345
...@@ -1323,14 +1416,10 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1323,14 +1416,10 @@ mail_send0 (compose_env_t *env, int save_to)
1323 1416
1324 mu_stream_destroy (&env->compstr); 1417 mu_stream_destroy (&env->compstr);
1325 mu_message_destroy (&msg, NULL); 1418 mu_message_destroy (&msg, NULL);
1326 mu_mime_destroy (&mime);
1327 return status; 1419 return status;
1328 } 1420 }
1329 else 1421 else
1330 save_dead_message_env (env); 1422 save_dead_message_env (env);
1331
1332 mu_stream_destroy (&env->compstr);
1333 mu_list_destroy (&attlist);
1334 return 1; 1423 return 1;
1335 } 1424 }
1336 1425
......
...@@ -228,6 +228,7 @@ static const struct mail_escape_entry mail_escape_table[] = { ...@@ -228,6 +228,7 @@ static const struct mail_escape_entry mail_escape_table[] = {
228 {"-", "-", "-[mail-command]", escape_command }, 228 {"-", "-", "-[mail-command]", escape_command },
229 {"+", "+", "+name [content-type [encoding]]", escape_attach }, 229 {"+", "+", "+name [content-type [encoding]]", escape_attach },
230 {"^", "^", "^N", escape_remove_attachment }, 230 {"^", "^", "^N", escape_remove_attachment },
231 {"/", "/", "/", escape_toggle_multipart_type },
231 {"?", "?", "?", escape_help }, 232 {"?", "?", "?", escape_help },
232 {"A", "A", "A", escape_sign }, 233 {"A", "A", "A", escape_sign },
233 {"a", "a", "a", escape_sign }, 234 {"a", "a", "a", escape_sign },
......
...@@ -706,7 +706,7 @@ util_save_outgoing (mu_message_t msg, char *savefile) ...@@ -706,7 +706,7 @@ util_save_outgoing (mu_message_t msg, char *savefile)
706 if (rc) 706 if (rc)
707 { 707 {
708 mu_error (_("Cannot create output mailbox `%s': %s"), 708 mu_error (_("Cannot create output mailbox `%s': %s"),
709 filename, strerror (rc)); 709 filename, mu_strerror (rc));
710 free (filename); 710 free (filename);
711 return; 711 return;
712 } 712 }
...@@ -714,13 +714,13 @@ util_save_outgoing (mu_message_t msg, char *savefile) ...@@ -714,13 +714,13 @@ util_save_outgoing (mu_message_t msg, char *savefile)
714 rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT); 714 rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
715 if (rc) 715 if (rc)
716 mu_error (_("Cannot open output mailbox `%s': %s"), 716 mu_error (_("Cannot open output mailbox `%s': %s"),
717 filename, strerror (rc)); 717 filename, mu_strerror (rc));
718 else 718 else
719 { 719 {
720 rc = mu_mailbox_append_message (outbox, msg); 720 rc = mu_mailbox_append_message (outbox, msg);
721 if (rc) 721 if (rc)
722 mu_error (_("Cannot append message to `%s': %s"), 722 mu_error (_("Cannot append message to `%s': %s"),
723 filename, strerror (rc)); 723 filename, mu_strerror (rc));
724 } 724 }
725 725
726 mu_mailbox_close (outbox); 726 mu_mailbox_close (outbox);
......