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.
Showing
6 changed files
with
148 additions
and
99 deletions
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, | ... | ... |
... | @@ -277,6 +277,10 @@ struct mailvar_symbol mailvar_tab[] = | ... | @@ -277,6 +277,10 @@ struct mailvar_symbol mailvar_tab[] = |
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 | 279 | ||
280 | { { "mime" }, | ||
281 | MAILVAR_TYPEMASK (mailvar_type_boolean), | ||
282 | N_("always compose MIME messages") }, | ||
283 | |||
280 | /* These will be implemented later */ | 284 | /* These will be implemented later */ |
281 | { { "onehop", }, MAILVAR_HIDDEN, NULL }, | 285 | { { "onehop", }, MAILVAR_HIDDEN, NULL }, |
282 | 286 | ||
... | @@ -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,80 +499,31 @@ saveatt (void *item, void *data) | ... | @@ -499,80 +499,31 @@ 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; |
505 | mu_body_t body; | ||
506 | mu_stream_t str; | ||
507 | struct atchinfo *aptr; | ||
510 | 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 | |||
545 | mu_stream_close (output); | ||
546 | mu_stream_destroy (&output); | ||
547 | 511 | ||
548 | /* 3. Copy "Content-*" headers from the original message */ | 512 | aptr = mu_alloc (sizeof (*aptr)); |
549 | mu_message_get_header (part, &outhdr); | 513 | aptr->id = NULL; |
550 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); | 514 | aptr->encoding = default_encoding ? mu_strdup (default_encoding) : NULL; |
551 | mu_iterator_next (itr)) | 515 | aptr->content_type = mu_strdup (default_content_type ? |
552 | { | 516 | default_content_type : "text/plain"); |
553 | const char *name, *value; | 517 | aptr->name = NULL; |
554 | 518 | aptr->filename = NULL; | |
555 | if (mu_iterator_current_kv (itr, (const void **)&name, | 519 | aptr->source = str; |
556 | (void**)&value) == 0) | 520 | aptr->skip_empty = skip_empty_attachments || multipart_alternative; |
557 | { | 521 | if (!env->attlist) |
558 | if (mu_c_strncasecmp (name, "Content-", 8) == 0) | 522 | env->attlist = attlist_new (); |
559 | mu_header_set_value (outhdr, name, value, 0); | 523 | rc = mu_list_prepend (env->attlist, aptr); |
560 | } | 524 | if (rc) |
561 | } | 525 | mu_diag_funcall (MU_DIAG_ERROR, "mu_list_prepend", NULL, rc); |
562 | 526 | return rc; | |
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 | } | 527 | } |
577 | 528 | ||
578 | static int | 529 | static int |
... | @@ -581,16 +532,18 @@ add_attachments (compose_env_t *env, mu_message_t *pmsg) | ... | @@ -581,16 +532,18 @@ add_attachments (compose_env_t *env, mu_message_t *pmsg) |
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 | { | ||
618 | mu_mime_destroy (&mime); | ||
619 | mu_iterator_destroy (&itr); | ||
620 | return 1; | 560 | 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 | { | ... | ... |
-
Please register or sign in to post a comment