Commit 57c987c1 57c987c11677924928716730abdd68c3d75ea044 by Sergey Poznyakoff

Improve the --skip-empty-attachment functionality

This also includes small bugfixes in the MIME library code.

The --skip-empty-attachment option now affects the original
body as well.  If the option is in effect, and the body is empty,
it will not be included to the composed message.

* libmailutils/mime/mime.c (_mime_body_stream_read): Return EOF
if there are no parts in the message.
(mu_mime_get_num_parts): If message is not multipart, report 1 part
without setting mime->nmtp_parts.  Setting it caused coredump in
other MIME functions (namely, in mu_mime_add_part.
* mail/send.c (add_body): New function.  Adds original body to
the MIME, honoring the --skip-empty-attachment option.
* doc/texinfo/programs.texi: Update.
1 parent 5b77e1ad
...@@ -3048,7 +3048,9 @@ Sets the return email address for outgoing mail. ...@@ -3048,7 +3048,9 @@ Sets the return email address for outgoing mail.
3048 @itemx --no-skip-empty-attachments 3048 @itemx --no-skip-empty-attachments
3049 Don't create attachments that would have zero-size body. This 3049 Don't create attachments that would have zero-size body. This
3050 option affects all attachments created by @option{--attach} and 3050 option affects all attachments created by @option{--attach} and
3051 @option{--attach-fd} options appearing after it in the command line. 3051 @option{--attach-fd} options appearing after it in the command line,
3052 as well as the body of the original message.
3053
3052 To cancel its effect, use the @option{--no-skip-empty-attachments} option. 3054 To cancel its effect, use the @option{--no-skip-empty-attachments} option.
3053 3055
3054 @item -s @var{subj} 3056 @item -s @var{subj}
...@@ -3542,7 +3544,38 @@ The option @option{--skip-empty-attachments} instructs @command{mail} ...@@ -3542,7 +3544,38 @@ The option @option{--skip-empty-attachments} instructs @command{mail}
3542 to skip creating attachments that would have zero-size body. This 3544 to skip creating attachments that would have zero-size body. This
3543 option affects all attachments created by @option{--attach} and 3545 option affects all attachments created by @option{--attach} and
3544 @option{--attach-fd} options appearing after it in the command line. 3546 @option{--attach-fd} options appearing after it in the command line.
3545 To cancel its effect, use the @option{--no-skip-empty-attachments} option. 3547 It also affects the handling of the original message body. To cancel
3548 its effect, use the @option{--no-skip-empty-attachments} option.
3549
3550 Here are some examples illustrating how it works.
3551
3552 First, consider the following command line
3553
3554 @example
3555 $ mail --attach=archive.tar </dev/null
3556 @end example
3557
3558 Assume that @file{archive.tar} is not empty.
3559
3560 This will create a MIME message of two parts: the first part having
3561 @samp{text/html} type and empty body, and the second part of type
3562 @samp{application/octet-stream}, with the content copied from the file
3563 @file{archive.tar}.
3564
3565 Now, if you do:
3566
3567 @example
3568 $ mail --attach=archive.tar --skip-empty-attachments </dev/null
3569 @end example
3570
3571 @noindent
3572 then the created MIME message will contain only one part: that
3573 containing @file{archive.tar}.
3574
3575 If the file @file{archive.tar} has zero length, the resulting archive
3576 will still contain the @samp{application/octet-stream} part of zero
3577 length. However, if you place the @option{--skip-empty-attachments}
3578 option before @option{--attach}, then the produced message will be empty.
3546 3579
3547 The following Perl program serves as an example of using 3580 The following Perl program serves as an example of using
3548 @command{mail} from a script to construct a MIME message on the fly. 3581 @command{mail} from a script to construct a MIME message on the fly.
......
...@@ -662,7 +662,10 @@ _mime_body_stream_read (mu_stream_t stream, char *buf, size_t buflen, size_t *nb ...@@ -662,7 +662,10 @@ _mime_body_stream_read (mu_stream_t stream, char *buf, size_t buflen, size_t *nb
662 size_t total = 0; 662 size_t total = 0;
663 663
664 if (mime->nmtp_parts == 0) 664 if (mime->nmtp_parts == 0)
665 return EINVAL; 665 {
666 *nbytes = 0;
667 return 0;
668 }
666 669
667 if ((ret = _mime_set_content_type (mime)) == 0) 670 if ((ret = _mime_set_content_type (mime)) == 0)
668 { 671 {
...@@ -1055,7 +1058,10 @@ mu_mime_get_num_parts (mu_mime_t mime, size_t *nmtp_parts) ...@@ -1055,7 +1058,10 @@ mu_mime_get_num_parts (mu_mime_t mime, size_t *nmtp_parts)
1055 return (ret); 1058 return (ret);
1056 } 1059 }
1057 else 1060 else
1058 mime->nmtp_parts = 1; 1061 {
1062 *nmtp_parts = 1;
1063 return 0;
1064 }
1059 } 1065 }
1060 *nmtp_parts = mime->nmtp_parts; 1066 *nmtp_parts = mime->nmtp_parts;
1061 return (ret); 1067 return (ret);
......
...@@ -408,50 +408,52 @@ saveatt (void *item, void *data) ...@@ -408,50 +408,52 @@ saveatt (void *item, void *data)
408 } 408 }
409 409
410 static int 410 static int
411 add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) 411 add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
412 { 412 {
413 mu_message_t inmsg, outmsg, part;
414 mu_body_t body; 413 mu_body_t body;
415 mu_header_t inhdr, outhdr; 414 mu_message_t part;
416 mu_iterator_t itr;
417 mu_mime_t mime;
418 mu_stream_t str, output; 415 mu_stream_t str, output;
419 int rc; 416 mu_header_t outhdr;
420 char *p; 417 char *p;
418 int rc;
421 419
422 if (mu_list_is_empty (attlist)) 420 mu_message_get_body (inmsg, &body);
421 if (skip_empty_attachments)
423 { 422 {
424 *pmime = NULL; 423 size_t size;
425 return 0; 424 rc = mu_body_size (body, &size);
425 if (rc)
426 {
427 mu_diag_funcall (MU_DIAG_ERROR, "mu_body_size", NULL, rc);
428 return -1;
429 }
430 if (size == 0)
431 return 0;
426 } 432 }
433
434 /* Add original message as the first part */
427 435
428 inmsg = *pmsg; 436 /* 1. Create the part and obtain a reference to its stream */
429 437 if ((rc = mu_message_create (&part, NULL)) == 0)
430 /* Create a mime object */
431 rc = mu_mime_create (&mime, NULL, 0);
432 if (rc)
433 { 438 {
434 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc); 439 mu_body_t pbody;
435 return 1; 440
441 mu_message_get_body (part, &pbody);
442 mu_body_get_streamref (pbody, &output);
436 } 443 }
437 444 else
438 /* Add original message as its first part */ 445 {
439 /* 1. Create the part and obtain a reference to its stream */ 446 mu_diag_funcall (MU_DIAG_ERROR, "mu_message_create", NULL, rc);
440 mu_message_create (&part, NULL); 447 return -1;
441 mu_message_get_body (part, &body); 448 }
442 mu_body_get_streamref (body, &output); 449
443
444 /* 2. Get original body stream and copy it out to the part's body */ 450 /* 2. Get original body stream and copy it out to the part's body */
445 mu_message_get_body (inmsg, &body);
446 mu_body_get_streamref (body, &str); 451 mu_body_get_streamref (body, &str);
447 mu_stream_copy (output, str, 0, NULL); 452 mu_stream_copy (output, str, 0, NULL);
448 453
449 mu_stream_close (output); 454 mu_stream_close (output);
450 mu_stream_destroy (&output); 455 mu_stream_destroy (&output);
451 456
452 mu_message_get_header (inmsg, &inhdr);
453 mu_header_get_iterator (inhdr, &itr);
454
455 /* 3. Copy "Content-*" headers from the original message */ 457 /* 3. Copy "Content-*" headers from the original message */
456 mu_message_get_header (part, &outhdr); 458 mu_message_get_header (part, &outhdr);
457 for (mu_iterator_first (itr); !mu_iterator_is_done (itr); 459 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
...@@ -477,11 +479,50 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) ...@@ -477,11 +479,50 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
477 mu_mime_add_part (mime, part); 479 mu_mime_add_part (mime, part);
478 mu_message_unref (part); 480 mu_message_unref (part);
479 481
482 return 0;
483 }
484
485 static int
486 add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
487 {
488 mu_message_t inmsg, outmsg;
489 mu_header_t inhdr, outhdr;
490 mu_iterator_t itr;
491 mu_mime_t mime;
492 int rc;
493
494 if (mu_list_is_empty (attlist))
495 {
496 *pmime = NULL;
497 return 0;
498 }
499
500 inmsg = *pmsg;
501
502 /* Create a mime object */
503 rc = mu_mime_create (&mime, NULL, 0);
504 if (rc)
505 {
506 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc);
507 return 1;
508 }
509
510 mu_message_get_header (inmsg, &inhdr);
511 mu_header_get_iterator (inhdr, &itr);
512
513 if (add_body (inmsg, itr, mime))
514 {
515 mu_mime_destroy (&mime);
516 mu_iterator_destroy (&itr);
517 return 1;
518 }
519
480 /* Add the respective attachments */ 520 /* Add the respective attachments */
481 rc = mu_list_foreach (attlist, saveatt, mime); 521 rc = mu_list_foreach (attlist, saveatt, mime);
482 if (rc) 522 if (rc)
483 { 523 {
484 mu_mime_destroy (&mime); 524 mu_mime_destroy (&mime);
525 mu_iterator_destroy (&itr);
485 return 1; 526 return 1;
486 } 527 }
487 528
...@@ -492,6 +533,7 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) ...@@ -492,6 +533,7 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
492 { 533 {
493 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc); 534 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc);
494 mu_mime_destroy (&mime); 535 mu_mime_destroy (&mime);
536 mu_iterator_destroy (&itr);
495 return 1; 537 return 1;
496 } 538 }
497 539
...@@ -515,6 +557,7 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) ...@@ -515,6 +557,7 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
515 557
516 mu_message_unref (outmsg); 558 mu_message_unref (outmsg);
517 mu_message_unref (inmsg); 559 mu_message_unref (inmsg);
560
518 *pmsg = outmsg; 561 *pmsg = outmsg;
519 *pmime = mime; 562 *pmime = mime;
520 return 0; 563 return 0;
...@@ -1193,6 +1236,7 @@ mail_send0 (compose_env_t *env, int save_to) ...@@ -1193,6 +1236,7 @@ mail_send0 (compose_env_t *env, int save_to)
1193 status = mu_message_create (&msg, NULL); 1236 status = mu_message_create (&msg, NULL);
1194 if (status) 1237 if (status)
1195 break; 1238 break;
1239
1196 /* Fill the body. */ 1240 /* Fill the body. */
1197 mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL); 1241 mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL);
1198 status = fill_body (msg, env->compstr); 1242 status = fill_body (msg, env->compstr);
......