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.
@itemx --no-skip-empty-attachments
Don't create attachments that would have zero-size body. This
option affects all attachments created by @option{--attach} and
@option{--attach-fd} options appearing after it in the command line.
@option{--attach-fd} options appearing after it in the command line,
as well as the body of the original message.
To cancel its effect, use the @option{--no-skip-empty-attachments} option.
@item -s @var{subj}
......@@ -3542,7 +3544,38 @@ The option @option{--skip-empty-attachments} instructs @command{mail}
to skip creating attachments that would have zero-size body. This
option affects all attachments created by @option{--attach} and
@option{--attach-fd} options appearing after it in the command line.
To cancel its effect, use the @option{--no-skip-empty-attachments} option.
It also affects the handling of the original message body. To cancel
its effect, use the @option{--no-skip-empty-attachments} option.
Here are some examples illustrating how it works.
First, consider the following command line
@example
$ mail --attach=archive.tar </dev/null
@end example
Assume that @file{archive.tar} is not empty.
This will create a MIME message of two parts: the first part having
@samp{text/html} type and empty body, and the second part of type
@samp{application/octet-stream}, with the content copied from the file
@file{archive.tar}.
Now, if you do:
@example
$ mail --attach=archive.tar --skip-empty-attachments </dev/null
@end example
@noindent
then the created MIME message will contain only one part: that
containing @file{archive.tar}.
If the file @file{archive.tar} has zero length, the resulting archive
will still contain the @samp{application/octet-stream} part of zero
length. However, if you place the @option{--skip-empty-attachments}
option before @option{--attach}, then the produced message will be empty.
The following Perl program serves as an example of using
@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
size_t total = 0;
if (mime->nmtp_parts == 0)
return EINVAL;
{
*nbytes = 0;
return 0;
}
if ((ret = _mime_set_content_type (mime)) == 0)
{
......@@ -1055,7 +1058,10 @@ mu_mime_get_num_parts (mu_mime_t mime, size_t *nmtp_parts)
return (ret);
}
else
mime->nmtp_parts = 1;
{
*nmtp_parts = 1;
return 0;
}
}
*nmtp_parts = mime->nmtp_parts;
return (ret);
......
......@@ -408,50 +408,52 @@ saveatt (void *item, void *data)
}
static int
add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
add_body (mu_message_t inmsg, mu_iterator_t itr, mu_mime_t mime)
{
mu_message_t inmsg, outmsg, part;
mu_body_t body;
mu_header_t inhdr, outhdr;
mu_iterator_t itr;
mu_mime_t mime;
mu_message_t part;
mu_stream_t str, output;
int rc;
mu_header_t outhdr;
char *p;
int rc;
if (mu_list_is_empty (attlist))
mu_message_get_body (inmsg, &body);
if (skip_empty_attachments)
{
*pmime = NULL;
size_t size;
rc = mu_body_size (body, &size);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_body_size", NULL, rc);
return -1;
}
if (size == 0)
return 0;
}
inmsg = *pmsg;
/* Add original message as the first part */
/* Create a mime object */
rc = mu_mime_create (&mime, NULL, 0);
if (rc)
/* 1. Create the part and obtain a reference to its stream */
if ((rc = mu_message_create (&part, NULL)) == 0)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc);
return 1;
}
mu_body_t pbody;
/* Add original message as its first part */
/* 1. Create the part and obtain a reference to its stream */
mu_message_create (&part, NULL);
mu_message_get_body (part, &body);
mu_body_get_streamref (body, &output);
mu_message_get_body (part, &pbody);
mu_body_get_streamref (pbody, &output);
}
else
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_message_create", NULL, rc);
return -1;
}
/* 2. Get original body stream and copy it out to the part's body */
mu_message_get_body (inmsg, &body);
mu_body_get_streamref (body, &str);
mu_stream_copy (output, str, 0, NULL);
mu_stream_close (output);
mu_stream_destroy (&output);
mu_message_get_header (inmsg, &inhdr);
mu_header_get_iterator (inhdr, &itr);
/* 3. Copy "Content-*" headers from the original message */
mu_message_get_header (part, &outhdr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
......@@ -477,11 +479,50 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
mu_mime_add_part (mime, part);
mu_message_unref (part);
return 0;
}
static int
add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
{
mu_message_t inmsg, outmsg;
mu_header_t inhdr, outhdr;
mu_iterator_t itr;
mu_mime_t mime;
int rc;
if (mu_list_is_empty (attlist))
{
*pmime = NULL;
return 0;
}
inmsg = *pmsg;
/* Create a mime object */
rc = mu_mime_create (&mime, NULL, 0);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc);
return 1;
}
mu_message_get_header (inmsg, &inhdr);
mu_header_get_iterator (inhdr, &itr);
if (add_body (inmsg, itr, mime))
{
mu_mime_destroy (&mime);
mu_iterator_destroy (&itr);
return 1;
}
/* Add the respective attachments */
rc = mu_list_foreach (attlist, saveatt, mime);
if (rc)
{
mu_mime_destroy (&mime);
mu_iterator_destroy (&itr);
return 1;
}
......@@ -492,6 +533,7 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc);
mu_mime_destroy (&mime);
mu_iterator_destroy (&itr);
return 1;
}
......@@ -515,6 +557,7 @@ add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
mu_message_unref (outmsg);
mu_message_unref (inmsg);
*pmsg = outmsg;
*pmime = mime;
return 0;
......@@ -1193,6 +1236,7 @@ mail_send0 (compose_env_t *env, int save_to)
status = mu_message_create (&msg, NULL);
if (status)
break;
/* Fill the body. */
mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL);
status = fill_body (msg, env->compstr);
......