diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-01-13 14:45:09 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-01-14 00:08:52 +0200 |
commit | eabddf8f5f18afddc13281a5fbaa562f75e76b5f (patch) | |
tree | f50191fdbba7ca5b7b7d297e6c0fa93e077f57fd | |
parent | 0c76b2b1a4325a8e96292161ee95523ecb6cbc14 (diff) | |
download | mailutils-eabddf8f5f18afddc13281a5fbaa562f75e76b5f.tar.gz mailutils-eabddf8f5f18afddc13281a5fbaa562f75e76b5f.tar.bz2 |
mail: new options to read attachments from file descriptors
* mail/mail.c (default_encoding,default_content_type)
(content_name,content_filename): New statics.
(cli_attach): Use the value of --content-name option as the
content-type name parameter.
The "-" argument instructs the program to read attachment from
stdin (eqv --attach-fd=0)
(cli_attach_fd): New function.
(mail_options): New options: --content-name, --content-filename,
--attach-fd
* mail/mail.h (send_attach_file_default): Remove.
(send_attach_file): New proto.
* mail/send.c (atchinfo): New fields: id, name, source.
(atchinfo_free): Update.
(default_encoding,default_content_type): Remove globals.
(send_attach_file): Rewrite.
(send_attach_file_default): Remove.
(escape_list_attachments): Print attachment identifier instead
of the file name, which can be NULL.
(saveatt): Use mu_attachment_create and mu_attachment_copy_from_stream.
* NEWS: Update.
* doc/texinfo/programs.texi: Document the new options.
-rw-r--r-- | NEWS | 50 | ||||
-rw-r--r-- | doc/texinfo/programs.texi | 137 | ||||
-rw-r--r-- | mail/mail.c | 54 | ||||
-rw-r--r-- | mail/mail.h | 6 | ||||
-rw-r--r-- | mail/send.c | 111 |
5 files changed, 321 insertions, 37 deletions
@@ -1,4 +1,4 @@ -GNU mailutils NEWS -- history of user-visible changes. 2016-12-17 +GNU mailutils NEWS -- history of user-visible changes. 2017-01-13 Copyright (C) 2002-2017 Free Software Foundation, Inc. See the end of file for copying conditions. @@ -7,6 +7,54 @@ Please send mailutils bug reports to <bug-mailutils@gnu.org>. Version 3.1.90 (Git) +* mail + +** Modifying attachment name and filename + +Two new options are provided for modifying attachment name (a.k.a +description), and file name: + + --content-name=STRING + Sets the attachment name (description). Technically speaking, it + is the "name" parameter in the Content-Type MIME header. + + --content-filename=NAME + Sets the file name (the "filename" parameter in the + Content-Description MIME header of the outgoing message. + +Both options affect only the next `--attach' or `--attach-fd' option. + +* Constructing attachments from command line + +The new option `--attach-fd=N' instructs mail to read attachment from +file descriptor N. By default, the attachments created using this +option are unnamed, i.e. neither name parameter of the Content-Type +header, nor the filename parameter of the Content-Disposition header +are set. Use the --content-name and --content-filename options to +change these. + +The option `--attach-fd=0' causes attachment to be read from the +standard input. The option `--attach=-' has the same effect. For +obvious reasons, the interactive mode is suppressed in this case. + +The `--attach-fd' option is useful when calling `mail' from another +program. + +Example: + +Suppose that the 'mail' binary is opened at file descriptor 5 and +the mail.c file is opened at descriptor 6, the following command +line sends them as attachments: + + mail --encoding=base64 \ + --content-type=application/octet-stream \ + --content-name="the mail(1) binary" --content-filename="mail" \ + --attach-fd=5 \ + --encoding=binary\ + --content-type=text/plain --content-name="mail.c source file"\ + --content-filename=mail.c --attach-fd=6 \ + root@example.org + Version 3.1.1 - 2016-12-15 diff --git a/doc/texinfo/programs.texi b/doc/texinfo/programs.texi index 3e3f98ed7..7d1a6ec52 100644 --- a/doc/texinfo/programs.texi +++ b/doc/texinfo/programs.texi @@ -2915,6 +2915,7 @@ Configuration Files}, for a detailed description of their format. * Invoking Mail:: Command Line Options. * Specifying Messages:: How to Specify Message Sets. * Composing Mail:: Composing Mail. +* Attachments:: Attaching Files. * Reading Mail:: Reading Mail. * Scripting:: Scripting. * Mail Variables:: How to Alter the Behavior of @command{mail}. @@ -2938,9 +2939,22 @@ mail sending mode, otherwise it operates in mail reading mode. @table @option @item -A @var{file} @itemx --attach=@var{file} -Attach @var{file} to the composed message. The encoding and content -type are controlled by the @option{--encoding} and -@option{--content-type} options, correspondingly. +Attach @var{file} to the composed message. The encoding, content +type, and content description are controlled by the +@option{--encoding}, @option{--content-type}, and +@option{--content-name} options, correspondingly. + +The option @option{--attach=-} instructs @command{mail} to read the +file to be attached from the standard input. Interactive shell is +disabled in this case. + +@item --attach-fd=@var{fd} +Read attachment body from the file descriptor @var{fd}. The +descriptor must be open for reading. This option is useful when +calling @command{mail} from another program. + +See the options @option{--encoding}, @option{--content-type}, +@option{--content-name}, and @option{--content-filename}. @item -a @var{header}:@var{value} @itemx --append=@var{header}:@var{value} @@ -2950,6 +2964,14 @@ Append the given header to the composed message. This options sets the content type to be used by all subsequent @option{--attach} options. +@item --content-filename=@var{name} +Set the @samp{filename} parameter in the @samp{Content-Disposition} +header for the next @option{--attach-fd} option. + +@item --content-name=@var{text} +Set the @samp{name} parameter (description) in the @samp{Content-Type} +header for the next @option{--attach} or @option{--attach-fd} option. + @item -E @var{command} @itemx --exec=@var{command} Execute @var{command} before opening the mailbox. Any number of @@ -3412,6 +3434,115 @@ the old contents of your message. @c ********************************************************************* +@node Attachments +@subsection Sending Attachments + +The simplest way to attach a file from command line is by using the +@option{--attach} (@option{-A}) option. Its argument specifies the +file to attach. For example, the following will attach the content +of the file @file{archive.tar}: + +@example +$ mail --attach=archive.tar +@end example + +By default, the content type will be set to +@samp{application/octet-stream}, and the attachment will be encoded +using the @samp{base64} encoding. To change the content type, use the +@option{--content-type} option. For example, to send an HTML +attachment: + +@example +$ mail --content-type=text/html --attach=in.html +@end example + +The @option{--content-type} option affects all @option{--attach} +options that follow it. To change the content type, simply add +another @option{--content-type} option. For example, to send both +the HTML file and the archive: + +@example +$ mail --content-type=text/html --attach=in.html \ + --content-type=application/x-tar --attach=archive.tar +@end example + +Similarly, the encoding to use is set up by the @option{--encoding} +option. As well as @option{--content-type}, this option affects all +attachments supplied after it in the command line, until changed by +the eventual next appearance of the same option. Extending the above +example: + +@example +$ mail --content-type=text/html --encoding=quoted-printable \ + --attach=in.html \ + --content-type=application/x-tar --encoding=base64 \ + --attach=archive.tar +@end example + +Each attachment can also be assigned a @dfn{description} and a +@dfn{file name}. Normally, these are the same as the file name +supplied with the @option{--attach} option. However, you can change +either or both of them using the @option{--content-name} and +@option{--content-filename}, correspondingly. Both of these options +affect only the next @option{--attach} (or @option{--attach-fd}, see +below) option. + +All the examples above will enter the usual interactive shell, +allowing you to compose the body of the message. If that's not +needed, the non-interactive use can be forced by redirecting +@file{/dev/null} to the standard input, e.g.: + +@example +$ mail --attach=archive.tar < /dev/null +@end example + +This will normally produce a message saying: + +@example +mail: Null message body; hope that's ok +@end example + +To suppress this message, unset the @samp{nullbodymsg} variable, +as shown in the example below: + +@example +$ mail -E 'set nonullbodymsg' --attach=archive.tar < /dev/null +@end example + +The option @option{--attach=-} forces @command{mail} to read the file +to be attached from the standard input stream. This option implies +disables the interactive mode and sets @samp{nonullbodymsg} +implicitly, so that the above example can be rewritten as: + +@example +$ mail --attach=- < archive.tar +@end example + +Special option is provided to facilitate the use of @command{mail} +in scripts. The @option{--attach-fd=@var{N}} instructs the program to +read the data to be attached from the file descriptor @var{N}. The +above example is equivalent to: + +@example +$ mail --attach-fd=0 < archive.tar +@end example + +Attachments created using this option have neither filename not +description set, so normally the use of @option{--content-name} and/or +@option{--content-filename} is advised. + +@example +$ mail --subject 'mail(1)' \ + --content-name="The mail(1) binary" --content-filename="mail" \ + --attach-fd 5 \ + --encoding=binary --content-type=text/plain \ + --content-name="mail.c source file" --content-filename=mail.c \ + --attach-fd 6 gray@@example.org 5</usr/bin/mail \ + 6<mailutils/mail/mail.c +@end example + +@c ********************************************************************* + @node Reading Mail @subsection Reading Mail diff --git a/mail/mail.c b/mail/mail.c index 71fd81280..ca3f08e19 100644 --- a/mail/mail.c +++ b/mail/mail.c @@ -35,6 +35,11 @@ const char *program_version = "mail (" PACKAGE_STRING ")"; int hint; char *file; char *user; + +char *default_encoding; +char *default_content_type; +char *content_name; +char *content_filename; static void cli_f_option (struct mu_parseopt *po, struct mu_option *opt, char const *arg) @@ -134,9 +139,45 @@ cli_append (struct mu_parseopt *po, struct mu_option *opt, char const *arg) static void cli_attach (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { + int fd = -1; + hint |= HINT_SEND_MODE; - if (send_attach_file_default (arg)) + if (strcmp (arg, "-") == 0) + { + arg = NULL; + fd = 0; + } + if (send_attach_file (fd, arg, content_filename, content_name, + default_content_type, default_encoding)) exit (1); + + free (content_name); + content_name = NULL; + free (content_filename); + content_filename = NULL; +} + +static void +cli_attach_fd (struct mu_parseopt *po, struct mu_option *opt, char const *arg) +{ + int rc, fd; + + hint |= HINT_SEND_MODE; + rc = mu_str_to_c (arg, mu_c_int, &fd, NULL); + if (rc) + { + mu_parseopt_error (po, _("%s: bad descriptor"), arg); + exit (po->po_exit_error); + } + + if (send_attach_file (fd, NULL, content_filename, content_name, + default_content_type, default_encoding)) + exit (1); + + free (content_name); + content_name = NULL; + free (content_filename); + content_filename = NULL; } static struct mu_option mail_options[] = { @@ -212,10 +253,21 @@ static struct mu_option mail_options[] = { N_("set content type for subsequent --attach options"), mu_c_string, &default_content_type }, + { "content-name", 0, N_("NAME"), MU_OPTION_DEFAULT, + N_("set the Content-Type name parameter for the next --attach option"), + mu_c_string, &content_name }, + { "content-filename", 0, N_("NAME"), MU_OPTION_DEFAULT, + N_("set the Content-Disposition filename parameter for the next --attach option"), + mu_c_string, &content_filename }, + { "attach", 'A', N_("FILE"), MU_OPTION_DEFAULT, N_("attach FILE"), mu_c_string, NULL, cli_attach }, + { "attach-fd", 0, N_("FD"), MU_OPTION_DEFAULT, + N_("attach from file descriptor FD"), + mu_c_string, NULL, cli_attach_fd }, + MU_OPTION_END }, *options[] = { mail_options, NULL }; diff --git a/mail/mail.h b/mail/mail.h index 7efc0be94..35a81d3a6 100644 --- a/mail/mail.h +++ b/mail/mail.h @@ -258,7 +258,11 @@ extern char *mail_expand_name (const char *name); extern void send_append_header (char const *text); extern void send_append_header2 (char const *name, char const *value, int mode); -extern int send_attach_file_default (const char *name); +extern int send_attach_file (int fd, + const char *filename, + const char *content_filename, + const char *content_name, + const char *content_type, const char *encoding); extern int escape_check_args (int argc, char **argv, int minargs, int maxargs); diff --git a/mail/send.c b/mail/send.c index 1ab1c1881..8456ad854 100644 --- a/mail/send.c +++ b/mail/send.c @@ -134,55 +134,97 @@ mail_sendheader (int argc, char **argv) /* Attachments */ struct atchinfo { + char *id; char *encoding; char *content_type; + char *name; char *filename; + mu_stream_t source; }; static mu_list_t attlist; -char *default_encoding; -char *default_content_type; - static void atchinfo_free (void *p) { struct atchinfo *ap = p; + free (ap->id); free (ap->encoding); free (ap->content_type); + free (ap->name); free (ap->filename); + mu_stream_destroy (&ap->source); free (ap); } int -send_attach_file (const char *name, +send_attach_file (int fd, + const char *realname, + const char *content_filename, const char *content_name, const char *content_type, const char *encoding) { int rc; struct stat st; struct atchinfo *aptr; mu_list_t list; - - if (stat (name, &st)) + mu_stream_t stream = NULL; + char *id = NULL; + + if (fd >= 0) { - if (errno == ENOENT) + rc = mu_fd_stream_create (&stream, NULL, fd, MU_STREAM_READ); + if (rc) { - mu_error (_("%s: file does not exist"), name); + mu_error (_("can't open descriptor %d: %s"), fd, mu_strerror (rc)); return 1; } - else + mu_asprintf (&id, "fd %d", fd); + if (fd == 0) { - mu_error (_("%s: cannot stat: %s"), name, mu_strerror (errno)); - return 1; + mu_stream_destroy (&mu_strin); + mu_nullstream_create (&mu_strin, MU_STREAM_READ); + mu_stream_ioctl (mu_strin, MU_IOCTL_NULLSTREAM, + MU_IOCTL_NULLSTREAM_SET_PATTERN, NULL); + util_do_command ("set nullbody nullbodymsg"); } } - - if (!S_ISREG (st.st_mode)) + else if (realname) { - mu_error (_("%s: not a regular file"), name); - return 1; - } + if (!content_filename) + content_filename = realname; + if (stat (realname, &st)) + { + if (errno == ENOENT) + { + mu_error (_("%s: file does not exist"), realname); + return 1; + } + else + { + mu_error (_("%s: cannot stat: %s"), realname, + mu_strerror (errno)); + return 1; + } + } + + if (!S_ISREG (st.st_mode)) + { + mu_error (_("%s: not a regular file"), realname); + return 1; + } + rc = mu_mapfile_stream_create (&stream, realname, MU_STREAM_READ); + if (rc) + { + mu_error (_("can't open file %s: %s"), + realname, mu_strerror (rc)); + return 1; + } + mu_asprintf (&id, "\"%s\"", realname); + } + else + abort (); + if (!encoding) encoding = "base64"; mu_filter_get_list (&list); @@ -190,6 +232,8 @@ send_attach_file (const char *name, if (rc) { mu_error (_("unsupported encoding: %s"), encoding); + free (id); + mu_stream_destroy (&stream); return 1; } @@ -205,11 +249,13 @@ send_attach_file (const char *name, } aptr = mu_alloc (sizeof (*aptr)); + aptr->id = id; aptr->encoding = mu_strdup (encoding); aptr->content_type = mu_strdup (content_type ? - content_type : - "application/octet-stream"); - aptr->filename = mu_strdup (name); + content_type : "application/octet-stream"); + aptr->name = content_name ? mu_strdup (content_name) : NULL; + aptr->filename = content_filename ? mu_strdup (content_filename) : NULL; + aptr->source = stream; rc = mu_list_append (attlist, aptr); if (rc) { @@ -220,12 +266,6 @@ send_attach_file (const char *name, } int -send_attach_file_default (const char *name) -{ - return send_attach_file (name, default_content_type, default_encoding); -} - -int escape_list_attachments (int argc, char **argv, compose_env_t *env) { mu_iterator_t itr; @@ -246,7 +286,7 @@ escape_list_attachments (int argc, char **argv, compose_env_t *env) continue; mu_printf ("%3d %-12s %-30s %-s\n", - i, aptr->filename, aptr->content_type, aptr->encoding); + i, aptr->id, aptr->content_type, aptr->encoding); } mu_iterator_destroy (&itr); @@ -266,7 +306,8 @@ escape_attach (int argc, char **argv, compose_env_t *env) case 3: content_type = argv[2]; case 2: - return send_attach_file (argv[1], content_type, encoding); + return send_attach_file (-1, argv[1], argv[1], argv[1], + content_type, encoding); default: return escape_check_args (argc, argv, 2, 4); } @@ -309,12 +350,20 @@ saveatt (void *item, void *data) int rc; size_t nparts; char *p; - - rc = mu_message_create_attachment (aptr->content_type, aptr->encoding, - aptr->filename, &part); + + rc = mu_attachment_create (&part, aptr->content_type, aptr->encoding, + aptr->name, aptr->filename); + if (rc) + { + mu_error (_("can't create attachment %s: %s"), + aptr->id, mu_strerror (rc)); + return 1; + } + + rc = mu_attachment_copy_from_stream (part, aptr->source, aptr->encoding); if (rc) { - mu_error (_("cannot attach \"%s\": %s"), aptr->filename, + mu_error (_("cannot attach %s: %s"), aptr->filename, mu_strerror (rc)); return 1; } |