summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--imap/ChangeLog15
-rw-r--r--imap/Makefile.am6
-rw-r--r--imap/imapd-ssl.dist.in.git9
-rw-r--r--imap/imapd.c4
-rw-r--r--imap/imapd.dist.in.git14
-rw-r--r--imap/imapd.sgml221
-rw-r--r--imap/imaplogin.c2
-rw-r--r--imap/pop3d-ssl.dist.in.git7
-rw-r--r--imap/smap.c4
-rw-r--r--maildir/.gitignore4
-rw-r--r--maildir/Makefile.am35
-rw-r--r--maildir/configure.ac21
-rw-r--r--maildir/maildirmake.sgml1
-rw-r--r--maildir/maildirwatch.c672
-rw-r--r--maildir/maildirwatch.h50
-rw-r--r--maildir/maildirwatch.sgml114
-rw-r--r--maildir/maildirwatchprog.c201
-rw-r--r--maildrop/Makefile.am5
-rw-r--r--maildrop/configure.ac2
-rw-r--r--sqwebmail/ChangeLog4
-rw-r--r--sqwebmail/Makefile.am5
-rw-r--r--tcpd/configure.ac10
-rw-r--r--tcpd/libcouriergnutls.c186
-rw-r--r--tcpd/libcouriertls.c231
-rw-r--r--tcpd/starttls.c1
-rw-r--r--unicode/.gitignore1
-rw-r--r--unicode/ChangeLog22
-rw-r--r--unicode/Makefile.am60
-rw-r--r--unicode/biditest2.C80
-rw-r--r--unicode/book.xml238
-rw-r--r--unicode/configure.ac24
-rw-r--r--unicode/courier-unicode-version.m4.in45
-rw-r--r--unicode/courier-unicode.h.in65
-rw-r--r--unicode/courier-unicode.spec.in37
-rw-r--r--unicode/m4/courier-unicode.m441
-rw-r--r--unicode/unicode_bidi.c131
-rw-r--r--unicode/unicodecpp.C71
37 files changed, 1846 insertions, 793 deletions
diff --git a/imap/ChangeLog b/imap/ChangeLog
index 42d9c1e..f5dedf7 100644
--- a/imap/ChangeLog
+++ b/imap/ChangeLog
@@ -1,3 +1,18 @@
+2021-02-23 Sam Varshavchik <mrsam@courier-mta.com>
+
+ * Implement support for the TLS ALPN extension.
+
+2021-02-18 Sam Varshavchik <mrsam@courier-mta.com>
+
+ * Replace FAM/Gamin with inotify.
+
+ * Install the maildirwatch tool.
+
+ * Remove the IMAP_ENHANCEDIDLE setting from the imapd configuration
+ file.
+
+ * Update RPM spec file.
+
5.0.13
2021-03-01 Sam Varshavchik <mrsam@courier-mta.com>
diff --git a/imap/Makefile.am b/imap/Makefile.am
index dda6235..a363bc1 100644
--- a/imap/Makefile.am
+++ b/imap/Makefile.am
@@ -74,11 +74,9 @@ imapd_SOURCES=fetch.c fetchinfo.c fetchinfo.h imapd.c imapd.h \
search.c searchinfo.c searchinfo.h \
storeinfo.c storeinfo.h
-imapd_DEPENDENCIES=libimapd.la \
- ../maildir/maildir.libdeps @dblibrary@
+imapd_DEPENDENCIES=libimapd.la @dblibrary@
-imapd_LDADD=libimapd.la `cat ../maildir/maildir.libdeps` \
- @dblibrary@ @DEBUGLIB@ @LDAUTH@ -lcourierauth
+imapd_LDADD=libimapd.la @dblibrary@ @DEBUGLIB@ @LDAUTH@ -lcourierauth
pop3login_SOURCES=pop3login.c pop3dcapa.c proxy.c proxy.h
pop3login_DEPENDENCIES=../tcpd/libspipe.la ../tcpd/libspipe.la libpop3d.la
diff --git a/imap/imapd-ssl.dist.in.git b/imap/imapd-ssl.dist.in.git
index 35880c8..7c351be 100644
--- a/imap/imapd-ssl.dist.in.git
+++ b/imap/imapd-ssl.dist.in.git
@@ -5,7 +5,7 @@
# Do not alter lines that begin with ##, they are used when upgrading
# this configuration.
#
-# Copyright 2000 - 2019 Double Precision, Inc. See COPYING for
+# Copyright 2000 - 2021 Double Precision, Inc. See COPYING for
# distribution information.
#
# This configuration file sets various options for the Courier-IMAP server
@@ -316,6 +316,13 @@ TLS_VERIFYPEER=NONE
TLS_CACHEFILE=@localstatedir@/couriersslpop3cache
TLS_CACHESIZE=524288
+##NAME: TLS_ALPN:0
+#
+# Application level protocol negotiation should be enabled by default, and
+# should be commented out only in case of compatibility issues.
+
+TLS_ALPN=imap
+
##NAME: MAILDIRPATH:0
#
# MAILDIRPATH - directory name of the maildir directory.
diff --git a/imap/imapd.c b/imap/imapd.c
index c0b5908..f2f4bbd 100644
--- a/imap/imapd.c
+++ b/imap/imapd.c
@@ -4324,9 +4324,7 @@ int uid=0;
read_eol();
- if ((p=getenv("IMAP_ENHANCEDIDLE")) == NULL
- || !atoi(p)
- || imapenhancedidle())
+ if (imapenhancedidle())
imapidle();
curtoken=nexttoken();
if (strcmp(curtoken->tokenbuf, "DONE") == 0)
diff --git a/imap/imapd.dist.in.git b/imap/imapd.dist.in.git
index 372cab3..2674c28 100644
--- a/imap/imapd.dist.in.git
+++ b/imap/imapd.dist.in.git
@@ -282,20 +282,6 @@ IMAP_USELOCKS=1
IMAP_SHAREDINDEXFILE=@sysconfdir@/shared/index
-##NAME: IMAP_ENHANCEDIDLE:0
-#
-# If Courier was compiled with the File Alteration Monitor, setting
-# IMAP_ENHANCEDIDLE to 1 enables enhanced IDLE mode, where multiple
-# clients may open the same folder concurrently, and receive updates to
-# folder contents in realtime. See the imapd(8) man page for additional
-# information.
-#
-# IMPORTANT: IMAP_USELOCKS *MUST* also be set to 1, and IDLE must be included
-# in the IMAP_CAPABILITY list.
-#
-
-IMAP_ENHANCEDIDLE=0
-
##NAME: IMAP_TRASHFOLDERNAME:0
#
# The name of the magic trash Folder. For MSOE compatibility,
diff --git a/imap/imapd.sgml b/imap/imapd.sgml
index 43b8ec3..f95932d 100644
--- a/imap/imapd.sgml
+++ b/imap/imapd.sgml
@@ -1,5 +1,5 @@
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
-<!-- Copyright 1998 - 2007 Double Precision, Inc. See COPYING for -->
+<!-- Copyright 1998 - 2021 Double Precision, Inc. See COPYING for -->
<!-- distribution information. -->
<refentry id="imapd">
<info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info>
@@ -12,12 +12,12 @@
<refnamediv>
<refname>imapd</refname>
- <refpurpose>The <application moreinfo="none">Courier</application> IMAP server</refpurpose>
+ <refpurpose>The <application>Courier</application> IMAP server</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis sepchar=" ">
- <command moreinfo="none">@libexecdir@/couriertcpd</command>
+ <command>@libexecdir@/couriertcpd</command>
<arg choice="req" rep="norepeat">couriertcpd options</arg>
<arg choice="req" rep="norepeat">@prefix@/sbin/imaplogin</arg>
<arg choice="opt" rep="repeat"><replaceable>modules</replaceable></arg>
@@ -26,7 +26,7 @@
</cmdsynopsis>
<cmdsynopsis sepchar=" ">
- <command moreinfo="none">@prefix@/bin/imapd</command>
+ <command>@prefix@/bin/imapd</command>
<arg choice="req" rep="norepeat">./Maildir</arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -35,48 +35,48 @@
<title>DESCRIPTION</title>
<para>
-<command moreinfo="none">imapd</command> is the <application moreinfo="none">Courier</application>
+<command>imapd</command> is the <application>Courier</application>
IMAP server that provides IMAP access to
Maildir mailboxes.
-Normally you don't have to worry about it, as <command moreinfo="none">imapd</command>
+Normally you don't have to worry about it, as <command>imapd</command>
runs automatically after receiving a network connection, accompanied by
the appropriate userid and password.</para>
<para>
-<command moreinfo="none">couriertcpd</command> opens network ports that receive incoming
+<command>couriertcpd</command> opens network ports that receive incoming
IMAP connections.
After an incoming network connections is established,
-<command moreinfo="none">couriertcpd</command>
+<command>couriertcpd</command>
runs the command specified by its first argument, which is
-<command moreinfo="none">imaplogin</command> passing the remaining arguments to
-<command moreinfo="none">imaplogin</command>.
-<command moreinfo="none">imaplogin</command> reads the IMAP login userid and password,
+<command>imaplogin</command> passing the remaining arguments to
+<command>imaplogin</command>.
+<command>imaplogin</command> reads the IMAP login userid and password,
then runs the modules specified by its remaining options, which
-are <application moreinfo="none">Courier</application>
+are <application>Courier</application>
server authentication modules described in the
<ulink url="authlib.html"><citerefentry><refentrytitle>authlib</refentrytitle><manvolnum>7</manvolnum></citerefentry></ulink>
manual page.</para>
<para>
The last daisy-chained command is
-<command moreinfo="none">imapd</command>, which is the actual IMAP server,
+<command>imapd</command>, which is the actual IMAP server,
which is started from the logged-in account's home directory.
-The sole argument to <command moreinfo="none">imapd</command> is the pathname
+The sole argument to <command>imapd</command> is the pathname
to the default IMAP mailbox, which is usually
-<filename moreinfo="none">./Maildir</filename>.
+<filename>./Maildir</filename>.
Some authentication modules are capable of specifying a different
filename, by setting the <envar>MAILDIR</envar> environment variable.
</para>
<para>
-<command moreinfo="none">imapd</command> may also be invoked from the shell prompt, in which
-case it issues a <literal moreinfo="none">PREAUTH</literal> response, then changes the
+<command>imapd</command> may also be invoked from the shell prompt, in which
+case it issues a <literal>PREAUTH</literal> response, then changes the
current directory to either its
argument, or the contents of the <envar>MAILDIR</envar> environment
variable, then attempts to talk IMAP on standard input and output.</para>
<para>
-<command moreinfo="none">imapd</command> implements IMAP4REV1, as defined by
+<command>imapd</command> implements IMAP4REV1, as defined by
<ulink url="http://www.rfc-editor.org/rfc/rfc2060.txt">RFC 2060</ulink>.</para>
</refsect1>
@@ -89,11 +89,11 @@ variable, then attempts to talk IMAP on standard input and output.</para>
<term><envar>AUTH*</envar></term>
<listitem>
<simpara>
-<command moreinfo="none">imapd</command> examines several environment variables whose
+<command>imapd</command> examines several environment variables whose
names start with AUTH - these environment variables are set by
-<command moreinfo="none">imaplogin</command> and the authentication modules.
+<command>imaplogin</command> and the authentication modules.
Their absence tells
-<command moreinfo="none">imapd</command> that it's running from the command line.
+<command>imapd</command> that it's running from the command line.
</simpara>
</listitem>
</varlistentry>
@@ -103,15 +103,15 @@ Their absence tells
<listitem>
<simpara>
<envar>MAILDIR</envar> - if defined,
-<command moreinfo="none">imapd</command> changes its directory to the
+<command>imapd</command> changes its directory to the
one specified by this environment variable.
-Otherwise <command moreinfo="none">imapd</command> changes
+Otherwise <command>imapd</command> changes
its directory to the one specified on the command line.</simpara>
</listitem>
</varlistentry>
<varlistentry>
- <term><filename moreinfo="none">`<command moreinfo="none">pwd</command>`/.</filename></term>
+ <term><filename>`<command>pwd</command>`/.</filename></term>
<listitem>
<simpara>
The current directory is assumed to be the main INBOX
@@ -120,7 +120,7 @@ Maildir.</simpara>
</varlistentry>
<varlistentry>
- <term><filename moreinfo="none">`<command moreinfo="none">pwd</command>`/.<replaceable>folder</replaceable></filename></term>
+ <term><filename>`<command>pwd</command>`/.<replaceable>folder</replaceable></filename></term>
<listitem>
<simpara>
Maildir folders, each one containing their own
@@ -131,25 +131,16 @@ tmp, new, cur, etc...</simpara>
<para>
Other environment variables are initialized from the
-<filename moreinfo="none">@sysconfdir@/imapd</filename> and
-<filename moreinfo="none">@sysconfdir@/imapd-ssl</filename> configuration files.
+<filename>@sysconfdir@/imapd</filename> and
+<filename>@sysconfdir@/imapd-ssl</filename> configuration files.
These files are loaded into the environment by the system startup script
-that runs <command moreinfo="none">couriertcpd</command>.</para>
+that runs <command>couriertcpd</command>.</para>
<refsect2 id="imapd_realtime_concurrent_folder_status_updates">
<title>Realtime concurrent folder status updates</title>
<para>
-Setting the <literal moreinfo="none">IMAP_ENHANCEDIDLE</literal> to
-<literal moreinfo="none">1</literal> in
-<filename moreinfo="none">@sysconfdir@/imapd</filename> enables realtime concurrent folder
-status updates.
-When relatime folder status updates are enabled all IMAP mail clients
-that have the same folder open will be
-immediately notified of any changes to the folder's contents.</para>
-
- <para>
-The <application moreinfo="none">Courier</application> IMAP server always allows
+The <application>Courier</application> IMAP server always allows
more than one mail client to have the
same folder opened.
However, when two or more clients have the same folder opened, the mail
@@ -161,79 +152,19 @@ No provisions exists to notify the mail client immediately when the folder's
contents are modified by another mail client.</para>
<para>
-The <literal moreinfo="none">IDLE</literal> extension to the base IMAP protocol provides
+The <literal>IDLE</literal> extension to the base IMAP protocol provides
a delivery mechanism for notifying mail clients of changes to the mail
-folder's contents. Although at this time it's not known to which extent
-the <literal moreinfo="none">IDLE</literal> extension is supported by IMAP mail clients,
-the <application moreinfo="none">Courier</application> IMAP server fully implements
-the <literal moreinfo="none">IDLE</literal>
-extension provided that the following requirements are met:
-</para>
+folder's contents.</para>
<variablelist>
<varlistentry>
- <term><application moreinfo="none">Gamin</application> or <application moreinfo="none">FAM</application></term>
- <listitem>
- <para>
-Either <ulink url="http://www.gnome.org/~veillard/gamin/">Gamin</ulink> or
-<ulink url="http://oss.sgi.com/projects/fam/">FAM</ulink>
-must be properly installed and
-configured prior to installing
-the <application moreinfo="none">Courier</application> IMAP server.</para>
-
- <para>
-<application moreinfo="none">Gamin</application>/<application moreinfo="none">FAM</application>
-is an application library that
-provides an interface to the operating system's kernel
-that applications can use to be notified when specific files
-or directories are changed, and the
-<application moreinfo="none">Courier</application> IMAP server leverages this API to
-implement realtime concurrent folder status updates.
-According to the most recently available documentation,
-<application moreinfo="none">Gamin</application> is a Linux-specific library, and
-<application moreinfo="none">FAM</application>
-builds and runs on Linux and IRIX.
-<application moreinfo="none">FAM</application>
-should also build on other platforms, but without a supported kernel monitor
-FAM will fall back to a polling mode.
-At press time,
-<application moreinfo="none">FAM</application>'s
-web site reports that
-<application moreinfo="none">FAM</application>
-succesfully builds (in polling mode) on FreeBSD and Solaris.</para>
-
- <para>
-<application moreinfo="none">FAM</application> (but not <application moreinfo="none">Gamin</application>)
-also works with NFS filesystems.
-On NFS clients <command moreinfo="none">fam</command> transparently forwards file monitoring
-requests to a peer <command moreinfo="none">fam</command> process on the NFS server.</para>
-
- <para>
-Installation and configuration of
-<application moreinfo="none">Gamin</application> or
-<application moreinfo="none">FAM</application> is beyond
-the scope of this document. This documentation presumes that Gamin or FAM is
-succesfully installed. Use the resources and tools on
-<application moreinfo="none">Gamin</application>'s or
-<application moreinfo="none">FAM</application>'s web site
-for assistance with setting them up.
-Systems that use GNOME or KDE desktops already have
-<application moreinfo="none">FAM</application> or
-<application moreinfo="none">Gamin</application>
-installed, as
-<application moreinfo="none">FAM</application> or <application moreinfo="none">Gamin</application>
-is used by the current versions of both desktops.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><literal moreinfo="none">IDLE</literal> IMAP capability</term>
+ <term><literal>IDLE</literal> IMAP capability</term>
<listitem>
<para>
-<literal moreinfo="none">IDLE</literal>
+<literal>IDLE</literal>
must be listed in the
<envar>IMAP_CAPABILITY</envar>
-setting in the <filename moreinfo="none">@sysconfdir@/imapd</filename>
+setting in the <filename>@sysconfdir@/imapd</filename>
configuration file.</para>
</listitem>
</varlistentry>
@@ -242,7 +173,7 @@ configuration file.</para>
<term><envar>IMAP_USELOCKS</envar></term>
<listitem>
<para>
-This setting in <filename moreinfo="none">@sysconfdir@/imapd</filename>
+This setting in <filename>@sysconfdir@/imapd</filename>
must be enabled.
This setting uses dot-lock files to synchronize updates to folder indexes
between multiple IMAP clients that have the same folder opened.</para>
@@ -256,36 +187,20 @@ daemon.</para>
<varlistentry>
<term>An IMAP mail client that fully supports the
-<literal moreinfo="none">IDLE</literal>
+<literal>IDLE</literal>
protocol extension.</term>
<listitem>
<para>
-Of course, an IMAP client that supports the <literal moreinfo="none">IDLE</literal>
+Of course, an IMAP client that supports the <literal>IDLE</literal>
protocol extension is required.
-At press time the status and extent of <literal moreinfo="none">IDLE</literal> support
+At press time the status and extent of <literal>IDLE</literal> support
in most IMAP mail clients is not known.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><envar>IMAP_ENHANCEDIDLE</envar></term>
<listitem>
<para>
-This setting in <filename moreinfo="none">@sysconfdir@/imapd</filename>
-actually enables concurrent realtime folder status updates using the
-<literal moreinfo="none">IDLE</literal> extension.
-Note that it is possible to enable the <literal moreinfo="none">IDLE</literal> extension
-even if
-<application moreinfo="none">FAM</application> or
-<application moreinfo="none">Gamin</application>
-is not available, or without
-enabling either the <envar>IMAP_USELOCKS</envar> and/or
-<envar>IMAP_ENHANCEDIDLE</envar> settings.
-The resulting consequences are described are as follows:</para>
-
- <orderedlist inheritnum="ignore" continuation="restarts">
- <listitem>
- <para>
Without <envar>IMAP_USERLOCKS</envar> there exists a small possibility
that multiple mail clients will receive a slightly inconsistent folder index
if both clients try to update the contents of the folder at the same time.
@@ -294,17 +209,13 @@ downloading the same message twice from the server, and caching it incorrectly
in the local cache (if the IMAP client caches message contents).
Clearing the local message cache will quickly eliminate any residual
confusion that results from this situation.</para>
- </listitem>
- <listitem>
- <para>
-Without <application moreinfo="none">FAM</application> or <application moreinfo="none">Gamin</application>, and
-<envar>IMAP_ENHANCEDIDLE</envar> set, the
-<application moreinfo="none">Courier</application> IMAP server will
+
+ <para>
+Without inotify kernel interface, the
+<application>Courier</application> IMAP server will
manually check for changes to the folder's contents every 60 seconds,
in IDLE mode (instead of in real time).
</para>
- </listitem>
- </orderedlist>
</listitem>
</varlistentry>
</variablelist>
@@ -321,8 +232,8 @@ commands.
The following instructions describe the procedure for connecting to the
IMAP server, and manually issuing IMAP protocol commands, as if they
originate from an IMAP client.
-The following instructions use "<literal moreinfo="none">C:</literal>" to indicate IMAP
-client commands that must be entered, and "<literal moreinfo="none">S:</literal>" to
+The following instructions use "<literal>C:</literal>" to indicate IMAP
+client commands that must be entered, and "<literal>S:</literal>" to
indicate the expected replies from the server.</para>
<note>
@@ -343,7 +254,7 @@ Open two or three terminal windows.
In each window, connect to the IMAP server, and enter IDLE mode:
</para>
<programlisting format="linespecific">
-S:* OK Courier-IMAP ready. Copyright 1998-2002 Double Precision, Inc.
+S:* OK Courier-IMAP ready. Copyright 1998-2021 Double Precision, Inc.
See COPYING for distribution information.
C:a login <replaceable>userid</replaceable> <replaceable>password</replaceable>
S:a OK LOGIN Ok.
@@ -360,11 +271,11 @@ S:+ entering ENHANCED idle mode
</programlisting>
<note>
<para>
-The default <application moreinfo="none">Courier</application> IMAP server
+The default <application>Courier</application> IMAP server
configuration permits a maximum of four
connections from the same IP address.
It may be necessary to adjust this setting in
-<filename moreinfo="none">@sysconfdir@/imapd</filename>
+<filename>@sysconfdir@/imapd</filename>
for the duration of this test.</para>
</note>
@@ -374,14 +285,7 @@ for the duration of this test.</para>
<para>
The last message from the server must be "entering ENHANCED idle mode".
Otherwise, it means that some of the necessary prerequisites have not been
-met.
-Verify that <application moreinfo="none">FAM</application>
-or <application moreinfo="none">Gamin</application> was set up prior to installing
-The <application moreinfo="none">Courier</application> IMAP server
-(use <citerefentry><refentrytitle>ldd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-to verify that the <command moreinfo="none">imapd</command> executable is linked with
-the <filename moreinfo="none">libfam</filename> library), and verify the settings in the
-<filename moreinfo="none">@sysconfdir@/imapd</filename>.</para>
+met.</para>
</listitem>
<listitem>
@@ -390,7 +294,7 @@ Open another terminal window, connect to the server, and modify the flags
of one of the messages:</para>
<programlisting format="linespecific">
-S:* OK Courier-IMAP ready. Copyright 1998-2002 Double Precision, Inc.
+S:* OK Courier-IMAP ready. Copyright 1998-2021 Double Precision, Inc.
See COPYING for distribution information.
C:a login <replaceable>userid</replaceable> <replaceable>password</replaceable>
S:a OK LOGIN Ok.
@@ -410,14 +314,11 @@ a OK STORE completed.
<listitem>
<para>
-The last command sets the <literal moreinfo="none">\Deleted</literal> flag on the first
+The last command sets the <literal>\Deleted</literal> flag on the first
message in the folder.
Immediately after entering the last command,
-"<literal moreinfo="none">* 1 FETCH (FLAGS (\Deleted))</literal>" should also appear
+"<literal>* 1 FETCH (FLAGS (\Deleted))</literal>" should also appear
in all other terminal windows.
-On systems where <application moreinfo="none">FAM</application> uses the fall-back polling
-mode this response may appear after a brief delay of a few seconds.
-The delay should never exceed 15-20 seconds.
</para>
</listitem>
@@ -425,9 +326,9 @@ The delay should never exceed 15-20 seconds.
<para>
Verify that all terminal windows reliably receive folder status updates in
real time by alternatively entering the commands
-"<literal moreinfo="none">a STORE 1 -FLAGS (\Deleted)</literal>"
+"<literal>a STORE 1 -FLAGS (\Deleted)</literal>"
and
-"<literal moreinfo="none">a STORE 1 +FLAGS (\Deleted)</literal>",
+"<literal>a STORE 1 +FLAGS (\Deleted)</literal>",
to toggle the deleted flag on the first message.
Observe that the message is received by all terminal windows quickly,
and reliably.</para>
@@ -435,8 +336,8 @@ and reliably.</para>
<listitem>
<para>
-With the <literal moreinfo="none">\Deleted</literal> flag set on the first message,
-enter the <command moreinfo="none">EXPUNGE</command> command, which removes the deleted
+With the <literal>\Deleted</literal> flag set on the first message,
+enter the <command>EXPUNGE</command> command, which removes the deleted
message from the folder:</para>
<programlisting format="linespecific">
@@ -450,7 +351,7 @@ S:a OK EXPUNGE completed
<para>
The lines that begin with the "*" character should also appear in all other
terminal windows (depending on the initial folder state one of the terminal
-windows may have a different <literal moreinfo="none">RECENT</literal> message, which is
+windows may have a different <literal>RECENT</literal> message, which is
fine).
</para>
</listitem>
@@ -470,13 +371,13 @@ messages should appear in every terminal window:
<para>
The numbers in these messages may be different, depending upon the
initial contents of the test mail folder.
-One of the terminal windows should have a different <literal moreinfo="none">RECENT</literal>
+One of the terminal windows should have a different <literal>RECENT</literal>
count,
and one of the terminal windows should include a
-<literal moreinfo="none">\Recent</literal> flag in the untagged
-<literal moreinfo="none">FLAGS</literal> message.
+<literal>\Recent</literal> flag in the untagged
+<literal>FLAGS</literal> message.
These difference are acceptable; the important thing is to make sure that
-all terminal windows have the same <literal moreinfo="none">EXISTS</literal> message.
+all terminal windows have the same <literal>EXISTS</literal> message.
</para>
</listitem>
</orderedlist>
@@ -487,7 +388,7 @@ all terminal windows have the same <literal moreinfo="none">EXISTS</literal> mes
<para>
<ulink url="authlib.html"><citerefentry><refentrytitle>authlib</refentrytitle><manvolnum>7</manvolnum></citerefentry></ulink>,
-
+
<ulink url="userdb.html"><citerefentry><refentrytitle>userdb</refentrytitle><manvolnum>8</manvolnum></citerefentry></ulink></para>
</refsect1>
diff --git a/imap/imaplogin.c b/imap/imaplogin.c
index 8e8289e..7b407fa 100644
--- a/imap/imaplogin.c
+++ b/imap/imaplogin.c
@@ -481,7 +481,7 @@ int main(int argc, char **argv)
writes("* OK [CAPABILITY ");
imapcapability();
writes("] Courier-IMAP ready. "
- "Copyright 1998-2019 Double Precision, Inc. "
+ "Copyright 1998-2021 Double Precision, Inc. "
"See COPYING for distribution information.\r\n");
fprintf(stderr, "DEBUG: Connection, ip=[%s], port=[%s]\n", ip, port);
writeflush();
diff --git a/imap/pop3d-ssl.dist.in.git b/imap/pop3d-ssl.dist.in.git
index dd352cd..4ed7944 100644
--- a/imap/pop3d-ssl.dist.in.git
+++ b/imap/pop3d-ssl.dist.in.git
@@ -313,6 +313,13 @@ TLS_VERIFYPEER=NONE
TLS_CACHEFILE=@localstatedir@/couriersslimapcache
TLS_CACHESIZE=524288
+##NAME: TLS_ALPN:0
+#
+# Application level protocol negotiation should be enabled by default, and
+# should be commented out only in case of compatibility issues.
+
+TLS_ALPN=pop3
+
##NAME: MAILDIRPATH:0
#
# MAILDIRPATH - directory name of the maildir directory.
diff --git a/imap/smap.c b/imap/smap.c
index 6f43a6e..56690b3 100644
--- a/imap/smap.c
+++ b/imap/smap.c
@@ -4215,9 +4215,7 @@ void smap()
if (strcmp(p, "IDLE") == 0)
{
- if ((p=getenv("IMAP_ENHANCEDIDLE")) == NULL
- || !atoi(p)
- || imapenhancedidle())
+ if (imapenhancedidle())
imapidle();
read_timeout(60);
diff --git a/maildir/.gitignore b/maildir/.gitignore
index 9939f4f..7fab6ba 100644
--- a/maildir/.gitignore
+++ b/maildir/.gitignore
@@ -9,7 +9,6 @@
/mailbot.h
/maildir.5
/maildir.html
-/maildir.libdeps
/maildiracl
/maildiracl.1
/maildiracl.1.in
@@ -29,6 +28,9 @@
/maildirquota.7
/maildirquota.html
/maildirsharedrc.h
+/maildirwatch
+/maildirwatch.1
+/maildirwatch.html
/quotawarnmsg.h
/sharedindexinstall
/sharedindexsplit
diff --git a/maildir/Makefile.am b/maildir/Makefile.am
index 60f0681..54c0602 100644
--- a/maildir/Makefile.am
+++ b/maildir/Makefile.am
@@ -12,6 +12,7 @@ DOCS= deliverquota.html.in deliverquota.8.in \
maildir.html maildir.5 \
maildiracl.html.in \
maildirmake.html.in maildirmake.1.in maildirquota.html maildirquota.7 \
+ maildirwatch.html maildirwatch.1 \
maildirkw.html maildirkw.1
if HAVE_SGML
@@ -23,7 +24,7 @@ BUILT_SOURCES=maildirsharedrc.h maildirfilterconfig.h quotawarnmsg.h \
endif
noinst_DATA=deliverquota.html maildirmake.html deliverquota.8 maildirmake.1 \
- maildiracl.html maildiracl.1 maildir.libdeps
+ maildiracl.html maildiracl.1
libmaildir_la_SOURCES=autoresponse.c autoresponse.h \
maildiraclt.c maildiraclt.h \
@@ -49,7 +50,7 @@ libmaildir_la_SOURCES=autoresponse.c autoresponse.h \
maildirwatch.c maildirwatch.h loginexec.c loginexec.h
noinst_PROGRAMS=deliverquota maildirmake testmaildirfilter maildirkwtest \
- maildirkw maildiracl maildiraclttest testmaildirsearch
+ maildirkw maildiracl maildiraclttest testmaildirsearch maildirwatch
deliverquota_SOURCES=deliverquota.c
deliverquota_DEPENDENCIES=libmaildir.la ../rfc822/librfc822.la \
@@ -77,24 +78,28 @@ maildirkwtest_DEPENDENCIES=libmaildir.la
maildirkwtest_LDFLAGS=-static
maildirkw_SOURCES=maildirkw.c
-maildirkw_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la \
- `cat maildir.libdeps`
+maildirkw_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la
maildirkw_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la maildir.libdeps
+ ../numlib/libnumlib.la
maildirkw_LDFLAGS=-static
+maildirwatch_SOURCES=maildirwatchprog.c
+maildirwatch_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la
+maildirwatch_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
+ ../numlib/libnumlib.la
+maildirwatch_LDFLAGS=-static
+
maildiracl=maildiracl.c
-maildiracl_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la \
- `cat maildir.libdeps`
+maildiracl_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la
maildiracl_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la maildir.libdeps
+ ../numlib/libnumlib.la
maildiracl_LDFLAGS=-static
maildiraclttest_SOURCES=testmaildiraclt.c
maildiraclttest_LDADD=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la `cat maildir.libdeps`
+ ../numlib/libnumlib.la
maildiraclttest_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la maildir.libdeps
+ ../numlib/libnumlib.la
maildiraclttest_LDFLAGS=-static
testmaildirsearch_SOURCES=testmaildirsearch.c
@@ -130,11 +135,9 @@ autoresponsequota.h: config.status
quotawarnmsg.h: config.status
echo '#define QUOTAWARNMSG "$(sysconfdir)/quotawarnmsg"' >quotawarnmsg.h
-maildir.libdeps: config.status
- echo @LIBFAM@ >maildir.libdeps
clean-local:
- rm -rf maildir.libdeps testmd
+ rm -rf testmd
check-am:
@SHELL@ $(srcdir)/testsuite 2>&1 | cmp - $(srcdir)/testsuite.txt
@@ -188,6 +191,12 @@ maildirkw.html: maildirkw.sgml ../docbook/sgml2html
maildirkw.1: maildirkw.sgml ../docbook/sgml2man
../docbook/sgml2man maildirkw.sgml maildirkw.1 "--stringparam man.base.url.for.relative.links http://www.courier-mta.org/"
+maildirwatch.html: maildirwatch.sgml ../docbook/sgml2html
+ ../docbook/sgml2html maildirwatch.sgml maildirwatch.html
+
+maildirwatch.1: maildirwatch.sgml ../docbook/sgml2man
+ ../docbook/sgml2man maildirwatch.sgml maildirwatch.1
+
endif
deliverquota.html: deliverquota.html.in
diff --git a/maildir/configure.ac b/maildir/configure.ac
index d35162a..fdbf576 100644
--- a/maildir/configure.ac
+++ b/maildir/configure.ac
@@ -74,26 +74,9 @@ AX_COURIER_UNICODE_CXXFLAGS
AC_SUBST(COURIER_UNICODE_CXXFLAGS)
dnl Checks for library functions.
-AC_CHECK_HEADER(fam.h, :, :)
AC_CHECK_FUNCS(symlink readlink strcasecmp utime utimes)
-AC_CHECK_LIB(fam, FAMOpen, [
- LIBFAM=-lfam
- AC_DEFINE_UNQUOTED(HAVE_FAM,1,
- [ Whether libfam.a is available ])
-
- AC_CHECK_HEADER(fam.h, : , [
-AC_MSG_WARN([[The development header files and libraries for fam,]])
-AC_MSG_WARN([[the File Alteration Monitor, are not installed.]])
-AC_MSG_WARN([[You appear to have the FAM runtime libraries installed,]])
-AC_MSG_WARN([[so you need to simply install the additional development]])
-AC_MSG_WARN([[package for your operating system.]])
-AC_MSG_ERROR([[FAM development libraries not found.]]) ]
- )
- ])
-
-AC_SUBST(LIBFAM)
-
-echo "$LIBFAM" >maildir.libdeps
+
+AC_CHECK_FUNCS(inotify_init inotify_init1)
AC_CACHE_CHECK([for missing gethostname prototype],maildir_cv_SYS_GETHOSTNAME,
diff --git a/maildir/maildirmake.sgml b/maildir/maildirmake.sgml
index e8f4d70..57aebd6 100644
--- a/maildir/maildirmake.sgml
+++ b/maildir/maildirmake.sgml
@@ -584,6 +584,7 @@ Updating /home/mrsam/.mailfilter</programlisting>
<ulink url="maildir.html"><citerefentry><refentrytitle>maildir</refentrytitle><manvolnum>5</manvolnum></citerefentry></ulink>,
<ulink url="maildiracl.html"><citerefentry><refentrytitle>maildiracl</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
<ulink url="maildirkw.html"><citerefentry><refentrytitle>maildirkw</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
+<ulink url="maildirwatch.html"><citerefentry><refentrytitle>maildirwatch</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
<ulink url="maildrop.html"><citerefentry><refentrytitle>maildrop</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
<ulink url="maildirquota.html"><citerefentry><refentrytitle>maildirquota</refentrytitle><manvolnum>7</manvolnum></citerefentry></ulink>,
<ulink url="deliverquota.html"><citerefentry><refentrytitle>deliverquota</refentrytitle><manvolnum>8</manvolnum></citerefentry></ulink>,
diff --git a/maildir/maildirwatch.c b/maildir/maildirwatch.c
index 36b85ab..4faac13 100644
--- a/maildir/maildirwatch.c
+++ b/maildir/maildirwatch.c
@@ -1,5 +1,5 @@
/*
-** Copyright 2002-2009 Double Precision, Inc.
+** Copyright 2002-2021 Double Precision, Inc.
** See COPYING for distribution information.
*/
@@ -14,25 +14,16 @@
#include <errno.h>
#include <signal.h>
#include <sys/signal.h>
+#if HAVE_INOTIFY_INIT
+#include <sys/inotify.h>
+#include <poll.h>
+#include <fcntl.h>
+#endif
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
-
-#if HAVE_FAM
-static struct maildirwatch_fam *maildirwatch_currentfam;
-
-static void alarm_handler(int signum)
-{
- static const char msg[]=
- "Timeout initializing the FAM library. Your FAM library is broken.\n";
-
- (void)write(2, msg, sizeof(msg)-1);
- kill(getpid(), SIGKILL);
-}
-#endif
-
struct maildirwatch *maildirwatch_alloc(const char *maildir)
{
char wd[PATH_MAX];
@@ -60,38 +51,36 @@ struct maildirwatch *maildirwatch_alloc(const char *maildir)
strcat(strcpy(w->maildir, wd), maildir);
-#if HAVE_FAM
- if (!maildirwatch_currentfam)
- {
- if ((maildirwatch_currentfam
- =malloc(sizeof(*maildirwatch_currentfam))) != NULL)
- {
- maildirwatch_currentfam->broken=0;
- maildirwatch_currentfam->refcnt=0;
+#if HAVE_INOTIFY_INIT
+#if HAVE_INOTIFY_INIT1
+#ifdef IN_CLOEXEC
+#else
+#undef HAVE_INOTIFY_INIT1
+#endif
+#ifdef IN_NONBLOCK
+#else
+#undef HAVE_INOTIFY_INIT1
+#endif
+#endif
- signal(SIGALRM, alarm_handler);
- alarm(15);
- if (FAMOpen(&maildirwatch_currentfam->fc) < 0)
- {
- errno=EIO;
- free(maildirwatch_currentfam);
- maildirwatch_currentfam=NULL;
- }
- alarm(0);
- signal(SIGALRM, SIG_DFL);
- }
- }
+#if HAVE_INOTIFY_INIT1
+ w->inotify_fd=inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+#else
+ w->inotify_fd=inotify_init();
- if (!maildirwatch_currentfam)
+ if (w->inotify_fd >= 0 &&
+ (fcntl(w->inotify_fd, F_SETFL, O_NONBLOCK) < 0 ||
+ fcntl(w->inotify_fd, F_SETFD, FD_CLOEXEC)))
{
- free(w->maildir);
- free(w);
- w=NULL;
+ close(w->inotify_fd);
+ w->inotify_fd=-1;
}
- else
+#endif
+
+ if (w->inotify_fd < 0)
{
- w->fam=maildirwatch_currentfam;
- ++w->fam->refcnt;
+ maildirwatch_free(w);
+ w=NULL;
}
#endif
return w;
@@ -99,25 +88,10 @@ struct maildirwatch *maildirwatch_alloc(const char *maildir)
void maildirwatch_free(struct maildirwatch *w)
{
-#if HAVE_FAM
- if (--w->fam->refcnt == 0)
+#if HAVE_INOTIFY_INIT
+ if (w->inotify_fd >= 0)
{
- w->fam->broken=1;
- if (maildirwatch_currentfam &&
- maildirwatch_currentfam->broken)
- {
- /*
- ** Last reference to the current FAM connection,
- ** keep it active.
- */
-
- w->fam->broken=0;
- }
- else /* Some other connection, with no more refs */
- {
- FAMClose(&w->fam->fc);
- free(w->fam);
- }
+ close(w->inotify_fd);
}
#endif
@@ -127,125 +101,128 @@ void maildirwatch_free(struct maildirwatch *w)
void maildirwatch_cleanup()
{
-#if HAVE_FAM
-
- if (maildirwatch_currentfam && maildirwatch_currentfam->refcnt == 0)
- {
- FAMClose(&maildirwatch_currentfam->fc);
- free(maildirwatch_currentfam);
- maildirwatch_currentfam=NULL;
- }
-#endif
}
-#if HAVE_FAM
-static void maildirwatch_fambroken(struct maildirwatch *w)
-{
- w->fam->broken=1;
-
- if (maildirwatch_currentfam && maildirwatch_currentfam->broken)
- maildirwatch_currentfam=NULL;
- /* Broke the current connection, create another one, next time. */
-
-}
+#if HAVE_INOTIFY_INIT
/*
-** If the current connection is marked as broken, try to reconnect.
+** Poll the inotify file descriptor. Returns 0 on timeout, non-0 if
+** the inotify file descriptor becomes ready before the timeout expires.
*/
-static void maildirwatch_famunbreak(struct maildirwatch *w)
+static int poll_inotify(struct maildirwatch *w)
{
- struct maildirwatch *cpy;
-
- if (!w->fam->broken)
- return;
+ time_t now2;
- if ((cpy=maildirwatch_alloc(w->maildir)) == NULL)
- return;
+ int rc;
- /*
- ** maildirwatch_alloc succeeds only with a good connection.
- ** If this is the last reference to the broken connection, close it.
- */
+ struct pollfd pfd;
- if (--w->fam->refcnt == 0)
+ while (w->now < w->timeout)
{
- FAMClose(&w->fam->fc);
- free(w->fam);
- }
+ pfd.fd=w->inotify_fd;
+ pfd.events=POLLIN;
- w->fam=cpy->fam;
- ++w->fam->refcnt;
+ rc=poll(&pfd, 1, (w->timeout - w->now)*1000);
- maildirwatch_free(cpy);
-}
+ now2=time(NULL);
-static int waitEvent(struct maildirwatch *w)
-{
- int fd;
- fd_set r;
- struct timeval tv;
- time_t now2;
+ if (now2 < w->now)
+ return 1; /* System clock changed */
- int rc;
+ w->now=now2;
- while ((rc=FAMPending(&w->fam->fc)) == 0)
- {
- if (w->now >= w->timeout)
- return 0;
+ if (rc && pfd.revents & POLLIN)
+ return 1;
+ }
- fd=FAMCONNECTION_GETFD(&w->fam->fc);
+ return 0;
+}
- FD_ZERO(&r);
- FD_SET(fd, &r);
+/*
+** read() inotify_events from the inotify handler.
+*/
- tv.tv_sec= w->timeout - w->now;
- tv.tv_usec=0;
+static int read_inotify_events(int fd,
+ void (*callback)(struct inotify_event *ie,
+ void *arg),
+ void *arg)
+{
+ char inotify_buffer[sizeof(struct inotify_event)+NAME_MAX+1];
+ int l;
+ char *iecp;
- select(fd+1, &r, NULL, NULL, &tv);
- now2=time(NULL);
+ l=read(fd, inotify_buffer, sizeof(inotify_buffer));
- if (now2 < w->now)
- return 0; /* System clock changed */
+ if (l < 0 &&
+ (errno == EAGAIN || errno == EWOULDBLOCK))
+ l=0; /* Non-blocking socket timeout */
- w->now=now2;
+ if (l < 0)
+ {
+ fprintf(stderr, "ERR:inotify read: %s\n", strerror(errno));
+ return -1;
}
- return rc;
+ iecp=inotify_buffer;
+
+ while (iecp < inotify_buffer+l)
+ {
+ struct inotify_event *ie=
+ (struct inotify_event *)iecp;
+
+ iecp += sizeof(struct inotify_event)+ie->len;
+
+ (*callback)(ie, arg);
+ }
+ return 0;
}
-#endif
+struct unlock_info {
+ int handle;
+ int removed;
+ int deleted;
+};
-int maildirwatch_unlock(struct maildirwatch *w, int nseconds)
+static void unlock_handler(struct inotify_event *ie,
+ void *arg)
{
-#if HAVE_FAM
- FAMRequest fr;
- FAMEvent fe;
- int cancelled=0;
- char *p;
+ struct unlock_info *ui=(struct unlock_info *)arg;
- if (w->fam->broken)
+ if (ie->wd == ui->handle)
{
- errno=EIO;
- return -1;
+ if (ie->mask & IN_DELETE_SELF)
+ ui->removed=1;
+
+ if (ie->mask & IN_IGNORED)
+ ui->deleted=1;
}
+}
+#endif
- p=malloc(strlen(w->maildir)+ sizeof("/" WATCHDOTLOCK));
+static int do_maildirwatch_unlock(struct maildirwatch *w, int nseconds,
+ const char *p)
+{
+#if HAVE_INOTIFY_INIT
+ int cancelled=0;
- if (!p)
- return -1;
+ struct unlock_info ui;
- strcat(strcpy(p, w->maildir), "/" WATCHDOTLOCK);
+ ui.handle=inotify_add_watch(w->inotify_fd, p, IN_DELETE_SELF);
+ ui.removed=0;
+ ui.deleted=0;
- errno=EIO;
- if (FAMMonitorFile(&w->fam->fc, p, &fr, NULL) < 0)
+ if (ui.handle < 0)
{
- free(p);
- fprintf(stderr, "ERR:FAMMonitorFile: %s\n",
- strerror(errno));
+ if (errno == ENOENT)
+ {
+ /* Doesn't exist anymore, that's ok */
+ return 0;
+ }
+
+ fprintf(stderr, "ERR: %s: %s\n", p, strerror(errno));
return -1;
}
- free(p);
if (nseconds < 0)
nseconds=0;
@@ -254,69 +231,74 @@ int maildirwatch_unlock(struct maildirwatch *w, int nseconds)
w->timeout=w->now + nseconds;
- for (;;)
+ do
{
- if (waitEvent(w) != 1)
- {
- errno=EIO;
+ errno=ETIMEDOUT;
- if (!cancelled && FAMCancelMonitor(&w->fam->fc, &fr) == 0)
+ if (!poll_inotify(w))
+ {
+ if (!cancelled)
{
+ /*
+ ** Timeout on the lock, cancel the inotify.
+ */
w->timeout=w->now+15;
cancelled=1;
+ inotify_rm_watch(w->inotify_fd, ui.handle);
continue;
}
- if (!cancelled)
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
+ fprintf(stderr, "ERR:inotify timeout: %s\n",
+ strerror(errno));
- maildirwatch_fambroken(w);
break;
}
- errno=EIO;
+ read_inotify_events(w->inotify_fd, unlock_handler, &ui);
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
+ if (ui.removed && !cancelled)
{
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- break;
+ w->timeout=w->now+15;
+ cancelled=1;
+ inotify_rm_watch(w->inotify_fd, ui.handle);
}
- if (fe.fr.reqnum != fr.reqnum)
- continue;
+ /* We don't terminate the loop until we get IN_IGNORED */
- if (fe.code == FAMDeleted && !cancelled)
- {
- errno=EIO;
- if (FAMCancelMonitor(&w->fam->fc, &fr) == 0)
- {
- w->timeout=w->now+15;
- cancelled=1;
- continue;
- }
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- break;
- }
+ } while (!ui.deleted);
- if (fe.code == FAMAcknowledge)
- break;
- }
+ return ui.removed;
+#else
- if (w->fam->broken)
- return -1;
+ int n;
+ for (n=0; n<nseconds; ++n)
+ {
+ if (access(p, 0))
+ return 1;
+ sleep(1);
+ }
return 0;
-#else
- return -1;
#endif
}
-#define DIRCNT 3
+int maildirwatch_unlock(struct maildirwatch *w, int nseconds)
+{
+ char *p;
+ int rc;
+
+ p=malloc(strlen(w->maildir)+ sizeof("/" WATCHDOTLOCK));
+
+ if (!p)
+ return -1;
+
+ strcat(strcpy(p, w->maildir), "/" WATCHDOTLOCK);
+
+ rc=do_maildirwatch_unlock(w, nseconds, p);
+
+ free(p);
+ return rc;
+}
int maildirwatch_start(struct maildirwatch *w,
struct maildirwatch_contents *mc)
@@ -326,15 +308,7 @@ int maildirwatch_start(struct maildirwatch *w,
time(&w->now);
w->timeout = w->now + 60;
-#if HAVE_FAM
-
- maildirwatch_famunbreak(w);
-
- if (w->fam->broken)
- {
- errno=EIO;
- return (1);
- }
+#if HAVE_INOTIFY_INIT
{
char *s=malloc(strlen(w->maildir)
@@ -345,153 +319,58 @@ int maildirwatch_start(struct maildirwatch *w,
strcat(strcpy(s, w->maildir), "/new");
- mc->endexists_received=0;
- mc->ack_received=0;
- mc->cancelled=0;
-
- errno=EIO;
+ mc->handles[0]=inotify_add_watch(w->inotify_fd, s,
+ IN_CREATE |
+ IN_DELETE |
+ IN_MOVED_FROM |
+ IN_MOVED_TO);
- if (FAMMonitorDirectory(&w->fam->fc, s, &mc->new_req, NULL) < 0)
- {
- fprintf(stderr, "ERR:"
- "FAMMonitorDirectory(%s) failed: %s\n",
- s, strerror(errno));
- free(s);
- errno=EIO;
- return (-1);
- }
strcat(strcpy(s, w->maildir), "/cur");
- errno=EIO;
-
- if (FAMMonitorDirectory(&w->fam->fc, s, &mc->cur_req, NULL) < 0)
- {
- fprintf(stderr, "ERR:"
- "FAMMonitorDirectory(%s) failed: %s\n",
- s, strerror(errno));
- errno=EIO;
-
- if (FAMCancelMonitor(&mc->w->fam->fc, &mc->new_req) < 0)
- {
- free(s);
- maildirwatch_fambroken(w);
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- errno=EIO;
- return (-1);
- }
- mc->cancelled=1;
- mc->ack_received=2;
- }
+ mc->handles[1]=inotify_add_watch(w->inotify_fd, s,
+ IN_CREATE |
+ IN_DELETE |
+ IN_MOVED_FROM |
+ IN_MOVED_TO);
strcat(strcpy(s, w->maildir), "/" KEYWORDDIR);
- errno=EIO;
-
- if (FAMMonitorDirectory(&w->fam->fc, s,
- &mc->courierimapkeywords_req, NULL)<0)
- {
- fprintf(stderr, "ERR:"
- "FAMMonitorDirectory(%s) failed: %s\n",
- s, strerror(errno));
-
- errno=EIO;
-
- if (FAMCancelMonitor(&mc->w->fam->fc, &mc->new_req)<0)
- {
- free(s);
- maildirwatch_fambroken(w);
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- errno=EIO;
- return (-1);
- }
-
- errno=EIO;
-
- if (FAMCancelMonitor(&mc->w->fam->fc, &mc->cur_req)<0)
- {
- free(s);
- maildirwatch_fambroken(w);
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- errno=EIO;
- return (-1);
- }
-
- mc->cancelled=1;
- mc->ack_received=1;
- }
+ mc->handles[2]=inotify_add_watch(w->inotify_fd, s,
+ IN_CREATE |
+ IN_DELETE |
+ IN_MOVED_FROM |
+ IN_MOVED_TO);
free(s);
}
+
return 0;
#else
return 1;
#endif
}
-#define CANCEL(ww) \
- errno=EIO; if (FAMCancelMonitor(&w->fam->fc, \
- &ww->new_req) || \
- FAMCancelMonitor(&w->fam->fc, \
- &ww->cur_req) || \
- FAMCancelMonitor(&w->fam->fc, \
- &ww->courierimapkeywords_req)) \
- {\
- maildirwatch_fambroken(w); \
- fprintf(stderr, \
- "ERR:FAMCancelMonitor: %s\n", \
- strerror(errno)); \
- return (-1); \
- }
-
int maildirwatch_started(struct maildirwatch_contents *mc,
int *fdret)
{
-#if HAVE_FAM
- struct maildirwatch *w=mc->w;
+#if HAVE_INOTIFY_INIT
+ int n;
+#endif
- if (w->fam->broken)
- return (1);
+ *fdret= -1;
- *fdret=FAMCONNECTION_GETFD(&w->fam->fc);
+#if HAVE_INOTIFY_INIT
- while (FAMPending(&w->fam->fc))
+ for (n=0; n<sizeof(mc->handles)/sizeof(mc->handles[0]); ++n)
{
- FAMEvent fe;
-
- errno=EIO;
+ if (mc->handles[n] < 0)
+ return -1;
+ }
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
- {
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- return (-1);
- }
+ *fdret=mc->w->inotify_fd;
- switch (fe.code) {
- case FAMDeleted:
- if (!mc->cancelled)
- {
- mc->cancelled=1;
- CANCEL(mc);
- }
- break;
- case FAMAcknowledge:
- if (++mc->ack_received >= DIRCNT)
- return -1;
- break;
- case FAMEndExist:
- ++mc->endexists_received;
- break;
- default:
- break;
- }
- }
+ return 1;
- return (mc->endexists_received >= DIRCNT && mc->ack_received == 0);
#else
*fdret= -1;
@@ -499,6 +378,31 @@ int maildirwatch_started(struct maildirwatch_contents *mc,
#endif
}
+#if HAVE_INOTIFY_INIT
+
+struct check_info {
+ struct maildirwatch_contents *mc;
+ int *changed;
+ int handled;
+};
+
+static void check_handler(struct inotify_event *ie,
+ void *arg)
+{
+ struct check_info *ci=(struct check_info *)arg;
+ int n;
+
+ ci->handled=1;
+
+ for (n=0; n<sizeof(ci->mc->handles)/sizeof(ci->mc->handles[0]); ++n)
+ {
+ if (ie->wd == ci->mc->handles[n])
+ *ci->changed=1;
+ }
+
+}
+#endif
+
int maildirwatch_check(struct maildirwatch_contents *mc,
int *changed,
int *fdret,
@@ -506,6 +410,12 @@ int maildirwatch_check(struct maildirwatch_contents *mc,
{
struct maildirwatch *w=mc->w;
time_t curTime;
+#if HAVE_INOTIFY_INIT
+ struct check_info ci;
+
+ ci.mc=mc;
+ ci.changed=changed;
+#endif
*changed=0;
*fdret=-1;
@@ -516,100 +426,104 @@ int maildirwatch_check(struct maildirwatch_contents *mc,
w->timeout=curTime; /* System clock changed */
w->now=curTime;
-#if HAVE_FAM
+ *timeout=60;
- if (!w->fam->broken)
+#if HAVE_INOTIFY_INIT
+ if (maildirwatch_started(mc, fdret) > 0)
{
- *fdret=FAMCONNECTION_GETFD(&w->fam->fc);
-
- while (FAMPending(&w->fam->fc))
- {
- FAMEvent fe;
+ *timeout=60 * 60;
- errno=EIO;
+ *fdret=w->inotify_fd;
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
- {
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- return (-1);
- }
+ ci.handled=1;
- switch (fe.code) {
- case FAMDeleted:
- case FAMCreated:
- case FAMMoved:
- if (!mc->cancelled)
- {
- mc->cancelled=1;
- CANCEL(mc);
- }
- break;
- case FAMAcknowledge:
- ++mc->ack_received;
- default:
- break;
- }
+ while (ci.handled)
+ {
+ ci.handled=0;
+ read_inotify_events(w->inotify_fd, check_handler, &ci);
}
-
- *changed=mc->ack_received >= DIRCNT;
- *timeout=60 * 60;
- return 0;
}
#endif
- *timeout=60;
-
- if ( (*changed= w->now >= w->timeout) != 0)
- w->timeout = w->now + 60;
+ if (w->now >= w->timeout)
+ {
+ w->timeout = w->now + *timeout;
+ *changed=1;
+ }
return 0;
}
-void maildirwatch_end(struct maildirwatch_contents *mc)
+#if HAVE_INOTIFY_INIT
+
+struct end_info {
+ struct maildirwatch_contents *mc;
+ int unwatched;
+};
+
+static void end_handler(struct inotify_event *ie,
+ void *arg)
{
-#if HAVE_FAM
- struct maildirwatch *w=mc->w;
+ struct end_info *ei=(struct end_info *)arg;
+ int n;
- if (!w->fam->broken)
+ for (n=0; n<sizeof(ei->mc->handles)/sizeof(ei->mc->handles[0]); ++n)
{
- if (!mc->cancelled)
+ if (ie->wd == ei->mc->handles[n] &&
+ ie->mask & IN_IGNORED)
{
- mc->cancelled=1;
-
-#define return(x)
- CANCEL(mc);
-#undef return
+ ei->mc->handles[n]=-1;
+ ei->unwatched=1;
}
}
+}
+
+static int maildir_end_unwatch(struct maildirwatch_contents *mc)
+{
+ int n;
- while (!w->fam->broken && mc->ack_received < DIRCNT)
+ for (n=0; n<sizeof(mc->handles)/sizeof(mc->handles[0]); ++n)
{
- FAMEvent fe;
+ if (mc->handles[n] >= 0)
+ {
+ inotify_rm_watch(mc->w->inotify_fd,
+ mc->handles[n]);
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
- time(&w->now);
- w->timeout=w->now + 15;
+void maildirwatch_end(struct maildirwatch_contents *mc)
+{
+#if HAVE_INOTIFY_INIT
+ struct end_info ei;
- errno=EIO;
+ time(&mc->w->now);
+ mc->w->timeout=mc->w->now + 15;
- if (waitEvent(w) != 1)
+ if (maildir_end_unwatch(mc)) /* Send the first inotify_rm_watch */
+ {
+ while (1)
{
- fprintf(stderr, "ERR:FAMPending: timeout\n");
- maildirwatch_fambroken(w);
- break;
- }
+ if (poll_inotify(mc->w) != 1)
+ {
+ fprintf(stderr, "ERR:inotify timeout: %s\n",
+ strerror(errno));
+ break;
+ }
- errno=EIO;
+ ei.mc=mc;
+ ei.unwatched=0;
+ read_inotify_events(mc->w->inotify_fd,
+ end_handler, &ei);
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
- {
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- break;
+ if (ei.unwatched)
+ {
+ /* Send the next inotify_rm_watch? */
+ if (!maildir_end_unwatch(mc))
+ break; /* Nope, all done! */
+ }
}
-
- if (fe.code == FAMAcknowledge)
- ++mc->ack_received;
}
#endif
}
diff --git a/maildir/maildirwatch.h b/maildir/maildirwatch.h
index 330baac..5f49623 100644
--- a/maildir/maildirwatch.h
+++ b/maildir/maildirwatch.h
@@ -1,7 +1,7 @@
#ifndef maildirwatch_h
#define maildirwatch_h
/*
-** Copyright 2002 Double Precision, Inc.
+** Copyright 2002-2021 Double Precision, Inc.
** See COPYING for distribution information.
*/
@@ -15,16 +15,9 @@ extern "C" {
#endif
/*
-** These function leverage libfam.a to watch for maildir changes.
-**
-** If libfam.a is not available, these functions are compiled to no-ops
+** These function use inotify to watch for maildir changes.
*/
-#if HAVE_FAM
-#include <fam.h>
-#endif
-
-
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
@@ -36,20 +29,11 @@ extern "C" {
#endif
#endif
-#if HAVE_FAM
-struct maildirwatch_fam {
- FAMConnection fc;
- int broken;
- unsigned refcnt;
-};
-
-#endif
-
struct maildirwatch {
char *maildir;
-#if HAVE_FAM
- struct maildirwatch_fam *fam;
+#if HAVE_INOTIFY_INIT
+ int inotify_fd;
#endif
time_t now;
time_t timeout;
@@ -79,27 +63,16 @@ int maildirwatch_unlock(struct maildirwatch *w, int nseconds);
struct maildirwatch_contents {
struct maildirwatch *w;
-#if HAVE_FAM
- FAMRequest new_req;
- FAMRequest cur_req;
- FAMRequest courierimapkeywords_req;
-
- unsigned short endexists_received;
- unsigned short ack_received;
-
- unsigned short cancelled;
-
+#if HAVE_INOTIFY_INIT
+ int handles[3];
#endif
-
};
/*
** maildirwatch_start() initiates the process of monitoring the maildir.
-** The monitoring process does not get started right away, since FAM needs
-** to acknowledge th monitoring requests first.
**
-** Returns: 0 - monitoring request sent.
-** 1 - FAM is not available, will fall back to 60 second polls.
+** Returns: 0 - monitoring started.
+** 1 - inotify not available, will fall back to 60 second polls.
** -1 - Fatal error.
*/
@@ -107,10 +80,11 @@ int maildirwatch_start(struct maildirwatch *p,
struct maildirwatch_contents *w);
/*
-** Check if FAM started monitoring yet.
+** Check the status of inotify monitoring.
**
** Returns: 1 - Monitoring has started, or we're in fallback mode.
** 0 - Not yet, *fdret is initialized to file descriptor to wait on.
+** (not used at this time).
** -1 - A fatal error occured, fall back to polling mode.
**
** maildirwatch_started() returns right away, without blocking.
@@ -127,7 +101,9 @@ int maildirwatch_started(struct maildirwatch_contents *w,
** -1 - Fatal error.
**
** *fdret and *timeout get initialized to the file descriptor to wait on,
-** and the requested timeout. *fdret may be negative in polling mode.
+** and the requested timeout. *fdret may be negative in polling mode, this
+** should be interpreted as: if *changed is not set, sleep for this period of
+** time.
*/
int maildirwatch_check(struct maildirwatch_contents *w,
diff --git a/maildir/maildirwatch.sgml b/maildir/maildirwatch.sgml
new file mode 100644
index 0000000..3787434
--- /dev/null
+++ b/maildir/maildirwatch.sgml
@@ -0,0 +1,114 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<!-- Copyright 2021 Double Precision, Inc. See COPYING for -->
+<!-- distribution information. -->
+<refentry id="maildirwatch">
+ <info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info>
+
+ <refmeta>
+ <refentrytitle>maildirwatch</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="manual">Double Precision, Inc.</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>maildirwatch</refname>
+ <refpurpose>wait for changes to a maildir</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command>maildirwatch</command>
+ <arg choice="req" rep="norepeat"><replaceable>maildir</replaceable></arg>
+ <arg choice="req" rep="norepeat"><replaceable>command</replaceable></arg>
+
+ <arg choice="opt" rep="repeat"><replaceable>argument</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="maildirwatch_description">
+ <title>DESCRIPTION</title>
+
+ <para>
+ <command>maildirwatch</command> repeatedly invokes the
+ <command><replaceable>command</replaceable></command>, with any optional
+ <command><replaceable>argument</replaceable></command>s, an external
+ command, as follows:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ The first time the
+ <command><replaceable>command</replaceable></command> gets executed
+ is immediately after
+ <command>maildirwatch</command> starts.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Subsequent invocatons of
+ <command><replaceable>command</replaceable></command> occur whenever
+ there <emphasis>may</emphasis> be some kind of a change
+ to the contents of the
+ <filename><replaceable>maildir</replaceable></filename>.
+ </para>
+
+ <para>
+ <command>maildirwatch</command> does not determine what, if anything,
+ changed in the
+ <filename><replaceable>maildir</replaceable></filename>.
+ <command>maildirwatch</command>
+ might
+ occasionally raise a false alarm and run the
+ <command><replaceable>command</replaceable></command> even though
+ nothing changed, but that's rare. It's also possible that by
+ the time <command><replaceable>command</replaceable></command>
+ actually runs, then whatever was changed in the
+ <filename><replaceable>maildir</replaceable></filename> is no longer
+ changed; it became what it was before it was changed (a mystery
+ that will remain unsolved forever).
+ It's up to the
+ <command><replaceable>command</replaceable></command> to
+ intelligently figure out
+ if it needs to do something about whatever it finds in the
+ <filename><replaceable>maildir</replaceable></filename>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <command>maildirwatch</command> is a rudimentary mechanism for writing
+ shell scripts that deal with newly-delivered mail, in
+ some form or fashion.
+ </para>
+ </refsect1>
+
+ <refsect1 id="maildirwatch_exit">
+ <title>EXIT CODE</title>
+
+ <para>
+ A non-zero exit code indicates that the specified
+ <filename><replaceable>maildir</replaceable></filename> does not
+ exist or is corrupted.
+ </para>
+
+ <para>
+ Otherwise
+ <command>maildirwatch</command> runs until the
+ <command><replaceable>command</replaceable></command> exits with a
+ non-zero exit code.
+ <command>maildirwatch</command> prints
+ <command><replaceable>command</replaceable></command>'s exit code on
+ standard output, and terminates with an exit code of 0.
+ </para>
+ </refsect1>
+
+ <refsect1 id="maildirwatch_see_also">
+ <title>SEE ALSO</title>
+
+ <para>
+<ulink url="maildirmake.html"><citerefentry><refentrytitle>maildirmake</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>.</para>
+ </refsect1>
+
+</refentry>
diff --git a/maildir/maildirwatchprog.c b/maildir/maildirwatchprog.c
new file mode 100644
index 0000000..f169da8
--- /dev/null
+++ b/maildir/maildirwatchprog.c
@@ -0,0 +1,201 @@
+/*
+** Copyright 2021 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <poll.h>
+#include "maildirwatch.h"
+
+static void usage()
+{
+ printf("Usage: maildirwatch maildir program arguments...\n");
+ exit(1);
+}
+
+static int forkexec(int argc, char **argv)
+{
+ pid_t p=fork();
+ int s;
+
+ if (p < 0)
+ {
+ perror("fork");
+ return 0;
+ }
+
+ if (p == 0)
+ {
+ char **argvptr=malloc(sizeof(char *)*(argc+1));
+ int n;
+
+ if (!argvptr)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ for (n=0; n<argc; ++n)
+ argvptr[n]=argv[n];
+ argvptr[n]=0;
+ execvp(argvptr[0], argvptr);
+ perror(argv[0]);
+ exit(1);
+ }
+
+ if (waitpid(p, &s, 0) != p)
+ return 0;
+
+ if (!WIFEXITED(s))
+ {
+ if (WIFSIGNALED(s))
+ printf("-%d\n", (int)WTERMSIG(s));
+ else
+ printf("-0\n");
+ return 0;
+ }
+
+ if (WEXITSTATUS(s))
+ {
+ printf("%d\n", (int)WEXITSTATUS(s));
+ return 0;
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct maildirwatch *watch;
+ struct maildirwatch_contents mc;
+ int fdret;
+ int rc;
+
+ if (argc < 3)
+ usage();
+
+ /*
+ ** Call maildirwatch_alloc, then maildirwatch_start.
+ */
+
+ watch=maildirwatch_alloc(argv[1]);
+
+ if (maildirwatch_start(watch, &mc) < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ ** poll() the returned file descriptor as long as
+ ** maildirwatch_started returns 0.
+ */
+
+ while ((rc=maildirwatch_started(&mc, &fdret)) == 0)
+ {
+ struct pollfd pfd;
+
+ pfd.fd=fdret;
+ pfd.events=POLLIN;
+
+ if (poll(&pfd, 1, -1) < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+ }
+
+ if (rc < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ ** The maildir is now being monitored.
+ */
+
+ while (forkexec(argc-2, argv+2))
+ {
+ int was_changed=0;
+
+ while (1)
+ {
+ int changed;
+ int timeout;
+
+ /*
+ ** maildirwatch_check() checks if the maildir has
+ ** been changed.
+ */
+ int rc=maildirwatch_check(&mc, &changed, &fdret,
+ &timeout);
+
+
+ if (rc < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ ** If it's changed, you can call it again.
+ ** maildirwatch_check() does not block.
+ */
+ if (changed)
+ {
+ was_changed=1;
+ continue;
+ }
+
+ if (was_changed)
+ break;
+
+ /*
+ ** If it's not changed, poll() for the number of
+ ** seconds specified by the timeout.
+ **
+ ** In polling mode fdret is 0, so sleep, then
+ ** check the maildir manually.
+ */
+
+ if (fdret < 0)
+ {
+ sleep(timeout);
+ continue;
+ }
+
+ struct pollfd pfd;
+
+ pfd.fd=fdret;
+ pfd.events=POLLIN;
+
+ if (poll(&pfd, 1, timeout * 1000) < 0)
+ {
+ perror("poll");
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ ** Cleanup.
+ */
+
+ maildirwatch_end(&mc);
+ maildirwatch_free(watch);
+ return (0);
+}
diff --git a/maildrop/Makefile.am b/maildrop/Makefile.am
index 96b0d3c..5f94c37 100644
--- a/maildrop/Makefile.am
+++ b/maildrop/Makefile.am
@@ -44,10 +44,9 @@ maildrop_SOURCES=deliver.C deliverdotlock.C deliverdotlock.h \
rematchstr.C rematchstr.h search.C search.h token.C \
token.h varlist.C varlist.h dovecotauth.c dovecotauth.h
-maildrop_DEPENDENCIES = libmdcommon.la ../maildir/maildir.libdeps
+maildrop_DEPENDENCIES = libmdcommon.la
-maildrop_LDADD = libmdcommon.la `cat ../maildir/maildir.libdeps`\
- @AUTHLDADD@ -lpcre
+maildrop_LDADD = libmdcommon.la @AUTHLDADD@ -lpcre
maildrop_LDFLAGS=
reformail_SOURCES=reformail.C
diff --git a/maildrop/configure.ac b/maildrop/configure.ac
index 2574b8c..0970db5 100644
--- a/maildrop/configure.ac
+++ b/maildrop/configure.ac
@@ -2,7 +2,7 @@ dnl
dnl Copyright 1998 - 2017 Double Precision, Inc. See COPYING for
dnl distribution information.
-AC_INIT(maildrop, 3.0.1, [courier-users@lists.sourceforge.net])
+AC_INIT(maildrop, 3.0.2, [courier-users@lists.sourceforge.net])
>confdefs.h # Kill PACKAGE_ macros
diff --git a/sqwebmail/ChangeLog b/sqwebmail/ChangeLog
index d4dd1c8..e051850 100644
--- a/sqwebmail/ChangeLog
+++ b/sqwebmail/ChangeLog
@@ -1,3 +1,7 @@
+2021-02-19 Sam Varshavchik <mrsam@courier-mta.com>
+
+ * spec file: additional fixes.
+
6.0.5
2021-02-05 Sam Varshavchik <mrsam@courier-mta.com>
diff --git a/sqwebmail/Makefile.am b/sqwebmail/Makefile.am
index 7daba38..d71cebf 100644
--- a/sqwebmail/Makefile.am
+++ b/sqwebmail/Makefile.am
@@ -96,9 +96,8 @@ sqwebmaild_SOURCES=acl.c acl.h \
strftime.c strftime.h \
sqwebmail.h token.c token.h $(SQISPELLSOURCES)
-sqwebmaild_LDADD=libwebmail.la `cat ../maildir/maildir.libdeps` \
- @NETLIBS@ @LDAUTH@ -lcourierauth -lpcre
-sqwebmaild_DEPENDENCIES=libwebmail.la ../maildir/maildir.libdeps
+sqwebmaild_LDADD=libwebmail.la @NETLIBS@ @LDAUTH@ -lcourierauth -lpcre
+sqwebmaild_DEPENDENCIES=libwebmail.la
sqwebmail_SOURCES=sqwebmaild.c
sqwebmail_DEPENDENCIES= ../cgi/libcgi.la
diff --git a/tcpd/configure.ac b/tcpd/configure.ac
index 0aa6f8a..c3bb9cb 100644
--- a/tcpd/configure.ac
+++ b/tcpd/configure.ac
@@ -318,6 +318,11 @@ if test "$have_ssl" = "yes"
then
LIBCOURIERTLSOPENSSL=libcouriertlsopenssl.la
+ AC_CHECK_LIB(ssl, SSL_CTX_set_alpn_protos, [
+ AC_DEFINE_UNQUOTED(HAVE_OPENSSL_ALPN, 1,
+ [ Whether OpenSSL supports ALPN ])
+ ])
+
if test "$KRB5CONFIG" != "krb5-config"
then
AC_MSG_CHECKING(whether OpenSSL requires Kerberos)
@@ -394,6 +399,11 @@ then
gnutlsdep="`$PKGCONFIG --libs gnutls`"
AC_DEFINE([HAVE_GNUTLS3], [1], [Use GnuTLS3])
fi
+
+ AC_CHECK_LIB(gnutls, gnutls_alpn_set_protocols, [
+ AC_DEFINE_UNQUOTED(HAVE_GNUTLS_ALPN, 1,
+ [ Whether OpenSSL supports ALPN ])
+ ])
fi
AC_CHECK_SIZEOF(gnutls_transport_ptr_t,0, [
diff --git a/tcpd/libcouriergnutls.c b/tcpd/libcouriergnutls.c
index 5fc0ef9..18ea01c 100644
--- a/tcpd/libcouriergnutls.c
+++ b/tcpd/libcouriergnutls.c
@@ -161,6 +161,23 @@ static void nonsslerror(struct tls_info *info, const char *pfix)
(*info->tls_err_msg)(errmsg, info->app_data);
}
+#if HAVE_GNUTLS_ALPN
+
+static void sslerror(ssl_context ctx, int errcode, const char *pfix)
+{
+ char errmsg[512];
+ const char *err;
+
+ strcat(strcpy(errmsg, pfix), ": ");
+
+ err=gnutls_strerror(errcode);
+
+ strncat(errmsg, err, sizeof(errmsg)-1-strlen(errmsg));
+ errmsg[sizeof(errmsg)-1]=0;
+ (*ctx->info_cpy.tls_err_msg)(errmsg, ctx->info_cpy.app_data);
+}
+#endif
+
static const char *safe_getenv(ssl_context context, const char *n,
const char *def)
{
@@ -722,6 +739,154 @@ static int verify_client(ssl_handle ssl, int fd)
return 0;
}
+#if HAVE_GNUTLS_ALPN
+/*
+** Parse the TLS_ALPN setting, repeatedly invoking a callback with:
+** 1) size of the protocol string
+** 2) the protocol string
+** 3) pass-through parameter.
+*/
+
+static gnutls_alpn_flags_t alpn_parse(const char *alpn,
+ void (*cb)(unsigned char l,
+ const char *p,
+ void *arg),
+ void *arg)
+{
+ gnutls_alpn_flags_t flags=GNUTLS_ALPN_MANDATORY;
+
+ while (alpn && *alpn)
+ {
+ const char *p;
+ unsigned char l;
+
+ /* Commas and spaces are delimiters here */
+
+ if (*alpn==',' || isspace(*alpn))
+ {
+ ++alpn;
+ continue;
+ }
+
+ /* Look for the next comma, spaces, or end of string */
+ p=alpn;
+
+ while (*alpn && *alpn != ',' && !isspace(*alpn))
+ ++alpn;
+
+ /*
+ ** We now have the character count and the string.
+ ** Defend against a bad setting by checking for overflow.
+ */
+ l=alpn - p;
+
+ if (l != alpn - p)
+ continue;
+
+ if (*p != '@')
+ (*cb)(l, p, arg);
+ else
+ {
+ if (l > 1)
+ switch (p[1]) {
+ case 'o':
+ case 'O':
+ flags &= GNUTLS_ALPN_MANDATORY;
+ break;
+ case 's':
+ case 'S':
+ flags |= GNUTLS_ALPN_SERVER_PRECEDENCE;
+ break;
+ }
+ }
+ }
+
+ return flags;
+}
+
+static void alpn_count(unsigned char l,
+ const char *p,
+ void *arg)
+{
+ ++*(unsigned *)arg;
+}
+
+static void alpn_save(unsigned char l,
+ const char *name,
+ void *arg)
+{
+ gnutls_datum_t **p=(gnutls_datum_t **)arg;
+
+ (*p)->data=(unsigned char *)name;
+ (*p)->size=l;
+ ++*p;
+}
+
+static int alpn_set(ssl_context ctx,
+ gnutls_session_t session)
+{
+ const char *p=safe_getenv(ctx, "TLS_ALPN", "");
+ unsigned n=0;
+ gnutls_datum_t *datums;
+ gnutls_datum_t *datum_ptr;
+ gnutls_alpn_flags_t flags;
+ int ret;
+
+ alpn_parse(p, alpn_count, &n);
+
+ if (n == 0)
+ return GNUTLS_E_SUCCESS;
+
+ if ((datums=(gnutls_datum_t *)malloc(n * sizeof(gnutls_datum_t)))
+ == NULL)
+ {
+ return GNUTLS_E_SUCCESS;
+ }
+
+ datum_ptr=datums;
+
+ flags=alpn_parse(p, alpn_save, &datum_ptr);
+
+ ret=gnutls_alpn_set_protocols(session, datums, n, flags);
+ free((char *)datums);
+
+ return ret;
+}
+
+#if 0
+static int verify_alpn(ssl_handle ssl)
+{
+ const char *p=safe_getenv(ssl->ctx, "TLS_ALPN", "");
+ unsigned n=0;
+
+ gnutls_datum_t protocol;
+ int rc;
+
+ alpn_parse(p, alpn_count, &n);
+
+ rc=gnutls_alpn_get_selected_protocol(ssl->session, &protocol);
+ if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ {
+ fprintf(stderr, "Not negotiated\n");
+
+ return 0;
+ }
+ if (rc != GNUTLS_E_SUCCESS)
+ {
+ fprintf(stderr, "ERROR: %d %s\n",
+ rc, gnutls_strerror(rc));
+ return 0;
+ }
+
+ fprintf(stderr, "Negotiated ");
+ fwrite(protocol.data, protocol.size, 1, stderr);
+ fprintf(stderr, "\n");
+
+ return 0;
+}
+#endif
+#endif
+
static int dohandshake(ssl_handle ssl, int fd, fd_set *r, fd_set *w)
{
int rc;
@@ -734,10 +899,13 @@ static int dohandshake(ssl_handle ssl, int fd, fd_set *r, fd_set *w)
{
ssl->info_cpy.connect_interrupted=0;
-
if (verify_client(ssl, fd))
return -1;
+#if 0
+ if (verify_alpn(ssl))
+ return -1;
+#endif
if (ssl->info_cpy.connect_callback != NULL &&
!(*ssl->info_cpy.connect_callback)(ssl,
ssl->info_cpy.app_data))
@@ -1046,6 +1214,7 @@ static int get_server_cert(gnutls_session_t session,
{
if (ssl->ctx->certfile)
nonsslerror(&ssl->info_cpy, ssl->ctx->certfile);
+ free(vhost_buf);
return 0;
}
@@ -1053,6 +1222,7 @@ static int get_server_cert(gnutls_session_t session,
free(certfilename);
if (keyfilename)
free(keyfilename);
+ free(vhost_buf);
return rc;
}
@@ -1535,6 +1705,20 @@ RT |
return NULL;
}
+#if HAVE_GNUTLS_ALPN
+ {
+ int result_rc;
+
+ result_rc=alpn_set(ctx, ssl->session);
+
+ if (result_rc != GNUTLS_E_SUCCESS)
+ {
+ sslerror(ctx, result_rc, "gnutls_alpn_set_protocols");
+ tls_free_session(ssl);
+ return (NULL);
+ }
+ }
+#endif
if (ssl->dhparams_initialized)
{
gnutls_certificate_set_dh_params(ssl->xcred, ssl->dhparams);
diff --git a/tcpd/libcouriertls.c b/tcpd/libcouriertls.c
index 5105030..39c7d49 100644
--- a/tcpd/libcouriertls.c
+++ b/tcpd/libcouriertls.c
@@ -445,6 +445,9 @@ static void load_dh_params(SSL_CTX *ctx, const char *filename,
static int check_readable_file(const char *filename)
{
+ if (!filename)
+ return 0;
+
return (access(filename, R_OK) == 0) ? 1 : 0;
}
@@ -729,6 +732,203 @@ static int server_cert_cb(ssl_handle ssl, int *ad, void *arg)
return SSL_TLSEXT_ERR_OK;
}
+#if HAVE_OPENSSL_ALPN
+
+/*
+** Parse the TLS_ALPN setting, repeatedly invoking a callback with:
+** 1) size of the protocol string
+** 2) the protocol string
+** 3) pass-through parameter.
+*/
+
+static void alpn_parse(const char *alpn,
+ void (*cb)(unsigned char l,
+ const char *p,
+ void *arg),
+ void *arg)
+{
+ while (alpn && *alpn)
+ {
+ const char *p;
+ unsigned char l;
+
+ /* Commas and spaces are delimiters here */
+
+ if (*alpn==',' || isspace(*alpn))
+ {
+ ++alpn;
+ continue;
+ }
+
+ /* Look for the next comma, spaces, or end of string */
+ p=alpn;
+
+ while (*alpn && *alpn != ',' && !isspace(*alpn))
+ ++alpn;
+
+ /*
+ ** We now have the character count and the string.
+ ** Defend against a bad setting by checking for overflow.
+ */
+ l=alpn - p;
+
+ if (l != alpn - p)
+ continue;
+
+ if (*p != '@') /* Used in the gnutls version */
+ (*cb)(l, p, arg);
+ }
+}
+
+struct tls_avpn_to_protocol_list_info {
+ void (*cb)(const char *, unsigned int, void *);
+ void *arg;
+};
+
+/*
+** Take the result of alpn_parse and construct a character stream, in ALPN
+** protocol-list format.
+*/
+
+static void tls_avpn_to_protocol_list_cb(unsigned char l,
+ const char *p,
+ void *arg)
+{
+ struct tls_avpn_to_protocol_list_info *ptr=
+ (struct tls_avpn_to_protocol_list_info *)arg;
+
+ (*ptr->cb)((char *)&l, 1, ptr->arg);
+ (*ptr->cb)(p, l, ptr->arg);
+}
+
+static void tls_avpn_to_protocol_list(const char *alpn,
+ void (*cb)(const char *, unsigned int,
+ void *),
+ void *arg)
+{
+ struct tls_avpn_to_protocol_list_info info;
+
+ info.cb=cb;
+ info.arg=arg;
+
+ alpn_parse(alpn, tls_avpn_to_protocol_list_cb, &info);
+}
+
+/*
+** Create a discrete unsigned char buffer with the ALPN protocol-list
+** string.
+**
+** First we use tls_avpn_to_protocol_list to count the size of the string,
+** then malloc it, then create it for real-sies.
+*/
+
+struct alpn_info {
+ unsigned char *p;
+ unsigned int l;
+};
+
+static void alpn_proto_count(const char *ptr, unsigned int n,
+ void *arg)
+{
+ struct alpn_info *info=(struct alpn_info *)arg;
+
+ info->l += n;
+}
+
+static void alpn_proto_save(const char *ptr, unsigned int n,
+ void *arg)
+{
+ struct alpn_info *info=(struct alpn_info *)arg;
+
+ memcpy(info->p, ptr, n);
+ info->p+=n;
+}
+
+static void parse_tls_alpn(const struct tls_info *info,
+ struct alpn_info *alpn)
+{
+ const char *s=safe_getenv(info, "TLS_ALPN");
+ unsigned char *buffer;
+
+ alpn->p=NULL;
+ alpn->l=0;
+
+ tls_avpn_to_protocol_list(s, alpn_proto_count, alpn);
+
+ if (alpn->l==0)
+ return;
+
+ buffer=(unsigned char *)malloc(alpn->l);
+
+ if (!buffer)
+ {
+ nonsslerror(info, "malloc");
+ exit(1);
+ }
+ alpn->p=buffer;
+ tls_avpn_to_protocol_list(s, alpn_proto_save, alpn);
+ alpn->p=buffer;
+}
+
+struct tls_alpn_server_info {
+ const unsigned char *in;
+ unsigned char inl;
+ int found;
+};
+
+static void alpn_proto_search(unsigned char l,
+ const char *p,
+ void *arg)
+{
+ struct tls_alpn_server_info *info=
+ (struct tls_alpn_server_info *)arg;
+
+ if (info->inl == l &&
+ memcmp(info->in, p, l) == 0)
+ {
+ info->found=1;
+ }
+}
+
+static int tls_alpn_server_cb(SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg)
+{
+ struct tls_info *info=(struct tls_info *)SSL_get_app_data(ssl);
+ const char *s=safe_getenv(info, "TLS_ALPN");
+
+ while (inlen)
+ {
+ struct tls_alpn_server_info search_info;
+
+ if (*in >= inlen || *in == 0) /* Won't assume */
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ search_info.in=in+1;
+ search_info.inl=*in;
+ search_info.found=0;
+
+ alpn_parse(s, alpn_proto_search, &search_info);
+ if (search_info.found)
+ {
+ *outlen=search_info.inl;
+ *out=search_info.in;
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ inlen -= *in;
+ --inlen;
+ in += *in;
+ ++in;
+ }
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+}
+
+#endif
+
SSL_CTX *tls_create(int isserver, const struct tls_info *info)
{
return tls_create_int(isserver, info, 0);
@@ -963,10 +1163,41 @@ SSL_CTX *tls_create_int(int isserver, const struct tls_info *info,
if (isserver)
{
+#if HAVE_OPENSSL_ALPN
+ struct alpn_info alpn;
+
+ parse_tls_alpn(info, &alpn);
+
+ if (alpn.p)
+ {
+ free((char *)alpn.p);
+
+ SSL_CTX_set_alpn_select_cb(ctx, tls_alpn_server_cb,
+ NULL);
+ }
+#endif
SSL_CTX_set_tlsext_servername_callback(ctx, server_cert_cb);
}
else
{
+#if HAVE_OPENSSL_ALPN
+ struct alpn_info alpn;
+
+ parse_tls_alpn(info, &alpn);
+
+ if (alpn.p)
+ {
+ int ret=SSL_CTX_set_alpn_protos(ctx, alpn.p, alpn.l);
+ free((char *)alpn.p);
+
+ if (ret)
+ {
+ sslerror(info, "SSL_CTX_set_alpn_protos", -1);
+ tls_destroy(ctx);
+ return (NULL);
+ }
+ }
+#endif
SSL_CTX_set_client_cert_cb(ctx, client_cert_cb);
}
return (ctx);
diff --git a/tcpd/starttls.c b/tcpd/starttls.c
index e7b805f..76fcf3b 100644
--- a/tcpd/starttls.c
+++ b/tcpd/starttls.c
@@ -550,6 +550,7 @@ static int dossl(int fd, int argn, int argc, char **argv)
if (!ssl)
{
close(fd);
+ tls_destroy(ctx);
return (1);
}
diff --git a/unicode/.gitignore b/unicode/.gitignore
index 8905e05..e749d69 100644
--- a/unicode/.gitignore
+++ b/unicode/.gitignore
@@ -26,6 +26,7 @@
/config.sub
/courier-unicode.h
/courier-unicode.spec
+/courier-unicode-version.m4
/depcomp
/docs.stamp
/enttest
diff --git a/unicode/ChangeLog b/unicode/ChangeLog
index 378fede..35cffe6 100644
--- a/unicode/ChangeLog
+++ b/unicode/ChangeLog
@@ -1,3 +1,25 @@
+2021-02-24 Sam Varshavchik <mrsam@courier-mta.com>
+
+ * Implement unicode_bidi_needs_embed(), unicode_bidi_cleaned_size(),
+ unicode::bidi_override,
+
+2.2.1
+
+2021-02-14 Sam Varshavchik <mrsam@courier-mta.com>
+
+ * unicode_bidi_calc and unicode_bidi_calc_levels return a
+ unicode_bidi_direction object, to indicate whether the
+ computer paragraph embedding level was explicitly computed or
+ defaulted.
+
+2021-02-13 Sam Varshavchik <mrsam@courier-mta.com>
+
+ * courier-unicode-version.m4: split version checking macro into its
+ own m4 file.
+
+ * unicode::bidi_calc - an empty string with an explicit embedding
+ level should return the requested embedding level.
+
2.2
2020-11-05 Sam Varshavchik <mrsam@courier-mta.com>
diff --git a/unicode/Makefile.am b/unicode/Makefile.am
index 7ba36f1..dc502b3 100644
--- a/unicode/Makefile.am
+++ b/unicode/Makefile.am
@@ -24,7 +24,7 @@ noinst_PROGRAMS=unicodetest graphemetest linebreaktest wordbreaktest \
enttest scripttest biditest biditest2
aclocaldir=$(datadir)/aclocal
-aclocal_DATA=m4/courier-unicode.m4
+aclocal_DATA=m4/courier-unicode.m4 courier-unicode-version.m4
update-www:
@$(MAKE) update-www-unicode
@@ -89,42 +89,46 @@ include_HEADERS=courier-unicode.h \
man_MANS= \
$(srcdir)/man/courier-unicode.7 \
- $(srcdir)/man/unicode\:\:bidi.3 \
- $(srcdir)/man/unicode\:\:bidi_calc.3 \
- $(srcdir)/man/unicode\:\:bidi_calc_types.3 \
- $(srcdir)/man/unicode\:\:bidi_cleanup.3 \
- $(srcdir)/man/unicode\:\:bidi_embed.3 \
- $(srcdir)/man/unicode\:\:bidi_embed_paragraph_level.3 \
- $(srcdir)/man/unicode\:\:bidi_get_direction.3 \
- $(srcdir)/man/unicode\:\:bidi_logical_order.3 \
- $(srcdir)/man/unicode\:\:bidi_reorder.3 \
- $(srcdir)/man/unicode\:\:iconvert\:\:convert.3 \
- $(srcdir)/man/unicode\:\:iconvert\:\:convert_tocase.3 \
- $(srcdir)/man/unicode\:\:iconvert\:\:fromu.3 \
- $(srcdir)/man/unicode\:\:iconvert\:\:tou.3 \
- $(srcdir)/man/unicode\:\:iso_8859_1.3 \
- $(srcdir)/man/unicode\:\:linebreak_callback_base.3 \
- $(srcdir)/man/unicode\:\:linebreak_callback_save_buf.3 \
- $(srcdir)/man/unicode\:\:linebreak_iter.3 \
- $(srcdir)/man/unicode\:\:linebreakc_callback_base.3 \
- $(srcdir)/man/unicode\:\:linebreakc_iter.3 \
- $(srcdir)/man/unicode\:\:tolower.3 \
- $(srcdir)/man/unicode\:\:toupper.3 \
- $(srcdir)/man/unicode\:\:ucs_2.3 \
- $(srcdir)/man/unicode\:\:ucs_4.3 \
- $(srcdir)/man/unicode\:\:utf_8.3 \
- $(srcdir)/man/unicode\:\:wordbreak_callback_base.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_calc.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_calc_types.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_cleanup.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_embed.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_embed_paragraph_level.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_get_direction.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_logical_order.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_needs_embed.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_override.3 \
+ $(srcdir)/man/unicode[\:][\:]bidi_reorder.3 \
+ $(srcdir)/man/unicode[\:][\:]iconvert[\:][\:]convert.3 \
+ $(srcdir)/man/unicode[\:][\:]iconvert[\:][\:]convert_tocase.3 \
+ $(srcdir)/man/unicode[\:][\:]iconvert[\:][\:]fromu.3 \
+ $(srcdir)/man/unicode[\:][\:]iconvert[\:][\:]tou.3 \
+ $(srcdir)/man/unicode[\:][\:]iso_8859_1.3 \
+ $(srcdir)/man/unicode[\:][\:]linebreak_callback_base.3 \
+ $(srcdir)/man/unicode[\:][\:]linebreak_callback_save_buf.3 \
+ $(srcdir)/man/unicode[\:][\:]linebreak_iter.3 \
+ $(srcdir)/man/unicode[\:][\:]linebreakc_callback_base.3 \
+ $(srcdir)/man/unicode[\:][\:]linebreakc_iter.3 \
+ $(srcdir)/man/unicode[\:][\:]tolower.3 \
+ $(srcdir)/man/unicode[\:][\:]toupper.3 \
+ $(srcdir)/man/unicode[\:][\:]ucs_2.3 \
+ $(srcdir)/man/unicode[\:][\:]ucs_4.3 \
+ $(srcdir)/man/unicode[\:][\:]utf_8.3 \
+ $(srcdir)/man/unicode[\:][\:]wordbreak_callback_base.3 \
$(srcdir)/man/unicode_bidi.3 \
$(srcdir)/man/unicode_bidi_bracket_type.3 \
$(srcdir)/man/unicode_bidi_calc.3 \
$(srcdir)/man/unicode_bidi_calc_levels.3 \
$(srcdir)/man/unicode_bidi_calc_types.3 \
+ $(srcdir)/man/unicode_bidi_cleaned_size.3 \
$(srcdir)/man/unicode_bidi_cleanup.3 \
$(srcdir)/man/unicode_bidi_direction.3 \
$(srcdir)/man/unicode_bidi_embed.3 \
$(srcdir)/man/unicode_bidi_embed_paragraph_level.3 \
$(srcdir)/man/unicode_bidi_logical_order.3 \
$(srcdir)/man/unicode_bidi_mirror.3 \
+ $(srcdir)/man/unicode_bidi_needs_embed.3 \
$(srcdir)/man/unicode_bidi_reorder.3 \
$(srcdir)/man/unicode_bidi_setbnl.3 \
$(srcdir)/man/unicode_bidi_type.3 \
@@ -219,7 +223,7 @@ libcourier_unicode_la_SOURCES=\
bidi_mirroring.h \
unicode_categories.c
-libcourier_unicode_la_LDFLAGS=-version-info 6:0:2
+libcourier_unicode_la_LDFLAGS=-version-info 7:0:0
EXTRA_DIST=$(noinst_SCRIPTS) $(man_MANS) $(PACKAGE).spec \
m4/courier-unicode.m4 \
@@ -444,7 +448,7 @@ docs.stamp:
rm -f man/*.[123456789]
mv man.tmp/* man
rm -rf html.tmp man.tmp
- perl -e '$$f=join("",<STDIN>); $$p=join("", map { " \\\n \$$(srcdir)/$$_" } glob("man/*.[123456789]")); $$p=~s/:/\\:/g; $$f =~ s/\nman_MANS=([^\n]|\n[^\n])*/\nman_MANS=$$p/s; print $$f' <Makefile.am >Makefile.am.new
+ perl -e '$$f=join("",<STDIN>); $$p=join("", map { " \\\n \$$(srcdir)/$$_" } glob("man/*.[123456789]")); $$p=~s/:/\[\\:\]/g; $$f =~ s/\nman_MANS=([^\n]|\n[^\n])*/\nman_MANS=$$p/s; print $$f' <Makefile.am >Makefile.am.new
cmp Makefile.am Makefile.am.new || mv -f Makefile.am.new Makefile.am; rm -f Makefile.am.new
touch docs.stamp
diff --git a/unicode/biditest2.C b/unicode/biditest2.C
index 8e9d7da..a14b3ea 100644
--- a/unicode/biditest2.C
+++ b/unicode/biditest2.C
@@ -204,14 +204,14 @@ void character_test()
? unicode::bidi_calc(s, direction)
: unicode::bidi_calc(s);
- if (std::get<1>(ret) != paragraph_embedding_level)
+ if (std::get<1>(ret).direction != paragraph_embedding_level)
{
std::cerr << "Regression, line "
<< linenum
<< ": expected "
<< paragraph_embedding_level
<< " paragraph embedding level, got "
- << (int)std::get<1>(ret)
+ << (int)std::get<1>(ret).direction
<< std::endl;
exit(1);
}
@@ -274,6 +274,9 @@ void character_test()
std::reverse(b+index, b+index+n);
});
+ size_t cleaned_size=unicode_bidi_cleaned_size(s.c_str(),
+ s.size(), 0);
+
n=0;
unicode::bidi_cleanup
(s, levels,
@@ -285,6 +288,17 @@ void character_test()
++n;
});
+ if (cleaned_size != s.size())
+ {
+ std::cerr << "Regression, line "
+ << linenum
+ << ": default cleaned size"
+ << std::endl
+ << " Expected size: " << cleaned_size
+ << ", actual size: " << s.size()
+ << std::endl;
+ exit(1);
+ }
if (render_order != actual_render_order)
{
std::cerr << "Regression, line "
@@ -408,6 +422,12 @@ void character_test()
}
unicode::bidi_reorder(new_string, std::get<0>(ret));
+
+ cleaned_size=unicode_bidi_cleaned_size
+ (new_string.c_str(),
+ new_string.size(),
+ UNICODE_BIDI_CLEANUP_CANONICAL);
+
unicode::bidi_cleanup(new_string,
std::get<0>(ret),
[]
@@ -416,6 +436,20 @@ void character_test()
},
UNICODE_BIDI_CLEANUP_CANONICAL);
+ if (cleaned_size != new_string.size())
+ {
+ std::cerr << "Regression, line "
+ << linenum
+ << ": canonoical cleaned size"
+ << std::endl
+ << " Expected size: "
+ << cleaned_size
+ << ", actual size: "
+ << new_string.size()
+ << std::endl;
+ exit(1);
+ }
+
/* New string is now back in logical order */
if (new_string == s && std::get<0>(ret) == levels)
@@ -548,34 +582,62 @@ void null_character_test()
},
UNICODE_BIDI_CLEANUP_EXTRA,
0, 3);
+
+ s=U"";
+ res=unicode::bidi_calc(s, UNICODE_BIDI_RL);
+
+ if (std::get<1>(res).direction != UNICODE_BIDI_RL)
+ {
+ std::cerr << "Paragraph embedding level not honored"
+ << std::endl;
+ exit(1);
+ }
}
void direction_test()
{
static const struct {
- const char32_t *str;
+ std::u32string str;
unicode_bidi_level_t direction;
int is_explicit;
+ bool needs_embed;
} tests[]={
{
U"Hello",
UNICODE_BIDI_LR,
1,
+ true,
},
{
U" ",
UNICODE_BIDI_LR,
0,
+ true,
},
{
U"",
UNICODE_BIDI_LR,
0,
+ true,
},
{
U"שלום",
UNICODE_BIDI_RL,
1,
+ true,
+ },
+ {
+ U"Helloש",
+ UNICODE_BIDI_LR,
+ 1,
+ true,
+ },
+ {
+ U"Hello" + std::u32string{unicode::literals::LRO}
+ + U"ש",
+ UNICODE_BIDI_LR,
+ 1,
+ false,
},
};
@@ -589,6 +651,18 @@ void direction_test()
std::cerr << "direction_test failed\n";
exit(1);
}
+
+ std::u32string s=t.str;
+ auto levels=std::get<0>(unicode::bidi_calc(s, t.direction));
+ unicode::bidi_reorder(s, levels);
+ unicode::bidi_cleanup(s, levels);
+
+ if (unicode::bidi_needs_embed(s, levels, &t.direction)
+ != t.needs_embed)
+ {
+ std::cerr << "needs embed failed\n";
+ exit(1);
+ }
}
}
diff --git a/unicode/book.xml b/unicode/book.xml
index c3ebc33..4f0fd71 100644
--- a/unicode/book.xml
+++ b/unicode/book.xml
@@ -99,11 +99,39 @@ See COPYING for distribution information.
<para>
Download the current version of the library from
<ulink url="/download.html#unicode">https://www.courier-mta.org/download.html#unicode</ulink>.
- After unpacking the tarball, run the configure script, which takes
- the usual options, followed by <command>make</command>, then
- <command>make install</command>.
+ Use the downloaded tarball to create an appropriate installation
+ package for your operating system distribution.
+ The typical sequence of commands is:
</para>
+ <blockquote>
+ <informalexample>
+ <programlisting>
+./configure # Takes the default configure script options
+make
+make install DESTDIR=/tmp/courier-unicode-instimage # For example.</programlisting>
+ </informalexample>
+ </blockquote>
+
+ <para>
+ The library uses a stock configure script, <command>make</command>
+ and <command>make install</command> command that respects the
+ <varname>DESTDIR</varname> setting to create an installation image
+ in the directory specified by <varname>DESTDIR</varname>.
+ </para>
+
+ <note>
+ <para>
+ <command>make install</command> will not take any explicit action
+ to uninstall any older version of the library, or remove any files
+ from an older version that do not exist any more in the new version.
+ The library's installation image should be used to prepare an
+ installable package in a native package format for your operating
+ system distribution. Use your native system distribution's package
+ manager to properly install and uninstall the library's package.
+ </para>
+ </note>
+
<para>
To use the library, <quote>#include &lt;courier-unicode.h&gt;</quote> and link
with <literal>-lcourier-unicode</literal>.
@@ -306,7 +334,9 @@ See COPYING for distribution information.
<refname>unicode_bidi_calc</refname>
<refname>unicode_bidi_reorder</refname>
<refname>unicode_bidi_cleanup</refname>
+ <refname>unicode_bidi_cleaned_size</refname>
<refname>unicode_bidi_logical_order</refname>
+ <refname>unicode_bidi_needs_embed</refname>
<refname>unicode_bidi_embed</refname>
<refname>unicode_bidi_embed_paragraph_level</refname>
@@ -331,7 +361,7 @@ See COPYING for distribution information.
</funcprototype>
<funcprototype>
- <funcdef>void <function>unicode_bidi_calc_levels</function></funcdef>
+ <funcdef>struct unicode_bidi_direction <function>unicode_bidi_calc_levels</function></funcdef>
<paramdef>const char32_t *<parameter>p</parameter></paramdef>
<paramdef>const unicode_bidi_type_t *<parameter>types</parameter></paramdef>
<paramdef>size_t <parameter>n</parameter></paramdef>
@@ -340,7 +370,7 @@ See COPYING for distribution information.
</funcprototype>
<funcprototype>
- <funcdef>void <function>unicode_bidi_calc</function></funcdef>
+ <funcdef>struct unicode_bidi_direction <function>unicode_bidi_calc</function></funcdef>
<paramdef>const char32_t *<parameter>p</parameter></paramdef>
<paramdef>size_t <parameter>n</parameter></paramdef>
<paramdef>unicode_bidi_level_t *<parameter>levels</parameter></paramdef>
@@ -367,7 +397,14 @@ See COPYING for distribution information.
</funcprototype>
<funcprototype>
- <funcdef>size_t <function>unicode_bidi_logical_order</function></funcdef>
+ <funcdef>size_t <function>unicode_bidi_cleaned_size</function></funcdef>
+ <paramdef>const char32_t *<parameter>string</parameter></paramdef>
+ <paramdef>size_t <parameter>n</parameter></paramdef>
+ <paramdef>int <parameter>options</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>unicode_bidi_logical_order</function></funcdef>
<paramdef>char32_t *<parameter>string</parameter></paramdef>
<paramdef>unicode_bidi_level_t *<parameter>levels</parameter></paramdef>
<paramdef>size_t <parameter>n</parameter></paramdef>
@@ -377,6 +414,14 @@ See COPYING for distribution information.
</funcprototype>
<funcprototype>
+ <funcdef>int <function>unicode_bidi_needs_embed</function></funcdef>
+ <paramdef>const char32_t *<parameter>string</parameter></paramdef>
+ <paramdef>const unicode_bidi_level_t *<parameter>levels</parameter></paramdef>
+ <paramdef>size_t <parameter>n</parameter></paramdef>
+ <paramdef>const unicode_bidi_level_t <parameter>*paragraph_embedding</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>size_t <function>unicode_bidi_embed</function></funcdef>
<paramdef>const char32_t *<parameter>string</parameter></paramdef>
<paramdef>const unicode_bidi_level_t *<parameter>levels</parameter></paramdef>
@@ -510,6 +555,9 @@ See COPYING for distribution information.
to remove the characters from the string which are used
by the bi-directional algorithm, and are not needed for
rendering the text.
+ <function>unicode_bidi_cleaned_size</function>() is
+ available to determine, in advance, how many characters
+ will remain.
</para>
</listitem>
</orderedlist>
@@ -617,8 +665,46 @@ See COPYING for distribution information.
return the resolved
paragraph direction level, which
always matches the passed in level, if specified, else it
- reports the
- derived one.
+ reports the derived one. These functions return a
+ <structname>unicode_bidi_direction</structname> structure:
+ </para>
+
+ <informaltable border='0'>
+ <tgroup cols="3">
+ <colspec colname='c1' />
+ <colspec colname='c2' />
+ <colspec colname='c3' />
+ <tbody>
+ <row>
+ <entry namest='c1' nameend='c3'>struct&nbsp;<structname>unicode_bidi_direction</structname>&nbsp;{</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry>unicode_bidi_level_t</entry>
+ <entry><varname>direction</varname>;</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry>int</entry>
+ <entry><varname>is_explicit</varname>;</entry>
+ </row>
+ <row>
+ <entry namest='c1' nameend='c3'>};</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ <varname>direction</varname> gives the paragraph embedding
+ level, <literal>UNICODE_BIDI_LR</literal> or
+ <literal> UNICODE_BIDI_RL</literal>.
+ <varname>is_explicit</varname> indicates whether:
+ the optional pointer to a
+ <literal>UNICODE_BIDI_LR</literal> or
+ <literal>UNICODE_BIDI_RL</literal> value was specified (and
+ returned in <varname>direction</varname>), or whether the
+ <varname>direction</varname> comes from an character with an
+ explicit direction indication.
</para>
<para>
@@ -794,18 +880,25 @@ See COPYING for distribution information.
with the <literal>UNICODE_BIDI_CLEANUP_CANONICAL</literal>
are in
<quote>canonical rendering order</quote>.
- <function>unicode_bidi_logical_order</function>() and
+ <function>unicode_bidi_logical_order</function>(),
+ <function>unicode_bidi_needs_embed</function>() and
<function>unicode_bidi_embed</function>() require the
canonical rendering order for their string and embedding level
values.
</para>
+ <para>
+ The parameters to <function>unicode_bidi_cleaned_size</function>()
+ are a pointer to the unicode string, its size, and
+ the bitmask option to <function>unicode_bidi_cleanup</function>().
+ </para>
</refsect2>
<refsect2 id="unicode_bidi_embed">
<title>Embedding bi-directional markers in Unicode text strings</title>
<para>
- <function>unicode_bidi_logical_order</function>() and
- <function>unicode_bidi_embed</function>() add various
+ <function>unicode_bidi_logical_order</function>() rearranges
+ the string from rendering to its logical order.
+ <function>unicode_bidi_embed</function>() adds various
bi-directional markers to a Unicode string in canonical rendering
order. The resulting string is not guaranteed to be
identical to the
@@ -819,12 +912,18 @@ See COPYING for distribution information.
<function>unicode_bidi_cleanup()</function>
(with the canonical option),
with the same paragraph_embedding level.
+ <function>unicode_bidi_needs_embed</function>() attempts to
+ heuristically determine whether
+ <function>unicode_bidi_embed</function>() is required.
</para>
<para>
<function>unicode_bidi_logical_order</function>() gets called
first, followed by
- <function>unicode_bidi_embed</function>().
+ <function>unicode_bidi_embed</function>()
+ (or
+ <function>unicode_bidi_needs_embed</function>() in order to
+ determine whether bi-directional markers are required).
Finally, <function>unicode_bidi_embed_paragraph_level</function>()
optionally determines whether the resulting string's default
paragraph embedding level matches the one used for the actual
@@ -881,12 +980,12 @@ See COPYING for distribution information.
<itemizedlist>
<listitem>
<para>
- The Unicode string, and &hellip;
+ The Unicode string.
</para>
</listitem>
<listitem>
<para>
- &hellip; the directional embedding buffer, in canonical
+ The directional embedding buffer, in canonical
rendering order.
</para>
</listitem>
@@ -998,6 +1097,53 @@ See COPYING for distribution information.
</para>
</listitem>
</itemizedlist>
+
+ <para>
+ <function>unicode_bidi_needs_embed</function>() attempts to
+ heuristically determine whether the Unicode string, in logical
+ order, requires bi-directional markers.
+ The parameters to
+ <function>unicode_bidi_embed_paragraph_level</function>() are:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ The Unicode string.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The directional embedding buffer, in logical
+ rendering order.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The size of the string and the embedding level buffer.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A pointer to an explicit paragraph embedding level, either
+ <literal>UNICODE_BIDI_LR</literal> or
+ <literal>UNICODE_BIDI_RL</literal>; or a
+ <literal>NULL</literal> pointer (see
+ <function>unicode_bidi_calc_types</function>()'s
+ explanation for this parameter).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <function>unicode_bidi_needs_embed</function>() returns 0
+ if the Unicode string does not need explicit directional
+ markers, or 1 if it does. This is done by using
+ <function>unicode_bidi_calc()</function>,
+ <function>unicode_bidi_reorder()</function>,
+ <function>unicode_bidi_logical_order</function> and then
+ checking if the end result is different from what was passed
+ in.
+ </para>
</refsect2>
<refsect2 id="unicode_bidi_misc">
<title>Miscellaneous utility functions</title>
@@ -2837,9 +2983,11 @@ See COPYING for distribution information.
<refname>unicode::bidi_reorder</refname>
<refname>unicode::bidi_cleanup</refname>
<refname>unicode::bidi_logical_order</refname>
+ <refname>unicode::bidi_needs_embed</refname>
<refname>unicode::bidi_embed</refname>
<refname>unicode::bidi_embed_paragraph_level</refname>
<refname>unicode::bidi_get_direction</refname>
+ <refname>unicode::bidi_override</refname>
<refpurpose>unicode bi-directional algorithm</refpurpose>
</refnamediv>
@@ -2871,12 +3019,12 @@ See COPYING for distribution information.
<funcsynopsis>
<funcprototype>
- <funcdef>std::tuple&lt;std::vector&lt;unicode_bidi_level_t&gt;, unicode_bidi_level_t&gt; <function>unicode::bidi_calc</function></funcdef>
+ <funcdef>std::tuple&lt;std::vector&lt;unicode_bidi_level_t&gt;, struct unicode_bidi_direction&gt; <function>unicode::bidi_calc</function></funcdef>
<paramdef>const unicode::bidi_calc_types &amp;<parameter>ustring</parameter></paramdef>
</funcprototype>
<funcprototype>
- <funcdef>std::tuple&lt;std::vector&lt;unicode_bidi_level_t&gt;, unicode_bidi_level_t&gt; <function>unicode::bidi_calc</function></funcdef>
+ <funcdef>std::tuple&lt;std::vector&lt;unicode_bidi_level_t&gt;, struct unicode_bidi_direction&gt; <function>unicode::bidi_calc</function></funcdef>
<paramdef>const unicode::bidi_calc_types &amp;<parameter>ustring</parameter></paramdef>
<paramdef>unicode_bidi_level_t <parameter>embedding_level</parameter></paramdef>
</funcprototype>
@@ -2943,6 +3091,15 @@ See COPYING for distribution information.
</funcprototype>
<funcprototype>
+ <funcdef>bool <function>unicode::bidi_needs_embed</function></funcdef>
+ <paramdef>const std::u32string &amp;<parameter>string</parameter></paramdef>
+ <paramdef>const std::vector &lt;unicode_bidi_level_t&gt; &amp;<parameter>levels</parameter></paramdef>
+ <paramdef>const unicode_bidi_level_t (<parameter>paragraph_embedding</parameter>=NULL</paramdef>
+ <paramdef>size_t <parameter>starting_pos</parameter>=0</paramdef>
+ <paramdef>size_t <parameter>n</parameter>=(size_t)-1</paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>unicode::bidi_embed</function></funcdef>
<paramdef>const std::u32string &amp;<parameter>string</parameter></paramdef>
<paramdef>const std::vector &lt;unicode_bidi_level_t&gt; &amp;<parameter>levels</parameter></paramdef>
@@ -2969,6 +3126,13 @@ See COPYING for distribution information.
<paramdef>size_t <parameter>starting_pos</parameter>=0</paramdef>
<paramdef>size_t <parameter>n</parameter>=(size_t)-1</paramdef>
</funcprototype>
+
+ <funcprototype>
+ <funcdef>std::u32string <function>bidi_override</function></funcdef>
+ <paramdef>const std::u32string &amp;<parameter>string</parameter></paramdef>
+ <paramdef>unicode_bidi_level_t <parameter>direction</parameter></paramdef>
+ <paramdef>int <parameter>cleanup_options</parameter>=0</paramdef>
+ </funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -2999,7 +3163,7 @@ See COPYING for distribution information.
<programlisting><![CDATA[
std::u32string text;
-auto [levels, level]=unicode::bidi_calc(text);
+auto [levels, direction]=unicode::bidi_calc(text);
]]></programlisting>
</informalexample>
@@ -3022,7 +3186,7 @@ types.setbnl(text); // Optional
// types.types is a std::vector of enum_bidi_types_t values
-auto [levels, level]=unicode::bidi_calc(types);
+auto [levels, direction]=unicode::bidi_calc(types);
]]></programlisting>
</informalexample>
@@ -3106,7 +3270,8 @@ auto [levels, level]=unicode::bidi_calc(types);
<para>
<function>unicode::bidi_reorder</function>,
<function>unicode::bidi_cleanup</function>,
- <function>unicode::bidi_logical_order</function> and
+ <function>unicode::bidi_logical_order</function>,
+ <function>unicode::bidi_needs_embed</function> and
<function>unicode::bidi_get_direction</function>
take two optional
parameters (defaulted values or overloaded) specifying
@@ -3124,6 +3289,41 @@ auto [levels, level]=unicode::bidi_calc(types);
</para>
</listitem>
+ <listitem>
+ <para>
+ <function>unicode::bidi_override</function>
+ modifies the passed-in <parameter>string</parameter> as
+ follows:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <function>unicode::bidi_cleanup</function>() is applied
+ with the specified, or defaulted,
+ <replaceable>cleanup_options</replaceable>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Either the <literal>LRO</literal> or an
+ <literal>RLO</literal> override marker gets prepended
+ to the Unicode string, forcing the entire string to
+ be interpreted in a single rendering direction, when
+ processed by the Unicode bi-directional algorithm.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <function>unicode::bidi_override</function> makes it
+ possible to use a Unicode-aware application or algorithm
+ in a context that only works with text that's always
+ displayed in a fixed direction, allowing graceful handling
+ of input containing bi-directional text.
+ </para>
+ </listitem>
</itemizedlist>
<refsect2 id="unicode_cpp_bidi_literals">
diff --git a/unicode/configure.ac b/unicode/configure.ac
index 5c366de..1cc3b76 100644
--- a/unicode/configure.ac
+++ b/unicode/configure.ac
@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
-AC_INIT([courier-unicode], [2.2], [courier-users@lists.sourceforge.net])
+AC_INIT([courier-unicode], [2.2.1.20210220], [courier-users@lists.sourceforge.net])
>confdefs.h # Kill PACKAGE_ macros
@@ -117,5 +117,25 @@ fi
CFLAGS="-I.. -I$srcdir/.. $CFLAGS"
CXXFLAGS="-I.. -I$srcdir/.. $CXXFLAGS"
+set -- `echo "$VERSION" | tr '.' ' '`
+
+v=$1
+r=`echo "00"$2 | sed 's/.*(...)$/$1/'`
+p=$3
+
+if test "$p" = ""
+ then p="0"
+fi
+
+p=`echo "00"$p | sed 's/.*(...)$/$[]1/'`
+
+HVERSION="$v$r$p"
+AC_SUBST(HVERSION)
+
AM_CONDITIONAL(HAVE_DOCS,[test -f $srcdir/docbook/icon.gif])
-AC_OUTPUT(Makefile packaging/freebsd10/Makefile courier-unicode.spec courier-unicode.h)
+AC_OUTPUT(Makefile
+ packaging/freebsd10/Makefile
+ courier-unicode.spec
+ courier-unicode.h
+ courier-unicode-version.m4
+)
diff --git a/unicode/courier-unicode-version.m4.in b/unicode/courier-unicode-version.m4.in
new file mode 100644
index 0000000..94b0c04
--- /dev/null
+++ b/unicode/courier-unicode-version.m4.in
@@ -0,0 +1,45 @@
+dnl Checks for the correct version of the courier-unicode library.
+
+AC_DEFUN([AX_COURIER_UNICODE_VERSION],[
+
+AC_MSG_CHECKING(courier-unicode library and version)
+
+vers="$1"
+
+if test "$vers" = ""
+then
+ vers=@VERSION@
+fi
+
+set -- `echo "$vers" | tr '.' ' '`
+
+v=$[]1
+r=`echo "00"$[]2 | sed 's/.*(...)$/$[]1/'`
+
+p=$[]3
+
+if test "$p" = ""
+ then p="0"
+fi
+
+p=`echo "00"$p | sed 's/.*(...)$/$[]1/'`
+
+AC_TRY_COMPILE([
+#include <courier-unicode.h>
+#ifndef COURIER_UNICODE_VERSION
+#define COURIER_UNICODE_VERSION 0
+#endif
+
+#if COURIER_UNICODE_VERSION < ]$v$r$p[
+#error "courier-unicode ]$vers[ library is required"
+#endif
+
+],[],[],
+AC_MSG_ERROR([
+ERROR: The Courier Unicode Library ]$vers[ header files appear not to be installed.
+You may need to upgrade the library or install a separate development
+subpackage in addition to the main package.])
+)
+
+AC_MSG_RESULT([ok])
+])
diff --git a/unicode/courier-unicode.h.in b/unicode/courier-unicode.h.in
index 57603da..2999ee3 100644
--- a/unicode/courier-unicode.h.in
+++ b/unicode/courier-unicode.h.in
@@ -41,7 +41,7 @@ typedef uint32_t char32_t;
#endif
#endif
-#define COURIER_UNICODE_VERSION 220
+#define COURIER_UNICODE_VERSION @HVERSION@
/*
** The system default character set, from the locale.
@@ -604,10 +604,10 @@ struct unicode_bidi_direction {
struct unicode_bidi_direction unicode_bidi_get_direction(const char32_t *p,
size_t n);
-extern unicode_bidi_level_t unicode_bidi_calc(const char32_t *p, size_t n,
- unicode_bidi_level_t *bufp,
- const unicode_bidi_level_t *
- initial_embedding_level);
+struct unicode_bidi_direction unicode_bidi_calc(const char32_t *p, size_t n,
+ unicode_bidi_level_t *bufp,
+ const unicode_bidi_level_t *
+ initial_embedding_level);
extern void unicode_bidi_reorder(char32_t *p,
unicode_bidi_level_t *levels,
@@ -652,13 +652,15 @@ extern void unicode_bidi_setbnl(char32_t *p,
const enum_bidi_type_t *types,
size_t n);
-extern unicode_bidi_level_t unicode_bidi_calc_levels(const char32_t *p,
- const enum_bidi_type_t
- *types,
- size_t n,
- unicode_bidi_level_t *bufp,
- const unicode_bidi_level_t
- *initial_embedding_level);
+extern struct unicode_bidi_direction
+unicode_bidi_calc_levels(const char32_t *p,
+ const enum_bidi_type_t
+ *types,
+ size_t n,
+ unicode_bidi_level_t *bufp,
+ const unicode_bidi_level_t
+ *initial_embedding_level);
+
/* Bitmask options to unicode_bidi_cleanup */
/*
@@ -703,6 +705,10 @@ extern size_t unicode_bidi_cleanup(char32_t *string,
void (*removed_callback)(size_t, void *),
void *);
+extern size_t unicode_bidi_cleaned_size(const char32_t *string,
+ size_t n,
+ int options);
+
extern void unicode_bidi_logical_order(char32_t *string,
unicode_bidi_level_t *levels,
size_t n,
@@ -711,6 +717,12 @@ extern void unicode_bidi_logical_order(char32_t *string,
void *),
void *arg);
+extern int unicode_bidi_needs_embed(const char32_t *string,
+ const unicode_bidi_level_t *levels,
+ size_t n,
+ const unicode_bidi_level_t *
+ paragraph_embedding);
+
extern void unicode_bidi_embed(const char32_t *string,
const unicode_bidi_level_t *levels,
size_t n,
@@ -2231,7 +2243,7 @@ struct bidi_calc_types {
//! can be constructed explicitly, and then passed in directly.
std::tuple<std::vector<unicode_bidi_level_t>,
- unicode_bidi_level_t> bidi_calc(const bidi_calc_types &s);
+ struct unicode_bidi_direction> bidi_calc(const bidi_calc_types &s);
//! Calculate bidirectional embedding levels
@@ -2242,8 +2254,8 @@ std::tuple<std::vector<unicode_bidi_level_t>,
//! embedding level.
std::tuple<std::vector<unicode_bidi_level_t>,
- unicode_bidi_level_t> bidi_calc(const bidi_calc_types &s,
- unicode_bidi_level_t level);
+ struct unicode_bidi_direction> bidi_calc(const bidi_calc_types &s,
+ unicode_bidi_level_t level);
//! Reorder bidirectional text
@@ -2322,6 +2334,14 @@ void bidi_logical_order(std::vector<unicode_bidi_level_t> &levels,
size_t starting_pos=0,
size_t n=(size_t)-1);
+//! Whether directional and isolation markers are needed.
+
+bool bidi_needs_embed(const std::u32string &string,
+ const std::vector<unicode_bidi_level_t> &levels,
+ const unicode_bidi_level_t *paragraph_embedding=0,
+ size_t starting_pos=0,
+ size_t n=(size_t)-1);
+
//! Embed directional and isolation markers
//! Non-0 return value indicates the string and levels' sizes do not match.
@@ -2352,14 +2372,19 @@ std::u32string bidi_embed(const std::u32string &string,
//! In order for the unicode string to have the specified default
//! paragraph embedding level.
-extern char32_t bidi_embed_paragraph_level(const std::u32string &string,
- unicode_bidi_level_t level);
+char32_t bidi_embed_paragraph_level(const std::u32string &string,
+ unicode_bidi_level_t level);
//! Compute default direction of text
-extern unicode_bidi_direction bidi_get_direction(const std::u32string &string,
- size_t starting_pos=0,
- size_t n=(size_t)-1);
+unicode_bidi_direction bidi_get_direction(const std::u32string &string,
+ size_t starting_pos=0,
+ size_t n=(size_t)-1);
+
+//! Override bidi direction.
+std::u32string bidi_override(const std::u32string &s,
+ unicode_bidi_level_t direction,
+ int cleanup_options=0);
#if 0
{
diff --git a/unicode/courier-unicode.spec.in b/unicode/courier-unicode.spec.in
index f7d1eb6..440d6f1 100644
--- a/unicode/courier-unicode.spec.in
+++ b/unicode/courier-unicode.spec.in
@@ -1,5 +1,12 @@
Summary: Courier Unicode Library
+%if 0%{?compat:1}
+Name: courier-unicode%(echo @VERSION@ | tr -d '.')
+
+%define __brp_ldconfig %{nil}
+
+%else
Name: courier-unicode
+%endif
Version: @VERSION@
Release: 1%{?dist}%{?courier_release}
License: GPLv3
@@ -11,10 +18,14 @@ BuildRequires: perl
BuildRequires: gcc-c++
BuildRequires: %{__make}
+%if 0%{?compat:1}
+
+%else
%package devel
Summary: Courier Unicode Library development files
Group: Development/Libraries
Requires: %{name} = 0:%{version}-%{release}
+%endif
%description
This library implements several algorithms related to the Unicode
@@ -24,13 +35,17 @@ This package installs only the run-time libraries needed by applications that
use this library. Install the "courier-unicode-devel" package if you want
to develop new applications using this library.
+%if 0%{?compat:1}
+
+%else
%description devel
This package contains development files for the Courier Unicode Library.
Install this package if you want to develop applications that uses this
unicode library.
+%endif
%prep
-%setup -q
+%setup -q -n courier-unicode-@VERSION@
%configure
%build
%{__make} -s %{?_smp_mflags}
@@ -39,12 +54,27 @@ unicode library.
rm -rf $RPM_BUILD_ROOT
%{__make} install DESTDIR=$RPM_BUILD_ROOT
-%post -p /sbin/ldconfig
-%postun -p /sbin/ldconfig
+%if 0%{?compat:1}
+find $RPM_BUILD_ROOT%{_libdir} -type l -print | xargs rm -f
+rm -rf $RPM_BUILD_ROOT%{_includedir}
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+rm -rf $RPM_BUILD_ROOT%{_datadir}/aclocal
+rm -rf $RPM_BUILD_ROOT%{_mandir}
+%endif
%clean
rm -rf $RPM_BUILD_ROOT
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+
+%if 0%{?compat:1}
+%files
+%defattr(-,root,root,-)
+%{_libdir}/*.so.*
+
+%else
%files
%defattr(-,root,root,-)
@@ -58,6 +88,7 @@ rm -rf $RPM_BUILD_ROOT
%{_libdir}/*.la
%{_libdir}/*.a
%{_datadir}/aclocal/*.m4
+%endif
%changelog
* Sun Jan 12 2014 Sam Varshavchik <mrsam@octopus.email-scan.com> - 1.0
diff --git a/unicode/m4/courier-unicode.m4 b/unicode/m4/courier-unicode.m4
index dadf321..673bc65 100644
--- a/unicode/m4/courier-unicode.m4
+++ b/unicode/m4/courier-unicode.m4
@@ -50,44 +50,3 @@ AC_MSG_ERROR([*** A compiler with C++11 Unicode support was not found])
CXXFLAGS="$save_FLAGS"
AC_LANG_POP([C++])
])
-
-AC_DEFUN([AX_COURIER_UNICODE_VERSION],[
-
-AC_MSG_CHECKING(courier-unicode library and version)
-
-v="$1"
-
-if test "$v" = ""
-then
- v=2.2
-fi
-
-set -- `echo "$v" | tr '.' ' '`
-
-v=$[]1
-r=$[]2
-p=$[]3
-
-if test "$p" = ""
- then p="0"
-fi
-
-AC_TRY_COMPILE([
-#include <courier-unicode.h>
-#ifndef COURIER_UNICODE_VERSION
-#define COURIER_UNICODE_VERSION 0
-#endif
-
-#if COURIER_UNICODE_VERSION < ]$v$r$p[
-#error "courier-unicode ]$1[ library is required"
-#endif
-
-],[],[],
-AC_MSG_ERROR([
-ERROR: The Courier Unicode Library ]$1[ header files appear not to be installed.
-You may need to upgrade the library or install a separate development
-subpackage in addition to the main package.])
-)
-
-AC_MSG_RESULT([ok])
-])
diff --git a/unicode/unicode_bidi.c b/unicode/unicode_bidi.c
index b23b833..772f9fe 100644
--- a/unicode/unicode_bidi.c
+++ b/unicode/unicode_bidi.c
@@ -464,7 +464,7 @@ struct directional_status_stack_entry {
typedef struct {
struct directional_status_stack_entry *head;
- unicode_bidi_level_t paragraph_embedding_level;
+ struct unicode_bidi_direction paragraph_embedding_level;
const char32_t *chars;
enum_bidi_type_t *types;
const enum_bidi_type_t *orig_types;
@@ -618,7 +618,7 @@ get_enum_bidi_type_for_paragraph_embedding_level(size_t i,
return p->p[i];
}
-static unicode_bidi_level_t
+static struct unicode_bidi_direction
compute_paragraph_embedding_level_from_types(const enum_bidi_type_t *p,
size_t i, size_t j)
{
@@ -628,7 +628,7 @@ compute_paragraph_embedding_level_from_types(const enum_bidi_type_t *p,
return compute_paragraph_embedding_level
(i, j,
get_enum_bidi_type_for_paragraph_embedding_level,
- &info).direction;
+ &info);
}
static directional_status_stack_t
@@ -642,10 +642,18 @@ directional_status_stack_init(const char32_t *chars,
stack=(directional_status_stack_t)calloc(1, sizeof(*stack));
- stack->paragraph_embedding_level=
- initial_embedding_level
- ? *initial_embedding_level & 1
- : compute_paragraph_embedding_level_from_types(types, 0, n);
+ if (initial_embedding_level)
+ {
+ stack->paragraph_embedding_level.direction=
+ *initial_embedding_level & 1;
+ stack->paragraph_embedding_level.is_explicit=1;
+ }
+ else
+ {
+ stack->paragraph_embedding_level=
+ compute_paragraph_embedding_level_from_types(types,
+ 0, n);
+ }
stack->chars=chars;
stack->orig_types=types;
@@ -666,7 +674,8 @@ directional_status_stack_init(const char32_t *chars,
stack->size=n;
directional_status_stack_push(stack,
- stack->paragraph_embedding_level,
+ stack->paragraph_embedding_level
+ .direction,
do_neutral, 0);
return stack;
@@ -736,7 +745,7 @@ void unicode_bidi_setbnl(char32_t *p,
}
}
-unicode_bidi_level_t
+struct unicode_bidi_direction
unicode_bidi_calc(const char32_t *p, size_t n, unicode_bidi_level_t *bufp,
const unicode_bidi_level_t *initial_embedding_level)
{
@@ -748,7 +757,7 @@ unicode_bidi_calc(const char32_t *p, size_t n, unicode_bidi_level_t *bufp,
unicode_bidi_calc_types(p, n, buf);
- unicode_bidi_level_t level=
+ struct unicode_bidi_direction level=
unicode_bidi_calc_levels(p,
buf,
n,
@@ -762,7 +771,7 @@ unicode_bidi_calc(const char32_t *p, size_t n, unicode_bidi_level_t *bufp,
static void unicode_bidi_cl(directional_status_stack_t stack);
-unicode_bidi_level_t
+struct unicode_bidi_direction
unicode_bidi_calc_levels(const char32_t *p,
const enum_bidi_type_t *types,
size_t n,
@@ -779,12 +788,12 @@ unicode_bidi_calc_levels(const char32_t *p,
stack=directional_status_stack_init(p, types, n, bufp,
initial_embedding_level);
- unicode_bidi_level_t paragraph_embedding_level=
+ struct unicode_bidi_direction paragraph_embedding_level=
stack->paragraph_embedding_level;
#ifdef BIDI_DEBUG
fprintf(DEBUGDUMP, "BIDI: START: Paragraph embedding level: %d\n",
- (int)stack->paragraph_embedding_level);
+ (int)paragraph_embedding_level.direction);
#endif
unicode_bidi_cl(stack);
@@ -971,7 +980,8 @@ static void unicode_bidi_cl(directional_status_stack_t stack)
}
cur_class=compute_paragraph_embedding_level_from_types
- (stack->types, i+1, j) == 1
+ (stack->types, i+1, j).direction
+ != UNICODE_BIDI_LR
? UNICODE_BIDI_TYPE_RLI
: UNICODE_BIDI_TYPE_LRI;
}
@@ -1104,7 +1114,8 @@ static void unicode_bidi_cl(directional_status_stack_t stack)
{
/* X8 */
- stack->levels[i]=stack->paragraph_embedding_level;
+ stack->levels[i]=
+ stack->paragraph_embedding_level.direction;
}
}
@@ -1203,9 +1214,9 @@ static void unicode_bidi_cl(directional_status_stack_t stack)
continue; /* Edge case */
unicode_bidi_level_t before=
- stack->paragraph_embedding_level;
+ stack->paragraph_embedding_level.direction;
unicode_bidi_level_t after=
- stack->paragraph_embedding_level;
+ stack->paragraph_embedding_level.direction;
size_t first_i=beg_iter.i;
@@ -1301,11 +1312,11 @@ static void unicode_bidi_cl(directional_status_stack_t stack)
case UNICODE_BIDI_TYPE_PDI:
if (seen_sb)
stack->levels[i]=
- stack->paragraph_embedding_level;
+ stack->paragraph_embedding_level.direction;
break;
case UNICODE_BIDI_TYPE_S:
case UNICODE_BIDI_TYPE_B:
- stack->levels[i]=stack->paragraph_embedding_level;
+ stack->levels[i]=stack->paragraph_embedding_level.direction;
seen_sb=1;
break;
default:
@@ -2052,12 +2063,14 @@ void unicode_bidi_reorder(char32_t *p,
level_run_layers_deinit(&layers);
}
-size_t unicode_bidi_cleanup(char32_t *string,
- unicode_bidi_level_t *levels,
- size_t n,
- int cleanup_options,
- void (*removed_callback)(size_t, void *),
- void *arg)
+static size_t unicode_bidi_count_or_cleanup(const char32_t *string,
+ char32_t *dest,
+ unicode_bidi_level_t *levels,
+ size_t n,
+ int cleanup_options,
+ void (*removed_callback)(size_t,
+ void *),
+ void *arg)
{
size_t i=0;
for (size_t j=0; j<n; ++j)
@@ -2079,13 +2092,34 @@ size_t unicode_bidi_cleanup(char32_t *string,
if (levels)
levels[i]=levels[j] & 1;
- string[i]=(cleanup_options & UNICODE_BIDI_CLEANUP_BNL)
- && cl == UNICODE_BIDI_TYPE_B ? '\n' : string[j];
+ if (dest)
+ dest[i]=(cleanup_options & UNICODE_BIDI_CLEANUP_BNL)
+ && cl == UNICODE_BIDI_TYPE_B ? '\n' : string[j];
++i;
}
return i;
}
+size_t unicode_bidi_cleanup(char32_t *string,
+ unicode_bidi_level_t *levels,
+ size_t n,
+ int cleanup_options,
+ void (*removed_callback)(size_t, void *),
+ void *arg)
+{
+ return unicode_bidi_count_or_cleanup(string, string, levels, n,
+ cleanup_options, removed_callback,
+ arg);
+}
+
+size_t unicode_bidi_cleaned_size(const char32_t *string,
+ size_t n,
+ int cleanup_options)
+{
+ return unicode_bidi_count_or_cleanup(string, NULL, NULL, n,
+ cleanup_options, NULL, NULL);
+}
+
void unicode_bidi_logical_order(char32_t *string,
unicode_bidi_level_t *levels,
size_t n,
@@ -2276,6 +2310,49 @@ static void emit_marker(struct bidi_embed_levelrun *p,
}
}
+int unicode_bidi_needs_embed(const char32_t *string,
+ const unicode_bidi_level_t *levels,
+ size_t n,
+ const unicode_bidi_level_t *paragraph_level)
+{
+ char32_t *string_cpy=(char32_t *)malloc(n * sizeof(char32_t));
+ unicode_bidi_level_t *levels_cpy=(unicode_bidi_level_t *)
+ malloc(n * sizeof(unicode_bidi_level_t));
+ size_t nn;
+ int ret;
+
+ if (!string_cpy || !levels_cpy)
+ abort();
+
+ memcpy(string_cpy, string, n * sizeof(char32_t));
+
+ struct unicode_bidi_direction direction=
+ unicode_bidi_calc(string_cpy, n,
+ levels_cpy, paragraph_level);
+
+ unicode_bidi_reorder(string_cpy, levels_cpy, n, NULL, NULL);
+ nn=unicode_bidi_cleanup(string_cpy, levels_cpy, n, 0,
+ NULL, NULL);
+
+ ret=0;
+ if (n == nn && (paragraph_level == NULL ||
+ direction.direction == *paragraph_level))
+ {
+ unicode_bidi_logical_order(string_cpy, levels_cpy, nn,
+ direction.direction,
+ NULL, NULL);
+ if (memcmp(string_cpy, string, n * sizeof(char32_t)) == 0 &&
+ memcmp(levels_cpy, levels, n * sizeof(unicode_bidi_level_t))
+ == 0)
+ {
+ ret=1;
+ }
+ }
+ free(string_cpy);
+ free(levels_cpy);
+ return ret;
+}
+
void unicode_bidi_embed(const char32_t *string,
const unicode_bidi_level_t *levels,
size_t n,
diff --git a/unicode/unicodecpp.C b/unicode/unicodecpp.C
index babb6bb..7bb6edc 100644
--- a/unicode/unicodecpp.C
+++ b/unicode/unicodecpp.C
@@ -581,18 +581,29 @@ void unicode::bidi_calc_types::setbnl(std::u32string &s)
unicode_bidi_setbnl(&s[0], &types[0], s.size());
}
-std::tuple<std::vector<unicode_bidi_level_t>, unicode_bidi_level_t>
+std::tuple<std::vector<unicode_bidi_level_t>,
+ struct unicode_bidi_direction>
unicode::bidi_calc(const bidi_calc_types &s)
{
return unicode::bidi_calc(s, UNICODE_BIDI_SKIP);
}
-std::tuple<std::vector<unicode_bidi_level_t>, unicode_bidi_level_t>
+std::tuple<std::vector<unicode_bidi_level_t>,
+ struct unicode_bidi_direction>
unicode::bidi_calc(const bidi_calc_types &st,
unicode_bidi_level_t paragraph_embedding_level)
{
+ std::tuple<std::vector<unicode_bidi_level_t>,
+ struct unicode_bidi_direction>
+ ret;
+ auto &direction_ret=std::get<1>(ret);
+
if (st.s.size() != st.types.size())
- return { {}, UNICODE_BIDI_LR };
+ {
+ direction_ret.direction=UNICODE_BIDI_LR;
+ direction_ret.is_explicit=false;
+ return ret;
+ }
const unicode_bidi_level_t *initial_embedding_level=0;
@@ -602,11 +613,17 @@ unicode::bidi_calc(const bidi_calc_types &st,
initial_embedding_level=&paragraph_embedding_level;
}
- std::tuple<std::vector<unicode_bidi_level_t>, unicode_bidi_level_t>
- ret;
-
std::get<0>(ret).resize(st.s.size());
- std::get<1>(ret)=UNICODE_BIDI_LR;
+
+ if (initial_embedding_level)
+ {
+ direction_ret.direction=paragraph_embedding_level;
+ direction_ret.is_explicit=1;
+ }
+ else
+ {
+ direction_ret.direction= UNICODE_BIDI_LR;
+ }
if (st.s.size())
{
@@ -932,3 +949,43 @@ unicode_bidi_direction unicode::bidi_get_direction(const std::u32string &string,
return unicode_bidi_get_direction(string.c_str()+starting_pos, n);
}
+
+bool unicode::bidi_needs_embed(const std::u32string &string,
+ const std::vector<unicode_bidi_level_t> &levels,
+ const unicode_bidi_level_t *paragraph_embedding,
+ size_t starting_pos,
+ size_t n)
+{
+ if (string.size() != levels.size())
+ return false;
+
+ auto s=levels.size();
+
+ if (starting_pos >= s)
+ return false;
+
+ if (n > s-starting_pos)
+ n=s-starting_pos;
+
+ return unicode_bidi_needs_embed(string.c_str(),
+ n == 0 ? NULL : &levels[starting_pos],
+ n,
+ paragraph_embedding) != 0;
+}
+
+std::u32string unicode::bidi_override(const std::u32string &s,
+ unicode_bidi_level_t direction,
+ int cleanup_options)
+{
+ std::u32string ret;
+
+ ret.reserve(s.size()+1);
+
+ ret.push_back(' ');
+ ret.insert(ret.end(), s.begin(), s.end());
+
+ bidi_cleanup(ret, [](size_t) {}, cleanup_options);
+ ret.at(0)=direction & 1 ? UNICODE_RLO : UNICODE_LRO;
+
+ return ret;
+}