summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-06-08 23:45:01 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-06-09 00:28:01 +0300
commitb8faf6b5e5afb0303c9f4765ea919d89643706ce (patch)
tree1d13a25d15c79f32d36252d74aaed6eddbf4b09e
parentbd2aa03e8c8e880ad0bd8fe309f171bfca1b10b0 (diff)
downloadmailutils-b8faf6b5e5afb0303c9f4765ea919d89643706ce.tar.gz
mailutils-b8faf6b5e5afb0303c9f4765ea919d89643706ce.tar.bz2
mail: fix semantics of mail sending and saving commands
* NEWS: Document changes. * doc/texinfo/programs/mail.texi: Document changes. * mail/Makefile.am (mail_SOURCES): Remove followup.c * mail/followup.c: Remove. * mail/mail.c: Fix the -F option. Rewrite send mode by calling mail_send directly. * mail/mail.h (mailvar_name_byname): Remove. (mail_send0): Rename to mail_compose_send. All uses changed. (util_message_sender): New function. * mail/mailvar.c (mailvar_tab): Remove the "byname" variable. "outfolder": Accept string and boolean values. (print_descr): Handle hard newlines. (mailvar_get): Use mailvar_type_whatever to test whether a variable is set, regardless of its type. * mail/reply.c: Rewrite to match POSIX standard. This affects the following commands: "reply", "Reply", "followup", "Followup". * mail/send.c (compose_header_set): Use util_merge_addresses when operating on address headers. (send_message): In mailx compatibility mode, assume the sendmail:// scheme. (mail_send0): Rename to mail_compose_send. Add Date header before saving the message, to make sure mu_mailbox_append_message won't bail out. * mail/table.c: New command: "Mail". * mail/testsuite/mail/send.exp: Fix expected output. * mail/testsuite/mail/write.exp: Likewise. * mail/util.c (util_message_sender): New function. (util_get_sender): Rewrite using util_message_sender. (util_outfolder_name,util_save_outgoing): Rewrite from scratch, Ensure mailx compatibility, * mail/write.c (mail_write): Don't use outfolder. * testsuite/mockmail.c: Support -i and -v options. * testsuite/mockmail.at: Unset MAIL_DUMP when testing.
-rw-r--r--NEWS8
-rw-r--r--doc/texinfo/programs/mail.texi98
-rw-r--r--mail/Makefile.am1
-rw-r--r--mail/followup.c87
-rw-r--r--mail/mail.c26
-rw-r--r--mail/mail.h4
-rw-r--r--mail/mailvar.c23
-rw-r--r--mail/reply.c274
-rw-r--r--mail/send.c36
-rw-r--r--mail/table.c2
-rw-r--r--mail/testsuite/mail/send.exp5
-rw-r--r--mail/testsuite/mail/write.exp16
-rw-r--r--mail/util.c180
-rw-r--r--mail/write.c57
-rw-r--r--po/POTFILES.in1
-rw-r--r--testsuite/mockmail.at12
-rw-r--r--testsuite/mockmail.c13
17 files changed, 510 insertions, 333 deletions
diff --git a/NEWS b/NEWS
index 98bc1a233..9c2e361d5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU mailutils NEWS -- history of user-visible changes. 2021-05-01
+GNU mailutils NEWS -- history of user-visible changes. 2021-06-08
See the end of file for copying conditions.
Please send mailutils bug reports to <bug-mailutils@gnu.org>.
@@ -31,6 +31,12 @@ issued along with a suggestion on what statement to use instead:
The 'retry-timeout' statement is a misnomer and is therefore
deprecated. Use 'retry-sleep' instead.
+* mail: fix semantics of mail sending and saving commands
+
+* mail: fix the -F option
+
+* mail: improve POSIX mailx compatible mode
+
Version 3.12, 2021-02-13
diff --git a/doc/texinfo/programs/mail.texi b/doc/texinfo/programs/mail.texi
index f9cf26462..6355c588c 100644
--- a/doc/texinfo/programs/mail.texi
+++ b/doc/texinfo/programs/mail.texi
@@ -107,8 +107,7 @@ Sets content transfer encoding for use by the subsequent
@itemx --byname
Record outgoing messages in a file named after the first recipient.
The name is the login-name portion of the address found first on the
-@samp{To:} line in the mail header. This option sets the @samp{byname}
-variable, which see (@pxref{byname}).
+@samp{To:} line in the mail header.
@item -f
@itemx --file
@@ -872,7 +871,8 @@ command, unless the variable @code{keepsave} is set.
@deffn {Mail command} Save [@var{msglist}]
@deffnx {Mail command} S [@var{msglist}]
Like @code{save}, but the file to append messages to is named after the
-sender of the first message in @var{msglist}. For example:
+sender of the first message in @var{msglist}. The file name is
+selected as described in @ref{Mail}. For example:
@example
@cartouche
@@ -921,14 +921,14 @@ states}).
@deffn {Mail command} copy [[@var{msglist}] @var{file}]
@deffnx {Mail command} c [[@var{msglist}] @var{file}]
-Similar to @code{save}, except that saved messages are not marked for
-deletion.
+Similar to @code{save}, except that saved messages are not marked as
+saved.
@end deffn
@deffn {Mail command} Copy [@var{msglist}]
@deffnx {Mail command} C [@var{msglist}]
-Similar to @code{Save}, except that saved messages are not marked for
-deletion.
+Similar to @code{Save}, except that saved messages are not marked as
+saved.
@end deffn
@node Editing Messages
@@ -985,40 +985,61 @@ alternate names is displayed.
@deffn {Mail command} mail [@var{address}...]
@deffnx {Mail command} m [@var{address}...]
-Switches to compose mode. After composing the message, sends messages to
+Switches to compose mode. After composing the message, sends it to
the specified addresses.
+
+If the @code{record} variable is set, the composed message will be
+saved in the folder named by it.
+@end deffn
+
+@anchor{Mail}
+@deffn {Mail command} Mail [@var{address}...]
+@deffnx {Mail command} M [@var{address}...]
+Same as @code{mail}, but the name of the file to save the composed
+message is determined as follows:
+
+If the @code{outfolder} variable is set, and has a string value,
+the filename is @file{@code{outfolder}/@var{recipient}}. If if
+has a boolean value @samp{true}, the filename is
+@file{@code{folder}/@var{recipient}}. Otherwise, the message will not
+be saved.
@end deffn
-@deffn {Mail command} reply [@var{msglist}]
-@deffnx {Mail command} respond [@var{msglist}]
-@deffnx {Mail command} r [@var{msglist}]
-For each message in @var{msglist}, switches to compose mode and sends
-the composed message to the sender and all recipients of the message.
+@deffn {Mail command} reply [@var{message}]
+@deffnx {Mail command} respond [@var{message}]
+@deffnx {Mail command} r [@var{message}]
+Mail a reply message to all recipients included in the header of the
+@var{message}. The subject header is formed by concatenating the
+value of the @code{replyprefix} variable and the subject from the
+message. If @code{record} is set to a filename, the response is
+saved at the end of that file.
@end deffn
@deffn {Mail command} Reply [@var{msglist}]
@deffnx {Mail command} Respond [@var{msglist}]
@deffnx {Mail command} R [@var{msglist}]
-Like @code{reply}, except that the composed message is sent only to
-originators of the specified messages.
+Mail a reply message to the sender of each message in the @var{msglist}.
+The subject header is formed by concatenating the value of the
+@code{replyprefix} variable and the subject header of from the
+first message in @var{msglist}. If @code{record} is set to a
+filename, the response is saved at the end of that file.
Notice, that setting mail variable @code{flipr} (@pxref{Mail
-Variables}) swaps the meanings of the two above commands,
-so that @code{reply} sends the message to the sender and all
-recipients of the message, whereas @code{Reply} sends it to
-originators only.
+Variables}) swaps the meanings of the two above commands
@end deffn
-@deffn {Mail command} followup [@var{msglist}]
-@deffnx {Mail command} fo [@var{msglist}]
-Switches to compose mode. After composing, sends the message to the
-originators and recipients of all messages in @var{msglist}.
+@deffn {Mail command} followup [@var{message}]
+@deffnx {Mail command} fo [@var{message}]
+Respond to @var{message}, recording the response in a file whose name
+is derived from the author of the message. @xref{Mail}, for a
+detailed discussion of how the file name is selected.
@end deffn
@deffn {Mail command} Followup [@var{msglist}]
@deffnx {Mail command} F [@var{msglist}]
-Similar to @code{followup}, but reply message is sent only to
-originators of messages in @var{msglist}.
+Same as @code{Reply}, but the response is saved in a file whose name
+is derived from the author of the first message. @xref{Mail}, for a
+detailed discussion of how the file name is selected.
@end deffn
To determine the sender of the message @command{mail} uses the
@@ -1999,20 +2020,6 @@ message, the next one will be typed automatically.
When set, every occurrence of @code{!} in arguments to @code{!}
command is replaced with the last executed command.
-@anchor{byname}
-@kwindex byname
-@item byname
-@*Type: Boolean
-@*Default: Unset
-@vrindex byname, mail variable
-
-Record outgoing messages in a file named after the first recipient.
-The name is the login-name portion of the address found first on the
-@samp{To:} line in the mail header. This variable overrides the
-@samp{record} variable.
-
-It is set by the @option{--byname} (@option{-F}) command line option.
-
@anchor{datefield}
@kwindex datefield
@item datefield
@@ -2600,13 +2607,18 @@ This variable is not used. It exists for compatibility with other
@kwindex outfolder
@item outfolder
-@*Type: String.
+@*Type: Boolean or String.
@*Default: Unset.
@vrindex outfolder, mail variable
-Contains the directory in which files created by @code{save},
-@code{write}, etc. commands will be stored. When unset, current
-directory is assumed.
+When set as boolean, causes the files used to record outgoing messages
+to be located in the directory specified by the @code{folder} variable
+(unless the pathname is absolute).
+
+If string, its value names the directory where to store these files.
+
+This variable affects the following commands: @code{Copy},
+@code{Save}, @code{Mail}, @code{Reply}, and @code{Followup}.
@kwindex PID
@item PID
diff --git a/mail/Makefile.am b/mail/Makefile.am
index a72e5387a..cfbd93f79 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -51,7 +51,6 @@ mail_SOURCES = \
exit.c\
file.c\
folders.c\
- followup.c\
from.c\
headers.c\
help.c\
diff --git a/mail/followup.c b/mail/followup.c
deleted file mode 100644
index a6c054bb9..000000000
--- a/mail/followup.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/* GNU Mailutils -- a suite of utilities for electronic mail
- Copyright (C) 1999-2021 Free Software Foundation, Inc.
-
- GNU Mailutils is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
-
- GNU Mailutils is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "mail.h"
-
-/*
- * fo[llowup] [msglist] -- GNU extension
- * F[ollowup] [msglist]
- */
-
-int
-mail_followup (int argc, char **argv)
-{
- mu_message_t msg;
- mu_header_t hdr;
- char *str;
- msgset_t *msglist, *mp;
- compose_env_t env;
- int status;
- size_t n;
-
- compose_init (&env);
- if (msgset_parse (argc, argv, MSG_NODELETED, &msglist))
- return 1;
-
- n = get_cursor ();
- if (n == 0)
- {
- mu_error (_("No applicable message"));
- return 1;
- }
-
- if (util_get_message (mbox, n, &msg))
- {
- msgset_free (msglist);
- return 1;
- }
-
- /* Create subject value */
- mu_message_get_header (msg, &hdr);
- if (mu_header_aget_value (hdr, MU_HEADER_SUBJECT, &str) == 0)
- {
- char *p = NULL;
-
- if (mu_unre_subject (str, NULL))
- util_strcat (&p, util_reply_prefix ());
- util_strcat (&p, str);
- free (str);
- compose_header_set (&env, MU_HEADER_SUBJECT, p, COMPOSE_REPLACE);
- free (p);
- }
-
- /* Generate "to" list */
- compose_header_set (&env, MU_HEADER_TO, util_get_sender (get_cursor (), 0),
- COMPOSE_SINGLE_LINE);
-
- /* Add authors of the subsequent messages to the to list
- (or should it be cc?) */
- for (mp = msglist; mp; mp = mp->next)
- compose_header_set (&env, MU_HEADER_TO,
- util_get_sender (msgset_msgno (mp), 0),
- COMPOSE_SINGLE_LINE);
-
- msgset_free (msglist);
-
- mu_printf ("To: %s\n",
- compose_header_get (&env, MU_HEADER_TO, ""));
- mu_printf ("Subject: %s\n\n",
- compose_header_get (&env, MU_HEADER_SUBJECT, ""));
-
- status = mail_send0 (&env, mu_isupper (argv[0][0]));
- compose_destroy (&env);
- return status;
-}
diff --git a/mail/mail.c b/mail/mail.c
index 723fdc88a..538fd709a 100644
--- a/mail/mail.c
+++ b/mail/mail.c
@@ -31,6 +31,8 @@ const char *program_version = "mail (" PACKAGE_STRING ")";
#define HINT_SEND_MODE 0x1
#define HINT_FILE_OPTION 0x2
+#define HINT_BYNAME 0x4
+
int hint;
char *file;
char *user;
@@ -72,6 +74,7 @@ cli_command_option (struct mu_parseopt *po, struct mu_option *opt,
case 'r':
util_cache_command (&command_list, "set return-address=%s", arg);
+ hint |= HINT_SEND_MODE;
break;
case 'q':
@@ -81,7 +84,7 @@ cli_command_option (struct mu_parseopt *po, struct mu_option *opt,
case 't':
read_recipients = 1;
util_cache_command (&command_list, "set editheaders");
- util_cache_command (&command_list, "setq mode=send");
+ hint |= HINT_SEND_MODE;
break;
case 'H':
@@ -105,7 +108,8 @@ cli_command_option (struct mu_parseopt *po, struct mu_option *opt,
break;
case 'F':
- util_cache_command (&command_list, "set byname");
+ hint |= HINT_SEND_MODE;
+ hint |= HINT_BYNAME;
break;
case 0:
@@ -581,13 +585,17 @@ main (int argc, char **argv)
/* Mode is just sending */
if (strcmp (mode, "send") == 0)
{
- char *buf = NULL;
- int rc;
-
- mu_argcv_string (argc, argv, &buf);
- rc = util_do_command ("mail %s", buf);
- free (buf);
- return mailvar_is_true (mailvar_name_mailx) ? 0 : rc;
+ --argv;
+ ++argc;
+ if (hint & HINT_BYNAME)
+ argv[0] = "Mail";
+ else
+ argv[0] = "mail";
+ if (mail_send (argc, argv))
+ rc = EXIT_FAILURE;
+ if (mailvar_is_true (mailvar_name_mailx))
+ rc = 0;
+ return rc;
}
/* Or acting as a normal reader */
else
diff --git a/mail/mail.h b/mail/mail.h
index 4482b5148..d2451f222 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -166,7 +166,6 @@ struct mailvar_variable
#define mailvar_name_asksub "asksub"
#define mailvar_name_autoinc "autoinc"
#define mailvar_name_autoprint "autoprint"
-#define mailvar_name_byname "byname"
#define mailvar_name_bang "bang"
#define mailvar_name_charset "charset"
#define mailvar_name_cmd "cmd"
@@ -321,7 +320,7 @@ int if_cond (void);
void mail_mainloop (char *(*input) (void *, int), void *closure, int do_history);
int mail_copy0 (int argc, char **argv, int mark);
-int mail_send0 (compose_env_t *env, int save_to);
+int mail_compose_send (compose_env_t *env, int save_to);
void free_env_headers (compose_env_t *env);
/*extern void print_message (mu_message_t mesg, char *prefix, int all_headers, FILE *file);*/
@@ -487,6 +486,7 @@ char *util_get_homedir (void);
char *util_fullpath (const char *inpath);
char *util_folder_path (const char *name);
char *util_get_sender (int msgno, int strip);
+char *util_message_sender (mu_message_t msg, int strip);
void util_slist_print (mu_list_t list, int nl);
int util_slist_lookup (mu_list_t list, const char *str);
diff --git a/mail/mailvar.c b/mail/mailvar.c
index 64b49938e..aaa3dcded 100644
--- a/mail/mailvar.c
+++ b/mail/mailvar.c
@@ -85,10 +85,6 @@ struct mailvar_symbol mailvar_tab[] =
MAILVAR_TYPEMASK (mailvar_type_boolean),
/* TRANSLATORS: "delete" and "dp" are command names. */
N_("delete command behaves like dp") },
- { { mailvar_name_byname },
- MAILVAR_TYPEMASK (mailvar_type_boolean),
- N_("record outgoing messages in a file named after the first recipient; "
- "overrides the `record' variable") },
{ { mailvar_name_bang, },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("replace every occurrence of ! in arguments to the shell command"
@@ -204,8 +200,12 @@ struct mailvar_symbol mailvar_tab[] =
MAILVAR_TYPEMASK (mailvar_type_string),
N_("display this text when sending a message with empty body") },
{ { mailvar_name_outfolder, },
- MAILVAR_TYPEMASK (mailvar_type_string),
- N_("keep created files in this folder") },
+ MAILVAR_TYPEMASK (mailvar_type_string) |
+ MAILVAR_TYPEMASK (mailvar_type_boolean),
+ N_("If boolean, causes the files used to record outgoing messages to"
+ " be located in the directory specified by the folder variable"
+ " (unless the pathname is absolute).\n"
+ "If string, names the directory where to store these files.") },
{ { mailvar_name_page, },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("pipe command terminates each message with a formfeed") },
@@ -343,10 +343,15 @@ print_descr (mu_stream_t out, const char *s, int n,
mu_stream_write (out, " ", 1, NULL);
for (p = s; *p && p < s + (rmargin - doc_col); p++)
- if (mu_isspace (*p))
+ if (*p == '\n')
+ {
+ space = p;
+ break;
+ }
+ else if (mu_isspace (*p))
space = p;
- if (!space || p < s + (rmargin - doc_col))
+ if (!space || (*space != '\n' && p < s + (rmargin - doc_col)))
{
mu_stream_printf (out, "%s", s);
s += strlen (s);
@@ -439,7 +444,7 @@ mailvar_get (void *ptr, const char *variable, enum mailvar_type type, int warn)
{
struct mailvar_variable *var = mailvar_find_variable (variable, 0);
- if (!var->set || var->type != type)
+ if (!var->set || (type != mailvar_type_whatever && var->type != type))
{
if (warn)
mu_error (_("No value set for \"%s\""), variable);
diff --git a/mail/reply.c b/mail/reply.c
index 524204956..d304a58d3 100644
--- a/mail/reply.c
+++ b/mail/reply.c
@@ -38,100 +38,242 @@ make_references (compose_env_t *env, mu_message_t msg)
}
/*
- * r[eply] [msglist] -- GNU extension
- * r[espond] [msglist] -- GNU extension
+ * r[eply] [message]
+ * r[espond] [message]
+ * Reply to all recipients of the message. Save to record.
+ * reply_all = 1, send_to = 0
* R[eply] [msglist]
* R[espond] [msglist]
+ * Reply to the sender of each message in msglist. Save to record.
+ * reply_all = 0, send_to = 0
+ * fo[llowup] message
+ * Reply to all recipients of the message. Save by name.
+ * reply_all = 1, send_to = 1
+ * F[ollowup] msglist
+ * Reply to the sender of each message in msglist. Save by name.
+ * reply_all = 0, send_to = 1
*/
-int
-reply0 (msgset_t *mspec, mu_message_t msg, void *data)
+static void
+compose_set_address (compose_env_t *env, char const *hname, char const *input)
{
- mu_header_t hdr;
- compose_env_t env;
- int status;
- char *str;
- char const *rcpt;
-
- set_cursor (msgset_msgno (mspec));
+ struct mu_address hint = MU_ADDRESS_HINT_INITIALIZER;
+ mu_address_t iaddr, oaddr = NULL, ap;
+ char *result = NULL;
- compose_init (&env);
+ if (mu_address_create_hint (&iaddr, input, &hint, 0))
+ result = mu_strdup (input);
+ else
+ {
+ for (ap = iaddr; ap; ap = ap->next)
+ {
+ const char *email;
+ if (mu_address_sget_email (ap, 1, &email) || email == NULL)
+ continue;
+ if (!(mailvar_is_true (mailvar_name_metoo) &&
+ mail_is_my_name (email)))
+ mu_address_union (&oaddr, ap);
+ }
+ mu_address_destroy (&iaddr);
+ mu_address_aget_printable (oaddr, &result);
+ mu_address_destroy (&oaddr);
+ }
+ if (result && result[0])
+ {
+ compose_header_set (env, hname, result, COMPOSE_SINGLE_LINE);
+ free (result);
+ }
+}
- mu_message_get_header (msg, &hdr);
+/*
+ * r[eply] [message]
+ * r[espond] [message]
+ * fo[llowup] message
+ *
+ * Reply to all recipients of a single message
+ */
+int
+respond_all (int argc, char **argv, int record_sender)
+{
+ int status;
+ compose_env_t env;
+ mu_message_t msg;
+ mu_header_t hdr;
+ char const *str;
+ char *p;
- compose_header_set (&env, MU_HEADER_TO,
- util_get_sender (msgset_msgno (mspec), 0),
- COMPOSE_SINGLE_LINE);
+ msgset_t *msgset = NULL;
- if (*(int*) data) /* reply starts with a lowercase */
+ if (msgset_parse (argc, argv, MSG_NODELETED, &msgset))
+ return 1;
+
+ if (msgset->next)
{
- /* Add all recepients of the originate letter */
-
- mu_address_t addr = NULL;
- size_t i, count = 0;
+ mu_error (_("Can't reply to multiple messages at once"));
+ status = 1;
+ }
+ else if (util_get_message (mbox, msgset_msgno (msgset), &msg))
+ {
+ status = 1;
+ }
+ else
+ {
+ set_cursor (msgset_msgno (msgset));
+
+ mu_message_get_header (msg, &hdr);
+
+ compose_init (&env);
- if (mu_header_aget_value (hdr, MU_HEADER_TO, &str) == 0)
+ p = util_message_sender (msg, 0);
+ if (p)
{
- mu_address_create (&addr, str);
- free (str);
- mu_address_get_count (addr, &count);
+ compose_set_address (&env, MU_HEADER_TO, p);
+ free (p);
}
-
- /* Make sure we do not include our alternate names */
- for (i = 1; i <= count; i++)
+
+ /* Add the rest of recipients */
+ if (mu_header_sget_value (hdr, MU_HEADER_TO, &str) == 0)
{
- const char *email;
- if (mu_address_sget_email (addr, i, &email) || email == NULL)
- continue;
- if (mailvar_is_true (mailvar_name_metoo) || !mail_is_my_name (email))
- compose_header_set (&env, MU_HEADER_TO,
- email,
- COMPOSE_SINGLE_LINE);
+ compose_set_address (&env, MU_HEADER_TO, str);
+ }
+
+ if (mu_header_sget_value (hdr, MU_HEADER_CC, &str) == 0)
+ {
+ compose_set_address (&env, MU_HEADER_CC, str);
}
- mu_address_destroy (&addr);
+ /* Add header line */
+ //FIXME: decode
+ if (mu_header_aget_value (hdr, MU_HEADER_SUBJECT, &p) == 0)
+ {
+ char *subj = NULL;
+
+ if (mu_unre_subject (p, NULL))
+ util_strcat (&subj, util_reply_prefix ());
+ util_strcat (&subj, p);
+ free (p);
+ compose_header_set (&env, MU_HEADER_SUBJECT, subj, COMPOSE_REPLACE);
+ free (subj);
+ }
+ else
+ compose_header_set (&env, MU_HEADER_SUBJECT, "", COMPOSE_REPLACE);
- /* Finally, add any Ccs */
- if (mu_header_aget_value (hdr, MU_HEADER_CC, &str) == 0)
- compose_header_set (&env, MU_HEADER_TO, str, COMPOSE_SINGLE_LINE);
+ mu_printf ("To: %s\n", compose_header_get (&env, MU_HEADER_TO, ""));
+ str = compose_header_get (&env, MU_HEADER_CC, NULL);
+ if (str)
+ mu_printf ("Cc: %s\n", str);
+ mu_printf ("Subject: %s\n\n",
+ compose_header_get (&env, MU_HEADER_SUBJECT, ""));
+
+ make_in_reply_to (&env, msg);
+ make_references (&env, msg);
+ status = mail_compose_send (&env, record_sender);
+ compose_destroy (&env);
+ util_mark_read (msg);
}
+ msgset_free (msgset);
+
+ return status;
+}
- if (mu_header_aget_value (hdr, MU_HEADER_SUBJECT, &str) == 0)
+/*
+ * R[eply] [msglist]
+ * R[espond] [msglist]
+ * F[ollowup] msglist
+ *
+ * Reply to the sender of each message in msglist.
+ */
+int
+respond_msg (int argc, char **argv, int record_sender)
+{
+ mu_message_t msg;
+ mu_header_t hdr;
+ compose_env_t env;
+ int status;
+ char *p;
+ char const *str;
+
+ msgset_t *msgset = NULL, *mp;
+
+ if (msgset_parse (argc, argv, MSG_NODELETED, &msgset))
+ return 1;
+
+ if (util_get_message (mbox, msgset_msgno (msgset), &msg))
{
- char *p = NULL;
-
- if (mu_unre_subject (str, NULL))
- util_strcat (&p, util_reply_prefix ());
- util_strcat (&p, str);
- free (str);
- compose_header_set (&env, MU_HEADER_SUBJECT, p, COMPOSE_REPLACE);
- free (p);
+ status = 1;
}
else
- compose_header_set (&env, MU_HEADER_SUBJECT, "", COMPOSE_REPLACE);
-
- mu_printf ("To: %s\n",
- compose_header_get (&env, MU_HEADER_TO, ""));
- rcpt = compose_header_get (&env, MU_HEADER_CC, NULL);
- if (rcpt)
- mu_printf ("Cc: %s\n", rcpt);
- mu_printf ("Subject: %s\n\n",
- compose_header_get (&env, MU_HEADER_SUBJECT, ""));
+ {
+ size_t last;
+
+ set_cursor (msgset_msgno (msgset));
+
+ mu_message_get_header (msg, &hdr);
+
+ compose_init (&env);
+
+ for (mp = msgset; mp; mp = mp->next)
+ {
+ mu_message_t mesg;
+ last = msgset_msgno (mp);
+ if (util_get_message (mbox, last, &mesg) == 0)
+ {
+ p = util_message_sender (mesg, 0);
+ if (p)
+ {
+ compose_set_address (&env, MU_HEADER_TO, p);
+ free (p);
+ }
+ util_mark_read (mesg);
+ }
+ }
+
+ /* Add subject header */
+ if (mu_header_aget_value (hdr, MU_HEADER_SUBJECT, &p) == 0)
+ {
+ char *subj = NULL;
+
+ if (mu_unre_subject (p, NULL))
+ util_strcat (&subj, util_reply_prefix ());
+ util_strcat (&subj, p);
+ free (p);
+ compose_header_set (&env, MU_HEADER_SUBJECT, subj, COMPOSE_REPLACE);
+ free (subj);
+ }
+ else
+ compose_header_set (&env, MU_HEADER_SUBJECT, "", COMPOSE_REPLACE);
+
+ mu_printf ("To: %s\n", compose_header_get (&env, MU_HEADER_TO, ""));
+ str = compose_header_get (&env, MU_HEADER_CC, NULL);
+ if (str)
+ mu_printf ("Cc: %s\n", str);
+ mu_printf ("Subject: %s\n\n",
+ compose_header_get (&env, MU_HEADER_SUBJECT, ""));
- make_in_reply_to (&env, msg);
- make_references (&env, msg);
- status = mail_send0 (&env, mailvar_is_true (mailvar_name_byname));
- compose_destroy (&env);
+ make_in_reply_to (&env, msg);
+ make_references (&env, msg);
+ status = mail_compose_send (&env, record_sender);
+ compose_destroy (&env);
- return status;
+ set_cursor (last);
+ }
+ msgset_free (msgset);
+
+ return status;
}
int
mail_reply (int argc, char **argv)
{
- int lower = mu_islower (argv[0][0]);
+ int all = mu_islower (argv[0][0]);
if (mailvar_is_true (mailvar_name_flipr))
- lower = !lower;
- return util_foreach_msg (argc, argv, MSG_NODELETED, reply0, &lower);
+ all = !all;
+ return (all ? respond_all : respond_msg) (argc, argv, 0);
}
-
+
+int
+mail_followup (int argc, char **argv)
+{
+ int all = mu_islower (argv[0][0]);
+ return (all ? respond_all : respond_msg) (argc, argv, 1);
+}
diff --git a/mail/send.c b/mail/send.c
index 35a5bc335..556012bff 100644
--- a/mail/send.c
+++ b/mail/send.c
@@ -693,6 +693,7 @@ mail_send (int argc, char **argv)
compose_env_t env;
int status;
int save_to = mu_isupper (argv[0][0]);
+
compose_init (&env);
if (argc < 2)
@@ -764,7 +765,7 @@ mail_send (int argc, char **argv)
COMPOSE_REPLACE);
}
- status = mail_send0 (&env, save_to);
+ status = mail_compose_send (&env, save_to);
compose_destroy (&env);
return status;
}
@@ -943,8 +944,7 @@ compose_header_set (compose_env_t *env, const char *name,
if (mu_header_aget_value (env->header, name, &old_value) == 0
&& old_value[0])
{
- if (is_address_field (name)
- && mailvar_is_true (mailvar_name_inplacealiases))
+ if (is_address_field (name))
{
status = util_merge_addresses (&old_value, value);
if (status == 0)
@@ -1144,12 +1144,27 @@ save_dead_message (mu_message_t msg)
static int
send_message (mu_message_t msg)
{
+ char *mailer_url = NULL;
char *sendmail;
int status;
if (mailvar_get (&sendmail, mailvar_name_sendmail,
mailvar_type_string, 0) == 0)
{
+ if (mailvar_is_true (mailvar_name_mailx))
+ {
+ /*
+ * Mailx compatibility: assume sendmail:// scheme.
+ */
+ if (!mu_is_proto (sendmail))
+ {
+ status = mu_asprintf (&mailer_url, "sendmail://%s", sendmail);
+ if (status)
+ return status;
+ sendmail = mailer_url;
+ }
+ }
+
if (sendmail[0] == '/')
status = msg_to_pipe (sendmail, msg);
else
@@ -1205,6 +1220,7 @@ send_message (mu_message_t msg)
mu_error (_("Variable sendmail not set: no mailer"));
status = ENOSYS;
}
+ free (mailer_url);
return status;
}
@@ -1235,7 +1251,7 @@ message_add_date (mu_message_t msg)
mu_diag_funcall (MU_DIAG_ERROR, "mu_header_set_value", MU_HEADER_DATE, rc);
}
-/* mail_send0(): shared between mail_send() and mail_reply();
+/* mail_compose_send(): shared between mail_send() and mail_reply();
If the variable "record" is set, the outgoing message is
saved after being sent. If "save_to" argument is non-zero,
@@ -1251,7 +1267,7 @@ message_add_date (mu_message_t msg)
addresses on the command line and message contents on standard input. */
int
-mail_send0 (compose_env_t *env, int save_to)
+mail_compose_send (compose_env_t *env, int save_to)
{
int done = 0;
int rc;
@@ -1396,11 +1412,16 @@ mail_send0 (compose_env_t *env, int save_to)
if (status)
break;
+ message_add_date (msg);
+
/* Save outgoing message */
if (save_to)
{
- char const *rcpt = compose_header_get (env, MU_HEADER_TO, NULL);
- if (rcpt)
+ mu_header_t hdr;
+ char const *rcpt;
+
+ mu_message_get_header (msg, &hdr);
+ if (mu_header_sget_value (hdr, MU_HEADER_TO, &rcpt) == 0)
{
mu_address_t addr = NULL;
struct mu_address hint = MU_ADDRESS_HINT_INITIALIZER;
@@ -1460,7 +1481,6 @@ mail_send0 (compose_env_t *env, int save_to)
/* Do we need to Send the message on the wire? */
if (status == 0 && sendit)
{
- message_add_date (msg);
status = send_message (msg);
if (status)
{
diff --git a/mail/table.c b/mail/table.c
index 6f08ba725..ca4cbcfec 100644
--- a/mail/table.c
+++ b/mail/table.c
@@ -84,6 +84,8 @@ static const struct mail_command_entry mail_command_table[] = {
mail_list, no_compl },
{ "m", "mail", "m[ail] [address...]", EF_SEND,
mail_send, alias_compl },
+ { "M", "Mail", "M[ail] [address...]", EF_SEND,
+ mail_send, alias_compl },
{ "mb", "mbox", "mb[ox] [msglist]", 0,
mail_mbox, msglist_compl },
{ "n", "next", "n[ext] [message]", 0,
diff --git a/mail/testsuite/mail/send.exp b/mail/testsuite/mail/send.exp
index b7660d1ec..0cb18a5bc 100644
--- a/mail/testsuite/mail/send.exp
+++ b/mail/testsuite/mail/send.exp
@@ -27,7 +27,7 @@ if ![mu_check_capability ENABLE_SENDMAIL] {
mail_command "setenv MAIL_DUMP=\"$MU_FOLDER_DIR/mta.diag\""
mail_test -noprompt "reply 1" \
- "To: <foobar@nonexistent.net>,<bar@dontmailme.org>"\
+ "To: \"Foo Bar\" <foobar@nonexistent.net>,\"Bar\" <bar@dontmailme.org>"\
"Subject: Re: Jabberwocky"\
""
@@ -46,12 +46,13 @@ if ![mu_check_capability ENABLE_SENDMAIL] {
"RCPT\[0\]: <foobar@nonexistent.net>"\
"RCPT\[1\]: <bar@dontmailme.org>"\
-re {^LENGTH: [0-9]+}\
- "To: <foobar@nonexistent.net>,<bar@dontmailme.org>"\
+ "To: \"Foo Bar\" <foobar@nonexistent.net>,\"Bar\" <bar@dontmailme.org>"\
"Subject: Re: Jabberwocky"\
"In-Reply-To: Your message of Fri, 28 Dec 2001 22:18:08 +0200"\
"\t<200112282018.fBSKI8N04906@nonexistent.net>"\
"References: <200112282018.fBSKI8N04906@nonexistent.net>"\
"X-Mailer: mail (GNU Mailutils $MU_TOOL_VERSION)"\
+ -re {^Date:[^\r\n]*}\
""\
"> Received: (from foobar@nonexistent.net)"\
"> by nonexistent.net id fBSKI8N04906"\
diff --git a/mail/testsuite/mail/write.exp b/mail/testsuite/mail/write.exp
index ac8733611..c54b00d12 100644
--- a/mail/testsuite/mail/write.exp
+++ b/mail/testsuite/mail/write.exp
@@ -92,26 +92,26 @@ mail_test "headers" \
-re {>N 1 Sergey Poznyakoff Tue Jul 16 12:11 *[0-9]+/[0-9]+ *MBOX}
mail_exit
-# Test write
+# Test write
+cd "$MU_FOLDER_DIR"
mail_start -reuse-spool "--file=%teaparty.mbox"
-mail_command "set outfolder=\"$MU_FOLDER_DIR\""
mail_test "write" \
- -re "\"$MU_FOLDER_DIR/1\" *\[0-9\]+/\[0-9\]+"
+ -re "\"[pwd]/1\" *\[0-9\]+/\[0-9\]+"
mail_test "write 2 3 tmp" \
- -re "\"$MU_FOLDER_DIR/tmp\" *\[0-9\]+/\[0-9\]+"
+ -re "\"[pwd]/tmp\" *\[0-9\]+/\[0-9\]+"
mail_test "Write 1 2 3" \
- -re "\"$MU_FOLDER_DIR/hare\" *\[0-9\]+/\[0-9\]+"
+ -re "\"[pwd]/hare\" *\[0-9\]+/\[0-9\]+"
mail_exit
# Now examine the created files
-mu_test_file "$MU_FOLDER_DIR/1" "Have some wine"
-mu_test_file "$MU_FOLDER_DIR/tmp" \
+mu_test_file "1" "Have some wine"
+mu_test_file "tmp" \
"I don't see any wine" \
"There isn't any"
-mu_test_file "$MU_FOLDER_DIR/hare" \
+mu_test_file "hare" \
"Have some wine" \
"I don't see any wine" \
"There isn't any"
diff --git a/mail/util.c b/mail/util.c
index 9df98e0f7..214b89f85 100644
--- a/mail/util.c
+++ b/mail/util.c
@@ -487,14 +487,11 @@ util_folder_path (const char *name)
}
char *
-util_get_sender (int msgno, int strip)
+util_message_sender (mu_message_t msg, int strip)
{
- mu_message_t msg = NULL;
- mu_address_t addr = NULL;
- char *buf = NULL, *p;
-
- mu_mailbox_get_message (mbox, msgno, &msg);
- addr = get_sender_address (msg);
+ mu_address_t addr = get_sender_address (msg);
+ char *buf;
+
if (!addr)
{
mu_envelope_t env = NULL;
@@ -503,29 +500,47 @@ util_get_sender (int msgno, int strip)
if (mu_envelope_sget_sender (env, &buffer)
|| mu_address_create (&addr, buffer))
{
- mu_error (_("Cannot determine sender name (msg %d)"), msgno);
+ mu_error (_("Cannot determine sender name"));
return NULL;
}
}
- if (mu_address_aget_email (addr, 1, &buf) || !buf)
- {
- mu_error (_("Cannot determine sender name (msg %d)"), msgno);
- mu_address_destroy (&addr);
- return NULL;
- }
-
if (strip)
{
+ char *p;
+ if (mu_address_aget_email (addr, 1, &buf) || !buf)
+ {
+ mu_error (_("Cannot determine sender name"));
+ mu_address_destroy (&addr);
+ return NULL;
+ }
+
p = strchr (buf, '@');
if (p)
*p = 0;
}
-
+ else if (mu_address_aget_printable (addr, &buf) || !buf)
+ {
+ mu_error (_("Cannot determine sender name"));
+ mu_address_destroy (&addr);
+ return NULL;
+ }
+
mu_address_destroy (&addr);
return buf;
}
+char *
+util_get_sender (int msgno, int strip)
+{
+ mu_message_t msg;
+ if (mu_mailbox_get_message (mbox, msgno, &msg) == 0)
+ {
+ return util_message_sender (msg, strip);
+ }
+ return 0;
+}
+
void
util_slist_print (mu_list_t list, int nl)
{
@@ -648,47 +663,91 @@ util_strcat (char **dest, const char *str)
char *
util_outfolder_name (char *str)
{
+ char *template = NULL;
+ char *folder;
char *outfolder;
- char *exp;
int rc;
if (!str)
- return NULL;
-
- switch (*str)
{
- case '/':
- case '~':
- case '+':
- rc = mu_mailbox_expand_name (str, &exp);
- if (rc)
+ mailvar_get (&template, mailvar_name_record, mailvar_type_string, 0);
+ str = template;
+ }
+ else
+ {
+ switch (*str)
{
- mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_expand_name", str, rc);
- return NULL;
+ case '/':
+ case '~':
+ case '+':
+ break;
+
+ default:
+ if (mailvar_is_true (mailvar_name_mailx))
+ {
+ if (mailvar_get (NULL, mailvar_name_outfolder,
+ mailvar_type_whatever, 0) == 0)
+ {
+ if (mailvar_get (&folder, mailvar_name_folder,
+ mailvar_type_string, 0) == 0)
+ template = mu_make_file_name (folder, str);
+ }
+ str = template;
+ }
+ else if (mailvar_get (&outfolder, mailvar_name_outfolder,
+ mailvar_type_string, 0) == 0)
+ {
+ str = template = mu_make_file_name (outfolder, str);
+ }
+ else if (mailvar_is_true (mailvar_name_outfolder))
+ {
+ if (mailvar_get (&folder, mailvar_name_folder,
+ mailvar_type_string, 0) == 0)
+ template = mu_make_file_name (folder, str);
+ str = template;
+ }
+ else
+ str = NULL;
+ break;
}
- break;
+ }
+
+ if (str)
+ {
+ char *tilde_template = NULL;
+ char *exp;
- default:
- if (mailvar_get (&outfolder, mailvar_name_outfolder,
- mailvar_type_string, 0) == 0)
+ if (mailvar_is_true (mailvar_name_mailx))
{
- char *s = mu_make_file_name (outfolder, str);
- rc = mu_mailbox_expand_name (s, &exp);
- if (rc)
+ switch (*str)
{
- mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_expand_name", s, rc);
- free (s);
- return NULL;
+ case '/':
+ case '~':
+ case '+':
+ break;
+
+ default:
+ if (mu_asprintf (&tilde_template, "~/%s", str))
+ {
+ mu_alloc_die ();
+ }
+ str = tilde_template;
}
- free (s);
+ }
+
+ rc = mu_mailbox_expand_name (str, &exp);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_expand_name", str, rc);
+ str = NULL;
}
else
- exp = mu_strdup (str);
- break;
-
+ str = exp;
+ free (tilde_template);
}
-
- return exp;
+ free (template);
+
+ return str;
}
/* Save an outgoing message. The SAVEFILE argument overrides the setting
@@ -696,38 +755,35 @@ util_outfolder_name (char *str)
void
util_save_outgoing (mu_message_t msg, char *savefile)
{
- char *record;
-
- if (mailvar_get (&record, mailvar_name_record, mailvar_type_string, 0) == 0)
+ char *filename = util_outfolder_name (savefile);
+ if (filename)
{
int rc;
mu_mailbox_t outbox;
- char *filename = util_outfolder_name (savefile ? savefile : record);
rc = mu_mailbox_create_default (&outbox, filename);
if (rc)
{
mu_error (_("Cannot create output mailbox `%s': %s"),
- filename, mu_strerror (rc));
- free (filename);
- return;
- }
-
- rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
- if (rc)
- mu_error (_("Cannot open output mailbox `%s': %s"),
filename, mu_strerror (rc));
+ }
else
{
- rc = mu_mailbox_append_message (outbox, msg);
+ rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
if (rc)
- mu_error (_("Cannot append message to `%s': %s"),
- filename, mu_strerror (rc));
+ mu_error (_("Cannot open output mailbox `%s': %s"),
+ filename, mu_strerror (rc));
+ else
+ {
+ rc = mu_mailbox_append_message (outbox, msg);
+ if (rc)
+ mu_error (_("Cannot append message to `%s': %s"),
+ filename, mu_strerror (rc));
+ }
+
+ mu_mailbox_close (outbox);
+ mu_mailbox_destroy (&outbox);
}
-
- mu_mailbox_close (outbox);
- mu_mailbox_destroy (&outbox);
-
free (filename);
}
}
diff --git a/mail/write.c b/mail/write.c
index 52247aea1..242afc4e1 100644
--- a/mail/write.c
+++ b/mail/write.c
@@ -28,43 +28,44 @@ mail_write (int argc, char **argv)
int rc;
mu_stream_t output;
char *filename = NULL;
+ char *namebuf = NULL;
msgset_t *msglist = NULL, *mp;
- int sender = 0;
size_t total_size = 0, total_lines = 0;
if (mu_isupper (argv[0][0]))
- sender = 1;
- else if (argc >= 2)
- filename = util_outfolder_name (argv[--argc]);
- else
- {
- size_t n = get_cursor ();
- char *p = NULL;
- if (n == 0)
- {
- mu_error (_("No applicable message"));
- return 1;
- }
- mu_asprintf (&p, "%lu", (unsigned long) n);
- filename = util_outfolder_name (p);
- free (p);
- }
-
- if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
{
- if (filename)
- free (filename);
- return 1;
+ if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
+ return 1;
+ filename = namebuf = util_get_sender (msgset_msgno (msglist), 1);
}
-
- if (sender)
+ else
{
- filename = util_outfolder_name (util_get_sender (msgset_msgno (msglist), 1));
- if (!filename)
+ if (argc >= 2)
{
- msgset_free (msglist);
- return 1;
+ filename = argv[--argc];
}
+ else
+ {
+ size_t n = get_cursor ();
+ if (n == 0)
+ {
+ mu_error (_("No applicable message"));
+ return 1;
+ }
+ mu_asprintf (&namebuf, "%lu", (unsigned long) n);
+ filename = namebuf;
+ }
+ if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
+ return 1;
+ }
+
+ rc = mu_mailbox_expand_name (filename, &filename);
+ free (namebuf);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_expand_name", NULL, rc);
+ msgset_free (msglist);
+ return 1;
}
rc = mu_file_stream_create (&output, filename,
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c42134ec6..53a3680b3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -73,7 +73,6 @@ mail/decode.c
mail/escape.c
mail/file.c
mail/folders.c
-mail/followup.c
mail/if.c
mail/inc.c
mail/mail.c
diff --git a/testsuite/mockmail.at b/testsuite/mockmail.at
index 20fd7d199..c3ffee520 100644
--- a/testsuite/mockmail.at
+++ b/testsuite/mockmail.at
@@ -36,7 +36,8 @@ Message text.
Please discard.
])
-AT_CHECK([mockmail root < msg0
+AT_CHECK([unset MAIL_DUMP
+mockmail root < msg0
cat mail.dump
],
[0],
@@ -54,7 +55,8 @@ Please discard.
]])
-AT_CHECK([mockmail -oi gray root < msg
+AT_CHECK([unset MAIL_DUMP
+mockmail -oi gray root < msg
cat mail.dump
],
[0],
@@ -72,7 +74,8 @@ Please discard.
]])
-AT_CHECK([mockmail -oi -f gray@example.com root < msg
+AT_CHECK([unset MAIL_DUMP
+mockmail -oi -f gray@example.com root < msg
cat mail.dump
],
[0],
@@ -90,7 +93,8 @@ Please discard.
]])
-AT_CHECK([mockmail -oi -t < msg
+AT_CHECK([unset MAIL_DUMP
+mockmail -oi -t < msg
cat mail.dump
],
[0],
diff --git a/testsuite/mockmail.c b/testsuite/mockmail.c
index 706fd8933..bae7381e4 100644
--- a/testsuite/mockmail.c
+++ b/testsuite/mockmail.c
@@ -3,7 +3,7 @@
mockmail - mock Sendmail binary for use in test suites
SYNOPSIS
- mockmta [-bm] [-f EMAIL] [-t] [-oi] [EMAIL ...]
+ mockmail [-bm] [-f EMAIL] [-itv] [-oi] [EMAIL ...]
DESCRIPTION
Mimicks the behavior of "sendmail -bm". Instead of delivering
@@ -23,9 +23,11 @@
OPTIONS
-bm Ignored for compatibility with Sendmail.
-f EMAIL Sets sender email address.
+ -i Same as -oi.
-t Read recipients from the message.
-oi Don't expect the incoming message to be terminated with
a dot. This also turns off dot unstuffing.
+ -v Ignored for compatibility with Sendmail.
ENVIRONMENT
@@ -290,7 +292,7 @@ main (int argc, char **argv)
progname = argv[0];
- while ((c = getopt (argc, argv, "b:f:to:")) != EOF)
+ while ((c = getopt (argc, argv, "b:f:ito:v")) != EOF)
{
switch (c)
{
@@ -306,6 +308,10 @@ main (int argc, char **argv)
from_person = optarg;
break;
+ case 'i':
+ dot = 0;
+ break;
+
case 't':
read_recipients = 1;
break;
@@ -322,6 +328,9 @@ main (int argc, char **argv)
}
break;
+ case 'v':
+ break;
+
default:
exit (EX_USAGE);
}

Return to:

Send suggestions and report system problems to the System administrator.