summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS52
-rw-r--r--configure.ac2
-rw-r--r--doc/texinfo/Makefile.am3
-rw-r--r--doc/texinfo/mailutils.texi22
-rw-r--r--doc/texinfo/programs.texi141
-rw-r--r--doc/texinfo/programs/dotlock.texi62
-rw-r--r--doc/texinfo/programs/mail.texi920
-rw-r--r--dotlock/dotlock.c28
-rw-r--r--imap4d/imap4d.c3
-rw-r--r--imap4d/imap4d.h4
-rw-r--r--imap4d/list.c68
-rw-r--r--imap4d/namespace.c43
-rw-r--r--imap4d/tests/.gitignore1
-rw-r--r--imap4d/tests/Makefile.am3
-rw-r--r--imap4d/tests/atlocal.in2
-rw-r--r--imap4d/tests/clt_list.at107
-rw-r--r--imap4d/tests/list.at73
-rw-r--r--imap4d/tests/testclient.c191
-rw-r--r--imap4d/tests/testsuite.at3
-rw-r--r--include/mailutils/address.h3
-rw-r--r--include/mailutils/locker.h198
-rw-r--r--include/mailutils/sys/imap.h4
-rw-r--r--lib/manlock.c20
-rw-r--r--libmailutils/address/addrstream.c34
-rw-r--r--libmailutils/base/amd.c2
-rw-r--r--libmailutils/base/locker.c1276
-rw-r--r--libmailutils/base/spawnvp.c117
-rw-r--r--libmailutils/cli/stdcapa.c104
-rw-r--r--libmailutils/list/clear.c1
-rw-r--r--libmailutils/mailbox/folder.c1
-rw-r--r--libmailutils/mailbox/fsfolder.c13
-rw-r--r--libmailutils/mailbox/mbx_default.c2
-rw-r--r--libmailutils/mime/mime.c212
-rw-r--r--libmailutils/string/mkfilename.c63
-rw-r--r--libmailutils/tests/fsfolder.c8
-rw-r--r--libmailutils/tests/fsfolder00.at22
-rw-r--r--libmailutils/tests/fsfolder02.at2
-rw-r--r--libmailutils/tests/fsfolder03.at14
-rw-r--r--libmailutils/tests/fsfolder04.at12
-rw-r--r--libmailutils/tests/lck.c126
-rw-r--r--libmailutils/tests/lock.at92
-rw-r--r--libmu_dbm/berkeley.c11
-rw-r--r--libmu_sieve/extensions/vacation.c2
-rw-r--r--libproto/dotmail/dotmail.c4
-rw-r--r--libproto/imap/folder.c141
-rw-r--r--libproto/imap/genlist.c29
-rw-r--r--libproto/imap/tests/imapfolder.c24
-rw-r--r--libproto/mbox/mboxrd.c15
-rw-r--r--mail/Makefile.am1
-rw-r--r--mail/copy.c46
-rw-r--r--mail/exit.c1
-rw-r--r--mail/folders.c100
-rw-r--r--mail/followup.c87
-rw-r--r--mail/mail.c28
-rw-r--r--mail/mail.h28
-rw-r--r--mail/mailline.c80
-rw-r--r--mail/mailvar.c92
-rw-r--r--mail/reply.c317
-rw-r--r--mail/send.c223
-rw-r--r--mail/table.c8
-rw-r--r--mail/tests/Followup.at197
-rw-r--r--mail/tests/Makefile.am9
-rw-r--r--mail/tests/Reply.at158
-rw-r--r--mail/tests/atlocal.in1
-rw-r--r--mail/tests/followup.at148
-rw-r--r--mail/tests/reply.at109
-rw-r--r--mail/tests/send.at45
-rw-r--r--mail/tests/sendbyname.at119
-rw-r--r--mail/tests/sendrec.at64
-rw-r--r--mail/tests/testsuite.at10
-rw-r--r--mail/testsuite/mail/send.exp5
-rw-r--r--mail/testsuite/mail/write.exp16
-rw-r--r--mail/util.c409
-rw-r--r--mail/write.c57
-rw-r--r--mda/lmtpd/lmtpd.c7
-rw-r--r--mda/mda/mda.c9
-rw-r--r--mda/putmail/putmail.c7
-rw-r--r--mh/tests/mhn.at8
-rw-r--r--po/POTFILES.in1
-rw-r--r--sieve/tests/vacation.at45
-rw-r--r--testsuite/mockmail.at12
-rw-r--r--testsuite/mockmail.c13
82 files changed, 4593 insertions, 2147 deletions
diff --git a/NEWS b/NEWS
index 4f0825bf2..480740b0b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,58 @@
-GNU mailutils NEWS -- history of user-visible changes. 2021-02-13
+GNU mailutils NEWS -- history of user-visible changes. 2021-06-10
See the end of file for copying conditions.
Please send mailutils bug reports to <bug-mailutils@gnu.org>.
+Version 3.12.90 (git)
+
+* Improved mailbox locking
+
+This fixes several bugs.
+
+* Changes in the 'locking' configuration statement.
+
+New statement 'type' configures the type of locking to use. Possible
+values are: 'default', 'dotlock', 'external', 'kernel', and 'null'.
+
+New statement 'pid-check' instructs mailutils to check if the PID of
+the process owning the lock still exists in the process table. If it
+doesn't the lock is considered stale and is removed.
+
+The 'flags' statement is deprecated. If used, an error message is
+issued along with a suggestion on what statement to use instead:
+
+ Flag Statement to use
+ --------+-------------------
+ E type external
+ R retry-count N
+ T expire-timeout N
+ P pid-check true
+
+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
+
+* New mail variables
+
+ - fullnames
+ Controls whether personal parts of emails are preserved in
+ recipient addresses when replying. Default is true.
+ - outfilename
+ Defines the algorithm to convert the recipient email to the
+ name of the file used to record outgoing messages to that
+ recipient. This affects the following commands: Copy,
+ Save, Mail, followup, and Followup. This is a string variable,
+ that can take one of the following values:
+ "local" - Local part of the email address is taken as the
+ file name. This is the default.
+ "email" - Entire email is takes as the file name.
+ "domain" - Domain part of the email is used as the file name.
+
Version 3.12, 2021-02-13
* Fix expunging of mailboxes in mbox and dotmail format in a read-only directory
diff --git a/configure.ac b/configure.ac
index 479360a99..a6f361444 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@ dnl You should have received a copy of the GNU General Public License along
dnl with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.63)
-AC_INIT([GNU Mailutils], [3.12], [bug-mailutils@gnu.org], [mailutils],
+AC_INIT([GNU Mailutils], [3.12.90], [bug-mailutils@gnu.org], [mailutils],
[http://mailutils.org])
AC_CONFIG_SRCDIR([libmailutils/mailbox/mailbox.c])
AC_CONFIG_AUX_DIR([build-aux])
diff --git a/doc/texinfo/Makefile.am b/doc/texinfo/Makefile.am
index 92a3c4d5c..186415d0d 100644
--- a/doc/texinfo/Makefile.am
+++ b/doc/texinfo/Makefile.am
@@ -44,7 +44,8 @@ programs_TEXINFOS = \
programs/pop3d.texi\
programs/imap4d.texi\
programs/comsatd.texi\
- programs/mailutils.texi
+ programs/mailutils.texi\
+ programs/dotlock.texi
clean-local:
rm -rf manual
diff --git a/doc/texinfo/mailutils.texi b/doc/texinfo/mailutils.texi
index 4a54dfd4a..1c0b22cde 100644
--- a/doc/texinfo/mailutils.texi
+++ b/doc/texinfo/mailutils.texi
@@ -138,10 +138,9 @@ Mailutils Programs
* messages:: Count the Number of Messages in a Mailbox.
* movemail:: Moves Mail from the User Maildrop to the Local File.
* readmsg:: Extract Messages from a Folder.
+* decodemail:: Decode multipart messages.
-* decodemail:: Decode MIME messages.
* sieve:: Mail Filtering Utility.
-
* guimb:: Mailbox Scanning and Processing Language.
* mda:: Local Mail Delivery Agent.
@@ -157,6 +156,7 @@ Mailutils Programs
* mh:: The MH Message Handling System.
* mailutils:: The Mailutils Multi-Purpose Tool.
+* dotlock:: The External Locker Utility.
Command Line
@@ -172,6 +172,7 @@ Mailutils Configuration File
* logging statement::
* debug statement::
* mailbox statement::
+* mime statement::
* locking statement::
* mailer statement::
* acl statement::
@@ -208,6 +209,7 @@ Debugging
* Invoking Mail:: Command Line Options.
* Reading Mail:: Reading Mail.
+* Saving and Recording:: Where Mail Messages are Stored.
* Composing Mail:: Composing Mail.
* MIME:: How to Attach Files.
* Scripting:: Scripting.
@@ -261,6 +263,12 @@ Composing Mail
* Opt-readmsg:: Invocation of @command{readmsg}.
* Conf-readmsg:: Configuration of @command{readmsg}.
+@command{decodemail} -- Decode multipart messages
+
+* Opt-decodemail:: Invocation of @command{decodemail}.
+* Conf-decodemail:: Configuration of @command{decodemail}.
+* Using-decodemail:: Purpose and caveats of @command{decodemail}.
+
@command{sieve}
* sieve interpreter:: A Sieve Interpreter
@@ -301,6 +309,15 @@ Scripting in @command{mda}
* Scheme MDA Filters::
* Python MDA Filters::
+lmtpd
+
+* MeTA1-lmtpd:: Using @command{lmtpd} with MeTA1.
+
+putmail
+
+* putmail options::
+* putmail configuration::
+
mimeview
* Mimeview Invocation::
@@ -361,6 +378,7 @@ mailutils
* mailutils imap:: IMAP4 client shell.
* mailutils send:: Send a message.
* mailutils smtp:: Run a SMTP session.
+* mailutils maildir_fixup:: Fix-up maildirs created by versions prior to 3.10.90
mailutils dbm
diff --git a/doc/texinfo/programs.texi b/doc/texinfo/programs.texi
index 5e4a07694..88d8d5a26 100644
--- a/doc/texinfo/programs.texi
+++ b/doc/texinfo/programs.texi
@@ -46,6 +46,7 @@ syntax.
* mh:: The MH Message Handling System.
* mailutils:: The Mailutils Multi-Purpose Tool.
+* dotlock:: The External Locker Utility.
@end menu
@node command line
@@ -1281,74 +1282,128 @@ pattern list, which contains:
@example
locking @{
# @r{Default locker flags.}
- flags @var{arg};
-
- # @r{Set timeout for acquiring the lock.}
- retry-timeout @var{arg};
+ type @code{default} | @code{dotlock} | @code{external} | @code{kernel} | @code{null};
# @r{Set the maximum number of times to retry acquiring the lock.}
retry-count @var{number};
+ # @r{Set the delay between two successive locking attempts.}
+ retry-sleep @var{arg};
+
# @r{Expire locks older than this amount of time.}
expire-timeout @var{number};
-
+
+ # @r{Check if PID of the lock owner is active, steal the lock if it not.}
+ pid-check @var{bool};
+
# @r{Use @var{prog} as external locker program.}
external-locker @var{prog};
@}
@end example
@subheading Description
-This block statement configures various parameters used when locking
+This compound statement configures various parameters used when locking
UNIX mailboxes in order to prevent simultaneous writes.
-It is important to note, that locking applies only to traditional
-UNIX mailboxes (@pxref{mbox}). All other
-mailbox types don't require locking.
+It is important to note, that locking applies only to monolithic
+mailboxes, i.e. mailboxes of @samp{mbox} and @samp{dotmail} types
+(@pxref{mbox}). Other mailbox types don't require locking.
-@deffn {Configuration} flags @var{string}
-Set locking flags. Argument is a string consisting of one or more of
-the following letters:
+@deffn {Configuration} type @var{string}
+Set locking type. Allowed arguments are:
-@table @asis
-@item E
-Use an external program to manage locks. The program is given by the
-@code{external-locker} statement (see below).
-
-@item R
-If the locking attempt failed, retry it. This is the default. The
-number of retries, and time interval between the two successive
-attempts is given by @code{retry-count} and @code{retry-timeout}
-statements, correspondingly.
-
-@item T
-If a lock file exists, check its modification time and, if it is
-older than a predefined amount of time, remove the lock. The amount
-of time is specified by @code{expire-timeout} statement.
-
-@item P
-Store the PID of the locking process in a lock file.
+@table @code
+@item default
+Default locking type. As of @command{mailutils} version
+@value{VERSION}, this is equivalent to @code{dotlock}.
+
+@item dotlock
+@anchor{dotlock locking type}
+A @samp{dotlock}-style locking. To lock a mailbox named @var{X}
+a @dfn{lock file} named @file{@var{X}.lock} is created. If
+@code{pid-check yes} is set, this file will contain the PID of the
+locking process, so that another process wishing to acquire the lock
+could verify if the lock is still in use.
+
+@item external
+@anchor{external locking type}
+Run external program to perform locking/unlocking operations. The
+name of the program is given by the @code{external-locker} statement
+(see below). If it is not given, the built-in default @samp{dotlock}
+is used.
+
+The locker program is invoked as follows:
+
+@example
+# To lock @var{mbox}:
+@var{locker} -f@var{expire_timeout} -r@var{retry_count} @var{mbox}
+# To unlock it:
+@var{locker} -u -f@var{expire_timeout} -r@var{retry_count} @var{mbox}
+@end example
+
+@noindent
+Here, @var{expire_timeout} is the value supplied with the
+@code{expire-timeout} configuration statement, and @var{retry_count}
+is the value supplied with the @code{retry-count} statement (see below).
+
+To properly interact with @command{mailutils}, the external locker
+program must use the following exit codes:
+
+@multitable @columnfractions 0.3 0.6
+@headitem Exit code @tab Meaning
+@item 0 @tab Success.
+@item 1 @tab Failed due to an error.
+@item 2 @tab Unlock requested (@option{-u}), but file is not locked.
+@item 3 @tab Lock requested, but file is already locked.
+@item 4 @tab Insufficient permissions.
+@end multitable
+
+@xref{dotlock}, for the description of the default external locker,
+shipped with mailutils.
+
+@item kernel
+Use kernel locking mechanism (@code{fcntl}(2)).
+
+@item null
+No locking at all. The statements below are silently ignored.
@end table
@end deffn
@deffn {Configuration} retry-count @var{number}
-Number of locking attempts. The @samp{P} flag must be set for this to
-take effect.
+Number of locking attempts. The default is 10.
@end deffn
-@deffn {Configuration} retry-timeout @var{seconds}
-Time interval, in seconds, between the two successive locking
-attempts. The @samp{P} flag must be set for this to take effect.
+@deffn {Configuration} retry-sleep @var{seconds}
+@deffnx {Configuration} retry-timeout @var{seconds}
+Time interval, in seconds, between two successive locking
+attempts. The default is 1 second. The @code{retry-timeout}
+statement is deprecated because of its misleading name.
@end deffn
@deffn {Configuration} expire-timeout @var{seconds}
-Remove existing lock file, if it is created more than this number of
-seconds ago. The @samp{T} flag must be set for this to take effect.
+Sets the expiration timeout. The existing lock file will be removed,
+if it was created more than this number of seconds ago. The default
+is 600.
+@end deffn
+
+@deffn {Configuration} pid-check @var{bool}
+This statement can be used if locking type is set to @code{dotlock}.
+If set to @code{true}, it instructs the locking algorithm to check
+if the PID of the lock owner is still running by the time when it
+tries to acquire the lock. This works as follows. When the lock file
+is created, the PID of the creating process is written to it. If
+another process tries to acquire the lock and sees that the lock file
+already exists, it reads the PID from the file and checks if a process
+with that PID still exists in the process table. If it does not, the
+process considers the lock file to be stale, removes it and locks the
+mailbox.
@end deffn
@deffn {Configuration} external-locker @var{string}
-Determines the external locker program to use. The @var{string}
-argument is the valid command line, starting with the full program
-name. The @samp{E} flag must be set for this to take effect.
+Sets the name of the external locker program to use, instead of the
+default @samp{dotlock}.
+
+This statement is in effect only when used together with @code{type external}.
@end deffn
@node mailer statement
@@ -1373,6 +1428,7 @@ The mailer statement contains a single sub-statement:
Set the mailer @acronym{URL}.
@end deffn
+@anchor{mailer URL}
GNU Mailutils supports three types of mailer @acronym{URL}s, described
in the table below:
@@ -2990,3 +3046,8 @@ Show payload information
@node mailutils
@section mailutils
@include programs/mailutils.texi
+
+@page
+@node dotlock
+@section dotlock
+@include programs/dotlock.texi
diff --git a/doc/texinfo/programs/dotlock.texi b/doc/texinfo/programs/dotlock.texi
new file mode 100644
index 000000000..2f7be2e8a
--- /dev/null
+++ b/doc/texinfo/programs/dotlock.texi
@@ -0,0 +1,62 @@
+@pindex dotlock
+
+A stand-alone mailbox-locking utility. It is the default program used
+by @command{mailutils} if the @code{locking.type} configuration
+statement is set to @code{external} (@pxref{external locking type}).
+
+The program usage syntax is:
+
+@example
+@group
+# To lock @var{mbox}:
+dotlock @var{options} @var{mbox}
+# To unlock it:
+dotlock -u @var{options} @var{mbox}
+@end group
+@end example
+
+By default the program implements the @samp{dotlock} locking
+(@pxref{dotlock locking type}). This can be changed either in the
+configuration file, or via the command line options.
+
+The following common configuration statements affect the behavior of
+@command{dotlock}:
+
+@multitable @columnfractions 0.3 0.6
+@headitem Statement @tab Reference
+@item debug @tab @xref{Debug Statement}.
+@item locking @tab @xref{Locking Statement}.
+@end multitable
+
+The program understands the following command line options:
+
+@table @option
+@item -d
+@itemx --debug
+Print details of failure reasons to stderr.
+
+@item -f[@var{n}]
+@itemx --force[=@var{n}]
+If a lock file exists and is more than @var{n} minutes old, forcibly
+remove it and re-lock the mailbox. Default @var{n} is 10 minutes.
+
+@item -p
+@itemx --pid-check
+Check if the PID of lock owner is still active. If not, break the
+lock.
+
+@item -r @var{n}
+@item --retry=@var{n}
+Number of times to retry acquiring the lock, if it is held by another
+process. The default is 10 times.
+
+@item -t @var{n}
+@itemx --delay=@var{n}
+Sets delay in seconds between two successive locking attempts.
+The default is 1 second.
+
+@item -u
+@itemx --unlock
+Unlock the mailbox.
+@end table
+
diff --git a/doc/texinfo/programs/mail.texi b/doc/texinfo/programs/mail.texi
index f9cf26462..569d78034 100644
--- a/doc/texinfo/programs/mail.texi
+++ b/doc/texinfo/programs/mail.texi
@@ -27,6 +27,7 @@ Configuration Files}, for a detailed description of their format.
@menu
* Invoking Mail:: Command Line Options.
* Reading Mail:: Reading Mail.
+* Saving and Recording:: Where Mail Messages are Stored.
* Composing Mail:: Composing Mail.
* MIME:: How to Attach Files.
* Scripting:: Scripting.
@@ -107,8 +108,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
@@ -737,7 +737,7 @@ Displays current mailbox summary. E.g.:
Prints out the messages from @var{msglist}. The variable @code{crt}
determines the minimum number of lines the body of the message must
contain in order to be piped through pager command specified
-by environment variable @code{PAGER}. If @code{crt} is set to a numeric
+by environment variable @env{PAGER}. If @code{crt} is set to a numeric
value, this value is taken as the minimum number of lines. Otherwise,
if @code{crt} is set without a value then the height of the terminal
screen is used to compute the threshold. The number of lines on
@@ -872,7 +872,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{saving mail by name}. For example:
@example
@cartouche
@@ -921,14 +922,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,42 +986,76 @@ 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 derived from its first recipient as outlined below.
+
+If the @code{outfolder} variable is set, and has a string value @var{s},
+the filename is @file{@var{s}/@var{recipient}}. If it is a boolean,
+then the @code{folder} variable is consulted. If it is set, then the
+filename is @file{@var{folder}/@var{recipient}}. Otherwise, the
+message will not be saved.
+
+The value @var{recipient} is derived from the email of the first
+recipient of the message. By default it is a local part of that
+email. If the @code{outfilename} variable has the value
+@samp{domain}, the domain part of the email is used. If this
+variable is set to @samp{email}, then entire email address is
+used.
+
+@xref{saving mail by name}, for a detailed discussion.
@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{saving mail by
+name}, for a 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{saving mail by
+name}, for a detailed discussion of how the file name is selected.
@end deffn
+By default, @command{mail} will preserve personal email parts when
+forming lists of recipient addresses. If this is not desired, unset
+the @code{fullnames} variable (@pxref{fullnames}).
+
To determine the sender of the message @command{mail} uses the
list of sender fields (@pxref{Controlling Sender Fields}). The first field
from this list is looked up in message headers. If it is found
@@ -1092,8 +1127,15 @@ Subject: Re: News
@kyindex sender, mail command
@kyindex nosender, mail command
-Commands @code{sender} and @code{nosender} are used to manipulate
-the contents of the sender field list.
+When @command{mail} needs to know the sender of a message, it
+looks it up in one or more headers of that message. Such headers
+constitute a @dfn{sender list}. The first header from the list
+that is present in the message and has a non-empty value is used.
+If none is found or if the sender list is empty, the value of the
+message envelope is used.
+
+The commands @code{sender} and @code{nosender} manipulate the sender
+list.
If the command @code{sender} is used without arguments, it displays
the contents of the sender field list. If arguments are given,
@@ -1161,6 +1203,55 @@ argument as a file name to execute and all subsequent arguments are
passed as positional parameters to this command. The @code{shell}
command can also be spelled as @code{!}.
+@node Saving and Recording
+@subsection Saving and Recording
+
+Several commands discussed in the previous section save messages in
+a disk file. The name of that file is either obtained from the
+@code{record} variable (@dfn{recording mail}) or is derived from the
+first recipient of the message (@dfn{saving by name}).
+
+The following commands record mails:
+
+@itemize @bullet
+@item @code{mail}
+@item @code{reply}
+@item @code{Reply}
+@end itemize
+
+The following commands save mail by name:
+
+@itemize @bullet
+@item @code{Copy}
+@item @code{Save}
+@item @code{Mail}
+@item @code{followup}
+@item @code{Followup}
+@end itemize
+
+@anchor{saving mail by name}
+Saving mail by name is controlled by three mail variables:
+@code{outfolder}, @code{folder}, and @code{outfilename}.
+The first, @code{outfolder}, is a boolean variable which, when set,
+enables saving mail by name. The @code{folder} variable defines a
+directory where mail files are stored. Name of file in that directory
+where the message will be saved is derived from the message
+recipient@footnote{In case of @code{Copy} and @code{Save}, message sender is
+used instead}. This process is controlled by the @code{outfilename}
+variable: if its value is @samp{local}, the file is named by the local
+part of the email (this is the default). If it is @samp{domain}, the
+domain part is used instead. Finally, if it's value is @samp{email},
+the entire email is used.
+
+As a GNU extension, @code{outfolder} can be a string variable. In
+that case its value names the directory to use instead of
+@code{folder}.
+
+The @code{mailx} variable, if set, disables GNU extensions. In this
+case, @code{outfolder} is used as a boolean value, and file names are
+derived from the local part of the email, ignoring the
+@code{outfilename} value.
+
@node Composing Mail
@subsection Composing Mail
@@ -1924,101 +2015,79 @@ set
Following variables control the behavior of GNU @command{mail}:
-@table @code
-@kwindex append
-@item append
-@*Type: Boolean, Read-Only
+@c -----------------------------------------
+@deftypevr {mail} boolean append
@*Default: True
-@vrindex append, mail variable
+@*Comment: Read-Only
-Messages saved in @file{mbox} are appended to the end rather than
+Messages saved in @file{mbox} are appended to the end, rather than
prepended. This is the default and cannot be changed. This variable
exists only for compatibility with other @command{mailx}
implementations.
+@end deftypevr
-@kwindex appenddeadletter
-@item appenddeadletter
-@*Type: Boolean.
-@*Default: False.
-@vrindex appenddeadletter, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean appenddeadletter
+@*Default: False
-If this variable is @code{True}, the contents of canceled letter is
+If this variable is set, the contents of canceled letter is
appended to the user's @file{dead.letter} file. Otherwise it overwrites
its contents.
+@end deftypevr
-@kwindex askbcc
-@item askbcc
-@*Type: Boolean.
-@*Default: False.
-@vrindex askbcc, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean askbcc
+@*Default: False
When set to @code{True} the user will be prompted to enter @code{Bcc}
field before composing the message.
+@end deftypevr
-@kwindex askcc
-@item askcc
-@*Type: Boolean.
-@*Default: True.
-@vrindex askcc, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean askcc
+@*Default: True
When set to @code{True} the user will be prompted to enter @code{Cc}
field before composing the message.
+@end deftypevr
-@kwindex asksub
-@item asksub
-@*Type: Boolean.
+@c -----------------------------------------
+@deftypevr {mail} boolean asksub
@*Default: True in interactive mode, False otherwise.
-@vrindex asksub, mail variable
When set to @code{True} the user will be prompted to enter @code{Subject}
field before composing the message.
+@end deftypevr
-@kwindex autoinc
-@item autoinc
-@*Type: Boolean.
-@*Default: True.
-@vrindex autoinc, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean autoinc
+@*Default: True
Automatically incorporate newly arrived messages.
+@end deftypevr
-@kwindex autoprint
-@item autoprint
-@*Type: Boolean.
-@*Default: False.
-@vrindex autoprint, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean autoprint
+@*Default: False
-Causes the delete command to behave like dp - thus, after deleting a
+Causes the delete command to behave like @code{dp}: after deleting a
message, the next one will be typed automatically.
+@end deftypevr
-@kwindex bang
-@item bang
-@*Type: Boolean.
-@*Default: False.
-@vrindex bang, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean bang
+@*Default: False
When set, every occurrence of @code{!} in arguments to @code{!}
-command is replaced with the last executed command.
+escape 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.
+@xref{Executing Shell Commands}, for details on the @code{!} escape.
+@end deftypevr
+@c -----------------------------------------
@anchor{datefield}
-@kwindex datefield
-@item datefield
-@*Type: Boolean.
-@*Default: False.
-@vrindex datefield, mail variable
+@deftypevr {mail} boolean datefield
+@*Default: False
By default the date in a header summary is taken from the @acronym{SMTP}
envelope of the message. Setting this variable tells @command{mail}
@@ -2027,69 +2096,68 @@ local time. Notice, that for messages lacking this field @command{mail}
will fall back to using @acronym{SMTP} envelope.
@xref{fromfield}.
+@end deftypevr
-@kwindex charset
-@item charset
-@*Type: string
+@c -----------------------------------------
+@deftypevr {mail} string charset
@*Default: @samp{auto}
-@vrindex charset, mail variable
The value of this variable is the character set used for input and
output operations. If the value is @samp{auto}, @command{mail} will
try to deduce the name of the character set from the value of
-@samp{LC_ALL} environment variable. If the variable contains the
+@env{LC_ALL} environment variable. If the variable contains the
character set part (e.g. @samp{nb_NO.utf-8}), it will be used.
Otherwise, @command{mail} will look up in its built-in database the
value of the character for this language/territory combination. If
-@samp{LC_ALL} is not set, the @samp{LANG} environment variable is
+@env{LC_ALL} is not set, the @env{LANG} environment variable is
inspected.
-The value of @samp{charset} controls both input and output
+The value of @code{charset} controls both input and output
operations. On input, it is used to set the value of the
@samp{charset} parameter in the @samp{Content-Type} MIME header, if
-its value begins with @samp{text/} and @samp{charset} is not present.
+its value begins with @samp{text/} and the @samp{charset} parameter is
+not present.
On output, it is used to display values of the header fields encodied
using RFC 2047. If the variable is unset, no decoding is performed
and the fields are printed as they are. Otherwise, they are recoded
to that character set.
+@end deftypevr
-@kwindex cmd
-@item cmd
-@*Type: String.
-@*Default: Unset.
-@vrindex cmd, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string cmd
+@*Default: Unset
Contains default shell command for @code{pipe}.
+@end deftypevr
-@kwindex columns
-@item columns
-@*Type: Numeric.
+@c -----------------------------------------
+@deftypevr {mail} numeric columns
@*Default: Detected at startup by querying the terminal device. If this
-fails, the value of environment variable @code{COLUMNS} is used.
-@vrindex columns, mail variable
+fails, the value of environment variable @env{COLUMNS} is used.
This variable contains the number of columns on terminal screen.
+@end deftypevr
-@kwindex crt
-@item crt
-@*Type: Boolean or Numeric
+@c -----------------------------------------
+@anchor{crt}
+@deftypevr {mail} numeric crt
+@deftypevrx {mail} boolean crt
@*Default: True in interactive mode, False otherwise.
-@vrindex crt, mail variable
The variable @code{crt} determines the minimum number of lines the body
of the message must contain in order to be piped through pager command
-specified by environment variable @code{PAGER}. If @code{crt} is set
+specified by environment variable @env{PAGER}. If @code{crt} is set
to a numeric value, this value is taken as the threshold. Otherwise,
if @code{crt} is set without a value, then the height of the terminal
screen is used to compute the threshold. The number of lines on
screen is controlled by @code{screen} variable.
+@end deftypevr
-@kwindex debug
-@item debug
-@*Type: String to boolean
-@*Default: Not set
-@vrindex debug, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean debug
+@deftypevrx {mail} string debug
+@*Default: Unset
Sets mailutils debug level. If set to string, the value must be a
valid Mailutils debugging specification. @xref{Debug Statement}, for
@@ -2099,12 +2167,11 @@ If unset (i.e. @code{set nodebug}), clears and disables all debugging
information. If set to @samp{true} (i.e. @code{set debug}), sets
maximum debugging (@samp{<trace7}) on mailbox and its underlying
objects.
+@end deftypevr
-@kwindex decode-fallback
-@item decode-fallback
-@*Type: String.
-@*Default: @samp{none}.
-@vrindex decode-fallback, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string decode-fallback
+@*Default: @samp{none}
This variable controls the way to represent characters that cannot
be rendered using current character set. It can have three values:
@@ -2122,98 +2189,94 @@ your setup, this may screw-up your terminal settings.
Unprintable characters are represented by their octal codes. Printable
ones are printed @samp{as is}.
@end table
+@end deftypevr
-@kwindex debug
-@item debug
-@*Type: Boolean
-@*Default: Unset
-@vrindex debug, mail variable
-
-This variable is not used. It exists for compatibility with other
-@command{mailx} implementations and for future use.
-
-@kwindex dot
-@item dot
-@*Type: Boolean.
-@*Default: False.
-@vrindex dot, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean dot
+@*Default: False
-If @code{True}, causes @command{mail} to interpret a period alone on a line as the
+If set, causes @command{mail} to interpret a period alone on a line as the
terminator of a message you are sending.
+@end deftypevr
-@kwindex emptystart
-@item emptystart
-@*Type: Boolean.
-@*Default: False.
-@vrindex emptystart, mail variable
-
-If the mailbox is empty, @command{mail} normally prints @samp{No mail for user} and
-exits immediately. If this option is set, @command{mail} will start no matter is
-the mailbox empty or not.
-
-@kwindex editheaders
-@item editheaders
-@*Type: Boolean.
-@*Default: False.
-@vrindex editheaders, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean editheaders
+@*Default: False
When set, @command{mail} will include message headers in the text to
be the @code{~e} and @code{~v} escapes, thus allowing you to customize
the headers.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean emptystart
+@*Default: False
-@kwindex escape
-@item escape
-@*Type: String.
-@*Default: ~
-@vrindex escape, mail variable
+If the mailbox is empty, @command{mail} normally prints @samp{No mail
+for user} and exits immediately. If this option is set,
+@command{mail} will start no matter is the mailbox empty or not.
+@end deftypevr
-If defined, the first character of this option gives the character to
-denoting escapes.
+@c -----------------------------------------
+@deftypevr {mail} string escape
+@*Default: @samp{~}
-@kwindex flipr
-@item flipr
-@*Type: Boolean
+Current value of the command escape character.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean flipr
@*Default: Unset
-@vrindex flipr, mail variable
If set, the variable @code{flipr} swaps the meanings of @code{reply}
and @code{Reply} commands (@pxref{Replying}).
+@end deftypevr
+@c -----------------------------------------
@anchor{folder variable}
-@kwindex folder
-@item folder
-@*Type: String.
-@*Default: Unset.
-@vrindex folder, mail variable
+@deftypevr {mail} string folder
+@*Default: Unset
The name of the directory to use for storing folders of messages. If
unset, @env{$HOME} is assumed.
+@end deftypevr
+@c -----------------------------------------
@anchor{fromfield}
-@kwindex fromfield
-@item fromfield
-@*Type: Boolean.
-@*Default: True.
+@deftypevr {mail} boolean fromfield
+@*Default: True
By default the sender address is taken from the @samp{From} header.
Unsetting this variable tells @command{mail} to obtain it from the
-@acronym{SMTP} envelope, instead.
+@acronym{SMTP} envelope instead.
@xref{datefield}.
+@end deftypevr
+
+@c -----------------------------------------
+@anchor{fullnames}
+@deftypevr {mail} boolean fullnames
+@*Default: True
+
+Preserve personal parts (comments) of recipient addresses when replying to a
+message.
+
+When unset, only emails will be used.
-@kwindex header
-@item header
-@*Type: Boolean.
+@xref{Replying}.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean header
@*Default: True, unless started with @option{--nosum} (@option{-N}) option.
-@vrindex header, mail variable
Whether to run @code{headers} command automatically after entering
interactive mode.
+@end deftypevr
+@c -----------------------------------------
@anchor{headline}
-@kwindex headline
-@item headline
-@*Type: String
+@deftypevr {mail} string headline
@*Default: @samp{%>%a%4m %18f %16d %3l/%-5o %s}
Format string to use for the header summary. The @samp{%} character
@@ -2258,10 +2321,10 @@ Message attribute. One of the following letters, or a single
horizontal space, if none of them applies:
@multitable @columnfractions 0.2 0.6
-@item @samp{M} @tab the message was copied to the mailbox (@samp{mbox} command)
-@item @samp{P} @tab the message was preserved (@samp{hold} command)
-@item @samp{*} @tab the message was saved (@samp{save} or @samp{Save})
-@item @samp{T} @tab the message was tagged (@samp{tag})
+@item @samp{M} @tab the message was copied to the mailbox (@code{mbox} command)
+@item @samp{P} @tab the message was preserved (@code{hold} command)
+@item @samp{*} @tab the message was saved (@code{save} or @code{Save})
+@item @samp{T} @tab the message was tagged (@code{tag})
@item @samp{R} @tab the message was read
@item @samp{N} @tab the message is new (was not seen)
@item @samp{U} @tab the message was seen, but wasn't read
@@ -2269,7 +2332,7 @@ horizontal space, if none of them applies:
@item %d
The date when the message was received. It is determined from the
-message header set by the @samp{datefield} variable
+message header defined by the @samp{datefield} variable
(@pxref{datefield}). If that variable is not set, or the requested
header is not present in the message, the date from the envelope is
used.
@@ -2343,13 +2406,12 @@ A @samp{<} for the current message, otherwise a space.
@item %%
A @samp{%} character.
@end table
+@end deftypevr
-@kwindex hold
-@item hold
+@c -----------------------------------------
@anchor{the hold variable}
-@*Type: Boolean.
-@*Default: False.
-@vrindex hold, mail variable
+@deftypevr {mail} boolean hold
+@*Default: False
Determines the location where to store the messages in state
@samp{read} and (if the @code{keepsave} is also set) @samp{saved}.
@@ -2363,60 +2425,56 @@ detailed information on how such messages are processed
when the mailbox is being closed.
@xref{keepsave}, for the discussion of the @code{keepsave} variable.
+@end deftypevr
-@kwindex ignore
-@item ignore
-@*Type: Boolean.
-@*Default: False.
-@vrindex ignore, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean ignore
+@*Default: False
When set to @code{True}, @command{mail} will ignore keyboard interrupts
when composing messages. Otherwise an interrupt will be taken as a
signal to abort composing.
+@end deftypevr
-@kwindex ignoreeof
-@item ignoreeof
-@*Type: Boolean.
-@*Default: False.
-@vrindex ignoreeof, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean ignoreeof
+@*Default: False
Controls whether typing EOF character terminates the letter being
composed.
+@end deftypevr
-@kwindex indentprefix
-@item indentprefix
-@*Type: String.
+@c -----------------------------------------
+@deftypevr {mail} string indentprefix
@*Default: "\t" (a tab character).
-@vrindex indentprefix, mail variable
String used by the @code{~m} tilde escape for indenting quoted messages.
+@end deftypevr
-@kwindex inplacealiases
-@item inplacealiases
-@*Type: Boolean
+@c -----------------------------------------
+@deftypevr {mail} boolean inplacealiases
@*Default: False
If set, @command{mail} will expand aliases in the address header field
before entering send mode (@pxref{Composing Mail}). By default, the
address header fields are left intact while composing, the alias
expansion takes place immediately before sending message.
+@end deftypevr
-@kwindex keep
-@item keep
-@*Type: Boolean, Read-Only
+@c -----------------------------------------
+@deftypevr {mail} boolean keep
+@*Comment: Read-Only
@*Default: True
-@vrindex append, mail variable
Truncate the user's system mailbox when it is empty, instead of
removing it. This is the default and cannot be changed. This variable
exists only for compatibility with other @command{mailx} implementations.
+@end deftypevr
-@kwindex keepsave
+@c -----------------------------------------
@anchor{keepsave}
-@item keepsave
-@*Type: Boolean.
-@*Default: False.
-@vrindex keepsave, mail variable
+@deftypevr {mail} boolean keepsave
+@*Default: False
Controls whether saved messages should be retained. The location
where they will be retained is controlled by the @code{hold} variable
@@ -2427,12 +2485,12 @@ mailbox.
@xref{saved messages}, for a detailed information on how
the saved messages are processed when the mailbox is being closed.
+@end deftypevr
-@kwindex mailx
-@item mailx
-@*Type: Boolean.
-@*Default: False.
-@vrindex mailx, mail variable
+@c -----------------------------------------
+@anchor{mailx mail variable}
+@deftypevr {mail} boolean mailx
+@*Default: False
When set, enables @dfn{mailx compatibility mode}. This mode
has the following effects:
@@ -2446,13 +2504,27 @@ the body.
@item In send mode, if the composition was interrupted, @command{mail}
will exit with zero status. By default it exits with zero status only
if the message was sent successfully.
+
+@item The @code{outfolder} variable is treated as boolean.
+@pxref{outfolder}.
+
+@item The value of @code{outfilename} is ignored (assumed to be
+@samp{local}). @pxref{outfilename}.
+
+@item The values of @code{folder} and @code{record} variables are
+assumed relative to the home directory, unless they begin with
+@samp{/}, @samp{~}, or @samp{+}.
+
+@item If the value of the @code{sendmail} variable does not begin with
+a scheme specification, @samp{sendmail:/} is assumed. @xref{sendmail
+mail variable}.
@end itemize
+@end deftypevr
-@kwindex metamail
-@item metamail
-@*Type: Boolean or String.
-@*Default: True.
-@vrindex metamail, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean metamail
+@deftypevrx {mail} string metamail
+@*Default: True
This variable controls operation of @code{decode} command. If
it is unset, @code{decode} will not attempt any interpretation
@@ -2471,23 +2543,21 @@ set metamail
# Use external program to display MIME parts:
set metamail="metamail -m mail -p"
@end example
+@end deftypevr
-@kwindex mime
-@item mime
-@*Type: String
-@*Default: Unset (false)
-@vrindex mime, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string mime
+@*Default: False
If set, this variable instructs @command{mail} to compose MIME
messages.
It can be set from the command line using @option{--mime} option.
+@end deftypevr
-@kwindex mimenoask
-@item mimenoask
-@*Type: String
-@*Default: Empty
-@vrindex mimenoask, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string mimenoask
+@*Default: Unset
By default @command{mail} asks for confirmation before running
interpreter to view a part of the multi-part message. If this variable
@@ -2503,22 +2573,21 @@ set mimenoask=text/*,image/jpeg
will disable prompting before displaying any textual files, no
matter what their subtype is, and before displaying files with
type @samp{image/jpeg}.
+@end deftypevr
-@kwindex metoo
-@item metoo
-@*Type: Boolean.
-@*Default: False.
-@vrindex metoo, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean metoo
+@*Default: False
Usually, when an alias is expanded that contains the sender, the sender
is removed from the expansion. Setting this option causes the sender to
be included in the group.
+@end deftypevr
-@kwindex mode
-@item mode
-@*Type: String, Read-Only
+@c -----------------------------------------
+@deftypevr {mail} string mode
+@*Comment: Read-Only
@*Default: The name of current operation mode.
-@vrindex mode, mail variable
This variable keeps the name of the current operation mode. Its
possible values are:
@@ -2543,12 +2612,11 @@ The program operates in read mode. This is the default.
The program operates in send mode. This means it was given one or more
recipient addresses in the command line.
@end table
+@end deftypevr
-@kwindex nullbody
-@item nullbody
-@* Type: Boolean
-@* Default: True
-@vrindex nullbody, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean nullbody
+@*Default: True
Controls whether @command{mail} accepts messages with an empty
body. The default value, @code{true}, means such messages are sent,
@@ -2568,151 +2636,169 @@ outputs something on its standard output or error:
/bin/mail -E'set nonullbody' -s 'Periodic synchronization'
@end group
@end example
+@end deftypevr
-@kwindex showenvelope
-@item showenvelope
-@*Type: Boolean
-@*Default: Unset
-
-If this variable is set, the @code{print} command will include the
-@acronym{STMP} envelope in its output.
-
-@kwindex nullbodymsg
-@item nullbodymsg
-@*Type: String
-@*Default: Null message body; hope that's ok
-@vrindex nullbodymsg
+@c -----------------------------------------
+@deftypevr {mail} string nullbodymsg
+@*Default: @samp{Null message body; hope that's ok}
-Keeps the text of the warning, displayed by @command{mail} before
+Text of the warning displayed by @command{mail} before
sending an empty message. When available, the translation of
this text, in accordance with the current locale, is displayed.
Unsetting this variable disables the warning.
+@end deftypevr
-@kwindex onehop
-@item onehop
-@*Type: Boolean
+@c -----------------------------------------
+@deftypevr {mail} boolean onehop
@*Default: Unset
@vrindex onehop, mail variable
This variable is not used. It exists for compatibility with other
@command{mailx} implementations and for future use.
+@end deftypevr
-@kwindex outfolder
-@item outfolder
-@*Type: String.
-@*Default: Unset.
-@vrindex outfolder, mail variable
+@c -----------------------------------------
+@anchor{outfilename}
+@deftypevr {mail} string outfilename
+@*Comment: Three-state: @samp{local}, @samp{email}, @samp{domain}.
+@*Default: @samp{local}
-Contains the directory in which files created by @code{save},
-@code{write}, etc. commands will be stored. When unset, current
-directory is assumed.
+Defines the algorithm to convert the recipient email to the name of
+the file used to record outgoing messages to that recipient. This
+affects the following commands: @code{Copy}, @code{Save}, @code{Mail},
+@code{followup}, and @code{Followup}. The following values are allowed:
-@kwindex PID
-@item PID
-@*Type: String, Read-Only
-@*Default: PID of the process.
-@vrindex PID, mail variable
+@table @code
+@item local
+Local part of the email address is taken as the file name. This is
+the default.
-PID of the current @command{mail} process.
+@item email
+Entire email is takes as the file name.
-@kwindex page
-@item page
-@*Type: Boolean.
-@*Default: False.
-@vrindex page, mail variable
+@item domain
+Domain part of the email is used as the file name.
+@end table
+@end deftypevr
+
+@c -----------------------------------------
+@anchor{outfolder}
+@deftypevr {mail} boolean outfolder
+@deftypevrx {mail} string outfolder
+@*Default: Unset
+
+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 set to @code{True}, the @code{pipe} command will emit a linefeed
+If set to a string value, names the directory where to store these files.
+
+This variable affects the following commands: @code{Copy},
+@code{Save}, @code{Mail}, @code{followup}, and @code{Followup}.
+
+In mailx compatibility mode, only boolean value is allowed.
+@pxref{mailx mail variable}.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean page
+@*Default: Unset
+
+If set, the @code{pipe} command will emit a linefeed
character after printing each message.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} string PID
+@*Comment: Read-Only
+@*Default: PID of the process.
-@kwindex prompt
-@item prompt
-@*Type: String.
+PID of the current @command{mail} process.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} string prompt
@*Default: "? "
-@vrindex prompt, mail variable
Contains the command prompt sequence.
+@end deftypevr
-@kwindex quiet
-@item quiet
-@*Type: Boolean
+@c -----------------------------------------
+@deftypevr {mail} boolean quiet
@*Default: Unset
-@vrindex quiet, mail variable
This variable is not used. It exists for compatibility with other
@command{mailx} implementations and for future use.
+@end deftypevr
-@kwindex quit
-@item quit
-@*Type: Boolean.
+@c -----------------------------------------
+@deftypevr {mail} boolean quit
@*Default: False, unless started with @option{--quit} (@option{-q}) option.
-@vrindex quit, mail variable
When set, causes keyboard interrupts to terminate the program.
+@end deftypevr
-@kwindex rc
-@item rc
-@*Type: Boolean.
+@c -----------------------------------------
+@deftypevr {mail} boolean rc
@*Default: True, unless started with @option{--norc} (@option{-N}) option.
-@vrindex rc, mail variable
When this variable is set, @command{mail} will read the system-wide
-configuration file upon startup. See @ref{Mail Configuration Files}.
+configuration file upon startup. @xref{Mail Configuration Files}.
+@end deftypevr
-@kwindex readonly
-@item readonly
-@*Type: Boolean
+@c -----------------------------------------
+@deftypevr {mail} boolean readonly
@*Default: False
-@vrindex readonly, mail variable
When set, mailboxes are opened in readonly mode. In this mode, any
@command{mail} commands that alter the contents of the mailbox are
disabled. These commands include, but are not limited to:
@code{delete}, @code{save} and @code{mbox}.
+@end deftypevr
+
+@c -----------------------------------------
+@anchor{record}
+@deftypevr {mail} string record
+@*Default: Unset
-@kwindex record
-@item record
-@*Type: String.
-@*Default: Unset.
-@vrindex record, mail variable
+When set, outgoing messages produced by the following commmands will
+be saved to the named file: @code{mail}, @code{reply}, @code{Reply}.
-When set, any outgoing message will be saved to the named file.
+See also @ref{outfolder} and @ref{outfilename}.
+@end deftypevr
-@kwindex recursivealiases
-@item recursivealiases
-@*Type: Boolean
+@c -----------------------------------------
+@deftypevr {mail} boolean recursivealiases
@*Default: True
When set, @command{mail} will expand aliases recursively.
+@end deftypevr
-@kwindex regex
-@item regex
-@*Type: Boolean.
+@c -----------------------------------------
+@deftypevr {mail} boolean regex
@*Default: True.
-@vrindex regex, mail variable
-Setting this to @code{True} enables use of regular expressions in
+If set, enables the use of regular expressions in
@samp{/.../} message specifications.
+@end deftypevr
-@kwindex replyprefix
-@item replyprefix
-@*Type: String
+@c -----------------------------------------
+@deftypevr {mail} string replyprefix
@*Default: @samp{Re: }
-@vrindex replyprefix, mail variable
Sets the prefix that will be used when constructing the subject line
of a reply message.
+@end deftypevr
-@kwindex replyregex
-@item replyregex
-@*Type: String
+@c -----------------------------------------
+@deftypevr {mail} string replyregex
@*Default: @samp{^re: *}
-@vrindex replyregex, mail variable
Sets the regular expression used to recognize subjects of reply
messages. If the @code{Subject} header of the message matches this
expression, the value of @code{replyprefix} will not be prepended to
-it before replying. The expression should be a POSIX extended regular
+it before replying. The value should be a POSIX extended regular
expression. The comparison is case-insensitive.
For example, to recognize usual English, Polish, Norwegian and German
@@ -2724,113 +2810,121 @@ set replyregex="^(re|odp|aw|ang)(\\[[0-9]+\\])?:[[:blank:]]"
@noindent
(Notice the quoting of backslash characters).
+@end deftypevr
+@c -----------------------------------------
@anchor{return-address}
-@kwindex return-address
-@item return-address
-@*Type: String
+@deftypevr {mail} string return-address
@*Default: unset
-@vrindex return-address, mail variable.
Sets the return email address to use when sending messages. If unset,
-the address is composed from the current user name and the host name.
+return address is composed from the current user name and the host name.
+@end deftypevr
-@kwindex save
-@item save
-@*Type: Boolean.
+@c -----------------------------------------
+@deftypevr {mail} boolean save
@*Default: True.
-@vrindex save, mail variable
When set, the aborted messages will be stored in the user's
@file{dead.file}. See also @code{appenddeadletter}.
+@end deftypevr
-@kwindex screen
-@item screen
-@*Type: Numeric.
+@c -----------------------------------------
+@deftypevr {mail} numeric screen
@*Default: Detected at startup by querying the terminal device. If this
-fails, the value of environment variable @code{LINES} is used.
-@vrindex screen, mail variable
-
-This variable contains the number of lines on terminal screen.
-
-@kwindex sendmail
-@item sendmail
-@*Type: String.
-@*Default: sendmail:/usr/lib/sendmail
-@vrindex sendmail, mail variable
-
-Contains URL of the mail transport agent.
-
-@kwindex sendwait
-@item sendwait
-@*Type: Boolean
+fails, the value of environment variable @env{LINES} is used.
+
+This variable contains the number of lines on terminal screen. See
+also @ref{crt}.
+@end deftypevr
+
+@c -----------------------------------------
+@anchor{sendmail mail variable}
+@deftypevr {mail} string sendmail
+@*Default: @samp{sendmail:/usr/lib/sendmail}
+
+Contains URL of the mail transport agent. If the value begins with a
+scheme specifier, it must be one of the mailer URL schemes supported
+by mailutils (@pxref{mailer URL}). Otherwise, if not in mailx
+compatibility mode, the value starting with directory separator
+(@samp{/}) is treated as the external command that will be started as
+is and the composed message will be piped to its standard input.
+
+In mailx compatibility mode (@pxref{mailx mail variable}), the
+@samp{sendmail:} prefix is assumed.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean sendwait
@*Default: Unset
-@vrindex sendwait, mail variable
This variable is not used. It exists for compatibility with other
@command{mailx} implementations and for future use.
+@end deftypevr
-@kwindex showto
-@item showto
-@*Type: Boolean
-@*Default: False
-@vrindex showto, mail variable
-
-If the message was sent by the user, print its recipient address in
-the header summary.
-
-@kwindex Sign
-@item Sign
-@*Type: String.
-@*Default: Unset.
-@vrindex Sign, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string Sign
+@*Default: Unset
Contains the filename holding users signature. The contents of this
file is appended to the end of a message being composed by @code{~A}
escape.
+@end deftypevr
-@kwindex sign
-@item sign
-@*Type: String.
-@*Default: Unset.
-@vrindex sign, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string sign
+@*Default: Unset
Contains the user's signature. The contents of this variable is appended
to the end of a message being composed by @code{~a} escape. Use
@code{Sign} variable, if your signature occupies more than one line.
+@end deftypevr
-@kwindex showto
-@item showto
-@*Type: Boolean
-@*Default: unset
-@vrindex showto, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean showenvelope
+@*Default: Unset
+
+If this variable is set, the @code{print} command will include the
+@acronym{SMTP} envelope in its output.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean showto
+@*Default: Unset
If this variable is set, @command{mail} will show @code{To:} addresses
instead of @code{From:} for all messages that come from the user that
invoked the program.
+@end deftypevr
-@kwindex subject
-@item subject
-@*Type: String.
-@*Default: Unset.
-@vrindex subject, mail variable
+@c -----------------------------------------
+@deftypevr {mail} string subject
+@*Default: Unset
Contains default subject line. This will be used when @code{asksub} is
off.
+@end deftypevr
-@kwindex toplines
-@item toplines
-@*Type: Numeric.
+@c -----------------------------------------
+@deftypevr {mail} numeric toplines
@*Default: 5
-@vrindex toplines, mail variable
Number of lines to be displayed by @code{top} and @code{Top} commands.
+@end deftypevr
+
+@c -----------------------------------------
+@deftypevr {mail} boolean variable-pretty-print
+@deftypevrx {mail} boolean varpp
+@*Default: False
+
+If this variable is set, the listing output by @command{set} contains short
+descriptions before each variable. @xref{Setting and Unsetting the Variables}.
+@end deftypevr
-@kwindex variable-strict
-@item variable-strict
-@itemx varstrict
-@*Type: Boolean.
-@*Default: False.
+@c -----------------------------------------
+@deftypevr {mail} boolean variable-strict
+@deftypevrx {mail} boolean varstrict
+@*Default: False
Setting this variable enables strict control over variable
settings. In this mode, @command{mail} refuses to set read-only
@@ -2838,29 +2932,18 @@ variables. Also, if the user is trying to set an unknown variable,
@command{mail} prints a warning.
@xref{Setting and Unsetting the Variables}.
+@end deftypevr
-@kwindex variable-pretty-print
-@item variable-pretty-print
-@itemx varpp
-@*Type: Boolean.
-@*Default: False.
-
-If this variable is set, the listing output by @command{set} contains short
-descriptions before each variable. @xref{Setting and Unsetting the Variables}.
-
-@kwindex verbose
-@item verbose
-@*Type: Boolean.
-@*Default: False.
-@vrindex verbose, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean verbose
+@*Default: False
When set, the actual delivery of messages is displayed on the user's terminal.
+@end deftypevr
-@kwindex xmailer
-@item xmailer
-@*Type: Boolean.
-@*Default: Set.
-@vrindex xmailer, mail variable
+@c -----------------------------------------
+@deftypevr {mail} boolean xmailer
+@*Default: True
Controls whether the header @samp{X-Mailer} should be added to
outgoing messages. The default value of this header is
@@ -2868,8 +2951,7 @@ outgoing messages. The default value of this header is
@example
X-Mailer: mail (GNU Mailutils @value{VERSION})
@end example
-
-@end table
+@end deftypevr
@node Mail Configuration Files
@subsection Personal and System-wide Configuration Files
diff --git a/dotlock/dotlock.c b/dotlock/dotlock.c
index 1bb11590b..303f6d943 100644
--- a/dotlock/dotlock.c
+++ b/dotlock/dotlock.c
@@ -28,7 +28,6 @@
static const char *file;
static int unlock;
-static int flags;
static unsigned retries;
static unsigned force;
static int debug;
@@ -119,6 +118,7 @@ int
main (int argc, char *argv[])
{
mu_locker_t locker = 0;
+ mu_locker_hints_t hints = { .flags = 0 };
int err = 0;
pid_t usergid = getgid ();
pid_t mailgid = getegid ();
@@ -149,31 +149,27 @@ main (int argc, char *argv[])
if (force)
{
- force *= 60;
- flags |= MU_LOCKER_TIME;
+ hints.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
+ hints.expire_time = force * 60;
}
- if (retries || retry_sleep)
- flags |= MU_LOCKER_RETRY;
+ if (retries)
+ {
+ hints.flags |= MU_LOCKER_FLAG_RETRY;
+ hints.retry_count = retries;
+ hints.retry_sleep = retry_sleep;
+ }
if (pid_check)
- flags |= MU_LOCKER_PID;
+ hints.flags |= MU_LOCKER_FLAG_CHECK_PID;
- if ((err = mu_locker_create (&locker, file, flags)))
+ if ((err = mu_locker_create_ext (&locker, file, hints.flags != 0 ? &hints : NULL)))
{
if (debug)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create", NULL, err);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create_ext", NULL, err);
return MU_DL_EX_ERROR;
}
- if (force != 0)
- mu_locker_set_expire_time (locker, force);
-
- if (retries)
- mu_locker_set_retries (locker, retries);
- if (retry_sleep)
- mu_locker_set_retry_sleep (locker, retry_sleep);
-
if (setegid (mailgid) < 0)
return MU_DL_EX_ERROR;
diff --git a/imap4d/imap4d.c b/imap4d/imap4d.c
index 8aa99703d..1d9b15c58 100644
--- a/imap4d/imap4d.c
+++ b/imap4d/imap4d.c
@@ -442,8 +442,7 @@ prefix_section_parser (enum mu_cfg_section_stage stage,
}
else if (!pfx->prefix)
{
- pfx->prefix = mu_alloc (strlen (pfx->dir) + 1);
- translate_delim (pfx->prefix, pfx->dir, pfx->delim, '/');
+ pfx->prefix = namespace_encode_delim (pfx, pfx->dir);
}
}
return 0;
diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index 42a245cdd..b61fa09bb 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -421,6 +421,10 @@ char *namespace_translate_name (char const *name,
struct namespace_prefix const **pfx);
char *namespace_get_name (char const *name, mu_record_t *rec, int *mode);
+char *namespace_decode_delim (struct namespace_prefix const *pfx,
+ char const *src);
+char *namespace_encode_delim (struct namespace_prefix const *pfx,
+ char const *src);
void translate_delim (char *dst, char const *src, int dst_delim, int src_delim);
diff --git a/imap4d/list.c b/imap4d/list.c
index 5e1aa891e..c1a2b71e1 100644
--- a/imap4d/list.c
+++ b/imap4d/list.c
@@ -18,12 +18,50 @@
#include <dirent.h>
#include <pwd.h>
+/*
+ * IMPORTANT NOTE:
+ *
+ * The LIST command takes two arguments: 'reference' and 'mailbox',
+ * which in the code below is referred to as 'wcard'. Now, RFC 3501
+ * states, that:
+ *
+ * If the reference argument is not a level of mailbox
+ * hierarchy (that is, it is a \NoInferiors name), and/or
+ * the reference argument does not end with the hierarchy
+ * delimiter, it is implementation-dependent how this is
+ * interpreted. For example, a reference of "foo/bar" and
+ * mailbox name of "rag/baz" could be interpreted as
+ * "foo/bar/rag/baz", "foo/barrag/baz", or "foo/rag/baz".
+ * A client SHOULD NOT use such a reference argument except
+ * at the explicit request of the user. A hierarchical
+ * browser MUST NOT make any assumptions about server
+ * interpretation of the reference unless the reference is
+ * a level of mailbox hierarchy AND ends with the hierarchy
+ * delimiter.
+ *
+ * Mailutils' approach is basically to concatenate the two
+ * arguments with a hierarchy separator (as per RFC 2342) in
+ * between. In detail:
+ *
+ * 1. Given two arguments, 'reference' and 'wcard', the reference is
+ * used to look up the matching namespace (first approximation).
+ * 2. If wcard contains non-wildcard directory prefix, that prefix
+ * is removed and appended to the reference, separated by the
+ * namespace delimiter.
+ * 3. The updated reference is used to look up the final namespace.
+ * 4. If the namespace prefix ends with a delimiter, wcard is appended
+ * to it.
+ * 5. Otherwise, the name to look up is formed by concatenating the
+ * namespace prefix, namespace delimiter, and wcard.
+ */
+
struct refinfo
{
char const *refptr; /* Original reference */
size_t reflen; /* Length of the original reference */
struct namespace_prefix const *pfx;
- size_t dirlen; /* Length of the current directory prefix */
+ int delim; /* If not 0, this character will be inserted between
+ the original reference and mailbox name */
char *buf;
size_t bufsize;
};
@@ -36,7 +74,7 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
size_t size;
char *p;
- name = resp->name + refinfo->dirlen;
+ name = resp->name;
/* There can be only one INBOX */
if (refinfo->reflen == 0 && mu_c_strcasecmp (name, "INBOX") == 0)
@@ -77,12 +115,14 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
{
memcpy (refinfo->buf, refinfo->refptr, refinfo->reflen);
p = refinfo->buf + refinfo->reflen;
+ if (refinfo->delim)
+ *p++ = refinfo->delim;
}
else
p = refinfo->buf;
if (*name)
translate_delim (p, name, refinfo->pfx->delim, resp->separator);
-
+
name = refinfo->buf;
if (strpbrk (name, "\"{}"))
@@ -97,6 +137,7 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
return 0;
}
+/* Return 1 if the string REF matches exactly the prefix in PFX. */
static int
match_pfx (struct namespace_prefix const *pfx, char const *ref)
{
@@ -104,7 +145,9 @@ match_pfx (struct namespace_prefix const *pfx, char const *ref)
for (; *q; p++, q++)
{
- if (*p == 0 || *p != *q)
+ if (*p == 0)
+ return *q == pfx->delim && q[1] == 0;
+ if (*p != *q)
return 0;
}
if (*p == pfx->delim)
@@ -119,8 +162,6 @@ list_ref (char const *ref, char const *wcard, char const *cwd,
int rc;
struct refinfo refinfo;
mu_folder_t folder;
- char const *dir;
- mu_url_t url;
struct mu_folder_scanner scn = MU_FOLDER_SCANNER_INITIALIZER;
if (!wcard[0])
@@ -168,14 +209,10 @@ list_ref (char const *ref, char const *wcard, char const *cwd,
refinfo.refptr = ref;
refinfo.reflen = strlen (ref);
- mu_folder_get_url (folder, &url);
- mu_url_sget_path (url, &dir);
- refinfo.dirlen = strlen (dir);
-
- if (refinfo.refptr[refinfo.reflen-1] == pfx->delim)
- refinfo.reflen--;
- else if (strcmp (ref, pfx->prefix) == 0)
- refinfo.dirlen++;
+ /* Insert delimiter after the reference prefix, unless the latter already
+ ends with a delimiter or is the same as the namespace prefix. */
+ if (ref[refinfo.reflen-1] != pfx->delim && strcmp (ref, pfx->prefix))
+ refinfo.delim = pfx->delim;
/* The special name INBOX is included in the output from LIST, if
INBOX is supported by this server for this user and if the
@@ -189,7 +226,7 @@ list_ref (char const *ref, char const *wcard, char const *cwd,
mu_imap_wildmatch_ci (wcard, "INBOX", MU_HIERARCHY_DELIMITER) == 0)
io_untagged_response (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
- scn.pattern = (void*) wcard;
+ scn.pattern = namespace_decode_delim (pfx, wcard);
scn.enumfun = list_fun;
scn.enumdata = &refinfo;
if (refinfo.pfx->record)
@@ -199,6 +236,7 @@ list_ref (char const *ref, char const *wcard, char const *cwd,
}
mu_folder_scan (folder, &scn);
+ free (scn.pattern);
mu_list_destroy (&scn.records);
mu_folder_destroy (&folder);
free (refinfo.buf);
diff --git a/imap4d/namespace.c b/imap4d/namespace.c
index d9a29af40..e1bb3dd43 100644
--- a/imap4d/namespace.c
+++ b/imap4d/namespace.c
@@ -55,14 +55,6 @@ cmplen (const char *aname, const void *adata,
return strlen (bname) - strlen (aname);
}
-void
-translate_delim (char *dst, char const *src, int dst_delim, int src_delim)
-{
- do
- *dst++ = *src == src_delim ? dst_delim : *src;
- while (*src++);
-}
-
static void
trim_delim (char *str, int delim)
{
@@ -155,21 +147,21 @@ namespace_init (void)
mu_assoc_sort_r (prefixes, cmplen, NULL);
}
+void
+translate_delim (char *dst, char const *src, int dst_delim, int src_delim)
+{
+ do
+ *dst++ = *src == src_delim ? dst_delim : *src;
+ while (*src++);
+}
+
static char *
prefix_translate_name (struct namespace_prefix const *pfx, char const *name,
size_t namelen)
{
size_t pfxlen = strlen (pfx->prefix);
- int delim = 0;
- if (pfxlen && pfx->prefix[pfxlen-1] == pfx->delim)
- {
- pfxlen--;
- delim = pfx->delim;
- }
-
- if ((pfxlen <= namelen && memcmp (pfx->prefix, name, pfxlen) == 0)
- && (delim == 0 || (name[pfxlen] == delim || name[pfxlen] == 0)))
+ if (pfxlen <= namelen && memcmp (pfx->prefix, name, pfxlen) == 0)
{
char *tmpl, *p;
@@ -370,6 +362,23 @@ namespace_get_name (char const *name, mu_record_t *rec, int *mode)
*mode = namespace[pfx->ns].mode;
return path;
}
+
+char *
+namespace_decode_delim (struct namespace_prefix const *pfx, char const *src)
+{
+ char *dst = mu_alloc (strlen (src) + 1);
+ translate_delim (dst, src, '/', pfx->delim);
+ return dst;
+}
+
+char *
+namespace_encode_delim (struct namespace_prefix const *pfx, char const *src)
+{
+ char *dst = mu_alloc (strlen (src) + 1);
+ translate_delim (dst, src, pfx->delim, '/');
+ return dst;
+}
+
static int
prefix_printer(void *item, void *data)
diff --git a/imap4d/tests/.gitignore b/imap4d/tests/.gitignore
index 8e0167aeb..23e9b4357 100644
--- a/imap4d/tests/.gitignore
+++ b/imap4d/tests/.gitignore
@@ -2,6 +2,7 @@ ckiconv
atconfig
atlocal
package.m4
+testclient
testsuite
testsuite.dir
testsuite.log
diff --git a/imap4d/tests/Makefile.am b/imap4d/tests/Makefile.am
index 7ee89c5fa..7f0f55076 100644
--- a/imap4d/tests/Makefile.am
+++ b/imap4d/tests/Makefile.am
@@ -21,7 +21,7 @@ include $(top_srcdir)/testsuite/testsuite.am
## -------------------------- ##
AM_CPPFLAGS = $(MU_LIB_COMMON_INCLUDES)
-noinst_PROGRAMS = ckiconv
+noinst_PROGRAMS = ckiconv testclient
LDADD = $(MU_LIB_MAILUTILS)
TESTSUITE_AT += \
@@ -29,6 +29,7 @@ TESTSUITE_AT += \
append00.at\
append01.at\
close-expunge.at\
+ clt_list.at\
create01.at\
create02.at\
examine.at\
diff --git a/imap4d/tests/atlocal.in b/imap4d/tests/atlocal.in
index ad87aa077..06db7df52 100644
--- a/imap4d/tests/atlocal.in
+++ b/imap4d/tests/atlocal.in
@@ -2,7 +2,7 @@
# Configurable variable values for Mailutils test suite.
# Copyright (C) 2011-2021 Free Software Foundation, Inc.
-PATH=@abs_builddir@:@abs_top_builddir@/imap4d:$top_srcdir:$srcdir:$PATH
+PATH=@abs_builddir@:@abs_top_builddir@/imap4d:$top_srcdir:@abs_top_builddir@/libproto/imap/tests:$PATH
MU_ULONG_MAX_1=@MU_ULONG_MAX_1@
make_config() {
diff --git a/imap4d/tests/clt_list.at b/imap4d/tests/clt_list.at
new file mode 100644
index 000000000..c7bfac5f6
--- /dev/null
+++ b/imap4d/tests/clt_list.at
@@ -0,0 +1,107 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 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/>.
+
+m4_pushdef([IMAP4D_HOMEDIR],[spool])
+
+AT_SETUP([list])
+AT_KEYWORDS([imap list folder])
+AT_CHECK([
+# Prepare namespaces
+mkdir spool spool/A spool/B
+
+mkdir archive archive/c archive/d archive/d/e
+
+mkdir other other/foo other/foo/a other/foo/b other/bar other/bar/qux
+
+touch spool/sent spool/outgoing spool/A/backup spool/A/cancelled
+touch archive/01 archive/02 archive/03
+touch archive/d/01 archive/d/02 archive/d/e/03 archive/d/e/04
+
+touch other/foo/draft other/bar/draft other/bar/X
+
+make_config spool
+])
+
+AT_CHECK([
+m4_changecom([//])
+testclient imap4d.conf 'imapfolder url=$URL dnl
+ list "" "" dnl
+ list "" % dnl
+ list "" \* dnl
+ list A/ % dnl
+ list "#archive:" % dnl
+ list "#archive:" \* dnl
+ list "#archive:" "d.e" dnl
+ list "#archive:d." "e" dnl
+ list "#archive:d." "e.%" dnl
+ list "#archive:d." \*'
+m4_changecom([#])
+],
+[0],
+[# LIST "" ""
+d- / 0 ""
+# LIST "" "%"
+d- / 0 "A"
+d- / 0 "B"
+-f 0 "INBOX"
+-f / 0 "outgoing"
+-f / 0 "sent"
+# LIST "" "*"
+d- / 0 "A"
+-f / 1 "A/backup"
+-f / 1 "A/cancelled"
+d- / 0 "B"
+-f 0 "INBOX"
+-f / 0 "outgoing"
+-f / 0 "sent"
+# LIST "A/" "%"
+-f / 1 "A/backup"
+-f / 1 "A/cancelled"
+# LIST "#archive:" "%"
+-f . 0 "#archive:01"
+-f . 0 "#archive:02"
+-f . 0 "#archive:03"
+d- . 0 "#archive:c"
+d- . 0 "#archive:d"
+# LIST "#archive:" "*"
+-f . 0 "#archive:01"
+-f . 0 "#archive:02"
+-f . 0 "#archive:03"
+d- . 0 "#archive:c"
+d- . 0 "#archive:d"
+-f . 1 "#archive:d.01"
+-f . 1 "#archive:d.02"
+d- . 1 "#archive:d.e"
+-f . 2 "#archive:d.e.03"
+-f . 2 "#archive:d.e.04"
+# LIST "#archive:" "d.e"
+d- . 1 "#archive:d.e"
+# LIST "#archive:d." "e"
+d- . 1 "#archive:d.e"
+# LIST "#archive:d." "e.%"
+-f . 2 "#archive:d.e.03"
+-f . 2 "#archive:d.e.04"
+# LIST "#archive:d." "*"
+-f . 1 "#archive:d.01"
+-f . 1 "#archive:d.02"
+d- . 1 "#archive:d.e"
+-f . 2 "#archive:d.e.03"
+-f . 2 "#archive:d.e.04"
+])
+
+
+
+AT_CLEANUP \ No newline at end of file
diff --git a/imap4d/tests/list.at b/imap4d/tests/list.at
index 1e532d244..6b2e6d0e4 100644
--- a/imap4d/tests/list.at
+++ b/imap4d/tests/list.at
@@ -39,6 +39,11 @@ AT_CLEANUP
])
dnl ----------------------------------------------------------------------
+LIST_CHECK([],[list01],
+["" ""],
+[* LIST (\NoSelect) "/" ""
+])
+
LIST_CHECK([],[list02],
["" "*"],
[dnl
@@ -101,9 +106,12 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
])
LIST_CHECK([],[],
-["" "#archive:%"],
+["#archive:" "*"],
[* LIST (\NoInferiors) "." #archive:mbox
+* LIST (\NoInferiors) "." #archive:old.mbox1
+* LIST (\NoInferiors) "." #archive:old.very.saved
* LIST (\NoSelect) "." #archive:old
+* LIST (\NoSelect) "." #archive:old.very
],
[mkdir archive archive/old archive/old/very
cp $abs_top_srcdir/testsuite/spool/mbox archive
@@ -112,12 +120,9 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
])
LIST_CHECK([],[],
-["" "archive.*"],
-[* LIST (\NoInferiors) "." archive.mbox
-* LIST (\NoInferiors) "." archive.old.mbox1
-* LIST (\NoInferiors) "." archive.old.very.saved
-* LIST (\NoSelect) "." archive.old
-* LIST (\NoSelect) "." archive.old.very
+["" "#archive:%"],
+[* LIST (\NoInferiors) "." #archive:mbox
+* LIST (\NoSelect) "." #archive:old
],
[mkdir archive archive/old archive/old/very
cp $abs_top_srcdir/testsuite/spool/mbox archive
@@ -126,12 +131,9 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
])
LIST_CHECK([],[],
-["archive" "*"],
-[* LIST (\NoInferiors) "." archive.mbox
-* LIST (\NoInferiors) "." archive.old.mbox1
-* LIST (\NoInferiors) "." archive.old.very.saved
-* LIST (\NoSelect) "." archive.old
-* LIST (\NoSelect) "." archive.old.very
+["#archive:" "%"],
+[* LIST (\NoInferiors) "." #archive:mbox
+* LIST (\NoSelect) "." #archive:old
],
[mkdir archive archive/old archive/old/very
cp $abs_top_srcdir/testsuite/spool/mbox archive
@@ -140,7 +142,22 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
])
LIST_CHECK([],[],
-["archive." "*"],
+["#archive:" "a.b"],
+[* LIST (\NoSelect) "." #archive:a.b
+],
+[mkdir archive archive/a archive/a/b
+])
+
+LIST_CHECK([],[],
+["#archive:" "a.b.c"],
+[* LIST (\NoInferiors) "." #archive:a.b.c
+],
+[mkdir archive archive/a archive/a/b
+cp $abs_top_srcdir/testsuite/spool/mbox archive/a/b/c
+])
+
+LIST_CHECK([],[],
+["" "archive.*"],
[* LIST (\NoInferiors) "." archive.mbox
* LIST (\NoInferiors) "." archive.old.mbox1
* LIST (\NoInferiors) "." archive.old.very.saved
@@ -163,18 +180,28 @@ LIST_CHECK([],[],
],
[mkdir archive archive/old archive/old/very
cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+# NOTE: Mailbox name with a directory delimiter will be ignored
cp $abs_top_srcdir/testsuite/spool/mbox archive/mbox.1
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["archive" "*"],
+[],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
])
LIST_CHECK([],[],
-["~" "*"],
+["~/" "*"],
[1 NO LIST The requested item could not be found
])
LIST_CHECK([],[],
-["~foo" "%"],
+["~foo/" "%"],
[* LIST (\NoInferiors) "/" ~foo/mbox
* LIST (\NoSelect) "/" ~foo/dir
],
@@ -185,7 +212,7 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
])
LIST_CHECK([],[],
-["~foo" "*"],
+["~foo/" "*"],
[* LIST (\NoInferiors) "/" ~foo/dir/saved
* LIST (\NoInferiors) "/" ~foo/mbox
* LIST (\NoSelect) "/" ~foo/dir
@@ -209,7 +236,7 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
])
LIST_CHECK([],[],
-["~foo" "%/*"],
+["~foo/" "%/*"],
[* LIST (\NoInferiors) "/" ~foo/dir/saved
],
[mkdir home home/foo home/foo/dir home/bar
@@ -221,7 +248,7 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
# ###
LIST_CHECK([],[],
-["other" "*"],
+["other/" "*"],
[1 NO LIST The requested item could not be found
])
@@ -261,7 +288,7 @@ cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
])
LIST_CHECK([],[],
-["other/foo" "%/*"],
+["other/foo/" "%/*"],
[* LIST (\NoInferiors) "/" other/foo/dir/saved
],
[mkdir home home/foo home/foo/dir home/bar
@@ -282,21 +309,21 @@ LIST_CHECK([root ref + asterisk],[list03],
])
LIST_CHECK([absolute reference + asterisk],[list04],
-["/foo/bar/baz" "*"],
+["/foo/bar/baz/" "*"],
[],
[mkdir IMAP4D_HOMEDIR/folder
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/one,IMAP4D_HOMEDIR/folder)
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/two,IMAP4D_HOMEDIR/folder)])
LIST_CHECK([absolute reference + percent],[list05],
-["/foo/bar/baz" "%"],
+["/foo/bar/baz/" "%"],
[],
[mkdir IMAP4D_HOMEDIR/folder
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/one,IMAP4D_HOMEDIR/folder)
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/two,IMAP4D_HOMEDIR/folder)])
LIST_CHECK([absolute reference + mailbox],[list06],
-["/foo/bar/baz" "mbox"],
+["/foo/bar/baz/" "mbox"],
[],
[mkdir IMAP4D_HOMEDIR/folder
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/one,IMAP4D_HOMEDIR/folder)
diff --git a/imap4d/tests/testclient.c b/imap4d/tests/testclient.c
new file mode 100644
index 000000000..59719dc57
--- /dev/null
+++ b/imap4d/tests/testclient.c
@@ -0,0 +1,191 @@
+/*
+ NAME
+ testclient - test imap client library using GNU imap4d
+
+ SYNOPSIS
+ testclient CONFIG_FILE CLIENT_COMMAND
+
+ DESCRIPTION
+ Auxiliary tool for testing the mailutils IMAP client library.
+
+ Arguments are:
+ CONFIG_FILE - name of the imap4d configuration file
+ CLIENT_COMMAND - command to run.
+
+ The tool finds first unused TCP port on localhost and starts
+ listening on that port in the background. When a connection
+ arrives, it starts imap4d in inetd mode with the given
+ configuratiom file.
+
+ The master process then adds the following two variables to the
+ environment:
+
+ PORT - port number it is listening on
+ URL - mailutils URL (imap://127.0.0.1:$PORT)
+
+ Finally, it executes /bin/sh -c CLIENT_COMMAND. The client
+ command is supposed to connect to the port and issue some IMAP
+ commands.
+
+ The program imposes a 60 second timeout on the execution time.
+
+ LICENSE
+ Copyright (C) 2021 Free Software Foundation, Inc.
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+
+char const *progname;
+
+static void
+error (int exit_code, int error_code, char const *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (stderr, "%s: ", progname);
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ if (error_code)
+ fprintf (stderr, ": %s", strerror (error_code));
+ fputc ('\n', stderr);
+ if (exit_code)
+ exit (exit_code);
+}
+
+static int
+listener_setup (char *scheme)
+{
+ struct addrinfo *ap, hints;
+ socklen_t len;
+ int fd;
+ int i;
+ char serv[80];
+ char urlbuf[80];
+
+ /* Find first free port */
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET;
+
+ if ((i = getaddrinfo ("127.0.0.1", NULL, &hints, &ap)) != 0)
+ error (EX_OSERR, errno, "getaddrinfo: %s", gai_strerror (i));
+
+ fd = socket (PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ error (EX_OSERR, errno, "socket");
+ i = 1;
+ setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i));
+
+ ((struct sockaddr_in*)ap->ai_addr)->sin_port = 0;
+ if (bind (fd, ap->ai_addr, ap->ai_addrlen) < 0)
+ error (EX_OSERR, errno, "bind");
+
+ if (listen (fd, 8) == -1)
+ error (EX_OSERR, errno, "listen");
+
+ len = ap->ai_addrlen;
+ if (getsockname (fd, ap->ai_addr, &len))
+ error(EX_OSERR, errno, "getsockname");
+
+ /* Prepare environment */
+ if ((i = getnameinfo (ap->ai_addr, len, NULL, 0, serv, sizeof serv,
+ NI_NUMERICSERV)) != 0)
+ error (EX_OSERR, errno, "getnameinfo: %s", gai_strerror (i));
+
+ setenv ("PORT", serv, 1);
+ snprintf (urlbuf, sizeof urlbuf, "%s://127.0.0.1:%s", scheme, serv);
+ setenv ("URL", urlbuf, 1);
+
+ return fd;
+}
+
+void
+usage (void)
+{
+ printf ("usage: %s CONFIG_FILE COMMAND\n", progname);
+}
+
+int
+main (int argc, char **argv)
+{
+ int lfd;
+ pid_t pid;
+
+ progname = argv[0];
+
+ if (argc != 3)
+ {
+ usage ();
+ exit (EX_USAGE);
+ }
+
+ lfd = listener_setup ("imap");
+ pid = fork ();
+ if (pid == -1)
+ error (EX_OSERR, errno, "fork");
+
+ if (pid == 0)
+ {
+ /* Run server */
+ int fd;
+ char *sargv[] = {
+ "imap4d",
+ "--inetd",
+ "--preauth",
+ "--foreground",
+ "--no-config",
+ "--config-file",
+ argv[1],
+ "--set",
+ ".logging.syslog=off",
+ NULL
+ };
+ struct sockaddr_in sin;
+ socklen_t len = sizeof sin;
+
+ alarm (60);//FIXME
+ fd = accept (lfd, (struct sockaddr *)&sin, &len);
+
+ if (fd == -1)
+ error (EX_OSERR, errno, "accept");
+
+ dup2 (fd, 0);
+ dup2 (fd, 1);
+ if (fd > 1)
+ close (fd);
+ close (lfd);
+
+ execvp (sargv[0], sargv);
+ _exit (127);
+ }
+
+ execlp ("/bin/sh", "/bin/sh", "-c", argv[2], NULL);
+ error (EX_OSERR, errno, "execlp");
+}
diff --git a/imap4d/tests/testsuite.at b/imap4d/tests/testsuite.at
index d58303dd8..f8edf9d52 100644
--- a/imap4d/tests/testsuite.at
+++ b/imap4d/tests/testsuite.at
@@ -102,3 +102,6 @@ m4_include([fetch.at])
AT_BANNER([IDEF Checks])
m4_include([IDEF0955.at])
m4_include([IDEF0956.at])
+
+AT_BANNER([Client library])
+m4_include([clt_list.at])
diff --git a/include/mailutils/address.h b/include/mailutils/address.h
index 0f3c97c46..f1f7b8d0a 100644
--- a/include/mailutils/address.h
+++ b/include/mailutils/address.h
@@ -63,6 +63,9 @@ struct mu_address
struct mu_address *next;
};
+#define MU_ADDRESS_HINT_INITIALIZER \
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
extern int mu_address_create_null (mu_address_t *);
extern int mu_address_create_hint (mu_address_t *, const char *,
diff --git a/include/mailutils/locker.h b/include/mailutils/locker.h
index 21aed9de9..082bbd942 100644
--- a/include/mailutils/locker.h
+++ b/include/mailutils/locker.h
@@ -25,105 +25,56 @@ extern "C" {
#endif
/* lock expiry time */
-#define MU_LOCKER_EXPIRE_TIME (10 * 60)
-#define MU_LOCKER_RETRIES (10)
-#define MU_LOCKER_RETRY_SLEEP (1)
-#define MU_LOCKER_EXTERNAL_PROGRAM "dotlock"
+#define MU_LOCKER_DEFAULT_EXPIRE_TIME (10 * 60)
+#define MU_LOCKER_DEFAULT_RETRY_COUNT 10
+#define MU_LOCKER_DEFAULT_RETRY_SLEEP 1
+#define MU_LOCKER_DEFAULT_EXT_LOCKER "dotlock"
/* return codes for the external locker */
-#define MU_DL_EX_PERM 4 /* insufficient permissions */
-#define MU_DL_EX_EXIST 3 /* lock requested, but file is already locked */
-#define MU_DL_EX_NEXIST 2 /* unlock requested, but file is not locked */
-#define MU_DL_EX_ERROR 1 /* failed due to some other error */
-#define MU_DL_EX_OK 0 /* success */
-
-enum mu_locker_set_mode
+enum
{
- mu_locker_assign,
- mu_locker_set_bit,
- mu_locker_clear_bit
+ MU_DL_EX_OK = 0, /* success */
+ MU_DL_EX_ERROR = 1, /* failed due to some other error */
+ MU_DL_EX_NEXIST = 2, /* unlock requested, but file is not locked */
+ MU_DL_EX_EXIST = 3, /* lock requested, but file is already locked */
+ MU_DL_EX_PERM = 4, /* insufficient permissions */
};
-
-/* mu_locker_create() flags */
/* Locker types */
+enum
+ {
+ MU_LOCKER_TYPE_DOTLOCK = 0, /* Dotlock-style locking. The default. */
+ MU_LOCKER_TYPE_EXTERNAL = 1, /* Use external program to lock the file. */
+ MU_LOCKER_TYPE_KERNEL = 2, /* Use kernel locking (flock,lockf,ioctl) */
+ MU_LOCKER_TYPE_NULL = 3, /* No locking at all. */
+ };
-#define MU_LOCKER_TYPE_DOTLOCK 0
-#define MU_LOCKER_TYPE_EXTERNAL 1
- /* Use an external program to lock the file. This is necessary
- for programs having permission to access a file, but do not
- have write permission on the directory that contains that file. */
-#define MU_LOCKER_TYPE_KERNEL 2
- /* Use kernel locking (flock, lockf or ioctl) */
-#define MU_LOCKER_TYPE_NULL 3
- /* Special locker type: means no lock. This is to be used with
- temporary mailboxes stored in memory. */
-
-#define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
-#define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
-#define MU_LOCKER_IS_TYPE(f,t) (MU_LOCKER_FLAG_TO_TYPE(f) == (t))
-#define MU_LOCKER_SET_TYPE(f,t) ((f) = MU_LOCKER_TYPE_TO_FLAG(t) | MU_LOCKER_OPTIONS(f))
-#define MU_LOCKER_TYPE_MASK 0xff00
-#define MU_LOCKER_OPTION_MASK 0x00ff
-#define MU_LOCKER_OPTIONS(f) ((f) & MU_LOCKER_OPTION_MASK)
+#define MU_LOCKER_TYPE_DEFAULT MU_LOCKER_TYPE_DOTLOCK
-#define MU_LOCKER_NULL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_NULL)
-#define MU_LOCKER_DOTLOCK MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_DOTLOCK)
-#define MU_LOCKER_EXTERNAL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_EXTERNAL)
-#define MU_LOCKER_KERNEL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_KERNEL)
-
-/* Options */
-
-#define MU_LOCKER_SIMPLE 0x0000
- /* Just try and dotlock the file, not the default because its usually
- better to retry. */
-#define MU_LOCKER_RETRY 0x0001
- /* This requests that we loop retries times, sleeping retry_sleep
- seconds in between trying to obtain the lock before failing with
- MU_LOCK_CONFLICT. */
-#define MU_LOCKER_TIME 0x0002
- /* This mode checks the last update time of the lock, then removes
- it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this,
- then the servers better periodically update the lock on the
- file... do they? */
-#define MU_LOCKER_PID 0x0004
- /* PID locking is only useful for programs that aren't using
- an external dotlocker, non-setgid programs will use a dotlocker,
- which locks and exits imediately. This is a protection against
- a server crashing, it's not generally useful. */
+typedef struct
+{
+ int flags;
+ int type;
+ unsigned retry_count;
+ unsigned retry_sleep;
+ unsigned expire_time;
+ char *ext_locker;
+} mu_locker_hints_t;
+
+/* Locker hint flags */
+#define MU_LOCKER_FLAG_RETRY 0x0001 /* retry_count and retry_sleep are set */
+#define MU_LOCKER_FLAG_EXPIRE_TIME 0x0002 /* expire_time is set */
+#define MU_LOCKER_FLAG_CHECK_PID 0x0004 /* check if lock owner PID is active */
+#define MU_LOCKER_FLAG_EXT_LOCKER 0x0008 /* ext_locker is set */
+#define MU_LOCKER_FLAG_TYPE 0x0010 /* type is set */
+
+#define MU_LOCKER_FLAGS_ALL (\
+ MU_LOCKER_FLAG_TYPE | \
+ MU_LOCKER_FLAG_RETRY | \
+ MU_LOCKER_FLAG_EXPIRE_TIME | \
+ MU_LOCKER_FLAG_EXT_LOCKER | \
+ MU_LOCKER_FLAG_CHECK_PID )
-#define MU_LOCKER_DEFAULT (MU_LOCKER_DOTLOCK | MU_LOCKER_RETRY)
-
-/* Use these flags for as the default locker flags (the default defaults
- * to MU_LOCKER_DEFAULT). A flags of 0 resets the flags back to the
- * the default.
- */
-extern int mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode);
-extern void mu_locker_set_default_retry_timeout (time_t to);
-extern void mu_locker_set_default_retry_count (size_t n);
-extern void mu_locker_set_default_expire_timeout (time_t t);
-extern int mu_locker_set_default_external_program (char const *path);
-
-/* A flags of 0 means that the default will be used. */
-extern int mu_locker_create (mu_locker_t *, const char *filename, int flags);
-extern void mu_locker_destroy (mu_locker_t *);
-
-/* Time is measured in seconds. */
-
-extern int mu_locker_set_flags (mu_locker_t, int);
-extern int mu_locker_mod_flags (mu_locker_t locker, int flags,
- enum mu_locker_set_mode mode);
-extern int mu_locker_set_expire_time (mu_locker_t, int);
-extern int mu_locker_set_retries (mu_locker_t, int);
-extern int mu_locker_set_retry_sleep (mu_locker_t, int);
-extern int mu_locker_set_external (mu_locker_t, const char* program);
-
-extern int mu_locker_get_flags (mu_locker_t, int*);
-extern int mu_locker_get_expire_time (mu_locker_t, int*);
-extern int mu_locker_get_retries (mu_locker_t, int*);
-extern int mu_locker_get_retry_sleep (mu_locker_t, int*);
-extern int mu_locker_get_external (mu_locker_t, char**);
-
enum mu_locker_mode
{
mu_lck_shr, /* Shared (advisory) lock */
@@ -132,12 +83,79 @@ enum mu_locker_mode
locking otherwise */
};
+#define MU_LOCKFILE_MODE 0644
+
+extern int mu_locker_create_ext (mu_locker_t *, const char *, mu_locker_hints_t *);
+extern int mu_locker_modify (mu_locker_t, mu_locker_hints_t *);
+extern void mu_locker_destroy (mu_locker_t *);
+
extern int mu_locker_lock_mode (mu_locker_t, enum mu_locker_mode);
extern int mu_locker_lock (mu_locker_t);
extern int mu_locker_touchlock (mu_locker_t);
extern int mu_locker_unlock (mu_locker_t);
extern int mu_locker_remove_lock (mu_locker_t);
+extern int mu_locker_get_hints (mu_locker_t lck, mu_locker_hints_t *hints);
+
+extern mu_locker_hints_t mu_locker_defaults;
+
+/*
+ * Deprecated defines and interfaces.
+ */
+
+/* Legacy definitions for locker defaults */
+#define MU_LOCKER_EXPIRE_TIME MU_LOCKER_DEFAULT_EXPIRE_TIME
+#define MU_LOCKER_RETRIES MU_LOCKER_DEFAULT_RETRY_COUNT
+#define MU_LOCKER_RETRY_SLEEP MU_LOCKER_DEFAULT_RETRY_SLEEP
+#define MU_LOCKER_EXTERNAL_PROGRAM MU_LOCKER_DEFAULT_EXT_LOCKER
+
+/* Legacy definitions of locker types */
+#define MU_LOCKER_DOTLOCK (MU_LOCKER_TYPE_DOTLOCK << 8)
+#define MU_LOCKER_EXTERNAL (MU_LOCKER_TYPE_EXTERNAL << 8)
+#define MU_LOCKER_KERNEL (MU_LOCKER_TYPE_KERNEL << 8)
+#define MU_LOCKER_NULL (MU_LOCKER_TYPE_NULL << 8)
+
+/* Legacy definitions of locker flags (a.k.a. options). */
+#define MU_LOCKER_SIMPLE 0x0000
+#define MU_LOCKER_RETRY MU_LOCKER_FLAG_RETRY
+#define MU_LOCKER_TIME MU_LOCKER_FLAG_EXPIRE_TIME
+#define MU_LOCKER_PID MU_LOCKER_FLAG_CHECK_PID
+
+#define MU_LOCKER_DEFAULT (MU_LOCKER_DOTLOCK | MU_LOCKER_RETRY)
+
+/* The following was used to pack/unpack flags and locker type: */
+#define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
+#define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
+#define MU_LOCKER_TYPE_MASK 0xff00
+#define MU_LOCKER_OPTION_MASK 0x00ff
+
+enum mu_locker_set_mode
+ {
+ mu_locker_assign,
+ mu_locker_set_bit,
+ mu_locker_clear_bit
+ };
+
+extern int mu_locker_create (mu_locker_t *, const char *, int) MU_DEPRECATED;
+
+extern int mu_locker_set_default_flags (int, enum mu_locker_set_mode) MU_DEPRECATED;
+extern void mu_locker_set_default_retry_timeout (time_t) MU_DEPRECATED;
+extern void mu_locker_set_default_retry_count (size_t) MU_DEPRECATED;
+extern void mu_locker_set_default_expire_timeout (time_t) MU_DEPRECATED;
+extern int mu_locker_set_default_external_program (char const *) MU_DEPRECATED;
+
+extern int mu_locker_set_flags (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_mod_flags (mu_locker_t, int, enum mu_locker_set_mode) MU_DEPRECATED;
+extern int mu_locker_set_expire_time (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_set_retries (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_set_retry_sleep (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_set_external (mu_locker_t, const char *) MU_DEPRECATED;
+
+extern int mu_locker_get_flags (mu_locker_t, int *) MU_DEPRECATED;
+extern int mu_locker_get_expire_time (mu_locker_t, int *) MU_DEPRECATED;
+extern int mu_locker_get_retries (mu_locker_t, int *) MU_DEPRECATED;
+extern int mu_locker_get_retry_sleep (mu_locker_t, int *) MU_DEPRECATED;
+
#ifdef __cplusplus
}
#endif
diff --git a/include/mailutils/sys/imap.h b/include/mailutils/sys/imap.h
index 381443147..4752b9e71 100644
--- a/include/mailutils/sys/imap.h
+++ b/include/mailutils/sys/imap.h
@@ -110,6 +110,10 @@ struct _mu_imap
int mbox_writable:1; /* Is it open read/write? */
struct mu_imap_stat mbox_stat; /* Stats obtained from it */
+ /* Folder data */
+ int separator; /* Separator character */
+ size_t prefix_len; /* Path prefix length */
+
/* Callbacks */
struct
{
diff --git a/lib/manlock.c b/lib/manlock.c
index 0c32115cc..e06387807 100644
--- a/lib/manlock.c
+++ b/lib/manlock.c
@@ -128,14 +128,17 @@ mailbox_open_and_lock (mu_mailbox_t mbox, int flags)
if (lock)
{
- status = mu_locker_get_flags (lock, &flags);
+ mu_locker_hints_t hints;
+
+ hints.flags = MU_LOCKER_FLAG_TYPE;
+ status = mu_locker_get_hints (lock, &hints);
if (status)
{
- mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_get_flags", urlstr,
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_get_hints", urlstr,
status);
return MU_ERR_FAILURE;
}
- if (flags & MU_LOCKER_NULL)
+ if (hints.type == MU_LOCKER_TYPE_NULL)
lock = NULL;
}
@@ -157,10 +160,10 @@ mailbox_open_and_lock (mu_mailbox_t mbox, int flags)
if (!fname)
return MU_ERR_FAILURE;
- status = mu_locker_create (&lock, fname, 0);
+ status = mu_locker_create_ext (&lock, fname, NULL);
if (status)
{
- mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create", fname, status);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create_ext", fname, status);
free (fname);
return MU_ERR_FAILURE;
}
@@ -245,13 +248,14 @@ manlock_lock (mu_mailbox_t mbox)
mu_locker_t lock = NULL;
const char *name;
int status;
-
+ mu_locker_hints_t hints = { .flags = MU_LOCKER_FLAG_CHECK_PID };
+
if (!manlock_mandatory_locking)
return 0;
mu_mailbox_get_url (mbox, &url);
name = mu_url_to_string (url);
- mu_mailbox_get_locker (mbox, &lock);
- mu_locker_mod_flags (lock, MU_LOCKER_PID, mu_locker_set_bit);
+ mu_mailbox_get_locker (mbox, &lock);
+ mu_locker_modify (lock, &hints);
if ((status = mu_locker_lock (lock)))
{
mu_diag_output (MU_DIAG_NOTICE, _("locking mailbox `%s' failed: %s"),
diff --git a/libmailutils/address/addrstream.c b/libmailutils/address/addrstream.c
index 4bcf181ed..0fbaad28a 100644
--- a/libmailutils/address/addrstream.c
+++ b/libmailutils/address/addrstream.c
@@ -19,6 +19,7 @@
# include <config.h>
#endif
#include <stdlib.h>
+#include <string.h>
#include <mailutils/address.h>
#include <mailutils/stream.h>
@@ -37,23 +38,34 @@ mu_stream_format_address (mu_stream_t str, mu_address_t addr)
if (comma)
mu_stream_write (str, ",", 1, NULL);
- if (addr->personal)
+ if (!addr->personal &&
+ !addr->comments &&
+ !addr->domain &&
+ !addr->route)
{
- mu_stream_printf (str, "\"%s\"", addr->personal);
- space++;
+ /* Local user name: print as is */
+ mu_stream_write (str, addr->email, strlen (addr->email), NULL);
}
-
- if (addr->comments)
+ else
{
+ if (addr->personal)
+ {
+ mu_stream_printf (str, "\"%s\"", addr->personal);
+ space++;
+ }
+
+ if (addr->comments)
+ {
+ if (space)
+ mu_stream_write (str, " ", 1, NULL);
+ mu_stream_printf (str, "(%s)", addr->comments);
+ space++;
+ }
+
if (space)
mu_stream_write (str, " ", 1, NULL);
- mu_stream_printf (str, "(%s)", addr->comments);
- space++;
+ mu_stream_printf (str, "<%s>", addr->email);
}
-
- if (space)
- mu_stream_write (str, " ", 1, NULL);
- mu_stream_printf (str, "<%s>", addr->email);
comma++;
}
}
diff --git a/libmailutils/base/amd.c b/libmailutils/base/amd.c
index 349a8060a..e488da153 100644
--- a/libmailutils/base/amd.c
+++ b/libmailutils/base/amd.c
@@ -500,7 +500,7 @@ amd_open (mu_mailbox_t mailbox, int flags)
_amd_prop_create (amd);
if (mailbox->locker == NULL)
- mu_locker_create (&mailbox->locker, "/dev/null", 0);
+ mu_locker_create_ext (&mailbox->locker, "/dev/null", NULL);
return 0;
}
diff --git a/libmailutils/base/locker.c b/libmailutils/base/locker.c
index ef26fc52b..520bdca11 100644
--- a/libmailutils/base/locker.c
+++ b/libmailutils/base/locker.c
@@ -41,8 +41,6 @@
#include <mailutils/util.h>
#include <mailutils/io.h>
-#define LOCKFILE_ATTR 0644
-
/* First draft by Brian Edmond. */
/* For subsequent modifications, see the GNU mailutils ChangeLog. */
@@ -51,10 +49,11 @@ struct _mu_locker
unsigned refcnt; /* Number of times mu_locker_lock was called */
enum mu_locker_mode mode; /* Current locking mode (if refcnt > 0) */
+ int type;
char *file;
int flags;
int expire_time;
- int retries;
+ int retry_count;
int retry_sleep;
union lock_data
@@ -63,62 +62,16 @@ struct _mu_locker
{
char *dotlock;
char *nfslock;
- } dot;
+ } dot; /* MU_LOCKER_TYPE_DOTLOCK */
struct
{
char *name;
- } external;
+ } external; /* MU_LOCKER_TYPE_EXTERNAL */
- struct
- {
- int fd;
- } kernel;
+ int fd; /* MU_LOCKER_TYPE_KERNEL */
} data;
};
-
-#define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
-
-struct locker_tab
-{
- int (*init) (mu_locker_t);
- void (*destroy) (mu_locker_t);
- int (*prelock) (mu_locker_t);
- int (*lock) (mu_locker_t, enum mu_locker_mode);
- int (*unlock) (mu_locker_t);
-};
-
-static int init_dotlock (mu_locker_t);
-static void destroy_dotlock (mu_locker_t);
-static int lock_dotlock (mu_locker_t, enum mu_locker_mode);
-static int unlock_dotlock (mu_locker_t);
-
-static int init_external (mu_locker_t);
-static void destroy_external (mu_locker_t);
-static int lock_external (mu_locker_t, enum mu_locker_mode);
-static int unlock_external (mu_locker_t);
-
-static int init_kernel (mu_locker_t);
-static int lock_kernel (mu_locker_t, enum mu_locker_mode);
-static int unlock_kernel (mu_locker_t);
-
-static int prelock_common (mu_locker_t);
-
-static struct locker_tab locker_tab[] = {
- /* MU_LOCKER_TYPE_DOTLOCK */
- { init_dotlock, destroy_dotlock, prelock_common,
- lock_dotlock, unlock_dotlock },
- /* MU_LOCKER_TYPE_EXTERNAL */
- { init_external, destroy_external, prelock_common,
- lock_external, unlock_external },
- /* MU_LOCKER_TYPE_KERNEL */
- { init_kernel, NULL, NULL, lock_kernel, unlock_kernel },
- /* MU_LOCKER_TYPE_NULL */
- { NULL, NULL, NULL, NULL, NULL }
-};
-
-#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
-
static int
stat_check (const char *file, int fd, int links)
@@ -145,20 +98,17 @@ stat_check (const char *file, int fd, int links)
{
/* If the link and stat don't report the same info, or the
file is a symlink, fail the locking. */
-#define CHK(X) if(X) err = EINVAL
-
- CHK (!S_ISREG (fn_stat.st_mode));
- CHK (!S_ISREG (fd_stat.st_mode));
- CHK (fn_stat.st_nlink != links);
- CHK (fn_stat.st_dev != fd_stat.st_dev);
- CHK (fn_stat.st_ino != fd_stat.st_ino);
- CHK (fn_stat.st_mode != fd_stat.st_mode);
- CHK (fn_stat.st_nlink != fd_stat.st_nlink);
- CHK (fn_stat.st_uid != fd_stat.st_uid);
- CHK (fn_stat.st_gid != fd_stat.st_gid);
- CHK (fn_stat.st_rdev != fd_stat.st_rdev);
-
-#undef CHK
+ if (!S_ISREG (fn_stat.st_mode)
+ || !S_ISREG (fd_stat.st_mode)
+ || fn_stat.st_nlink != links
+ || fn_stat.st_dev != fd_stat.st_dev
+ || fn_stat.st_ino != fd_stat.st_ino
+ || fn_stat.st_mode != fd_stat.st_mode
+ || fn_stat.st_nlink != fd_stat.st_nlink
+ || fn_stat.st_uid != fd_stat.st_uid
+ || fn_stat.st_gid != fd_stat.st_gid
+ || fn_stat.st_rdev != fd_stat.st_rdev)
+ err = EINVAL;
}
if (localfd != -1)
close (localfd);
@@ -195,471 +145,23 @@ prelock_common (mu_locker_t locker)
of 1, that we have permission to read, etc., or don't lock it. */
return check_file_permissions (locker->file);
}
-
-
-static int mu_locker_default_flags = MU_LOCKER_DEFAULT;
-static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP;
-static size_t mu_locker_retry_count = MU_LOCKER_RETRIES;
-static time_t mu_locker_expire_timeout = MU_LOCKER_EXPIRE_TIME;
-static char *mu_locker_external_program = NULL;
-
-int
-mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode)
-{
- switch (mode)
- {
- case mu_locker_assign:
- mu_locker_default_flags = flags;
- break;
-
- case mu_locker_set_bit:
- mu_locker_default_flags |= flags;
- break;
-
- case mu_locker_clear_bit:
- mu_locker_default_flags &= ~flags;
- break;
-
- default:
- return EINVAL;
- }
- return 0;
-}
-
-void
-mu_locker_set_default_retry_timeout (time_t to)
-{
- mu_locker_retry_timeout = to;
-}
-
-void
-mu_locker_set_default_retry_count (size_t n)
-{
- mu_locker_retry_count = n;
-}
-
-void
-mu_locker_set_default_expire_timeout (time_t t)
-{
- mu_locker_expire_timeout = t;
-}
-
-int
-mu_locker_set_default_external_program (char const *path)
-{
- char *p = strdup (path);
- if (!p)
- return ENOMEM;
- free (mu_locker_external_program);
- mu_locker_external_program = p;
- return 0;
-}
-
-int
-mu_locker_mod_flags (mu_locker_t locker, int flags,
- enum mu_locker_set_mode mode)
-{
- unsigned otype, ntype;
- int new_flags;
-
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- switch (mode)
- {
- case mu_locker_assign:
- new_flags = flags;
- break;
-
- case mu_locker_set_bit:
- new_flags = locker->flags | flags;
- break;
-
- case mu_locker_clear_bit:
- new_flags = locker->flags & ~flags;
- break;
-
- default:
- return EINVAL;
- }
-
- otype = MU_LOCKER_TYPE (locker);
- if (otype >= MU_LOCKER_NTYPES)
- return EINVAL;
- ntype = MU_LOCKER_FLAG_TO_TYPE (new_flags);
- if (ntype >= MU_LOCKER_NTYPES)
- return EINVAL;
-
- if (ntype != otype)
- {
- int rc;
-
- if (locker_tab[otype].destroy)
- locker_tab[otype].destroy (locker);
- locker->flags = new_flags;
- if (locker_tab[ntype].init)
- {
- rc = locker_tab[ntype].init (locker);
- if (rc)
- locker->flags = MU_LOCKER_NULL;
- return rc;
- }
- }
- else
- locker->flags = new_flags;
-
- return 0;
-}
-
-int
-mu_locker_set_flags (mu_locker_t locker, int flags)
-{
- return mu_locker_mod_flags (locker, flags, mu_locker_assign);
-}
-
-int
-mu_locker_set_expire_time (mu_locker_t locker, int etime)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (etime <= 0)
- return EINVAL;
-
- locker->expire_time = etime;
-
- return 0;
-}
-
-int
-mu_locker_set_retries (mu_locker_t locker, int retries)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (retries <= 0)
- return EINVAL;
-
- locker->retries = retries;
-
- return 0;
-}
-
-int
-mu_locker_set_retry_sleep (mu_locker_t locker, int retry_sleep)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (retry_sleep <= 0)
- return EINVAL;
-
- locker->retry_sleep = retry_sleep;
-
- return 0;
-}
-
-int
-mu_locker_set_external (mu_locker_t locker, const char* program)
-{
- char* p = NULL;
-
- if (!locker)
- return MU_ERR_LOCKER_NULL;
- if (MU_LOCKER_TYPE (locker) != MU_LOCKER_TYPE_EXTERNAL)
- return EINVAL;
-
- /* program can be NULL */
- if (program != 0)
- {
- p = strdup (program);
- if (!p)
- return ENOMEM;
- }
-
- free (locker->data.external.name);
- locker->data.external.name = p;
-
- return 0;
-}
-
-int
-mu_locker_get_flags (mu_locker_t locker, int *flags)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!flags)
- return EINVAL;
-
- *flags = locker->flags;
-
- return 0;
-}
-
-int
-mu_locker_get_expire_time (mu_locker_t locker, int *ptime)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!ptime)
- return EINVAL;
-
- *ptime = locker->expire_time;
-
- return 0;
-}
-
-int
-mu_locker_get_retries (mu_locker_t locker, int *retries)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!retries)
- return EINVAL;
-
- *retries = locker->retries;
-
- return 0;
-}
-
-int
-mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!retry_sleep)
- return EINVAL;
-
- *retry_sleep = locker->retry_sleep;
-
- return 0;
-}
-
-int
-mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
-{
- unsigned type;
- mu_locker_t l;
- char *filename;
- int err = 0;
-
- if (plocker == NULL)
- return MU_ERR_OUT_PTR_NULL;
-
- if (fname == NULL)
- return EINVAL;
-
- if ((err = mu_unroll_symlink (fname, &filename)))
- {
- if (err == ENOENT)
- {
- /* Try the directory part. If it unrolls successfully (i.e.
- all its components exist), tuck the filename part back in
- the resulting path and use it as the lock filename. */
- char *p, *new_name, *tmp = strdup (fname);
- if (!tmp)
- return ENOMEM;
- p = strrchr (tmp, '/');
- if (!p)
- filename = tmp;
- else
- {
- *p = 0;
- err = mu_unroll_symlink (tmp, &filename);
- if (err)
- {
- free (tmp);
- return err;
- }
-
- new_name = mu_make_file_name_suf (filename, p + 1, NULL);
- free (tmp);
- free (filename);
- if (!new_name)
- return ENOMEM;
- filename = new_name;
- }
- }
- else
- return err;
- }
-
- l = calloc (1, sizeof (*l));
-
- if (l == NULL)
- {
- free (filename);
- return ENOMEM;
- }
-
- l->file = filename;
-
- if (l->file == NULL)
- {
- free (l);
- return ENOMEM;
- }
-
- if (strcmp (filename, "/dev/null") == 0)
- l->flags = MU_LOCKER_NULL;
- else if (flags)
- l->flags = flags;
- else
- l->flags = mu_locker_default_flags;
-
- l->expire_time = mu_locker_expire_timeout;
- l->retries = mu_locker_retry_count;
- l->retry_sleep = mu_locker_retry_timeout;
-
- type = MU_LOCKER_TYPE (l);
-
- if (type >= MU_LOCKER_NTYPES)
- {
- free (l->file);
- return EINVAL;
- }
-
- /* Initialize locker-type-specific data */
- err = locker_tab[type].init ? locker_tab[type].init (l) : 0;
- if (err)
- {
- mu_locker_destroy (&l);
- return err;
- }
-
- *plocker = l;
-
- return 0;
-}
-
-void
-mu_locker_destroy (mu_locker_t *plocker)
-{
- if (plocker && *plocker)
- {
- unsigned type = MU_LOCKER_TYPE (*plocker);
- if (type < MU_LOCKER_NTYPES)
- {
- if (locker_tab[type].destroy)
- locker_tab[type].destroy (*plocker);
- free ((*plocker)->file);
- free (*plocker);
- *plocker = NULL;
- }
- }
-}
-
-int
-mu_locker_lock_mode (mu_locker_t lock, enum mu_locker_mode mode)
-{
- int rc;
- unsigned type;
- unsigned retries = 1;
-
- if (lock == NULL || (type = MU_LOCKER_TYPE (lock)) >= MU_LOCKER_NTYPES)
- return EINVAL;
-
- if (locker_tab[type].prelock && (rc = locker_tab[type].prelock (lock)))
- return rc;
-
- /* Is the lock already applied? */
- if (lock->refcnt > 0)
- {
- lock->refcnt++;
- if (mode == lock->mode)
- return 0;
- }
-
- lock->mode = mode;
-
- if (lock->flags & MU_LOCKER_RETRY)
- retries = lock->retries;
-
- if (locker_tab[type].lock)
- {
- while (retries--)
- {
- rc = locker_tab[type].lock (lock, mode);
- if (rc == EAGAIN && retries)
- sleep (lock->retry_sleep);
- else
- break;
- }
-
- if (rc == EAGAIN)
- rc = MU_ERR_LOCK_CONFLICT;
- }
- else
- rc = 0;
-
- if (rc == 0)
- lock->refcnt++;
-
- return rc;
-}
-
-int
-mu_locker_lock (mu_locker_t lock)
-{
- return mu_locker_lock_mode (lock, mu_lck_exc);
-}
-
-int
-mu_locker_unlock (mu_locker_t lock)
-{
- int rc = 0;
- unsigned type;
-
- if (!lock)
- return MU_ERR_LOCKER_NULL;
-
- if (lock->refcnt == 0)
- return MU_ERR_LOCK_NOT_HELD;
-
- if ((rc = check_file_permissions (lock->file)))
- return rc;
-
- if (--lock->refcnt > 0)
- return 0;
-
- type = MU_LOCKER_TYPE (lock);
- if (locker_tab[type].unlock)
- rc = locker_tab[type].unlock (lock);
- else
- rc = 0;
-
- return rc;
-}
-
-int
-mu_locker_remove_lock (mu_locker_t lock)
-{
- if (!lock)
- return MU_ERR_LOCKER_NULL;
-
- /* Force the reference count to 1 to unlock the file. */
- lock->refcnt = 1;
- return mu_locker_unlock (lock);
-}
-
+/* Dotlock type */
#define DOTLOCK_SUFFIX ".lock"
-/* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
+/* expire a stale lock (if MU_LOCKER_FLAG_CHECK_PID or
+ MU_LOCKER_FLAG_EXPIRE_TIME) */
static void
expire_stale_lock (mu_locker_t lock)
{
int stale = 0;
int fd = open (lock->data.dot.dotlock, O_RDONLY);
+
if (fd == -1)
return;
/* Check to see if this process is still running. */
- if (lock->flags & MU_LOCKER_PID)
+ if (lock->flags & MU_LOCKER_FLAG_CHECK_PID)
{
char buf[16];
pid_t pid;
@@ -680,7 +182,7 @@ expire_stale_lock (mu_locker_t lock)
}
/* Check to see if the lock expired. */
- if (lock->flags & MU_LOCKER_TIME)
+ if (lock->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
{
struct stat stbuf;
@@ -696,16 +198,16 @@ expire_stale_lock (mu_locker_t lock)
}
static int
-init_dotlock (mu_locker_t locker)
+init_dotlock (mu_locker_t lck, mu_locker_hints_t *hints)
{
char *tmp, *p;
/* Make sure the spool directory is writable */
- tmp = strdup (locker->file);
+ tmp = strdup (lck->file);
if (!tmp)
return ENOMEM;
- strcpy (tmp, locker->file);
+ strcpy (tmp, lck->file);
p = strrchr (tmp, '/');
if (!p)
{
@@ -720,33 +222,36 @@ init_dotlock (mu_locker_t locker)
if (access (tmp, W_OK))
{
/* Fallback to kernel locking */
+ mu_locker_hints_t hints = {
+ .flags = MU_LOCKER_FLAG_TYPE,
+ .type = MU_LOCKER_TYPE_KERNEL
+ };
free (tmp);
- return mu_locker_set_flags (locker,
- MU_LOCKER_KERNEL|MU_LOCKER_OPTIONS(locker->flags));
+ return mu_locker_modify (lck, &hints);
}
free (tmp);
+
+ lck->data.dot.dotlock = malloc (strlen (lck->file)
+ + sizeof (DOTLOCK_SUFFIX));
- locker->data.dot.dotlock = malloc (strlen (locker->file)
- + sizeof (DOTLOCK_SUFFIX));
-
- if (!locker->data.dot.dotlock)
+ if (!lck->data.dot.dotlock)
return ENOMEM;
- strcpy (locker->data.dot.dotlock, locker->file);
- strcat (locker->data.dot.dotlock, DOTLOCK_SUFFIX);
+ strcpy (lck->data.dot.dotlock, lck->file);
+ strcat (lck->data.dot.dotlock, DOTLOCK_SUFFIX);
return 0;
}
static void
-destroy_dotlock (mu_locker_t locker)
+destroy_dotlock (mu_locker_t lck)
{
- free (locker->data.dot.dotlock);
- free (locker->data.dot.nfslock);
+ free (lck->data.dot.dotlock);
+ free (lck->data.dot.nfslock);
}
static int
-lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
+lock_dotlock (mu_locker_t lck, enum mu_locker_mode mode)
{
int rc;
char *host = NULL;
@@ -754,14 +259,14 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
int err = 0;
int fd;
- if (locker->data.dot.nfslock)
+ if (lck->data.dot.nfslock)
{
- unlink (locker->data.dot.nfslock);
- free (locker->data.dot.nfslock);
- locker->data.dot.nfslock = NULL;
+ unlink (lck->data.dot.nfslock);
+ free (lck->data.dot.nfslock);
+ lck->data.dot.nfslock = NULL;
}
- expire_stale_lock (locker);
+ expire_stale_lock (lck);
/* build the NFS hitching-post to the lock file */
@@ -769,17 +274,17 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
if (rc)
return rc;
time (&now);
- rc = mu_asprintf (&locker->data.dot.nfslock,
+ rc = mu_asprintf (&lck->data.dot.nfslock,
"%s.%lu.%lu.%s",
- locker->file,
+ lck->file,
(unsigned long) getpid (),
(unsigned long) now, host);
free (host);
if (rc)
return rc;
- fd = open (locker->data.dot.nfslock,
- O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR);
+ fd = open (lck->data.dot.nfslock,
+ O_WRONLY | O_CREAT | O_EXCL, MU_LOCKFILE_MODE);
if (fd == -1)
{
if (errno == EEXIST)
@@ -790,32 +295,32 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
close (fd);
/* Try to link to the lockfile. */
- if (link (locker->data.dot.nfslock, locker->data.dot.dotlock) == -1)
+ if (link (lck->data.dot.nfslock, lck->data.dot.dotlock) == -1)
{
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
if (errno == EEXIST)
return EAGAIN;
return errno;
}
- if ((fd = open (locker->data.dot.dotlock, O_RDWR)) == -1)
+ if ((fd = open (lck->data.dot.dotlock, O_RDWR)) == -1)
{
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
return errno;
}
- err = stat_check (locker->data.dot.nfslock, fd, 2);
+ err = stat_check (lck->data.dot.nfslock, fd, 2);
if (err)
{
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
if (err == EINVAL)
return MU_ERR_LOCK_BAD_LOCK;
return errno;
}
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
- if (locker->flags & MU_LOCKER_PID)
+ if (lck->flags & MU_LOCKER_FLAG_CHECK_PID)
{
char buf[16];
sprintf (buf, "%ld", (long) getpid ());
@@ -826,14 +331,14 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
}
static int
-unlock_dotlock (mu_locker_t locker)
+unlock_dotlock (mu_locker_t lck)
{
- if (unlink (locker->data.dot.dotlock) == -1)
+ if (unlink (lck->data.dot.dotlock) == -1)
{
int err = errno;
if (err == ENOENT)
{
- locker->refcnt = 0; /*FIXME?*/
+ lck->refcnt = 0; /*FIXME?*/
err = MU_ERR_LOCK_NOT_HELD;
return err;
}
@@ -841,32 +346,10 @@ unlock_dotlock (mu_locker_t locker)
}
return 0;
}
-
-int
-mu_locker_touchlock (mu_locker_t lock)
-{
- if (!lock)
- return MU_ERR_LOCKER_NULL;
-
- if (MU_LOCKER_TYPE (lock) != MU_LOCKER_TYPE_DOTLOCK)
- return 0;
-
- if (lock->refcnt > 0)
- return utime (lock->data.dot.dotlock, NULL);
-
- return MU_ERR_LOCK_NOT_HELD;
-}
-
/* Kernel locking */
static int
-init_kernel (mu_locker_t locker)
-{
- return 0;
-}
-
-static int
-lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
+lock_kernel (mu_locker_t lck, enum mu_locker_mode mode)
{
int fd;
struct flock fl;
@@ -888,10 +371,10 @@ lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
return EINVAL;
}
- fd = open (locker->file, O_RDWR);
+ fd = open (lck->file, O_RDWR);
if (fd == -1)
return errno;
- locker->data.kernel.fd = fd;
+ lck->data.fd = fd;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
@@ -910,7 +393,7 @@ lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
}
static int
-unlock_kernel (mu_locker_t locker)
+unlock_kernel (mu_locker_t lck)
{
struct flock fl;
@@ -918,7 +401,7 @@ unlock_kernel (mu_locker_t locker)
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0; /* Unlock entire file */
- if (fcntl (locker->data.kernel.fd, F_SETLK, &fl))
+ if (fcntl (lck->data.fd, F_SETLK, &fl))
{
#ifdef EACCESS
if (errno == EACCESS)
@@ -928,24 +411,27 @@ unlock_kernel (mu_locker_t locker)
return EAGAIN;
return errno;
}
- close (locker->data.kernel.fd);
+ close (lck->data.fd);
+ lck->data.fd = -1;
return 0;
}
+/* External locking */
static int
-init_external (mu_locker_t locker)
+init_external (mu_locker_t lck, mu_locker_hints_t *hints)
{
- if (!(locker->data.external.name = strdup (mu_locker_external_program ?
- mu_locker_external_program :
- MU_LOCKER_EXTERNAL_PROGRAM)))
+ char const *ext_locker = hints->flags & MU_LOCKER_FLAG_EXT_LOCKER
+ ? hints->ext_locker
+ : MU_LOCKER_DEFAULT_EXT_LOCKER;
+ if (!(lck->data.external.name = strdup (ext_locker)))
return ENOMEM;
return 0;
}
static void
-destroy_external (mu_locker_t locker)
+destroy_external (mu_locker_t lck)
{
- free (locker->data.external.name);
+ free (lck->data.external.name);
}
/*
@@ -954,28 +440,27 @@ destroy_external (mu_locker_t locker)
#define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)
static int
-external_locker (mu_locker_t l, int lock)
+external_locker (mu_locker_t lck, int lock)
{
int err = 0;
char *av[6];
int ac = 0;
char aforce[3 + DEC_DIGS_PER_INT + 1];
char aretry[3 + DEC_DIGS_PER_INT + 1];
- int status = 0;
+ int status;
- av[ac++] = l->data.external.name ?
- l->data.external.name : MU_LOCKER_EXTERNAL_PROGRAM;
+ av[ac++] = lck->data.external.name;
- if (l->flags & MU_LOCKER_TIME)
+ if (lck->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
{
- snprintf (aforce, sizeof (aforce), "-f%d", l->expire_time);
+ snprintf (aforce, sizeof (aforce), "-f%d", lck->expire_time);
aforce[sizeof (aforce) - 1] = 0;
av[ac++] = aforce;
}
- if (l->flags & MU_LOCKER_RETRY)
+ if (lck->flags & MU_LOCKER_FLAG_RETRY)
{
- snprintf (aretry, sizeof (aretry), "-r%d", l->retries);
+ snprintf (aretry, sizeof (aretry), "-r%d", lck->retry_count);
aretry[sizeof (aretry) - 1] = 0;
av[ac++] = aretry;
}
@@ -983,13 +468,17 @@ external_locker (mu_locker_t l, int lock)
if (!lock)
av[ac++] = "-u";
- av[ac++] = l->file;
+ av[ac++] = lck->file;
av[ac++] = NULL;
if ((err = mu_spawnvp (av[0], av, &status)))
- return err;
-
+ {
+ perror ("mu_spawnvp");
+ fprintf (stderr, "errcode %d\n", err);
+ return err;
+ }
+
if (!WIFEXITED (status))
{
err = MU_ERR_LOCK_EXT_KILLED;
@@ -1004,7 +493,7 @@ external_locker (mu_locker_t l, int lock)
case MU_DL_EX_OK:
err = 0;
- l->refcnt = lock;
+ lck->refcnt = lock;
break;
case MU_DL_EX_NEXIST:
@@ -1030,14 +519,591 @@ external_locker (mu_locker_t l, int lock)
}
static int
-lock_external (mu_locker_t locker, enum mu_locker_mode mode)
+lock_external (mu_locker_t lck, enum mu_locker_mode mode)
+{
+ return external_locker (lck, 1);
+}
+
+static int
+unlock_external (mu_locker_t lck)
+{
+ return external_locker (lck, 0);
+}
+
+mu_locker_hints_t mu_locker_defaults = {
+ .flags = MU_LOCKER_FLAG_TYPE | MU_LOCKER_FLAG_RETRY,
+ .type = MU_LOCKER_TYPE_DEFAULT,
+ .retry_count = MU_LOCKER_DEFAULT_RETRY_COUNT,
+ .retry_sleep = MU_LOCKER_DEFAULT_RETRY_SLEEP
+};
+
+struct locker_tab
+{
+ int (*init) (mu_locker_t, mu_locker_hints_t *);
+ void (*destroy) (mu_locker_t);
+ int (*prelock) (mu_locker_t);
+ int (*lock) (mu_locker_t, enum mu_locker_mode);
+ int (*unlock) (mu_locker_t);
+};
+
+static struct locker_tab locker_tab[] = {
+ /* MU_LOCKER_TYPE_DOTLOCK */
+ { init_dotlock, destroy_dotlock, prelock_common,
+ lock_dotlock, unlock_dotlock },
+ /* MU_LOCKER_TYPE_EXTERNAL */
+ { init_external, destroy_external, prelock_common,
+ lock_external, unlock_external },
+ /* MU_LOCKER_TYPE_KERNEL */
+ { NULL, NULL, NULL, lock_kernel, unlock_kernel },
+ /* MU_LOCKER_TYPE_NULL */
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
+
+int
+mu_locker_create_ext (mu_locker_t *plocker, const char *fname,
+ mu_locker_hints_t *user_hints)
+{
+ mu_locker_t lck;
+ char *filename;
+ int err = 0;
+ mu_locker_hints_t hints;
+
+ if (plocker == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (fname == NULL)
+ return EINVAL;
+
+ if ((err = mu_unroll_symlink (fname, &filename)))
+ {
+ if (err == ENOENT)
+ {
+ /* Try the directory part. If it unrolls successfully (i.e.
+ all its components exist), tuck the filename part back in
+ the resulting path and use it as the lock filename. */
+ char *p, *new_name, *tmp = strdup (fname);
+ if (!tmp)
+ return ENOMEM;
+ p = strrchr (tmp, '/');
+ if (!p)
+ filename = tmp;
+ else
+ {
+ *p = 0;
+ err = mu_unroll_symlink (tmp, &filename);
+ if (err)
+ {
+ free (tmp);
+ return err;
+ }
+
+ new_name = mu_make_file_name_suf (filename, p + 1, NULL);
+ free (tmp);
+ free (filename);
+ if (!new_name)
+ return ENOMEM;
+ filename = new_name;
+ }
+ }
+ else
+ return err;
+ }
+
+ lck = calloc (1, sizeof (*lck));
+
+ if (lck == NULL)
+ {
+ free (filename);
+ return ENOMEM;
+ }
+
+ lck->file = filename;
+
+ hints = user_hints ? *user_hints : mu_locker_defaults;
+ if ((hints.flags & MU_LOCKER_FLAG_TYPE) == 0)
+ {
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_DEFAULT;
+ }
+ err = mu_locker_modify (lck, &hints);
+ if (err)
+ mu_locker_destroy (&lck);
+ else
+ *plocker = lck;
+
+ return err;
+}
+
+int
+mu_locker_modify (mu_locker_t lck, mu_locker_hints_t *hints)
+{
+ if (!lck || !hints)
+ return EINVAL;
+
+ if (hints->flags & MU_LOCKER_FLAG_TYPE)
+ {
+ struct _mu_locker new_lck;
+ int type;
+
+ if (hints->type < 0 || hints->type >= MU_LOCKER_NTYPES)
+ return EINVAL;
+
+ if (lck->flags == 0 || hints->type != lck->type)
+ {
+ if (strcmp (lck->file, "/dev/null") == 0)
+ type = MU_LOCKER_TYPE_NULL;
+ else
+ type = hints->type;
+
+ memset (&new_lck, 0, sizeof (new_lck));
+ new_lck.type = type;
+ new_lck.file = lck->file;
+ if (locker_tab[type].init)
+ {
+ int rc = locker_tab[type].init (&new_lck, hints);
+ if (rc)
+ {
+ if (locker_tab[type].destroy)
+ locker_tab[type].destroy (&new_lck);
+ return rc;
+ }
+ }
+
+ if (lck->flags != 0 && locker_tab[lck->type].destroy)
+ locker_tab[lck->type].destroy (lck);
+
+ *lck = new_lck;
+ }
+ }
+
+ if (hints->flags & MU_LOCKER_FLAG_RETRY)
+ {
+ lck->retry_count = hints->retry_count > 0
+ ? hints->retry_count
+ : MU_LOCKER_DEFAULT_RETRY_COUNT;
+ lck->retry_sleep = hints->retry_sleep > 0
+ ? hints->retry_sleep
+ : MU_LOCKER_DEFAULT_RETRY_SLEEP;
+ }
+
+ if (hints->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
+ lck->expire_time = hints->expire_time > 0 ? hints->expire_time
+ : MU_LOCKER_DEFAULT_EXPIRE_TIME;
+
+ lck->flags = hints->flags;
+
+ return 0;
+}
+
+void
+mu_locker_destroy (mu_locker_t *plocker)
+{
+ if (plocker && *plocker)
+ {
+ mu_locker_t lck = *plocker;
+ if (locker_tab[lck->type].destroy)
+ locker_tab[lck->type].destroy (lck);
+ free (lck->file);
+ free (lck);
+ *plocker = NULL;
+ }
+}
+
+int
+mu_locker_lock_mode (mu_locker_t lck, enum mu_locker_mode mode)
+{
+ int rc;
+ unsigned retries = 1;
+
+ if (!lck || lck->type < 0 || lck->type >= MU_LOCKER_NTYPES)
+ return EINVAL;
+
+ if (locker_tab[lck->type].prelock && (rc = locker_tab[lck->type].prelock (lck)))
+ return rc;
+
+ /* Is the lock already applied? */
+ if (lck->refcnt > 0)
+ {
+ lck->refcnt++;
+ if (mode == lck->mode)
+ return 0;
+ }
+
+ lck->mode = mode;
+
+ if (lck->flags & MU_LOCKER_FLAG_RETRY)
+ retries = lck->retry_count;
+
+ if (locker_tab[lck->type].lock)
+ {
+ while (retries--)
+ {
+ rc = locker_tab[lck->type].lock (lck, mode);
+ if (rc == EAGAIN && retries)
+ sleep (lck->retry_sleep);
+ else
+ break;
+ }
+
+ if (rc == EAGAIN)
+ rc = MU_ERR_LOCK_CONFLICT;
+ }
+ else
+ rc = 0;
+
+ if (rc == 0)
+ lck->refcnt++;
+
+ return rc;
+}
+
+int
+mu_locker_lock (mu_locker_t lck)
+{
+ return mu_locker_lock_mode (lck, mu_lck_exc);
+}
+
+int
+mu_locker_unlock (mu_locker_t lck)
+{
+ int rc = 0;
+
+ if (!lck)
+ return MU_ERR_LOCKER_NULL;
+
+ if (lck->refcnt == 0)
+ return MU_ERR_LOCK_NOT_HELD;
+
+ if ((rc = check_file_permissions (lck->file)))
+ return rc;
+
+ if (--lck->refcnt > 0)
+ return 0;
+
+ if (locker_tab[lck->type].unlock)
+ rc = locker_tab[lck->type].unlock (lck);
+ else
+ rc = 0;
+
+ return rc;
+}
+
+int
+mu_locker_remove_lock (mu_locker_t lck)
+{
+ if (!lck)
+ return MU_ERR_LOCKER_NULL;
+
+ /* Force the reference count to 1 to unlock the file. */
+ lck->refcnt = 1;
+ return mu_locker_unlock (lck);
+}
+
+int
+mu_locker_touchlock (mu_locker_t lck)
{
- return external_locker (locker, 1);
+ if (!lck)
+ return MU_ERR_LOCKER_NULL;
+
+ if (lck->type != MU_LOCKER_TYPE_DOTLOCK)
+ return 0;
+
+ if (lck->refcnt > 0)
+ return utime (lck->data.dot.dotlock, NULL);
+
+ return MU_ERR_LOCK_NOT_HELD;
+}
+
+int
+mu_locker_get_hints (mu_locker_t lck, mu_locker_hints_t *hints)
+{
+ if (!lck || !hints)
+ return EINVAL;
+
+ if (hints->flags & MU_LOCKER_FLAG_TYPE)
+ hints->type = lck->type;
+
+ hints->flags &= ~(lck->flags & hints->flags);
+
+ if (hints->flags & MU_LOCKER_FLAG_RETRY)
+ {
+ hints->retry_count = lck->retry_count;
+ hints->retry_sleep = lck->retry_sleep;
+ }
+ if (hints->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
+ hints->expire_time = lck->expire_time;
+ if (hints->flags & MU_LOCKER_FLAG_EXT_LOCKER)
+ {
+ if (lck->type == MU_LOCKER_TYPE_EXTERNAL)
+ {
+ if ((hints->ext_locker = strdup (lck->data.external.name)) == NULL)
+ return errno;
+ }
+ else
+ hints->ext_locker = NULL;
+ }
+
+ return 0;
+}
+
+/* Deprecated interfaces */
+
+int
+mu_locker_create (mu_locker_t *lck, const char *filename, int flags)
+{
+ mu_locker_hints_t hints, *hp;
+
+ if (flags == 0)
+ hp = NULL;
+ else
+ {
+ hints.type = MU_LOCKER_FLAG_TO_TYPE(flags);
+ hints.flags = flags & MU_LOCKER_OPTION_MASK;
+ hp = &hints;
+ }
+ return mu_locker_create_ext (lck, filename, hp);
+}
+
+int
+mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode)
+{
+ int type = MU_LOCKER_FLAG_TO_TYPE (flags);
+ flags &= MU_LOCKER_OPTION_MASK;
+ switch (mode)
+ {
+ case mu_locker_assign:
+ mu_locker_defaults.flags = flags;
+ mu_locker_defaults.type = type;
+ break;
+
+ case mu_locker_set_bit:
+ mu_locker_defaults.flags |= flags;
+ mu_locker_defaults.type = type;
+ break;
+
+ case mu_locker_clear_bit:
+ mu_locker_defaults.flags &= flags;
+ if (type != MU_LOCKER_TYPE_DOTLOCK)
+ mu_locker_defaults.type = MU_LOCKER_TYPE_DOTLOCK;
+ break;
+ }
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_TYPE;
+ return 0;
+}
+
+void
+mu_locker_set_default_retry_timeout (time_t to)
+{
+ mu_locker_defaults.retry_sleep = to;
+}
+
+void
+mu_locker_set_default_retry_count (size_t n)
+{
+ mu_locker_defaults.retry_count = n;
+}
+
+void
+mu_locker_set_default_expire_timeout (time_t t)
+{
+ mu_locker_defaults.expire_time = t;
+}
+
+int
+mu_locker_set_default_external_program (char const *path)
+{
+ char *p = strdup (path);
+ if (!p)
+ return errno;
+ free (mu_locker_defaults.ext_locker);
+ mu_locker_defaults.ext_locker = p;
+ return 0;
}
static int
-unlock_external (mu_locker_t locker)
+legacy_locker_mod_flags (mu_locker_t lck, int flags,
+ enum mu_locker_set_mode mode)
{
- return external_locker (locker, 0);
+ mu_locker_hints_t hints;
+ int type = MU_LOCKER_FLAG_TO_TYPE (flags);
+
+ flags &= MU_LOCKER_OPTION_MASK;
+
+ switch (mode)
+ {
+ case mu_locker_assign:
+ hints.flags = flags | MU_LOCKER_FLAG_TYPE;
+ hints.type = type;
+ break;
+
+ case mu_locker_set_bit:
+ hints.flags = lck->flags | flags | MU_LOCKER_FLAG_TYPE;
+ hints.type = type;
+ break;
+
+ case mu_locker_clear_bit:
+ hints.flags = lck->flags & ~flags;
+ if (type != MU_LOCKER_TYPE_DOTLOCK)
+ {
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_DOTLOCK;
+ }
+ break;
+ }
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_flags (mu_locker_t lck, int flags)
+{
+ return legacy_locker_mod_flags (lck, flags, mu_locker_assign);
+}
+
+int
+mu_locker_mod_flags (mu_locker_t lck, int flags,
+ enum mu_locker_set_mode mode)
+{
+ return legacy_locker_mod_flags (lck, flags, mode);
+}
+
+int
+mu_locker_set_expire_time (mu_locker_t lck, int v)
+{
+ mu_locker_hints_t hints;
+
+ if (v < 0)
+ return EINVAL;
+ hints.flags = MU_LOCKER_FLAG_EXPIRE_TIME;
+ hints.expire_time = v;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_retries (mu_locker_t lck, int v)
+{
+ mu_locker_hints_t hints;
+
+ if (v < 0)
+ return EINVAL;
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ hints.retry_count = v;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_retry_sleep (mu_locker_t lck, int v)
+{
+ mu_locker_hints_t hints;
+
+ if (v < 0)
+ return EINVAL;
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ hints.retry_sleep = v;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_external (mu_locker_t lck, const char *program)
+{
+ mu_locker_hints_t hints;
+
+ if (lck->type != MU_LOCKER_TYPE_EXTERNAL)
+ return EINVAL;
+
+ if (!program)
+ program = MU_LOCKER_DEFAULT_EXT_LOCKER;
+
+ hints.flags = MU_LOCKER_FLAG_EXT_LOCKER;
+ hints.ext_locker = (char*) program;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_get_flags (mu_locker_t lck, int *flags)
+{
+ mu_locker_hints_t hints;
+ int rc;
+
+ if (!flags)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAGS_ALL;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ *flags = hints.flags | MU_LOCKER_TYPE_TO_FLAG (hints.type);
+ return 0;
+}
+
+int
+mu_locker_get_expire_time (mu_locker_t lck, int *pv)
+{
+ int rc;
+ mu_locker_hints_t hints;
+
+ if (!pv)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAG_EXPIRE_TIME;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ if ((hints.flags & MU_LOCKER_FLAG_EXPIRE_TIME) == 0)
+ *pv = 0;
+ else
+ {
+ if (hints.expire_time > INT_MAX)
+ return ERANGE;
+ *pv = hints.expire_time;
+ }
+ return 0;
+}
+
+int
+mu_locker_get_retries (mu_locker_t lck, int *pv)
+{
+ int rc;
+ mu_locker_hints_t hints;
+
+ if (!pv)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ if ((hints.flags & MU_LOCKER_FLAG_RETRY) == 0)
+ *pv = 0;
+ else
+ {
+ if (hints.expire_time > INT_MAX)
+ return ERANGE;
+ *pv = hints.retry_count;
+ }
+ return 0;
}
+int
+mu_locker_get_retry_sleep (mu_locker_t lck, int *pv)
+{
+ int rc;
+ mu_locker_hints_t hints;
+
+ if (!pv)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ if ((hints.flags & MU_LOCKER_FLAG_RETRY) == 0)
+ *pv = 0;
+ else
+ {
+ if (hints.expire_time > INT_MAX)
+ return ERANGE;
+ *pv = hints.retry_sleep;
+ }
+ return 0;
+}
+
+/* mu_locker_get_external was never implemented */
+
diff --git a/libmailutils/base/spawnvp.c b/libmailutils/base/spawnvp.c
index ec0959a4c..6585a15dd 100644
--- a/libmailutils/base/spawnvp.c
+++ b/libmailutils/base/spawnvp.c
@@ -39,70 +39,89 @@ mu_spawnvp (const char *prog, char *av[], int *stat)
int err = 0;
int progstat;
struct sigaction ignore;
- struct sigaction saveintr;
- struct sigaction savequit;
+
+ struct sigsave {
+ int signo;
+ void *handler;
+ int saved;
+ struct sigaction act;
+ };
+
+ static struct sigsave sigsave[] = {
+ { SIGINT, SIG_IGN, 0 },
+ { SIGQUIT, SIG_IGN, 0 },
+ { SIGCHLD, SIG_DFL, 0 }
+ };
+ static int nsigsave = sizeof (sigsave) / sizeof (sigsave[0]);
+
+ int i;
+
sigset_t chldmask;
sigset_t savemask;
if (!prog || !av)
return EINVAL;
- ignore.sa_handler = SIG_IGN; /* ignore SIGINT and SIGQUIT */
ignore.sa_flags = 0;
sigemptyset (&ignore.sa_mask);
-
- if (sigaction (SIGINT, &ignore, &saveintr) < 0)
- return errno;
- if (sigaction (SIGQUIT, &ignore, &savequit) < 0)
+ for (i = 0; i < nsigsave; i++)
{
- sigaction (SIGINT, &saveintr, NULL);
- return errno;
+ ignore.sa_handler = sigsave[i].handler;
+ if (sigaction (sigsave[i].signo, &ignore, &sigsave[i].act) < 0)
+ {
+ err = errno;
+ break;
+ }
+ sigsave[i].saved = 1;
}
- sigemptyset (&chldmask); /* now block SIGCHLD */
- sigaddset (&chldmask, SIGCHLD);
-
- if (sigprocmask (SIG_BLOCK, &chldmask, &savemask) < 0)
+ if (err == 0)
{
- sigaction (SIGINT, &saveintr, NULL);
- sigaction (SIGQUIT, &savequit, NULL);
- return errno;
+ sigemptyset (&chldmask); /* now block SIGCHLD */
+ sigaddset (&chldmask, SIGCHLD);
+
+ if (sigprocmask (SIG_BLOCK, &chldmask, &savemask) < 0)
+ err = errno;
+ else
+ {
+ pid = fork ();
+
+ if (pid < 0)
+ {
+ err = errno;
+ }
+ else if (pid == 0)
+ { /* child */
+ for (i = 0; i < nsigsave; i++)
+ {
+ sigaction (sigsave[i].signo, &sigsave[i].act, NULL);
+ }
+ sigprocmask (SIG_SETMASK, &savemask, NULL);
+
+ execvp (prog, av);
+ _exit (127); /* exec error */
+ }
+ else
+ { /* parent */
+ while (waitpid (pid, &progstat, 0) < 0)
+ if (errno != EINTR)
+ {
+ err = errno; /* error other than EINTR from waitpid() */
+ break;
+ }
+ if (err == 0 && stat)
+ *stat = progstat;
+ }
+ }
}
-
- pid = fork ();
-
- if (pid < 0)
+ /* restore previous signal actions & reset signal mask */
+ for (i = 0; i < nsigsave; i++)
{
- err = errno;
- }
- else if (pid == 0)
- { /* child */
- /* restore previous signal actions & reset signal mask */
- sigaction (SIGINT, &saveintr, NULL);
- sigaction (SIGQUIT, &savequit, NULL);
- sigprocmask (SIG_SETMASK, &savemask, NULL);
-
- execvp (prog, av);
- _exit (127); /* exec error */
+ if (!sigsave[i].saved)
+ break;
+ if (sigaction (sigsave[i].signo, &sigsave[i].act, NULL) < 0)
+ err = err ? err : errno;
}
- else
- { /* parent */
- while (waitpid (pid, &progstat, 0) < 0)
- if (errno != EINTR)
- {
- err = errno; /* error other than EINTR from waitpid() */
- break;
- }
- if (err == 0 && stat)
- *stat = progstat;
- }
-
- /* restore previous signal actions & reset signal mask */
- /* preserve first error number, but still try and reset the signals */
- if (sigaction (SIGINT, &saveintr, NULL) < 0)
- err = err ? err : errno;
- if (sigaction (SIGQUIT, &savequit, NULL) < 0)
- err = err ? err : errno;
if (sigprocmask (SIG_SETMASK, &savemask, NULL) < 0)
err = err ? err : errno;
diff --git a/libmailutils/cli/stdcapa.c b/libmailutils/cli/stdcapa.c
index b72f024cb..12a088546 100644
--- a/libmailutils/cli/stdcapa.c
+++ b/libmailutils/cli/stdcapa.c
@@ -307,10 +307,9 @@ static struct mu_cfg_param mailbox_cfg[] = {
static int
cb_locker_flags (void *data, mu_config_value_t *val)
{
- int flags = 0;
char const *s;
static struct mu_kwd flag_tab[] = {
- { "external-locker", 'E' },
+ { "type external", 'E' },
{ "retry-count", 'R' },
{ "expire-timeout", 'T' },
{ "pid-check", 'P' },
@@ -334,7 +333,7 @@ cb_locker_flags (void *data, mu_config_value_t *val)
mu_diag_output (MU_DIAG_WARNING,
_("applying legacy flag %c, use %s instead"),
*s, kw);
- flags |= MU_LOCKER_PID;
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_CHECK_PID;
}
else
{
@@ -345,13 +344,11 @@ cb_locker_flags (void *data, mu_config_value_t *val)
*s, kw);
}
}
- if (flags)
- mu_locker_set_default_flags (flags, mu_locker_assign);
return 0;
}
static int
-cb_locker_retry_timeout (void *data, mu_config_value_t *val)
+cb_locker_retry_sleep (void *data, mu_config_value_t *val)
{
int rc;
time_t t;
@@ -366,17 +363,24 @@ cb_locker_retry_timeout (void *data, mu_config_value_t *val)
mu_strerror (rc));
free (errmsg);
}
- else if (t == 0)
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_clear_bit);
else
{
- mu_locker_set_default_retry_timeout (t);
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_set_bit);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = t;
}
return 0;
}
static int
+cb_locker_retry_timeout (void *data, mu_config_value_t *val)
+{
+ mu_diag_output (MU_DIAG_WARNING,
+ _("%s is deprecated, please use %s instead"),
+ "retry-timeout", "retry-sleep");
+ return cb_locker_retry_sleep (data, val);
+}
+
+static int
cb_locker_retry_count (void *data, mu_config_value_t *val)
{
int rc;
@@ -393,11 +397,11 @@ cb_locker_retry_count (void *data, mu_config_value_t *val)
free (errmsg);
}
else if (n == 0)
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_clear_bit);
+ mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_RETRY;
else
{
- mu_locker_set_default_retry_count (n);
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_set_bit);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_count = n;
}
return 0;
}
@@ -419,31 +423,56 @@ cb_locker_expire_timeout (void *data, mu_config_value_t *val)
free (errmsg);
}
else if (t == 0)
- mu_locker_set_default_flags (MU_LOCKER_TIME, mu_locker_clear_bit);
+ mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_EXPIRE_TIME;
else
{
- mu_locker_set_default_expire_timeout (t);
- mu_locker_set_default_flags (MU_LOCKER_TIME, mu_locker_set_bit);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
+ mu_locker_defaults.expire_time = t;
}
return 0;
}
static int
-cb_locker_external (void *data, mu_config_value_t *val)
+cb_locker_type (void *data, mu_config_value_t *val)
{
int t;
-
+ static struct mu_kwd ltab[] = {
+ { "dotlock", MU_LOCKER_TYPE_DOTLOCK },
+ { "default", MU_LOCKER_TYPE_DEFAULT },
+ { "external", MU_LOCKER_TYPE_EXTERNAL },
+ { "kernel", MU_LOCKER_TYPE_KERNEL },
+ { "null", MU_LOCKER_TYPE_NULL },
+ { NULL }
+ };
+
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
- if (mu_str_to_c (val->v.string, mu_c_bool, &t, NULL) == 0 && t == 0)
- {
- mu_locker_set_default_flags (MU_LOCKER_EXTERNAL, mu_locker_clear_bit);
- }
- else
+
+ if (mu_kwd_xlat_name (ltab, val->v.string, &t))
{
- mu_locker_set_default_external_program (val->v.string);
- mu_locker_set_default_flags (MU_LOCKER_EXTERNAL, mu_locker_set_bit);
+ mu_error (_("unrecognized locker type: %s"), val->v.string);
+ return 1;
}
+
+ free (mu_locker_defaults.ext_locker);
+ mu_locker_defaults.ext_locker = NULL;
+
+ mu_locker_defaults.type = t;
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_TYPE;
+
+ return 0;
+}
+
+static int
+cb_locker_external (void *data, mu_config_value_t *val)
+{
+ if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
+ return 1;
+
+ free (mu_locker_defaults.ext_locker);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_EXT_LOCKER;
+ mu_locker_defaults.ext_locker = strdup (val->v.string);
+
return 0;
}
@@ -459,22 +488,26 @@ cb_locker_pid_check (void *data, mu_config_value_t *val)
mu_error ("%s", _("not a boolean"));
return 1;
}
- mu_locker_set_default_flags (MU_LOCKER_PID,
- t ? mu_locker_set_bit : mu_locker_clear_bit);
+ if (t)
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_CHECK_PID;
+ else
+ mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_CHECK_PID;
return 0;
}
static struct mu_cfg_param locking_cfg[] = {
- /* FIXME: Flags are superfluous. */
- { "flags", mu_cfg_callback, NULL, 0, cb_locker_flags,
- N_("Default locker flags (E=external, R=retry, T=time, P=pid)."),
- N_("arg: string") },
- { "retry-timeout", mu_cfg_callback, NULL, 0, cb_locker_retry_timeout,
- N_("Set timeout for acquiring the lock."),
- N_("arg: interval")},
+ { "type", mu_cfg_callback, NULL, 0, cb_locker_type,
+ N_("Set locker type."),
+ N_("type: default | dotlock | external | kernel | null") },
{ "retry-count", mu_cfg_callback, NULL, 0, cb_locker_retry_count,
N_("Set the maximum number of times to retry acquiring the lock."),
N_("arg: integer") },
+ { "retry-sleep", mu_cfg_callback, NULL, 0, cb_locker_retry_sleep,
+ N_("Set the delay between two successive locking attempts."),
+ N_("arg: interval")},
+ { "retry-timeout", mu_cfg_callback, NULL, 0, cb_locker_retry_timeout,
+ N_("Deprecated alias of retry-sleep. Retained for backward compatibility."),
+ N_("arg: interval")},
{ "expire-timeout", mu_cfg_callback, NULL, 0, cb_locker_expire_timeout,
N_("Expire locks older than this amount of time."),
N_("arg: interval")},
@@ -484,6 +517,9 @@ static struct mu_cfg_param locking_cfg[] = {
{ "pid-check", mu_cfg_callback, NULL, 0, cb_locker_pid_check,
N_("Check if PID of the lock owner is active."),
N_("arg: bool") },
+ { "flags", mu_cfg_callback, NULL, 0, cb_locker_flags,
+ N_("Deprecated. Retained for backward compatibility."),
+ N_("arg: string") },
{ NULL, }
};
diff --git a/libmailutils/list/clear.c b/libmailutils/list/clear.c
index b7081dd1d..990779012 100644
--- a/libmailutils/list/clear.c
+++ b/libmailutils/list/clear.c
@@ -38,5 +38,6 @@ mu_list_clear (mu_list_t list)
free (previous);
}
list->head.next = list->head.prev = &list->head;
+ list->count = 0;
mu_monitor_unlock (list->monitor);
}
diff --git a/libmailutils/mailbox/folder.c b/libmailutils/mailbox/folder.c
index 76c296a85..4b3115c8a 100644
--- a/libmailutils/mailbox/folder.c
+++ b/libmailutils/mailbox/folder.c
@@ -392,6 +392,7 @@ mu_folder_enumerate (mu_folder_t folder, const char *name,
status = mu_list_create (&scn.result);
if (status)
return status;
+ mu_list_set_destroy_item (scn.result, mu_list_response_free);
}
status = mu_folder_scan (folder, &scn);
if (status == 0)
diff --git a/libmailutils/mailbox/fsfolder.c b/libmailutils/mailbox/fsfolder.c
index 805aea626..603388f76 100644
--- a/libmailutils/mailbox/fsfolder.c
+++ b/libmailutils/mailbox/fsfolder.c
@@ -146,7 +146,7 @@ _fsfolder_close (mu_folder_t folder MU_ARG_UNUSED)
static int
_fsfolder_rename (mu_folder_t folder, const char *oldpath,
- const char *newpath)
+ const char *newpath)
{
struct _mu_fsfolder *fsfolder = folder->data;
if (oldpath && newpath)
@@ -188,6 +188,7 @@ struct folder_scan_data
mu_folder_t folder;
char *dirname;
size_t dirlen;
+ size_t prefix_len;
size_t errcnt;
};
@@ -376,7 +377,7 @@ list_helper (struct mu_folder_scanner *scn,
}
else
{
- resp->name = fname;
+ resp->name = strdup (fname + data->prefix_len + 1);
resp->depth = depth;
resp->separator = '/';
resp->type = type;
@@ -386,8 +387,7 @@ list_helper (struct mu_folder_scanner *scn,
{
if (scn->enumfun (data->folder, resp, scn->enumdata))
{
- free (resp->name);
- free (resp);
+ mu_list_response_free (resp);
stop = 1;
break;
}
@@ -409,7 +409,7 @@ list_helper (struct mu_folder_scanner *scn,
fname = NULL;
}
else
- free (resp);
+ mu_list_response_free (resp);
}
if ((type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
@@ -458,6 +458,9 @@ _fsfolder_list (mu_folder_t folder, struct mu_folder_scanner *scn)
sdata.folder = folder;
sdata.dirname = get_pathname (fsfolder->dirname, scn->refname);
sdata.dirlen = strlen (sdata.dirname);
+ sdata.prefix_len = strlen (fsfolder->dirname);
+ if (sdata.prefix_len > 0 && fsfolder->dirname[sdata.prefix_len-1] == '/')
+ sdata.prefix_len--;
sdata.errcnt = 0;
list_helper (scn, &sdata, &iroot, sdata.dirname, 0);
free (sdata.dirname);
diff --git a/libmailutils/mailbox/mbx_default.c b/libmailutils/mailbox/mbx_default.c
index 389eb6962..2571d21b7 100644
--- a/libmailutils/mailbox/mbx_default.c
+++ b/libmailutils/mailbox/mbx_default.c
@@ -299,7 +299,7 @@ plus_expand (const char *file, char **buf)
else
{
file++;
-
+
if (folder_dir[0] == '/' || mu_is_proto (folder_dir))
{
char *p = mu_make_file_name (folder_dir, file);
diff --git a/libmailutils/mime/mime.c b/libmailutils/mime/mime.c
index fd611774e..eb5678f05 100644
--- a/libmailutils/mime/mime.c
+++ b/libmailutils/mime/mime.c
@@ -550,7 +550,9 @@ _mime_part_size (mu_mime_t mime, size_t *psize)
return ret;
if (mime->nmtp_parts == 1)
{
- ret = mu_message_size (mime->mtp_parts[0]->msg, &total);
+ mu_body_t part_body;
+ if ((ret = mu_message_get_body (mime->mtp_parts[0]->msg, &part_body)) == 0)
+ ret = mu_body_size (part_body, &total);
}
else
{
@@ -568,7 +570,8 @@ _mime_part_size (mu_mime_t mime, size_t *psize)
}
total += 2; /* ending boundary line */
}
- *psize = total;
+ if (ret == 0)
+ *psize = total;
return ret;
}
@@ -669,69 +672,12 @@ _mime_body_stream_read (mu_stream_t stream, char *buf, size_t buflen,
if ((ret = _mime_set_content_type (mime)) == 0)
{
- do
+ if (mime->nmtp_parts == 1)
{
- size_t part_nbytes = 0;
-
- if (buflen == 0)
- break;
- if (mime->nmtp_parts > 1)
- {
- size_t len;
-
- if (mime->flags & MIME_INSERT_BOUNDARY)
- {
- if ((mime->flags & MIME_ADDING_BOUNDARY) == 0)
- {
- mime->boundary_len = strlen (mime->boundary);
- mime->preamble = 2;
- if (mime->cur_part == mime->nmtp_parts)
- mime->postamble = 2;
- mime->flags |= MIME_ADDING_BOUNDARY;
- }
- while (mime->preamble)
- {
- mime->preamble--;
- ADD_CHAR (buf, '-', mime->cur_offset, buflen,
- total, nbytes);
- }
- len = strlen (mime->boundary) - mime->boundary_len;
- while (mime->boundary_len)
- {
- mime->boundary_len--;
- ADD_CHAR (buf,
- mime->boundary[len++],
- mime->cur_offset, buflen,
- total, nbytes);
- }
- while (mime->postamble)
- {
- mime->postamble--;
- ADD_CHAR (buf, '-', mime->cur_offset, buflen,
- total, nbytes);
- }
- mime->flags &=
- ~(MIME_INSERT_BOUNDARY | MIME_ADDING_BOUNDARY);
- mime->part_offset = 0;
- ADD_CHAR (buf, '\n', mime->cur_offset, buflen,
- total, nbytes);
- }
-
- if (!mime->part_stream)
- {
- if (mime->cur_part >= mime->nmtp_parts)
- {
- *nbytes = total;
- return 0;
- }
- ret = mu_message_get_streamref (mime->mtp_parts[mime->cur_part]->msg,
- &mime->part_stream);
- }
- }
- else if (!mime->part_stream)
+ if (!mime->part_stream)
{
mu_body_t part_body;
-
+
if (mime->cur_part >= mime->nmtp_parts)
{
*nbytes = total;
@@ -741,39 +687,123 @@ _mime_body_stream_read (mu_stream_t stream, char *buf, size_t buflen,
&part_body);
ret = mu_body_get_streamref (part_body, &mime->part_stream);
}
- if (ret)
- break;
- ret = mu_stream_seek (mime->part_stream, mime->part_offset,
- MU_SEEK_SET, NULL);
- if (ret)
- {
- mu_stream_destroy (&mime->part_stream);
- break;
- }
- while (buflen > 0 &&
- (ret = mu_stream_read (mime->part_stream, buf, buflen,
- &part_nbytes)) == 0)
+ if (ret == 0)
{
- if (part_nbytes)
- {
- mime->part_offset += part_nbytes;
- mime->cur_offset += part_nbytes;
- total += part_nbytes;
- buflen -= part_nbytes;
- buf += part_nbytes;
- }
- else
+ size_t part_nbytes = 0;
+ while (buflen > 0 &&
+ (ret = mu_stream_read (mime->part_stream, buf, buflen,
+ &part_nbytes)) == 0)
{
- mu_stream_destroy (&mime->part_stream);
- mime->flags |= MIME_INSERT_BOUNDARY;
- mime->cur_part++;
- ADD_CHAR (buf, '\n', mime->cur_offset, buflen,
- total, nbytes);
- break;
+ if (part_nbytes)
+ {
+ mime->part_offset += part_nbytes;
+ mime->cur_offset += part_nbytes;
+ total += part_nbytes;
+ buflen -= part_nbytes;
+ buf += part_nbytes;
+ }
+ else
+ {
+ mu_stream_destroy (&mime->part_stream);
+ mime->cur_part++;
+ break;
+ }
}
- }
+ }
}
- while (ret == 0 && mime->cur_part <= mime->nmtp_parts);
+ else
+ do
+ {
+ size_t part_nbytes = 0;
+
+ if (buflen == 0)
+ break;
+ else
+ {
+ size_t len;
+
+ if (mime->flags & MIME_INSERT_BOUNDARY)
+ {
+ if ((mime->flags & MIME_ADDING_BOUNDARY) == 0)
+ {
+ mime->boundary_len = strlen (mime->boundary);
+ mime->preamble = 2;
+ if (mime->cur_part == mime->nmtp_parts)
+ mime->postamble = 2;
+ mime->flags |= MIME_ADDING_BOUNDARY;
+ }
+ while (mime->preamble)
+ {
+ mime->preamble--;
+ ADD_CHAR (buf, '-', mime->cur_offset, buflen,
+ total, nbytes);
+ }
+ len = strlen (mime->boundary) - mime->boundary_len;
+ while (mime->boundary_len)
+ {
+ mime->boundary_len--;
+ ADD_CHAR (buf,
+ mime->boundary[len++],
+ mime->cur_offset, buflen,
+ total, nbytes);
+ }
+ while (mime->postamble)
+ {
+ mime->postamble--;
+ ADD_CHAR (buf, '-', mime->cur_offset, buflen,
+ total, nbytes);
+ }
+ mime->flags &=
+ ~(MIME_INSERT_BOUNDARY | MIME_ADDING_BOUNDARY);
+ mime->part_offset = 0;
+ ADD_CHAR (buf, '\n', mime->cur_offset, buflen,
+ total, nbytes);
+ }
+
+ if (!mime->part_stream)
+ {
+ if (mime->cur_part >= mime->nmtp_parts)
+ {
+ *nbytes = total;
+ return 0;
+ }
+ ret = mu_message_get_streamref (mime->mtp_parts[mime->cur_part]->msg,
+ &mime->part_stream);
+ }
+ }
+
+ if (ret)
+ break;
+ ret = mu_stream_seek (mime->part_stream, mime->part_offset,
+ MU_SEEK_SET, NULL);
+ if (ret)
+ {
+ mu_stream_destroy (&mime->part_stream);
+ break;
+ }
+ while (buflen > 0 &&
+ (ret = mu_stream_read (mime->part_stream, buf, buflen,
+ &part_nbytes)) == 0)
+ {
+ if (part_nbytes)
+ {
+ mime->part_offset += part_nbytes;
+ mime->cur_offset += part_nbytes;
+ total += part_nbytes;
+ buflen -= part_nbytes;
+ buf += part_nbytes;
+ }
+ else
+ {
+ mu_stream_destroy (&mime->part_stream);
+ mime->flags |= MIME_INSERT_BOUNDARY;
+ mime->cur_part++;
+ ADD_CHAR (buf, '\n', mime->cur_offset, buflen, total, nbytes);
+ break;
+ }
+ }
+ }
+ while (ret == 0 && mime->cur_part <= mime->nmtp_parts);
}
if (ret)
mu_stream_destroy (&mime->part_stream);
diff --git a/libmailutils/string/mkfilename.c b/libmailutils/string/mkfilename.c
index 653ac7a2a..d24d2f48a 100644
--- a/libmailutils/string/mkfilename.c
+++ b/libmailutils/string/mkfilename.c
@@ -21,30 +21,67 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <mailutils/alloc.h>
#include <mailutils/util.h>
+/*
+ * Given directory name DIR, file name FILE and optional suffix SUF,
+ * return full pathname composed from these three. In the resulting
+ * string, DIR and FILE are separated by '/'. If SUF is supplied, it
+ * is concatenated to the resulting string without additional
+ * separators.
+ *
+ * Corner cases:
+ * all three arguments are NULL or empty
+ * Return NULL and set errno to EINVAL.
+ * dir is NULL
+ * Return FILE and SUF concatenated.
+ * file is NULL, suf is not NULL
+ * Same as mu_make_file_name_suf(dir, suf, NULL);
+ * file is NULL, suf is NULL
+ * Return allocated copy of DIR.
+ */
char *
mu_make_file_name_suf (const char *dir, const char *file, const char *suf)
{
char *tmp;
- size_t dirlen = strlen (dir);
- size_t suflen = suf ? strlen (suf) : 0;
- size_t fillen = strlen (file);
+ size_t dirlen, suflen, fillen;
size_t len;
- while (dirlen > 0 && dir[dirlen-1] == '/')
- dirlen--;
-
- len = dirlen + (dir[0] ? 1 : 0) + fillen + suflen;
- tmp = mu_alloc (len + 1);
+ dirlen = dir ? strlen (dir) : 0;
+ fillen = file ? strlen (file) : 0;
+ suflen = suf ? strlen (suf) : 0;
+
+ len = suflen + fillen;
+ if (dirlen == 0)
+ {
+ if (len == 0)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ }
+ else
+ {
+ if (len) len++; // account for the '/' separator
+ while (dirlen > 0 && dir[dirlen-1] == '/')
+ dirlen--;
+ }
+ len += dirlen;
+
+ tmp = malloc (len + 1);
if (tmp)
{
- memcpy (tmp, dir, dirlen);
- if (dir[0])
- tmp[dirlen++] = '/';
- memcpy (tmp + dirlen, file, fillen);
- if (suf)
+ if (dirlen)
+ {
+ memcpy (tmp, dir, dirlen);
+ if (fillen || suflen)
+ tmp[dirlen++] = '/';
+ }
+ if (fillen)
+ memcpy (tmp + dirlen, file, fillen);
+ if (suflen)
memcpy (tmp + dirlen + fillen, suf, suflen);
tmp[len] = 0;
}
diff --git a/libmailutils/tests/fsfolder.c b/libmailutils/tests/fsfolder.c
index 6a4a8ec39..3f8333406 100644
--- a/libmailutils/tests/fsfolder.c
+++ b/libmailutils/tests/fsfolder.c
@@ -37,7 +37,6 @@
#include "tesh.h"
int sort_option;
-int prefix_len;
static int
compare_response (void const *a, void const *b)
@@ -81,7 +80,7 @@ com_list (int argc, char **argv, mu_assoc_t options, void *env)
{
if (sort_option)
mu_list_sort (list, compare_response);
- mu_list_foreach (list, _print_list_entry, &prefix_len);
+ mu_list_foreach (list, _print_list_entry, NULL);
mu_list_destroy (&list);
}
return 0;
@@ -211,7 +210,7 @@ com_scan (int argc, char **argv, mu_assoc_t options, void *env)
{
if (sort_option)
mu_list_sort (scn.result, compare_response);
- mu_list_foreach (scn.result, _print_list_entry, &prefix_len);
+ mu_list_foreach (scn.result, _print_list_entry, NULL);
mu_list_destroy (&scn.result);
}
mu_list_destroy (&scn.records);
@@ -347,9 +346,6 @@ main (int argc, char **argv)
if (fname[0] != '/')
{
char *cwd = mu_getcwd ();
- prefix_len = strlen (cwd);
- if (cwd[prefix_len-1] != '/')
- prefix_len++;
fname = mu_make_file_name (cwd, fname);
free (cwd);
}
diff --git a/libmailutils/tests/fsfolder00.at b/libmailutils/tests/fsfolder00.at
index e82ef28b2..574e74068 100644
--- a/libmailutils/tests/fsfolder00.at
+++ b/libmailutils/tests/fsfolder00.at
@@ -34,20 +34,20 @@ fsfolder -name=dir -sort dnl
],
[0],
[listing '' '*'
--f / 0 dir/bar
--f / 0 dir/foo
--f / 0 dir/foo.mbox
-d- / 0 dir/subdir
--f / 1 dir/subdir/baz.mbox
--f / 1 dir/subdir/file
+-f / 0 bar
+-f / 0 foo
+-f / 0 foo.mbox
+d- / 0 subdir
+-f / 1 subdir/baz.mbox
+-f / 1 subdir/file
listing 'subdir' '*'
--f / 0 dir/subdir/baz.mbox
--f / 0 dir/subdir/file
+-f / 0 subdir/baz.mbox
+-f / 0 subdir/file
listing '' '*.mbox'
--f / 0 dir/foo.mbox
--f / 1 dir/subdir/baz.mbox
+-f / 0 foo.mbox
+-f / 1 subdir/baz.mbox
listing 'subdir' '*.mbox'
--f / 0 dir/subdir/baz.mbox
+-f / 0 subdir/baz.mbox
])
AT_CLEANUP
diff --git a/libmailutils/tests/fsfolder02.at b/libmailutils/tests/fsfolder02.at
index f8d3fdfb9..562e7e3e2 100644
--- a/libmailutils/tests/fsfolder02.at
+++ b/libmailutils/tests/fsfolder02.at
@@ -29,7 +29,7 @@ fsfolder -name=dir rename bar baz
[renaming foo to bar
rename successful
listing '' '*'
--f / 0 dir/bar
+-f / 0 bar
renaming bar to baz
],
[fsfolder: mu_folder_rename(bar) failed: File exists
diff --git a/libmailutils/tests/fsfolder03.at b/libmailutils/tests/fsfolder03.at
index 0bee92adb..0b9fd9578 100644
--- a/libmailutils/tests/fsfolder03.at
+++ b/libmailutils/tests/fsfolder03.at
@@ -32,13 +32,13 @@ fsfolder -name=dir -sort scan -maxdepth=2
],
[0],
[maxdepth=1
-d- / 0 dir/a
-d- / 0 dir/b
+d- / 0 a
+d- / 0 b
maxdepth=2
-d- / 0 dir/a
-d- / 0 dir/b
-d- / 1 dir/a/b
-d- / 1 dir/b/b
-d- / 1 dir/b/c
+d- / 0 a
+d- / 0 b
+d- / 1 a/b
+d- / 1 b/b
+d- / 1 b/c
])
AT_CLEANUP
diff --git a/libmailutils/tests/fsfolder04.at b/libmailutils/tests/fsfolder04.at
index 5a6929958..4e08e7db9 100644
--- a/libmailutils/tests/fsfolder04.at
+++ b/libmailutils/tests/fsfolder04.at
@@ -29,13 +29,13 @@ fsfolder -name=dir -sort scan -type=reg
],
[0],
[type=any
--f / 0 dir/1
-d- / 0 dir/a
-d- / 0 dir/b
--f / 1 dir/a/2
+-f / 0 1
+d- / 0 a
+d- / 0 b
+-f / 1 a/2
type=reg
--f / 0 dir/1
--f / 1 dir/a/2
+-f / 0 1
+-f / 1 a/2
])
AT_CLEANUP
diff --git a/libmailutils/tests/lck.c b/libmailutils/tests/lck.c
index fe7ac9213..e1cce1da2 100644
--- a/libmailutils/tests/lck.c
+++ b/libmailutils/tests/lck.c
@@ -3,11 +3,11 @@
lck - mailutils locking test
SYNOPSIS
- lck [-akpu?] [-e COMMAND] [-H SECONDS] [-r N] [-t SECONDS] [-x SECONDS]
+ lck [-akpuv?] [-eCOMMAND] [-f SECONDS] [-H SECONDS] [-r N] [-t SECONDS]
[--abandon] [--delay=SECONDS] [--expire=SECONDS]
- [--external=COMMAND] [--help] [--hold=SECONDS] [--kernel]
+ [--external[=COMMAND]] [--help] [--hold=SECONDS] [--kernel]
[--pid-check] [--retry=N] [--show-config-options] [--unlock]
- [--usage] FILE
+ [--usage] [--verbose] FILE
DESCRIPTION
Tests the mailutils locking mechanism. Unless --hold (-H) or --abandon
@@ -24,13 +24,21 @@
The master waits for child to successfully lock the file and attempts to
obtain the lock. If successful, it exits with the 0 status. On errors,
- the termination status is 0.
+ the termination status is:
+
+ 1 An error other than described below occurred. This includes
+ usage errors.
+ 2 Unlock requested, but file is not locked.
+ 3 Lock requested, but file is already locked by another program.
+ 4 Insufficient permissions.
OPTIONS
Locking type (default: dotlock):
- -e, --external=COMMAND
- Use the external locker command.
+ -e, --external[=COMMAND]
+ Use the external locker command. If COMMAND is omitted,
+ the compiled-in default will be used (see
+ MU_LOCKER_DEFAULT_EXT_LOCKER in mailutils/locker.h.
-k, --kernel
Use kernel locking (fnctl).
@@ -46,7 +54,7 @@
-t, --delay=SECONDS
Delay between two successive locking attempts.
- -x, --expire=SECONDS
+ -f, --expire=SECONDS
Expire the lock after that many seconds.
Child operation modifiers
@@ -62,6 +70,11 @@
-u, --unlock
Release the existing lock.
+ -v, --verbose
+ If the lock (or unlock) operation fails, print the error
+ message on the stderr in addition to exiting with the error
+ status.
+
Informational options
--show-config-options
@@ -99,15 +112,12 @@
#include <signal.h>
#include <mailutils/mailutils.h>
-static int flags = 0;
+static mu_locker_hints_t hints = { .flags = 0 };
static int unlock;
-static unsigned retry_count;
-static unsigned retry_sleep = 0;
static int pid_check;
-static unsigned expire;
-static char const *extlocker;
static unsigned hold_time;
static int abandon_lock;
+static int verbose;
static void
cli_type (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
@@ -115,12 +125,18 @@ cli_type (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
switch (opt->opt_short)
{
case 'k':
- flags = MU_LOCKER_OPTIONS (flags) | MU_LOCKER_KERNEL;
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_KERNEL;
break;
case 'e':
- flags = MU_LOCKER_OPTIONS (flags) | MU_LOCKER_EXTERNAL;
- extlocker = arg;
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_EXTERNAL;
+ if (arg)
+ {
+ hints.flags |= MU_LOCKER_FLAG_EXT_LOCKER;
+ hints.ext_locker = strdup (arg);
+ }
break;
default:
@@ -133,22 +149,22 @@ struct mu_option options[] = {
{ "kernel", 'k', NULL, MU_OPTION_DEFAULT,
"use kernel locking", mu_c_void, NULL, cli_type },
- { "external", 'e', "COMMAND", MU_OPTION_DEFAULT,
+ { "external", 'e', "COMMAND", MU_OPTION_ARG_OPTIONAL,
"use external locker command", mu_c_void, NULL, cli_type },
MU_OPTION_GROUP ("Locking parameters"),
{ "retry", 'r', "N", MU_OPTION_DEFAULT,
"retry the lock N times",
- mu_c_uint, &retry_count },
+ mu_c_uint, &hints.retry_count },
{ "delay", 't', "SECONDS", MU_OPTION_DEFAULT,
"delay between two successive locking attempts (in seconds)",
- mu_c_uint, &retry_sleep },
+ mu_c_uint, &hints.retry_sleep },
{ "pid-check", 'p', NULL, MU_OPTION_DEFAULT,
"check if the PID of lock owner is still active",
mu_c_bool, &pid_check },
- { "expire", 'x', "SECONDS", MU_OPTION_DEFAULT,
+ { "expire", 'f', "SECONDS", MU_OPTION_DEFAULT,
"expire the lock after that many seconds",
- mu_c_uint, &expire },
+ mu_c_uint, &hints.expire_time },
MU_OPTION_GROUP ("Child operation modifiers"),
{ "hold", 'H', "SECONDS", MU_OPTION_DEFAULT,
@@ -161,6 +177,9 @@ struct mu_option options[] = {
MU_OPTION_GROUP ("Operation modifiers"),
{ "unlock", 'u', NULL, MU_OPTION_DEFAULT,
"unlock", mu_c_bool, &unlock },
+
+ { "verbose", 'v', NULL, MU_OPTION_DEFAULT,
+ "verbosely list errors", mu_c_bool, &verbose },
MU_OPTION_END
};
@@ -184,6 +203,29 @@ sighan (int sig)
}
}
+static void
+errcheck (int rc)
+{
+ switch (rc)
+ {
+ case 0:
+ break;
+
+ case MU_ERR_LOCK_CONFLICT:
+ exit (MU_DL_EX_EXIST);
+
+ case MU_ERR_LOCK_NOT_HELD:
+ exit (MU_DL_EX_NEXIST);
+
+ case EPERM:
+ case EACCES:
+ exit (MU_DL_EX_PERM);
+
+ default:
+ exit (MU_DL_EX_ERROR);
+ }
+}
+
int
main (int argc, char **argv)
{
@@ -202,26 +244,18 @@ main (int argc, char **argv)
if (argc != 1)
{
mu_error ("bad arguments; try %s --help for more info", mu_program_name);
- return 1;
+ return MU_DL_EX_ERROR;
}
file = argv[0];
- if (expire)
- flags |= MU_LOCKER_TIME;
- if (retry_count || retry_sleep)
- flags |= MU_LOCKER_RETRY;
+ if (hints.expire_time)
+ hints.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
+ if (hints.retry_count || hints.retry_sleep)
+ hints.flags |= MU_LOCKER_FLAG_RETRY;
if (pid_check)
- flags |= MU_LOCKER_PID;
+ hints.flags |= MU_LOCKER_FLAG_CHECK_PID;
- MU_ASSERT (mu_locker_create (&lck, file, flags));
- if (expire)
- mu_locker_set_expire_time (lck, expire);
- if (retry_count)
- mu_locker_set_retries (lck, retry_count);
- if (retry_sleep)
- mu_locker_set_retry_sleep (lck, retry_sleep);
- if (extlocker)
- mu_locker_set_external (lck, extlocker);
+ MU_ASSERT (mu_locker_create_ext (&lck, file, hints.flags > 0 ? &hints : NULL));
if (hold_time || abandon_lock)
{
@@ -232,14 +266,14 @@ main (int argc, char **argv)
if (pipe (p))
{
mu_diag_funcall (MU_DIAG_CRIT, "pipe", NULL, errno);
- return 1;
+ return MU_DL_EX_ERROR;
}
child_pid = fork ();
if (child_pid == -1)
{
mu_diag_funcall (MU_DIAG_CRIT, "fork", NULL, errno);
- return 1;
+ return MU_DL_EX_ERROR;
}
if (child_pid == 0)
@@ -253,8 +287,7 @@ main (int argc, char **argv)
rc = mu_locker_lock (lck);
fprintf (fp, "L%d\n", rc);
fclose (fp);
- if (rc)
- abort ();
+ errcheck (rc);
if (hold_time)
sleep (hold_time);
if (abandon_lock)
@@ -295,11 +328,13 @@ main (int argc, char **argv)
rc = mu_locker_remove_lock (lck);
else
rc = mu_locker_lock (lck);
- if (rc)
+ if (rc && verbose)
mu_diag_funcall (MU_DIAG_ERROR,
unlock ? "mu_locker_remove_lock" : "mu_locker_lock",
NULL, rc);
+ mu_locker_destroy (&lck);
+
if (child_pid > 0)
{
if (waitpid (child_pid, &child_status, WNOHANG) != child_pid)
@@ -317,19 +352,20 @@ main (int argc, char **argv)
if (status != 0)
{
mu_error ("child terminated with status %d", status);
- return 1;
+ return MU_DL_EX_ERROR;
}
}
else if (WIFSIGNALED (child_status))
{
mu_error ("child terminated on signal %d", WTERMSIG (child_status));
- return 1;
+ return MU_DL_EX_ERROR;
}
else
{
mu_error ("child terminated with unhandled status %d", child_status);
- return 1;
+ return MU_DL_EX_ERROR;
}
-
- return rc != 0;
+
+ errcheck (rc);
+ return 0;
}
diff --git a/libmailutils/tests/lock.at b/libmailutils/tests/lock.at
index 0886390b9..75fef24dd 100644
--- a/libmailutils/tests/lock.at
+++ b/libmailutils/tests/lock.at
@@ -33,15 +33,11 @@ LCK_TEST([retries], [--hold=4 --retry=10 --delay=1])
# Child holds lock for 4 seconds, master does two retries with one
# second interval and finishes before being able to acquire it.
LCK_TEST([conflict with previous locker], [--hold=4 --retry=2 --delay=1],
- [1],[],
- [lck: mu_locker_lock() failed: Conflict with previous locker
-])
+ [3])
# Child abandons the lock; master is not able to acquire it.
LCK_TEST([abandoned lock], [--abandon --retry=4 --delay=1],
- [1],[],
- [lck: mu_locker_lock() failed: Conflict with previous locker
-])
+ [3])
# Child abandons the lock; master asserts that its pid is not active and
# acquires it.
@@ -56,4 +52,88 @@ LCK_TEST([lock expiration],
# Default settings correspond to --retry=10 --delay=1 --expire=600
LCK_TEST([default settings], [--hold=2])
+AT_SETUP([external locker])
+AT_KEYWORDS([lock])
+
+AT_DATA([extlocker],
+[#!/bin/sh
+echo "$@" > extlocker.args
+])
+CWD=$(pwd)
+
+m4_pushdef([COND_SKIP],[test -f .skiptest && AT_SKIP_TEST])
+
+AT_CHECK([
+if chmod +x extlocker; then :; else touch .skiptest; fi >/dev/null 2>&1
+COND_SKIP
+],
+[ignore])
+
+AT_CHECK([COND_SKIP
+lck -e$CWD/extlocker file
+cat extlocker.args
+],
+[0],
+[file
+])
+
+AT_CHECK([COND_SKIP
+lck -e$CWD/extlocker --retry=4 --expire=35 file
+cat extlocker.args
+],
+[0],
+[-f35 -r4 file
+])
+
+AT_CHECK([COND_SKIP
+touch file
+lck -elck file
+])
+
+AT_CHECK([COND_SKIP
+lck -elck file
+],
+[3])
+
+AT_CHECK([COND_SKIP
+lck -elck -u file
+],
+[0])
+
+AT_CHECK([COND_SKIP
+lck -elck -u file
+],
+[2])
+
+# Child holds lock for 4 seconds, master acquires it when child has
+# released it.
+AT_CHECK([COND_SKIP
+lck -elck --hold=4 --retry=10 --delay=1 file
+])
+
+AT_CHECK([COND_SKIP
+lck -elck -u file
+])
+
+AT_CHECK([COND_SKIP
+# Child holds lock for 4 seconds, master does two retries with one
+# second interval and finishes before being able to acquire it.
+lck -elck --hold=4 --retry=2 --delay=1 file
+],
+[3])
+
+AT_CHECK([COND_SKIP
+# Master is not able to acquire abandoned lock.
+lck -elck --retry=4 --delay=1 file
+],
+[3])
+
+AT_CHECK([COND_SKIP
+# Master waits until lock has expired and acquires it.
+lck -elck --abandon --expire=3 --retry=10 --delay=1 file
+])
+
+m4_popdef([COND_SKIP])
+AT_CLEANUP
+
m4_popdef([LCK_TEST])
diff --git a/libmu_dbm/berkeley.c b/libmu_dbm/berkeley.c
index d14082f59..7651980fc 100644
--- a/libmu_dbm/berkeley.c
+++ b/libmu_dbm/berkeley.c
@@ -69,8 +69,9 @@ static int
do_bdb_open (mu_dbm_file_t mdb, int flags, int mode)
{
struct bdb_file *bdb_file = mdb->db_descr;
- int f, rc, locker_flags;
+ int f, rc;
enum mu_locker_mode locker_mode;
+ mu_locker_hints_t hints = { .flags = MU_LOCKER_FLAG_RETRY };
int tfd = -1;
switch (flags)
@@ -96,12 +97,10 @@ do_bdb_open (mu_dbm_file_t mdb, int flags, int mode)
#ifdef DB_FCNTL_LOCKING
f |= DB_FCNTL_LOCKING;
- locker_flags = MU_LOCKER_KERNEL;
-#else
- locker_flags = 0;
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_KERNEL;
#endif
- rc = mu_locker_create (&bdb_file->locker, mdb->db_name,
- locker_flags|MU_LOCKER_RETRY);
+ rc = mu_locker_create_ext (&bdb_file->locker, mdb->db_name, &hints);
if (rc)
return rc;
diff --git a/libmu_sieve/extensions/vacation.c b/libmu_sieve/extensions/vacation.c
index 91887e258..aaf71d734 100644
--- a/libmu_sieve/extensions/vacation.c
+++ b/libmu_sieve/extensions/vacation.c
@@ -365,7 +365,7 @@ check_db (mu_sieve_machine_t mach, char *from)
mu_sieve_abort (mach);
}
- rc = mu_locker_create (&locker, file, 0);
+ rc = mu_locker_create_ext (&locker, file, NULL);
if (rc)
{
mu_sieve_error (mach, _("%lu: cannot lock %s: %s"),
diff --git a/libproto/dotmail/dotmail.c b/libproto/dotmail/dotmail.c
index 533032032..56ae0a9f6 100644
--- a/libproto/dotmail/dotmail.c
+++ b/libproto/dotmail/dotmail.c
@@ -119,11 +119,11 @@ dotmail_open (mu_mailbox_t mailbox, int flags)
if (mailbox->locker == NULL
&& (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT)))
{
- rc = mu_locker_create (&mailbox->locker, dmp->name, 0);
+ rc = mu_locker_create_ext (&mailbox->locker, dmp->name, NULL);
if (rc)
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("%s:%s (%s): %s",
- __func__, "mu_locker_create", dmp->name,
+ __func__, "mu_locker_create_ext", dmp->name,
mu_strerror (rc)));
}
diff --git a/libproto/imap/folder.c b/libproto/imap/folder.c
index 94e289c0d..d99b5bc65 100644
--- a/libproto/imap/folder.c
+++ b/libproto/imap/folder.c
@@ -261,14 +261,93 @@ _enumerate_helper (void *item, void *data)
return clos->fun (clos->folder, rp, clos->data);
}
+static int
+_mu_imap_folder_separator (mu_folder_t folder, int *sep)
+{
+ mu_imap_t imap = folder->data;
+
+ if (imap->separator == 0)
+ {
+ struct mu_list_response *resp;
+ char const *path;
+ int rc;
+ mu_list_t list;
+
+ if (mu_url_sget_path (folder->url, &path) || strcmp (path, "INBOX") == 0)
+ path = "";
+
+ rc = mu_imap_list_new (imap, path, "", &list);
+ if (rc)
+ return rc;
+ if (mu_list_head (list, (void**)&resp))
+ return ENOENT; /* FIXME: Better return code? */
+ imap->separator = resp->separator;
+ imap->prefix_len = strlen (path) + 1;
+ mu_list_destroy (&list);
+ }
+
+ if (sep)
+ *sep = imap->separator;
+
+ return 0;
+}
+
+static int
+_mu_imap_folder_pathname (mu_folder_t folder, char const *name, char **retname)
+{
+ char const *folder_path;
+ char *pathname;
+
+ if (mu_url_sget_path (folder->url, &folder_path) ||
+ strcmp (folder_path, "INBOX") == 0)
+ {
+ pathname = strdup (name);
+ }
+ else
+ {
+ int sep;
+ size_t len;
+ int rc;
+
+ rc = _mu_imap_folder_separator (folder, &sep);
+ if (rc)
+ return rc;
+
+ len = strlen (folder_path) + (name ? strlen (name) : 0) + 2;
+ pathname = malloc (len);
+ if (pathname)
+ {
+ char *p = mu_stpcpy (pathname, folder_path);
+ *p++ = sep;
+ if (name)
+ mu_stpcpy (p, name);
+ }
+ }
+
+ if (!pathname)
+ return errno;
+
+ *retname = pathname;
+
+ return 0;
+}
+
/* NOTE: scn->records is ignored */
static int
_mu_imap_folder_list (mu_folder_t folder, struct mu_folder_scanner *scn)
{
mu_imap_t imap = folder->data;
mu_list_t list;
- int rc = mu_imap_list_new (imap, scn->refname, scn->pattern, &list);
+ int rc;
+ char *refname;
+
+ rc = _mu_imap_folder_pathname (folder, scn->refname, &refname);
+ if (rc)
+ return rc;
+ rc = mu_imap_list_new (imap, refname, scn->pattern, &list);
+ free (refname);
+
if (rc)
return rc;
@@ -322,7 +401,15 @@ _mu_imap_folder_lsub (mu_folder_t folder, const char *ref, const char *name,
mu_list_t flist)
{
mu_imap_t imap = folder->data;
- return mu_imap_lsub (imap, ref, name, flist);
+ char *refpath;
+ int rc;
+
+ if ((rc = _mu_imap_folder_pathname (folder, ref, &refpath)) == 0)
+ {
+ rc = mu_imap_lsub (imap, refpath, name, flist);
+ free (refpath);
+ }
+ return rc;
}
/* Subscribe to the named mailbox. */
@@ -330,14 +417,30 @@ static int
_mu_imap_folder_subscribe (mu_folder_t folder, const char *name)
{
mu_imap_t imap = folder->data;
- return mu_imap_subscribe (imap, name);
+ char *path;
+ int rc;
+
+ if ((rc = _mu_imap_folder_pathname (folder, name, &path)) == 0)
+ {
+ rc = mu_imap_subscribe (imap, path);
+ free (path);
+ }
+ return rc;
}
/* Unsubscribe from the mailbox. */
static int
_mu_imap_folder_unsubscribe (mu_folder_t folder, const char *name)
{
mu_imap_t imap = folder->data;
- return mu_imap_unsubscribe (imap, name);
+ char *path;
+ int rc;
+
+ if ((rc = _mu_imap_folder_pathname (folder, name, &path)) == 0)
+ {
+ rc = mu_imap_unsubscribe (imap, path);
+ free (path);
+ }
+ return rc;
}
/* Remove a mailbox. */
@@ -345,16 +448,36 @@ static int
_mu_imap_folder_delete (mu_folder_t folder, const char *name)
{
mu_imap_t imap = folder->data;
- return mu_imap_delete (imap, name);
+ char *path;
+ int rc;
+
+ if ((rc = _mu_imap_folder_pathname (folder, name, &path)) == 0)
+ {
+ rc = mu_imap_delete (imap, path);
+ free (path);
+ }
+ return rc;
}
-/* Rename OLDPATH to NEWPATH */
+/* Rename OLDNAME to NEWNAME */
static int
-_mu_imap_folder_rename (mu_folder_t folder, const char *oldpath,
- const char *newpath)
+_mu_imap_folder_rename (mu_folder_t folder, const char *oldname,
+ const char *newname)
{
mu_imap_t imap = folder->data;
- return mu_imap_rename (imap, oldpath, newpath);
+ char *oldpath, *newpath;
+ int rc;
+
+ if ((rc = _mu_imap_folder_pathname (folder, oldname, &oldpath)) == 0)
+ {
+ if ((rc = _mu_imap_folder_pathname (folder, newname, &newpath)) == 0)
+ {
+ rc = mu_imap_rename (imap, oldpath, newpath);
+ free (newpath);
+ }
+ free (oldpath);
+ }
+ return rc;
}
typedef int (*auth_method_t) (mu_authority_t);
diff --git a/libproto/imap/genlist.c b/libproto/imap/genlist.c
index 5d9ba1be3..cff337b6f 100644
--- a/libproto/imap/genlist.c
+++ b/libproto/imap/genlist.c
@@ -78,7 +78,8 @@ list_untagged_handler (mu_imap_t imap, mu_list_t resp, void *data)
_mu_imap_list_nth_element_is_string (resp, 0, clos->command))
{
struct mu_list_response *rp;
-
+ char const *name;
+
rp = calloc (1, sizeof (*rp));
if (!rp)
{
@@ -95,7 +96,31 @@ list_untagged_handler (mu_imap_t imap, mu_list_t resp, void *data)
elt = _mu_imap_list_at (resp, 3);
if (!(elt && elt->type == imap_eltype_string))
return;
- rp->name = strdup (elt->v.string);
+
+ name = elt->v.string;
+ /*
+ * Skip first imap->prefix_len characters of the returned name. This
+ * is the length of the path part in the folder URL. Note, however,
+ * that there is a case when an IMAP server can return a name shorter
+ * than that: namely, when given an empty mailbox (wref) name. As per
+ * RFC 3501:
+ *
+ * An empty ("" string) mailbox name argument is a special request to
+ * return the hierarchy delimiter and the root name of the name given
+ * in the reference.
+ *
+ * The following conditional takes this into account. It is based on
+ * a reasonable assumption that the root name is always shorter than
+ * the reference name.
+ *
+ * FIXME: Perhaps it would be safer to compare with the pathname prefix
+ * instead. To do so, mu_imap_t should include the prefix (or a pointer
+ * to mu_folder_t).
+ */
+ if (strlen (name) > imap->prefix_len)
+ name += imap->prefix_len;
+
+ rp->name = strdup (name);
if (!rp->name)
{
free (rp);
diff --git a/libproto/imap/tests/imapfolder.c b/libproto/imap/tests/imapfolder.c
index f01ef0513..a8c33d6b7 100644
--- a/libproto/imap/tests/imapfolder.c
+++ b/libproto/imap/tests/imapfolder.c
@@ -33,7 +33,7 @@ static int
_print_list_entry (void *item, void *data)
{
struct mu_list_response *resp = item;
- mu_printf ("%c%c %c %4d %s\n",
+ mu_printf ("%c%c %c %4d \"%s\"\n",
(resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY) ? 'd' : '-',
(resp->type & MU_FOLDER_ATTRIBUTE_FILE) ? 'f' : '-',
resp->separator ? resp->separator : ' ',
@@ -42,18 +42,35 @@ _print_list_entry (void *item, void *data)
return 0;
}
+static int
+_comp_list_entry (const void *a, const void *b)
+{
+ struct mu_list_response const *ra = a;
+ struct mu_list_response const *rb = b;
+ int i = strcmp (ra->name, rb->name);
+
+ if (i != 0)
+ return i;
+ if (ra->depth < rb->depth)
+ return -1;
+ if (ra->depth > rb->depth)
+ return 1;
+ return 0;
+}
+
static void
com_list (mu_folder_t folder, char **argv)
{
int rc;
mu_list_t list;
- mu_printf ("listing %s %s\n", argv[0], argv[1]);
+ mu_printf ("# LIST \"%s\" \"%s\"\n", argv[0], argv[1]);
rc = mu_folder_list (folder, argv[0], argv[1], 0, &list);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_list", argv[0], rc);
else
{
+ mu_list_sort (list, _comp_list_entry);
mu_list_foreach (list, _print_list_entry, NULL);
mu_list_destroy (&list);
}
@@ -71,6 +88,7 @@ com_lsub (mu_folder_t folder, char **argv)
mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_lsub", argv[0], rc);
else
{
+ mu_list_sort (list, _comp_list_entry);
mu_list_foreach (list, _print_list_entry, NULL);
mu_list_destroy (&list);
}
@@ -192,7 +210,7 @@ main (int argc, char **argv)
if (!fname)
{
- mu_error ("URL not specified");
+ usage ();
exit (1);
}
diff --git a/libproto/mbox/mboxrd.c b/libproto/mbox/mboxrd.c
index aec3ddf9b..6357bbbc7 100644
--- a/libproto/mbox/mboxrd.c
+++ b/libproto/mbox/mboxrd.c
@@ -126,11 +126,11 @@ mboxrd_open (mu_mailbox_t mailbox, int flags)
&& mailbox->locker == NULL
&& (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT)))
{
- rc = mu_locker_create (&mailbox->locker, dmp->name, 0);
+ rc = mu_locker_create_ext (&mailbox->locker, dmp->name, NULL);
if (rc)
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("%s:%s (%s): %s",
- __func__, "mu_locker_create", dmp->name,
+ __func__, "mu_locker_create_ext", dmp->name,
mu_strerror (rc)));
}
@@ -790,7 +790,8 @@ mboxrd_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset)
mu_strerror (rc)));
}
mu_stream_unref (stream);
-
+ free (buf);
+
if (force_init_uids)
{
dmp->uidvalidity = (unsigned long) time (NULL);
@@ -1194,11 +1195,12 @@ mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg)
if (rc)
{
+ int rc1;
mu_stream_destroy (&istr);
- rc = mu_stream_truncate (mailbox->stream, size);
- if (rc)
+ rc1 = mu_stream_truncate (mailbox->stream, size);
+ if (rc1)
mu_error (_("cannot truncate stream after failed append: %s"),
- mu_stream_strerror (mailbox->stream, rc));
+ mu_stream_strerror (mailbox->stream, rc1));
return rc;
}
@@ -1996,6 +1998,7 @@ mboxrd_detect (char const *name)
}
}
free (buf);
+ mu_stream_destroy (&str);
}
}
return res;
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/copy.c b/mail/copy.c
index 0d29bc679..129ada5fe 100644
--- a/mail/copy.c
+++ b/mail/copy.c
@@ -97,6 +97,10 @@ append_to_file (char const *filename, msgset_t *msglist, int mark,
mu_stream_t ostr, mstr;
mu_message_t msg;
mu_locker_t locker;
+ mu_locker_hints_t hints = {
+ .flags = MU_LOCKER_FLAG_TYPE | MU_LOCKER_FLAG_RETRY,
+ .type = MU_LOCKER_TYPE_KERNEL
+ };
mu_stream_stat_buffer stat;
status = mu_file_stream_create (&ostr, filename,
@@ -108,8 +112,7 @@ append_to_file (char const *filename, msgset_t *msglist, int mark,
return 1;
}
- status = mu_locker_create (&locker, filename,
- MU_LOCKER_KERNEL|MU_LOCKER_RETRY);
+ status = mu_locker_create_ext (&locker, filename, &hints);
if (status)
{
mu_error (_("Cannot create locker %s: %s"),
@@ -220,13 +223,21 @@ mail_copy0 (int argc, char **argv, int mark)
msgset_t *msglist = NULL;
struct append_stat totals = { 0, 0 };
int rc;
- char *filename;
-
+ char *filename, *storage = NULL;
+
if (mu_isupper (argv[0][0]))
{
if (msgset_parse (argc, argv, MSG_NODELETED, &msglist))
return 1;
- filename = util_outfolder_name (util_get_sender (msgset_msgno (msglist), 1));
+ storage = util_get_sender (msgset_msgno (msglist), 1);
+ filename = util_outfolder_name (storage);
+ if (filename)
+ {
+ free (storage);
+ storage = filename;
+ }
+ else
+ filename = storage;
}
else
{
@@ -235,19 +246,22 @@ mail_copy0 (int argc, char **argv, int mark)
return 1;
}
- if (mail_expand_name (filename, &url))
- return 1;
- filename = (char*) mu_url_to_string (url);
- if (mu_url_is_scheme (url, "file") || mu_url_is_scheme (url, "mbox"))
- rc = append_to_file (filename, msglist, mark, &totals);
- else
- rc = append_to_mailbox (url, msglist, mark, &totals);
+ rc = mail_expand_name (filename, &url);
if (rc == 0)
- mu_printf ("\"%s\" %3lu/%-5lu\n", filename,
- (unsigned long) totals.lines, (unsigned long) totals.size);
- mu_url_destroy (&url);
+ {
+ filename = (char*) mu_url_to_string (url);
+ if (mu_url_is_scheme (url, "file") || mu_url_is_scheme (url, "mbox"))
+ rc = append_to_file (filename, msglist, mark, &totals);
+ else
+ rc = append_to_mailbox (url, msglist, mark, &totals);
+ if (rc == 0)
+ mu_printf ("\"%s\" %3lu/%-5lu\n", filename,
+ (unsigned long) totals.lines, (unsigned long) totals.size);
+ mu_url_destroy (&url);
+ }
+ free (storage);
msgset_free (msglist);
- return 0;
+ return rc;
}
int
diff --git a/mail/exit.c b/mail/exit.c
index c1929c828..5bc5aec29 100644
--- a/mail/exit.c
+++ b/mail/exit.c
@@ -20,5 +20,6 @@ int
mail_exit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
mu_mailbox_close (mbox);
+ mu_mailbox_destroy (&mbox);
exit (0);
}
diff --git a/mail/folders.c b/mail/folders.c
index c85feeb46..217a653b0 100644
--- a/mail/folders.c
+++ b/mail/folders.c
@@ -15,33 +15,103 @@
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include "mail.h"
+#include <mailutils/folder.h>
-/*
- * folders
- */
+static int
+list_response_printer (void *item, void *data)
+{
+ struct mu_list_response *resp = item;
+
+ mu_printf ("%s", resp->name);
+ if (resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
+ mu_printf ("%c", resp->separator);
+ mu_printf ("\n");
+ return 0;
+}
+
+static int
+show_folders (mu_folder_t folder)
+{
+ mu_list_t list;
+
+ if (mu_folder_is_local (folder))
+ {
+ char *lister = getenv ("LISTER");
+ if (lister && *lister)
+ {
+ char const *path;
+ mu_url_t url;
+ int rc;
+
+ mu_folder_get_url (folder, &url);
+ if ((rc = mu_url_sget_path (url, &path)) == 0)
+ {
+ util_do_command("! %s '%s'", getenv ("LISTER"), path);
+ return 0;
+ }
+ else
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_url_sget_path", NULL, rc);
+ /* Retry using standard folder lister */
+ }
+ }
+ }
+
+ if (mu_folder_list (folder, "", "%", 1, &list) == 0)
+ {
+ mu_list_foreach (list, list_response_printer, NULL);
+ mu_list_destroy (&list);
+ }
+
+ return 0;
+}
int
mail_folders (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
- char *path;
-
- if (mailvar_get (&path, mailvar_name_folder, mailvar_type_string, 1))
+ int rc;
+ mu_folder_t folder;
+ mu_url_t url;
+ char *folder_path, *temp_folder_path = NULL;
+
+ if (mailvar_get (&folder_path, mailvar_name_folder, mailvar_type_string, 1))
return 1;
- if (path[0] != '/' && path[0] != '~')
+ if (!mu_is_proto (folder_path) && folder_path[0] != '/' &&
+ folder_path[0] != '~')
{
- char *tmp = mu_alloc (strlen (path) + 3);
+ char *tmp = mu_alloc (strlen (folder_path) + 3);
tmp[0] = '~';
tmp[1] = '/';
- strcpy (tmp + 2, path);
- path = util_fullpath (tmp);
+ strcpy (tmp + 2, folder_path);
+ temp_folder_path = util_fullpath (tmp);
+ folder_path = temp_folder_path;
free (tmp);
}
- else
- path = util_fullpath (path);
- util_do_command("! %s '%s'", getenv ("LISTER"), path);
- free (path);
+ rc = mu_url_create (&url, folder_path);
+ if (rc == 0)
+ {
+ rc = util_get_folder (&folder, url, any_folder);
+ if (rc == 0)
+ {
+ url = NULL; /* Prevent double free: folder steals url */
+ rc = show_folders (folder);
+ mu_folder_destroy (&folder);
+ }
+ else
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_folder",
+ folder_path, rc);
+ }
+ }
+ else
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_url_create", folder_path, rc);
+ }
- return 0;
+ free (temp_folder_path);
+ mu_url_destroy (&url);
+ return rc;
}
+
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..c14af51c5 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:
@@ -398,6 +402,8 @@ static char *default_setup[] = {
"set fromfield",
"set headline=\"%>%a%4m %18f %16d %3L/%-5o %s\"",
"unset folder",
+ "set fullnames",
+ "set outfilename=local",
/* Start in mail reading mode */
"setq mode=read",
@@ -485,7 +491,7 @@ main (int argc, char **argv)
else
util_do_command ("set columns=%d", util_getcols ());
- /* Set the default mailer to sendmail. */
+ /* Set the default mailer to sendmail. FIXME: Minor memory leak. */
mailvar_set (mailvar_name_sendmail,
mu_strdup ("sendmail:" PATH_SENDMAIL), mailvar_type_string,
MOPTF_OVERWRITE);
@@ -581,13 +587,15 @@ 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";
+ return mail_send (argc, argv)
+ ? (mailvar_is_true (mailvar_name_mailx) ? 0 : EXIT_FAILURE)
+ : 0;
}
/* Or acting as a normal reader */
else
diff --git a/mail/mail.h b/mail/mail.h
index 8ca2d8ecd..fec644197 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"
@@ -182,6 +181,7 @@ struct mailvar_variable
#define mailvar_name_flipr "flipr"
#define mailvar_name_folder "folder"
#define mailvar_name_fromfield "fromfield"
+#define mailvar_name_fullnames "fullnames"
#define mailvar_name_gnu_last_command "gnu-last-command"
#define mailvar_name_header "header"
#define mailvar_name_headline "headline"
@@ -199,6 +199,7 @@ struct mailvar_variable
#define mailvar_name_mode "mode"
#define mailvar_name_nullbody "nullbody"
#define mailvar_name_nullbodymsg "nullbodymsg"
+#define mailvar_name_outfilename "outfilename"
#define mailvar_name_outfolder "outfolder"
#define mailvar_name_page "page"
#define mailvar_name_prompt "prompt"
@@ -321,7 +322,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);*/
@@ -421,6 +422,15 @@ msgset_str (const msgset_t *set)
return mu_coord_string (set->crd);
}
+/* Constants for naming outgoing mail files */
+enum
+ {
+ outfilename_local, /* File is named by local part of the recipient email. */
+ outfilename_email, /* File is named by entire email. */
+ outfilename_domain /* File is named by domain part. */
+ };
+extern int outfilename_mode;
+
#define MDHINT_SELECTED_HEADERS 0x1
struct mime_descend_closure
@@ -487,6 +497,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);
@@ -495,6 +506,7 @@ void util_slist_remove (mu_list_t *list, char *value);
void util_slist_destroy (mu_list_t *list);
char *util_slist_to_string (mu_list_t list, const char *delim);
void util_strcat (char **dest, const char *str);
+char *util_outfilename (mu_address_t addr);
char *util_outfolder_name (char *str);
void util_save_outgoing (mu_message_t msg, char *savefile);
int util_error_range (size_t msgno);
@@ -506,7 +518,8 @@ void util_msgset_iterate (msgset_t *msgset,
void util_get_content_type (mu_header_t hdr, char **value, char **args);
void util_get_hdr_value (mu_header_t hdr, const char *name, char **value);
int util_merge_addresses (char **addr_str, const char *value);
-int util_header_expand (mu_header_t *hdr);
+void util_address_expand_aliases (mu_address_t *paddr);
+int util_header_expand_aliases (mu_header_t *hdr);
int util_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *msg);
int util_get_message_part (mu_mailbox_t mbox, msgset_t *msgset,
mu_message_t *ret_msg);
@@ -546,6 +559,15 @@ const char *alias_iterate_next (alias_iterator_t itr);
const char *alias_iterate_first (const char *p, alias_iterator_t *itr);
void alias_iterate_end (alias_iterator_t *itr);
+/* Auxiliary functions for accessing folders */
+enum
+ {
+ any_folder,
+ local_folder
+ };
+
+int util_get_folder (mu_folder_t *pfolder, mu_url_t url, int type);
+
int mail_sender (int argc, char **argv);
int mail_nosender (int argc, char **argv);
mu_address_t get_sender_address (mu_message_t msg);
diff --git a/mail/mailline.c b/mail/mailline.c
index 0b57cbece..c6ecdf1df 100644
--- a/mail/mailline.c
+++ b/mail/mailline.c
@@ -18,7 +18,6 @@
#include <sys/stat.h>
#include <dirent.h>
#include <mailutils/folder.h>
-#include <mailutils/auth.h>
#ifdef WITH_READLINE
static char **ml_command_completion (char *cmd, int start, int end);
@@ -476,7 +475,6 @@ struct filegen
mu_iterator_t itr; /* Iterator over this list */
size_t prefix_len; /* Length of initial prefix */
char repl; /* Character to replace initial prefix with */
- size_t path_len; /* Length of pathname part */
int flags;
};
@@ -487,69 +485,7 @@ filegen_free (struct filegen *fg)
mu_list_destroy (&fg->list);
}
-enum
- {
- any_folder,
- local_folder
- };
-
-#define PREFIX_AUTO ((size_t)-1)
-
-static int
-new_folder (mu_folder_t *pfolder, mu_url_t url, int type)
-{
- mu_folder_t folder;
- int rc;
-
- rc = mu_folder_create_from_record (&folder, url, NULL);
- if (rc)
- {
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_create",
- mu_url_to_string (url), rc);
- return -1;
- }
-
- if (!mu_folder_is_local (folder))
- {
- if (type == local_folder)
- {
- /* TRANSLATORS: The subject of this sentence ("folder") is the
- name of the variable. Don't translate it. */
- mu_error ("%s", _("folder must be set to a local folder"));
- mu_folder_destroy (&folder);
- return -1;
- }
-
- /* Set ticket for a remote folder */
- rc = mu_folder_attach_ticket (folder);
- if (rc)
- {
- mu_authority_t auth = NULL;
-
- if (mu_folder_get_authority (folder, &auth) == 0 && auth)
- {
- mu_ticket_t tct;
- mu_noauth_ticket_create (&tct);
- rc = mu_authority_set_ticket (auth, tct);
- if (rc)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_authority_set_ticket",
- NULL, rc);
- }
- }
- }
-
- rc = mu_folder_open (folder, MU_STREAM_READ);
- if (rc)
- {
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_open",
- mu_url_to_string (url), rc);
- mu_folder_destroy (&folder);
- return -1;
- }
-
- *pfolder = folder;
- return 0;
-}
+#define PREFIX_AUTO 0
static int
folder_match_url (mu_folder_t folder, mu_url_t url)
@@ -583,7 +519,6 @@ filegen_init (struct filegen *fg,
mu_url_t url;
int rc;
int free_folder;
- char const *urlpath;
rc = mu_url_create (&url, folder_path);
if (rc)
@@ -605,7 +540,7 @@ filegen_init (struct filegen *fg,
if (!folder)
{
- if (new_folder (&folder, url, type))
+ if (util_get_folder (&folder, url, type))
return -1;
free_folder = 1;
}
@@ -620,14 +555,7 @@ filegen_init (struct filegen *fg,
strcat (wcard, "%");
pathref[i] = 0;
- mu_url_sget_path (url, &urlpath);
- fg->path_len = strlen (urlpath);
- if (fg->path_len > 0 && urlpath[fg->path_len - 1] != '/')
- fg->path_len++;
- if (prefix_len == PREFIX_AUTO)
- fg->prefix_len = fg->path_len;
- else
- fg->prefix_len = prefix_len;
+ fg->prefix_len = prefix_len;
mu_folder_list (folder, pathref, wcard, 1, &fg->list);
free (wcard);
@@ -648,7 +576,7 @@ filegen_init (struct filegen *fg,
struct mu_list_response *resp;
mu_list_head (fg->list, (void**)&resp);
if ((resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
- && strcmp (resp->name + fg->path_len, text) == 0)
+ && strcmp (resp->name, text) == 0)
ml_set_completion_append_character (resp->separator);
}
}
diff --git a/mail/mailvar.c b/mail/mailvar.c
index 64b49938e..e428d3a4d 100644
--- a/mail/mailvar.c
+++ b/mail/mailvar.c
@@ -15,6 +15,7 @@
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include "mail.h"
+#include "mailutils/kwd.h"
#define MAILVAR_ALIAS 0x0001
#define MAILVAR_RDONLY 0x0002
@@ -52,6 +53,10 @@ static int set_folder (enum mailvar_cmd cmd,
struct mailvar_variable *);
static int set_headline (enum mailvar_cmd,
struct mailvar_variable *);
+static int set_outfilename (enum mailvar_cmd,
+ struct mailvar_variable *);
+static int set_escape (enum mailvar_cmd,
+ struct mailvar_variable *);
struct mailvar_symbol mailvar_tab[] =
{
@@ -85,10 +90,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"
@@ -133,7 +134,8 @@ struct mailvar_symbol mailvar_tab[] =
N_("start interactive mode if the mailbox is empty") },
{ { mailvar_name_escape, },
MAILVAR_TYPEMASK (mailvar_type_string),
- N_("character denoting escapes") },
+ N_("command escape character"),
+ set_escape },
{ { mailvar_name_flipr, },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("swap the meaning of reply and Reply commands") },
@@ -204,8 +206,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") },
@@ -289,6 +295,19 @@ struct mailvar_symbol mailvar_tab[] =
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("always compose MIME messages") },
+ { { mailvar_name_fullnames },
+ MAILVAR_TYPEMASK (mailvar_type_boolean),
+ N_("when replying, preserve personal parts of recipient emails") },
+
+ { { mailvar_name_outfilename },
+ MAILVAR_TYPEMASK (mailvar_type_string),
+ N_("how to create outgoing file name: local, domain, email"),
+ set_outfilename },
+
+ { { mailvar_name_PID },
+ MAILVAR_TYPEMASK (mailvar_type_string) | MAILVAR_RDONLY,
+ N_("PID of this process") },
+
/* These will be implemented later */
{ { mailvar_name_onehop, }, MAILVAR_HIDDEN, NULL },
@@ -296,10 +315,6 @@ struct mailvar_symbol mailvar_tab[] =
MAILVAR_TYPEMASK (mailvar_type_boolean) | MAILVAR_HIDDEN,
N_("suppress the printing of the version when first invoked") },
- { { mailvar_name_PID },
- MAILVAR_TYPEMASK (mailvar_type_string) | MAILVAR_RDONLY,
- N_("PID of this process") },
-
{ { NULL }, }
};
@@ -343,10 +358,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 +459,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);
@@ -726,6 +746,50 @@ set_debug (enum mailvar_cmd cmd, struct mailvar_variable *var)
return 0;
}
+static int
+set_outfilename (enum mailvar_cmd cmd, struct mailvar_variable *var)
+{
+ static struct mu_kwd kwtab[] = {
+ { "local", outfilename_local },
+ { "email", outfilename_email },
+ { "domain", outfilename_domain },
+ { NULL }
+ };
+
+ switch (cmd)
+ {
+ case mailvar_cmd_set:
+ if (mu_kwd_xlat_name (kwtab, var->value.string, &outfilename_mode))
+ {
+ mu_error (_("unsupported value for %s"), var->name);
+ return 1;
+ }
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+set_escape (enum mailvar_cmd cmd, struct mailvar_variable *var)
+{
+ if (cmd == mailvar_cmd_set)
+ {
+ if (var->value.string[0] == 0)
+ {
+ mu_error (_("escape character cannot be empty"));
+ return 1;
+ }
+ else if (var->value.string[1] != 0)
+ {
+ mu_error (_("escape value must be a single character"));
+ return 1;
+ }
+ }
+ return 0;
+}
size_t
_mailvar_symbol_count (int set)
diff --git a/mail/reply.c b/mail/reply.c
index 524204956..6c1b44572 100644
--- a/mail/reply.c
+++ b/mail/reply.c
@@ -38,100 +38,285 @@ 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);
-
- mu_message_get_header (msg, &hdr);
+ 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);
+ }
+}
- compose_header_set (&env, MU_HEADER_TO,
- util_get_sender (msgset_msgno (mspec), 0),
- COMPOSE_SINGLE_LINE);
+static void
+compose_remove_personal (compose_env_t *env, char *hname)
+{
+ char const *value;
- if (*(int*) data) /* reply starts with a lowercase */
+ value = compose_header_get (env, hname, NULL);
+ if (value)
{
- /* Add all recepients of the originate letter */
+ mu_address_t addr = NULL, ap;
+ struct mu_address hint = MU_ADDRESS_HINT_INITIALIZER;
+ char *result;
+
+ mu_address_create_hint (&addr, value, &hint, 0);
+ for (ap = addr; ap; ap = ap->next)
+ {
+ free (ap->printable);
+ ap->printable = NULL;
+ free (ap->comments);
+ ap->comments = NULL;
+ free (ap->personal);
+ ap->personal = NULL;
+ free (ap->route);
+ ap->route = NULL;
+ }
+ mu_address_aget_printable (addr, &result);
+ compose_header_set (env, hname, result, COMPOSE_REPLACE);
+ free (result);
+ mu_address_destroy (&addr);
+ }
+}
- mu_address_t addr = NULL;
- size_t i, count = 0;
+/*
+ * 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;
- if (mu_header_aget_value (hdr, MU_HEADER_TO, &str) == 0)
+ msgset_t *msgset = NULL;
+
+ if (msgset_parse (argc, argv, MSG_NODELETED, &msgset))
+ return 1;
+
+ if (msgset->next)
+ {
+ 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);
+
+ 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);
}
- mu_address_destroy (&addr);
+ if (mu_header_sget_value (hdr, MU_HEADER_CC, &str) == 0)
+ {
+ compose_set_address (&env, MU_HEADER_CC, str);
+ }
+
+ /* 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);
+ if (!mailvar_is_true (mailvar_name_fullnames))
+ {
+ compose_remove_personal (&env, MU_HEADER_TO);
+ compose_remove_personal (&env, MU_HEADER_CC);
+ }
+
+ 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);
+
+ if (!mailvar_is_true (mailvar_name_fullnames))
+ {
+ compose_remove_personal (&env, MU_HEADER_TO);
+ compose_remove_personal (&env, MU_HEADER_CC);
+ }
+
+ 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 1c33c0de5..1bdfe1134 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;
}
@@ -889,78 +890,91 @@ compose_header_set (compose_env_t *env, const char *name,
const char *value, int mode)
{
int status;
- char *old_value;
-
+ char *expansion = NULL;
+
if (!value || value[0] == 0)
return EINVAL;
+ if (is_address_field (name)
+ && mailvar_is_true (mailvar_name_inplacealiases))
+ {
+ struct mu_address hint = MU_ADDRESS_HINT_INITIALIZER;
+ mu_address_t a;
+
+ status = mu_address_create_hint (&a, value, &hint, 0);
+ if (status)
+ {
+ mu_error (_("Cannot parse address `%s': %s"),
+ value, mu_strerror (status));
+ return status;
+ }
+ util_address_expand_aliases (&a);
+ status = mu_address_aget_printable (a, &expansion);
+ mu_address_destroy (&a);
+ if (status)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_address_aget_printable",
+ NULL, status);
+ return status;
+ }
+ value = expansion;
+ }
+
if (!env->header
&& (status = mu_header_create (&env->header, NULL, 0)) != 0)
{
mu_error (_("Cannot create header: %s"), mu_strerror (status));
- return status;
}
-
- switch (mode)
+ else
{
- case COMPOSE_REPLACE:
- if (is_address_field (name)
- && mailvar_is_true (mailvar_name_inplacealiases))
+ switch (mode)
{
- char *exp = alias_expand (value);
- status = mu_header_set_value (env->header, name, exp ? exp : value, 1);
- free (exp);
- }
- else
- status = mu_header_set_value (env->header, name, value, 1);
- break;
+ case COMPOSE_REPLACE:
+ status = mu_header_set_value (env->header, name, value, 1);
+ break;
- case COMPOSE_APPEND:
- if (is_address_field (name)
- && mailvar_is_true (mailvar_name_inplacealiases))
- {
- char *exp = alias_expand (value);
- status = mu_header_append (env->header, name, exp ? exp : value);
- free (exp);
- }
- else
- status = mu_header_append (env->header, name, value);
- break;
+ case COMPOSE_APPEND:
+ status = mu_header_append (env->header, name, value);
+ break;
- case COMPOSE_SINGLE_LINE:
- 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))
- {
- char *exp = alias_expand (value);
- status = util_merge_addresses (&old_value, exp ? exp : value);
- if (status == 0)
- status = mu_header_set_value (env->header, name, old_value, 1);
- free (exp);
- }
- else
- {
- size_t size = strlen (old_value) + strlen (value) + 2;
- char *p = realloc (old_value, size);
- if (!p)
- status = ENOMEM;
- else
- {
- old_value = p;
- strcat (old_value, ",");
- strcat (old_value, value);
- status = mu_header_set_value (env->header, name, old_value,
- 1);
- }
- }
- free (old_value);
+ case COMPOSE_SINGLE_LINE:
+ {
+ char *old_value;
+
+ if (mu_header_aget_value (env->header, name, &old_value) == 0
+ && old_value[0])
+ {
+ if (is_address_field (name))
+ {
+ status = util_merge_addresses (&old_value, value);
+ if (status == 0)
+ status = mu_header_set_value (env->header, name,
+ old_value, 1);
+ }
+ else
+ {
+ size_t size = strlen (old_value) + strlen (value) + 2;
+ char *p = realloc (old_value, size);
+ if (!p)
+ status = ENOMEM;
+ else
+ {
+ old_value = p;
+ strcat (old_value, ",");
+ strcat (old_value, value);
+ status = mu_header_set_value (env->header,
+ name, old_value,
+ 1);
+ }
+ }
+ free (old_value);
+ }
+ else
+ status = mu_header_set_value (env->header, name, value, 1);
+ }
}
- else
- status = compose_header_set (env, name, value, COMPOSE_REPLACE);
}
-
+ free (expansion);
return status;
}
@@ -1130,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
@@ -1151,8 +1180,10 @@ send_message (mu_message_t msg)
if (mailvar_get (&return_address_str, mailvar_name_return_address,
mailvar_type_string, 0) == 0)
{
- status = mu_address_create (&return_address,
- return_address_str);
+ struct mu_address hint = MU_ADDRESS_HINT_INITIALIZER;
+ status = mu_address_create_hint (&return_address,
+ return_address_str,
+ &hint, 0);
if (status)
{
mu_error (_("invalid return address: %s"),
@@ -1189,10 +1220,38 @@ send_message (mu_message_t msg)
mu_error (_("Variable sendmail not set: no mailer"));
status = ENOSYS;
}
+ free (mailer_url);
return status;
}
-/* mail_send0(): shared between mail_send() and mail_reply();
+#define MU_DATETIME_RFC822_LENGTH 31
+
+static void
+message_add_date (mu_message_t msg)
+{
+ mu_header_t hdr;
+ char buf[MU_DATETIME_RFC822_LENGTH+1];
+ struct tm ltm;
+ time_t t;
+ int rc;
+
+ rc = mu_message_get_header (msg, &hdr);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", NULL, rc);
+ return;
+ }
+
+ t = time (NULL);
+ localtime_r (&t, &ltm);
+
+ mu_strftime (buf, sizeof (buf), MU_DATETIME_FORM_RFC822, &ltm);
+ rc = mu_header_set_value (hdr, MU_HEADER_DATE, buf, 1);
+ if (rc)
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_header_set_value", MU_HEADER_DATE, rc);
+}
+
+/* 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,
@@ -1208,7 +1267,7 @@ send_message (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;
@@ -1327,7 +1386,7 @@ mail_send0 (compose_env_t *env, int save_to)
if (mailvar_is_true (mailvar_name_xmailer))
mu_header_set_value (env->header, MU_HEADER_X_MAILER, program_version, 1);
- if (util_header_expand (&env->header) == 0)
+ if (util_header_expand_aliases (&env->header) == 0)
{
mu_message_t msg = NULL;
int status = 0;
@@ -1353,23 +1412,23 @@ 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;
-
- mu_address_create (&addr, rcpt);
- mu_address_aget_email (addr, 1, &savefile);
+ struct mu_address hint = MU_ADDRESS_HINT_INITIALIZER;
+
+ mu_address_create_hint (&addr, rcpt, &hint, 0);
+ savefile = util_outfilename (addr);
mu_address_destroy (&addr);
- if (savefile)
- {
- char *p = strchr (savefile, '@');
- if (p)
- *p = 0;
- }
}
}
util_save_outgoing (msg, savefile);
@@ -1454,8 +1513,18 @@ msg_to_pipe (const char *cmd, mu_message_t msg)
{
mu_stream_t progstream, msgstream;
int status, rc;
-
- status = mu_command_stream_create (&progstream, cmd, MU_STREAM_WRITE);
+ char *argv[4];
+
+ argv[0] = getenv ("SHELL");
+ if (!argv[0])
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = (char*) cmd;
+ argv[3] = NULL;
+ status = mu_prog_stream_create (&progstream,
+ argv[0],
+ 3, argv,
+ 0, NULL, MU_STREAM_WRITE);
if (status)
{
mu_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status));
diff --git a/mail/table.c b/mail/table.c
index 6f08ba725..1038702e5 100644
--- a/mail/table.c
+++ b/mail/table.c
@@ -62,7 +62,7 @@ static const struct mail_command_entry mail_command_table[] = {
mail_file, file_compl },
{ "folders", "folders", "folders", 0,
mail_folders, no_compl },
- { "fo", "followup", "fo[llowup] [msglist]", EF_SEND,
+ { "fo", "followup", "fo[llowup] [message]", EF_SEND,
mail_followup, msglist_compl },
{ "f", "from", "f[rom] [msglist]", 0,
mail_from, msglist_compl },
@@ -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,
@@ -108,9 +110,9 @@ static const struct mail_command_entry mail_command_table[] = {
mail_reply, msglist_compl },
{ "R", "Respond", "R[espond] [msglist]", EF_SEND,
mail_reply, msglist_compl },
- { "r", "reply", "r[eply] [msglist]", EF_SEND,
+ { "r", "reply", "r[eply] [message]", EF_SEND,
mail_reply, msglist_compl },
- { "r", "respond", "r[espond] [msglist]", EF_SEND,
+ { "r", "respond", "r[espond] [message]", EF_SEND,
mail_reply, msglist_compl },
{ "ret", "retain", "ret[ain] [header-field]", 0,
mail_retain, no_compl },
diff --git a/mail/tests/Followup.at b/mail/tests/Followup.at
new file mode 100644
index 000000000..b93d36800
--- /dev/null
+++ b/mail/tests/Followup.at
@@ -0,0 +1,197 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([Followup])
+AT_KEYWORDS([reply])
+
+AT_DATA([inbox],
+[From hare@wonder.land Mon Jul 29 22:00:08 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3301
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:06 +0100
+Date: Mon, 29 Jul 2002 22:00:01 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3301@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Invitation
+
+Have some wine
+
+From alice@wonder.land Mon Jul 29 22:00:09 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3302
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:07 +0100
+Date: Mon, 29 Jul 2002 22:00:02 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3302@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+
+I don't see any wine
+
+From hatter@example.org Mon Jul 29 22:00:10 2002
+Received: (from hatter@example.org)
+ by wonder.land id 3303
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:08 +0100
+Date: Mon, 29 Jul 2002 22:00:03 +0100
+From: Hatter <hatter@example.org>
+Message-Id: <200207292200.3303@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Re: Invitation
+
+There isn't any
+
+From alice@wonder.land Mon Jul 29 22:00:11 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3304
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date: Mon, 29 Jul 2002 22:00:04 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3304@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+
+Then it wasn't very civil of you to offer it
+
+From hare@wonder.land Mon Jul 29 22:00:12 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3305
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:10 +0100
+Date: Mon, 29 Jul 2002 22:00:05 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3305@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Re: Invitation
+
+It wasn't very civil of you to sit down without being invited
+])
+
+AT_CHECK([touch record
+mkdir folder outfolder
+])
+
+AT_DATA([com],
+[set dot indentprefix=">"
+retain To
+Followup 2 1 3
+~m
+.
+x
+])
+
+AT_CHECK([
+unset MAIL_DUMP
+MUT_MAIL_CMD -f ./inbox \
+ -E "set noheader" \
+ -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder" < com
+],
+[0],
+[To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+
+Interpolating: 2
+(continue)
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' \
+ mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 3
+RCPT[[0]]: <alice@wonder.land>
+RCPT[[1]]: <hare@wonder.land>
+RCPT[[2]]: <hatter@example.org>
+LENGTH: 396
+To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:02 +0100
+ <200207292200.3302@wonder.land>
+References: <200207292200.3302@wonder.land>
+
+>To: March Hare <hare@wonder.land>
+>
+>I don't see any wine
+>
+
+])
+
+AT_CHECK([sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' folder/alice
+],
+[0],
+[Envelope
+To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:02 +0100
+ <200207292200.3302@wonder.land>
+References: <200207292200.3302@wonder.land>
+
+>To: March Hare <hare@wonder.land>
+>
+>I don't see any wine
+>
+
+])
+
+#
+#
+#
+AT_CHECK([
+unset MAIL_DUMP
+MUT_MAIL_CMD -f ./inbox \
+ -E "set noheader" \
+ -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder=$(pwd)/outfolder" < com
+],
+[0],
+[To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+
+Interpolating: 2
+(continue)
+])
+
+AT_CHECK([sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' outfolder/alice
+],
+[0],
+[Envelope
+To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:02 +0100
+ <200207292200.3302@wonder.land>
+References: <200207292200.3302@wonder.land>
+
+>To: March Hare <hare@wonder.land>
+>
+>I don't see any wine
+>
+
+])
+
+
+AT_CLEANUP
+
diff --git a/mail/tests/Makefile.am b/mail/tests/Makefile.am
index f04b30408..dafbef646 100644
--- a/mail/tests/Makefile.am
+++ b/mail/tests/Makefile.am
@@ -26,8 +26,15 @@ TESTSUITE_AT +=\
copy02.at\
copy03.at\
copy04.at\
- nohome.at\
+ followup.at\
+ Followup.at\
hold.at\
+ nohome.at\
+ reply.at\
+ Reply.at\
+ send.at\
+ sendrec.at\
+ sendbyname.at\
testsuite.at\
version.at
diff --git a/mail/tests/Reply.at b/mail/tests/Reply.at
new file mode 100644
index 000000000..08639c1bc
--- /dev/null
+++ b/mail/tests/Reply.at
@@ -0,0 +1,158 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([Reply])
+AT_KEYWORDS([reply])
+
+AT_DATA([inbox],
+[From hare@wonder.land Mon Jul 29 22:00:08 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3301
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:06 +0100
+Date: Mon, 29 Jul 2002 22:00:01 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3301@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Invitation
+
+Have some wine
+
+From alice@wonder.land Mon Jul 29 22:00:09 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3302
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:07 +0100
+Date: Mon, 29 Jul 2002 22:00:02 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3302@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+
+I don't see any wine
+
+From hatter@example.org Mon Jul 29 22:00:10 2002
+Received: (from hatter@example.org)
+ by wonder.land id 3303
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:08 +0100
+Date: Mon, 29 Jul 2002 22:00:03 +0100
+From: Hatter <hatter@example.org>
+Message-Id: <200207292200.3303@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Re: Invitation
+
+There isn't any
+
+From alice@wonder.land Mon Jul 29 22:00:11 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3304
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date: Mon, 29 Jul 2002 22:00:04 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3304@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+
+Then it wasn't very civil of you to offer it
+
+From hare@wonder.land Mon Jul 29 22:00:12 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3305
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:10 +0100
+Date: Mon, 29 Jul 2002 22:00:05 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3305@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Re: Invitation
+
+It wasn't very civil of you to sit down without being invited
+])
+
+AT_CHECK([touch record
+mkdir folder outfolder
+])
+
+AT_CHECK([
+AT_DATA([com],
+[set dot indentprefix=">"
+retain To
+Reply 2 1 3
+~m
+.
+x
+])
+
+unset MAIL_DUMP
+MUT_MAIL_CMD -f ./inbox \
+ -E "set noheader" \
+ -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder" < com
+],
+[0],
+[To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+
+Interpolating: 2
+(continue)
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' \
+ mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 3
+RCPT[[0]]: <alice@wonder.land>
+RCPT[[1]]: <hare@wonder.land>
+RCPT[[2]]: <hatter@example.org>
+LENGTH: 396
+To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:02 +0100
+ <200207292200.3302@wonder.land>
+References: <200207292200.3302@wonder.land>
+
+>To: March Hare <hare@wonder.land>
+>
+>I don't see any wine
+>
+
+])
+
+AT_CHECK([sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' record
+],
+[0],
+[Envelope
+To: "Alice" <alice@wonder.land>,"March Hare" <hare@wonder.land>,"Hatter" <hatter@example.org>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:02 +0100
+ <200207292200.3302@wonder.land>
+References: <200207292200.3302@wonder.land>
+
+>To: March Hare <hare@wonder.land>
+>
+>I don't see any wine
+>
+
+])
+
+
+AT_CLEANUP
+
diff --git a/mail/tests/atlocal.in b/mail/tests/atlocal.in
index d68a03508..dfe1b0720 100644
--- a/mail/tests/atlocal.in
+++ b/mail/tests/atlocal.in
@@ -4,6 +4,7 @@
PATH=@abs_top_builddir@/testsuite:@abs_builddir@:@abs_top_builddir@/mail:$top_srcdir:$srcdir:$PATH
testsuitedir=@abs_top_srcdir@/testsuite
+MAILER=sendmail://@abs_top_builddir@/testsuite/mockmail
MALLOC_CHECK_=2
export MALLOC_CHECK_
catmbox() {
diff --git a/mail/tests/followup.at b/mail/tests/followup.at
new file mode 100644
index 000000000..cf55233cf
--- /dev/null
+++ b/mail/tests/followup.at
@@ -0,0 +1,148 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([followup])
+AT_KEYWORDS([followup])
+
+AT_DATA([inbox],
+[From hare@wonder.land Mon Jul 29 22:00:08 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3301
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:06 +0100
+Date: Mon, 29 Jul 2002 22:00:01 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3301@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Invitation
+
+Have some wine
+
+])
+
+AT_CHECK([touch record
+mkdir folder outfolder
+])
+
+AT_DATA([com],
+[set dot indentprefix=">"
+retain To
+followup 1
+~m
+.
+x
+])
+
+AT_CHECK([
+unset MAIL_DUMP
+MUT_MAIL_CMD -f ./inbox \
+ -E "set noheader" \
+ -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder" < com
+],
+[0],
+[To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+
+Interpolating: 1
+(continue)
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' \
+ mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 2
+RCPT[[0]]: <hare@wonder.land>
+RCPT[[1]]: <alice@wonder.land>
+LENGTH: 356
+To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:01 +0100
+ <200207292200.3301@wonder.land>
+References: <200207292200.3301@wonder.land>
+
+>To: Alice <alice@wonder.land>
+>
+>Have some wine
+>
+
+])
+
+AT_CHECK([
+sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' folder/hare],
+[0],
+[Envelope
+To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:01 +0100
+ <200207292200.3301@wonder.land>
+References: <200207292200.3301@wonder.land>
+
+>To: Alice <alice@wonder.land>
+>
+>Have some wine
+>
+
+])
+
+#
+#
+#
+AT_CHECK([
+unset MAIL_DUMP
+MUT_MAIL_CMD -f ./inbox \
+ -E "set noheader" \
+ -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder=$(pwd)/outfolder" < com
+],
+[0],
+[To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+
+Interpolating: 1
+(continue)
+])
+
+AT_CHECK([
+sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' outfolder/hare],
+[0],
+[Envelope
+To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:01 +0100
+ <200207292200.3301@wonder.land>
+References: <200207292200.3301@wonder.land>
+
+>To: Alice <alice@wonder.land>
+>
+>Have some wine
+>
+
+])
+
+AT_CLEANUP
+
diff --git a/mail/tests/reply.at b/mail/tests/reply.at
new file mode 100644
index 000000000..45542c9d6
--- /dev/null
+++ b/mail/tests/reply.at
@@ -0,0 +1,109 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([reply])
+AT_KEYWORDS([reply])
+
+AT_DATA([inbox],
+[From hare@wonder.land Mon Jul 29 22:00:08 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3301
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:06 +0100
+Date: Mon, 29 Jul 2002 22:00:01 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3301@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Invitation
+
+Have some wine
+
+])
+
+AT_CHECK([touch record
+mkdir folder outfolder
+])
+
+AT_CHECK([
+AT_DATA([com],
+[set dot indentprefix=">"
+retain To
+reply 1
+~m
+.
+x
+])
+
+unset MAIL_DUMP
+MUT_MAIL_CMD -f ./inbox \
+ -E "set noheader" \
+ -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder" < com
+],
+[0],
+[To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+
+Interpolating: 1
+(continue)
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' \
+ mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 2
+RCPT[[0]]: <hare@wonder.land>
+RCPT[[1]]: <alice@wonder.land>
+LENGTH: 356
+To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:01 +0100
+ <200207292200.3301@wonder.land>
+References: <200207292200.3301@wonder.land>
+
+>To: Alice <alice@wonder.land>
+>
+>Have some wine
+>
+
+])
+
+AT_CHECK([
+sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' record],
+[0],
+[Envelope
+To: "March Hare" <hare@wonder.land>,"Alice" <alice@wonder.land>
+Subject: Re: Invitation
+In-Reply-To: Your message of Mon, 29 Jul 2002 22:00:01 +0100
+ <200207292200.3301@wonder.land>
+References: <200207292200.3301@wonder.land>
+
+>To: Alice <alice@wonder.land>
+>
+>Have some wine
+>
+
+])
+
+AT_CLEANUP
+
diff --git a/mail/tests/send.at b/mail/tests/send.at
new file mode 100644
index 000000000..89053b0ed
--- /dev/null
+++ b/mail/tests/send.at
@@ -0,0 +1,45 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([send])
+
+AT_DATA([text],
+[Test message.
+Bye.
+])
+
+AT_CHECK([unset MAIL_DUMP
+MUT_MAIL_CMD -E "set sendmail=$MAILER" -s 'Test 22.1' gray@example.org < text
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 1
+RCPT[[0]]: <gray@example.org>
+LENGTH: 139
+Subject: Test 22.1
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+AT_CLEANUP
+
+
+
diff --git a/mail/tests/sendbyname.at b/mail/tests/sendbyname.at
new file mode 100644
index 000000000..f8680085f
--- /dev/null
+++ b/mail/tests/sendbyname.at
@@ -0,0 +1,119 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([send -F])
+
+AT_DATA([text],
+[Test message.
+Bye.
+])
+
+#
+# Send with record by name
+#
+AT_CHECK([touch record
+mkdir folder outfolder
+])
+
+AT_CHECK([unset MAIL_DUMP
+MUT_MAIL_CMD -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder" \
+ -F \
+ -s 'Test 22.3' gray@example.org < text
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 1
+RCPT[[0]]: <gray@example.org>
+LENGTH: 139
+Subject: Test 22.3
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+AT_CHECK([cat record])
+
+AT_CHECK([
+sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' folder/gray],
+[0],
+[Envelope
+Subject: Test 22.3
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+#
+# Send with record by name
+#
+AT_CHECK([unset MAIL_DUMP
+MUT_MAIL_CMD -E "set sendmail=$MAILER" \
+ -E "set record=$(pwd)/record" \
+ -E "set folder=$(pwd)/folder" \
+ -E "set outfolder=$(pwd)/outfolder" \
+ -F \
+ -s 'Test 22.3' gray@example.org < text
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 1
+RCPT[[0]]: <gray@example.org>
+LENGTH: 139
+Subject: Test 22.3
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+AT_CHECK([cat record])
+
+AT_CHECK([
+sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' outfolder/gray record],
+[0],
+[Envelope
+Subject: Test 22.3
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+AT_CLEANUP
+
diff --git a/mail/tests/sendrec.at b/mail/tests/sendrec.at
new file mode 100644
index 000000000..8f0dcf79e
--- /dev/null
+++ b/mail/tests/sendrec.at
@@ -0,0 +1,64 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2015-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/>.
+
+AT_SETUP([send (record)])
+
+AT_DATA([text],
+[Test message.
+Bye.
+])
+
+#
+# Send with record
+#
+AT_CHECK([unset MAIL_DUMP
+RECORD=$(pwd)/record
+MUT_MAIL_CMD -E "set sendmail=$MAILER" -E "set record=$RECORD" -s 'Test 22.2' gray@example.org < text
+])
+
+AT_CHECK([
+sed -e '/^SENDER:/d' \
+ -e '/^Date:/d' \
+ -e '/^X-Mailer:/d' mail.dump],
+[0],
+[MSGID: 0001
+NRCPT: 1
+RCPT[[0]]: <gray@example.org>
+LENGTH: 139
+Subject: Test 22.2
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+AT_CHECK([
+sed -e '1s/^From .*/Envelope/' \
+ -e '/^X-[[a-zA-Z]]*:/d' \
+ -e '/^Date:/d' record],
+[0],
+[Envelope
+Subject: Test 22.2
+To: <gray@example.org>
+
+Test message.
+Bye.
+
+])
+
+AT_CLEANUP
+
diff --git a/mail/tests/testsuite.at b/mail/tests/testsuite.at
index 4e785fd61..972d19e75 100644
--- a/mail/tests/testsuite.at
+++ b/mail/tests/testsuite.at
@@ -33,3 +33,13 @@ m4_include([copy04.at])
m4_include([align.at])
m4_include([D.at])
m4_include([hold.at])
+
+AT_BANNER([Sending and replying])
+m4_include([send.at])
+m4_include([sendrec.at])
+m4_include([sendbyname.at])
+
+m4_include([reply.at])
+m4_include([Reply.at])
+m4_include([followup.at])
+m4_include([Followup.at])
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 f92f91e36..f2defdf65 100644
--- a/mail/util.c
+++ b/mail/util.c
@@ -17,6 +17,8 @@
#include "mail.h"
#include <mailutils/util.h>
#include <mailutils/mime.h>
+#include <mailutils/folder.h>
+#include <mailutils/auth.h>
#include <pwd.h>
#ifdef HAVE_TERMIOS_H
# include <termios.h>
@@ -221,7 +223,7 @@ util_range_msg (size_t low, size_t high, int flags,
{
msgset_t *set = msgset_make_1 (low);
func (set, mesg, data);
- free (set);
+ msgset_free (set);
}
/* Bail out if we receive an interrupt. */
if (ml_got_interrupt () != 0)
@@ -484,15 +486,56 @@ util_folder_path (const char *name)
return folder;
}
+int outfilename_mode;
+
char *
-util_get_sender (int msgno, int strip)
+util_outfilename (mu_address_t addr)
{
- mu_message_t msg = NULL;
- mu_address_t addr = NULL;
- char *buf = NULL, *p;
+ char *buf, *p;
+ int rc;
+
+ if ((rc = mu_address_aget_email (addr, 1, &buf)) != 0)
+ {
+ mu_error (_("Cannot determine sender name: %s"), mu_strerror (rc));
+ return NULL;
+ }
+
+ switch (mailvar_is_true (mailvar_name_mailx)
+ ? outfilename_local : outfilename_mode)
+ {
+ case outfilename_local:
+ p = strchr (buf, '@');
+ if (p)
+ *p = 0;
+ break;
+
+ case outfilename_email:
+ break;
+
+ case outfilename_domain:
+ p = strchr (buf, '@');
+ if (p)
+ {
+ p++;
+ memmove (buf, p, strlen (p) + 1);
+ }
+ else
+ {
+ free (buf);
+ buf = mu_strdup ("localdomain");
+ }
+ break;
+ }
+ return buf;
+}
- mu_mailbox_get_message (mbox, msgno, &msg);
- addr = get_sender_address (msg);
+char *
+util_message_sender (mu_message_t msg, int strip)
+{
+ mu_address_t addr = get_sender_address (msg);
+ char *buf;
+ int rc;
+
if (!addr)
{
mu_envelope_t env = NULL;
@@ -501,29 +544,36 @@ 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)
+ if (strip)
{
- mu_error (_("Cannot determine sender name (msg %d)"), msgno);
- mu_address_destroy (&addr);
- return NULL;
+ buf = util_outfilename (addr);
}
-
- if (strip)
+ else if ((rc = mu_address_aget_printable (addr, &buf)) != 0)
{
- p = strchr (buf, '@');
- if (p)
- *p = 0;
+ mu_error (_("Cannot determine sender name: %s"), mu_strerror (rc));
+ buf = 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)
{
@@ -646,47 +696,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
@@ -694,38 +788,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);
}
}
@@ -886,13 +977,56 @@ is_address_field (const char *name)
return 0;
}
+void
+util_address_expand_aliases (mu_address_t *paddr)
+{
+ struct mu_address hint;
+ mu_address_t addr = *paddr;
+ mu_address_t new_addr = NULL;
+ int rc;
+
+ memset (&hint, 0, sizeof (hint));
+ while (addr)
+ {
+ struct mu_address *next = addr->next;
+ addr->next = NULL;
+
+ if (addr->domain == NULL)
+ {
+ char *exp = alias_expand (addr->local_part);
+ if (exp)
+ {
+ mu_address_destroy (&addr);
+ rc = mu_address_create_hint (&addr, exp, &hint, 0);
+ if (rc)
+ {
+ mu_error (_("Cannot parse address `%s': %s"),
+ exp, mu_strerror (rc));
+ free (exp);
+ continue;
+ }
+ free (exp);
+ }
+ }
+ mu_address_union (&new_addr, addr);
+ mu_address_destroy (&addr);
+
+ addr = next;
+ }
+ *paddr = new_addr;
+}
+
int
-util_header_expand (mu_header_t *phdr)
+util_header_expand_aliases (mu_header_t *phdr)
{
size_t i, nfields = 0;
mu_header_t hdr;
int errcnt = 0, rc;
+ if (mailvar_is_true (mailvar_name_inplacealiases))
+ /* If inplacealiases was set, aliases have been already expanded */
+ return 0;
+
rc = mu_header_create (&hdr, "", 0);
if (rc)
{
@@ -903,84 +1037,54 @@ util_header_expand (mu_header_t *phdr)
mu_header_get_field_count (*phdr, &nfields);
for (i = 1; i <= nfields; i++)
{
- const char *name, *value;
+ const char *name;
+ char *value;
if (mu_header_sget_field_name (*phdr, i, &name))
continue;
- if (mu_header_sget_field_value (*phdr, i, &value))
+ if (mu_header_aget_field_value (*phdr, i, &value))
continue;
if (is_address_field (name))
{
+ struct mu_address hint;
const char *s;
- mu_address_t addr = NULL;
- struct mu_wordsplit ws;
- size_t j;
-
- if (mu_header_sget_value (hdr, name, &s) == 0)
- mu_address_create (&addr, s);
-
- ws.ws_delim = ",";
- if (mu_wordsplit (value, &ws,
- MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS|
- MU_WRDSF_WS|
- MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
+ mu_address_t a = NULL;
+
+ mu_string_unfold (value, NULL);
+
+ memset (&hint, 0, sizeof (hint));
+ rc = mu_address_create_hint (&a, value, &hint, 0);
+ if (rc == 0)
{
- errcnt++;
- mu_error (_("cannot split line `%s': %s"), value,
- mu_wordsplit_strerror (&ws));
+ mu_address_t ha = NULL;
+ util_address_expand_aliases (&a);
+ if (mu_header_sget_value (hdr, name, &s) == 0)
+ mu_address_create_hint (&ha, s, &hint, 0);
+ mu_address_union (&ha, a);
+ mu_address_destroy (&a);
+ a = ha;
}
else
{
- for (j = 0; j < ws.ws_wordc; j++)
- {
- char *exp_mem = NULL;
- mu_address_t new_addr;
- char *p = ws.ws_wordv[j];
- char *exp;
-
- if (mailvar_is_true (mailvar_name_inplacealiases))
- /* If inplacealiases was set, the value was a
- lready expanded */
- exp = p;
- else
- {
- exp_mem = alias_expand (p);
- exp = exp_mem ? exp_mem : p;
- }
- rc = mu_address_create (&new_addr, exp);
- if (rc)
- {
- errcnt++;
- if (exp_mem)
- mu_error (_("Cannot parse address `%s'"
- " (while expanding `%s'): %s"),
- exp, p, mu_strerror (rc));
- else
- mu_error (_("Cannot parse address `%s': %s"),
- p, mu_strerror (rc));
- }
-
- mu_address_union (&addr, new_addr);
- mu_address_destroy (&new_addr);
- free (exp_mem);
- }
+ mu_error (_("Cannot parse address `%s': %s"),
+ value, mu_strerror (rc));
+ }
- if (addr)
- {
- const char *newvalue;
+ if (a)
+ {
+ const char *newvalue;
- rc = mu_address_sget_printable (addr, &newvalue);
- if (rc == 0 && newvalue)
- mu_header_set_value (hdr, name, newvalue, 1);
- mu_address_destroy (&addr);
- }
+ rc = mu_address_sget_printable (a, &newvalue);
+ if (rc == 0 && newvalue)
+ mu_header_set_value (hdr, name, newvalue, 1);
+ mu_address_destroy (&a);
}
- mu_wordsplit_free (&ws);
}
else
mu_header_append (hdr, name, value);
+ free (value);
}
if (errcnt == 0)
@@ -1204,3 +1308,60 @@ open_pager (size_t lines)
}
return str;
}
+
+int
+util_get_folder (mu_folder_t *pfolder, mu_url_t url, int type)
+{
+ mu_folder_t folder;
+ int rc;
+
+ rc = mu_folder_create_from_record (&folder, url, NULL);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_create",
+ mu_url_to_string (url), rc);
+ return -1;
+ }
+
+ if (!mu_folder_is_local (folder))
+ {
+ if (type == local_folder)
+ {
+ /* TRANSLATORS: The subject of this sentence ("folder") is the
+ name of the variable. Don't translate it. */
+ mu_error ("%s", _("folder must be set to a local folder"));
+ mu_folder_destroy (&folder);
+ return -1;
+ }
+
+ /* Set ticket for a remote folder */
+ rc = mu_folder_attach_ticket (folder);
+ if (rc)
+ {
+ mu_authority_t auth = NULL;
+
+ if (mu_folder_get_authority (folder, &auth) == 0 && auth)
+ {
+ mu_ticket_t tct;
+ mu_noauth_ticket_create (&tct);
+ rc = mu_authority_set_ticket (auth, tct);
+ if (rc)
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_authority_set_ticket",
+ NULL, rc);
+ }
+ }
+ }
+
+ rc = mu_folder_open (folder, MU_STREAM_READ);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_open",
+ mu_url_to_string (url), rc);
+ mu_folder_destroy (&folder);
+ return -1;
+ }
+
+ *pfolder = folder;
+ return 0;
+}
+
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/mda/lmtpd/lmtpd.c b/mda/lmtpd/lmtpd.c
index 8e64eb5a7..6fae4a9db 100644
--- a/mda/lmtpd/lmtpd.c
+++ b/mda/lmtpd/lmtpd.c
@@ -183,10 +183,9 @@ main (int argc, char **argv)
MU_APP_INIT_NLS ();
/* Default locker settings */
- mu_locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
- mu_locker_assign);
- mu_locker_set_default_retry_timeout (1);
- mu_locker_set_default_retry_count (300);
+ mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = 1;
+ mu_locker_defaults.retry_count = 300;
/* Register needed modules */
MU_AUTH_REGISTER_ALL_MODULES ();
diff --git a/mda/mda/mda.c b/mda/mda/mda.c
index d96ae03d4..76f4fc762 100644
--- a/mda/mda/mda.c
+++ b/mda/mda/mda.c
@@ -95,11 +95,10 @@ main (int argc, char **argv)
/* Native Language Support */
MU_APP_INIT_NLS ();
- /* Default locker settings */
- mu_locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
- mu_locker_assign);
- mu_locker_set_default_retry_timeout (1);
- mu_locker_set_default_retry_count (300);
+ /* Set locker defaults. */
+ mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = 1;
+ mu_locker_defaults.retry_count = 300;
/* Register needed modules */
MU_AUTH_REGISTER_ALL_MODULES ();
diff --git a/mda/putmail/putmail.c b/mda/putmail/putmail.c
index 3fa6a57ea..8174a68b6 100644
--- a/mda/putmail/putmail.c
+++ b/mda/putmail/putmail.c
@@ -44,10 +44,9 @@ main (int argc, char **argv)
MU_APP_INIT_NLS ();
/* Default locker settings */
- mu_locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
- mu_locker_assign);
- mu_locker_set_default_retry_timeout (1);
- mu_locker_set_default_retry_count (300);
+ mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = 1;
+ mu_locker_defaults.retry_count = 300;
/* Register needed modules */
MU_AUTH_REGISTER_ALL_MODULES ();
diff --git a/mh/tests/mhn.at b/mh/tests/mhn.at
index 62260172e..f66ba4e05 100644
--- a/mh/tests/mhn.at
+++ b/mh/tests/mhn.at
@@ -601,7 +601,6 @@ MIME-Version: 1.0
Content-Type: text/plain
#when sent, this line will start with only one #
-
])
MH_CHECK([mhn-compose: charset],
@@ -624,7 +623,6 @@ Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Cze=C5=9B=C4=87
-
])
MH_CHECK([mhn-compose: forward],
@@ -744,7 +742,6 @@ Content-Description: Single line part
--BOUNDARY-2--
--BOUNDARY-1--
-
])
MH_CHECK([mhn-compose: forward (current folder)],
@@ -865,7 +862,6 @@ Content-Description: Single line part
--BOUNDARY-2--
--BOUNDARY-1--
-
])
MH_CHECK([mhn-compose: forward (single message)],
@@ -932,8 +928,6 @@ Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
-
-
])
MH_CHECK([mhn-compose: external data],
@@ -965,7 +959,6 @@ Content-Type: application/octet-stream; type=tar; conversions=compress
Content-ID: 1
Content-Description: GNU Mailutils distribution
-
])
@@ -1064,7 +1057,6 @@ All mimsy were the borogoves,
And the mome raths outgrabe.
-
--BOUNDARY-2
Content-Type: text/plain
Content-ID: 3
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/sieve/tests/vacation.at b/sieve/tests/vacation.at
index 90452613d..39e9ce330 100644
--- a/sieve/tests/vacation.at
+++ b/sieve/tests/vacation.at
@@ -33,7 +33,7 @@ cat $MAIL_DUMP
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: bar@dontmailme.org
-LENGTH: 321
+LENGTH: 320
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -44,7 +44,6 @@ In-Reply-To: Your message of Fri, 28 Dec 2001 23:28:08 +0200
References: <200112232808.fERKR9N16790@dontmailme.org>
I'm on vacation
-
]],
[VACATION on msg uid 1
VACATION on msg uid 2
@@ -67,7 +66,7 @@ cat $MAIL_DUMP
SENDER: roadrunner@acme.example.com
NRCPT: 1
RCPT[0]: coyote@desert.example.org
-LENGTH: 240
+LENGTH: 239
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -76,12 +75,11 @@ Subject: =?UTF-8?Q?Re:_I_have_a_present_for_you?=
In-Reply-To: Your message of Sun May 6 22:16:47 2001
I'm on vacation
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: bar@dontmailme.org
-LENGTH: 321
+LENGTH: 320
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -92,7 +90,6 @@ In-Reply-To: Your message of Fri, 28 Dec 2001 23:28:08 +0200
References: <200112232808.fERKR9N16790@dontmailme.org>
I'm on vacation
-
]],
[VACATION on msg uid 1
VACATION on msg uid 2
@@ -115,7 +112,7 @@ cat $MAIL_DUMP
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: coyote@desert.example.org
-LENGTH: 240
+LENGTH: 239
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -124,12 +121,11 @@ Subject: =?UTF-8?Q?Re:_I_have_a_present_for_you?=
In-Reply-To: Your message of Sun May 6 22:16:47 2001
I'm on vacation
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: b1ff@de.res.example.com
-LENGTH: 232
+LENGTH: 231
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -138,12 +134,11 @@ Subject: =?UTF-8?Q?Re:_$$$_YOU,_TOO,_CAN_BE_A_MILLIONAIRE!_$$$?=
In-Reply-To: Your message of TBD
I'm on vacation
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: bar@dontmailme.org
-LENGTH: 321
+LENGTH: 320
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -154,7 +149,6 @@ In-Reply-To: Your message of Fri, 28 Dec 2001 23:28:08 +0200
References: <200112232808.fERKR9N16790@dontmailme.org>
I'm on vacation
-
]],
[VACATION on msg uid 1
VACATION on msg uid 2
@@ -178,7 +172,7 @@ cat $MAIL_DUMP
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: coyote@desert.example.org
-LENGTH: 240
+LENGTH: 239
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -187,12 +181,11 @@ Subject: =?UTF-8?Q?Re:_I_have_a_present_for_you?=
In-Reply-To: Your message of Sun May 6 22:16:47 2001
I'm on vacation
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: b1ff@de.res.example.com
-LENGTH: 232
+LENGTH: 231
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -201,12 +194,11 @@ Subject: =?UTF-8?Q?Re:_$$$_YOU,_TOO,_CAN_BE_A_MILLIONAIRE!_$$$?=
In-Reply-To: Your message of TBD
I'm on vacation
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: bar@dontmailme.org
-LENGTH: 321
+LENGTH: 320
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -217,7 +209,6 @@ In-Reply-To: Your message of Fri, 28 Dec 2001 23:28:08 +0200
References: <200112232808.fERKR9N16790@dontmailme.org>
I'm on vacation
-
]],
[VACATION on msg uid 1
VACATION on msg uid 2
@@ -243,7 +234,7 @@ cat $MAIL_DUMP
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: coyote@desert.example.org
-LENGTH: 251
+LENGTH: 250
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: base64
@@ -252,12 +243,11 @@ Subject: =?UTF-8?Q?Re:_I_have_a_present_for_you?=
In-Reply-To: Your message of Sun May 6 22:16:47 2001
SSdtIG9uIHZhY2F0aW9uLg==
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: b1ff@de.res.example.com
-LENGTH: 243
+LENGTH: 242
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: base64
@@ -266,12 +256,11 @@ Subject: =?UTF-8?Q?Re:_$$$_YOU,_TOO,_CAN_BE_A_MILLIONAIRE!_$$$?=
In-Reply-To: Your message of TBD
SSdtIG9uIHZhY2F0aW9uLg==
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: bar@dontmailme.org
-LENGTH: 332
+LENGTH: 331
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: base64
@@ -282,7 +271,6 @@ In-Reply-To: Your message of Fri, 28 Dec 2001 23:28:08 +0200
References: <200112232808.fERKR9N16790@dontmailme.org>
SSdtIG9uIHZhY2F0aW9uLg==
-
]],
[VACATION on msg uid 1
VACATION on msg uid 2
@@ -387,7 +375,7 @@ cat ./mta.diag
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: coyote@desert.example.org
-LENGTH: 326
+LENGTH: 325
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -401,12 +389,11 @@ I will attend to your message as soon as I'm back.
Best regards,
Ty Coon
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: b1ff@de.res.example.com
-LENGTH: 318
+LENGTH: 317
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -420,12 +407,11 @@ I will attend to your message as soon as I'm back.
Best regards,
Ty Coon
-
MSGID: 0001
SENDER: foobar@nonexistent.net
NRCPT: 1
RCPT[0]: bar@dontmailme.org
-LENGTH: 407
+LENGTH: 406
MIME-Version: 1.0
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -441,7 +427,6 @@ I will attend to your message as soon as I'm back.
Best regards,
Ty Coon
-
]],
[VACATION on msg uid 1
VACATION on msg uid 2
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.