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
1 GNU mailutils NEWS -- history of user-visible changes. 2017-04-13 1 GNU mailutils NEWS -- history of user-visible changes. 2017-04-19
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
...@@ -90,6 +90,20 @@ defined. Instead, the following constants are defined in config.h: ...@@ -90,6 +90,20 @@ defined. Instead, the following constants are defined in config.h:
90 90
91 * mail: sending multipart messages 91 * mail: sending multipart messages
92 92
93 ** New option --mime
94
95 This option instructs mail to compose output messages in MIME format.
96 The options --content-type and --encoding turn this option on. As a
97 side effect, both --content-type and --encoding now affect the message
98 body read from the standard input as well.
99
100 ** New variable 'mime'
101
102 The 'mime' variable, if set instructs mail to compose output messages
103 in MIME format. In fact, the '--mime' option is equivalent to
104 '-E set mime', except that it takes effect after all options are
105 processed.
106
93 ** New option --alternative 107 ** New option --alternative
94 108
95 When used with --attach or --attach-fd options, this option sets the 109 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. ...@@ -2987,7 +2987,7 @@ Configuration Files}, for a detailed description of their format.
2987 * Invoking Mail:: Command Line Options. 2987 * Invoking Mail:: Command Line Options.
2988 * Specifying Messages:: How to Specify Message Sets. 2988 * Specifying Messages:: How to Specify Message Sets.
2989 * Composing Mail:: Composing Mail. 2989 * Composing Mail:: Composing Mail.
2990 * Attachments:: Attaching Files. 2990 * MIME:: How to Attach Files.
2991 * Reading Mail:: Reading Mail. 2991 * Reading Mail:: Reading Mail.
2992 * Scripting:: Scripting. 2992 * Scripting:: Scripting.
2993 * Mail Variables:: How to Alter the Behavior of @command{mail}. 2993 * Mail Variables:: How to Alter the Behavior of @command{mail}.
...@@ -3084,6 +3084,14 @@ Print header summary to stdout and exit. ...@@ -3084,6 +3084,14 @@ Print header summary to stdout and exit.
3084 @itemx --ignore 3084 @itemx --ignore
3085 Ignore interrupts when composing the message. 3085 Ignore interrupts when composing the message.
3086 3086
3087 @item -M
3088 @itemx --mime
3089 @itemx --no-mime
3090 The @option{--mime} option instructs @command{mail} to compose MIME
3091 messages. It is equivalent for @option{-E 'set mime'}, except that it
3092 is processed after all other options. The @option{--no-mime} disables
3093 the MIME compose mode, and is a shortcut for @option{-E 'set nomime'},
3094
3087 @item -N 3095 @item -N
3088 @itemx --nosum 3096 @itemx --nosum
3089 Do not display initial header summary. 3097 Do not display initial header summary.
...@@ -3522,8 +3530,53 @@ the old contents of your message. ...@@ -3522,8 +3530,53 @@ the old contents of your message.
3522 3530
3523 @c ********************************************************************* 3531 @c *********************************************************************
3524 3532
3525 @node Attachments 3533 @node MIME
3526 @subsection Sending Attachments 3534 @subsection Composing Multipart Messages
3535
3536 Multipart messages (or MIME, for short) can be used to send text in
3537 character set other than ASCII, attach non-text files, send multiple
3538 parts in alternative formats, etc.
3539
3540 Technically speaking, the boolean variable @code{mime}
3541 controls this feature. If it is set (@pxref{Setting and Unsetting
3542 the Variables}), @command{MIME} will create MIME messages by default.
3543 The variable can be set in the global or user configuration file
3544 (@pxref{Mail Configuration Files}), using the following command:
3545
3546 @example
3547 set mime
3548 @end example
3549
3550 It can also be set from the command line, using the @option{--mime}
3551 option.
3552
3553 GNU @command{mail} automatically turns on the MIME mode, when it is
3554 requested to send a non-plaintext message, or a message in character
3555 set other than ASCII, when the encoding is specified, or when
3556 attachments are given.
3557
3558 To send a message in another character set, specify it with the
3559 @option{--content-type} option:
3560
3561 @example
3562 mail --content-type 'text/plain; charset=utf-8'
3563 @end example
3564
3565 The @option{--encoding} specifies the encoding to use:
3566
3567 @example
3568 mail --content-type 'text/plain; charset=utf-8' --encoding=base64
3569 @end example
3570
3571 Its argument is any encoding supported by GNU mailutils. The two most
3572 often used encodings are @samp{base64} and @samp{quoted-printable}.
3573
3574 To specify the charset from @command{mail} interactive section, enable
3575 the ``edit headers'' mode (@code{set editheaders}) and add the
3576 needed @code{Content-Type} header manually.
3577
3578 GNU @command{mail} also gives you a possibility to attach files to the
3579 message being sent.
3527 3580
3528 The simplest way to attach a file from command line is by using the 3581 The simplest way to attach a file from command line is by using the
3529 @option{--attach} (@option{-A}) option. Its argument specifies the 3582 @option{--attach} (@option{-A}) option. Its argument specifies the
...@@ -3545,9 +3598,9 @@ $ mail --content-type=text/html --attach=in.html ...@@ -3545,9 +3598,9 @@ $ mail --content-type=text/html --attach=in.html
3545 @end example 3598 @end example
3546 3599
3547 The @option{--content-type} option affects all @option{--attach} 3600 The @option{--content-type} option affects all @option{--attach}
3548 options that follow it. To change the content type, simply add 3601 options that follow it, and the message body (if any). To change the
3549 another @option{--content-type} option. For example, to send both 3602 content type, simply add another @option{--content-type} option. For
3550 the HTML file and the archive: 3603 example, to send both the HTML file and the archive:
3551 3604
3552 @example 3605 @example
3553 $ mail --content-type=text/html --attach=in.html \ 3606 $ mail --content-type=text/html --attach=in.html \
...@@ -3556,8 +3609,9 @@ $ mail --content-type=text/html --attach=in.html \ ...@@ -3556,8 +3609,9 @@ $ mail --content-type=text/html --attach=in.html \
3556 3609
3557 Similarly, the encoding to use is set up by the @option{--encoding} 3610 Similarly, the encoding to use is set up by the @option{--encoding}
3558 option. As well as @option{--content-type}, this option affects all 3611 option. As well as @option{--content-type}, this option affects all
3559 attachments supplied after it in the command line, until changed by 3612 attachments supplied after it in the command line as well as the
3560 the eventual next appearance of the same option. Extending the above 3613 message body read from the standard input, until changed by
3614 the eventual next instance of the same option. Extending the above
3561 example: 3615 example:
3562 3616
3563 @example 3617 @example
...@@ -5066,6 +5120,17 @@ set metamail ...@@ -5066,6 +5120,17 @@ set metamail
5066 set metamail="metamail -m mail -p" 5120 set metamail="metamail -m mail -p"
5067 @end example 5121 @end example
5068 5122
5123 @kwindex mime
5124 @item mime
5125 @*Type: String
5126 @*Default: Unset (false)
5127 @vrindex mime, mail variable
5128
5129 If set, this variable instructs @command{mail} to compose MIME
5130 messages.
5131
5132 It can be set from the command line using @option{--mime} option.
5133
5069 @kwindex mimenoask 5134 @kwindex mimenoask
5070 @item mimenoask 5135 @item mimenoask
5071 @*Type: String 5136 @*Type: String
......
...@@ -36,6 +36,7 @@ int hint; ...@@ -36,6 +36,7 @@ int hint;
36 char *file; 36 char *file;
37 char *user; 37 char *user;
38 38
39 int mime_option;
39 int skip_empty_attachments; 40 int skip_empty_attachments;
40 char *default_encoding; 41 char *default_encoding;
41 char *default_content_type; 42 char *default_content_type;
...@@ -274,6 +275,10 @@ static struct mu_option mail_options[] = { ...@@ -274,6 +275,10 @@ static struct mu_option mail_options[] = {
274 N_("attach from file descriptor FD"), 275 N_("attach from file descriptor FD"),
275 mu_c_string, NULL, cli_attach_fd }, 276 mu_c_string, NULL, cli_attach_fd },
276 277
278 { "mime", 'M', NULL, MU_OPTION_DEFAULT,
279 N_("compose MIME messages"),
280 mu_c_bool, &mime_option },
281
277 MU_OPTION_END 282 MU_OPTION_END
278 }, *options[] = { mail_options, NULL }; 283 }, *options[] = { mail_options, NULL };
279 284
...@@ -483,6 +488,11 @@ main (int argc, char **argv) ...@@ -483,6 +488,11 @@ main (int argc, char **argv)
483 /* argument parsing */ 488 /* argument parsing */
484 mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv); 489 mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv);
485 490
491 if (default_content_type || default_encoding)
492 mime_option = 1;
493 if (mime_option)
494 util_cache_command (&command_list, "set mime");
495
486 if (read_recipients) 496 if (read_recipients)
487 { 497 {
488 argv += argc; 498 argv += argc;
......
...@@ -380,6 +380,7 @@ extern int util_get_crt (void); ...@@ -380,6 +380,7 @@ extern int util_get_crt (void);
380 extern struct mailvar_variable *mailvar_find_variable (const char *var, int create); 380 extern struct mailvar_variable *mailvar_find_variable (const char *var, int create);
381 extern int mailvar_get (void *ptr, const char *variable, 381 extern int mailvar_get (void *ptr, const char *variable,
382 enum mailvar_type type, int warn); 382 enum mailvar_type type, int warn);
383 int mailvar_is_true (char const *name);
383 384
384 extern void mailvar_print (int set); 385 extern void mailvar_print (int set);
385 extern void mailvar_variable_format (mu_stream_t, 386 extern void mailvar_variable_format (mu_stream_t,
......
...@@ -276,6 +276,10 @@ struct mailvar_symbol mailvar_tab[] = ...@@ -276,6 +276,10 @@ struct mailvar_symbol mailvar_tab[] =
276 { { "xmailer", }, 276 { { "xmailer", },
277 MAILVAR_TYPEMASK (mailvar_type_boolean), 277 MAILVAR_TYPEMASK (mailvar_type_boolean),
278 N_("add the `X-Mailer' header to the outgoing messages") }, 278 N_("add the `X-Mailer' header to the outgoing messages") },
279
280 { { "mime" },
281 MAILVAR_TYPEMASK (mailvar_type_boolean),
282 N_("always compose MIME messages") },
279 283
280 /* These will be implemented later */ 284 /* These will be implemented later */
281 { { "onehop", }, MAILVAR_HIDDEN, NULL }, 285 { { "onehop", }, MAILVAR_HIDDEN, NULL },
...@@ -450,6 +454,12 @@ mailvar_get (void *ptr, const char *variable, enum mailvar_type type, int warn) ...@@ -450,6 +454,12 @@ mailvar_get (void *ptr, const char *variable, enum mailvar_type type, int warn)
450 return 0; 454 return 0;
451 } 455 }
452 456
457 int
458 mailvar_is_true (char const *name)
459 {
460 return mailvar_get (NULL, name, mailvar_type_boolean, 0) == 0;
461 }
462
453 /* Initialize mailvar_list entry: clear set indicator and free any memory 463 /* Initialize mailvar_list entry: clear set indicator and free any memory
454 associated with the data */ 464 associated with the data */
455 void 465 void
......
...@@ -499,98 +499,51 @@ saveatt (void *item, void *data) ...@@ -499,98 +499,51 @@ saveatt (void *item, void *data)
499 } 499 }
500 500
501 static int 501 static int
502 add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime) 502 add_body (mu_message_t inmsg, compose_env_t *env)
503 { 503 {
504 mu_body_t body;
505 mu_message_t part;
506 mu_stream_t str, output;
507 mu_header_t outhdr;
508 char *p;
509 int rc; 504 int rc;
510 505 mu_body_t body;
506 mu_stream_t str;
507 struct atchinfo *aptr;
508
511 mu_message_get_body (inmsg, &body); 509 mu_message_get_body (inmsg, &body);
512 if (skip_empty_attachments || multipart_alternative)
513 {
514 size_t size;
515 rc = mu_body_size (body, &size);
516 if (rc)
517 {
518 mu_diag_funcall (MU_DIAG_ERROR, "mu_body_size", NULL, rc);
519 return -1;
520 }
521 if (size == 0)
522 return 0;
523 }
524
525 /* Add original message as the first part */
526
527 /* 1. Create the part and obtain a reference to its stream */
528 if ((rc = mu_message_create (&part, NULL)) == 0)
529 {
530 mu_body_t pbody;
531
532 mu_message_get_body (part, &pbody);
533 mu_body_get_streamref (pbody, &output);
534 }
535 else
536 {
537 mu_diag_funcall (MU_DIAG_ERROR, "mu_message_create", NULL, rc);
538 return -1;
539 }
540
541 /* 2. Get original body stream and copy it out to the part's body */
542 mu_body_get_streamref (body, &str); 510 mu_body_get_streamref (body, &str);
543 mu_stream_copy (output, str, 0, NULL);
544 511
545 mu_stream_close (output); 512 aptr = mu_alloc (sizeof (*aptr));
546 mu_stream_destroy (&output); 513 aptr->id = NULL;
547 514 aptr->encoding = default_encoding ? mu_strdup (default_encoding) : NULL;
548 /* 3. Copy "Content-*" headers from the original message */ 515 aptr->content_type = mu_strdup (default_content_type ?
549 mu_message_get_header (part, &outhdr); 516 default_content_type : "text/plain");
550 for (mu_iterator_first (itr); !mu_iterator_is_done (itr); 517 aptr->name = NULL;
551 mu_iterator_next (itr)) 518 aptr->filename = NULL;
552 { 519 aptr->source = str;
553 const char *name, *value; 520 aptr->skip_empty = skip_empty_attachments || multipart_alternative;
554 521 if (!env->attlist)
555 if (mu_iterator_current_kv (itr, (const void **)&name, 522 env->attlist = attlist_new ();
556 (void**)&value) == 0) 523 rc = mu_list_prepend (env->attlist, aptr);
557 { 524 if (rc)
558 if (mu_c_strncasecmp (name, "Content-", 8) == 0) 525 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_prepend", NULL, rc);
559 mu_header_set_value (outhdr, name, value, 0); 526 return rc;
560 } 527 }
561 }
562 528
563 /* 4. Add the content type and content ID headers. */
564 mu_header_set_value (outhdr, MU_HEADER_CONTENT_TYPE,
565 default_content_type ? default_content_type : "text/plain",
566 0);
567 mu_rfc2822_msg_id (0, &p);
568 mu_header_set_value (outhdr, MU_HEADER_CONTENT_ID, p, 1);
569 free (p);
570
571 /* 5. Add part to the mime object */
572 mu_mime_add_part (mime, part);
573 mu_message_unref (part);
574
575 return 0;
576 }
577
578 static int 529 static int
579 add_attachments (compose_env_t *env, mu_message_t *pmsg) 530 add_attachments (compose_env_t *env, mu_message_t *pmsg)
580 { 531 {
581 mu_message_t inmsg, outmsg; 532 mu_message_t inmsg, outmsg;
582 mu_header_t inhdr, outhdr; 533 mu_header_t inhdr, outhdr;
583 mu_iterator_t itr; 534 mu_iterator_t itr;
584 mu_mime_t mime;
585 int rc; 535 int rc;
586 536
537 inmsg = *pmsg;
538
539 if (mailvar_is_true ("mime") && add_body (inmsg, env))
540 return 1;
541
587 if (mu_list_is_empty (env->attlist)) 542 if (mu_list_is_empty (env->attlist))
588 return 0; 543 return 0;
589 544
590 inmsg = *pmsg;
591
592 /* Create a mime object */ 545 /* Create a mime object */
593 rc = mu_mime_create (&mime, NULL, 546 rc = mu_mime_create (&env->mime, NULL,
594 env->alt ? 547 env->alt ?
595 MU_MIME_MULTIPART_ALT : MU_MIME_MULTIPART_MIXED); 548 MU_MIME_MULTIPART_ALT : MU_MIME_MULTIPART_MIXED);
596 if (rc) 549 if (rc)
...@@ -600,39 +553,35 @@ add_attachments (compose_env_t *env, mu_message_t *pmsg) ...@@ -600,39 +553,35 @@ add_attachments (compose_env_t *env, mu_message_t *pmsg)
600 } 553 }
601 554
602 mu_message_get_header (inmsg, &inhdr); 555 mu_message_get_header (inmsg, &inhdr);
603 mu_header_get_iterator (inhdr, &itr);
604
605 if (add_body (inmsg, itr, mime))
606 {
607 mu_mime_destroy (&mime);
608 mu_iterator_destroy (&itr);
609 return 1;
610 }
611
612 env->mime = mime;
613 556
614 /* Add the respective attachments */ 557 /* Add the respective attachments */
615 rc = mu_list_foreach (env->attlist, saveatt, env); 558 rc = mu_list_foreach (env->attlist, saveatt, env);
616 if (rc) 559 if (rc)
617 { 560 return 1;
618 mu_mime_destroy (&mime);
619 mu_iterator_destroy (&itr);
620 return 1;
621 }
622 561
623 /* Get the resulting message */ 562 /* Get the resulting message */
624 rc = mu_mime_get_message (mime, &outmsg); 563 rc = mu_mime_get_message (env->mime, &outmsg);
625 564
626 if (rc) 565 if (rc)
627 { 566 {
628 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc); 567 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc);
629 mu_mime_destroy (&mime);
630 mu_iterator_destroy (&itr);
631 return 1; 568 return 1;
632 } 569 }
633 570
634 /* Copy rest of headers from the original message */ 571 /* Copy rest of headers from the original message */
635 mu_message_get_header (outmsg, &outhdr); 572 rc = mu_message_get_header (outmsg, &outhdr);
573 if (rc)
574 {
575 mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", NULL, rc);
576 return 1;
577 }
578
579 rc = mu_header_get_iterator (inhdr, &itr);
580 if (rc)
581 {
582 mu_diag_funcall (MU_DIAG_ERROR, "mu_header_get_iterator", NULL, rc);
583 return 1;
584 }
636 for (mu_iterator_first (itr); !mu_iterator_is_done (itr); 585 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
637 mu_iterator_next (itr)) 586 mu_iterator_next (itr))
638 { 587 {
...@@ -1356,7 +1305,7 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1356,7 +1305,7 @@ mail_send0 (compose_env_t *env, int save_to)
1356 1305
1357 mu_message_set_header (msg, env->header, NULL); 1306 mu_message_set_header (msg, env->header, NULL);
1358 env->header = NULL; 1307 env->header = NULL;
1359 1308
1360 status = add_attachments (env, &msg); 1309 status = add_attachments (env, &msg);
1361 if (status) 1310 if (status)
1362 break; 1311 break;
......