Import ezmlm-idx 0.40 idx idx/0.40
authorMark Wooding <mdw@metalzone.distorted.org.uk>
Tue, 14 Feb 2006 14:25:57 +0000 (14:25 +0000)
committerMark Wooding <mdw@metalzone.distorted.org.uk>
Tue, 14 Feb 2006 14:25:57 +0000 (14:25 +0000)
179 files changed:
BIN
CHANGES.idx [new file with mode: 0644]
DOWNGRADE.idx [new file with mode: 0644]
FAQ.idx [new file with mode: 0644]
FILES.idx [new file with mode: 0644]
INSTALL.idx [new file with mode: 0644]
LICENCE.TXT [new file with mode: 0644]
MAN
Makefile
README.idx [new file with mode: 0644]
TARGETS [new file with mode: 0644]
UPGRADE.idx [new file with mode: 0644]
VERSION
author.c [new file with mode: 0644]
auto_cron.h [new file with mode: 0644]
case_diffs.c [new file with mode: 0644]
case_starts.c [new file with mode: 0644]
checktag.c [new symlink]
concatHDR.c [new file with mode: 0644]
conf-cron [new file with mode: 0644]
conf-sqlcc [new symlink]
conf-sqlld [new symlink]
constmap.c
constmap.h
copy.c [new file with mode: 0644]
copy.h [new file with mode: 0644]
date2yyyymm.c [new file with mode: 0644]
dateline.c [new file with mode: 0644]
decodeB.c [new file with mode: 0644]
decodeHDR.c [new file with mode: 0644]
decodeQ.c [new file with mode: 0644]
encodeB.c [new file with mode: 0644]
encodeQ.c [new file with mode: 0644]
env.c [new file with mode: 0644]
error.c
error.h
errtxt.h [new file with mode: 0644]
ezcgi.css [new file with mode: 0644]
ezcgirc [new file with mode: 0644]
ezmlm-accept.1 [new file with mode: 0644]
ezmlm-accept.sh [new file with mode: 0644]
ezmlm-archive.1 [new file with mode: 0644]
ezmlm-archive.c [new file with mode: 0644]
ezmlm-cgi.1 [new file with mode: 0644]
ezmlm-cgi.c [new file with mode: 0644]
ezmlm-check.1 [new file with mode: 0644]
ezmlm-check.sh [new file with mode: 0644]
ezmlm-clean.1 [new file with mode: 0644]
ezmlm-clean.c [new file with mode: 0644]
ezmlm-cron.1 [new file with mode: 0644]
ezmlm-cron.c [new file with mode: 0644]
ezmlm-gate.1 [new file with mode: 0644]
ezmlm-gate.c [new file with mode: 0644]
ezmlm-get.1 [new file with mode: 0644]
ezmlm-get.c [new file with mode: 0644]
ezmlm-glconf.1 [new file with mode: 0644]
ezmlm-glconf.sh [new file with mode: 0644]
ezmlm-idx.1 [new file with mode: 0644]
ezmlm-idx.c [new file with mode: 0644]
ezmlm-issubn.1 [new file with mode: 0644]
ezmlm-issubn.c [new file with mode: 0644]
ezmlm-limit.1 [new file with mode: 0644]
ezmlm-limit.c [new file with mode: 0644]
ezmlm-list.1
ezmlm-list.c
ezmlm-make.1
ezmlm-make.c
ezmlm-manage.1
ezmlm-manage.c
ezmlm-mktab [new symlink]
ezmlm-mktab.1 [new file with mode: 0644]
ezmlm-moderate.1 [new file with mode: 0644]
ezmlm-moderate.c [new file with mode: 0644]
ezmlm-receipt.1 [new file with mode: 0644]
ezmlm-receipt.c [new file with mode: 0644]
ezmlm-reject.1
ezmlm-reject.c
ezmlm-request.1 [new file with mode: 0644]
ezmlm-request.c [new file with mode: 0644]
ezmlm-return.1
ezmlm-return.c
ezmlm-send.1
ezmlm-send.c
ezmlm-split.1 [new file with mode: 0644]
ezmlm-split.c [new file with mode: 0644]
ezmlm-store.1 [new file with mode: 0644]
ezmlm-store.c [new file with mode: 0644]
ezmlm-sub.1
ezmlm-sub.c
ezmlm-test.1 [new file with mode: 0644]
ezmlm-test.sh [new file with mode: 0644]
ezmlm-tstdig.1 [new file with mode: 0644]
ezmlm-tstdig.c [new file with mode: 0644]
ezmlm-unsub.1
ezmlm-unsub.c
ezmlm-warn.1
ezmlm-warn.c
ezmlm-weed.1
ezmlm-weed.c
ezmlm.5
ezmlmglrc [new file with mode: 0644]
ezmlmglrc.5 [new file with mode: 0644]
ezmlmrc.5 [new file with mode: 0644]
ezmlmrc.ch_GB [new file with mode: 0644]
ezmlmrc.cs [new file with mode: 0644]
ezmlmrc.da [new file with mode: 0644]
ezmlmrc.de [new file with mode: 0644]
ezmlmrc.en_US [new file with mode: 0644]
ezmlmrc.es [new file with mode: 0644]
ezmlmrc.fr [new file with mode: 0644]
ezmlmrc.id [new file with mode: 0644]
ezmlmrc.it [new file with mode: 0644]
ezmlmrc.jp [new file with mode: 0644]
ezmlmrc.pl [new file with mode: 0644]
ezmlmrc.pt [new file with mode: 0644]
ezmlmrc.pt_BR [new file with mode: 0644]
ezmlmrc.ru [new file with mode: 0644]
ezmlmrc.sv [new file with mode: 0644]
ezmlmsubrc [new file with mode: 0644]
ezmlmsubrc.5 [new file with mode: 0644]
idx.h [new file with mode: 0644]
idx.patch [new file with mode: 0644]
idxthread.c [new file with mode: 0644]
idxthread.h [new file with mode: 0644]
issub.c [changed from file to symlink]
log.c
logmsg.c [new symlink]
makehash.c [new file with mode: 0644]
makehash.h [new file with mode: 0644]
mime.h [new file with mode: 0644]
opensql.c [new symlink]
putsubs.c [new symlink]
qmail-qmqpc.tar.gz [new file with mode: 0644]
qmail-verh.tar.gz [new file with mode: 0644]
qmail.c
qmail.h
searchlog.c [new symlink]
sub_mysql/README [new file with mode: 0644]
sub_mysql/checktag.c [new file with mode: 0644]
sub_mysql/conf-sqlcc [new file with mode: 0644]
sub_mysql/conf-sqlld [new file with mode: 0644]
sub_mysql/ezmlm-mktab [new file with mode: 0755]
sub_mysql/issub.c [new file with mode: 0644]
sub_mysql/logmsg.c [new file with mode: 0644]
sub_mysql/opensql.c [new file with mode: 0644]
sub_mysql/putsubs.c [new file with mode: 0644]
sub_mysql/searchlog.c [new file with mode: 0644]
sub_mysql/subscribe.c [new file with mode: 0644]
sub_mysql/tagmsg.c [new file with mode: 0644]
sub_mysql/to40x [new file with mode: 0755]
sub_pgsql/README [new file with mode: 0644]
sub_pgsql/checktag.c [new file with mode: 0644]
sub_pgsql/conf-sqlcc [new file with mode: 0644]
sub_pgsql/conf-sqlld [new file with mode: 0644]
sub_pgsql/ezmlm-mktab [new file with mode: 0755]
sub_pgsql/issub.c [new file with mode: 0644]
sub_pgsql/logmsg.c [new file with mode: 0644]
sub_pgsql/opensql.c [new file with mode: 0644]
sub_pgsql/putsubs.c [new file with mode: 0644]
sub_pgsql/searchlog.c [new file with mode: 0644]
sub_pgsql/subscribe.c [new file with mode: 0644]
sub_pgsql/tagmsg.c [new file with mode: 0644]
sub_std/README [new file with mode: 0644]
sub_std/checktag.c [new file with mode: 0644]
sub_std/conf-sqlcc [new file with mode: 0644]
sub_std/conf-sqlld [new file with mode: 0644]
sub_std/ezmlm-mktab [new file with mode: 0755]
sub_std/issub.c [new file with mode: 0644]
sub_std/logmsg.c [new file with mode: 0644]
sub_std/opensql.c [new file with mode: 0644]
sub_std/putsubs.c [new file with mode: 0644]
sub_std/searchlog.c [new file with mode: 0644]
sub_std/subscribe.c [new file with mode: 0644]
sub_std/tagmsg.c [new file with mode: 0644]
subscribe.c [changed from file to symlink]
subscribe.h
tagmsg.c [new symlink]
unfoldHDR.c [new file with mode: 0644]
yyyymm.h [new file with mode: 0644]

diff --git a/BIN b/BIN
index 76f0320..1c9db2b 100644 (file)
--- a/BIN
+++ b/BIN
@@ -1,11 +1,31 @@
 d:::755:::
+c:::755:/:ezmlm-accept:
+c:::755:/:ezmlm-archive:
+c:::755:/:ezmlm-issubn:
+c:::755:/:ezmlm-glconf:
 c:::755:/:ezmlm-make:
+c:::755:/:ezmlm-mktab:
 c:::755:/:ezmlm-manage:
 c:::755:/:ezmlm-send:
+c:::755:/:ezmlm-request:
 c:::755:/:ezmlm-reject:
 c:::755:/:ezmlm-return:
 c:::755:/:ezmlm-warn:
 c:::755:/:ezmlm-weed:
 c:::755:/:ezmlm-list:
+c:::755:/:ezmlm-clean:
+c:::755:/:ezmlm-cron:
+c:::755:/:ezmlm-limit:
+c:::755:/:ezmlm-store:
+c:::755:/:ezmlm-split:
+c:::755:/:ezmlm-moderate:
 c:::755:/:ezmlm-sub:
 c:::755:/:ezmlm-unsub:
+c:::644:/:ezmlmrc:
+c:::644:/:ezmlmglrc:
+c:::644:/:ezmlmsubrc:
+c:::755:/:ezmlm-idx:
+c:::755:/:ezmlm-check:
+c:::755:/:ezmlm-gate:
+c:::755:/:ezmlm-tstdig:
+c:::755:/:ezmlm-get:
diff --git a/CHANGES.idx b/CHANGES.idx
new file mode 100644 (file)
index 0000000..49a4f8a
--- /dev/null
@@ -0,0 +1,842 @@
+$Id: CHANGES.idx,v 1.66 1999/12/24 04:33:03 lindberg Exp $
+$Name: ezmlm-idx-040 $
+
+BUGS:  -ezmlm-idx does not implement a full rfc822 parser. Comments can be
+        inserted to defeat the MIME parsing mechanism, such as:
+        Content-Type: (comment) test/plain
+        This type of comment, while legal, does not occur in normal messages.
+        Guiding info past ezmlm-idx MIME parsing is easier done by enclosing
+        a message within a message. The mechanism isn't intended as a
+        "secure" way of preventing e.g. "application/ms-tnef" from being
+        disseminated.
+        If you think this is a problem, E-mail lindberg@id.wustl.edu.
+        Mimeremove/reject work only at the outer MIME layer.
+       -ezmlm-request requires addresses to be user@host. While rfc822
+        compliant, addresses like ``"the man" @ host'' are not accepted.
+       -ezmlm-cgi doesn't deal optimally with holes in the archive.
+       -ezmlm-cgi seems not to work with Chinese. The problem has been to
+        find someone willing/able to participate in debugging.
+
+TODO:
+-ezmlm-cgi to run directly under tcpserver.
+
+Acknowledgements, some are missing. Please write lindberg@id.wustl.edu
+if you're missing.
+
+Some ezmlmrc are not updated and won't work right:
+cn_GB,cs,pl,ru,da,pt_BR,pt
+
+If you fix them, please send them to lindberg@id.wustl.edu for inclusion
+in the next version.
+
+ezmlm-idx-0.40, 19991220
+========================
+-Fixed bug in sub_mysql/issub.c that caused return of undef pointer for
+ non-subs. Security problem for sender checks, but not for moderation. Of
+ course only mysql version affected.
+-Updated ezmlmrc translations. Thanks all who worked on this.
+-added ezmlmrc.es. Thanks Vicent Mas/Francesc Alted/Sonia Lorente/Cyndy DePoy.
+-added ezmlmrc.cn_GB. Thanks HG.
+-ezmlm-weed: Groupwise garbage filter added. Thanks, SA.
+-mysql: include files now don't have a dir. This is so that one can have
+ them in e.g. /usr/include. conf-sqlcc adjusted.
+-Removed utils/. Not updated - add when done.
+-Full PostgresSQL support. Thanks, MS.
+-Corrected ordering of searchlog output for MySQL version to match default.
+ Thanks, MS.
+-ezmlm-moderate/store: Fork /bin/sh ... instead of sh for people with unset
+ PATH. Thanks, TM.
+-ezmlm-manage: rfc2369 headers added where possible also to admin
+ messages. Main finesse is that the WELCOME message has a custom
+ List-Unsubscribe header. -U/-S logic deconvoluted. -nN added.
+-ezmlm-store: -B to send only header of message to moderate, rather than
+ the entire message. Used for send-only list with very large posts.
+-ezmlm-reject: -b to reject messages with body or subject starting with
+ ``subscribe'' or ``unsubscribe''.
+-support for List-ID (draft). Added to all list messages if DIR/listid is
+ present. The first line of that file is added after "List-ID: ".
+-ezmlmrc: Added List-Subscribe header. Rewrite some texts, move some info
+ from bottom to help. Add ezmlm-archive (-i), add some switches to make
+ announcement lists easier.
+-ezmlm-make: Modifed to make -I default since -i now controls ezmlm-archive
+ with all lists configured with ezmlm-get. Also, ezmlm-make will not overwrite
+ files tagged with #E in ezmlmrc when in edit mode (-+ or -e). This preserves
+ manual customization. The behavior can be overridden with -++ or -ee to
+ reset the list texts (old behavior). Added version check for ezmlmrc Tx, SA.
+-ezmlm-archive and ezmlm-cgi added: Very easy to set up WWW access to list
+ archive. Support for various charsets incl iso-2022-*. For Russian,
+ one would need to do substitutions when making entries in archive/n/index.
+ This is not supported yet, so summary view entries may be incorrect if
+ the messages use Russian text in a charset different from the one the list
+ uses. Probably doesn't work for Chinese - lack of willing/able testers.
+-qmail-qmqp support via idx.h and DIR/qmqpservers. If your bandwidth is
+ limited, look at this (see ezmlm-send(1)/ezmlm-get(1)/ezmlm(5)). If you
+ have large lists, sublist them locally and specify different QMQP servers
+ for each sublist via DIR/qmqpservers. Added qmail-qmqpc.tar.gz patch to
+ allow specifying QMQP server on command line. With this, DIR/qmqpservers can
+ be used to specify different servers for different lists for busy servers.
+-ezmlm-gate: added -q file. Each line in "file" is processed as per
+ dot-qmail(5). 111 => temp_err, 99 => post, 100 => moderate, 0 => check sub.
+ Any other exit code will result in a bounce.
+ NOTE: This changes the behavior of ezmlm-gate used with only one DIR
+ argument (not used in any std listsetup for previous versions). Before,
+ this tested for subscriber status, now messages will
+ be moderated. This use is not set up by any ezmlm-make option and should
+ be _extremely_ rate. To obtain the old behavior, simply specify the
+ list directory twice.
+-Made conf-sqlcc and conf-sqlld to support both phases of the build. Adjusted
+ mysql includes to not assume they're in a "mysql" subdir.
+
+ezmlm-idx-0.324, 19990905 (PostgresSQL)
+=======================================
+-Minor typo in ezmlmrc.fr fixed.
+-Added [partial] PostgresSQL interface by Magnus Stalaker. See INSTALL.idx.
+-Added ezmlm-store Cc: docs mistakenly missed in 0.323.
+-Fixed problem with "it/itall" target (Thanks MW).
+
+
+ezmlm-idx-0.323, 19990815 (bug fix)
+===================================
+-ezmlm-store: Cc: list-accept-subscribe remote admin conf added to mod
+ requests. For lists using ezmlm-gate, this allows adding poster as allowed
+ alias. Thanks RVI for suggesting.
+-Added qmail-verh.tar.gz (0.03). This is the 0.02 qmail patch and a patch
+ to change ezmlmrc to take advantage of it. See docs inside.
+-conf-sqlcc/conf-sqlld instead of conf-sql to allow include file additions
+ (conf-cc) and libraries (conf-ld) for RDBMS interfaces.
+-Added ezmlmrc.id and ezmlmrc.it. Thanks APN and RDC.
+-ezmlm-manage: Mime boundary was missing for subscribe notifications for
+ moderated lists using QP/base64 encoding. Thanks TF.
+-ezmlm-get: Logic to prevent digest triggering from list-@host.
+-ezmlm-get: Digest parts have file names list_msgnum.txt instead of
+ list.msgnum to work around a MS Outlook bug. Thanks, MH.
+-ezmlm-request: Support for multipart MIME when used as a majordomo emulator.
+ Thanks, Johannes Ehrfeldt for reporting the bug.
+-ezmlm-send: trailer was added to mutipart/non-alternative. Problems with
+ multipart/signed. Now, trailer for multipart is added only to mixed,
+ digest, and parallel. Thanks BPF.
+-ezmlm-reject.1: typo.
+-ezmlm-test.sh multiple minor changes to work on more platforms. Still, if
+ it doesn't work, the problem is most likely here, and not in ezmlm(idx)
+ itself. Thanks RM DM and others.
+-ezmlm-moderate: added missing newline at beginning of MIME reject message.
+ Thanks, XXX.
+-idx.h fixed ALT_XX alternative command names which erroneously started '-'.
+ Looks bad, but was of no consequence.
+
+
+ezmlm-idx-0.322, 19990505 (bug fix)
+===================================
+-ezmlmrc.*: Added x-confirm-reading-to and x-pmrqc. Thanks JK.
+-ezmlmrc.ru: Added Russian version contributed by RVI. Thanks.
+-ezmlmrc: Czech version of ezmlmrc is ezmlmrc.cz. Should be ezmlmrc.cs. Fixed.
+-searchlog.c: Made the MySQL version also match in address field to make
+ the records returned the same as for std version.
+-ezmlm-store: -allow-subscribe confirm address in Cc: header to make it easier
+ for the moderator to add the poster to the allow database. (Thanks, RVI.)
+-ezmlm-manage: -query from remote admin for subscriber failed due to use of
+ static storage within issub(). Fixed.
+-ezmlm-manage: Made remote admin unsub failure return error. For sub it's
+ important to be silent for multi-moderator setups, but since unsub is never
+ moderated, it's ok to return negative feedback, since it may be needed by
+ remote admins. As a consequence it was trivial to add -Q[Q] not quiet to
+ provide [un]subscribe feedback to the owner.
+-ezmlm-send/reject: "boundary" in Content-type can be delimited by ';'.
+ Without, some messages could be erroneously rejected when using mimeremove.
+ New error text added to errtxt.h.
+
+
+ezmlm-idx-0.321, 19990322 (bug fix)
+===================================
+-added ezmlm-limit to limit list traffic. As this is a bug fix release,
+ ezmlm-limit should be considered experimental and is not built/installed
+ by default. (Thanks, TM for suggesting.)
+-subscribe.c: in some cases a ``T'' was incorrectly added before the address
+ in the DIR/Log subscription log. Corrected (Thanks, PH.).
+-ezmlm-test: reordered some tests to avoid timing problems on some
+ platforms. Tested hostname output since it may return a non-FQDN host name.
+ Added tests for most of the fixed bugs.
+-ezmlm-tstdig: improved DEFAULT logic. Failed with existing incorrect inlocal
+ in virtual domains.
+-ezmlm-manage bug: deny access control fixed.
+-ezmlm-request/ezmlm-tstdig: qmail>=1.02 doesn't set DEFAULT when there is
+ nothing matching. Unable to detect qmail version here. Changed logic to
+ always be correct with qmail>=1.02 and virtually always for earlier versions.
+ Missing inlocal is taken as meaning qmail>=1.02.
+-ezmlm-request: mdomo emulator: Corrected address listed for WHICH.
+-ezmlm-request: Silent exit on bounces (NULL sender). Fake SENDER spam to the
+ majordomo address otherwise causes double bounces to postmaster who has
+ better things to do.
+-ezmlmrc: corrected missing ezmlm-return in bouncer for normal (^6W) lists.
+ (Thanks Matt McGlynn.)
+-opensql.c: psql needs to be destroyed for ezmlm-reqest WHICH which may open
+ connections to several servers.
+-searchlog.c: made inbuf/ssin static. Was incorrect and generated linker
+ warning on some systems, but had no functional consequences. Thanks, SOH.
+-ezmlm-test: converted "export ENVIRON=value" to "ENVIRON=value; export ENVIRON"
+ to work with Solaris sh. Also, moved final ezmlm-warn section to the end
+ to avoid timing issue on some systems. "ps" test for qmail made non-fatal,
+ as differing options on different ps make it more likely that ps use is
+ the problem than that qmail is not running. (Thanks, SOH.)
+
+
+ezmlm-idx-0.32, 19990222
+========================
+-updated ezmlm-test. It tests most functions, and for important new features
+ or bugs discovered we'll add new tests in future. Thanks ME for patient
+ testing, suggestions, and "de-bashing".
+-added a rfc2369 headers for list-help, list-post, and list-unsubscribe.
+ It takes little space, and may promote support in MUAs.
+-ezmlm-get: list-get.9999999_x now always returns at least HISTGET messages
+ (default 30) provided that "9999999" is larger that the latest message number.
+ This allows embedding of an address for new subscribers to get at least what
+ has transpired since the latest digest and usually 30 messages before that,
+ to quickly see what's going on.
+-ezmlm-get: list-get for non-digested lists now returns the latest HISTGET
+ (default 30) messages instead of the latest message. To get the latest message,
+ mail list-get-9999999, where "9999999" is larger than the latest message
+ number.
+-Added ezmlm-manage/get -B switch to suppress text/bottom + request addition to
+ ezmlm-manage messages (except of course -help).
+-ezmlm-send uses copy() for trailer and headeradd. <#h#>, <#l#>, and <#n#>
+ in these texts are substituted with outhost, outlocal, and message number,
+ respectively. Now you can add ``To receive this thread, mail
+ <#l#>-get.<#n#>@<#h#>'' to the trailer. Removed dir/sequence feature, since
+ this can now be done with headeradd. copy() modified to suppress blank
+ lines when copying headers. extern decl in copy.h fixed. A consequence
+ is that headeradd isn't added to the archive copy which is good. <#n#>
+ for ezmlm-get is the latest message processed, except for the digest, where
+ it is the first message in the digest (which is also the digest msg #).
+-Added ezmlm-split (previously separate). Modified to use the same split
+ mechanisms as the SQL routines. -D switch to support easier reorganisation,
+ with extensive docs in man page. Interconversion between ezmlm-split and SQL
+ use is now relatively easy.
+-Reorganized files. The subscriber db sources are now in sub_std/ for the
+ standard ezmlm db, sub_mysql/ for the mysql version. Normally, there are
+ symlinks to the std version. ``make mysql'' changes the symlinks to sub_mysql
+ and ``make std'' can be used to set them back.
+-Replaced ezmlm-glmake with ``ezmlm-make -Cezmlmglrc''. See ezmlmglrc.5.
+-Added status.pl for status monitoring of ``list trees'' via httpd. In utils/.
+-Added feedback logging support with ezmlm-receipt and ezmlm-make -w. ezmlm-send
+ and ezmlm-get for the SQL version log info to the db.
+-Added support for setting up a minimal (SQL-enabled) sublist:
+ ezmlm-make -Cezmlmsubrc. See ezmlmsubrc.5.
+-Added ezmlm-make -w to remove ezmlm-warn invocation and if used with -6 to
+ configure ezmlm-receipt in place of ezmlm-return. Used for main list in
+ SQL-enabled list clusters for feedback and bounce logging.
+-Added MySQL support. ezmlm-make -6. This is a _major_ change and makes it
+ trivial to do sublisting and dynamically redistribute load. see ezmlm.5.
+-Modified ezmlm-return to add -dD switches. Previous design decision to have
+ ezmlm-return autodetect digest vs normal bounces cause problems when
+ sublisting a digest list. -dD force non-digest vs digest function and we'll
+ set uyp new lists with that, but keep autodetection for compatibility with
+ existing lists. ezmlmrc files modified appropriately. German version
+ updated and improved by Frank Tegtmeyer. Thanks!
+-Modified ezmlm-warn/return bounce handling. DIR/bounce now has a ``h''
+ subdir, further subdivided a-p for storage of h... files, where the first
+ character of the hash is used for the subdir, and a ``d'' subdir, containing
+ subdirs, each holding bounce (d... files) for a 10,000 s interval. This way,
+ only ``ripe'' bounce files are processed by ezmlm-warn. Also, ``lastd'' to
+ prevent unnecessary reparsing of bounces and ``lasth'' to keep track for
+ ``h'' subdir cleaning of remaining files older than 3x time out.
+-Modified all programs to use DEFAULT if available. With qmail>=1.02, this
+ allows one to ignore inhost/inlocal so that special adjustment for virtual
+ domain lists is no longer required. Works the old way with qmail<1.02.
+-DIR/extra and DIR/blacklist for extra allowed SENDERs and SENDER blacklisting
+ have been renamed DIR/allow and DIR/deny. This is used sparingly and the old
+ nomenclature was confusing. ``mkdir DIR/allow; mv DIR/extra/* DIR/allow''
+ and the corresponding move for DIR/deny will take care of it.
+-Extended the remote admin -log command. Now, -log.xxx will return only
+ Log lines matching 'xxx'. '.' matches '.' and '_' matches any character.
+-The ``From:'' line of subscribers confirmation message will be logged to
+ ``Log''. This allows the administrator to use the Log to by name locate the
+ subscription address of a user.
+
+
+ezmlm-idx-0.316, 19990815 (bug fixes only)
+==========================================
+-Migrated back fixes for 0.322 -> 0.323 that are applicable to 0.31x:
+-ezmlm-get: Made filename extension constant for digest parts to work around
+ Outlook bug.
+-ezmlm-manage: Mime boundary added for mod-sub-ok message when QP/base64 is
+ used.
+-ezmlm-manage.1: Typo.
+-ezmlm-moderate: Missing newline for non-MIME reject message.
+-ezmlm-request: Support by global interface of multipart/alternative messages.
+-ezmlm-send: Trailer only for some types of multipart messages.
+-ezmlm-reject.1: Typo.
+
+
+ezmlm-idx-0.315, 19990505 (bug fixes only)
+==========================================
+-Bug fixes fed back from up to 0.322.
+-ezmlm-send/reject: "boundary" in Content-type can be delimited by ';'.
+ Without, some messages could be erroneously rejected when using mimeremove.
+ New error text added to errtxt.h.
+-ezmlm-manage: -query from remote admin for subscriber failed due to use of
+ static storage within issub(). Fixed.
+-ezmlm-manage bug: deny access control fixed.
+
+
+ezmlm-idx-0.314, 19990216 (bug fixes only)
+==========================================
+-Added ezmlm-test, a script that tests [most] ezmlm + idx program functions.
+-added ezmlmrc.cz (Thanks, Jan Kasprzak).
+-ezmlm-make -e failed if DIR/config didn't exist. Fixed. (Thanks, SOH.)
+-ezmlm-make man page bug for virtual domain (Thanks, XX).
+-ezmlm-get segfaulted when run from the command line with LOCAL undefined.
+ Fixed. (Thanks, ME.)
+
+ezmlm-idx-0.313, 19981121 (bug fixes almost only)
+=================================================
+-ezmlm-get returns 0 for success in editor and command line use, rather than
+ 99. No impact on current lists, but makes more sense.
+-ezmlm-warn tests >= for timeout rather than >. No impact other than easier
+ use with future test script.
+-ezmlm-manage -get function was broken - fixed. Needed for backwards compat
+ with ezmlm-0.53. (Thanks, KS.)
+-Reorganized INSTALL.idx, UPGRADE.idx, README.idx and introduced FILES.idx
+ to simplify. Made corrections to FAQ.idx, but no additions.
+-ezmlm-send no longer adds trailer to multipart/mixed messages. Also, multipart
+ messages consisting of only "mimeremove" parts will be bounced. (Thanks, BE.)
+ The trailer is suppressed for base64-encoded messages. In both cases,
+ trailer addition might corrupt the message. Default charset is added if none
+ specified (was left empty).
+-The US version of ezmlmrc is now ``ezmlmrc.en_US'' and ``make'' by default
+ copies it to ezmlmrc. Added support to Makefile and docs to copy in other
+ versions via e.g. ``make jp''. Target ezmlmrc is there to not overwrite with
+ ezmlmrc.en_US on ``make setup''.
+-Changed ezmlmrc so that headeradd/headerremove/sub-ok/mod-sub are not rewritten
+ when editing lists. (Thanks, GS.)
+-In ezmlm-idx and ezmlm-send, made dirs without trailing slash (mkdir with
+ it bombs on Ultrix). (Thanks, BW.)
+-added check for NULL sender is zapnosub() in ezmlm-get. Fixed NULL pointer
+ use when the command is -dig- and no digest code was given on the command
+ line.
+-added 'x' to FORMATS in idx.h. (Thanks, MMcL.)
+-ezmlm-reject changed to honor subject continuation lines. Eudora likes to
+ break right after "Subject:" which caused rejection for empty subject.
+ (Thanks, SOH.) Same for To:. Also deals with quoted content types. Error
+ message now always states the offending MIME type.
+-Added "(null)", "(none)", and "(no subject)" to rejected subjects. What are
+ these MUA authors thinking??
+-ezmlm-reject bug fixed to reject commands in subjects. (Thanks, JH.)
+-ezmlm-request bug fixed to allow correct processing of complete command
+ addresses in the subject, even for virtual domains. (Thanks, EC.)
+-ezmlmrc.de corrected (Thanks, FT).
+-Made local check in ezmlm-tstdig case-insensitive (Thanks, Rick Myers).
+-Fixed ezmlm-glconf to work with 'sh', not only 'bash' (missing ';' before '}')
+ (Thanks, MS).
+-ezmlm-manage: text/bottom added also when moderated subscription is
+ completed (Thanks, SOH). Added -log command to ezmlm-manage. Allows remote
+ admins to retrieve Log if -l switch is set. Added -log into to mod-help via
+ ezmlmrc.
+-Made character defines sun4 cc compliant in decode{Q|B}.c and unfoldHDR.c.
+-Added support for "Sv:" and "Rv:" reply indicators (Nordic, Spanish).
+-Improved/expanded ezmlm-check.
+-Added -+ switch to ezmlm-make. Makes letter switches sticky (not -cC).
+ <#F#> is all letter switches, not only the ones set. (Thanks, SOH). Also,
+ added virtual domain info to ezmlm-make.1.
+
+ezmlm-idx-0.312, 19980805 (bug fixes only and support scripts added)
+====================================================================
+-Fixed declaration of char16table[] in decodeQ.c to get clean mips64 compile
+ (Thanks, MF).
+-Fixed ezmlm-request so that <#l#> and <#h#> are substituted correctly.
+-Added ezmlm-glmake to create a global list setup. Left ezdomo.tar.gz since that
+ is what's documented in the FAQ.
+-Fixed inconsistencies/errors in the ezdomo.tar.gz example files (Thanks, PN).
+-Changed ezmlm-return.c to understand qmail preVERP error messages even if
+ qmail is patched to use MIME error messages.
+-Added ezmlm-glconf and man page to the package. The program creates a config
+ file for the "majordomo-style" interface from a user's existing lists.
+-Updated ezmlmrc and translations to not overwrite text/faq|info when editing
+ the list and add a note on -faq/-info to text/bottom and on -list/-edit
+ to text/mod-help. Thanks to the "usual suspects" for the translations!
+-Corrected small bug in ezmlmrc.de and ezmlmrc.da.
+-Corrected small bug in ezmlm-check -s switch handling (Thanks, DS).
+-Fixed bug in unfoldHDR.c affecting handling of subject prefixes without
+ message number (Thanks, LL).
+
+ezmlm-idx-0.311, 19980701 (bug fix only)
+========================================
+-Restored the mistakenly amputated ezmlmrc (English only, others were ok).
+-Fixed ezmlm-gate to tolerate unset $SENDER (Thanks, AP).
+
+ezmlm-idx-0.31, 19980630
+========================
+-Added ezmlm-manage -m switch for moderated unsubscription, pre request.
+ (Thanks, FT).
+-made cookie for accept/reject the same: ezmlm-moderate/ezmlm-store.
+-ezmlmrc.da (Thanks, TF). Other ezmlmrc updated (Thanks: see README.idx).
+-copy.c modified to allow NUL, multiple tags per line, and <#h#> => outhost.
+ (Thanks, TM).
+-ezmlm-manage sends replies to "-help" from "list-return-@". This will break
+ all autoresponder loops for admin messages after 1 round. Otherwise,
+ ezmlm-warn and a broken responder can start an endless manage<->responder
+ exchange. (Thanks, MM.)
+-Simplified tosubs(). Slightly less efficient with qmail, but avoids a lot
+ of problems.
+-Added umask(022) to ezmlm-unsub.c and ezmlm-sub.c, in case they 
+ are the ones to create dir/lock.
+-ezmlm-moderate and ezmlm-gate changed to pass on switches for exec'd
+ programs. ezmlm-moderate sets sender for ezmlm-send so that -C switch
+ works also for moderated lists.
+-ezmlm-accept now autoconfigured with ezmlm bin path and installed.
+-ezmlm-manage allows remote access to the ``extra'' db (-allow-subscribe)
+ with same restrictions as for the main list. Access to the ``blacklist''
+ db for remote admins only (-deny-subscribe).
+-ezmlmrc changed to skip ezmlm-reject in editor of sublists.
+-ezmlm-reject now rejects messages that do not contain the list
+ address in To:/Cc: (Thanks, DJB). ezmlmrc modified, since it requires
+ the ezmlm-reject line to have the list dir on the command line. Not full
+ rfc822 parser (see BUGS in ezmlm-reject.1!).
+-made missing file in copy.c a permanent error. This way, it's discovered
+ sooner, as the list owner may not have access to maillog.
+-docs updates, including adding terminal '/' to directories to make them
+ clearly distinct from files.
+-Safer storage of return-path by ezmlm-store.
+-ezmlm-send now adds a "Return-Path:" header to the archive copy. This way,
+ listowners without maillog access can get all the info (together with
+ "received:" and "delivered-to: lines).
+-ezmlm-send -R switch to remove "Received:" headers that cause bounces to users
+ (due to too low sendmail hopcount) that can subscribe and are reached by
+ probes just fine. "received:" headers still go to the archive (Thanks, TM).
+-Fixed minor bug in "reply-indicator" trimming (unfoldHDR.c).
+-Make default behavior of -get command to send the messages that have arrived
+ since the last digest (up to MAXGET) if there has ever been a digest (only
+ last message otherwise).
+-"prefix" number substitution changed to LAST '#'. Support for rfc2047 encoded
+ prefix. With iso-2022-* this will work only if characters after '#' are
+ ascii only, due to the removal of redundant ESC sequences from subjects before
+ comparison, and the need for any line to end in ascii. Improved robustness
+ of unfoldHDR.c (could be crashed with some illegal encoded words).
+-Make reject messages come From: list-owner@listhost in ezmlm-moderate. A
+ "Reply-To: address" is added if "-t address" is on the command
+  line (Thanks, YG).
+-ezmlm-get digesting now orders headers only for rfc1153 (mandated), otherwise
+ displays them as they come. The default headers included are the same, but
+ this can be overridden with dir/digheaders containing a list of the headers
+ to include.
+-ezmlm-request takes the first line starting with a letter and interprets it
+ as a command, if the subject is empty or does not start with a letter.
+ Only one line is interpreted.
+-ezmlm-request if used with "-f config" interprets "config" as a config file
+ of lists and list info. When used with this switch, it ignores the subject and
+ expects a command on the first body line. This is to service e.g. the
+ majordomo@host address. See man page.
+-ezmlm-request understands a number of command synonyms to make it easier for
+ users familiar to other MLMs. These are just translated into regular ezmlm
+ commands for use with the list.
+-ezmlm-manage services -faq, -info, and -query commands (see man page). Stubs
+for faq/info added to ezmlmrc and to edit-list via ezmlmrc.
+-ezmlm-check fixes for grep not understanding '-q' and a ezmlm-check -S switch
+ to avoid subscriber listing (Thanks, TEE).
+
+ezmlm-idx-0.302, 19980617 (bug fixes only)
+==========================================
+-Removed args from copy_insertsubject() call in ezmlm-send.c
+-made ezmlm-make enforce absolute "dot".
+-Fixed MIME newline bug in ezmlm-moderate/store/clean. (Thanks, JS).
+-Fixed missing arg in postmsg() call from previous fix (Thanks, MF).
+
+ezmlm-idx-0.301, 19980508 (bug fixes only)
+==========================================
+-Digest format is multipart/digest again. The new format MIXED/'x' is
+ identical, except that it is multipart/mixed. The former works well for
+ most MUA. The latter is better for Pine when ezmlm texts are content-
+ transfer-encoded.
+-RFC1153 format ignores content-transfer-encoding. This works fine with
+ us-ascii. For non-us-ascii it works if the mail path is 8-bit clean. RFC1153
+ is really a us-ascii format, since the messages are dependent on the
+ encoding of the main message. This way is as good as it can get.
+-Added many content-types to mimeremove in ezmlmrc (Thanks, SP).
+-Removed most of the -x stuff in ezmlmrc. Now, -x only adds MIME content-type
+ stripping (extensive) and limits message body size to 2-40000 bytes. If you
+ want "Reply-To:" munging, edit ezmlmrc. The "mailto:" stuff was bad, and 
+ the references to list-owner@host for intractable problems are now standard.
+-Changed ezmlmrc extensions to iso 639: SV - Swedish.
+-Added ezmlmrc files for DE, PL (Thanks, FT, SP).
+-Fixed ezmlm-get: -index reply lacked MIME end boundary (Thanks, KI).
+-Fixed bug in ezmlm-get: outformat error when using digest trigger messages
+ in dir/editor. (Thanks YG).
+-Fixed bug in ezmlm-clean: cp_setlocal wasn't called.
+-Improved ezmlmrc files so that digest links and dirs are not created for
+ on-digested lists. Decreases number of .qmail files.
+-Fixed bug in ezmlm-get: Headers from trigger message are no longer copied
+ to digest (but are still copied to -index/-get/-thread reply). (Thanks STH.)
+-Fixed ezmlm-0.53 bug in ezmlm-return. The "d..." file was the same for all
+ addresses of a pre-VERP bounce, resulting in only the first one being
+ processed correctly (sent to DJB).
+-Changed ezmlm-get to include dir/headeradd headers for digest. (Thanks PH.)
+-Changed ezmlm-get to send digest "To: list@list" from "list-digest@host".
+ This way, the list address is one of the "reply-to-all" addresses, just
+ as for list messages. (Thanks PH.)
+-Corrected ezmlmrc: in some places "d" was used for "n" switch resuling in
+ missing dir/text files for editing if editing was enabled and digest
+ were not. (Thanks JS.)
+-Corrected ezmlmrc/se/jp to add terminal ';' to list in ezmlm-issubn
+ lines. Caused trouble with strict /bin/sh as on FreeBSD. (Thanks STH.)
+-Corrected ezmlm-make man page to show that "digestcode" is optional.
+
+
+ezmlm-idx-0.30, 19980325
+========================
+-Fixed bug in continuation line handling for digest. In some circumstances,
+ continuations ended up with the wrong header. (Thanks SA).
+-Changed digest format to multipart/mixed as pine has trouble with an
+ encoded text/plain part first in multipart/digest (although this is
+ rfc2046-ok). (Thanks SA).
+-Updated FAQ, *.idx, and man pages for new functionality.
+-Changed subdb.a folding qputsubs.c into putsubs.c and passing a pointer
+ to the write function. Better and necessary to allow QP/base64 output by
+ the ezmlm-manage -list command.
+-Added ezmlm-warn -t switch to modify bounce time-out as well as moved the
+ compile-time default to idx.h (Thanks Mark Delany).
+-Added note on "make clean" to INSTALL/UPGRADE.idx (Thanks Peter Hunter).
+-Added support for message numbers in the prefix. This violates mail standards,
+ but is very popular in Japan. (Thanks KI for inspiring this.) ezmlm-idx-0.30
+ does prefix differently, so that it modifies the subject as little as possible
+ for compliance with IETF recs and rfcs. ezmlm-idx-0.23 when prefix was used,
+ used a processed and unfolded subject, which was less correct, and could cause
+ problems with very long subjects.
+-Changed MAXEDIT to 10k. This should accomodate all reasonable dir/text files.
+ A limit _is_ required (see FAQ: SECURITY).
+-Made any _user-generated_ commands case-insensitive throughout (ezmlm-get,
+ -manage, -request). Note: ezmlm-generated ones, such as "list-accept" are
+ still case-sensitive. (Thanks GS for reminding me.)
+-Separated ezmlm-make switches for SENDER checks. -u for posts, -g for archive.
+ Also changed names of other switches. -n for text file editing, -d for
+ digest list. ezmlm-manage edit is now -eE, but the old -dD is still accepted.
+-Modified most programs to service requests for both list and digest list,
+ depending on LOCAL. For ezmlm-warn a command line -d switch is used to
+ process digest bounces. Created copy.h/c as a general routine that
+ does encoding and on-the-fly substitution of <#l#> to the name of the list
+ being serviced. ezmlmrc modified to set up list appropriately and create
+ a digest list for switch -d. The very rudimentary digest list is always
+ called 'list-digest' for the list 'list' and lives as a subscriber and a
+ bounce dir in dir/digest. log.c modified to take the dir as an argument.
+ copy.c introduced. Can deal with encoding and substitution for <#l#>,
+ <#B#>, <#R#>.
+-Made dir/extra standard. All subscriber restriction (ezmlm-make -u) is
+ to accept addresses that are subscribers of either list or in dir/extra.
+-Added REMOVE as a forbidden subject in ezmlm-reject.
+-Added support for rejection based on message and message part MIME
+ Content-type. ezmlm-send can from composite MIME messages filter out
+ body parts based on content-type. Trailers are added as MIME parts
+ to composite MIME messages and in the Content-transfer-encoding of the
+ message for single body messages.
+-min and max message size restrictions moved from ezmlm-send/store to
+ ezmlm-reject.
+-Added support for optional base64 and quoted-printable encoding of trailer
+ and all outgoing ezmlm messages. The same encodings are accepted as input
+ for moderator comments and edited text files.
+-Added hash for threading due to inconsistent/incorrect handling of LWSP
+ by iso-2022-jp MUAs. Now threading works for rfc2047 encoded subjects.
+-Added support for rfc2047 encoded From: lines and continuation for From:
+ lines.
+-Moved relevant parts of idxsub.c into ezmlm-send and ezmlm-idx.
+-Added MIME en/decoding routines to support rfc2047 encoded subject lines.
+ Also, added code to remove redundant ESC sequences when unfolding headers
+ in iso-2022-jp* and iso-2022-cn* and iso-2022-kr. Only iso-2022-jp has been
+ tested.
+-Added ezmlm-idx -d switch to use date from 'Date:' header instead of the
+ qmail received line. For old archives processed through ezmlm-send to
+ re-archive. Remove (GMT), (PST), and DOW, and make 2-digit years xx where
+ xx >= 70 19xx and others 20xx.
+
+
+ezmlm-idx-0.231, 19980302
+=========================
+-ezmlm-check.sh elsif -> elif.
+-Allowed ezmlm-make -a switch (was missing; Thanks MF).
+-Removed subject truncation for index: for prefixed lists it is used and
+rfc2047-encoded subjects are often longer.
+-Fixed idxsub.c bug (subject continuation lines skipped by ezmlm-idx if
+ 'Subject:' is after 'From:'). (Thanks, SOH.)
+
+
+ezmlm-idx-0.23, 19980120
+========================
+-ezmlm-check fixed to remove bash-specific pattern matching. (Thanks, VV).
+-ezmlm-both now makes completely functional list & digest list with digest
+ creation from dir/editor. If address is given, it becomes the owner, remote
+ admin, and a subscriber of both lists. By default, the remote admin is allowed
+ to get a subscriber list.
+-updated ezmlmrc.fr.
+-ezmlm commands now have alternates defined via idx.h, allowing aliases, rather
+ than replacement of command names in non-English domain.
+-Modified ezmlm-tstdig to run easily from within dir/editor and dir/manager.
+ Had to add dir/tstdig timestamp when run from dir/editor, to prevent triggering
+ a new digest while one is being made. Now no new request is issued within one
+ hour after the last one.
+ Changed ezmlmrc to make this default for digested lists.
+-New switches for ezmlm-make.
+-Added ezmlm-get -f and -t switches for easier use on the command line and
+ in dir/editor. Made it "context-sensitive" by testing LOCAL.
+-Via ezmlmrc added -q switch for processing commands to list-request,
+ a -3 hrs switch to configure ezmlm-tstdig in dir/manager, and some cleanup
+ in texts.
+-Modified ezmlm-make to fall back to system ezmlmrc files (with warning)
+ if the -c switch is used by no custom ezmlmrc file is found. This makes
+ it possible for a GUI to rely more on ezmlm-make.
+-Added ezmlm-request to redirect 'Subject:' line requests.
+-Added ezmlm-cron, a very restrictive crond interface enabling users
+ without direct cron access to generate digest trigger messages. Suitable
+ to be run from scripts for non-shell users as well. See ezmlm-cron.1.
+-modified ezmlmrc to set up sublists (-0 sublist@subhost), directories in
+ dir/modpost|modsub|remote with -789, and SENDER restriction on posts and
+ archive retrieval (-6 digest_dir). Doc'd in ezmlm-make.1.
+-modified ezmlm-make to allow </file#^5/> meaning -5 not defined/empty.
+-ezmlm-issubn -n[egate] switch to make 'blacklisting' of SENDERs easier.
+-ezmlm-get TOC loop starts at first message with the given subject, rather
+ than first message in range.
+-Added time and author to the subject index. The TOC now has author info.
+ Changes made so that the programs run fine with old or mixed old/new subject
+ index files.
+-removed == n == from rfc1153 digest messages.
+-Allowed for -dig.code[range]-targetlocal=targethost in ezmlm-get. Makes
+ it easier to trigger digests since SENDER "faking" is no longer required.
+-ezmlmrc changed to add 'Precedence: bulk' to dir/headeradd. For "vacation".
+-Added ezmlm-manage -edit and -edit.file actions. If the -d switch is
+ used, they allow remote admins to edit dir/text files.
+-Modified ezmlm-send to use the main list message number if the message
+ sender has the correct format and if the list is a non-archived sublist.
+-Added tosubs.c, putsubs.c, qputsubs.c for subscriber address output,
+ modified issub.c and subscribe.c, and put all into subdb.a. subscribe.h
+ modified to become the general header file for subscriber database interface.
+ issub() returns (char *)matching_address for compatibility with later packages.
+ Modified all programs interfacing with subscriber databases to use these
+ routines. In all a consistent interface. Using ezmlm with a different
+ subscriber database manager requires changes to these routines only.
+-Modified ezmlm-make and ezmlmrc to store all config info in dir/config, and
+ also read it from there if ezmlm-make is invoked in edit mode with the 
+ 'dir' argument only.
+-Fixed ezmlm-manage to make all output MIME using dir/charset (Thanks, SK).
+
+
+ezmlm-idx-0.221, 19980101
+=========================
+-Fixed ezmlm-send bug causing problems with undefined SENDER.
+ (Thanks, Sadhu, TR).
+-Fixed ezmlm-make lock file name bug (Thanks, SOH).
+
+
+ezmlm-idx-0.22, 19971210
+========================
+-Added 'make clean' and TARGETS. (Thanks, TF).
+-man page and FAQ updates. Some of FAQ info moved into a 'USAGE' section
+ of the man pages.
+-Bug fixed: caused ezmlm-make to ignore the -c switch.
+-Added -34567890 switches to ezmlm-make. They take a command line argument
+ and substitute it for <#3#>, <#4#>, etc, in ezmlmrc.
+-Added code to ezmlm-gate to enable it to pass switches to both ezmlm-send
+ and ezmlm-store (which have and need to keep non-overlapping switches).
+-Added multi-moddir capability to ezmlm-gate, which now becomes the
+ standard tool for SENDER-restricting posts. ezmlm-store does this the
+ slightly more involved but much more secure way.
+-Added -C no copy to sender switch to ezmlm-send. Useful for small lists.
+-Added -d digdir switch to ezmlm-get. When specified, SENDER is accepted
+ for -get/thread/index only if a subscriber of the main list or the list
+ based in digdir.
+-ezmlm-manage sends help in reply to a specific list-help request, even if
+ the list is not public.
+-Added ezmlm-send -cC switches controlling post to self (Thanks TF, JS).
+-Modified ezmlmrc to add a few switches: -t (trailer), -f (prefix),
+ -l (subscriber list to remote admins, if any).
+-Added ezmlm-tstdig which tests if a specified number of messages, message
+ body bytes, or time have passed since the last digest creation. Added
+ support for this in ezmlm-send and ezmlm-get.
+-Added ezmlm-issub, and ezmlm-issubn (tests if SENDER is in any of a number
+ of subscriber databases). Thanks Mark Delany for the original ezmlm-issub.
+-Modified issub.c and subscribe.c to make subscriber database storage
+ and comparisons case-insensitive (backwards compatible).
+-Removed ezmlm-warn from ezmlmrc setup of DIR/bouncer. This helps big
+ lists with lots of bounces.
+-Added locking of list dir to ezmlm-make when used in -e [edit] mode.
+-Bug: ezmlm-send would make all subjects 'Re: something' even if they were
+ not replies for non-indexed lists with a subject prefix. Thanks, Ximenes
+ Zalteca (ximenes@mythic.net) for reporting this.
+
+
+ezmlm-idx-0.21, 19971115
+========================
+-Slightly improved ezmlm-both (now works even for non-remote admin setups).
+ It's still very pedestrian.
+-Added a few rfc1893 error codes.
+-Added -rR option to ezmlm-clean to control sender notification of time-out.
+-Bug: in ezmlm-idx missing message->continue (was break). Caused abort if
+ archive messages are missing (uncommon, but possible if user decides to
+ delete a few). (Tanks MF).
+-More strict on Re[...]: in idxsub: '...' has to be digits and ']' is
+ required. ':' is optional for broken MUAs. Swedish 'Ang:' accommodated.
+-Added 'Content-Disposition:' header to digest messages from ezmlm-get per
+ rfc2183 (Thanks, SK). Removed Content-Type: message/rfc822 txt, since it
+ is the default.
+-Updated man pages and FAQ for new features.
+-ezmlm-send no longer requires DIR/num. If it's not there, it's assumed to
+ be zero and created. This helps ezmlm-make -e.
+-Added ezmlm-make '-e' 'edit' switch. Modified ezmlmrc accordingly.
+-Added ezmlm-check script to help diagnose ezmlm list setup errors. Need to
+ add checks for DIR/text messages.
+-ezmlm-store now pipes the message to ezmlm-send if DIR/modpost is not present.
+ Removing DIR/modpost will make a message moderated list non-moderated.
+-Added ezmlm-gate which pipes to ezmlm-send/ezmlm-mode depending on membership
+ in an address database. This is now a installed C program.
+-ezmlm-get no longer requires index to be executable. Also, improved handling
+ of missing index files in indexed archive (no FATAL error, except for master
+ subject in 'thread' and that should be fixable ...). Added new format specifier
+ 'n' for 'not threaded'. Affects -get and -dig.
+-Bug: Fixed ezmlm-idx to create index via temp file (indexn) and chmod to 700.
+ (Thanks Toshinori Maeno).
+-Removed charset from multipart/digest header (Thanks SK).
+-Bug: Added <#D#> to ezmlm-warn line under </owner/> in ezmlmrc.
+
+
+ezmlm-idx-0.20, 19971030
+========================
+-Requests to ezmlm-moderate to accept/reject already processed messages
+ returns an error ONLY if the requested action is different from the actual
+ fate of the message. Keeps moderators happier in multi-moderator lists,
+ without info loss, with less traffic.
+-A moderator's (un)subscribe CONFIRM reply results in target notification,
+ only if the target's status changed. Multiple moderators confirming no
+ longer yields multiple messages to target.
+-ezmlm-manage copy() takes 2 arguments, the file name and a fall-back file
+ name in case the first file does not exist. CONFIRM requests to moderators
+ use text/mod-(un)sub-confirm (fall back: text/(un)sub-confirm). ezmlmrc
+ changed to set these up.
+-Added MIME header to ezmlm-get -index response (to get DIR/charset effect).
+-Updated docs and FAQ.idx.
+-Pre-release compile testing on several platforms. (Thanks YG, ARB, MF, SK).
+-Added DIR/msgsize. If it exists and is not 0, ezmlm-send and ezmlm-store will
+ bounce posts in excess of 'msgsize' bytes body size.
+-Fixed ezmlm-clean to that when multiple messages time out, the message-id:s
+ are different.
+-ezmlm-clean and ezmlm-moderate now by default return the post as a
+ MIME attachment. Use '-M' for old format.
+-Rewrote ezmlm-make to work from the ezmlmrc template file for better
+ and easier customization. Changed moderator rejection comment tag to '%%%'
+ from '###' to support '#' as a comment character in ezmlmrc. '###' still
+ works.
+-Added DIR/sequence for optional message number header, and DIR/charset for
+ character set. Thanks, SK. Default Compiled is now US-ASCII, as per rfc2046.
+-Added TEST.idx, SECURITY.idx.
+-ezmlm-get allows remote admins archive access even for non-public lists. The
+ ezmlm-get -pP switches override DIR/public effects.
+-ezmlm-manage still does moderator-initiated (un)sub when DIR/public is absent.
+-Fixed bug: Updated MIME to rfc2046. With this the ezmlm-get 'm' format is no
+ longer rfc1153-like. 'r' still gives rfc1153. Moderation attachments are now
+ message/rfc822. Hopefully, the pine attachment viewer will catch up.
+ Thanks, YG [and Mutt developers] for teaching me the errors of my ways. Also,
+ see ezmlm-get.1(BUGS).
+-ezmlm-manage gives moderator help and does list if target is moderator,
+ rather than sender. Allows moderator to access from secondary account (to
+ which mail sent to the primary account is forwarded).
+-ezmlm-store checks DIR/modpost for leading '/' and if found assumes that the
+ first line is the base directory for message moderators.
+-ezmlm-manage checks DIR/modsub and DIR/remote contents for a leading '/'.
+ If found, it is assumed to be the base-dir for moderators. NB: if both
+ DIR/modsub and DIR/remote have entries, only DIR/modsub is used, i.e. it
+ is not possible to have different dbs for remote admin and (un)sub mods.
+-Modified ezmlm-manage to add separate moderator confirm commands (tc/vc)
+ to allow also subscription moderator via forwarded mail. This is essentially
+ a total rewrite of ezmlm-manage. Also, the -S and -U switches were added to
+ allow optional omission of the user confirm phase for (un)subscribe.
+ USE OF THESE SWITCHES DRASTICALLY DECREASES SECURITY, except for
+ moderated lists. Since there have been many request for this (and it
+ seems reasonable in some circumstances) these options have been added.
+ Make sure you understand the consequences if you use them.
+-Fixed bug: Added exit code to ezmlm-moderate. Thanks BG.
+-Added include for sys/types.h to ezmlm-clean.c and ezmlm-store.c, changed
+ ulong/uint to unsigned long/int, removed void from empty parameter lists for
+ two functions, all to compile on BSDI 2.1 and Sun. Thanks, ARB and TE.
+-Combined ezmlm-mod with ezmlm-idx and fused mod.h into idx.h.
+
+ezmlm-idx-0.12, 19970910
+========================
+-Cleanups to allow compile on SGI.  Thanks MF.
+
+ezmlm-idx-0.11, 19970907
+========================
+-Fixed bug: ezmlm-idx failed to check for error return from issub.c.
+-Fixed bug: ezmlm-idx would crash on empty subjects.
+-Moved out command names and lengths to idx.h making changing to other
+ languages easier. Please use this only for totally local lists.
+-Added -cC switches to ezmlm-get to disable all but -dig. Useful for secret
+ lists that are archived and digested.
+-Fixed bug: -dig would give "nothing to digest" with only 1 new message. Now
+ it sends that message out (previously, this message would have been in the
+ next digest as soon as more messages had arrived).
+-Added DIR/prefix. If it exists, it is prefixed to outgoing subjects. Not
+ added to archive/index. ezmlm-idx deals with it. 'Re: ' is prefixed for
+ replies, since we use reply-parsed subject in ezmlm-send. A lot of pain for
+ little gain, but for some reason this seems to be a 'must'.
+-Changed idxsub routines to return 1 if reply indicators were fund, 0 else.
+-Changed idxthread routines from int to void (don't return anything).
+-Split out errtxt.h from idx.h in anticipation of ezmlm-mod.
+-changed ezmlm-send to honor 'mailing-list' in headerremove.
+-fixed ezmlm-make to deal with non-archived and non-indexed lists and to
+ point out that -index works in sets of 100.
+-ezmlm-get error msg on non-sub -thread request fixed.
+-Added DIR/text/trailer. If it exists, it is added to all messages, as
+ suggested by Fred B. Ringel. It is not copied to the archive.
+-ezmlm-make changed to adapt text/bottom to archived/indexed status of list.
+-Fixed usage text for ezmlm-send.
+-'Reply-To:' header from messages is included in standard MIME digests
+ (after reading Dan's comments on Reply-To munging). rfc1153 doesn't allow it.
+-Changed ezmlm-get.c default to NOT restrict get/index/thread to subscribers.
+-Fixed ezmlm-get.1 to correctly reflect effect of -s/-S switches.
+-Fixed FAQ and added more info.
+
+ezmlm-idx-0.10, 19970801
+========================
+-Added several items to FAQ.
+-Made digestcode case-insensitive.
+-Fixed format bugs causing trouble with PINE (Thanks Fred B. Ringel).
+-Improved handling of missing index file in indexed archives.
+-Added info on -get and -index restrictions to text/bottom.
+-Added FAQ.idx (a little silly at the moment ;-).
+-Added CHANGES.idx.
+ Updated README.idx, UPDATE.idx, ezmlm.5 and other man pages.
+-Added text/digest to allow standard info to be added to the list. A place
+ holder is put in by ezmlm-manage, since the exact info depends on the
+ digest list.
+-Moved all error messages into defines in idx.h to support alternative
+ languages.
+-folded subject indexing into ezmlm-send.
+-ezmlm-get.c: Added -s (default) restricting -get, -index, -thread to
+ subscribers. -S on the command line removes this restriction.
+-Added support for the horrid German 'Aw:' and 'Aw[..]:' replies.
+-All linear white space is converted to a single ' ' in subject indexing.
+ Existing archives should be re-indexed using the new ezmlm-idx. Probably rarely
+ needed, but it's more correct.
+-Added support for subject continuation lines (rfc822). Probably very rarely
+ needed.
+-Limited '-thread' to +/- 2000 messages (defines in ezmlm-get.c) from the
+ "master message" to limit resource use on large archives.
+-Changed limit for '-get' to 50 messages, '-index' to 20 sets (2000 subjects).
+-Added digestcode support to ezmlm-make as a 5th argument. Default=blank.
+-Added arguments to digest option. If one is given, digesting starts with that
+ message and dignum/digissue are used/updated. If both are given, dignum and
+ digissue are not updated and the issue number is the number of the first
+ message in the archive.
+-Added digest support. Enabled by specifying a 'digestcode'last on the
+ ezmlm-get command line.
+-Removed the now redundant -fF (filter) and -qQ (quote) options.
+-Changed message format for returned messages allowing rfc1153, MIME
+ multipart/digest, or MIME multipart/digest ordering and retaining only headers
+ as per rfc1153, with the addition of 'Content-Type:' to better support
+ alternative character sets. The latter format is the default.
+-Fixed bug in -thread: search for messages was set to start at the beginning
+ of the index containing the "master message", rather than at the beginning of
+ the archive.
+-Fixed bug: A few stralloc* were incorrectly stdio* in sindex.c.
+
+ezmlm-idx-0.02, 19970702
+========================
+-Adapted to changes in ezmlm-0.52 => ezmlm-0.53.
+
+ezmlm-idx-0.01, 19970615
+========================
diff --git a/DOWNGRADE.idx b/DOWNGRADE.idx
new file mode 100644 (file)
index 0000000..59a1114
--- /dev/null
@@ -0,0 +1,36 @@
+$Id: DOWNGRADE.idx,v 1.3 1997/12/30 21:30:56 lindberg Exp $
+$Name: ezmlm-idx-040 $
+
+MODIFYING ezmlm-idx lists to work with "virgin" ezmlm-0.53
+
+With ezmlm-idx-0.22, the routines issub.c and subscribe.c are modified to
+store subscriber addresses using a hash based on the lower case address,
+rather than a case sensitive hash. This was done to avoid the many problems
+with subscribers using mixed case addresses (User@host and user@host) without
+realizing that these are different.
+
+These changes have no impact on lower case-only addresses. For mixed case
+addresses, the case in the local part is retained. If not found, a second
+lookup is done with a hash based on the mixed case address. This results in
+backwards compatibility with ezmlm-0.53 subscriber list.
+
+In the unlikely event that you use ezmlm-idx and then decide to go back to
+ezmlm-0.53 alone, mixed case addresses stored in the new location
+(case-insensitive hash) will not be found by the old ezmlm-0.53 programs.
+To place them in the ezmlm-0.53 position, do the following with your lists
+after installing the "virgin" ezmlm-0.53 binaries and backing up everything
+under DIR/subscribers:
+
+       % ezmlm-list DIR >tmp.tmp
+       % rm -rf DIR/subscribers/*
+       % xargs ezmlm-sub DIR <tmp.tmp
+
+This just recreates the subscriber database with the old style hash.
+
+This procedure can also be used to recover corrupted subscriber databases,
+by editing tmp.tmp before resubscribing.
+
+You can also identify the addresses that would be affected by:
+
+       % ezmlm-list | grep -G '[A-Z]'
+
diff --git a/FAQ.idx b/FAQ.idx
new file mode 100644 (file)
index 0000000..38a46eb
--- /dev/null
+++ b/FAQ.idx
@@ -0,0 +1,6072 @@
+  EZFAQ 0.40 - ezmlm-idx and ezmlm FAQ
+  Fred Lindberg, lindberg@id.wustl.edu & Fred B. Ringel,
+  fredr@rivertown.net
+  22-NOV-1999
+
+  This document is a collection of frequently asked questions about
+  ezmlm-idx. Where applicable, ezmlm itself is also covered. This FAQ
+  presumes familiarity with Unix, and with the basic concepts of E-mail
+  and mailing lists.  This FAQ is updated for ezmlm-0.53 and ezmlm-
+  idx-0.40.
+  ______________________________________________________________________
+
+  Table of Contents
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  1. General Information
+
+     1.1 Acknowledgements
+     1.2 What is this document?
+     1.3 Terminology
+     1.4 What is the difference between ezmlm and ezmlm-idx?
+     1.5 Where can I get all of the ezmlm-related programs?
+     1.6 Where can I find documentation for ezmlm and patches?
+     1.7 Where do I send comments on this document?
+     1.8 How to experiment with new versions of ezmlm-idx.
+
+  2. Quick start
+
+  3. Overview of mailing list management and mailing list managers
+
+  4. Overview of ezmlm function
+
+     4.1 The basic setup.
+     4.2 Inventions in ezmlm.
+     4.3 The qmail delivery mechanism.
+     4.4 What the different programs do.
+     4.5 What the different files in the list directory do.
+     4.6 The paper path for posts.
+     4.7 The ezmlm path for moderation messages.
+     4.8 The ezmlm path for administrative messages.
+     4.9 The ezmlm path for bounces.
+     4.10 Messages to list-owner and list-digest-owner.
+     4.11 Structure of subscriber databases.
+     4.12 Local case in E-mail addresses.
+     4.13 Testing SENDER to allow posts only from list subscribers.
+     4.14 How cookies work.
+     4.15 How moderator E-mail addresses are stored.
+     4.16 How subscription moderation works.
+     4.17 How remote administration works.
+     4.18 How message moderation works.
+     4.19 How QMQP support works
+     4.20 How messages are stored in the archive.
+     4.21 How the message index works.
+     4.22 How threading works.
+     4.23 How digests work.
+     4.24 How WWW archive access works.
+     4.25 How ezmlm-tstdig works.
+     4.26 How sublists work.
+     4.27 How sublisting can be made transparent to the user.
+     4.28 How to service commands in the subject line.
+     4.29 How to support alternative command names.
+     4.30 How to add your own commands.
+     4.31 How remote administrators can retrieve a subscriber list
+     4.32 How remote administrators can determine the number of subscribers
+     4.33 How remote admins can see if an address is a subscriber or not
+     4.34 How remote administrators can search the subscription log
+     4.35 How text file editing works.
+     4.36 How subject line prefixes work.
+     4.37 How bounces are handled.
+     4.38 How the info and faq commands work.
+     4.39 How the global ezmlm list address works.
+     4.40 How ezmlm-cron works.
+     4.41 How ezmlm-make works.
+     4.42 What names can I use for my lists?
+     4.43 Lists in virtual domains
+     4.44 How do I make customization simple for me/my users?
+
+  5. ezmlm support for SQL databases.
+
+     5.1 Why use an SQL database with ezmlm?
+     5.2 Why not to use an SQL database with ezmlm.
+     5.3 Tables used for (My)SQL support.
+        5.3.1 Address tables.
+        5.3.2 Subscriber log tables.
+        5.3.3 Message logging tables.
+     5.4 How to set up a simple list with SQL support.
+        5.4.1 Helper programs for SQL-enabled lists.
+     5.5 Manually manipulating the subscribers of a SQL-enabled list.
+     5.6 Converting to and from and SQL database.
+     5.7 Optimizing MySQL for ezmlm.
+        5.7.1 Address SELECTs, additions, removals.
+     5.8 Maintenance of the MySQL database.
+
+  6. Possible error conditions in ezmlm lists.
+
+     6.1 What do I do if ezmlm doesn't work?
+     6.2 How do I report ezmlm bugs?
+     6.3 Where do I send suggestions for ezmlm-idx improvements?
+     6.4 Using ezmlm-test to check the ezmlm(-idx) programs.
+     6.5 Using ezmlm-check to find setup errors.
+     6.6 Posts are rejected: Sorry, no mailbox here by that name (#5.1.1).
+     6.7 Post are not sent to subscribers.
+     6.8 ezmlm-make fails: usage: ezmlm-make ...
+     6.9 ezmlm-make fails: Unable to create ...
+     6.10 ezmlm-make fails: ... ezmlmrc does not exist
+     6.11 Index/get/thread requests fail quietly or with errors from ezmlm-manage.
+     6.12 Digest triggering requests fail.
+     6.13 Remote administration (un)subscribe confirm requests go to the user, not the moderator.
+     6.14 (Un)subscribers does not receive a (un)subscribe acknowledgement
+     6.15 Messages posted to a moderated list are sent out without moderation.
+     6.16 Messages posted to a moderated list do not result in moderation requests.
+     6.17 Moderation request replies do not result in the appropriate action.
+     6.18 Moderator comments with moderation request replies are not added to the post/sent to the poster.
+     6.19 Some headers are missing from messages in the digest.
+     6.20 Some Received: headers are missing from messages.
+     6.21 My Mutt users cannot thread their digest messages.
+     6.22 Posts fail: Message already has Mailing-List (#5.7.2).
+     6.23 The last line of a
+     6.24 No CONFIRM requests are sent to moderators.
+     6.25 Deliveries fail ``temporary qmail-queue error''
+     6.26 How to deal with corrupted subscriber lists
+     6.27 Vacation program replies are treated as bounces by ezmlm.
+     6.28 Digests do not come at regular hours.
+     6.29 Preventing loops from misconfigured subscriber addresses.
+     6.30 A user can subscribe and receives warning and probe messages, but no messages from the list.
+
+  7. Customizing ezmlm-make operation via ezmlmrc
+
+     7.1 Using ezmlm-make to edit existing lists.
+     7.2 What is ezmlmrc?
+     7.3 Changing defaults for
+     7.4 Changing default moderator directories.
+     7.5 Adapting ezmlm-make for virtual domains.
+     7.6 Setting up ezmlm-make for special situations.
+
+  8. Restricting message posting to the list.
+
+     8.1 Requiring the list address in To:/Cc: headers.
+     8.2 Rejecting messages sent from other mailing lists.
+     8.3 Restricting posts based on the Subject line.
+     8.4 Restricting the size of posts.
+     8.5 Restricting posts based on MIME content-type.
+     8.6 Restricting posts to list subscribers.
+     8.7 Restricting posts to an arbitrary set of E-mail addresses (higher security option).
+     8.8 Completely restricting posts.
+     8.9 A general solution to restricting posts based on SENDER.
+
+  9. Customizing outgoing messages.
+
+     9.1 Adding a trailer to outgoing messages.
+     9.2 Adding a subject prefix to outgoing messages.
+     9.3 Adding a header to outgoing messages.
+     9.4 Adding a message number header.
+     9.5 Removing headers from outgoing messages.
+     9.6 Removing MIME parts from messages.
+     9.7 Limiting ``Received:'' headers in outgoing messages.
+     9.8 Setting ``Reply-To: list@host''.
+     9.9 Configuring the list so posts are not copied to the original sender.
+     9.10 Customizing ezmlm notification messages.
+     9.11 Specifying character set and content-transfer-encoding for outgoing ezmlm messages.
+
+  10. Customizing archive retrieval.
+
+     10.1 Specifying the format for retrieved messages.
+     10.2 Specifying the default format for digests and archive retrieval.
+     10.3 Limiting the number of messages per -get/-index request.
+
+  11. Restricting archive retrieval.
+
+     11.1 Restricting archive access to subscribers.
+     11.2 Restricting available archive retrieval commands.
+     11.3 Restricting archive retrieval to moderators.
+     11.4 Allowing archive retrieval from a non-public list.
+
+  12. Customizing digests.
+
+     12.1 Setting up a digest list.
+     12.2 Generating daily digests.
+     12.3 Generating the first digest.
+     12.4 Adding standard administrative information to digests.
+     12.5 Controlling the digest format.
+     12.6 Customizing bounce handling.
+
+  13. Remote administration.
+
+     13.1 How can I remotely add moderators, subscriber aliases, etc?
+     13.2 Moderating posts from a secondary account.
+     13.3 Moderating subscription from a secondary account.
+     13.4 Automatically approving posts or subscriptions.
+     13.5 Allowing remote administrators to get a subscriber list.
+     13.6 Allowing remote administrators to retrieve or search a subscription log.
+     13.7 Allowing users to get a subscriber list.
+     13.8 Changing the timeout for messages in the moderation queue.
+     13.9 Finding out how many messages are waiting for moderation.
+     13.10 Using the same moderators for multiple lists.
+     13.11 Using different moderators for message and subscription moderation.
+     13.12 Setting up moderated lists with the list owner as the ``super moderator'' able to add/remove moderators remotely.
+     13.13 Customizing ezmlm administrative messages.
+     13.14 Manually approving a message awaiting moderation.
+     13.15 Manually rejecting a message awaiting moderation.
+
+  14. Sublists.
+
+     14.1 Sublists of ezmlm lists.
+     14.2 Sublists of non-ezmlm lists.
+     14.3 How to set up a cluster of list and sublists with standard databases.
+
+  15. Migration to Ezmlm from other Mailing List Managers.
+
+     15.1 Basic Concepts.
+     15.2 Setting up ezmlm to respond to host-centric commands.
+     15.3 Commands of other mailinglist managers recognized by ezmlm.
+        15.3.1 Listproc/Listserv.
+        15.3.2 Majordomo.
+        15.3.3 Smartlist.
+
+  16. Optimizing list performance.
+
+     16.1 Crond-generated digests for better performance.
+     16.2 Optimizing execution of ezmlm-warn(1).
+     16.3 Decreasing ezmlm-warn time out to increase performance.
+     16.4 Use ezmlm without ezmlm-idx for maximum performance.
+     16.5 Not archiving to maximize performance.
+     16.6 Sublists to maximize performance.
+
+  17. Miscellaneous.
+
+     17.1 How do I quickly change the properties of my list?
+     17.2 Open archived list with daily digests.
+     17.3 Variations in moderation
+     17.4 Lists that allow remote admin, but not user initiated subscription or archive retrieval.
+     17.5 Lists that allow remote admin, user archive retrieval, but not user-initiated subscription.
+     17.6 Lists that restrict archive retrieval to subscribers.
+     17.7 Lists that do not allow archive retrieval at all.
+     17.8 Lists that do not allow archive retrieval and do not allow digest triggering per mail.
+     17.9 Lists that allow archive retrieval only to moderators, but allow user-initiated subscription.
+     17.10 Lists that do not require user confirmation for (un)subscription.
+     17.11 Announcement lists for a small set of trusted posters
+     17.12 Announcement lists allowing moderated posts from anyone.
+     17.13 Announcement lists with less security and more convenience.
+
+  18. Ezmlm-idx compile time options.
+
+     18.1 Location of binaries.
+     18.2 Location of man pages.
+     18.3 Base directory of qmail-installation.
+     18.4 Short header texts, etc.
+     18.5 Arbitrary limits.
+     18.6 Command names.
+     18.7 Error messages.
+     18.8 Paths and other odd configuration items.
+
+  19. Multiple language support.
+
+     19.1 Command names.
+     19.2 Text files.
+     19.3 Multi-byte character code support.
+
+  20. Subscriber notification of moderation events.
+
+     20.1 General opinions.
+     20.2 Users should know that the list is subscription moderated.
+     20.3 Subscribers should know that posts are moderated.
+     20.4 Senders of posts should be notified of rejections.
+
+  21. Ezmlm-idx security.
+
+     21.1 General assumptions.
+     21.2 SENDER manipulation.
+     21.3 ezmlm cookies.
+     21.4 Lists without remote admin/subscription moderation.
+     21.5 Message moderation.
+     21.6 Subscription moderation.
+     21.7 Remote administration.
+     21.8 Remote editing of ezmlm text files.
+     21.9 Digest generation and archive retrieval.
+     21.10 Convenience for security: the ezmlm-manage ``-S'' and ``-U'' switches.
+     21.11 Denial of service.
+     21.12 Moderator anonymity.
+     21.13 Confidentiality of subscriber E-mail addresses.
+     21.14 Help message for moderators.
+     21.15 Sublists.
+     21.16 SQL databases.
+     21.17 Reporting security problems.
+
+
+  ______________________________________________________________________
+
+  1\b1.\b.  G\bGe\ben\bne\ber\bra\bal\bl I\bIn\bnf\bfo\bor\brm\bma\bat\bti\bio\bon\bn
+
+
+  1\b1.\b.1\b1.\b.  A\bAc\bck\bkn\bno\bow\bwl\ble\bed\bdg\bge\bem\bme\ben\bnt\bts\bs
+
+  Many ezmlm users have contributed to improvements in ezmlm-idx. These
+  are listed in the R\bRE\bEA\bAD\bDM\bME\bE.\b.i\bid\bdx\bx file in the ezmlm-idx distribution.
+  Others have through questions and suggestions inspired parts in this
+  FAQ, or pointed out errors or omissions. Thanks! Direct contributions
+  are attributed to the respective authors in the text. Thanks again!
+
+
+  1\b1.\b.2\b2.\b.  W\bWh\bha\bat\bt i\bis\bs t\bth\bhi\bis\bs d\bdo\boc\bcu\bum\bme\ben\bnt\bt?\b?
+
+  This FAQ contains answers to many questions that arise while
+  installing ezmlm, ezmlm-idx, and while setting up and managing ezmlm
+  mailing lists. See ``'' for a brief summary of what is ezmlm and what
+  is ezmlm-idx.
+
+  Many aspects of ezmlm are covered in several places in this FAQ. The
+  early sections explain how ezmlm works. Later sections discuss how to
+  deal with possible errors/problems. Subsequent sections discuss
+  details of customization and list setup in a _\bH_\bO_\bW_\bT_\bO form. Finally,
+  there are sections on information philosophy for moderated lists and
+  on security aspects on ezmlm lists.
+
+  This is an evolving document.  If you find any errors, or wish to
+  comment, please do so to the authors.  This FAQ is currently aimed at
+  system administrators and knowledgeable users, and heavily weighted
+  towards questions specific to the ezmlm-idx add-on.
+
+  If you have problems with the ezmlm-idx package, please start by
+  reading the ``man'' pages which come with each program, then this
+  document and other ezmlm documentation which is identified here. If
+  you have exhausted these resources, try the ezmlm and qmail mailing
+  lists and their respective mailing list archives. If you have solved a
+  problem not in the documentation, write it up as a proposed section of
+  a FAQ and send it to the authors. This way, it can be added to the
+  next version of this FAQ.
+
+
+  1\b1.\b.3\b3.\b.  T\bTe\ber\brm\bmi\bin\bno\bol\blo\bog\bgy\by
+
+  This document uses a number of terms. Here are the meanings ascribed
+  to them by the authors.
+
+     D\bDI\bIR\bR
+        The base directory of the list.
+
+
+     S\bSE\bEN\bND\bDE\bER\bR
+        The envelope sender of the message, as passed to ezmlm by qmail
+        via the $SENDER environment variable.
+
+
+     L\bLO\bOC\bCA\bAL\bL
+        The local part of the envelope recipient. For list-get-1@host,
+        it is usually _\bl_\bi_\bs_\bt_\b-_\bg_\be_\bt_\b-_\b1. If host is a virtual domain,
+        controlled by _\bu_\bs_\be_\br_\b-_\bs_\bu_\bb, then local would be _\bu_\bs_\be_\br_\b-_\bs_\bu_\bb_\b-_\bl_\bi_\bs_\bt_\b-_\bg_\be_\bt_\b-_\b1.
+
+
+     m\bmo\bod\bdd\bdi\bir\br
+        Base directory for moderators.  Moderator E-mail addresses are
+        stored in a hashed database in m\bmo\bod\bdd\bdi\bir\br/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/. By default,
+        ``moddir'' is D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/.
+
+        To add or remove moderators:
+
+
+          % ezmlm-sub DIR/moddir moderator@host.domain
+          % ezmlm-unsub DIR/moddir moderator@host.domain
+
+
+
+
+
+     d\bdo\bot\btd\bdi\bir\br
+
+        The second argument of ezmlm-make is the main .qmail file for
+        the list. dotdir is the directory in which this ``dot file''
+        resides, i.e. the directory part of the ``dot'' argument. This
+        is usually the home directory of the user controlling the list
+        (but NOT necessarily of the one creating the list). Thus, _\bd_\bo_\bt_\bd_\bi_\br
+        is ~\b~a\bal\bli\bia\bas\bs/\b/ if ``root'' creates a list:
+
+
+           # ezmlm-make ~alias/list ~alias/.qmail-list ...
+
+
+
+
+     _\bd_\bo_\bt_\bd_\bi_\br is where the .\b.e\bez\bzm\bml\blm\bmr\brc\bc file is expected when the ezmlm-
+     make(1) ``-c'' switch is used (see ``Customizing ezmlm-make opera-
+     tion'').
+
+
+     e\bez\bzm\bml\blm\bm b\bbi\bin\bna\bar\bry\by d\bdi\bir\bre\bec\bct\bto\bor\bry\by
+        The directory where the ezmlm-binaries are normally stored, as
+        defined at compile time in c\bco\bon\bnf\bf-\b-b\bbi\bin\bn.  This is compiled into the
+        programs and does not change just because you have moved the
+        program.
+
+
+     e\bez\bzm\bml\blm\bm-\b-g\bge\bet\bt(\b(1\b1)\b)
+        This is a reference to the ezmlm-get.1 man page.  Access it with
+        one of the following:
+
+
+          % man ezmlm-get
+          % man 1 ezmlm-get
+
+
+
+
+     or if you have not yet installed ezmlm-idx (replace ``xxx'' with
+     the version number):
+
+
+          % cd ezmlm-idx-0.xxx
+          % man ./ezmlm-get.1
+
+
+
+     b\bba\bas\bse\bed\bdi\bir\br
+        The list directory when referencing the list subscriber address
+        database.  For E-mail addresses stored in a set of files within
+        D\bDI\bIR\bR/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/, the ``basedir'' is ``DIR''.
+
+
+     a\bad\bdd\bdr\bre\bes\bss\bs d\bda\bat\bta\bab\bba\bas\bse\be
+        A collection of E-mail addresses stored in a set of files within
+        the ``subscribers'' subdirectory of the basedir,
+        D\bDI\bIR\bR/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/.
+
+
+     m\bme\bes\bss\bsa\bag\bge\be m\bmo\bod\bde\ber\bra\bat\bto\bor\br
+        An address to which moderation requests for posts to the list
+        are sent. The moderation requests are formatted with
+        ``From:''-``reject'' and a ``To:''-``accept'' default headers
+        for moderator replies. A reply to the ``reject'' address leads
+        to the rejection of the post. A reply to the ``accept'' address
+        leads to the acceptance of the post. Any E-mail address can be a
+        moderator E-mail address. Any number of moderator E-mail
+        addresses can be used. If a post is sent from a moderator E-mail
+        address, the moderation request is sent to that E-mail address
+        only. If a post is sent from an E-mail address that is not a
+        moderator, a moderation request is sent to all moderators.
+
+        The first reply to the moderation request determines the fate of
+        the message. Further requests for the action already taken are
+        silently ignored, while a request for the contrary action
+        results in an error message stating the actual fate of the
+        message. Thus, if you want to ``accept'' the message and it has
+        already been accepted, you receive no reply, but if you attempt
+        to ``reject'' it, you will receive an error message stating that
+        the message already has been accepted.
+
+        Most lists are not message moderated. If they are, the owner is
+        usually a ``message moderator'', sometimes together with a few
+        other trusted users.
+
+        For an announcement list, it is common to make all the
+        ``official announcers'' ``message moderators''. This way, they
+        can post securely and ``accept'' their own posts, while posts
+        from other users will be sent to this set of ``official
+        announcers'' for approval.
+
+
+     s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn m\bmo\bod\bde\ber\bra\bat\bto\bor\br
+        An E-mail address where subscription moderation requests are
+        sent. A subscription moderation request is sent after a user has
+        confirmed her intention to subscribe. The subscription
+        moderation request is sent to all moderators. As soon as a reply
+        to this message is received, the user is subscribed and
+        notified. Any E-mail address can be a subscription moderator and
+        any number of subscription moderators can be used.
+
+        Unsubscribe requests are never moderated (except when the ezmlm-
+        manage(1) ``-U'' flag is used and the sender attempts to remove
+        an address other than the one s/he is sending from). It is hard
+        to imagine a legitimate mailing list that would want to prevent
+        unsubscriptions.
+
+
+     r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bto\bor\br
+        When a remote administrator subscribes or unsubscribes a list
+        member, the ``confirm'' request is sent back to the remote
+        administrator, rather than to the subscriber's E-mail address.
+        This allows the remote administrator to (un)subscribe any list
+        member without the cooperation of the subscriber at that
+        address. Any E-mail address can be a remote administrator and
+        any number of E-mail addresses can be remote administrators.
+
+        The set of E-mail addresses that are ``remote administrators''
+        and ``subscription moderators'' are always the same. This set of
+        E-mail addresses can be ``remote administrators'',
+        ``subscription moderators'' or both.
+
+        For most lists, the owner would be the ``remote administrator'',
+        if s/he wishes to moderate messages, the owner would be the
+        ``message moderator'' and if s/he wishes to moderate
+        subscriptions the owner would also be the ``subscription
+        moderator''.
+
+        The list's ``message moderator(s)'' can be the same, but can
+        also be set up to be completely different.
+
+
+     C\bCh\bha\ban\bng\bgi\bin\bng\bg l\bli\bis\bst\bt `\b``\b`o\bow\bwn\bne\ber\brs\bsh\bhi\bip\bp'\b''\b'
+        Within this FAQ there are references to the need to check or
+        change the list ``ownership.'' This is not a reference to the
+        individual user who is the ``list-owner'', but a reference to
+        the ownership of the files by your operating system which make
+        up the list and reside in D\bDI\bIR\bR/\b/.
+
+        To change the ownership of D\bDI\bIR\bR/\b/ and everything within:
+
+
+          % chown -R user DIR
+          % chgrp -R group DIR
+
+
+
+
+     Depending on your system/shell, it may be possible to combine these
+     commands into either:
+
+
+          % chown -R user.group DIR
+          % chown -R user:group DIR
+
+
+
+
+
+  1\b1.\b.4\b4.\b.  W\bWh\bha\bat\bt i\bis\bs t\bth\bhe\be d\bdi\bif\bff\bfe\ber\bre\ben\bnc\bce\be b\bbe\bet\btw\bwe\bee\ben\bn e\bez\bzm\bml\blm\bm a\ban\bnd\bd e\bez\bzm\bml\blm\bm-\b-i\bid\bdx\bx?\b?
+
+  ezmlm-0.53 is a qmail-based mailing list manager written by Dan J.
+  Bernstein.  It has all the basic functionality of a mailing list
+  manager, such as subscriber address management including automated
+  bounce handling as well as message distribution and archiving.
+
+  ezmlm-idx is an add-on to ezmlm. It adds multi-message threaded
+  message retrieval from the archive, digests, message and subscription
+  moderation, and a number of remote administration function. It
+  modifies the configuration program ezmlm-make(1) so that it uses a
+  text file template rather than compiled-in texts in list creation. In
+  this manner, ezmlm-idx allows easy setup of lists in different
+  languages and customization of default list setup. ezmlm-idx also adds
+  MIME handling, and other support to streamline use with languages
+  other than English. As an ezmlm add-on, ezmlm-idx does not work
+  without ezmlm and tries to be compatible with ezmlm as much as
+  possible.  ezmlm-idx also modifies the ezmlm subscriber database to be
+  case insensitive to avoid many unsubscribe problems.
+
+  New in ezmlm-idx-0.40 are better support for announcement lists,
+  support for QMQP to offload message distribution onto external hosts,
+  simplified optional SQL database use (MySQL or PostgreSQL), more
+  flexibility in determining which messages should be moderated, a WWW
+  interface to the list archives, and many small improvements.
+
+  ezmlm-idx-0.32 adds improved handling of very large lists with
+  optimized bounce handling, ezmlm-split(1) for forwarding (un)subscribe
+  requests to sublists to allow sublisting transparent to the
+  subscriber, and SQL support to allow sublisting with improved message
+  authentication and monitoring of list function, as well as dynamic
+  addition/removal/reconfiguration of sublists. Also, subscriber
+  ``From:'' lines are logged with support for finding a subscription
+  address from a name. The qmail DEFAULT variable is used, if present.
+  Together, these additions eliminate the most common problems making
+  ezmlm use and administration even easier.
+
+  This document is a FAQ for ezmlm-idx. However, many of the basic items
+  that are discussed also apply to ezmlm per se. Referring to the two
+  paragraphs above, it should be relatively easy to figure out which
+  features require ezmlm-idx.
+
+
+  1\b1.\b.5\b5.\b.  W\bWh\bhe\ber\bre\be c\bca\ban\bn I\bI g\bge\bet\bt a\bal\bll\bl o\bof\bf t\bth\bhe\be e\bez\bzm\bml\blm\bm-\b-r\bre\bel\bla\bat\bte\bed\bd p\bpr\bro\bog\bgr\bra\bam\bms\bs?\b?
+
+  We have now registered ezmlm.org to make access to ezmlm-idx and
+  related programs/documentation easier. www.ezmlm.org is currently an
+  alias for Fred B. Ringel's www.rivertown.net/~ezmlm/ and ftp.ezmlm.org
+  an alias for Fred Lindberg's ftp.id.wustl.edu.
+
+
+     D\bDa\ban\bn J\bJ.\b. B\bBe\ber\brn\bns\bst\bte\bei\bin\bn'\b's\bs e\bez\bzm\bml\blm\bm-\b-0\b0.\b.5\b53\b3
+
+     +\bo  <ftp://cr.yp.to/pub/software/ezmlm-0.53.tar.gz>
+
+     +\bo  <ftp://ftp.ezmlm.org/pub/qmail/ezmlm-0.53.tar.gz>
+
+     +\bo  <ftp://ftp.ntnu.no/pub/unix/mail/qmail/ezmlm-0.53.tar.gz>
+
+     +\bo  <ftp://ftp.pipex.net/mirrors/qmail/ezmlm-0.53.tar.gz>
+
+     +\bo  <ftp://ftp.jp.qmail.org/qmail/ezmlm-0.53.tar.gz>
+
+     +\bo  <ftp://ftp.rifkin.technion.ac.il/pub/qmail/ezmlm-0.53.tar.gz>
+
+     +\bo  <ftp://ftp.mira.net.au/unix/mail/qmail/ezmlm-0.53.tar.gz>
+
+     +\bo  <http://www.qmail.org/>
+
+     T\bTh\bhe\be l\bla\bat\bte\bes\bst\bt v\bve\ber\brs\bsi\bio\bon\bn o\bof\bf e\bez\bzm\bml\blm\bm-\b-i\bid\bdx\bx
+        ezmlm-idx releases are numbered ``ezmlm-idx-0.xy[z]''.  Versions
+        with the same ``x'' are backwards compatible. A change in ``x''
+        signifies major changes, some of which _\bm_\ba_\by require list changes
+        (see UPGRADE.idx). However, backwards compatibility with
+        ezmlm-0.53 list will be maintained. Thus, this is an issue only
+        if you are already using an older version of ezmlm-idx.
+
+        Addition of ``z'' are bug fixes only. Thus, ezmlm-idx-0.301 is
+        ezmlm-idx-0.30 with known bugs fixed (but no other significant
+        changes).  When available, patches are named
+        ``filename-0.xy[z].diff'', where ``0.xy[z]'' corresponds to the
+        release to which they apply.  When a number of bugs (or a
+        significant bug) are found a bug-fix release is made
+        incorporating all the patches for the previous version.
+
+        To get the latest features, look for the highest number (``e.g.
+        ezmlm-idx-0.40''). Any bugs in versions with new features are
+        expected to be limited to the new features.
+
+        To get the most solid version, get the highest 3-digit number,
+        i.e. a bug fix. If you already run a version in that series and
+        a new bug fix is released, see CHANGES.idx to determine if it is
+        worthwhile to upgrade. Most bugs so far have been relevant only
+        when using lists in very unusual ways or with rarely used
+        options.
+
+
+     +\bo  <ftp://ftp.ezmlm.org/pub/patches/>
+
+     +\bo  <ftp://gd.tuwien.ac.at/infosys/mail/qmail/ezmlm-patches/> ftp
+        mirror in Austria.
+
+     +\bo  <http://gd.tuwien.ac.at/infosys/mail/qmail/ezmlm-patches/> http
+        access to the same mirror.
+
+     +\bo  <ftp://ftp.win.or.jp/pub/network/mail/qmail/ezmlm-idx/> ftp
+        mirror in Japan.
+
+     e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) f\bfi\bil\ble\bes\bs f\bfo\bor\br d\bdi\bif\bff\bfe\ber\bre\ben\bnt\bt l\bla\ban\bng\bgu\bua\bag\bge\bes\bs
+        The latest versions at the time of release of a package are
+        included in that package. Thus, this directory will have a file
+        labeled with the current ezmlm-idx version number only if it has
+        been updated later than the package.  ezmlmrc(5) files are
+        updated and new ones are added all the time, also with bug fix
+        releases. Therefore, always look at the latest package. Please
+        note that ezmlmrc may change significantly between versions.
+        Thus, do not expect the ezmlm-idx-0.324 ezmlmrc.es to work with
+        ezmlm-idx-0.40.
+
+        ezmlmrc(5) files contain some release-specific configurations.
+        Do not use a later file (other than from bug fix releases) with
+        an earlier version of the programs. It is usually OK to use a
+        version from an earlier package (see UPGRADE.idx), but some new
+        functionality may nor be available.
+
+        To contribute an ezmlmrc(5) file in a new language, start with
+        the en_US version from the latest package, and send the gzipped
+        file to lindberg@id.wustl.edu. Please leave comments intact and
+        in English and do not change the order of items in the file.
+        This will facilitate maintenance.
+
+
+     +\bo  <ftp://ftp.ezmlm.org/pub/patches/ezmlmrc/>
+
+     +\bo  <ftp://gd.tuwien.ac.at/infosys/mail/qmail/ezmlm-
+        patches/ezmlmrc/>
+
+     +\bo  <http://gd.tuwien.ac.at/infosys/mail/qmail/ezmlm-
+        patches/ezmlmrc/>
+
+     +\bo  <ftp://ftp.win.or.jp/pub/network/mail/qmail/ezmlm-idx/ezmlmrc/>
+
+     e\bez\bzm\bml\blm\bm-\b-i\bis\bss\bsu\bub\bb-\b-0\b0.\b.0\b05\b5
+
+     +\bo  <ftp://ftp.ezmlm.org/pub/patches/ezmlm-issub-0.05.tar.gz>.  Use
+        ezmlm-issub only if you do not use ezmlm-idx. The same
+        functionality is available in ezmlm-idx and the packages are not
+        compatible.
+
+     +\bo  Also via mirrors mentioned above.
+
+
+     R\bRP\bPM\bMs\bs a\ban\bnd\bd S\bSR\bRP\bPM\bMS\bS o\bof\bf q\bqm\bma\bai\bil\bl,\b, e\bez\bzm\bml\blm\bm a\ban\bnd\bd e\bez\bzm\bml\blm\bm-\b-i\bid\bdx\bx
+
+     +\bo  <ftp://ftp.ezmlm.org/pub/patches/>
+
+     +\bo  <ftp://summersoft.fay.ar.us/pub/qmail/>
+
+
+  1\b1.\b.6\b6.\b.  W\bWh\bhe\ber\bre\be c\bca\ban\bn I\bI f\bfi\bin\bnd\bd d\bdo\boc\bcu\bum\bme\ben\bnt\bta\bat\bti\bio\bon\bn f\bfo\bor\br e\bez\bzm\bml\blm\bm a\ban\bnd\bd p\bpa\bat\btc\bch\bhe\bes\bs?\b?
+
+
+     m\bma\ban\bn p\bpa\bag\bge\bes\bs
+        All ezmlm component programs come with their own man pages.
+        Thus, for info on _\be_\bz_\bm_\bl_\bm_\b-_\bs_\be_\bn_\bd, type:
+
+
+
+          % man ezmlm-send
+
+
+
+
+     or if you have unpacked ezmlm, but not made it or installed it:
+
+
+
+          % cd ezmlm-0.53
+          % man ./ezmlm-send.1
+
+
+
+
+
+     e\bez\bzm\bml\blm\bm(\b(5\b5)\b)
+        General info on ezmlm and list directories is in e\bez\bzm\bml\blm\bm.\b.5\b5:
+
+
+
+          % man ezmlm
+
+
+
+
+     or
+
+
+
+          % cd ezmlm-0.53
+          % man ./ezmlm.5
+
+
+
+
+     _\bN_\bO_\bT_\bE_\b: Installation of the ezmlm-idx package updates some existing
+     man pages to reflect changes made by the patch (e.g.  ezmlm-
+     send(1), ezmlm(5)).
+
+
+     T\bTe\bex\bxt\bt f\bfi\bil\ble\bes\bs i\bin\bn t\bth\bhe\be d\bdi\bis\bst\btr\bri\bib\bbu\but\bti\bio\bon\bn
+        ezmlm comes with a R\bRE\bEA\bAD\bDM\bME\bE file with general instructions, an
+        I\bIN\bNS\bST\bTA\bAL\bLL\bL file with installation instructions, an U\bUP\bPG\bGR\bRA\bAD\bDE\bE file for
+        upgrading from a previous version and a C\bCH\bHA\bAN\bNG\bGE\bES\bS file with
+        information on changes from previous versions. ezmlm-idx comes
+        with similar files suffixed with ``.\b.i\bid\bdx\bx''. Most other patches or
+        add-ons contain similar files and man pages and should contain
+        identifying suffixes (.iss for ezmlm-issub, for example).  For a
+        discussion of the authors' understanding of ezmlm security, see
+        ``Ezmlm-idx security''.
+
+
+     `\b``\b`E\bEz\bzm\bma\ban\bn'\b''\b',\b, a\ban\bn e\bez\bzm\bml\blm\bm/\b/i\bid\bdx\bx m\bma\ban\bnu\bua\bal\bl
+        The ezmlm manual is a brief manual that is meant for list
+        subscribers, list moderators and remote administrators, and as
+        an introduction for list owners. It is useful even if you do not
+        use ezmlm-idx. Features requiring ezmlm-idx are marked as such.
+        The manual is available as a set of html files, as a text file,
+        and in a ``letter'' and ``A4'' postscript version:
+
+     +\bo  ezman for download <ftp://ftp.ezmlm.org/pub/patches/ezman/>
+
+     +\bo  An on-line html version <http://www.ezmlm.org/ezman>
+
+
+     T\bTh\bhi\bis\bs F\bFA\bAQ\bQ
+        This FAQ is built from a sgml source. It is available in the
+        following formats:
+
+     +\bo  A text file <ftp://ftp.ezmlm.org/pub/patches/ezfaq.txt.gz>
+
+     +\bo  An on-line html version <http://www.ezmlm.org/>
+
+     +\bo  Html for download
+        <ftp://ftp.ezmlm.org/pub/patches/ezfaq.html.tar.gz>
+
+     +\bo  A postscript (letter) version
+        <ftp://ftp.ezmlm.org/pub/patches/ezfaq.ps.gz>
+
+     +\bo  A postscript (A4) version
+        <ftp://ftp.ezmlm.org/pub/patches/ezfaq.ps4.gz>
+
+     +\bo  Via mirrors mentioned for the ezmlm-idx package.
+
+     +\bo  An up-to-date text version,F\bFA\bAQ\bQ.\b.i\bid\bdx\bx, included with the ezmlm-idx
+        package.
+
+
+     W\bWW\bWW\bW r\bre\bes\bso\bou\bur\brc\bce\bes\bs
+
+        A\bAn\bn o\bon\bn-\b-l\bli\bin\bne\be v\bve\ber\brs\bsi\bio\bon\bn o\bof\bf t\bth\bhi\bis\bs F\bFA\bAQ\bQ
+           <http://www.ezmlm.org/>The main site with an up-to-date
+           mirror list.  <http://www.de.ezmlm.org/>German mirror.
+           <http://www.pl.ezmlm.org/www.ezmlm.org/>Polish mirror.
+           <http://www.jp.ezmlm.org/>Japanese mirror.
+           <http://www.pt.ezmlm.org/>Portuguese mirror.
+           <http://www.at.ezmlm.org/>Austrian mirror.
+           <http://www.ca.ezmlm.org/ezmlm/>Canadian mirror.
+
+        G\bGe\ben\bne\ber\bra\bal\bl q\bqm\bma\bai\bil\bl a\ban\bnd\bd e\bez\bzm\bml\blm\bm i\bin\bnf\bfo\bo
+
+        +\bo  Dan J. Bernstein's qmail page
+           <http://www.pobox.com/~djb/qmail.html>
+
+        +\bo  Dan J. Bernstein's ezmlm page
+           <http://www.pobox.com/~djb/ezmlm.html>
+
+        +\bo  Russell Nelson's qmail page <http://www.qmail.org>
+
+        +\bo  Mirrors of www.qmail.org <http://www.ISO.qmail.org>.
+           Substitute your two-letter country abbreviation for ``ISO''.
+
+        T\bTh\bhe\be q\bqm\bma\bai\bil\bl m\bma\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bt a\bar\brc\bch\bhi\biv\bve\be
+
+
+        +\bo  <http://www.ornl.gov/cts/archives/mailing-lists/qmail/>
+
+        T\bTh\bhe\be e\bez\bzm\bml\blm\bm m\bma\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bt a\bar\brc\bch\bhi\biv\bve\be
+
+        +\bo  <http://sunsite.auc.dk/mhonarc-archives/ezmlm/>
+           <http://www.ezmlm.org/archive/> This archive of the ezmlm
+           list is searchable from 11/97-present. ezmlm-cgi(1) is used
+           to allow direct access to the sublist archive.
+
+     M\bMa\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bts\bs
+        Please read other documentation and mailing list archives before
+        posting questions to the lists. It's also useful to ``lurk'' on
+        the list for a few days, (i.e. to subscribe and read without
+        posting) before asking your questions on the list.
+
+        To subscribe, send mail to the E-mail addresses listed:
+
+     +\bo  Dan Bernstein's ezmlm list: ezmlm-subscribe@list.cr.yp.to
+
+     +\bo  A digest version of the ezmlm list fredr-ezmlm-digest-
+        subscribe@rivertown.net
+
+     +\bo  Dan Bernstein's qmail list: qmail-subscribe@list.cr.yp.to
+
+     +\bo  The Japanese ezmlm list: ezmlm-subscribe@jp.qmail.org
+
+     +\bo  The Japanese qmail list: qmail-subscribe@jp.qmail.org
+
+     +\bo  A ezmlm/idx digest list of djb-qmail: qmail-digest-
+        subscribe@id.wustl.edu
+
+     +\bo  A ezmlm/idx sublist of djb-qmail (you can test ezmlm-idx
+        commands): qmail-index@id.wustl.edu
+
+
+  1\b1.\b.7\b7.\b.  W\bWh\bhe\ber\bre\be d\bdo\bo I\bI s\bse\ben\bnd\bd c\bco\bom\bmm\bme\ben\bnt\bts\bs o\bon\bn t\bth\bhi\bis\bs d\bdo\boc\bcu\bum\bme\ben\bnt\bt?\b?
+
+  To the authors via E-mail:
+
+  +\bo  Fred Lindberg, lindberg@id.wustl.edu
+
+  +\bo  Fred B. Ringel, fredr@rivertown.net
+
+
+  1\b1.\b.8\b8.\b.  H\bHo\bow\bw t\bto\bo e\bex\bxp\bpe\ber\bri\bim\bme\ben\bnt\bt w\bwi\bit\bth\bh n\bne\bew\bw v\bve\ber\brs\bsi\bio\bon\bns\bs o\bof\bf e\bez\bzm\bml\blm\bm-\b-i\bid\bdx\bx.\b.
+
+  ezmlm-idx>=0.23 writes D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg in a standard format.  If ezmlm-
+  make(1) is invoked with the ``-e'' or ``-+'' switch and the ``DIR''
+  argument only, ezmlm-make(1) will read other arguments from this file.
+  The difference between the switches is that with ``-e'' the options
+  used are the ones specified on the command line, whereas with ``-+''
+  they are the ones currently active for the list, as overridden by any
+  command line options.  Thus, with just:
+
+
+               % ezmlm-make -+ DIR
+
+
+
+
+  you can rebuild the list, without affecting any archives, list state
+  variables, etc. You will _\bl_\bo_\bs_\be _\bm_\ba_\bn_\bu_\ba_\bl _\bc_\bu_\bs_\bt_\bo_\bm_\bi_\bz_\ba_\bt_\bi_\bo_\bn_\bs _\bt_\bo _\bs_\bo_\bm_\be _\bo_\bf _\by_\bo_\bu_\br
+  _\bf_\bi_\bl_\be_\bs. However, text files and D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd are protected against
+  being overwritten, so that your manual customizations of these files
+  are retained. To override this protection, simply specify the used
+  edit switch twice, e.g. ``-ee'' and ``-++'', respectively. This is a
+  feature introduced in ezmlm-idx-0.40.
+
+  To test a new version of ezmlm-idx or to run several version, make the
+  new version as per I\bIN\bNS\bST\bTA\bAL\bLL\bL.\b.i\bid\bdx\bx (if you haven't used ezmlm-idx before)
+  or U\bUP\bPG\bGR\bRA\bAD\bDE\bE.\b.i\bid\bdx\bx (if you've got a previous version of ezmlm-idx
+  installed), setting c\bco\bon\bnf\bf-\b-b\bbi\bin\bn to a new directory. You can use either
+  the current directory or any other directory. If not using the current
+  dir, you also have to:
+
+
+               % make setup
+
+
+
+
+  If you now edit the list using the new ezmlm-make program, the list
+  will automatically be configured to use the new binaries. To change
+  back to the ``default'' installation, just edit the list again, this
+  time with the old ezmlm-make(1).
+
+  If your system has an /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bmr\brc\bc file, you may need to temporarily
+  place the e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) file for the ezmlm version you want to test in
+  d\bdo\bot\btd\bdi\bir\br of the list and use the ezmlm-make(1) ``-c'' switch (see
+  ``Terminology: dotdir'').
+
+  ezmlm-idx>=0.314 comes with ezmlm-test(1), a program that tests most
+  functions of ezmlm+idx and can  be used before installation.
+
+
+  2\b2.\b.  Q\bQu\bui\bic\bck\bk s\bst\bta\bar\brt\bt
+
+
+  1. Create a use ``eztest'' for testing. If you use another name, add
+     the switch ``-u another_name'' to the ezmlm-test(1) line below.
+     (The space between the switch and the argument is required.)
+
+  2. Unpack the ezmlm-0.53 distribution.
+
+  3. Unpack the ezmlm-idx distribution.
+
+  4. Move the ezmlm-idx files to the ezmlm-0.53 directory.
+
+  5. Edit c\bco\bon\bnf\bf-\b-b\bbi\bin\bn and c\bco\bon\bnf\bf-\b-m\bma\ban\bn to reflect the target directories.
+
+  6. build and install:
+
+
+               % cd ezmlm-0.53
+               % patch < idx.patch
+               % make; make man
+               % su
+               # su eztest
+               % ./ezmlm-test
+               % exit
+               # make setup
+               # exit
+
+
+
+
+  7. Make a list and digest list
+
+
+
+
+
+          % ezmlm-make -rdugm -5 me@host ~/list ~/.qmail-list me-list host
+          % ezmlm-sub ~/list me@host
+          % ezmlm-sub ~/list/digest me@host
+          % ezmlm-sub ~/list/mod me@host
+
+
+
+
+  where ``me'' is your user name and ``host'' the host your list is on.
+
+  Now, you are the owner, remote administrator, and subscriber of both
+  list@host and the accompanying digest list list-digest@host. Only
+  subscribers are allowed to access the archive and to post. To post to
+  the list, mail to list@host. For a user to subscribe, s/he should mail
+  to list-subscribe@host and for help to list-help@host.
+
+  When a non-subscriber posts, you will be asked to approve, reject, or
+  ignore the request. If you want to subscriber joe@joehost.dom, mail
+  list-subscribe-joe=joehost.dom@host.
+
+  Digests are generated about every two days, when 30 messages have
+  arrived since the last digest, or when more than 64 kbytes of message
+  body has arrived. To manage the digest list, use the same commands as
+  the main list, but replace ``list'' with ``list-digest''.
+
+  The sender restriction on posting used in this setup works, but is not
+  secure. For more info, read the man pages (start with ezmlm(5) and
+  ezmlm-make(1)), this FAQ (F\bFA\bAQ\bQ.\b.i\bid\bdx\bx in the distribution),
+  R\bRE\bEA\bAD\bDM\bME\bE/\b/R\bRE\bEA\bAD\bDM\bME\bE.\b.i\bid\bdx\bx, I\bIN\bNS\bST\bTA\bAL\bLL\bL/\b/I\bIN\bNS\bST\bTA\bAL\bLL\bL.\b.i\bid\bdx\bx, and U\bUP\bPG\bGR\bRA\bAD\bDE\bE.\b.i\bid\bdx\bx.
+
+
+  3\b3.\b.  O\bOv\bve\ber\brv\bvi\bie\bew\bw o\bof\bf m\bma\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bt m\bma\ban\bna\bag\bge\bem\bme\ben\bnt\bt a\ban\bnd\bd m\bma\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bt m\bma\ban\bna\bag\bge\ber\brs\bs
+
+  (To be written. Until then, please consult the
+  <http://www.ezmlm.org/ezman/> manual for ezmlm and ezmlm-idx related
+  material.)
+
+
+  4\b4.\b.  O\bOv\bve\ber\brv\bvi\bie\bew\bw o\bof\bf e\bez\bzm\bml\blm\bm f\bfu\bun\bnc\bct\bti\bio\bon\bn
+
+
+  4\b4.\b.1\b1.\b.  T\bTh\bhe\be b\bba\bas\bsi\bic\bc s\bse\bet\btu\bup\bp.\b.
+
+  In designing ezmlm, _\bD_\ba_\bn _\bJ_\b. _\bB_\be_\br_\bn_\bs_\bt_\be_\bi_\bn has used the unix philosophy of
+  small component programs with limited and well defined functions.
+  Requests for specific functions can then be met by the addition of new
+  programs.
+
+  Thanks to the program execution mechanism Dan built into qmail, it is
+  easy to execute several small programs per delivery in a defined
+  sequence. It is also very easy to add shell scripts for further
+  customization.
+
+
+  4\b4.\b.2\b2.\b.  I\bIn\bnv\bve\ben\bnt\bti\bio\bon\bns\bs i\bin\bn e\bez\bzm\bml\blm\bm.\b.
+
+  Dan J. Bernstein has written ezmlm in C. It is written for speed and
+  reliability even in the face of power loss and NFS.  These features
+  are augmented to a large extent by the ruggedness of the qmail (also
+  by Dan) delivery mechanism (see qmail-command(8)).
+
+  ezmlm uses some routines and techniques that still are not frequently
+  seen in many mailing list managers. For example, subscriber E-mail
+  addresses are stored in a hash so that searches require reading only,
+  at most, 2% of the E-mail addresses.  ezmlm has a optional message
+  archive, where messages are stored 100 per directory, again to allow
+  more efficient storage and retrieval. Important files are written
+  under a new name and, only when safely written, moved in place, to
+  assure that crashes do not leave the list in an undefined state.
+
+  In addition, ezmlm has a number of new inventions. One of these is
+  bounce detection, which generates an automatic warning containing
+  information identifying the messages which have bounced, followed by a
+  probe message to the E-mail addresses for which mail has bounced.  If
+  the probe bounces, the address is unsubscribed. Thus, the system won't
+  remove E-mail addresses due to temporary bounces: it takes 12 days
+  after the first bounce before a warning is sent, and another 12 days
+  of bounces after the warning bounce before the probe message is set.
+
+  Another Dan J. Bernstein invention is the use of cryptographic cookies
+  based on a timestamp, address, and action. These are used to assure
+  that the user sending a request to subscribe or unsubscribe really
+  controls the target address.  It is also used to prevent forgery of
+  warning or probe messages to make it exceedingly difficult to subvert
+  the bounce detection mechanism to unsubscribe another user.
+
+
+  4\b4.\b.3\b3.\b.  T\bTh\bhe\be q\bqm\bma\bai\bil\bl d\bde\bel\bli\biv\bve\ber\bry\by m\bme\bec\bch\bha\ban\bni\bis\bsm\bm.\b.
+
+  See qmail(7), qmail-local(8), qmail-command(8), envelopes(5), and dot-
+  qmail(5).  Briefly, qmail having resolved the delivery address
+  delivers it via the .\b.q\bqm\bma\bai\bil\bl file that most completely matches the
+  address. This file may be a link to another file, as is the case in
+  ezmlm lists. qmail then delivers the message according to successive
+  lines in this file forwarding it to an address, storing it, or piping
+  it to a program. In the latter case, the program is expected to exit 0
+  leading delivery to proceed to the next line in the .\b.q\bqm\bma\bai\bil\bl file, or 99
+  leading to success without delivery to succeeding lines. An exit code
+  of 100 is a permanent error leading to an error message to the SENDER.
+  An exit code of 111 is used for temporary errors, leading to re-
+  delivery until successful or until the queue lifetime of the message
+  has been exceeded.
+
+  Delivery granularity is the .\b.q\bqm\bma\bai\bil\bl file and re-deliveries start at the
+  top. Thus, if the message fails temporarily at a later line, the
+  delivery according to an earlier line will be repeated. Similarly,
+  qmail may have made deliveries successfully according to most of the
+  .\b.q\bqm\bma\bai\bil\bl file and then fail permanently. The SENDER is informed that the
+  delivery failed, but not about at which point.
+
+  ezmlm takes advantage of these basic mechanisms to build a fast,
+  efficient, and very configurable mailing list manager from a set of
+  small independent programs.
+
+
+  4\b4.\b.4\b4.\b.  W\bWh\bha\bat\bt t\bth\bhe\be d\bdi\bif\bff\bfe\ber\bre\ben\bnt\bt p\bpr\bro\bog\bgr\bra\bam\bms\bs d\bdo\bo.\b.
+
+  See ezmlm(5) and the man pages for the different programs (listed in
+  ezmlm(5)).
+
+
+  4\b4.\b.5\b5.\b.  W\bWh\bha\bat\bt t\bth\bhe\be d\bdi\bif\bff\bfe\ber\bre\ben\bnt\bt f\bfi\bil\ble\bes\bs i\bin\bn t\bth\bhe\be l\bli\bis\bst\bt d\bdi\bir\bre\bec\bct\bto\bor\bry\by d\bdo\bo.\b.
+
+  See ezmlm(5).
+
+
+  4\b4.\b.6\b6.\b.  T\bTh\bhe\be p\bpa\bap\bpe\ber\br p\bpa\bat\bth\bh f\bfo\bor\br p\bpo\bos\bst\bts\bs.\b.
+
+  Messages to the list are delivered to a .\b.q\bqm\bma\bai\bil\bl file, usually ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-
+  l\bli\bis\bst\btn\bna\bam\bme\be which is linked to D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br.  Here, the message is first
+  delivered to ezmlm-reject(1) which can reject messages based on
+  subject line contents, MIME content-type, and message body length. It
+  also by default rejects all messages that do not have the list address
+  in the ``To:'' or ``Cc:'' header. This eliminates most bulk spam. If
+  the list is set up for restrictions based on envelope SENDER, the next
+  delivery is to one or more instances of ezmlm-issubn(1).  If the
+  messages passed this check, it is usually delivered to ezmlm-send(1)
+  for distribution.  If the list is message moderated, it is instead
+  delivered to ezmlm-store(1) which queues the message and sends out a
+  moderation request.  ezmlm-gate(1) is used by some other setups. It
+  will for message moderated lists invoke ezmlm-send(1) directly if the
+  message is from a specific set of SENDERs, and in other cases ezmlm-
+  store(1) to send the message out for moderation.
+
+  You can specify a separate .\b.q\bqm\bma\bai\bil\bl-like file for ezmlm-gate(1).  The
+  lines will be executed and the return codes determine if the message
+  is rejected, sent to the list, or sent to the moderator. See man page
+  for details.
+
+  If the list is configured for digests, D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br also contains an
+  ezmlm-tstdig(1) line followed by an ezmlm-get(1) line. If ezmlm-
+  tstdig(1) determines that the criteria are met for digest generation,
+  it exits with an exit code of 0, causing the ezmlm-get(1) line to be
+  executed leading to a digest mailing. Otherwise, ezmlm-tstdig(1) exits
+  99, resulting in the remainder of the D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br file to be ignored
+  too long. The digest is not related to the message being delivered,
+  but the delivery is used to trigger execution of the relevant
+  programs.
+
+
+  In addition, D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br contains a number of house-keeping functions.
+  These are invocations of ezmlm-warn(1) to send out bounce warnings and
+  and (if the list is moderated) ezmlm-clean(1) to clean the moderation
+  queue of messages that have been ignored. Again, these functions are
+  not related to the specific message delivered, but the delivery itself
+  is used as a convenient ``trigger'' for processing.
+
+
+  4\b4.\b.7\b7.\b.  T\bTh\bhe\be e\bez\bzm\bml\blm\bm p\bpa\bat\bth\bh f\bfo\bor\br m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Replies to moderation requests are channeled to D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br. This
+  file contains an invocation of ezmlm-moderate(1) which invokes ezmlm-
+  send(1) for accepted messages and sends out a rejection notice for
+  rejected messages.  It also sends error messages if the message is not
+  found or already accepted/rejected _\bc_\bo_\bn_\bt_\br_\ba_\br_\by to the moderation message.
+  Thus, if you accept a message already accepted, no error message is
+  sent. ezmlm-clean(1) is also invoked from D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br for house
+  keeping.
+
+
+  4\b4.\b.8\b8.\b.  T\bTh\bhe\be e\bez\bzm\bml\blm\bm p\bpa\bat\bth\bh f\bfo\bor\br a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\biv\bve\be m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Administrative requests for both list and digest lists are captured by
+  ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\btn\bna\bam\bme\be-\b-d\bde\bef\bfa\bau\bul\blt\bt linked to D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br.  Here they are
+  delivered first to ezmlm-get(1) which processed archive retrieval
+  requests, exiting 99 after successful completion which causes the rest
+  of the delivery lines to be ignored. If the request is not for ezmlm-
+  get(1) it rapidly exits 0. This leads to invocation of ezmlm-manage(1)
+  which handles subscriber database functions, help messages, and (if
+  configured) editing of D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ files. Again, ezmlm-warn(1) lines are
+  included for bounce directory processing.
+
+  If configured, an ezmlm-request(1) line is present. This program
+  constructs valid ezmlm requests from command in the subject lines of
+  messages sent to listname-request@host and exits 99. These requests
+  are mailed and will then return to be processed by one of the other
+  programs.
+
+  4\b4.\b.9\b9.\b.  T\bTh\bhe\be e\bez\bzm\bml\blm\bm p\bpa\bat\bth\bh f\bfo\bor\br b\bbo\bou\bun\bnc\bce\bes\bs.\b.
+
+  Bounces to the list are handled by D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\ber\br. For the digest list
+  this is D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/b\bbo\bou\bun\bnc\bce\ber\br. The two were combined in previous
+  versions, which is still supported. As this leads to problems with
+  list names ending in ``digest'', the functions are separate with lists
+  set up or edited with ezmlm-idx>=0.32. The bounce is first delivery is
+  to ezmlm-weed(1) which removes delivery delay notification and other
+  junk. The second to ezmlm-return(1) which analyzes valid bounces
+  storing the information in D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\be/\b/ for the list and
+  D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/b\bbo\bou\bun\bnc\bce\be/\b/ for the digest.  This is the information that
+  ezmlm-warn(1) (invoked from D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br and D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br) uses and
+  processes for automatic bounce handling.  ezmlm-return(1) will also
+  unsubscribe a subscriber from whom a probe message has bounced.
+
+
+  4\b4.\b.1\b10\b0.\b.  M\bMe\bes\bss\bsa\bag\bge\bes\bs t\bto\bo l\bli\bis\bst\bt-\b-o\bow\bwn\bne\ber\br a\ban\bnd\bd l\bli\bis\bst\bt-\b-d\bdi\big\bge\bes\bst\bt-\b-o\bow\bwn\bne\ber\br.\b.
+
+  These are processed by D\bDI\bIR\bR/\b/o\bow\bwn\bne\ber\br and delivered to D\bDI\bIR\bR/\b/m\bma\bai\bil\blb\bbo\box\bx by
+  default. It is better to put the real owner address in this location.
+  This can be done manually, via editing of e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b), or via the
+  ezmlm-make(1) -5 switch. Again, some house-keeping functions are also
+  executed.
+
+
+  4\b4.\b.1\b11\b1.\b.  S\bSt\btr\bru\buc\bct\btu\bur\bre\be o\bof\bf s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br d\bda\bat\bta\bab\bba\bas\bse\bes\bs.\b.
+
+  ezmlm subscriber E-mail addresses are stored within D\bDI\bIR\bR/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/
+  as a hashed set of 53 files. The hash calculated from the address
+  determines which of the 53 files and address is stored in. Thus, to
+  find out if an address is a subscriber, ezmlm has to read at most
+  about 2% of the E-mail addresses.  The hash function insures that E-
+  mail addresses are reasonably evenly distributed among the 53 files.
+
+  Addresses in the files in D\bDI\bIR\bR/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/ are stored as strings
+  starting with ``T'', followed by the address, followed by a zero byte.
+  This is the same format as taken by qmail-queue(8) on file descriptor
+  1.  Thus, subscriber lists can be directly copied to qmail without any
+  further processing.
+
+  With ezmlm-idx>=0.32 you can use an SQL server for the subscriber
+  databases.  Please see the SQL section (``ezmlm support for SQL
+  datbases'').
+
+
+  4\b4.\b.1\b12\b2.\b.  L\bLo\boc\bca\bal\bl c\bca\bas\bse\be i\bin\bn E\bE-\b-m\bma\bai\bil\bl a\bad\bdd\bdr\bre\bes\bss\bse\bes\bs.\b.
+
+  rfc822 states that the host part of an address is case insensitive,
+  but that case of the local part should be respected and the
+  interpretation of it is the prerogative of the machine where the
+  mailbox exists.  Thus, ezmlm preserves the case of the local part, but
+  converts the host part to lower case. ezmlm proper also bases the hash
+  on the case of the local part, so that USER@host and user@host are not
+  (usually) stored in the same file.
+
+  Locally, deliveries are most often case insensitive, i.e. mail to
+  USER@host and user@host are delivered to the same mail box. A
+  consequence of this is that many users use E-mail addresses with
+  different case interchangeably.  The problem is that when USER@host is
+  subscribed, ezmlm will not find that address in response to an
+  unsubscribe request from user@host. This is even more problematic when
+  E-mail addresses have been added by hand to e.g. moderator lists.
+
+  ezmlm-idx>=0.22 changes address storage to make comparisons case
+  insensitive and store E-mail addresses based on the hash of the all
+  lower case address. Case is maintained for the local part. Thus, if
+  USER@host is subscribed, mail is set to USER@host, but user@host is
+  recognized as a subscriber and an unsubscribe request from user@host
+  will remove USER@host from the subscriber list.
+
+  To maintain backwards compatibility with old subscriber lists, a
+  second lookup is made for partially upper case E-mail addresses in
+  some cases. This will find USER@host subscribed with a case sensitive
+  hash as well.
+
+  If may be useful to move all old mixed case E-mail addresses to the
+  ``new'' positions.  Without this, USER@host subscribed with the old
+  system will be able to unsubscribe as USER@host, but not as user@host.
+  After the repositioning, s/he will be successfully able to use any
+  case in an unsubscribe request, e.g. UsEr@host. To do this:
+
+
+
+       % ezmlm-list DIR | grep -G '[A-Z]' > tmp.tmp
+       % xargs ezmlm-sub DIR < tmp.tmp
+
+
+
+
+  This works, because subscribing an address, even if it already exists,
+  will assure that it is stored with a case insensitive hash. On some
+  systems, the grep ``-G'' switch need/should not be used.
+
+
+  4\b4.\b.1\b13\b3.\b.  T\bTe\bes\bst\bti\bin\bng\bg S\bSE\bEN\bND\bDE\bER\bR t\bto\bo a\bal\bll\blo\bow\bw p\bpo\bos\bst\bts\bs o\bon\bnl\bly\by f\bfr\bro\bom\bm l\bli\bis\bst\bt s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs.\b.
+
+  This mode of operation is automatically set up if you specify the
+  ezmlm-make(1) ``-u'' switch. Since there may be some addresses that
+  should be allowed to post, but are not subscribers of list or list-
+  digest, ezmlm-make(1) sets up an additional address database in
+  D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/.  Use ezmlm-sub(1), ezmlm-unsub(1), and ezmlm-list(1) to
+  manipulate these addresses. If the list is configured for remote
+  administration (see ``How remote administration works''), you can
+  add/remove addresses from the D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/ database by mailing list-
+  allow-subscribe@listhost and list-allow-unsubscribe@listhost,
+  respectively. Other commands that access subscriber databases work in
+  the same manner.
+
+  To similarly restrict archive access, use the ezmlm-make(1) ``-g''
+  switch.
+
+  Since SENDER is under the control of a potential attacker, it is not
+  secure to use tests of SENDER for anything important. However, when
+  replies are always sent to SENDER (such as for archive access), a
+  check of SENDER can prevent the sending of information to E-mail
+  addresses not in the database.
+
+  To test sender, use the program ezmlm-issubn(1). It will return 0
+  (true for the shell, success for qmail deliveries) if SENDER is in at
+  least one of a set of subscriber databases. If not, it will return 99
+  (false for the shell: success, but skip remainder of .\b.q\bqm\bma\bai\bil\bl file for
+  qmail deliveries). The basedirs of the subscriber lists (i.e. the
+  directories in which the ``subscriber'' dirs are located) are given as
+  arguments.  ezmlm-issubn(1) can take any number of arguments.
+
+  Thus, to permit an action if SENDER is a subscriber to the list in any
+  of D\bDI\bIR\bR/\b/, D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/, or D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/ and exit silently, put the
+  following into the relevant .\b.q\bqm\bma\bai\bil\bl file:
+
+
+
+
+  |/usr/local/bin/ezmlm/ezmlm-issubn DIR DIR/digest DIR/allow [...]
+  |/path/action_program
+
+
+
+
+  Restricting your list to posts from your subscribers is as easy as
+  that. If your ezmlm binaries are in a different directory, you may
+  have to modify the ezmlm-issubn(1) path.
+
+  ezmlm-issubn(1) has a ``-n'' switch which ``negates/reverses'' the
+  exit code.  To do an action if SENDER is _\bN_\bO_\bT a subscriber of any of
+  the lists:
+
+
+
+       |/usr/local/bin/ezmlm/ezmlm-issubn -n DIR/deny [dir2 ...]
+       |/path/other_program
+
+
+
+
+  To automatically configure the list with a blacklist address database
+  in D\bDI\bIR\bR/\b/d\bde\ben\bny\by, use the ezmlm-make(1) ``-k'' switch. If the list is
+  configured for remote administration (see ``How remote administration
+  works'') and if you are a remote administrator, you can manipulate the
+  ``deny'' database remotely by sending mail to list-deny-subscribe-
+  user=userhost@listhost, etc.
+
+
+  4\b4.\b.1\b14\b4.\b.  H\bHo\bow\bw c\bco\boo\bok\bki\bie\bes\bs w\bwo\bor\brk\bk.\b.
+
+  Each ezmlm list has it's own ``key'' created by ezmlm-make at setup
+  time.  This key is stored in D\bDI\bIR\bR/\b/k\bke\bey\by, and you can improve it by adding
+  garbage of your own to it. However, changing the key will make all
+  outstanding cookies invalid, so this should be done when the list is
+  established.
+
+  When ezmlm receives an action request, such as ``subscribe'', it
+  constructs a cookie as a function of:
+
+  +\bo  the request,
+
+  +\bo  the time,
+
+  +\bo  and the target address.
+
+     The cookie and these items are then assembled into a address that
+     is sent out as the ``Reply-To:'' address in the confirmation
+     request sent to the subscriber. When the subscriber replies, ezmlm
+     first checks if the timestamp is more than 1,000,000 seconds old
+     (approx 11.6 days) and rejects the request if it is. Next, ezmlm
+     recalculates the cookie from the items.  If the cookies match, the
+     request is valid and will be completed. Depending on the
+     circumstances, ezmlm generates an error message or a new cookie
+     based on the current time and sends the target a new confirmation
+     request.
+
+  Dan has based these cookies on cryptographic functions that make it
+  very unlikely that a change in any part of the cookie or the items
+  will result in a valid combination. Thus, it is virtually impossible
+  to forge a request even for someone who has a number of valid requests
+  to analyze. Since the algorithm ezmlm uses is available, the security
+  rests on the key (and the correctness of the algorithm). Anyone who
+  knows the key for your lists can easily construct valid requests.
+
+  As ezmlm-make(1) doesn't use a truly random process to generate the
+  key, it is theoretically possible that someone with sufficient
+  knowledge about your system can guess your key. In practice, this is
+  very unlikely, and the safety of the system is orders of magnitude
+  higher than that of other mechanisms that you may rely on in your list
+  management and mail transport (exclusive of strong encryption, such as
+  _\bP_\bG_\bP).
+
+
+  4\b4.\b.1\b15\b5.\b.  H\bHo\bow\bw m\bmo\bod\bde\ber\bra\bat\bto\bor\br E\bE-\b-m\bma\bai\bil\bl a\bad\bdd\bdr\bre\bes\bss\bse\bes\bs a\bar\bre\be s\bst\bto\bor\bre\bed\bd.\b.
+
+  Moderator E-mail addresses are stored just like ezmlm subscriber
+  addresses, in a set of up to 53 files within the s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs
+  subdirectory of the list's b\bba\bas\bse\bed\bdi\bir\br/\b/.  For subscribers, the b\bba\bas\bse\bed\bdi\bir\br/\b/ is
+  the list directory itself, i.e. D\bDI\bIR\bR/\b/.  For moderators, the default is
+  D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/, which can be overridden by placing a b\bba\bas\bse\bed\bdi\bir\br name (starting
+  with a ``/'') in D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb, D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be, or D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt for
+  subscription moderation, remote administration, and message
+  moderation, respectively. This permits the use of one moderator
+  database for multiple lists. _\bN_\bo_\bt_\be_\b: _\bS_\bu_\bb_\bs_\bc_\br_\bi_\bp_\bt_\bi_\bo_\bn _\bm_\bo_\bd_\be_\br_\ba_\bt_\bo_\br_\bs _\ba_\bn_\bd _\br_\be_\bm_\bo_\bt_\be
+  _\ba_\bd_\bm_\bi_\bn_\bi_\bs_\bt_\br_\ba_\bt_\bo_\br_\bs _\ba_\br_\be _\ba_\bl_\bw_\ba_\by_\bs _\bt_\bh_\be _\bs_\ba_\bm_\be _\ba_\bd_\bd_\br_\be_\bs_\bs_\be_\bs_\b. _\bI_\bf _\bb_\bo_\bt_\bh D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb and
+  D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be contain paths, only the D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb path is used.
+
+
+  4\b4.\b.1\b16\b6.\b.  H\bHo\bow\bw s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn w\bwo\bor\brk\bks\bs.\b.
+
+  Subscription moderation is a simple extension of the ezmlm subscribe
+  mechanism. Once the user has confirmed the subscribe request, a new
+  request is constructed with a _\bd_\bi_\bf_\bf_\be_\br_\be_\bn_\bt _\ba_\bc_\bt_\bi_\bo_\bn _\bc_\bo_\bd_\be. This is sent out
+  to the moderator(s). When a moderator replies with a valid request and
+  cookie combination, the user is subscribed. The user is then also
+  welcomed to the list. Other moderators won't know that the request has
+  already been approved. If other moderators reply to the request, no
+  notification of the duplicate action is sent to the subscriber of the
+  duplicate action. Ezmlm knows that this is a repeat request since the
+  target address is already a subscriber.
+
+  The moderators are not informed about the result, unless there was an
+  error (subscribing a target that is already a subscriber is not
+  considered an error). This cuts down the number of messages a
+  moderator receives. Any list moderator knows (or _\bs_\bh_\bo_\bu_\bl_\bd know) the
+  qmail/ezmlm/unix paradigm: _\bi_\bf _\by_\bo_\bu_\b'_\br_\be _\bn_\bo_\bt _\bt_\bo_\bl_\bd _\bo_\bt_\bh_\be_\br_\bw_\bi_\bs_\be_\b, _\by_\bo_\bu_\br _\bc_\bo_\bm_\bm_\ba_\bn_\bd
+  _\bw_\ba_\bs _\bc_\ba_\br_\br_\bi_\be_\bd _\bo_\bu_\bt _\bs_\bu_\bc_\bc_\be_\bs_\bs_\bf_\bu_\bl_\bl_\by.  This may be counterintuitive to those
+  used to some other operating systems, but in our experience it doesn't
+  take long to get used to the reliability and efficiency of
+  U*ix/qmail/ezmlm.
+
+  Subscription moderation is enabled by creating D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb and adding
+  the subscription moderator to D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/:
+
+
+       % ezmlm-sub DIR/mod moderator@host
+
+
+
+
+  To use an alternative basedir for subscription moderators, place that
+  directory name with a leading ``/'' in D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb.
+
+
+  4\b4.\b.1\b17\b7.\b.  H\bHo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\bio\bon\bn w\bwo\bor\brk\bks\bs.\b.
+
+  The term ``remote administration'' is used to denote the ability of a
+  list administrator by E-mail to add or remove any E-mail address from
+  the subscriber list without the cooperation of the user. Normally,
+  when user@userhost sends a message to list-subscribe-
+  other=otherhost@listhost to subscribe other@otherhost, the
+  confirmation request goes to other@otherhost. However, if remote
+  administration is enabled and user@userhost is a moderator, a
+  confirmation request (with a different action code) is sent back to
+  user@userhost instead. The reply from the administrator is suppressed
+  in the welcome message sent to the new subscriber (other@otherhost).
+  This protects the identity of the remote administrator.
+
+  Remote administration is enabled by creating D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be and adding the
+  remote administrator E-mail address(es) to D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/:
+
+
+       % ezmlm-sub DIR/mod remoteadm@host
+
+
+
+
+  To use an alternative basedir for remote administrators, place that
+  directory name with a leading ``/'' in D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb.  Remote administra-
+  tors and subscription moderators databases always consist of the same
+  E-mail addresses.  If both are enabled and one of D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb and
+  D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be contains an alternative basedir name, this basedir is used
+  for both functions.  If both D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb and D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be contain direc-
+  tory names, the one in D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb is used for both functions.
+
+  Remote administrators can add and remove addresses to the digest list,
+  the ``allow'' list (user aliases for lists using SENDER restrictions
+  on posting and archive access), and if used the ``deny'' list
+  containing addresses that are denied posting rights to the list. The
+  latter is easy to circumvent and intended to block errant mail robots,
+  rather than human users.
+
+
+  4\b4.\b.1\b18\b8.\b.  H\bHo\bow\bw m\bme\bes\bss\bsa\bag\bge\be m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn w\bwo\bor\brk\bks\bs.\b.
+
+  ezmlm-store(1), invoked in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br, receives messages for message
+  moderated lists. If D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt does not exist, ezmlm-store(1) just
+  calls ezmlm-send(1) and the message is posted to the list as if it
+  were not moderated.  If D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt exists, ezmlm-store(1) places the
+  message in D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/p\bpe\ben\bnd\bdi\bin\bng\bg/\b/.  It also sends a moderation request to
+  all the moderators. Included with this request is a copy of the
+  message.  The ``From:'' and ``Reply-To:'' E-mail addresses contain
+  codes for ``reject'' and ``accept'', together with a unique message
+  name (derived from the message timestamp and process id) and a cookie
+  based on these items.  When a moderator replies, ezmlm-moderate(1) is
+  invoked via D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br.  ezmlm-moderate(1) validates the request,
+  and if the request is valid and the message is found in
+  D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/p\bpe\ben\bnd\bdi\bin\bng\bg/\b/, it carries out the requested action.
+
+  If the request is ``reject'' the post is returned to SENDER with an
+  explanation and an optional moderator comment. If the request is
+  ``accept'' the message is posted to the list via ezmlm-send(1). As the
+  request is processed, a stub for the message is created in
+  D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/r\bre\bej\bje\bec\bct\bte\bed\bd/\b/ or D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/a\bac\bcc\bce\bep\bpt\bte\bed\bd/\b/ for ``reject'' and ``accept''
+  requests, respectively.
+
+  If a valid reply is received but the message is no longer in
+  D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/p\bpe\ben\bnd\bdi\bin\bng\bg/\b/, ezmlm-moderate(1) looks for the corresponding stub
+  in D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/r\bre\bej\bje\bec\bct\bte\bed\bd/\b/ and D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/a\bac\bcc\bce\bep\bpt\bte\bed\bd/\b/.  If the stub is found and
+  the fate of the message was the one dictated by the new request, no
+  further action is taken. If, however, no stub is found or the request
+  and the actual message fate do not match, a notification is sent to
+  the moderator. This scheme was chosen to impart a maximum of
+  information with a minimum of messages. Also, it is the least
+  demoralizing setup for multiple moderator lists, where it is important
+  not to notify subsequent moderators that their work was in vain since
+  the action of the first responding moderator has already resulted in
+  processing of the message.
+
+  If a message is not ``rejected'' or ``accepted'' it remains in
+  D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/p\bpe\ben\bnd\bdi\bin\bng\bg/\b/ until it times out. Cleanup of both messages and
+  stubs is accomplished by ezmlm-clean(1) which is invoked through both
+  D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br and D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br for message moderated lists. ezmlm-
+  clean(1) looks at the timestamp used to generate the message/stub
+  name. If it is older than 120 hours (configurable in a range of 24-240
+  hours, by placing the value in D\bDI\bIR\bR/\b/m\bmo\bod\bdt\bti\bim\bme\be) it is removed.  Unless
+  suppressed with the ezmlm-clean(1) ``-R'' switch, the SENDER of the
+  message is notified.
+
+  By default, the E-mail addresses of message moderators are stored as a
+  subscriber list with a basedir of D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/.  This can be changed to
+  any other b\bba\bas\bse\bed\bdi\bir\br by placing the name of that directory with a leading
+  ``/'' in D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt.  Although the default basedirs for message
+  moderation and subscription moderation/remote administration are the
+  same, both the functions and actors are entirely independent.
+
+
+  4\b4.\b.1\b19\b9.\b.  H\bHo\bow\bw Q\bQM\bMQ\bQP\bP s\bsu\bup\bpp\bpo\bor\brt\bt w\bwo\bor\brk\bks\bs
+
+  qmail processes messages on a first-come-first-served basis. This
+  means that when it receives a post to 100,000 subscribers, it will try
+  all the recipients before processing the next message. Often, it is
+  desirable to offload this work to an external host so that the main
+  list host remains responsive to e.g. ``subscribe'' and archive access
+  commands, as well as to other mail is it is not a dedicated mail host.
+
+  ezmlm-idx allows the main distribution work to be offloaded to an
+  external server via the QMQP protocol. Configure qmail-qmqpc(1) on the
+  list host, and qmail-qmqpd(1) on the mail host (see qmail docs for
+  details), then create the file D\bDI\bIR\bR/\b/q\bqm\bmq\bqp\bps\bse\ber\brv\bve\ber\brs\bs/\b/0\b0. The list housed in
+  D\bDI\bIR\bR will now use the QMQP server for posts, by the local qmail for
+  other messages. If you apply the qmail-qmqpc.tar.gz patch (included in
+  the ezmlm-idx distribution), you can specify the QMQP server IP
+  addresses, one per line, in D\bDI\bIR\bR/\b/q\bqm\bmq\bqp\bps\bse\ber\brv\bve\ber\brs\bs/\b/0\b0, just as you normally
+  would in /\b/v\bva\bar\br/\b/q\bqm\bma\bai\bil\bl/\b/c\bco\bon\bnt\btr\bro\bol\bl/\b/q\bqm\bmq\bqp\bps\bse\ber\brv\bve\ber\brs\bs.  If the first server cannot
+  be contacted, the installation will try the second, and so on. The
+  advantage of controlling the servers locally is that you can specify
+  different servers for different lists. A good idea is to set up also
+  the list host as a QMQP server and use that as the last IP address.
+  This way, the list host will be used if the main QMQP server cannot be
+  contacted. Of course, ezmlm does not loose messages, but rather lets
+  qmail redeliver the post if no QMQP server is available.
+
+
+  4\b4.\b.2\b20\b0.\b.  H\bHo\bow\bw m\bme\bes\bss\bsa\bag\bge\bes\bs a\bar\bre\be s\bst\bto\bor\bre\bed\bd i\bin\bn t\bth\bhe\be a\bar\brc\bch\bhi\biv\bve\be.\b.
+
+  The structure of the ezmlm list archive is described in the ezmlm(5)
+  manual page.  Basically, the message is stored in D\bDI\bIR\bR/\b/a\bar\brc\bch\bhi\biv\bve\be/\b/n\bn/\b/m\bm,
+  where ``n'' is the message number divided by 100 and ``m'' the
+  remainder (2 digits). The first message is stored in D\bDI\bIR\bR/\b/a\bar\brc\bch\bhi\biv\bve\be/\b/0\b0/\b/0\b01\b1.
+
+
+  4\b4.\b.2\b21\b1.\b.  H\bHo\bow\bw t\bth\bhe\be m\bme\bes\bss\bsa\bag\bge\be i\bin\bnd\bde\bex\bx w\bwo\bor\brk\bks\bs.\b.
+
+  The ezmlm-idx(1) adds the option (default) of a message index to
+  ezmlm.  The ``From:'' line, the subject, the author's E-mail address
+  and name and the time of receipt are logged for each message as it is
+  received. The subject is ``normalized'' by concatenating split lines
+  and removing reply-indicators such as ``Re:''. A hash of the
+  normalized subject with all white space removed is also stored.  The
+  hash for any message within a thread is almost always the same and is
+  used together with the order of receipt to connect a set of messages
+  into a ``thread''. A hash is needed due to the inconsistent handling
+  by MUAs of white space in rfc2047-encoded subject headers.
+
+  The message index is stored as D\bDI\bIR\bR/\b/a\bar\brc\bch\bhi\biv\bve\be/\b/n\bn/\b/i\bin\bnd\bde\bex\bx, where ``n'' is the
+  message number mod 100.  Thus, the directory D\bDI\bIR\bR/\b/a\bar\brc\bch\bhi\biv\bve\be/\b/5\b52\b2/\b/ stores
+  messages 5200 through 5299 and the file ``index'' which contains the
+  index for those messages.
+
+  The message index can be retrieved with the -index command (see ezmlm-
+  get(1)). You can also retrieve a range of messages, a specific thread,
+  or generate a message digest (see ezmlm-get(1)). Each of these
+  commands can be disabled or restricted as desired by the list owner.
+
+  The ezmlm-idx(1) can be used at any time to either reconstruct an
+  existing index or create one an index for an existing message archive.
+  without one.
+
+
+  4\b4.\b.2\b22\b2.\b.  H\bHo\bow\bw t\bth\bhr\bre\bea\bad\bdi\bin\bng\bg w\bwo\bor\brk\bks\bs.\b.
+
+  A ezmlm thread is just a message number-ordered set of messages with
+  identical ``normalized'' subject entries. This is a very reliable
+  method for threading messages. It does not rely on any variably
+  present ``In-Reply-To:'' or ``References:'' headers. If the subject
+  changes, the continuation becomes a separate thread very close to the
+  original thread in a digest. ezmlm uses this mechanism to return
+  message sets threaded and with a thread and author index, unless
+  specifically told not to do so with the ``n'' format specifier.
+  Naturally, lists set up without a message index (using the ezmlm-make
+  ``-I'' switch) do not maintain thread information.
+
+
+  4\b4.\b.2\b23\b3.\b.  H\bHo\bow\bw d\bdi\big\bge\bes\bst\bts\bs w\bwo\bor\brk\bk.\b.
+
+  A ``digest'' is just an ordered collection of messages from a list,
+  usually sent out regularly depending on the time and traffic volume
+  since the last digest. Digest subscribers thus can read messages as
+  ``threads'' once daily, rather than receiving a constant trickle of
+  messages.
+
+  As a major change in ezmlm-idx-0.30, the digest is no longer a totally
+  separate ezmlm-list, but a part of the main list. This has security
+  advantages, makes setup and administration easier, saves space, and
+  allows a consistent way for subscribers of both ``list'' and ``list-
+  digest'' to retrieve missed messages from a single archive.
+
+  The digest of the list ``list'' is always called ``list-digest''. To
+  set up a list with a digest, simply use the ezmlm-make(1) ``-d''
+  switch. You subscribe to and unsubscribe from a digest the same way as
+  for the main list, except that the request is sent to e.g. list-
+  digest-subscribe@host rather than to list-subscribe@host.
+
+  Any option such as remote admin or subscription moderation that is
+  active for the list applies also to the digest list. Any restrictions
+  in posts or archive retrieval set up for the list, automatically
+  accept both subscribers of the main list and of the digest list.
+
+  The changes in ezmlm-idx>=0.30 allow all programs to service both list
+  and list-digest functions.  All digest-specific files are stored in
+  D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/.  Digest list subscriber addresses in
+  D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/ and digest list bounce information in
+  D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/b\bbo\bou\bun\bnc\bce\be/\b/. Text files are shared between list and digest. To
+  get the local part of the list or list-digest name in a context
+  sensitive manner, use ``<#l#>'' (lower case ``L'') in the text file.
+
+
+  In order to generate digest, the list needs to be archived and indexed
+  (both default).  You can retrieve sets of messages from the message
+  archive. Such sets are always returned to the SENDER of the request.
+  ``Digests'' are a special form of such a set/request. First, there are
+  no restrictions on the number of messages that can be in a digest
+  (which is balanced by the requirement for a ``digest code'' that needs
+  to be specified in order to create a digest based on a mailed
+  request).  Second, special files (D\bDI\bIR\bR/\b/d\bdi\big\bgi\bis\bss\bsu\bue\be and D\bDI\bIR\bR/\b/d\bdi\big\bgn\bnu\bum\bm) keep
+  track of the digest issue and the message number, amount, and time
+  when the last digest was created.  Thus, the system is adapted to make
+  it easy to create the regular collections of messages commonly
+  referred to as ``digests''.
+
+  Digest can be generated in several different ways:
+
+     C\bCo\bom\bmm\bma\ban\bnd\bd l\bli\bin\bne\be
+        ezmlm-get can be invoked on the command line, or via a script
+        from e.g.  crond(8):
+
+
+                  % ezmlm-get DIR
+
+
+
+
+     If for some reason the digest should be disseminated via a separate
+     list, the digest can be redirected to a ``target address'' with the
+     ezmlm-get(1) ``-t'' switch. This may be useful if a non-standard
+     digest list name is required. In this case, the list disseminating
+     the digest must be set up as a sublist of the main list (see ``How
+     sublists work'').
+
+
+     f\bfr\bro\bom\bm D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br
+        This is the default and does not require and additional setup.
+        It works well with most lists. The only possible advantage is
+        for very low traffic lists and for lists where it is important
+        that a digest be sent out at a specific time (as D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br
+        digests are triggered only when messages are received).
+
+        In D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br, ezmlm-get(1) needs to be combined with ezmlm-
+        tstdig(1) so that digests are generated only if certain criteria
+        are met (in this case, more than 30 messages, 64 kbytes of
+        message body or 48 hours since the latest digest). Add these
+        lines after the ezmlm-send line in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br:
+
+
+                  |/usr/local/bin/ezmlm/ezmlm-tstdig -t48 -m30 -k64 DIR || exit 99
+                  |/usr/local/bin/ezmlm/ezmlm-get diglist@host DIR || exit 0
+
+
+
+
+     To set this up automatically when you create the list:
+
+
+                  % ezmlm-make -d DIR dot local host [code]
+
+
+
+
+     Again, the ezmlm-get(1) ``-t'' switch can be used for non-standard
+     arrangements to redirect the digest.  The ezmlm-make(1) ``-4''
+     switch can be used to specify alternative ezmlm-tstdig(1) parame-
+     ters.
+
+     f\bfr\bro\bom\bm D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br
+        This is useful only if you want digests at specific times, and
+        you do not have access to crond(8) on the list host.  ezmlm-
+        get(1) is in it's normal place in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br before ezmlm-
+        manage(1), but a digest code is specified in the ezmlm-get(1)
+        command line. To trigger digests requires a regular trigger
+        messages generated from e.g. crond(8) (see below), but this can
+        be done from _any_ host, not only the list host.  ezmlm-make(1)
+        sets up ezmlm-get(1) this way if a digest ``code'' is given as
+        the 5th ezmlm-make(1) command line argument. However, you need
+        to set up the trigger messages separately (see below):
+
+
+                  % ezmlm-make DIR dot local host code
+
+
+
+
+     To also test for message volume with this setup, generate trigger
+     messages with the granularity you'd like, and add a ezmlm-tstdig(1)
+     line to D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. E.g., use a trigger message every 3 hours and
+     the following ezmlm-tstdig(1) line before ezmlm-get(1):
+
+
+                  |/usr/local/bin/ezmlm/ezmlm-tstdig -t24 -m30 -k64 DIR || exit 99
+
+
+
+
+     In general, a cron-triggered digest is preferred for very large
+     lists and for lists with very low traffic.  Again, the ezmlm-get(1)
+     ``-t'' switch can be used for non-standard arrangements to redirect
+     the digest.  For most lists, the digesting from D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br works
+     very well, and does not require any extra setup work.
+
+     C\bCo\bom\bmb\bbi\bin\bna\bat\bti\bio\bon\bn s\bse\bet\btu\bup\bps\bs
+        The default setup in the ezmlmrc(5) file included in the
+        distribution is the D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br triggered setup described above.
+        If you in addition use ezmlm-cron(1) or crond(8) directly to
+        generate trigger messages to list-dig.code@host, you can get
+        regular digests (via the trigger messages and D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br), with
+        extra digest sent when traffic is unusually high (via the ezmlm-
+        tstdig/ezmlm-get limits set in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br).  This works best
+        when the time argument on the ezmlm-tstdig(1) command line is
+        the same as the trigger message interval, and the other ezmlm-
+        tstdig(1) parameters are set so that they are only rarely
+        exceeded within the normal digest interval.
+
+
+  4\b4.\b.2\b24\b4.\b.  H\bHo\bow\bw W\bWW\bWW\bW a\bar\brc\bch\bhi\biv\bve\be a\bac\bcc\bce\bes\bss\bs w\bwo\bor\brk\bks\bs.\b.
+
+  If the list is set up with ezmlm-make -i, ezmlm-archive(1) will be
+  invoked from D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br. This program creates indices for threads,
+  subjects, and authors under D\bDI\bIR\bR/\b/a\bar\brc\bch\bhi\biv\bve\be from the i\bin\bnd\bde\bex\bx files.  ezmlm-
+  cgi(1) is set up per user or globally (see man page) and told about
+  different lists via the /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bm/\b/e\bez\bzc\bcg\bgi\bir\brc\bc file. ezmlm-cgi(1) presents
+  and used the index created by ezmlm-archive(1) and converts these and
+  the messages to html on-the-fly. To be as efficient as possible,
+  ezmlm-cgi(1) outputs only basic html. However, style sheets are
+  supported and can be used to customize formatting without modification
+  of ezmlm-cgi(1).  Extra buttons can be added via the config file. See
+  man page for details.
+
+
+
+
+  4\b4.\b.2\b25\b5.\b.  H\bHo\bow\bw e\bez\bzm\bml\blm\bm-\b-t\bts\bst\btd\bdi\big\bg w\bwo\bor\brk\bks\bs.\b.
+
+  ezmlm-tstdig(1) looks at D\bDI\bIR\bR/\b/n\bnu\bum\bm and D\bDI\bIR\bR/\b/d\bdi\big\bgn\bnu\bum\bm to determine how many
+  messages and how much traffic (in terms of bytes of message body) has
+  arrived to the list since the latest digest. It also determines how
+  much time has passed since the last digest was generated. If any of
+  the criteria specified by command line switches exists, ezmlm-
+  tstdig(1) exits 0, causing the invocation of the next line in the
+  .qmail file. If not, ezmlm-tstdig(1) exits 99 causing qmail to skip
+  the rest of the .qmail file. ezmlm-tstdig(1) looks at LOCAL to
+  determine if it is invoked in the command line, in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br, or in
+  D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. In the latter two cases, ezmlm-tstdig(1) verifies that
+  the list local address is correct. If invoked in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br, ezmlm-
+  tstdig(1) exits 0 for all action requests except list-dig, so that is
+  does not interfere with the normal functions of ezmlm-get(1) and
+  ezmlm-manage(1). ezmlm-tstdig(1) uses D\bDI\bIR\bR/\b/t\bts\bst\btd\bdi\big\bg as a flag to avoid
+  problems caused by starting the program when another copy is already
+  running.
+
+  ezmlm-make(1) automatically configures ezmlm-tstdig(1) with the
+  parameters ``-t48 -m30 -k64'', which can be overridden with the ``-3''
+  switch.
+
+
+  4\b4.\b.2\b26\b6.\b.  H\bHo\bow\bw s\bsu\bub\bbl\bli\bis\bst\bts\bs w\bwo\bor\brk\bk.\b.
+
+  ezmlm uses the concept of sublists.  Sublists are regular ezmlm lists,
+  except that they only accept messages from their parent list, which is
+  placed in the file D\bDI\bIR\bR/\b/s\bsu\bub\bbl\bli\bis\bst\bt.
+
+  sublists are used to split the load of a large mailing list among
+  several hosts. All you need to do to set up a local sublist of e.g.
+  the qmail@list.cr.yp.to list is to create a ezmlm list, and put
+  ``qmail@list.cr.yp.to'' into D\bDI\bIR\bR/\b/s\bsu\bub\bbl\bli\bis\bst\bt of you list, and subscribe
+  the sublist to the main qmail list. Now anyone can subscribe to your
+  local list which handles its own bounces, subscribe requests, etc.
+  The load on the main list is only the single message to your local
+  list.
+
+  Sublists will not add their own mailing list header and they will not
+  add a subject prefix. Normally, sublists will use their own message
+  number, rather than that used by the main list.  With ezmlm-idx>=0.23,
+  sublists that are not archived and not indexed, will instead use the
+  main list message number. This way, bounce messages from the sublist
+  can refer the subscriber to the main list archive. This is not done
+  for indexed/archived sublists for security reasons (an attacker could
+  overwrite messages in the sublist archive).
+
+  With ezmlm-idx>=0.31, there is support for using ezmlm as a sublist of
+  a mailing list run by another mailing list manager. To set this up,
+  set up a normal ezmlm sublist, then edit D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br so that the _\be_\bz_\bm_\bl_\bm_\b-
+  _\bs_\be_\bn_\bd line contains the command line option ``-\b-h\bh _\bX_\b-_\bL_\bi_\bs_\bt_\bp_\br_\bo_\bc_\be_\bs_\bs_\bo_\br_\b-
+  _\bV_\be_\br_\bs_\bi_\bo_\bn_\b:'' (before D\bDI\bIR\bR). As the header text, you need to use a header
+  that the main list manager adds to messages. Now your sublist will
+  accept only messages from the main list requiring that they come from
+  that list _\ba_\bn_\bd contain the header specified.
+
+  ezmlm-idx>=0.313 also has added protection against the malicious
+  subscription of the ezmlm list to mailing lists run by other list
+  managers. If the ezmlm-reject(1) line in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br has ``-h'' and
+  ``D\bDI\bIR\bR'' on it, ezmlm-reject(1) will read D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\brr\bre\bej\bje\bec\bct\bt and reject
+  messages that have any header specified in that file. See the ezmlm-
+  reject(1) man page for suitable headers.
+
+
+
+  4\b4.\b.2\b27\b7.\b.  H\bHo\bow\bw s\bsu\bub\bbl\bli\bis\bst\bti\bin\bng\bg c\bca\ban\bn b\bbe\be m\bma\bad\bde\be t\btr\bra\ban\bns\bsp\bpa\bar\bre\ben\bnt\bt t\bto\bo t\bth\bhe\be u\bus\bse\ber\br.\b.
+
+  Often you create a local sublist of a list that you do not control.
+  Local users know to subscribe to your local list. However,
+  occasionally, you want to run your own list as a main list and a
+  series of sublists per geographic site, or split onto several hosts if
+  the list is too large to be handled by a single computer. You may also
+  want to split the load of a ``well known'' list host that is getting
+  overwhelmed with traffic. ezmlm supports sublists, but here the fact
+  that the user has to interact with the correct sublist is a problem.
+  What if the user doesn't remember which sublist s/he is subscribed to?
+  What if you change the name of a sublist host or move a sublist to a
+  different host?
+
+  ezmlm-idx&-0.32 adds ezmlm-split(1), which allows sublisting
+  transparent to the user. This program is invoked before ezmlm-
+  manage(1) in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. If it detects a subscribe or unsubscribe
+  command, it will forward the command to the appropriate sublist based
+  on a ``split file'' D\bDI\bIR\bR/\b/s\bsp\bpl\bli\bit\bt. This file contains entries, one per
+  line, of the format:
+
+
+               domain:lo:hi:sublistname@sublisthost
+               edu:::othersub@otherhost
+               :1:26:third@thirdhost
+
+
+
+
+  For each address, a hash in the range 0-52 is calculated. The
+  ``domain'' is the last two parts of the host name, reversed. Thus, for
+  id.wustl.edu it would be ``edu.wustl''. The domain is considered to
+  match if the characters in the split file match. It is advisable to
+  use only the last part of the domain for compatibility with the SQL
+  version version  (see section ``ezmlm support for SQL datbases'').
+
+  Thus, any address *@*.domain with a hash between ``lo'' and ``hi''
+  inclusive would match the first line and be forwarded to
+  sublistname@sublisthost.  *@*.edu (independent of hash) would match
+  the second line and be forwarded to othersub@otherhost. Of remaining
+  requests, a request for any target address with a hash between 1 and
+  26 would be forwarded to the sublist third@thirdhost. Remaining
+  requests would be passed on to the local list.
+
+  The domain is useful for ``geographic'' splitting, and the hash for
+  load splitting (within a domain). The user interacts only with the
+  main list, and does not need to know from which sublist s/he is
+  serviced.
+
+  ezmlm-idx sublists use the message number of the main list message if
+  they are not indexed. This allows sublists to in bounce messages refer
+  the subscriber to the main list archive. Use ezmlm-make(1) in
+  conjunction with ezmlmsubrc(5) to set up the sublists. See man pages
+  for further details.
+
+  Since the addresses are stored locally, the system is very fast and
+  robust, but it is difficult to add new sublists. ezmlm-split(1) -D
+  supports parsing addresses on stdin and splitting them to stdout (see
+  man page). Thus, if you divide the domain of some sublist(s) onto a
+  net set of sublists, you can use ezmlm-list(1) to collect the
+  addresses, ezmlm-split -D with the new split file to split them, then
+  after clearing the local subscriber databases use ezmlm-sub(1) to add
+  the correct addresses to each new sublist.  The section on SQL support
+  describes an alternative way of managing sublists (see section ``ezmlm
+  support for SQL datbases'').
+
+  4\b4.\b.2\b28\b8.\b.  H\bHo\bow\bw t\bto\bo s\bse\ber\brv\bvi\bic\bce\be c\bco\bom\bmm\bma\ban\bnd\bds\bs i\bin\bn t\bth\bhe\be s\bsu\bub\bbj\bje\bec\bct\bt l\bli\bin\bne\be.\b.
+
+  Rfc2142 (standards track) says that for each mailing list list@host,
+  there MUST be an administrative address list-request@host. This is not
+  the default for ezmlm, but can be added with ezmlm-make(1) ``-q'',
+  which adds a ezmlm-request(1) line before the ezmlm-manage(1) line in
+  D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. This address is used to manage commands in the
+  ``Subject:'' line, by translating them into appropriate ezmlm command
+  messages.
+
+  When migrating from other mailing list managers which use this method
+  to issue list commands, configuring ezmlm to respond to such commands
+  may be useful. In addition, some software manufacturers sell MUAs and
+  mail gateways that are unable to correctly transport rfc822-compliant
+  Internet mail with certain characters in the local part of the
+  address.
+
+  ezmlm-request(1) services the list-request@host address per rfc2142
+  (standards track). It is usually invoked in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br before ezmlm-
+  get(1) and ezmlm-manage(1). It ignores all requests that are not for
+  the list-request address. For requests to the list-request@host
+  address, ezmlm-request(1) parses the ``Subject:'' line. If a ezmlm
+  command address starting with the contents of D\bDI\bIR\bR/\b/o\bou\but\btl\blo\boc\bca\bal\bl (e.g. list-
+  get45) is on the command line, ezmlm-request(1) generates the
+  corresponding full ezmlm request message. If the subject does not
+  start with the contents of D\bDI\bIR\bR/\b/o\bou\but\btl\blo\boc\bca\bal\bl, ezmlm-request(1) prefixes the
+  line with the contents of D\bDI\bIR\bR/\b/o\bou\but\btl\blo\boc\bca\bal\bl, thereby building a complete
+  ezmlm command. If a host name is specified, it must match the contents
+  of D\bDI\bIR\bR/\b/o\bou\but\bth\bho\bos\bst\bt, i.e. ezmlm-request(1) in this function will only
+  generate command messages for the local list.
+
+  Thus, a subject of ``subscribe'' to list-request@host will be auto-
+  magically rewritten as a message to list-subscribe-
+  userlocal=userhost@host.  Similarly, any ezmlm command or ``Reply-
+  To:'' address can be pasted into the subject field and sent to list-
+  request@host.  ezmlm-request(1) does not validate the command name,
+  but invalid commands result in a ``help'' message in reply via ezmlm-
+  manage(1). This allows ezmlm-request(1) to also service custom
+  commands, like list-faq@host that you may have created for your list.
+
+  If the ``Subject:'' is empty or does not start with a letter, ezmlm-
+  request(1) will attempt to interpret the first message body line that
+  starts with a letter in the first position.
+
+  When ezmlm-request(1) has successfully processed a ''request''
+  command, it exits 99 to skip the rest of D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br.
+
+  To set up a list to include ezmlm-request processing, use the ezmlm-
+  make(1) ``-q'' switch. The default is to not do this.
+
+
+  4\b4.\b.2\b29\b9.\b.  H\bHo\bow\bw t\bto\bo s\bsu\bup\bpp\bpo\bor\brt\bt a\bal\blt\bte\ber\brn\bna\bat\bti\biv\bve\be c\bco\bom\bmm\bma\ban\bnd\bd n\bna\bam\bme\bes\bs.\b.
+
+  ezmlm-idx>=0.23 allows alternate names for all user commands. This can
+  be used to e.g. make a message to list-remove@host to result in an
+  ``unsubscribe'' action. This may help migration from other mailing
+  list managers and in non-English environments. The use of aliases
+  allows ezmlm to respond to new command names, while always responding
+  correctly to the standard commands. If ezmlm-request(1) is used it
+  will automatically be able to deal with any commands you set up for
+  the list, within ezmlm or as separate programs.  See ``Multiple
+  language support'' on how to set up command aliases.
+
+
+
+
+  4\b4.\b.3\b30\b0.\b.  H\bHo\bow\bw t\bto\bo a\bad\bdd\bd y\byo\bou\bur\br o\bow\bwn\bn c\bco\bom\bmm\bma\ban\bnd\bds\bs.\b.
+
+  The qmail/ezmlm mechanism makes it very easy to add your own commands.
+  You can add them to D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br, but this requires great care in terms
+  of ordering and exit codes. Easier is to set them up separately with a
+  .\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt-\b-c\bco\bom\bmm\bma\ban\bnd\bd file.
+
+  Let's assume you want to allow anyone to determine how many
+  subscribers are subscribed to your list with the command list-
+  count@host.  Just create a program to do the work:
+
+
+               #!/bin/sh
+               DTLINE='Delivered-To: list-count@host processor'
+               grep "$DTLINE" > /dev/null &&
+                       { echo "This message is looping"; exit 100; }
+               {
+                 echo "$DTLINE"
+                 cat <<EOF
+                 From: list-help@host
+                 To: $SENDER
+                 Subject: list@host subscriber count
+
+                 Current number of subscribers:
+                 EOF
+                 ezmlm-list ~/DIR | wc -l
+               } | /var/qmail/qmail-inject -f list-return- "$SENDER"
+               exit 0
+
+
+
+
+  Then, create D\bDI\bIR\bR/\b/c\bco\bou\bun\bnt\bt containing ``|/path/program'' and then do ``ln
+  -sf DIR/count ~/.qmail-list-count''. Now, the command will pass the
+  message to ``program''. The first thing ``program'' looks for is its
+  delivered-to line to detect looping. If not found, it goes on to print
+  this header, followed by some minimal text and the subscriber number.
+  This can of course be made prettier with ezmlm-list error checking,
+  and maybe in perl, but shows how easy it is to extend ezmlm. All
+  thanks to the DJB/qmail delivery mechanism.
+
+
+  4\b4.\b.3\b31\b1.\b.  H\bHo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bto\bor\brs\bs c\bca\ban\bn r\bre\bet\btr\bri\bie\bev\bve\be a\ba s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br l\bli\bis\bst\bt
+
+  A user with shell access can always manipulate subscriber lists with
+  ezmlm-sub(1), ezmlm-unsub(1), and ezmlm-list(1) for the lists s/he
+  owns.
+
+  Sometimes a remote administrator requires a list of subscriber E-mail
+  addresses. At the same time, the list should be kept out of the hands
+  of spammers and all unauthorized entities. By default, ezmlm does not
+  allow remote subscriber list retrieval.  You can enable the ``-list''
+  command for remote retrieval of a subscriber list by using the ezmlm-
+  make(1) ``-l'' switch or by adding the ``-l'' switch to the ezmlm-
+  manage(1) line in DIR/manager. With this switch, ezmlm will permit
+  retrieval of a subscriber list, but only to remote administrators.
+  Subscribers cannot get the list membership, and any outsider would
+  have to be able to read a remote administrator's mail to get the list.
+  _\bN_\bo_\bt_\be_\b: _\bT_\bh_\bi_\bs _\bo_\bp_\bt_\bi_\bo_\bn _\bi_\bs _\bn_\bo_\bt _\bf_\bu_\bn_\bc_\bt_\bi_\bo_\bn_\ba_\bl _\bu_\bn_\bl_\be_\bs_\bs _\bt_\bh_\be _\bl_\bi_\bs_\bt _\bi_\bs _\bc_\bo_\bn_\bf_\bi_\bg_\bu_\br_\be_\bd _\bf_\bo_\br
+  _\br_\be_\bm_\bo_\bt_\be _\ba_\bd_\bm_\bi_\bn_\bi_\bs_\bt_\br_\ba_\bt_\bi_\bo_\bn_\b, _\bi_\b._\be_\b. _\bt_\bh_\be _\be_\bz_\bm_\bl_\bm_\b-_\bm_\ba_\bk_\be_\b(_\b1_\b) _\b`_\b`_\b-_\br_\bl_\b'_\b' _\bs_\bw_\bi_\bt_\bc_\bh_\be_\bs _\bn_\be_\be_\bd _\bt_\bo
+  _\bb_\bo_\bt_\bh _\bb_\be _\bu_\bs_\be_\bd_\b.
+
+  The list returned is unsorted for efficiency reasons. You can easily
+  sort it or use your mail reader to find a specific entry. The number
+  of subscribers is shown at the bottom of the list. To get the number
+  of subscribers from the command line, use:
+               % ezmlm-list DIR | wc -l
+
+
+
+
+
+  4\b4.\b.3\b32\b2.\b.  H\bHo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bto\bor\brs\bs c\bca\ban\bn d\bde\bet\bte\ber\brm\bmi\bin\bne\be t\bth\bhe\be n\bnu\bum\bmb\bbe\ber\br o\bof\bf s\bsu\bub\bb-\b-
+  s\bsc\bcr\bri\bib\bbe\ber\brs\bs
+
+  For the list aaa@example.com, send a message to aaa-listn@example.com.
+  This is preferable to the ``-list'' command for very large lists.
+
+
+  4\b4.\b.3\b33\b3.\b.  H\bHo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bns\bs c\bca\ban\bn s\bse\bee\be i\bif\bf a\ban\bn a\bad\bdd\bdr\bre\bes\bss\bs i\bis\bs a\ba s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br o\bor\br n\bno\bot\bt
+
+  For the list aaa@example.com, and subscriber user@host.cn send a
+  message to aaa-query=host.cn@example.com. Users can do this as well,
+  but in that case the reply is sent to the target address
+  (user@host.cn) and not to the SENDER to protect the subscriber
+  addresses.
+
+
+  4\b4.\b.3\b34\b4.\b.  H\bHo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bto\bor\brs\bs c\bca\ban\bn s\bse\bea\bar\brc\bch\bh t\bth\bhe\be s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn l\blo\bog\bg
+
+  The same conditions that enable remote administrators to retrieve a
+  subscriber list (see ``'') also enable the remote admin to retrieve
+  the subscription log, i.e. the log of changes made to the subscriber
+  list. The command is list-log@host. The entries are of the form ``date
+  timestamp dir event address comment''. ``dir'' is ``+'' for addition
+  of an address, ``-'' for removal, ``event'' is empty for normal
+  (un)subscribe ``manual'' for changes made with ezmlm-(un)sub, and
+  ``probe'' for removals via bounce handling. ``address'' is the
+  subscription address, and ``comment'' is empty or the subscribers
+  ``From:'' line. The log can be used to look at recent
+  additions/removals and to try to track down a subscriber address from
+  e.g. the name on the ``From:'' line. The log is written on a best-
+  effort basis. In contrast to the subscriber database, entries in the
+  log may be lost at a system crash.
+
+  The remote administrator can do a case-insensitive search through the
+  log with the command list-log.xxx@host, where ``xxx'' is any sequence
+  of letters/numbers that must occur on a line in order for that line to
+  be included in the reply. A ``_'' is a wild card and should be used
+  for special characters as well. Thus, to search for any entry with a
+  host name of host* mail list-log._host and to find entries for ``Keith
+  John...'' etc, use list-log.keith_john.
+
+  For SQL-enabled lists, this command searches the ``list_slog'' table.
+
+
+  4\b4.\b.3\b35\b5.\b.  H\bHo\bow\bw t\bte\bex\bxt\bt f\bfi\bil\ble\be e\bed\bdi\bit\bti\bin\bng\bg w\bwo\bor\brk\bks\bs.\b.
+
+  If a list is set up with the ezmlm-make(1) ``-n'' switch, or if the
+  ``-e'' switch is added to the ezmlm-manage(1) line in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br,
+  ezmlm allows remote administrators to edit the text files that make up
+  most of the ezmlm responses.  Of course, this will work only if remote
+  administration is enabled for the list. Replies are sent only if the
+  target address is a remote administrator.  Thus, ezmlm does not rely
+  on SENDER (easily forged) but on the notion that only the recipient
+  receives the message.  This is a reasonable assumption for remote
+  administrators that receive mail on the local system.
+
+  With this switch, ezmlm replies to the -edit command with a list of
+  the files in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/.  Only files where editing seems reasonable are
+  included in the list. The remote administrator can edit any file in
+  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ by sending e-mail containing the new text to -edit.file
+  where ``file'' is the name of the file replaced (edited). The file
+  must exist and the name consist of only lower case letters and '-'.
+  Any '-' (hyphen) must be substituted by a '_' (underscore). For remote
+  administrator convenience, the substitution has been made in the list
+  of files sent in reply to the -edit command.
+
+  In reply to this command, ezmlm sends a message with the file and
+  editing instructions. A ``cookie'' based on the date, file name, and
+  contents of the file is added to the ``Reply-To:'' address. The cookie
+  becomes invalid as soon as the file has been changed, or after 27
+  hours, whichever is shorter.  Also, the cookie cannot be used to edit
+  any other file, even if the other file has exactly the same contents.
+  If you sent an edit request, and decide not to edit the file, you can
+  simply delete the message.
+
+  To apply standard changes to all your text files it is easier to edit
+  ~\b~/\b/.\b.e\bez\bzm\bml\blm\bmr\brc\bc. To reset the list's text files back to their default
+  contents (as specified by e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b)), use the ezmlm-make(1) ``-ee''
+  switch together with any other switches used to set up the list, or
+  the ``-++'' switch and any switches that you whish to change from the
+  current configuration.
+
+
+  4\b4.\b.3\b36\b6.\b.  H\bHo\bow\bw s\bsu\bub\bbj\bje\bec\bct\bt l\bli\bin\bne\be p\bpr\bre\bef\bfi\bix\bxe\bes\bs w\bwo\bor\brk\bk.\b.
+
+  First of all, it is against a number of RFCs to modify the
+  ``Subject:'' header of messages. However, it is frequently requested
+  by users who have seen it on other list managers. Second, it is many
+  times worse to have a prefix that changes from message to message,
+  such as a prefix with the message number.  However, a number of lists,
+  especially in Japan, use this feature and in its absence these lists
+  might be unable to take advantage of ezmlm. Thus, while we recommend
+  against using a prefix, ezmlm-idx supports it.
+
+  To add a subject prefix, just put the text into D\bDI\bIR\bR/\b/p\bpr\bre\bef\bfi\bix\bx. The only
+  format that makes any sense is ``list:'' or ``(list)'' or such.
+
+  The message number prefix is activated by putting e.g. ``(list-#)''
+  into D\bDI\bIR\bR/\b/p\bpr\bre\bef\bfi\bix\bx. ``#'' is replaced by the message number. ezmlm
+  refuses to make more drastic changes in the subject of a message. As a
+  consequence, the message number prefix is added only when the subject
+  does not already contain a prefix. Thus, replies will have the message
+  number of the original message. Doing anything else and still
+  supporting rfc2047-encoded subjects in the archive threading (much
+  more important) would require decoding the subject, removing/editing
+  the prefix, and re-encoding the subject. This is far too invasive.
+
+  The entire thread can always be retrieved by sending a message to
+  list-thread-x where ``x'' is the message number in the prefix of any
+  message in the thread.
+
+
+  4\b4.\b.3\b37\b7.\b.  H\bHo\bow\bw b\bbo\bou\bun\bnc\bce\bes\bs a\bar\bre\be h\bha\ban\bnd\bdl\ble\bed\bd.\b.
+
+  Ezmlm messages are sent with an envelope sender (``Return-Path'') that
+  directs bounces to D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\ber\br and also via ``VERP'' contain
+  information about the intended recipient. Thus, programs run from
+  D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\ber\br know the subscriber for whom the message bounced. ezmlm-
+  weed(1) is used to weed out delivery delay notification and other
+  junk.  For others ezmlm-return(1) decides if the address is a
+  subscriber.  If so, it saves the first bounce message and a list of
+  bounced-message numbers. ezmlm-warn(1) executed from e.g. D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br
+  goes through these bounce files. If it finds any that are older than
+  1,000,000 seconds (about 11.6 days) it sends a warning message to the
+  subscriber. If this warning message bounces, ezmlm-return(1) sets up a
+  "warning flag" for the subscriber. If ezmlm-warn(1) finds a warning
+  flag older than 11.6 days, it sends a "probe" to the subscriber.  If
+  ezmlm-return(1) receives a bounced probe, the subscriber is
+  automatically unsubscribed.
+
+  The ezmlm-warn(1) ``-t'' switch can be used to change the time-out (in
+  days).  The ezmlm-warn(1) ``-d'' switch causes processing of ``list-
+  digest'' bounces rather than ``list'' bounces. ezmlm-weed(1) and
+  ezmlm-return(1) can handle bounces for either list.
+
+  ezmlm-warn(1) also removes any files in the bounce directory that are
+  older than 3 times the bounce time-out.
+
+  ezmlm-warn(1) is normally run from D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br. This can take quite a
+  lot of resources, if there are a large number of bouncing addresses
+  (>>1000) on a busy list, since by default all bounces are stored in a
+  single directory and ezmlm-warn(1) examines all of them with each
+  invocation.  ezmlm-idx->=0.32 changes bounce handling to improve
+  performance for large lists. Bounces are stored in subdirectories of
+  D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\be/\b/d\bd/\b/, one per 10,000 seconds. The corresponding address
+  hashes are stored in 16 subdirectories of D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\be/\b/h\bh/\b/. Instead of
+  looking at all bounces, ezmlm-warn(1) processes only the bounces in
+  D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\be/\b/d\bd/\b/ subdirectories that are ``due''. In addition, ezmlm-
+  warn(1) uses D\bDI\bIR\bR/\b/b\bbo\bou\bun\bnc\bce\be/\b/l\bla\bas\bst\btd\bd as a simple lockout, to assure that it
+  will do work only at most once every 5.5 hours. (Times are scaled to
+  the ezmlm-warn(1) ``-t'' argument if used.)  Together, these changes
+  assure that bounce handling will scale well in the default
+  configuration, even for very large lists.
+
+
+  4\b4.\b.3\b38\b8.\b.  H\bHo\bow\bw t\bth\bhe\be i\bin\bnf\bfo\bo a\ban\bnd\bd f\bfa\baq\bq c\bco\bom\bmm\bma\ban\bnd\bds\bs w\bwo\bor\brk\bk.\b.
+
+  The _\b-_\bi_\bn_\bf_\bo and _\b-_\bf_\ba_\bq commands simply reply with the contents of the
+  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/i\bin\bnf\bfo\bo and D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/f\bfa\baq\bq files. Edit these files directly or
+  remotely (see ``How to remotely edit dir/text files'').  The
+  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/i\bin\bnf\bfo\bo file should start with a single line that is meaningful
+  as is and describes the list. This will be used in later versions to
+  allow automatic assembly of the global ``list-of-lists'' (see ``How to
+  set up a global list address like majordomo@host or listserv@host'').
+
+
+  4\b4.\b.3\b39\b9.\b.  H\bHo\bow\bw t\bth\bhe\be g\bgl\blo\bob\bba\bal\bl e\bez\bzm\bml\blm\bm l\bli\bis\bst\bt a\bad\bdd\bdr\bre\bes\bss\bs w\bwo\bor\brk\bks\bs.\b.
+
+  Sometimes, it is desirable to have a host- or user-wide address that
+  can list available mailing lists.
+
+  ezmlm-request(1) can be used to set up a global address, such as
+  ezmlm@host which allows the user to see and interact with a number of
+  different mailing lists. This is especially useful when your users are
+  used to other mailing list managers, such as ``majordomo'' or
+  ``listproc''. ezmlm-request(1) is set up to answer requests to the
+  address (see ``How to set up a global list address like majordomo@host
+  or listserv@host'').  There, it interprets the first line of the
+  message body as a command. It will reply directly to ``lists'' and
+  ``which'' commands. All other commands will be used to construct
+  messages to the respective lists. Where other mailing list managers
+  use synonyms of ezmlm commands, ezmlm-request(1) recognizes these and
+  translates them to the corresponding ezmlm commands.  ezmlm-request(1)
+  will build commands also of unrecognized commands. Thus, if you create
+  new commands for a list, ezmlm-request(1) will automatically support
+  them.
+
+  If the user does not specify the complete list address, ezmlm-
+  request(1) will attempt to complete the name. See the ezmlm-reject(1)
+  man page for more info.
+
+
+  4\b4.\b.4\b40\b0.\b.  H\bHo\bow\bw e\bez\bzm\bml\blm\bm-\b-c\bcr\bro\bon\bn w\bwo\bor\brk\bks\bs.\b.
+
+  If you are a user and have crond(8) access, if you do not need to get
+  digests at specific times, or if you are a system administrator
+  setting up lists, there is no reason for you to use ezmlm-cron(1). If
+  you are a system administrator not allowing users crond(8) access or a
+  user that needs digests at specific times, but without crond(8)
+  access, read on.
+
+  ezmlm-cron(1) is a very restrictive interface to crond(8).  ezmlm-
+  cron(1) can be used to create digest trigger messages. If a list is
+  set up with a digest code (see ezmlm-make(1) and ezmlm-get(1)) ezmlm
+  will generate a digest from the list joe-sos@host sent to to
+  subscribers of joe-sos-digest@dighost when receiving a message to joe-
+  sos-dig-code@host where ``code'' is the digest code. ezmlm-cron(1) can
+  be used to generate such messages at regular intervals.  The file
+  e\bez\bzc\bcr\bro\bon\bnr\brc\bc is set up by the sysadmin and controls what trigger messages
+  specific users may set up via ezmlm-cron(1).
+
+  Usually, the ezcronrc of that use will have an entry like
+  ``user:user-:host:10'' allowing ``user'' to create trigger messages
+  for up to 10 lists with names starting with ``user-'' and on the host
+  ``host''.
+
+  To list the ezcronrc line controlling your use of ezmlm-cron(1):
+
+
+               % ezmlm-cron -c
+
+
+
+
+  To list all entries that you've created:
+
+
+               % ezmlm-cron -l
+
+
+
+
+  To add an entry to trigger digests from list@host every morning at
+  0230:
+
+
+               % ezmlm-cron -t 02:30 -i24 list@host code
+
+
+
+
+  A new entry for the same list overwrites an old entry.
+
+  To delete the entry above:
+
+
+               % ezmlm-cron -d list@host
+
+
+
+
+  or use ezmlm-cron to trigger messages at a different time:
+
+
+               % ezmlm-cron -t 16:16 -i24 list@host code
+
+
+
+  4\b4.\b.4\b41\b1.\b.  H\bHo\bow\bw e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be w\bwo\bor\brk\bks\bs.\b.
+
+  ezmlm lists allow almost infinite customization. The component build,
+  together with the qmail delivery mechanism makes it possible to create
+  any variant of list function imaginable. However, this complexity
+  makes it somewhat daunting to the average user wanting to set up a
+  mailing list. ezmlm-make(1) allows automated list setup, while
+  permitting a large amount of configurability.
+
+  At first glance, ezmlm-make(1) has many complicated options. However,
+  these can be applied iteratively through the ezmlm-make(1) edit
+  mechanism. Also, they are intended to be relatively complete so that
+  execution of ezmlm-make(1) by e.g. a GUI can be used to safely set up
+  and edit any list.
+
+  ezmlm-make(1) reads its command line arguments and switches, then
+  creates the list directory. If the ``-e'' edit or ``-+'' sticky edit
+  switches are not specified, ezmlm-make(1) will fail if the directory
+  already exists. The directory argument must be an absolute path
+  starting with a slash. The dot-qmail file argument, if specified, must
+  also be absolute.
+
+  ezmlm-make(1) next reads ezmlmrc(5) located in the /\b/e\bet\btc\bc/\b/ directory
+  with a default install. If not found, the file in the ezmlm binary
+  directory will be used. The second ezmlm-make command line argument
+  specify the root name of the .qmail files. If the ezmlm-make(1) ``-c''
+  switch is used, ezmlm-make(1) will look in that directory for a
+  .\b.e\bez\bzm\bml\blm\bmr\brc\bc file and use it instead. If this file does not exist, ezmlm-
+  make(1) will print a warning and use the previously discussed
+  ezmlmrc(5) files in the same order.  You can also use ``-C
+  _\be_\bz_\bm_\bl_\bm_\br_\bc_\b._\ba_\bl_\bt'' to use _\be_\bz_\bm_\bl_\bm_\br_\bc_\b._\ba_\bl_\bt as the ezmlmrc(5) file. Again, ezmlm-
+  make(1) will fall back to the others with a warning, if the specified
+  ezmlmrc(5) file is not found.
+
+  When not run in ``-e edit'' or ``-+'' sticky edit modes, ezmlm-make(1)
+  first creates the list directory.  It also as the last step of its
+  action creates D\bDI\bIR\bR/\b/k\bke\bey\by containing the key used for cookie generation.
+
+  The ezmlmrc(5) file consists of a number of file names relative to the
+  list directory, followed by conditional flags (see ezmlm-make(1) and
+  ezmlmrc(5) for details). If all the conditional flags (controlled by
+  the corresponding command line switches) are true, the lines that
+  follow are entered into the named file. There are also tags to erase
+  files.  Tags in the format <#X#> (where ``X'' is any number, except
+  ``1'' and ``2'') are replaced by the corresponding ezmlm-make(1)
+  switch argument. The ezmlm-make(1) command line arguments and the
+  ezmlm binary path can be similarly substituted into the text. Thus,
+  ezmlmrc(5) controls (within reason) the entire operation of ezmlm-
+  make(1). ezmlmrc(5) is also set up so that no messages or file
+  containing list state information are lost. Therefore, ezmlm-make(1)
+  can be used to safely edit existing lists. The only caveat is that the
+  list state is undefined while editing is in progress. Thus, it is
+  advisable to prevent mail delivery by setting the ``sticky'' bit on
+  the user's home directory while editing lists.
+
+  ezmlm-make(1) will create the file D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg. This files saves all
+  the flags that were set at the last execution of ezmlm-make, as well
+  as all the switch and command line arguments. When editing a list,
+  only ``DIR'' and the non-default letter switches need to be specified.
+  Other command line arguments and the ``digit switch'' arguments are
+  read from D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg.  To remove a digit switch, simply use it with
+  two single quotes as the argument.
+
+  You can also easily determine how a list was set up by looking at
+  D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg.
+
+  _\bN_\bo_\bt_\be_\b: D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ files will be created but not overwritten when using
+  the ``-e'' or ``-+'' edit switches. This is to preserve manual
+  customizations. To overwrite these and reset the files to the content
+  specified by e\bez\bzm\bml\blm\bmr\brc\bc, use ``-ee'' or ``-++''.
+
+  _\bN_\bo_\bt_\be_\b: As of ezmlm-idx-0.40 the ezmlm-make(1) ``-c'' and ``-C file''
+  switches are sticky when using ``-+'' or ``-++'', so you do not need
+  to specify them. This feature is disabled if ezmlm-make(1) is run as
+  root.
+
+
+  4\b4.\b.4\b42\b2.\b.  W\bWh\bha\bat\bt n\bna\bam\bme\bes\bs c\bca\ban\bn I\bI u\bus\bse\be f\bfo\bor\br m\bmy\by l\bli\bis\bst\bts\bs?\b?
+
+  Rather than restrict you to a single E-mail address (user@host), qmail
+  in the default setup gives you control over an infinite number of
+  addresses user-*@host. Of course, you (normally) have no way of
+  controlling elsewhere@host since that could lead to overlap between
+  users' ``e-mail address space''. As a consequence, all you mailing
+  lists have to be named user-xx@host where ``user'' is your user name
+  and ``xx'' is anything. You cannot create e.g. mylist@host, only user-
+  mylist@host. To create the list user-list@host do:
+
+
+               % ezmlm-make ~/list ~/.qmail-list user-list host
+
+
+
+
+  Notice that ``user'' is n\bno\bot\bt part of the .\b.q\bqm\bma\bai\bil\bl file name.
+
+  There are two way to create lists with names not starting with your
+  user name: First, qmail can be set up so that you control a virtual
+  domain (see below).  Second, the system administrator can set up lists
+  with arbitrary names within the ~\b~a\bal\bli\bia\bas\bs/\b/ directory.
+
+
+  4\b4.\b.4\b43\b3.\b.  L\bLi\bis\bst\bts\bs i\bin\bn v\bvi\bir\brt\btu\bua\bal\bl d\bdo\bom\bma\bai\bin\bns\bs
+
+  If you use qmail>=1.02 and ezmlm-idx>=0.32, lists under virtual
+  domains work just like other lists and require no adjustments. You can
+  choose any local name for the list and the ezmlm-make(1) argument
+  ``local'' is that name; ``host'' is the name of the virtual domain.
+
+
+  4\b4.\b.4\b44\b4.\b.  H\bHo\bow\bw d\bdo\bo I\bI m\bma\bak\bke\be c\bcu\bus\bst\bto\bom\bmi\biz\bza\bat\bti\bio\bon\bn s\bsi\bim\bmp\bpl\ble\be f\bfo\bor\br m\bme\be/\b/m\bmy\by u\bus\bse\ber\brs\bs?\b?
+
+  All non-default switches, ezmlm-issubn(1) setups, etc, can be made
+  standard for new lists by customizing the ezmlm-make(1) configuration
+  file named ``e\bez\bzm\bml\blm\bmr\brc\bc''.  A default e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) is installed in the
+  ezmlm binary directory. If installed, a system-wide customized ezmlmrc
+  file in /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bmr\brc\bc (or symlinked from there) overrides this.
+  Installing a ~\b~/\b/.\b.e\bez\bzm\bml\blm\bmr\brc\bc file in a user d\bdo\bot\btd\bdi\bir\br and using the ezmlm-
+  make(1) ``-c'' switch allows further per user customization (see
+  ``Customizing ezmlm-make operation'').
+
+
+  5\b5.\b.  e\bez\bzm\bml\blm\bm s\bsu\bup\bpp\bpo\bor\brt\bt f\bfo\bor\br S\bSQ\bQL\bL d\bda\bat\bta\bab\bba\bas\bse\bes\bs.\b.
+
+
+  5\b5.\b.1\b1.\b.  W\bWh\bhy\by u\bus\bse\be a\ban\bn S\bSQ\bQL\bL d\bda\bat\bta\bab\bba\bas\bse\be w\bwi\bit\bth\bh e\bez\bzm\bml\blm\bm?\b?
+
+  The main advantages are that you are using an address database system
+  that can easily be accessed from any number of other programs via
+  ODBC, perl, java, PHP, ... You can easily hook up ezmlm with your
+  customer database, etc.  ezmlm programs compiled with SQL support (and
+  when available also those compiled with support for other SQL servers)
+  are entirely backwards compatible. You can mix SQL dbs with normal
+  ezmlm dbs, and convert lists between them.
+
+
+  5\b5.\b.2\b2.\b.  W\bWh\bhy\by n\bno\bot\bt t\bto\bo u\bus\bse\be a\ban\bn S\bSQ\bQL\bL d\bda\bat\bta\bab\bba\bas\bse\be w\bwi\bit\bth\bh e\bez\bzm\bml\blm\bm.\b.
+
+  The main disadvantages of the SQL version are that you need to be
+  familiar with the SQL server, the binaries are quite a bit larger, and
+  you are trusting your addresses to a large database program, rather
+  than a small and easily audited set of ezmlm programs. Also, the SQL
+  server becomes a single point of failure.
+
+  Ezmlm with SQL support continues to rely on qmail stability. If
+  connection fails, ezmlm aborts with a temporary error causing
+  redelivery at a later time point.
+
+
+  5\b5.\b.3\b3.\b.  T\bTa\bab\bbl\ble\bes\bs u\bus\bse\bed\bd f\bfo\bor\br (\b(M\bMy\by)\b)S\bSQ\bQL\bL s\bsu\bup\bpp\bpo\bor\brt\bt.\b.
+
+  The basic philosophy is that the database can be on any host (if you
+  use SENDER restrictions, connectivity to the main host is more
+  important than to the sublists), and you choose the database and
+  ``table root'' names. The default database is ``ezmlm'' and the
+  default table root is ``list''. Each list has a separate table root.
+  Any number of lists can share a database.
+
+  The main list address table is named with the table root only, others
+  have that name with various suffixes. In the following ``list'' is
+  used as the table root.
+
+
+  5\b5.\b.3\b3.\b.1\b1.\b.  A\bAd\bdd\bdr\bre\bes\bss\bs t\bta\bab\bbl\ble\bes\bs.\b.
+
+
+     l\bli\bis\bst\bt
+        List subscriber addresses.
+
+     l\bli\bis\bst\bt_\b_d\bdi\big\bge\bes\bst\bt
+        Digest list subscriber addresses.
+
+     l\bli\bis\bst\bt_\b_a\bal\bll\blo\bow\bw
+        List subscriber alias addresses. Used only if SENDER
+        restrictions are used for the list. This is configured in the
+        default SQL list setup, but a local (ezmlm-style non-SQL)
+        database could also be used.
+
+     l\bli\bis\bst\bt_\b_d\bde\ben\bny\by
+        List deny addresses. This table is created, but the default
+        configuration, if it uses the ``deny'' addresses at all, will do
+        so with a local database.
+
+     l\bli\bis\bst\bt_\b_m\bmo\bod\bd
+        Moderator addresses. Created for completeness, but not used in
+        the default configuration. If moderators are used, the addresses
+        are stored in a local database.
+
+
+  5\b5.\b.3\b3.\b.2\b2.\b.  S\bSu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br l\blo\bog\bg t\bta\bab\bbl\ble\bes\bs.\b.
+
+  For each of the above tables, there is a ``*_slog'' table that
+  contains one row per transaction against the corresponding address
+  table. The entries contain a time stamp, the subscription address; a
+  direction indicator (``-'' for removals, ``+'' for additions); a type
+  indicator (blank for ezmlm-manage, ``m'' for ``manual'', ``p'' for
+  ``probe, i.e. bounce handling; and the subscriber ``From:'' line
+  contents (only additions and only when made by ezmlm-manage or by
+  ``ezmlm-sub(1) -n'').
+
+
+  5\b5.\b.3\b3.\b.3\b3.\b.  M\bMe\bes\bss\bsa\bag\bge\be l\blo\bog\bgg\bgi\bin\bng\bg t\bta\bab\bbl\ble\bes\bs.\b.
+
+  For both the list and the digest list, there are a pair of tables that
+  log messages:
+
+
+     l\bli\bis\bst\bt_\b_c\bco\boo\bok\bki\bie\be
+        The main list stores the message number and a pseudo-random
+        cookie in this table when it processes the message. The cookie
+        is derived from the secret D\bDI\bIR\bR/\b/k\bke\bey\by, the message sender and the
+        message number. Thus, it is non-repeating and virtually
+        impossible to guess beforehand. Sublists will check that the
+        cookie sent with the message is the same as the one received
+        with the message.
+
+        The digest list is created similarly, except that it is ezmlm-
+        get(1) that originates the message and creates the cookie.  This
+        is done in ``list_digest_cookie''.
+
+
+     l\bli\bis\bst\bt_\b_m\bml\blo\bog\bg
+        Both the main list and the sublists make entries in this table.
+        Each entry consists of a time stamp, a message number, a list
+        number, and a code. The code is 0 for message arrival, 1 for
+        ``finished processing'', 2 for ``receipt received'' and -1 for
+        bounce. The lists will refuse to process messages that do not
+        have the correct cookie, or if the message already has an entry
+        with a code of greater than 0. To inject a message at the
+        sublist, an attacker would have to inject a message with the
+        correct code before the list has processed the ``real'' message,
+        or subvert the SQL server. In practice, this is very hard to do,
+        unless the attacker has broken security at the database server
+        or a sublist. This authentication mechanism is intended to make
+        it safe to sublist moderated lists. It also blocks any message
+        duplication between main list and sublist from being propagated
+        to the subscribers.
+
+        The codes 2 for ``receipt received'' and -1 for bounce are
+        entered by ezmlm-receipt(1) at the main list. This program is
+        configured instead of ezmlm-return(1) if the main list was set
+        up with ``ezmlm-make -w6''.  ezmlm-receipt(1) checks the cookie
+        of messages addresses to mainlocal-return-receipt@mainhost and
+        if correct enters the ``receipt received'' code. This address is
+        normally in the subscriber database with a hash of 98, so that
+        each list sends a message to the address _\ba_\bf_\bt_\be_\br all subscriber
+        addresses.
+
+        Bounces of sublist messages should not lead to removal of the
+        sublist from the database. ezmlm-receipt(1) will instead log the
+        bounce to the ``list_mlog'' table. It will also store up to 50
+        bounces in the bounce directory. This helps error detection and
+        diagnosis. After the first 50 bounces, no more bounces are
+        stored, until you manually remove the old ones. This is to
+        prevent filling up your hard disk in case a configuration error
+        causes a deluge of bounces.
+
+        The digest list is treated in the same manner. Here, the tables
+        is ``list_digest_mlog'' and the feedback address is mainlocal-
+        digest-return-receipt@mainhost.
+
+
+
+
+  5\b5.\b.4\b4.\b.  H\bHo\bow\bw t\bto\bo s\bse\bet\bt u\bup\bp a\ba s\bsi\bim\bmp\bpl\ble\be l\bli\bis\bst\bt w\bwi\bit\bth\bh S\bSQ\bQL\bL s\bsu\bup\bpp\bpo\bor\brt\bt.\b.
+
+  To use SQL database support, you have to compile the programs with SQL
+  support. Currently, only MySQL support is available. See I\bIN\bNS\bST\bTA\bAL\bLL\bL.\b.i\bid\bdx\bx
+  in the package on how to do this.
+
+  The programs with SQL support will work exactly like the normal
+  programs for standard lists. However, if the file s\bsq\bql\bl exists in the
+  basedir, it turns on the SQL mode and it is expected to contain SQL
+  server connect info in the format
+
+       ``host:port:user:password:database:table''
+
+
+  Here, ``Host'' is the SQL database server host, ``port'' can be left
+  blank to use the default port, ``user'' and  ``password'' are connec-
+  tion credentials for a user you need to define and grant access to the
+  database. ``Table'' is the name of the address table (``list'' in the
+  examples above and ``list_digest'' for the corresponding digest list).
+  For list clusters, ``:sublist'' is suffixed to this info and it is the
+  name/address of the sublist.
+
+  For each address database, you also need to create the address table
+  as well as the ``*_slog'' subscription log table. In addition, you
+  should create a ``*_cookie'' and ``*_mlog'' table for message logging.
+  This is all it takes to start using an SQL database.
+
+
+  5\b5.\b.4\b4.\b.1\b1.\b.  H\bHe\bel\blp\bpe\ber\br p\bpr\bro\bog\bgr\bra\bam\bms\bs f\bfo\bor\br S\bSQ\bQL\bL-\b-e\ben\bna\bab\bbl\ble\bed\bd l\bli\bis\bst\bts\bs.\b.
+
+  Two programs are supplied in the distribution to make it easier to
+  create the database user and tables. Also, ezmlm-make(1) has support
+  for setting up SQL-enabled lists.
+
+
+     C\bCr\bre\bea\bat\bti\bin\bng\bg t\bth\bhe\be t\bta\bab\bbl\ble\bes\bs
+        ezmlm-mktab(1) will create the necessary tables:
+
+
+                  % ezmlm-mktab -d table
+
+
+
+
+     Pipe this into the SQL client with the appropriate administrator
+     credentials needed to create tables (see MySQL documentation, e.g.
+     <http://www.tcx.se/>).
+
+     For most lists, the only addresses that are stored in the SQL
+     database are the subscribers of list and digest, and the ``allow''
+     aliases. It is NOT normally advisable to store moderator addresses
+     there, since they are needed only at the main list and secrecy is
+     more important. ``Deny'' addresses are few and again only needed at
+     the main list. ``Allow'' are put in the SQL database when using the
+     default ezmlmrc file only to make all relevant addresses
+     manipulatable via the SQL server. The other tables are created, in
+     case they are  wanted (the cost for having them as empty table is
+     zero). The basedir/sql file is the decision point. If it exists, an
+     SQL table is used; if not a local ezmlm db is used.
+
+
+     C\bCr\bre\bea\bat\bti\bin\bng\bg a\ba u\bus\bse\ber\br e\ben\bnt\btr\bry\by
+        Create a user that has full access to the database from the list
+        host. How to do this depends on the RDBMS.
+
+
+     C\bCr\bre\bea\bat\bti\bin\bng\bg t\bth\bhe\be l\bli\bis\bst\bt
+        ezmlm-make(1) supports SQL-enabled lists with the ``-6'' switch:
+
+
+                  % ezmlm-make other_switches -6 'host:port:user:pw:db:table' \
+                          dir dot local host
+
+
+
+
+     Will create an SQL-enabled list that uses the SQL server for the
+     main list subscribers, digest list subscribers (if configured) and
+     ``allow'' poster alias addresses (if configured).
+
+
+  5\b5.\b.5\b5.\b.  M\bMa\ban\bnu\bua\bal\bll\bly\by m\bma\ban\bni\bip\bpu\bul\bla\bat\bti\bin\bng\bg t\bth\bhe\be s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs o\bof\bf a\ba S\bSQ\bQL\bL-\b-e\ben\bna\bab\bbl\ble\bed\bd l\bli\bis\bst\bt.\b.
+
+  ezmlm-sub(1), ezmlm-unsub(1), and ezmlm-list(1) work as you would
+  expect also with a SQL-enabled list. ezmlm-list(1) may be minimally
+  slower (depending on network speed) if the SQL server is not local.
+  ezmlm-sub(1) and ezmlm-unsub(1) will be faster, but this is noticeable
+  only with very large subscriber lists and addition/removal of large
+  numbers of addresses (more than several thousands).
+
+
+  5\b5.\b.6\b6.\b.  C\bCo\bon\bnv\bve\ber\brt\bti\bin\bng\bg t\bto\bo a\ban\bnd\bd f\bfr\bro\bom\bm a\ban\bnd\bd S\bSQ\bQL\bL d\bda\bat\bta\bab\bba\bas\bse\be.\b.
+
+  Just like other programs, ezmlm-list(1), ezmlm-sub(1), and ezmlm-
+  unsub(1) will work with normal address databases in the absence of
+  D\bDI\bIR\bR/\b/s\bsq\bql\bl.  However, they also have a ``-M'' switch to force this
+  behavior even in the presence of D\bDI\bIR\bR/\b/s\bsq\bql\bl. This is used to convert an
+  address database from the standard type to the SQL type:
+
+
+               % ezmlm-list -M dir | xargs ezmlm-sub dir
+
+
+
+
+  or from the SQL version to the standard type:
+
+
+               % ezmlm-list dir | xargs ezmlm-sub -M dir
+
+
+
+
+  To synchronize the two, remove one and then update it with ezmlm-
+  sub(1) from the other. Alternatively, sort the ezmlm-list(1) output
+  for both, use diff and sed/awk to get separate files of the differ-
+  ences, and use ezmlm-sub(1) and ezmlm-unsub(1) to apply the differ-
+  ences to the appropriate database.
+
+  This type of conversion can serve as a convenient means to convert a
+  list from one type to another, to back up databases, and to move
+  subscriber addresses from a standard list to a SQL table for other
+  purposes, or from a SQL database to a standard mailing list (you may
+  need to use addresses from a SQL table, without wanting your lists to
+  be dependent on an SQL server for day to day operation).
+
+  _\bN_\bo_\bt_\be_\b: This inter-conversion requires the D\bDI\bIR\bR/\b/s\bsq\bql\bl file. If you do not
+  run the list against an SQL server, you need to disable deliveries
+  before you temporarily create this file. Otherwise, the list will run
+  against the SQL database during the time D\bDI\bIR\bR/\b/s\bsq\bql\bl exists.
+
+
+  5\b5.\b.7\b7.\b.  O\bOp\bpt\bti\bim\bmi\biz\bzi\bin\bng\bg M\bMy\byS\bSQ\bQL\bL f\bfo\bor\br e\bez\bzm\bml\blm\bm.\b.
+
+
+  5\b5.\b.7\b7.\b.1\b1.\b.  A\bAd\bdd\bdr\bre\bes\bss\bs S\bSE\bEL\bLE\bEC\bCT\bTs\bs,\b, a\bad\bdd\bdi\bit\bti\bio\bon\bns\bs,\b, r\bre\bem\bmo\bov\bva\bal\bls\bs.\b.
+
+  ezmlm-idx-0.40 simplifies the SQL support and queries over ezmlm-
+  idx-0.32 at the cost of dropping distributed sublist support. We have
+  figured out a simpler way to support the latter, which hopefully will
+  be incorporated into ezmlm in the future (written under contract).
+
+  With the simplification, the queries are very straight forward, and
+  tuning is indicated only under extreme circumstances (very many very
+  large and busy lists or constant addition/removal of many addresses).
+
+
+  5\b5.\b.8\b8.\b.  M\bMa\bai\bin\bnt\bte\ben\bna\ban\bnc\bce\be o\bof\bf t\bth\bhe\be M\bMy\byS\bSQ\bQL\bL d\bda\bat\bta\bab\bba\bas\bse\be.\b.
+
+  Weekly to monthly error checks on MySQL tables is recommended. Best is
+  to use:
+
+
+               # isamchk -s -O readbuffer=2M */*.ISM
+
+
+
+
+  Other options allow automatic correction of errors, but are dangerous
+  if tables are accessed while isamchk is running.
+
+  Other isamchk options allow recovery of space after frequent
+  insert/delete of addresses (can also be done with ``OPTIMIZE TABLE''),
+  key optimization, etc.  See the MySQL documentation (
+  <http://www.tcx.se>) for more info.
+
+
+  6\b6.\b.  P\bPo\bos\bss\bsi\bib\bbl\ble\be e\ber\brr\bro\bor\br c\bco\bon\bnd\bdi\bit\bti\bio\bon\bns\bs i\bin\bn e\bez\bzm\bml\blm\bm l\bli\bis\bst\bts\bs.\b.
+
+
+  6\b6.\b.1\b1.\b.  W\bWh\bha\bat\bt d\bdo\bo I\bI d\bdo\bo i\bif\bf e\bez\bzm\bml\blm\bm d\bdo\boe\bes\bsn\bn'\b't\bt w\bwo\bor\brk\bk?\b?
+
+  Try to determine where the problem occurs and how to reproduce it:
+
+  +\bo  Do messages to ezmlm return an error message to the sender or not?
+
+  +\bo  What is/are the error message(s)?
+
+  +\bo  What does ezmlm log into the mail log?
+
+  +\bo  Are you using a setup with virtual domains, and qmail<1.02 or
+     ezmlm-idx<0.31? If so, have you adjusted D\bDI\bIR\bR/\b/i\bin\bnl\blo\boc\bca\bal\bl (see
+     ``Adapting ezmlm-make for virtual domains'')?
+
+  +\bo  Are posts sent out to the subscribers?
+
+  +\bo  Are there subscribers?
+
+
+       %  ezmlm-list DIR
+
+
+
+
+  +\bo  Are there moderators?
+
+
+
+  % ezmlm-list moddir
+
+
+
+
+  where ``moddir'' is the contents of D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be (for remote admin
+  lists), of D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb (for subscription moderated lists) or D\bDI\bIR\bR/\b/m\bmo\bod\bd-\b-
+  p\bpo\bos\bst\bt (for message moderation), if and only if the contents start with
+  a forward slash. The default in all cases is D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/. If both
+  D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb and D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be contain directory names, the one in D\bDI\bIR\bR/\b/m\bmo\bod\bd-\b-
+  s\bsu\bub\bb is used for both subscription moderation and remote admin.
+
+  +\bo  Are the ownerships of all files correct, i.e. read/writable for the
+     owner?
+
+
+       % chown -R user DIR
+
+
+
+
+  For lists under alias:
+
+
+       % chown -R alias DIR
+
+
+
+
+  If you use custom moderator databases, those directories and all their
+  contents must also be readable for the user under which the list oper-
+  ates (i.e. the user qmail changes to during the delivery).
+
+  +\bo  Read the qmail log and capture relevant parts.
+
+  +\bo  Did you customize the package at all? If so, try the default
+     settings which are known to work.
+
+  +\bo  Did you customize e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b)? Try to use the default copy (skip the
+     -c switch).
+
+  +\bo  Did your customization of .\b.e\bez\bzm\bml\blm\bmr\brc\bc fail to have an effect?
+     Remember to use the -c switch. The .\b.e\bez\bzm\bml\blm\bmr\brc\bc file used is the one in
+     ``dotdir'', i.e. the directory where the .\b.q\bqm\bma\bai\bil\bl files go, usually,
+     but NOT necessarily, the one in your home directory.
+
+  +\bo  Make sure you followed the instructions in man pages and other
+     documentation. Most of the problems are due to not closely
+     following the instructions. Try again with a new test list.
+
+  +\bo  Make sure to take notes of how the list was created (which flags
+     you used, etc.).
+
+  +\bo  use ezmlm-check(1) (see ``Using ezmlm-check to find setup
+     errors'').  and compare the variables identified by ezmlm-check to
+     D\bDI\bIR\bR/\b/i\bin\bnl\blo\boc\bca\bal\bl, etc. If you don't get a reply from ezmlm-check, then
+     message was not delivered properly. Check your qmail setup.
+
+  +\bo  Try to find your problem or a question/item close to it in the FAQ.
+
+  +\bo  If this didn't resolve the problem, post to the ezmlm mailing list,
+     describing how you set up the list, your general setup (especially
+     the relevant control files for a virtual domain), what works and
+     what doesn't and what results from different actions (log entries,
+     error messages).
+
+  If you have solved a problem that you believe might be more general,
+  please send a description of the problem and its solution to the
+  authors, ideally as a FAQ item.
+
+
+  6\b6.\b.2\b2.\b.  H\bHo\bow\bw d\bdo\bo I\bI r\bre\bep\bpo\bor\brt\bt e\bez\bzm\bml\blm\bm b\bbu\bug\bgs\bs?\b?
+
+  If you have found a bug in the ezmlm-idx additions, please send a bug
+  report by E-mail to lindberg@id.wustl.edu. Describe the error, your
+  setup, and your system in sufficient detail so that it can be
+  reproduced by third parties. Include relevant sections of mail log,
+  and information about any error messages returned. If you ran into a
+  problem and resolved it on your own, include a fix as a context diff
+  against the distribution.
+
+  If you have found a bug in ezmlm proper (unlikely), please send a
+  similar bug report to djb@cr.yp.to or djb-ezmlm@cr.yp.to. If you're
+  unsure where the bug is, you can start with lindberg@id.wustl.edu.  If
+  you have problems and questions, please refer to the documentation,
+  then to mailing list archives, then E-mail the ezmlm mailing list or
+  the authors.
+
+
+  6\b6.\b.3\b3.\b.  W\bWh\bhe\ber\bre\be d\bdo\bo I\bI s\bse\ben\bnd\bd s\bsu\bug\bgg\bge\bes\bst\bti\bio\bon\bns\bs f\bfo\bor\br e\bez\bzm\bml\blm\bm-\b-i\bid\bdx\bx i\bim\bmp\bpr\bro\bov\bve\bem\bme\ben\bnt\bts\bs?\b?
+
+  E-mail to lindberg@id.wustl.edu, ideally with a context diff.  For
+  ezmlm proper, ezmlm@list.cr.yp.to may be better.
+
+
+  6\b6.\b.4\b4.\b.  U\bUs\bsi\bin\bng\bg e\bez\bzm\bml\blm\bm-\b-t\bte\bes\bst\bt t\bto\bo c\bch\bhe\bec\bck\bk t\bth\bhe\be e\bez\bzm\bml\blm\bm(\b(-\b-i\bid\bdx\bx)\b) p\bpr\bro\bog\bgr\bra\bam\bms\bs.\b.
+
+  ezmlm-test(1) tests the different ezmlm(-idx) programs. It is useful
+  to test your installation. If this program succeeds, it is not likely
+  that you have problems due to platform-specific ezmlm(-idx) bugs. If
+  ezmlm-test(1) fails, this is the place to start. The program is good
+  at finding problems but not that easy to use to determine the cause.
+  Start by finding the place where it fails, recreate the conditions
+  (add ``exit 0'' just before the point of failure and set the
+  environment variables as set by the script), then try to run the
+  command manually. ~\b~/\b/_\b__\b_T\bTS\bST\bTD\bDI\bIR\bR_\b__\b_e\ber\brr\br may contain a relevant error
+  message.  For further help, E-mail lindberg@id.wustl.edu.
+
+
+  6\b6.\b.5\b5.\b.  U\bUs\bsi\bin\bng\bg e\bez\bzm\bml\blm\bm-\b-c\bch\bhe\bec\bck\bk t\bto\bo f\bfi\bin\bnd\bd s\bse\bet\btu\bup\bp e\ber\brr\bro\bor\brs\bs.\b.
+
+  ezmlm-check(1) is included in the ezmlm-idx distribution. ezmlm-
+  check(1) is an evolving shell script which when put into a .\b.q\bqm\bma\bai\bil\bl file
+  of a mailing list will return information about the environment
+  variables passed by qmail to ezmlm as well as the list setup. It also
+  attempts to check for common error conditions, such as HOST and
+  D\bDI\bIR\bR/\b/i\bin\bnh\bho\bos\bst\bt mismatch, missing files, etc. To use ezmlm-check(1), place
+  a line:
+
+
+       |/usr/local/bin/ezmlm/ezmlm-check 'DIR'
+
+
+
+
+  where ``DIR'' is the list directory, as the first line in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br
+  (for mail to list), D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br (for mail to list-subscribe, list-
+  help, etc), D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br (for mail to list-accept, list-reject).
+  ezmlm-check(1) will send its output to SENDER. The rest of the .\b.q\bqm\bma\bai\bil\bl
+  file will be ignored.  If you use a non-standard ezmlm binary direc-
+  tory, change the ezmlm-check(1) path accordingly.
+
+  ezmlm-check(1) in combination with mail logs and ezmlm error messages
+  should make it easy to diagnose setup problems. When done, don't
+  forget to remove the ezmlm-check(1) line. It is not security-proofed
+  against SENDER manipulation and with it in place, the list won't work.
+
+  ezmlm-check(1) does not check all aspects of list generation, but
+  catches all common errors when lists are created with ezmlm-make(1),
+  an many other errors as well. The ezmlm-check(1) reply is also very
+  valuable for support via E-mail.
+
+
+  6\b6.\b.6\b6.\b.  P\bPo\bos\bst\bts\bs a\bar\bre\be r\bre\bej\bje\bec\bct\bte\bed\bd:\b: S\bSo\bor\brr\bry\by,\b, n\bno\bo m\bma\bai\bil\blb\bbo\box\bx h\bhe\ber\bre\be b\bby\by t\bth\bha\bat\bt n\bna\bam\bme\be
+  (\b(#\b#5\b5.\b.1\b1.\b.1\b1)\b).\b.
+
+  qmail tried to deliver the mail, but there is no mailbox with that
+  name.  ezmlm-make(1) was used with incorrect arguments, often in
+  conjunction with a virtual domain setup. If the list is in a virtual
+  domain, the ``host'' argument for ezmlm-make(1) should be the virtual
+  domain, not the real host name.  See ``What names can I use for my
+  mailing lists?''  and ``Lists in virtual domains'' for more info.
+
+  Other possibilities are that your qmail setup is incorrect.  For a
+  virtual domain controlled by user ``virt'', create ~\b~v\bvi\bir\brt\bt/\b/.\b.q\bqm\bma\bai\bil\bl-\b-t\bte\bes\bst\bt
+  containing ``|/bin/echo "It worked"; exit 100''. Now send mail to
+  test@virtual.dom. If delivery works, you should get an error message
+  ``It worked'' back. If you get anything else, you need to adjust your
+  qmail setup. Similarly, for a normal user, create ~\b~u\bus\bse\ber\br/\b/.\b.q\bqm\bma\bai\bil\bl-\b-t\bte\bes\bst\bt
+  and mail user-test@host to test that you control extension addresses.
+  If this fails, contact your system administrator or adjust your qmail
+  setup.
+
+  If these tests worked, but your list still does not, you most likely
+  supplied an incorrect ``dot'' argument for ezmlm-manage(1). It should
+  be ~\b~v\bvi\bir\brt\bt/\b/.\b.q\bqm\bma\bai\bil\bl-\b-t\bte\bes\bst\bt for the list test@virtual.dom and ~\b~u\bus\bse\ber\br/\b/.\b.q\bqm\bma\bai\bil\bl-\b-
+  t\bte\bes\bst\bt for the list user-test@host.
+
+
+  6\b6.\b.7\b7.\b.  P\bPo\bos\bst\bt a\bar\bre\be n\bno\bot\bt s\bse\ben\bnt\bt t\bto\bo s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs.\b.
+
+
+     N\bNo\bon\bn-\b-m\bmo\bod\bde\ber\bra\bat\bte\bed\bd l\bli\bis\bst\bts\bs
+
+        1. Read the qmail log. Is your message delivered to the list?
+           You can also:
+
+
+
+             % cat DIR/num
+
+
+
+
+        2. Send a message to the list.
+
+        3. See if it was received/processed:
+
+
+
+             % cat DIR/num
+
+
+
+
+        If the number was incremented, the message went to the list, and
+        was successfully sent out in the opinion of ezmlm-send(1)
+        (ezmlm-send(1) doesn't mind if there are no subscribers, so
+        check that there really are both moderators and subscribers.
+        These are added with ezmlm-sub(1). You can not just put
+        addresses into a text file!).
+
+
+     M\bMe\bes\bss\bsa\bag\bge\be m\bmo\bod\bde\ber\bra\bat\bte\bed\bd l\bli\bis\bst\bts\bs
+
+        1. Check number of queued messages awaiting moderation:
+
+
+
+             % ls -l DIR/mod/pending
+
+
+
+
+        2. Send a message to the list.
+
+        3. Check if another message was added to the queue:
+
+
+
+             % ls -l DIR/mod/pending
+
+
+
+
+        A new file should have appeared. If this file has the owner exe-
+        cute bit set, it was successfully processed by ezmlm-store(1).
+        If this is true, but no moderation request was sent, then con-
+        tinue with ``Messages posted to the list do not result in moder-
+        ation requests''. If there is no new file, the message did not
+        reach ezmlm-store(1), or ezmlm-store(1) failed early. In both
+        cases, the mail log should tell you more.
+
+        If the message is there, but the owner execute bit is not set,
+        ezmlm-store(1) failed.  Check the mail log. Possible reasons
+        include a failure to find the ezmlm-send(1) binary or D\bDI\bIR\bR/\b/m\bms\bsg\bg-\b-
+        s\bsi\biz\bze\be is specified and the message body size is outside of the
+        allowed range (again, this is accompanied by an error message
+        and mail log entry).
+
+
+     G\bGe\ben\bne\ber\bra\bal\bl
+
+        1. If the message was not received/processed, there should be an
+           error message in the mail log.
+
+        2. Fix temporary and permanent errors with the help of qmail and
+           ezmlm documentation.
+
+        3. If there is no log entry at all, then the mail went to
+           another host. Check your qmail setup.
+
+        4. If mail was delivered to the list, but not forwarded to the
+           subscribers (check the qmail log - there should be an entry
+           for a new delivery to the list), t\bth\bhe\be m\bmo\bos\bst\bt c\bco\bom\bmm\bmo\bon\bn e\ber\brr\bro\bor\br i\bis\bs
+           t\bth\bha\bat\bt t\bth\bhe\ber\bre\be a\bar\bre\be n\bno\bo s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs.\b.  In this case, ezmlm-send(1)
+           sends a message from list-help@host, and logs success, but no
+           recipients are logged. To qmail, it is perfectly acceptable
+           to send a message without recipients, so no error message is
+           logged.
+
+        5. Check subscribers:
+
+
+                % ezmlm-list DIR
+
+
+
+
+        6. Assure that ownerships are correct on the list directories:
+
+
+                     % chown -R user DIR
+
+
+
+
+        For lists owned by the ``alias'' user (in ~alias):
+
+
+                     % chown -R alias DIR
+
+
+
+
+        7. Most other problems should be easily corrected with the help
+           of the qmail log.
+
+
+  6\b6.\b.8\b8.\b.  e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be f\bfa\bai\bil\bls\bs:\b: u\bus\bsa\bag\bge\be:\b: e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be .\b..\b..\b.
+
+  The command line you specified is incomplete. Usually, a command line
+  argument has been omitted or a switch was placed after the other
+  arguments rather than before.
+
+  The same error is issued when you attempt to invoke ezmlm-make(1) with
+  only the ``DIR'' argument without using the ``-e'' or ``-+'' switch.
+  Other command line arguments can be omitted only when editing lists
+  created or previously edited with ezmlm-make from ezmlm-idx>=0.23.
+
+  Some special situations use ezmlm-make(1) as a general script
+  processor, e.g.  the setting up of sublists with ezmlmsubrc(5) and of
+  a global interface with ezmlmglrc(5). Here, there is no ``memory'' so
+  all arguments have to be specified, even when using the ``-e'' or
+  ``-+'' switches.
+
+
+  6\b6.\b.9\b9.\b.  e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be f\bfa\bai\bil\bls\bs:\b: U\bUn\bna\bab\bbl\ble\be t\bto\bo c\bcr\bre\bea\bat\bte\be .\b..\b..\b.
+
+  This error occurs when ezmlm-make is used to set up a list, and it
+  tries to create a directory or a .\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt link that already exists.
+  Usually, this occurs because the list already exists. If you are
+  creating a new list, first erase remnants of any old test lists by
+  deleting the list directory and the link files: _\bN_\bO_\bT_\bE_\b: _\bD_\bO _\bN_\bO_\bT _\bU_\bS_\bE _\bT_\bH_\bE_\bS_\bE
+  _\bC_\bO_\bM_\bM_\bA_\bN_\bD_\bS _\bW_\bI_\bT_\bH_\bO_\bU_\bT _\bU_\bN_\bD_\bE_\bR_\bS_\bT_\bA_\bN_\bD_\bI_\bN_\bG _\bT_\bH_\bE_\bM_\b.  You may erase more than you
+  intended!
+
+
+
+       % rm -rf DIR
+       % rm -rf ~/.qmail-list ~/.qmail-list-*
+
+
+
+
+  If you want to save some files (such as in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/), make backup
+  copies first, run ezmlm-make, then copy the backups to D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/. Of
+  course, it is usually easier to create a custom .\b.e\bez\bzm\bml\blm\bmr\brc\bc, and than use
+  that for all your lists.
+
+  To use ezmlm-make(1) to modify an existing list, without changing the
+  subscriber or moderator lists or the message archive, use the ezmlm-
+  make ``-e'' switch. With this, you need to re-specify all desired
+  switches. If instead you use ``-+'' you need to specify only switches
+  that are changed/new.  NOTE: any customization that you've made to
+  program files like D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br will be overwritten. For instance, if
+  you manually added checks to D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br or added a pointer to a custom
+  moderator database in e.g.  D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb these changes will be lost.  To
+  retain such changes (especially ones that are common for several of
+  your lists), place them in a local ~\b~/\b/.\b.e\bez\bzm\bml\blm\bmr\brc\bc file instead. You can
+  either make such changes the default for your lists, or you can
+  configure ~\b~/\b/.\b.e\bez\bzm\bml\blm\bmr\brc\bc so that they are added only if a specific ezmlm-
+  make switch is used.  (see ``Customizing ezmlm-make operation'').
+
+
+  6\b6.\b.1\b10\b0.\b.  e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be f\bfa\bai\bil\bls\bs:\b: .\b..\b..\b. e\bez\bzm\bml\blm\bmr\brc\bc d\bdo\boe\bes\bs n\bno\bot\bt e\bex\bxi\bis\bst\bt
+
+  There is no readable ezmlmrc(5) file in /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bm nor in the ezmlm
+  binary directory. If you have .\b.e\bez\bzm\bml\blm\bmr\brc\bc in ``dotdir'' (see
+  ``Terminology: dotdir'') use the ezmlm-make(1) ``-c'' switch (see
+  ``Customizing ezmlm-make operation'').  _\bN_\bo_\bt_\be_\b: The default location for
+  a global edited e\bez\bzm\bml\blm\bmr\brc\bc file is /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bm/\b/e\bez\bzm\bml\blm\bmr\brc\bc as of ezmlm-
+  idx-0.40.
+
+
+  6\b6.\b.1\b11\b1.\b.  I\bIn\bnd\bde\bex\bx/\b/g\bge\bet\bt/\b/t\bth\bhr\bre\bea\bad\bd r\bre\beq\bqu\bue\bes\bst\bts\bs f\bfa\bai\bil\bl q\bqu\bui\bie\bet\btl\bly\by o\bor\br w\bwi\bit\bth\bh e\ber\brr\bro\bor\brs\bs f\bfr\bro\bom\bm
+  e\bez\bzm\bml\blm\bm-\b-m\bma\ban\bna\bag\bge\be.\b.
+
+  Make sure this is an indexed list and has an ``ezmlm-get'' line first
+  in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. If not, your commands are fed directly to ezmlm-
+  manage(1). If they contain ``-'', ezmlm-manage interprets the rest as
+  an address to which it sends the error message.  Usually, this results
+  in a "trash address" mail log entry and a bounce, which is why you
+  don't see any error message. The same happens if you send non-existing
+  commands followed by ``-'' and arguments. Thus, list-gugu-54@host
+  results in an ezmlm-manage error, resulting in help text being sent to
+  54@localhost ... When testing, try using syntax with a ``.'', not a
+  ``-'', after the action command, e.g. list-get.54_60@host. This will
+  assure that error messages get back to you.
+
+
+  6\b6.\b.1\b12\b2.\b.  D\bDi\big\bge\bes\bst\bt t\btr\bri\big\bgg\bge\ber\bri\bin\bng\bg r\bre\beq\bqu\bue\bes\bst\bts\bs f\bfa\bai\bil\bl.\b.
+
+  (Digest triggering by mail is a relic from older versions. Use the
+  standard setup with ezmlm-tstdig(1) as by ezmlm-make(1) ``-d'', or run
+  ezmlm-get(1) directly from the command line via crond(8).)
+
+  If you get an error message, it tells you why the request failed. If
+  you do not, see the previous item. Try using syntax without ``-''
+  after the ``dig'' command. Also, requests that would result in an
+  empty digest are silently ignored, but the reason why no digest was
+  created is logged to the mail log. This is done so that cron scripts
+  generating daily digest will just fail silently, rather than
+  generating an error, for what isn't really one.
+
+
+  6\b6.\b.1\b13\b3.\b.  R\bRe\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\bio\bon\bn (\b(u\bun\bn)\b)s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\be c\bco\bon\bnf\bfi\bir\brm\bm r\bre\beq\bqu\bue\bes\bst\bts\bs g\bgo\bo t\bto\bo t\bth\bhe\be
+  u\bus\bse\ber\br,\b, n\bno\bot\bt t\bth\bhe\be m\bmo\bod\bde\ber\bra\bat\bto\bor\br.\b.
+
+  Either the list is not set up for remote administration (i.e.
+  D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be does not exist), or the moderator is sending the request
+  from an address that is not in the moderator database (e.g. from
+  Fred@host.dom, when fred@host.dom is in the moderator db, but
+  Fred@host.dom is not). ezmlm-manage(1) has no way of knowing that the
+  SENDER is a moderator and treats the request as coming from a regular
+  user, i.e. it sends a confirmation request to the target address.
+  Correct the SENDER address, the address in the moderator db, or create
+  D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be. If you are using a non-default moderator db location, make
+  sure that the moddir name is in D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be (for remote admin only) or
+  D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb (if there is subscription moderation as well). In both
+  cases, the contents will be ignored unless they start with a ``/''.
+
+
+  6\b6.\b.1\b14\b4.\b.  (\b(U\bUn\bn)\b)s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs d\bdo\boe\bes\bs n\bno\bot\bt r\bre\bec\bce\bei\biv\bve\be a\ba (\b(u\bun\bn)\b)s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\be a\bac\bck\bkn\bno\bow\bwl\ble\bed\bdg\bge\be-\b-
+  m\bme\ben\bnt\bt
+
+  With normal ezmlm lists, a subscriber confirming a subscription or a
+  non-subscriber confirming a unsubscribe request results in a message
+  to the target address. This message is suppressed when the list is set
+  up for subscription and/or remote administration, so that
+  confirmations from multiple moderators do not result in multiple
+  messages to the target address. The target address is always notified
+  if the subscriber status of the address changes (from non-subscriber
+  to subscriber or vice versa).
+
+
+  6\b6.\b.1\b15\b5.\b.  M\bMe\bes\bss\bsa\bag\bge\bes\bs p\bpo\bos\bst\bte\bed\bd t\bto\bo a\ba m\bmo\bod\bde\ber\bra\bat\bte\bed\bd l\bli\bis\bst\bt a\bar\bre\be s\bse\ben\bnt\bt o\bou\but\bt w\bwi\bit\bth\bho\bou\but\bt m\bmo\bod\bde\ber\br-\b-
+  a\bat\bti\bio\bon\bn.\b.
+
+  The list is not set up as a moderated list. Check D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br.  If
+  should contain a ezmlm-store(1) line after the ezmlm-reject line if it
+  is a moderated list. No ezmlm-send(1) line should be in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br.
+  If there is, the list is not moderated. Also, D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt must exist.
+  If it does not, ezmlm-store(1) will post the messages directly (via
+  ezmlm-send(1)) without sending them out for moderation first. This
+  makes it easy to temporarily remove message moderation by simply
+  removing D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt, but may be confusing if the user is unaware of
+  this ezmlm-store(1) feature.
+
+
+  6\b6.\b.1\b16\b6.\b.  M\bMe\bes\bss\bsa\bag\bge\bes\bs p\bpo\bos\bst\bte\bed\bd t\bto\bo a\ba m\bmo\bod\bde\ber\bra\bat\bte\bed\bd l\bli\bis\bst\bt d\bdo\bo n\bno\bot\bt r\bre\bes\bsu\bul\blt\bt i\bin\bn m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn
+  r\bre\beq\bqu\bue\bes\bst\bts\bs.\b.
+
+
+  +\bo  Check that ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt is a link to D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br.
+
+  +\bo  Check that D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br contains ezmlm-store(1) and not ezmlm-
+     send(1).  If this is not the case, the list is not message
+     moderated.
+
+  +\bo  Check for the presence of D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt. If this file is missing, the
+     list is not moderated, even if D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br is set up with ezmlm-
+     store(1).
+
+  +\bo  Check qmail logs for error conditions during post delivery and
+     correct these. If the messages are delivered correctly, verify that
+     ezmlm-store(1) generated the moderation requests to the moderators.
+
+  +\bo  Check to see that there are indeed moderators:
+
+
+
+       % ezmlm-list moddir
+
+
+
+
+  where ``moddir'' is the contents of D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt if they start with a
+  ``/'', otherwise those of D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be (same ``/'' requirement), and
+  D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ by default.
+
+
+  +\bo  Check file ownerships.
+
+     Another common problem is directory ownerships, especially for
+     lists under ~alias. To correct this error, issue the following
+     command while in the ~alias directory (User the user/group of the
+     list owner; for ~alias lists user=alias, group=qmail):
+
+
+       % chown -R user DIR
+
+
+
+
+
+  6\b6.\b.1\b17\b7.\b.  M\bMo\bod\bde\ber\bra\bat\bti\bio\bon\bn r\bre\beq\bqu\bue\bes\bst\bt r\bre\bep\bpl\bli\bie\bes\bs d\bdo\bo n\bno\bot\bt r\bre\bes\bsu\bul\blt\bt i\bin\bn t\bth\bhe\be a\bap\bpp\bpr\bro\bop\bpr\bri\bia\bat\bte\be
+  a\bac\bct\bti\bio\bon\bn.\b.
+
+
+  +\bo  Check that the address in the moderation request is correct.
+
+  +\bo  Check that the ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt-\b-a\bac\bcc\bce\bep\bpt\bt-\b-d\bde\bef\bfa\bau\bul\blt\bt and ~\b~.\b./\b/q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt-\b-
+     r\bre\bej\bje\bec\bct\bt-\b-d\bde\bef\bfa\bau\bul\blt\bt links exists and point to D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br.
+
+  +\bo  Check that D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br invokes ezmlm-moderate(1), and that there
+     is a copy of ezmlm-send(1) in the ezmlm binary directory.
+
+  +\bo  Check the qmail log to see that the replies were delivered to this
+     address.
+
+  +\bo  Check directory ownerships. For lists under alias:
+
+
+
+       % chown -R alias DIR
+
+
+
+
+  _\bN_\bO_\bT_\bE_\b: This needs to be done every time you add/remove moderators as
+  ``root''. For user-controlled lists (i.e. you are ``user'' when run-
+  ning e.g. ezmlm-sub(1)) this is not a problem.
+
+  If setting up lists for _\ba_\bl_\bi_\ba_\bs, you can avoid many problems by setting
+  them up as ``alias'', i.e. use ``su alias'' not ``su''.
+
+  If setting up lists for a user controlling a virtual domain, you can
+  avoid many problems by assuming that uid (``su user'') before making
+  any changes.
+
+  +\bo  Check the qmail logs: After the delivery of the moderation request,
+     ezmlm-send(1) should run to send messages to all the list
+     subscribers.
+
+  +\bo  Make sure there are list subscribers:
+
+
+
+       % ezmlm-list DIR
+
+
+
+
+  Most error conditions, incorrect request cookies, etc, should result
+  in informative error messages in the mail log.
+
+
+  6\b6.\b.1\b18\b8.\b.  M\bMo\bod\bde\ber\bra\bat\bto\bor\br c\bco\bom\bmm\bme\ben\bnt\bts\bs w\bwi\bit\bth\bh m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn r\bre\beq\bqu\bue\bes\bst\bt r\bre\bep\bpl\bli\bie\bes\bs a\bar\bre\be n\bno\bot\bt
+  a\bad\bdd\bde\bed\bd t\bto\bo t\bth\bhe\be p\bpo\bos\bst\bt/\b/s\bse\ben\bnt\bt t\bto\bo t\bth\bhe\be p\bpo\bos\bst\bte\ber\br.\b.
+
+  Moderator comments are where the moderator chooses to ``reject'' the
+  message and inform the person posting which his/her message was
+  inappropriate.  However, if a moderator wants to comment on a\bac\bcc\bce\bep\bpt\bte\bed\bd
+  posts, the moderator may only do so via a follow-up post to the list.
+  This is to avoid anonymously tagged-on text to posts. If a moderator
+  has something to say to the list, they should (and can only) do so in
+  regular posts. If you want to edit posts before sending them to the
+  list, set up a moderated list with you as the only moderator. Into
+  D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br before the ezmlm-store(1) line, put a condredirect(1) line
+  that redirects all messages with a SENDER other than you to your
+  address. You can edit the contents ands repost, the message will pass
+  condredirect(1), and hit ezmlm-store(1). You will be asked to confirm
+  (needed to assure that nobody else can post directly) and when you do,
+  the messages is posted.
+
+  Moderator comments for ``reject(ed)'' posts need to be enclosed
+  between two lines (yes, the end marker is required), having ``%%%''
+  starting on one of the first 5 positions of the line. If there are
+  characters before the marker, these will be removed from any comment
+  line that starts with the same characters (e.g. the characters before
+  ``comment2'' in the example below will be removed):
+
+
+  %%%
+  comment
+  %%%
+
+
+  or:
+
+
+  > %%%
+  comment
+  > comment2
+  > %%%
+
+
+  but not:
+
+  %%
+  COMMENT
+  %%
+
+
+  and not:
+
+  %%% this is my comment %%%
+
+
+  or
+
+  ezmlm said>%%%
+  comment
+  ezmlm said>%%%
+
+
+
+
+  6\b6.\b.1\b19\b9.\b.  S\bSo\bom\bme\be h\bhe\bea\bad\bde\ber\brs\bs a\bar\bre\be m\bmi\bis\bss\bsi\bin\bng\bg f\bfr\bro\bom\bm m\bme\bes\bss\bsa\bag\bge\bes\bs i\bin\bn t\bth\bhe\be d\bdi\big\bge\bes\bst\bt.\b.
+
+  By default, only a subset of message headers are sent out in any
+  digest and archive retrieval requests. First, headers in
+  D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\brr\bre\bem\bmo\bov\bve\be are stripped. Most non-essential headers are excluded
+  when the default archive retrieval format (``m'') is used.  Use the
+  ``v'' or ``n'' format (see ezmlm-get(1)) to get all message headers
+  that are in the archive.
+
+
+  6\b6.\b.2\b20\b0.\b.  S\bSo\bom\bme\be R\bRe\bec\bce\bei\biv\bve\bed\bd:\b: h\bhe\bea\bad\bde\ber\brs\bs a\bar\bre\be m\bmi\bis\bss\bsi\bin\bng\bg f\bfr\bro\bom\bm m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  ezmlm-idx>=0.313 removes all but the latest ``Received:'' header from
+  messages sent to the list. This is done since messages, especially
+  sent via sublists, may have so many ``Received:'' headers that MTAs
+  with primitive ``loop detection'' erroneously reject them. The
+  subscriber can subscribe, since those messages have fewer such
+  headers, and will receive warning and probe messages, but never see
+  any posts.
+
+  To see all headers of a message for diagnostic purposes, mail
+  mainlist-getv.num@mainhost, where ``num'' is the message number.  All
+  ``Received:'' headers are stored in the archive copy of the message.
+
+  To disable ``Received:'' header pruning, use the ezmlm-send(1) ``-r''
+  switch.
+
+
+  6\b6.\b.2\b21\b1.\b.  M\bMy\by M\bMu\but\btt\bt u\bus\bse\ber\brs\bs c\bca\ban\bnn\bno\bot\bt t\bth\bhr\bre\bea\bad\bd t\bth\bhe\bei\bir\br d\bdi\big\bge\bes\bst\bt m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  The digest by default removed non-essential headers like ``In-Reply-
+  To:'' from messages. Modern MUAs, like _\bM_\bu_\bt_\bt can split out messages
+  from a digest and then thread them based on such headers. To include
+  these and all other headers in the digest messages, use the ``v'' or
+  ``n'' format as described on the ezmlm-get(1) man page. Normally, the
+  threading done by ezmlm is sufficient and the default format preferred
+  to reduce message and digest size, often by 25% or more.
+
+
+  6\b6.\b.2\b22\b2.\b.  P\bPo\bos\bst\bts\bs f\bfa\bai\bil\bl:\b: M\bMe\bes\bss\bsa\bag\bge\be a\bal\blr\bre\bea\bad\bdy\by h\bha\bas\bs M\bMa\bai\bil\bli\bin\bng\bg-\b-L\bLi\bis\bst\bt (\b(#\b#5\b5.\b.7\b7.\b.2\b2)\b).\b.
+
+  The list you are trying to post to is used as a sublist (a list fed
+  with messages from another (ezmlm) list), but not properly set up as a
+  sublist. Put  the name of the parent list (``origlist@orighost'')
+  which exactly matches the SENDER of the original (or parent) list into
+  D\bDI\bIR\bR/\b/s\bsu\bub\bbl\bli\bis\bst\bt.  Check the ownership of D\bDI\bIR\bR/\b/s\bsu\bub\bbl\bli\bis\bst\bt, to make sure that
+  the user controlling the list can read it.
+
+  Alternatively, use the ezmlm-make(1) ``-0 origlist@orighost'' switch
+  (see ``Customizing ezmlm-make operation'').
+
+
+  6\b6.\b.2\b23\b3.\b.  T\bTh\bhe\be l\bla\bas\bst\bt l\bli\bin\bne\be o\bof\bf a\ba D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/  f\bfi\bil\ble\be i\bis\bs i\big\bgn\bno\bor\bre\bed\bd.\b.
+
+  Only complete lines ending with ``newline'' are copied. The last line
+  in the D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ file most likely lacks a terminal ``newline''.
+
+
+  6\b6.\b.2\b24\b4.\b.  N\bNo\bo C\bCO\bON\bNF\bFI\bIR\bRM\bM r\bre\beq\bqu\bue\bes\bst\bts\bs a\bar\bre\be s\bse\ben\bnt\bt t\bto\bo m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs.\b.
+
+  Assuming that the user initiated the subscribe request, got a
+  ``confirm'' request, and replied correctly, there are two possible
+  causes for the problem: Either the list is not subscription moderated
+  (in this case the user is subscribed and received a note saying so) or
+  the list is subscription moderated but no moderators have been added
+  (ezmlm-manage(1) sends out the request and doesn't mind that there are
+  no recipients).
+
+  Check that the list is subscription moderated:
+
+
+  % cat DIR/modsub
+
+
+
+
+  If this fails the list is not subscription moderated. If it succeeds
+  with a directory name with a leading ``/'', this is your ``moddir''.
+  If not:
+
+
+
+       % cat DIR/remote
+
+
+
+
+  If this succeeds with a directory name with a leading ``/'', this is
+  your moddir, otherwise the moddir is ``D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/''.
+
+  Check for moderators:
+
+
+
+       % ezmlm-list moddir
+
+
+
+
+  If there are none, this is your problem. If there are some, check the
+  mail log to see what happened when the CONFIRM requests was supposed
+  to have gone out. Assure correct ownerships for the moderator db:
+
+
+
+       % chown -R user moddir
+
+
+
+
+  For ~alias:
+
+
+
+        # chown -R alias moddir
+
+
+
+
+  Another possible problem is that you are trying to use the remote
+  admin feature to subscribe a user, but you get no CONFIRM request.
+  Usually, this is due to your SENDER address not being in the moderator
+  database.  The CONFIRM request went to the target address instead,
+  since as far as ezmlm is concerned, you are a regular user.
+
+
+  6\b6.\b.2\b25\b5.\b.  D\bDe\bel\bli\biv\bve\ber\bri\bie\bes\bs f\bfa\bai\bil\bl `\b``\b`t\bte\bem\bmp\bpo\bor\bra\bar\bry\by q\bqm\bma\bai\bil\bl-\b-q\bqu\bue\beu\bue\be e\ber\brr\bro\bor\br'\b''\b'
+
+  Usually, this is due to a corrupted qmail queue (should affect all
+  mail) or a corrupted ezmlm subscriber database (See ``How to deal with
+  corrupted subscriber lists'').  ezmlm-idx>=0.40 has more informative
+  qmail error messages.
+
+
+
+
+
+  6\b6.\b.2\b26\b6.\b.  H\bHo\bow\bw t\bto\bo d\bde\bea\bal\bl w\bwi\bit\bth\bh c\bco\bor\brr\bru\bup\bpt\bte\bed\bd s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br l\bli\bis\bst\bts\bs
+
+  Dan has made ezmlm very robust, but a subscriber list can still become
+  corrupted due to e.g. disk errors. Usually, this will lead to a
+  ``temporary qmail-queue error'' because an address does not conform to
+  the standard format. Occasionally, two E-mail addresses are fused,
+  e.g.  ``addr1@hostTaddr2@host''.  To diagnose and fix this type of
+  error, disable deliveries (easiest is to ``chmod 0 DIR/lock''), back
+  up the contents of D\bDI\bIR\bR/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/, then:
+
+
+
+       % ezmlm-list DIR > tmp.tmp
+
+               ( edit tmp.tmp to fix any problems )
+
+       % rm -rf DIR/subscribers/*
+       % ezmlm-sub DIR < tmp.tmp
+
+
+
+
+  This will list all E-mail addresses, allow you to edit them, then re-
+  subscribe them.  Don't forget to re-enable deliveries.
+
+
+  6\b6.\b.2\b27\b7.\b.  V\bVa\bac\bca\bat\bti\bio\bon\bn p\bpr\bro\bog\bgr\bra\bam\bm r\bre\bep\bpl\bli\bie\bes\bs a\bar\bre\be t\btr\bre\bea\bat\bte\bed\bd a\bas\bs b\bbo\bou\bun\bnc\bce\bes\bs b\bby\by e\bez\bzm\bml\blm\bm.\b.
+
+  Standard vacation programs do not reply to messages that contain a
+  ``Precedence: bulk'' header. ezmlm-idx>=0.23 sets up lists with this
+  header in D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd. For older lists, use ``ezmlm-make -+'' or
+  ``ezmlm-make -e'' to update them, or just add a ``Precedence: bulk''
+  line to D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd.
+
+
+  6\b6.\b.2\b28\b8.\b.  D\bDi\big\bge\bes\bst\bts\bs d\bdo\bo n\bno\bot\bt c\bco\bom\bme\be a\bat\bt r\bre\beg\bgu\bul\bla\bar\br h\bho\bou\bur\brs\bs.\b.
+
+  In the default setup, ezmlm-tstdig(1) determines if a new digest is
+  due every time a message arrives to the list. Thus, even though ezmlm-
+  tstdig is set to produce digests 48 hours after the previous digest,
+  the digest will not be generated until a message arrives. If you'd
+  like digests at a specific time each day, use crond(8) and crontab(1)
+  to daily run:
+
+
+               % ezmlm-get DIR
+
+
+
+
+
+  6\b6.\b.2\b29\b9.\b.  P\bPr\bre\bev\bve\ben\bnt\bti\bin\bng\bg l\blo\boo\bop\bps\bs f\bfr\bro\bom\bm m\bmi\bis\bsc\bco\bon\bnf\bfi\big\bgu\bur\bre\bed\bd s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br a\bad\bdd\bdr\bre\bes\bss\bse\bes\bs.\b.
+
+  Occasionally, a subscriber address is misconfigured and automatically
+  sends a message back to the list. Sometimes, the subscriber's setup
+  has removed headers that ezmlm uses for loop detection or the
+  generated messages has nothing in common with the send-out. To block
+  such mail at the list, include the ezmlm-make(1) ``-k'' (kill) switch
+  and add the offending address to D\bDI\bIR\bR/\b/d\bde\ben\bny\by/\b/ with
+
+
+               % ezmlm-sub DIR/deny badadr@badhost
+
+
+
+
+  ezmlm-unsub(1) and ezmlm-list(1) can be used similarly to remove or
+  list the addresses. If your list is configured for remote administra-
+  tion (see ``How remote administration works''), and you are a remote
+  administrator, you can add the address by sending mail to list-deny-
+  badadr=badhost@listhost. Other subscriber database commands work as
+  well for list-deny.
+
+  In other instances, a configuration error somewhere close to the
+  subscriber creates a local mail loop throwing off messages to you.
+  They are often bounces that are sent to the list address or to ``list-
+  help'' due to configuration errors. Rather than accepting these, or
+  the often resulting double bounces to ``postmaster'', just add a
+  ``|/path/ezmlm-weed'' line first to D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br or D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. This
+  discards the bounce messages generated by the looping systems. ezmlm-
+  weed(1) is also useful in other settings where excessive numbers of
+  error messages are sent to the wrong address.
+
+
+  6\b6.\b.3\b30\b0.\b.  A\bA u\bus\bse\ber\br c\bca\ban\bn s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\be a\ban\bnd\bd r\bre\bec\bce\bei\biv\bve\bes\bs w\bwa\bar\brn\bni\bin\bng\bg a\ban\bnd\bd p\bpr\bro\bob\bbe\be m\bme\bes\bss\bsa\bag\bge\bes\bs,\b,
+  b\bbu\but\bt n\bno\bo m\bme\bes\bss\bsa\bag\bge\bes\bs f\bfr\bro\bom\bm t\bth\bhe\be l\bli\bis\bst\bt.\b.
+
+  ezmlm lists (ezmlm-idx>=0.31) remove ``Received:'' headers from
+  incoming messages by default. This can be prevented with the ezmlm-
+  send(1) ``-r'' switch.  When the headers are propagated, especially
+  sublist message may have many (15-20 or more), ``Received:'' headers.
+  If there is a poorly configured sendmail host with a ``hopcount'' set
+  too low, it will bounce these messages, incorrectly believing that the
+  many ``Received:'' headers are due to a mail loop.  The reason that
+  administrative from the list do not bounce is that they have fewer
+  ``Received:'' headers, since they originate from the sublist.
+
+  The message with all headers including the removed ``Received:''
+  headers can be retrieved from the list archive with the _\b-_\bg_\be_\bt_\bv command.
+  The top incoming ``Received:'' header is added by qmail at the receipt
+  to the list (or last sublist) host. This header is not removed, to
+  allow the recipient to determine when the message reached the list.
+
+
+  7\b7.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be o\bop\bpe\ber\bra\bat\bti\bio\bon\bn v\bvi\bia\ba e\bez\bzm\bml\blm\bmr\brc\bc
+
+
+  7\b7.\b.1\b1.\b.  U\bUs\bsi\bin\bng\bg e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be t\bto\bo e\bed\bdi\bit\bt e\bex\bxi\bis\bst\bti\bin\bng\bg l\bli\bis\bst\bts\bs.\b.
+
+  With ezmlm-make(1) (from ezmlm-idx >=0.21) you can use the ``-e''
+  switch to edit existing lists.  Invoke the ezmlm-make(1) command just
+  as you would to create the list anew, but change the switches to
+  reflect the desired change, and add the ``-e'' switch. ezmlm-make will
+  accept preexisting directories and overwrite or remove files to change
+  the setup.  The message counter (D\bDI\bIR\bR/\b/n\bnu\bum\bm), digest counters (D\bDI\bIR\bR/\b/d\bdi\big\bgn\bnu\bum\bm
+  and D\bDI\bIR\bR/\b/d\bdi\big\bgi\bis\bss\bsu\bue\be), the key (D\bDI\bIR\bR/\b/k\bke\bey\by) and the message archive will not
+  be affected.
+
+  If the list has been created or previously edited with ezmlm-make(1)
+  from ezmlm-idx>=0.23, the list remembers (via D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg) the
+  arguments and the switches. All you have to do is to use the ezmlm-
+  make(1) ``-+'' switch and specify options you wish to change, or use
+  the ``-e'' switch and specify all non-default options you'd like to
+  use.
+
+  _\bN_\bO_\bT_\bE_\b: ezmlm-make(1) ``-e'' and ``-+'' will OVERWRITE any manual
+  customizations you have made to the program files, but not text files
+  and D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd, D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\brr\bre\bem\bmo\bov\bve\be, etc. To reset all such files
+  (such as when changing list name), use ``-ee'' or ``-++''.
+
+  To make general customizations, please change e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) (see ``What
+  is ezmlmrc?''  or read on) instead and use the ``-c'' switch as well.
+  DO NOT use this option to change production lists without testing it
+  on other lists first.  Also, for some changes, removing or adding a
+  flag is sufficient (see ``How do I quickly change properties of my
+  list'').
+
+
+  7\b7.\b.2\b2.\b.  W\bWh\bha\bat\bt i\bis\bs e\bez\bzm\bml\blm\bmr\brc\bc?\b?
+
+  ezmlm-make(1) has a number of default switches that through e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b)
+  have defined functions. These allow creation of many standard lists.
+
+  In addition, ezmlm-make(1) operation is fully customizable via
+  modification of the template file, ezmlmrc(5) or .ezmlmrc. A default
+  ezmlmrc(5) is installed in the ezmlm binary directory.  The system
+  administrator can install a system-wide default e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) file in
+  /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bmr\brc\bc (or symlinked from there) which overrides the file in the
+  ezmlm binary directory. If the ezmlm-make(1) ``-c'' (custom) switch is
+  used, ezmlm-make(1) will look for .\b.e\bez\bzm\bml\blm\bmr\brc\bc in the ``dotdir'', i.e. the
+  directory in which the .\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt links are placed. This is usually a
+  set directory for a given user/virtual domain (usually, the home
+  directory for the user controlling the lists).
+
+  e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) controls everything except creation of the list directory
+  itself and the key used for cookie generation. The syntax of
+  e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) is documented in ezmlm-make(1), the ezmlmrc(5) man page,
+  and in the ezmlmrc(5) file installed in the ezmlm binary directory.
+  ezmlm-make limits its effects to within the list ``dot'' and ``DIR''
+  directories. In the ``dotdir'', only links to within ``DIR'' can be
+  created.
+
+
+  7\b7.\b.3\b3.\b.  C\bCh\bha\ban\bng\bgi\bin\bng\bg d\bde\bef\bfa\bau\bul\blt\bts\bs f\bfo\bor\br D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/  f\bfi\bil\ble\bes\bs.\b.
+
+  Copy the ezmlmrc(5) file from the ezmlm bin directory to .\b.e\bez\bzm\bml\blm\bmr\brc\bc in
+  your .\b.q\bqm\bma\bai\bil\bl file base directory (usually your home directory):
+
+
+       % cp /usr/local/bin/ezmlm/ezmlmrc ~/.ezmlmrc
+
+
+
+
+  The base e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) file lives in the ezmlm binary directory, which
+  may differ from ``/\b/u\bus\bsr\br/\b/l\blo\boc\bca\bal\bl/\b/b\bbi\bin\bn/\b/e\bez\bzm\bml\blm\bm/\b/e\bez\bzm\bml\blm\bmr\brc\bc'' if you do not have a
+  default setup.  If your system administrator has placed a ezmlmrc(5)
+  file into the /\b/e\bet\btc\bc directory, start with that one instead, as it is
+  likely to already contain some useful local customization and
+  comments.
+
+  Now edit ~\b~/\b/.\b.e\bez\bzm\bml\blm\bmr\brc\bc. Find the tag corresponding to the text file you
+  want to change, e.g. ``</text/mod-request/>'', and modify it
+  appropriately. Some tags have conditional flags, so that succeeding
+  text is copied only if specific switches are on/off. Thus, text
+  succeeding ``</text/file#rms/>'' is copied into D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/f\bfi\bil\ble\be if and
+  only if the ezmlm-make(1) ``-rms'' switches are all used. For more
+  info, see documentation in e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) and the ezmlm-make(1) man page.
+  To invoke a custom .\b.e\bez\bzm\bml\blm\bmr\brc\bc file, use the ezmlm-make(1) ``-c''
+  (custom) switch.
+
+
+  7\b7.\b.4\b4.\b.  C\bCh\bha\ban\bng\bgi\bin\bng\bg d\bde\bef\bfa\bau\bul\blt\bt m\bmo\bod\bde\ber\bra\bat\bto\bor\br d\bdi\bir\bre\bec\bct\bto\bor\bri\bie\bes\bs.\b.
+
+  See above. Edit the .\b.e\bez\bzm\bml\blm\bmr\brc\bc file to add a directory name to e.g.
+  ``</modsub/#s>''. Also, you need to create that directory, and the
+  subscribers subdirectory under it. NOTE: D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ is still required as
+  the base directory for the message moderation queue.
+  7\b7.\b.5\b5.\b.  A\bAd\bda\bap\bpt\bti\bin\bng\bg e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be f\bfo\bor\br v\bvi\bir\brt\btu\bua\bal\bl d\bdo\bom\bma\bai\bin\bns\bs.\b.
+
+  This is not necessary if you use qmail>=1.02 and ezmlm-idx>=0.32.
+
+  The problem with virtual domains is that ezmlm-make(1) by default puts
+  the list name in D\bDI\bIR\bR/\b/i\bin\bnl\blo\boc\bca\bal\bl. However, if the domain host1.dom.com is
+  controlled by the user ``virt'', then the local part of the address
+  for the list list@host.dom.com will be ``virt-list'', not ``list''.
+  This is easily accommodated by putting a .\b.e\bez\bzm\bml\blm\bmr\brc\bc file in ~\b~v\bvi\bir\brt\bt/\b/.  In
+  the ``</inlocal/>'' section of this file, enter ``virt-<#L#>'' instead
+  of ``<#L#>''.  Now, all lists created under ~\b~v\bvi\bir\brt\bt will be
+  automatically set up correctly.
+
+  Similarly, if host1.dom.com is controlled by virt-dom1 and
+  host2.dom.com by ``virt-dom2'', inlocal for list list@host1.dom.com
+  should be ``virt-dom1-list'' and for list@host2.dom.com should be
+  ``virt-dom2-list''. To accommodate this, put ``virt-<#1#>-<#L#>'' in
+  ``</inlocal/>''.
+
+  Running:
+
+
+       % ezmlm-make -c ~virt/LIST ~virt/.qmail-dom1-list \
+                  list host1.dom.com
+
+
+
+
+  will produce a L\bLI\bIS\bST\bT/\b/i\bin\bnl\blo\boc\bca\bal\bl of virt-dom1-list by substituting the
+  first part between two ``-'' (dom1) for ``<#1#>''. Two levels of
+  dashes are accommodated, i.e. ``<#2#>'' will be replaced by the second
+  part between two ``-'' (in this case empty (_\bS_\bi_\bc_\b!)).  For more info,
+  see ezmlm-make(1) and comments in e\bez\bzm\bml\blm\bmr\brc\bc.
+
+
+  7\b7.\b.6\b6.\b.  S\bSe\bet\btt\bti\bin\bng\bg u\bup\bp e\bez\bzm\bml\blm\bm-\b-m\bma\bak\bke\be f\bfo\bor\br s\bsp\bpe\bec\bci\bia\bal\bl s\bsi\bit\btu\bua\bat\bti\bio\bon\bns\bs.\b.
+
+  Ezmlm-make is very flexible. There are only three sets of special
+  command line switches: ``-vV'' for version info, ``-cC'' controlling
+  the use of a custom file .\b.e\bez\bzm\bml\blm\bmr\brc\bc in the ``dot'' directory, and
+  ``-eE'' for edit mode (i.e. reconfiguration of existing list setups).
+  All other switches are soft, i.e. controlled through e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b).  Many
+  switches, have special meanings via e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) and are documented in
+  the man page. Any other switches can be used for customization (_\bN_\bO_\bT_\bE_\b:
+  _\bw_\be _\bm_\ba_\by _\bu_\bs_\be _\bs_\bw_\bi_\bt_\bc_\bh_\be_\bs _\bo_\bt_\bh_\be_\br _\bt_\bh_\ba_\bn _\b`_\b`_\b-_\bx_\by_\bz_\b'_\b' _\bf_\bo_\br _\bs_\bp_\be_\bc_\bi_\bf_\bi_\bc _\bp_\bu_\br_\bp_\bo_\bs_\be_\bs _\bi_\bn
+  _\bf_\bu_\bt_\bu_\br_\be _\bv_\be_\br_\bs_\bi_\bo_\bn_\bs_\b.)  The ``-xyz'' switches will always be available for
+  your use, with the ``-x'' switch being configured for some
+  demo/special features in the distributed e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b).  You can use them
+  for anything you like. They are by default off=false. The complement
+  of these switches is ``-XYZ'' (by default on=true). You can use these
+  to cause specific changes in the list setup if a given switch is used.
+  For an example, see the ``-x'' switch as used and documented in the
+  default e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) file.  The switches ``-aip'' are set by default to
+  be backwards compatible with ezmlm-0.53. Other switches are ``off'' by
+  default.
+
+  Switches ``-a-z'' and ``-A-Z'' take no arguments.  Switches ``-0'' and
+  and ``-3-9'' take arguments.  When the ezmlm-make(1) ``-+'' switch is
+  used, the current settings for all these switches are read from the
+  list's D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg (if available).
+
+
+  8\b8.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\be p\bpo\bos\bst\bti\bin\bng\bg t\bto\bo t\bth\bhe\be l\bli\bis\bst\bt.\b.
+
+
+
+  8\b8.\b.1\b1.\b.  R\bRe\beq\bqu\bui\bir\bri\bin\bng\bg t\bth\bhe\be l\bli\bis\bst\bt a\bad\bdd\bdr\bre\bes\bss\bs i\bin\bn T\bTo\bo:\b:/\b/C\bCc\bc:\b: h\bhe\bea\bad\bde\ber\brs\bs.\b.
+
+  SPAM or junk mail is usually sent by mailing a single message to a
+  large number of (unwilling) recipients. As such, it usually does not
+  contain the E-mail address of all recipients (remember, junk mailers
+  pay for these address lists). By rejecting messages that do not have
+  the list address in the To: or Cc: header(s) a large fraction of spam
+  to the list can be filtered out.
+
+  This filter function is activated by default, but will work only if
+  you specify the list directory on the ezmlm-reject(1) command line. To
+  disable this restriction, remove the ``DIR'' argument from the ezmlm-
+  reject(1) command line, or add the ``-T'' switch.
+
+  By default, this error is logged, and an error message is sent to the
+  sender.  Since virtually all the failures will be SPAM and virtually
+  all spam has a faked SENDER, most of these error messages will go to
+  the postmaster.  Thus, you may want to use the ezmlm-reject ``-q''
+  switch (quiet) to suppress the sender notification.
+
+
+  8\b8.\b.2\b2.\b.  R\bRe\bej\bje\bec\bct\bti\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs s\bse\ben\bnt\bt f\bfr\bro\bom\bm o\bot\bth\bhe\ber\br m\bma\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bts\bs.\b.
+
+  ezmlm automatically detects are rejects messages that are sent from
+  other ezmlm mailing lists. Some other mailing list managers do not use
+  a rigorous mechanisms to verify subscribers. Thus, it is possible to
+  subscribe an ezmlm list address to such a mailing list. You can easily
+  block such a list by adding the address to the ``deny'' if you use the
+  ezmlm-make(1) ``-k'' option. However, you can also configure ezmlm-
+  reject(1) to reject messages based on specific headers placed into
+  D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\brr\bre\bej\bje\bec\bct\bt. A set of headers which will catch mailing list
+  managers known to us are listed in the ezmlm-reject(1) man page. To
+  activate this option, you must specify the ``-h'' switch and D\bDI\bIR\bR on
+  the ezmlm-reject(1) line in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br. Naturally, you can make this
+  the default by editing ezmlmrc(5) (See ``Customizing ezmlm-make
+  operation'').
+
+
+  8\b8.\b.3\b3.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg p\bpo\bos\bst\bts\bs b\bba\bas\bse\bed\bd o\bon\bn t\bth\bhe\be S\bSu\bub\bbj\bje\bec\bct\bt l\bli\bin\bne\be.\b.
+
+  ezmlm-reject(1) is by default configured to reject posts with empty
+  subject (``-s'' switch) or with a subject that consists of only an
+  administrative command word (``-c'' switch), such as ``subscribe''. To
+  remove these restrictions, use the ezmlm-reject(1) ``-S'' and ``-C''
+  switch, respectively. You can also into D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br before the ezmlm-
+  send(1) line add:
+
+
+               | grep -i 'subject:' | grep -if DIR/bad_words >/dev/null && \
+                       {echo "bad words found"; exit 100; }
+
+
+
+
+  to reject messages that have a line matching ``Subject:'' followed by
+  any bad word listed in D\bDI\bIR\bR/\b/b\bba\bad\bd_\b_w\bwo\bor\brd\bds\bs.
+
+
+  8\b8.\b.4\b4.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg t\bth\bhe\be s\bsi\biz\bze\be o\bof\bf p\bpo\bos\bst\bts\bs.\b.
+
+  If the ``DIR'' argument is specified on the ezmlm-reject(1) line in
+  D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br and D\bDI\bIR\bR/\b/m\bms\bsg\bgs\bsi\biz\bze\be exists and contains a number (in bytes)
+  greater than ``0'', then any posts with a body larger than the number
+  specified is rejected. The maximum message size can optionally be
+  followed by ``:'' and a minimum message body size in bytes.  For
+  moderated lists, messages that are too large are rejected and not sent
+  to the moderators. This feature can be used to prevent the posting an
+  entire digest to the list by setting D\bDI\bIR\bR/\b/m\bms\bsg\bgs\bsi\biz\bze\be slightly below the
+  message size set in your ezmlm-tstdig(1) innovation (if any). A
+  minimum size can catch a few administrative request sent to the main
+  list, but is otherwise not that useful. To always configure your lists
+  with a message size restriction, add to e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b):
+
+
+               </msgsize/>
+               max:min
+
+
+
+
+  The ezmlm-make(1) ``-x'' switch adds this with 40000:2.
+
+
+  8\b8.\b.5\b5.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg p\bpo\bos\bst\bts\bs b\bba\bas\bse\bed\bd o\bon\bn M\bMI\bIM\bME\bE c\bco\bon\bnt\bte\ben\bnt\bt-\b-t\bty\byp\bpe\be.\b.
+
+  ezmlm-reject(1) will look for D\bDI\bIR\bR/\b/m\bms\bsg\bgs\bsi\biz\bze\be, D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bej\bje\bec\bct\bt, and
+  D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bem\bmo\bov\bve\be if the ``DIR'' argument is specified (``DIR'' can be
+  left out to conserve resources on lists that do not use these
+  features). _\bN_\bo_\bt_\be_\b: _\bT_\bh_\be _\b`_\b`_\bD_\bI_\bR_\b'_\b' _\ba_\br_\bg_\bu_\bm_\be_\bn_\bt _\bi_\bs _\ba_\bl_\bs_\bo _\br_\be_\bq_\bu_\bi_\br_\be_\bd _\bf_\bo_\br _\bt_\bh_\be _\bt_\bh_\be
+  _\bT_\bo_\b:_\b/_\bC_\bc_\b: _\bl_\bi_\bs_\bt _\ba_\bd_\bd_\br_\be_\bs_\bs _\br_\be_\bs_\bt_\br_\bi_\bc_\bt_\bi_\bo_\bn _\b(_\bs_\be_\be _\b`_\b`_\bR_\be_\bq_\bu_\bi_\br_\bi_\bn_\bg _\bt_\bh_\be _\bl_\bi_\bs_\bt _\ba_\bd_\bd_\br_\be_\bs_\bs _\bi_\bn
+  _\bT_\bo_\b:_\b/_\bC_\bc_\b: _\bh_\be_\ba_\bd_\be_\br_\bs_\b'_\b'_\b)_\b.  If the message contains MIME parts that are of a
+  content-type listed in D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bej\bje\bec\bct\bt they are rejected. If the
+  message is a simple MIME message of a content-type listed in either
+  D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bej\bje\bec\bct\bt or D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bem\bmo\bov\bve\be it is also rejected.
+
+  There is currently no ezmlm-make(1) switch for D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bej\bje\bec\bct\bt, but it
+  can easily be configured by editing e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b). The ezmlm-make ``-x''
+  switch configures D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bem\bmo\bov\bve\be (see ``mimeremove'') for a list of
+  content-types).  Messages consisting solely of these content-types
+  (rare) will be rejected, and the corresponding MIME parts of composite
+  messages will be removed.
+
+
+  8\b8.\b.6\b6.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg p\bpo\bos\bst\bts\bs t\bto\bo l\bli\bis\bst\bt s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs.\b.
+
+  Use message moderation. As an alternative, implement a check against
+  SENDER by using ezmlm-issubn(1). The latter is easily defeated by
+  faking SENDER. Also, it prevents posts from legitimate subscribers
+  that are subscribed under a different address than the one they send
+  from.  Nevertheless, it may be useful in some situations. Add:
+
+
+
+       |/usr/local/bin/ezmlm/ezmlm-issubn 'DIR' 'DIR/digest' 'DIR/allow' ||
+          { echo "Sorry, you are not allowed to post to this list.";
+            exit 100; }
+
+
+
+
+  _\bA_\bL_\bL _\bO_\bN _\bO_\bN_\bE _\bL_\bI_\bN_\bE to D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br before the ezmlm-send(1) line. ``DIR''
+  is the main list directory. If your ezmlm binaries live in a different
+  directory, change the ezmlm-issubn(1) path accordingly. If you would
+  like denied posts to be dropped silently rather than bounced, change
+  the exit code to 99.
+
+  See ``Customizing ezmlm-make operation'' if you want your lists to
+  have some of these features by default or set by specific ezmlm-
+  make(1) switches. The ezmlm-make(1) ``-u'' switch by default sets up
+  restrictions this way.
+
+
+  If you do not want to allow digest subscribers to post, remove
+  D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/ from the ezmlm-issubn command line. To allow posts from an
+  address that is not a subscriber, simply add it to the addresses in
+  D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/:
+
+
+               % ezmlm-sub DIR/allow address@host
+
+
+
+
+  The ``allow'' database can be manipulated remotely by sending mail to
+  list-allow-subscribe@listhost, list-allow-unsubscribe@listhost, etc.
+  If configured for the list, the ``-list'' command for remote adminis-
+  trators will work for the ``allow'' database as well.
+
+  Please note that this setup is not secure, as it is easy to modify the
+  envelope SENDER. For more secure options, see ``Restricting posts to
+  an arbitrary set of E-mail addresses (higher security option)''.
+
+
+
+  8\b8.\b.7\b7.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg p\bpo\bos\bst\bts\bs t\bto\bo a\ban\bn a\bar\brb\bbi\bit\btr\bra\bar\bry\by s\bse\bet\bt o\bof\bf E\bE-\b-m\bma\bai\bil\bl a\bad\bdd\bdr\bre\bes\bss\bse\bes\bs
+  (\b(h\bhi\big\bgh\bhe\ber\br s\bse\bec\bcu\bur\bri\bit\bty\by o\bop\bpt\bti\bio\bon\bn)\b).\b.
+
+  The easiest way to achieve this is to simply set up a message
+  moderated list, and add all the e-mail addresses to the moderator db.
+  Use a custom location, if you want a different set of moderators for
+  subscription moderation/remote admin. If a "moderator" posts, only
+  s/he will get a confirmation request. If anybody else posts, the post
+  will be sent to all moderators.
+
+
+  To directly bounce posts from SENDERs not in the database, use the
+  ezmlm-store ``-P'' (not public) switch. This is more secure than a
+  simple ezmlm-issubn(1) construct, since faking SENDER to a moderator
+  address will result in a confirmation request to that moderator (which
+  s/he will reject/ignore), rather than a direct post. The draw-back is
+  that each post has to be confirmed, but with the speed of ezmlm the
+  request will arrive immediately after the post is made, so the
+  overhead should is The best choice depends on your particular needs in
+  the trade-off between security and convenience.
+
+  ``ezmlm-make -om'' will set up such a moderated list with ``ezmlm-
+  store -P''.  This is the most useful setup for an announcement list.
+
+
+  Setting a list up in this way with only the owner's address gives you
+  a pretty safe owner-only list.
+
+
+  8\b8.\b.8\b8.\b.  C\bCo\bom\bmp\bpl\ble\bet\bte\bel\bly\by r\bre\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg p\bpo\bos\bst\bts\bs.\b.
+
+  To completely prevent posting (for instance a message-of-the-day
+  list), set up a normal list, and just remove ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt and
+  D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br altogether. Make posts from the shell, or from shell
+  scripts or crond, by simply piping a (complete) message to ezmlm-
+  send(1):
+
+
+
+       % /usr/local/bin/ezmlm/ezmlm-send DIR < message
+
+
+
+
+  _\bN_\bO_\bT_\bE: This can be done by any user with write access to files within
+  the list directory, so make sure your file modes are set correctly.
+  The ezmlm-send(1) path may need to be changed to match your ezmlm
+  binary directory. It's also a good idea to not allow others to read
+  your list directory and D\bDI\bIR\bR/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/ and other address lists.
+
+
+  8\b8.\b.9\b9.\b.  A\bA g\bge\ben\bne\ber\bra\bal\bl s\bso\bol\blu\but\bti\bio\bon\bn t\bto\bo r\bre\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg p\bpo\bos\bst\bts\bs b\bba\bas\bse\bed\bd o\bon\bn S\bSE\bEN\bND\bDE\bER\bR.\b.
+
+  As discussed above, the security afforded by SENDER checks is minimal,
+  but nevertheless sufficient to keep out most spam and garbage.
+  However, some subscribers post from e-mail addresses other than their
+  subscription address, and users tend to become unfriendly when their
+  posts are denied even though they are subscribers. This is a general
+  solution to this problem which has minimal overhead for the list owner
+  and is essentially completely transparent to the subscriber.
+
+  Set up the list with ezmlm-gate(1) in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br in place of the
+  ezmlm-send(1) line.  To the ezmlm-gate(1) command line add the list
+  directory twice, then a digest directory D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/ (if it exists),
+  then D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/.  Create D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt. Add the list owner as a message
+  moderator.
+
+  With this setup, any message from a SENDER that is a subscriber of the
+  main list, the digest list or added to D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/, will be posted
+  directly, others will be sent to the list owner for approval. If the
+  list wants to automatically approve posts from that address in future
+  (e.g. it is an alias for a subscriber) s/he just adds it to the
+  database in D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/.  If the owner wants to approve this post, but
+  not necessarily future posts from that address, s/he just accepts the
+  message. To reject the message with a comment is equally easy. If the
+  owner wished to have the option to silently ignore posts (and not have
+  the SENDER notified that the post timed out), just add the ezmlm-
+  clean(1) ``-R'' switch in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br and D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br.
+
+  In this way, the normal subscriber is always happy and the ``behind
+  the scenes'' work of the owner is minimalized.
+
+  ezmlm-make creates lists with this setup if you specify the ``-u''
+  switch in addition to the ``-m'' switch:
+
+
+
+               % ezmlm-make -mu ~/list ~/.qmail-list joe-list host
+
+
+
+
+  If you omit the ``-m'' switch, the setup will reject posts from non-
+  subscribers that are not in the ``allow'' database.  ezmlm-both(1)
+  uses a set of similar ezmlm-make(1) invocations to create a list with
+  digest, optionally making you a remote admin, list owner, and
+  subscriber to both lists.
+
+
+  9\b9.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg o\bou\but\btg\bgo\boi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+
+  9\b9.\b.1\b1.\b.  A\bAd\bdd\bdi\bin\bng\bg a\ba t\btr\bra\bai\bil\ble\ber\br t\bto\bo o\bou\but\btg\bgo\boi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Put the text in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/t\btr\bra\bai\bil\ble\ber\br. The text is NOT copied to the
+  archived version of the message. This works also for sublists.  Tags
+  ``<#h#>'', ``<#l#>'', and ``<#n#>'' are replaced by the list host,
+  local name, and current message number, respectively.
+
+
+  9\b9.\b.2\b2.\b.  A\bAd\bdd\bdi\bin\bng\bg a\ba s\bsu\bub\bbj\bje\bec\bct\bt p\bpr\bre\bef\bfi\bix\bx t\bto\bo o\bou\but\btg\bgo\boi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Put the exact text in D\bDI\bIR\bR/\b/p\bpr\bre\bef\bfi\bix\bx. You can include the message number
+  assigned to the post in the list archive by adding the ``#'' character
+  in the text in D\bDI\bIR\bR/\b/p\bpr\bre\bef\bfi\bix\bx (example: put ``lsqb;listname-#rsqb;'' in
+  D\bDI\bIR\bR/\b/p\bpr\bre\bef\bfi\bix\bx).  ezmlm does not modify the subject other than by
+  prefixing it with the prefix.  ezmlm knows about rfc2047 encoded
+  subject and can detect a prefix within an encoded word. However, ezmlm
+  will not modify the subject itself. It will add a prefix only of none
+  has been added before. A consequence of this is that a message will
+  have the message number prefix of the first message in the thread
+  rather than a prefix with the number of the message itself. The entire
+  thread can always be retrieved with a message to list-thread-x@host,
+  where ``x'' is the number in the prefix.
+
+  We recommend against using the prefix feature and strongly against the
+  message number prefix. If you use it, make sure you understand the
+  drawbacks, of message modification and subjects that change between
+  message and reply.  ezmlm can deal with this, but other programs may
+  not be able to.
+
+  Sublists ignore D\bDI\bIR\bR/\b/p\bpr\bre\bef\bfi\bix\bx.
+
+  If you add a prefix, especially if you previously added it by other
+  means (procmail, etc.), use ezmlm-idx to re-index the archive. Due to
+  the way ezmlm-get(1) does threading from the subject, it works best if
+  you use exactly the same prefix as you did before.
+
+
+  9\b9.\b.3\b3.\b.  A\bAd\bdd\bdi\bin\bng\bg a\ba h\bhe\bea\bad\bde\ber\br t\bto\bo o\bou\but\btg\bgo\boi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Put the exact header text as a line in D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd.  Thus, if you'd
+  like a ``Precedence: bulk'' header added to outgoing messages, put a
+  line ``Precedence: bulk'' into D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd. This particular header
+  is already added via the default ezmlmrc(5). Any modifications you
+  wish to be active for all future lists should be made via modification
+  of ezmlmrc(5) (see ``Customizing ezmlm-make operation'').  As of
+  ezmlm-idx-0.32, the following tags can be used in D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd, and
+  will be substituted: <#n#> for the current message number, <#l#> for
+  the local part of the list (this will be the digest list for digests),
+  <#h#> for the host part of the list name. These substitutions are done
+  at the time of message delivery, in contrast to the ``capital letter''
+  tags substituted by ezmlm-make(1) when the list is set up.
+
+
+  9\b9.\b.4\b4.\b.  A\bAd\bdd\bdi\bin\bng\bg a\ba m\bme\bes\bss\bsa\bag\bge\be n\bnu\bum\bmb\bbe\ber\br h\bhe\bea\bad\bde\ber\br.\b.
+
+  Don't! A sequence header may be useful for users whose systems don't
+  pass on the ``Return-to:'' header to the MUA.
+
+  Use D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd with a header of the type ``X-Sequence: <#n#>''.
+
+  Bounced messages are identified by their local message numbers, i.e.
+  when ezmlm sends you a message about which messages bounced, it refers
+  to the message number of the sublist. To be consistent with these
+  numbers, and a local sublist archive, use D\bDI\bIR\bR/\b/s\bse\beq\bqu\bue\ben\bnc\bce\be on the sublist,
+  not the main list. To get consistent message numbering in digests,
+  digest have the message number of the first message in the digest.
+
+  ezmlm-idx tries to make message numbering problems with sublists a
+  little easier: sublists use the incoming message number, but only when
+  the sublist is not archived and not indexed. This restriction is
+  necessary for security reasons. Otherwise, an attacker could wreak
+  havoc in the local message archive by sending messages with faked
+  message numbers in the SENDER.
+
+  9\b9.\b.5\b5.\b.  R\bRe\bem\bmo\bov\bvi\bin\bng\bg h\bhe\bea\bad\bde\ber\brs\bs f\bfr\bro\bom\bm o\bou\but\btg\bgo\boi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Put the header up to, but excluding the ``:'' in D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\brr\bre\bem\bmo\bov\bve\be.
+
+
+  9\b9.\b.6\b6.\b.  R\bRe\bem\bmo\bov\bvi\bin\bng\bg M\bMI\bIM\bME\bE p\bpa\bar\brt\bts\bs f\bfr\bro\bom\bm m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  ezmlm-idx>=0.30 can strip parts from composite mime messages based on
+  content type. Just put the appropriate content-types such as
+  ``text/ms-word'' or ``text/html'' into D\bDI\bIR\bR/\b/m\bmi\bim\bme\ber\bre\bem\bmo\bov\bve\be. This is
+  automatically configured when using the ezmlm-make(1) ``-x'' switch.
+
+
+  9\b9.\b.7\b7.\b.  L\bLi\bim\bmi\bit\bti\bin\bng\bg `\b``\b`R\bRe\bec\bce\bei\biv\bve\bed\bd:\b:'\b''\b' h\bhe\bea\bad\bde\ber\brs\bs i\bin\bn o\bou\but\btg\bgo\boi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Sendmail still is being used on the majority of mail hubs. Sendmail
+  has very primitive loop detection, bouncing messages based on
+  excessive ``hopcount''.  The ``hopcount'' is determined by counting
+  ``Received:'' headers. ezmlm by default propagates ``Received:''
+  headers to facilitate message tracking. Thus, messages, especially
+  from a sublist, can have a number of ``Received:'' headers that
+  exceeds the ``hopcount'' set on poorly configured sendmail hosts.
+  Subscription confirmation requests, warning, and probe messages have
+  fewer ``Received:'' headers. Thus, a user may be able to receive
+  these, but not (some of the) list messages. Of course, the best is to
+  correct the configuration on the bouncing host, but this is often
+  under the control of neither list owner nor user.
+
+  To compensate for this problem, ezmlm-send(1) of ezmlm-idx->=0.313 by
+  default removes all ``Received:'' headers except the top one.  They
+  are still written to the archive, an can be retrieved from there using
+  the ``-getv'' command.  To cause ezmlm-send(1) to pass on all the
+  ``Received:'' headers, use the ezmlm-send(1) ``-r'' switch.
+
+
+  9\b9.\b.8\b8.\b.  S\bSe\bet\btt\bti\bin\bng\bg `\b``\b`R\bRe\bep\bpl\bly\by-\b-T\bTo\bo:\b: l\bli\bis\bst\bt@\b@h\bho\bos\bst\bt'\b''\b'.\b.
+
+  This is not recommended, since it leads to dissemination via the list
+  of messages returned from bad auto-responders and MTAs. Also, it may
+  lead to public replies to the list where personal replies were
+  intended. In addition, the original ``Reply-To:'' header is lost. If
+  you do want to add a reply-to list header, put ``reply-to'' into
+  D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\brr\bre\bem\bmo\bov\bve\be, and ``Reply-To: list@host.dom'' into D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\bra\bad\bdd\bd.
+
+
+  9\b9.\b.9\b9.\b.  C\bCo\bon\bnf\bfi\big\bgu\bur\bri\bin\bng\bg t\bth\bhe\be l\bli\bis\bst\bt s\bso\bo p\bpo\bos\bst\bts\bs a\bar\bre\be n\bno\bot\bt c\bco\bop\bpi\bie\bed\bd t\bto\bo t\bth\bhe\be o\bor\bri\big\bgi\bin\bna\bal\bl
+  s\bse\ben\bnd\bde\ber\br.\b.
+
+  For most mailing lists, you want all subscribers, including the sender
+  of a particular message, to get all messages. This way, the sender
+  sees that the message reached the list. For small lists, such as a
+  project group, it may be annoying for the members to receive their own
+  posts.
+
+  ezmlm-send(1) can be configured to exclude the sender from the
+  recipient E-mail addresses if configured with the ``-C'' switch. To
+  add this switch, edit the ezmlm-send(1) line of D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br.
+
+
+  9\b9.\b.1\b10\b0.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg e\bez\bzm\bml\blm\bm n\bno\bot\bti\bif\bfi\bic\bca\bat\bti\bio\bon\bn m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Most of ezmlm's more commonly used messages are stored in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/.
+  These messages can be edited manually for a list once it is set up, or
+  on a global basis via modification of e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b).  The messages may
+  also be edited via E-mail by remote administrators (remote admin must
+  also be enabled - ezmlm-make switch ``-r'') after the list is
+  established by creating the list using the ezmlm-make(1) ``-n'' (new
+  text files) (see ``How text file editing works'' and see ``Customizing
+  ezmlm-make operation'').
+
+  The most useful messages are D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/s\bsu\bub\bb-\b-o\bok\bk (and for subscription
+  moderated lists D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/m\bmo\bod\bd-\b-s\bsu\bub\bb) for new subscriber information (such
+  as the traditional ``welcome'' message, or a list charter or list
+  posting rules/guidelines); D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/u\bun\bns\bsu\bub\bb-\b-n\bno\bop\bp is useful for messages
+  to frustrated users unsuccessful in their unsubscribe attempts;
+  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/h\bhe\bel\blp\bp for general help information in reply to list-help@host
+  or unrecognized commands, D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/b\bbo\bot\btt\bto\bom\bm for inclusion at the bottom
+  of virtually all ezmlm messages; D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/m\bmo\bod\bd-\b-h\bhe\bel\blp\bp for moderator
+  information; D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/t\btr\bra\bai\bil\ble\ber\br for a (few) line(s) at the bottom of
+  each post; D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/d\bdi\big\bge\bes\bst\bt for information in the ``Administrivia''
+  section of digests.
+
+
+  9\b9.\b.1\b11\b1.\b.  S\bSp\bpe\bec\bci\bif\bfy\byi\bin\bng\bg c\bch\bha\bar\bra\bac\bct\bte\ber\br s\bse\bet\bt a\ban\bnd\bd c\bco\bon\bnt\bte\ben\bnt\bt-\b-t\btr\bra\ban\bns\bsf\bfe\ber\br-\b-e\ben\bnc\bco\bod\bdi\bin\bng\bg f\bfo\bor\br o\bou\but\bt-\b-
+  g\bgo\boi\bin\bng\bg e\bez\bzm\bml\blm\bm m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  All ezmlm replies, except errors handled directly by qmail, can be
+  sent in any character set and optionally with quoted-printable or
+  base64 content-transfer-encoding. D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ files are always 8-bit
+  files, but even though qmail has no problems with 8-bit mail, other
+  MTAs and MUAs do.  Problems due to this can be avoided by assuring
+  that outgoing ezmlm messages are 7bit by using the appropriate
+  content-transfer-encoding.
+
+  To specify a character set, put the name in D\bDI\bIR\bR/\b/c\bch\bha\bar\brs\bse\bet\bt (default: us-
+  ascii). To specify quoted-printable or base64 content-transfer-
+  encoding, add ``:Q'' or ``:B'' after the character set name in
+  D\bDI\bIR\bR/\b/c\bch\bha\bar\brs\bse\bet\bt.
+
+
+  1\b10\b0.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl.\b.
+
+
+  1\b10\b0.\b.1\b1.\b.  S\bSp\bpe\bec\bci\bif\bfy\byi\bin\bng\bg t\bth\bhe\be f\bfo\bor\brm\bma\bat\bt f\bfo\bor\br r\bre\bet\btr\bri\bie\bev\bve\bed\bd m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Add a format (f) specifier after the archive retrieval command:
+
+
+
+       list-getf@host
+
+
+
+
+  where ``f'' is ``r'' for rfc1153 format, ``m'' (mime; default) for
+  MIME multipart/digest with subset of ordered headers, and ``v'' (vir-
+  gin) MIME multipart/digest, i.e. with all headers retained from the
+  archive, and ``n'' (native) the same as ``v'' except that no threading
+  is performed and messages are returned in numerical order.  Under some
+  circumstances, it may be preferable to have a digest in ``multi-
+  part/mixed''.  The ``x'' (mixed) format is identical to ``m'' except
+  for this header.
+
+  For ezmlm-cron(1), just suffix the format code to the digest code.
+
+
+  1\b10\b0.\b.2\b2.\b.  S\bSp\bpe\bec\bci\bif\bfy\byi\bin\bng\bg t\bth\bhe\be d\bde\bef\bfa\bau\bul\blt\bt f\bfo\bor\brm\bma\bat\bt f\bfo\bor\br d\bdi\big\bge\bes\bst\bts\bs a\ban\bnd\bd a\bar\brc\bch\bhi\biv\bve\be
+  r\bre\bet\btr\bri\bie\bev\bva\bal\bl.\b.
+
+  The ezmlm-get(1) ``-f'' switch can be used to change the default
+  format (MIME with removal of less relevant headers) to other formats.
+  The format specifiers are the same as for individual archive
+  retrievals (see ``Specifying the format for retrieved messages'').
+
+
+  1\b10\b0.\b.3\b3.\b.  L\bLi\bim\bmi\bit\bti\bin\bng\bg t\bth\bhe\be n\bnu\bum\bmb\bbe\ber\br o\bof\bf m\bme\bes\bss\bsa\bag\bge\bes\bs p\bpe\ber\br -\b-g\bge\bet\bt/\b/-\b-i\bin\bnd\bde\bex\bx r\bre\beq\bqu\bue\bes\bst\bt.\b.
+
+  By default, a single -get request returns a maximum of 100 messages,
+  and a single -index request 2000 subjects entries (20 files of 100
+  subjects entries each). This can be changed by editing MAXGET, and
+  MAXINDEX in i\bid\bdx\bx.\b.h\bh and recompiling. Remember to edit t\bte\bex\bxt\bt/\b/b\bbo\bot\btt\bto\bom\bm,
+  t\bte\bex\bxt\bt/\b/b\bbo\bou\bun\bnc\bce\be, and e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) to reflect these changes so that your
+  users won't get confused.
+
+
+  1\b11\b1.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl.\b.
+
+
+  1\b11\b1.\b.1\b1.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg a\bar\brc\bch\bhi\biv\bve\be a\bac\bcc\bce\bes\bss\bs t\bto\bo s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs.\b.
+
+  If you use ezmlm-get(1), archive retrieval can be restricted by using
+  the ezmlm-make(1) ``-g'' (guard archive) switch. This in turn sets
+  ezmlm-get(1) up with its ``-s'' switch, allowing access only to
+  addresses that are subscribers of the list, or of the digest list, or
+  that are present in an extra address database stored in D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/.
+  Addresses can be added remotely by mailing list-allow-
+  useralias=userhost@listhost. Other commands, such as ``subscribe''
+  work as expected. As you can see, the different programs have many
+  options and ezmlm-make(1) organizes most of them into the most useful
+  sets to make it easier. Don't hesitate to look at the ezmlmrc(5) man
+  page and man pages for individual commands. There are many useful
+  options to more finely tune your lists to your taste. Via modification
+  of ezmlmrc(5) you can make your favorite options the default!
+
+  Since ezmlm-get always sends the reply to SENDER, this assures that
+  only subscribers can get archive excerpts. Since SENDER is easily
+  faked, anyone can still request archive info (and drain system
+  resources), but replies go only to subscriber E-mail addresses.  The
+  D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/ database can be used to manually add addresses that should
+  be given archive access, but are not subscribers. This may be an
+  address of a subscriber who posts from an address other than his or
+  her subscription address.
+
+
+  1\b11\b1.\b.2\b2.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg a\bav\bva\bai\bil\bla\bab\bbl\ble\be a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl c\bco\bom\bmm\bma\ban\bnd\bds\bs.\b.
+
+  If you want to disable all archive retrieval except digest creation,
+  simply add the ``-C'' command line switch to the ezmlm-get(1) line in
+  D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. If you don't want digest creation via trigger messages
+  and D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br, but use other means to created digests, you can
+  remove the ezmlm-get(1) line from D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br.
+
+
+  1\b11\b1.\b.3\b3.\b.  R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl t\bto\bo m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs.\b.
+
+  If D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc does not exist, ezmlm-manage(1) and ezmlm-get(1) modify
+  their behavior. They disallow user requests, but for remote
+  administration lists, honor moderator requests.  Thus, for a remote
+  admin list without D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc, only subscription moderators or remote
+  administrators can receive archive retrievals and only remote
+  administrators can subscribe and unsubscribe user addresses.
+
+  If you'd like this restriction of archive retrieval with maintained
+  user-initiated ezmlm-manage(1) subscription functions, use the ezmlm-
+  get(1) ``-P'' (not public) switch, and retain D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc. Also, look
+  at the ezmlm-make ``-b'' switch.
+
+
+  1\b11\b1.\b.4\b4.\b.  A\bAl\bll\blo\bow\bwi\bin\bng\bg a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl f\bfr\bro\bom\bm a\ba n\bno\bon\bn-\b-p\bpu\bub\bbl\bli\bic\bc l\bli\bis\bst\bt.\b.
+
+  A non-public list lacks D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc. ezmlm-manage(1) will reject user
+  requests for (un) subscription and for archive retrieval. The
+  restriction on archive retrieval can be removed with the ezmlm-get(1)
+  ``-p'' (public) switch.
+
+
+  1\b12\b2.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg d\bdi\big\bge\bes\bst\bts\bs.\b.
+
+
+  1\b12\b2.\b.1\b1.\b.  S\bSe\bet\btt\bti\bin\bng\bg u\bup\bp a\ba d\bdi\big\bge\bes\bst\bt l\bli\bis\bst\bt.\b.
+
+  Digests are integrated with normal ezmlm lists if you use ezmlm-
+  idx>=0.30.  Just add the ezmlm-make(1) ``-d'' switch to your list
+  setup. To add digests to an existing list created with ezmlm-idx>=0.23
+  use:
+
+
+               % ezmlm-make -+d DIR
+
+
+
+
+  For ezmlm-0.53 or older lists, you just need to re-specify also other
+  switches and the other ezmlm-make(1) arguments.
+
+
+  1\b12\b2.\b.2\b2.\b.  G\bGe\ben\bne\ber\bra\bat\bti\bin\bng\bg d\bda\bai\bil\bly\by d\bdi\big\bge\bes\bst\bts\bs.\b.
+
+  The easiest way to generate trigger messages is to use crond(8) and
+  execute ezmlm-get(1) daily. To do this, create the list with:
+
+
+               ezmlm-make -d dir dot local host
+
+
+
+
+  and add a line to your crontab file:
+
+
+               30 04 * * * ezmlm-get dir
+
+
+
+
+  and execute crontab(1). This will generate a digest each day at 04:30
+  am. In addition, a digest will be generated at any time when the lat-
+  est post makes it more than 30 messages or more than 64 kbytes of mes-
+  sage body since the latest digest. If you do not want these extra
+  digests, edit D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br and remove the ezmlm-tstdig(1) and ezmlm-
+  get(1) lines.
+
+  If you do not need the digests to go out at a particular time, use the
+  standard setup, but edit D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br to put ``-t 24'' on the ezmlm-
+  tstdig(1) line instead of the default ``-t 48'' for 48 hours. This is
+  even easier.  You can modify all parameters by editing e\bez\bzm\bml\blm\bmr\brc\bc or by
+  using the ezmlm-make(1) ``-4'' argument when creating/editing the
+  list. This is described in the ezmlm-make(1) man page, and the options
+  etc, are described in the ezmlm-tstdig(1) man page.
+
+
+
+
+
+  1\b12\b2.\b.3\b3.\b.  G\bGe\ben\bne\ber\bra\bat\bti\bin\bng\bg t\bth\bhe\be f\bfi\bir\brs\bst\bt d\bdi\big\bge\bes\bst\bt.\b.
+
+  If you want the first digest to start with issue 1 and the first
+  message in your archive, no special action is required.
+
+  If you want the first digest to start at message 123 and you have
+  shell access, put '122' into D\bDI\bIR\bR/\b/d\bdi\big\bgn\bnu\bum\bm.
+
+  If you want the next digest to start at message 456, you can always
+  edit D\bDI\bIR\bR/\b/d\bdi\big\bgn\bnu\bum\bm to contain '455'. If you want the next digest to be
+  named issue 678, put '677' into D\bDI\bIR\bR/\b/d\bdi\big\bgi\bis\bss\bsu\bue\be.
+
+
+  1\b12\b2.\b.4\b4.\b.  A\bAd\bdd\bdi\bin\bng\bg s\bst\bta\ban\bnd\bda\bar\brd\bd a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\biv\bve\be i\bin\bnf\bfo\bor\brm\bma\bat\bti\bio\bon\bn t\bto\bo d\bdi\big\bge\bes\bst\bts\bs.\b.
+
+  The text in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/d\bdi\big\bge\bes\bst\bt is copied into  the ``Administrivia''
+  section of the digest.  This information can be customized on a
+  system-wide basis by editing /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bmr\brc\bc, on a user-wide basis by
+  editing ~\b~/\b/.\b.e\bez\bzm\bml\blm\bmr\brc\bc, or for the list by directly editing the
+  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/d\bdi\big\bge\bes\bst\bt file, or by a remote administrator by editing the file
+  via e-mail, if the list has been set up using the ezmlm-make(1)
+  ``-nr'' switches (see ``How text file editing works'').
+
+
+  1\b12\b2.\b.5\b5.\b.  C\bCo\bon\bnt\btr\bro\bol\bll\bli\bin\bng\bg t\bth\bhe\be d\bdi\big\bge\bes\bst\bt f\bfo\bor\brm\bma\bat\bt.\b.
+
+  You can control the default format that ezmlm-get(1) uses for its
+  output by using the ``-f x'' switch. For individual digests triggered
+  by mail or other archive access, add a format specifier after the
+  digestcode:
+
+
+
+       list-dig.codef@host
+
+
+
+
+  For example:
+
+
+
+       joe-sos-dig.gagax@id.com
+
+
+
+
+  where ``x'' is ``r'' for rfc1153 format, ``m'' (default) for MIME mul-
+  tipart/digest with a subset of headers, ``v'' for virgin MIME multi-
+  part/digest, i.e. with all headers retained from the archive, ``n''
+  produces format similar to ``v'', without threading and with messages
+  in numerical order. The ``x'' format is identical to the default ``m''
+  format, but the digest content-type is ``multipart/alternative''
+  rather than ``multipart/digest''. This helps with a pine bug if you
+  are using quoted-printable/base64 encoding of ezmlm messages.
+
+  With digests triggered directly from crond(8), just use the ``-f''
+  format specifier:
+
+
+               ezmlm-get -fx DIR
+
+
+
+
+  The same switch can also be used for standard digest triggering from
+  D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br. Just add the ``-fx'' switch to the ezmlm-get(1) command
+  line there. Edit ~\b~/\b/e\bez\bzm\bml\blm\bmr\brc\bc to assure that such customizations will be
+  used for future list creations/edits.
+
+
+  1\b12\b2.\b.6\b6.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg b\bbo\bou\bun\bnc\bce\be h\bha\ban\bnd\bdl\bli\bin\bng\bg.\b.
+
+  The time out for bounce messages is normally 11.6 days. This means
+  that a bad address will take longer that 3 weeks to be removed.
+  Usually, this delay is desirable. After all, it is much worse to
+  remove a subscriber just because the address had temporary problems
+  that to send a few extra messages and receive a few extra bounces.
+
+  However, for large lists, bounce handling can consume a considerable
+  amount of resources. To decrease the load, remove all ezmlm-warn(1)
+  lines from the D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br, and D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br files. Instead, execute:
+
+
+       /path/ezmlm-warn DIR
+       /path/ezmlm-warn -d DIR
+
+
+
+
+  daily during off-peak hours via a cron script. The second line can be
+  omitted if you are not using the digest capability of the list.
+
+  This should not be necessary for ezmlm-idx>=0.32. That version adds
+  much more efficient bounce handling, making this type of modification
+  usable only for extremely large lists with many bad addresses (unusual
+  for ezmlm lists) and for hosts that are working near the limit of
+  their capacity (where shifting some qmail load to off-peak hours is
+  worth the effort).
+
+  In addition, you may want to reduce the time out for bounces from 11.6
+  to a lower number of days, e.g. 5. To do so, add ``-t 5'' to the
+  ezmlm-warn(1) command line.
+
+  If you start with a list from a list manager that does not have bounce
+  handling, chances are that you have many bad addresses in your list.
+  You can always execute:
+
+
+       /path/ezmlm-warn -t0 DIR
+       /path/ezmlm-warn -d -t0 DIR
+
+
+
+
+  to move bounce handling one step forward per execution. Users whose
+  mail has bounced will be sent a warning. Users for whom the warning
+  message has bounced will be sent a probe.
+
+
+  1\b13\b3.\b.  R\bRe\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\bio\bon\bn.\b.
+
+
+  1\b13\b3.\b.1\b1.\b.  H\bHo\bow\bw c\bca\ban\bn I\bI r\bre\bem\bmo\bot\bte\bel\bly\by a\bad\bdd\bd m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs,\b, s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br a\bal\bli\bia\bas\bse\bes\bs,\b, e\bet\btc\bc?\b?
+
+  On any list, the D\bDI\bIR\bR/\b/a\bal\bll\blo\bow\bw/\b/ database can be manipulated remotely via
+  mail to list-allow-subscribe@listhost, etc. The rules for
+  adding/removing/listing addresses to this database are the same as for
+  the main list. Thus, if a user on an open list wants to be able to
+  post from alias@al.host.com s/he can send a message to list-allow-
+  subscribe-alias=al.host.com@listhost and reply to the confirmation
+  request. Now, s/he can post from this address even on a subscriber-
+  only list and even though the address is not a real subscriber.
+
+  It can be confusing to some users that you use ``subscribe'' here, but
+  you don't get any messages. If you explain to them that this is just
+  another collection of addresses they will understand. You can also
+  send the initial message on their behalf. If you are a remote admin,
+  you can even complete the transaction adding the alias without
+  subscriber participation.
+
+  Addresses can also be unsubscribed from the ``allow'' database.
+  However, there is usually no good reason to do so.
+
+  If configured, the D\bDI\bIR\bR/\b/d\bde\ben\bny\by/\b/ database can be manipulated, but only by
+  remote administrators, by mail to e.g.  list-deny-
+  baduser=badhost@listhost. Normal users cannot access this database.
+
+  To remotely administrate the D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ databases (i.e., without shell
+  access), you need to set up a non-public, remotely administered list
+  which ``resides'' within the D\bDI\bIR\bR/\b/m\bmo\bod\bd. _\bP_\bl_\be_\ba_\bs_\be _\bc_\ba_\br_\be_\bf_\bu_\bl_\bl_\by _\bc_\bo_\bn_\bs_\bi_\bd_\be_\br _\bt_\bh_\be
+  _\bi_\bm_\bp_\bl_\bi_\bc_\ba_\bt_\bi_\bo_\bn_\bs _\bo_\bf _\bm_\ba_\bk_\bi_\bn_\bg _\bi_\bt _\bp_\bo_\bs_\bs_\bi_\bb_\bl_\be _\bt_\bo _\br_\be_\bm_\bo_\bt_\be_\bl_\by _\ba_\bd_\bd_\b, _\br_\be_\bm_\bo_\bv_\be_\b, _\ba_\bn_\bd _\bl_\bi_\bs_\bt
+  _\bm_\bo_\bd_\be_\br_\ba_\bt_\bo_\br_\bs_\b. _\bI_\bn _\bm_\ba_\bn_\by _\bc_\bi_\br_\bc_\bu_\bm_\bs_\bt_\ba_\bn_\bc_\be_\bs_\b, _\bt_\bh_\bi_\bs _\bi_\bs _\bd_\ba_\bn_\bg_\be_\br_\bo_\bu_\bs_\b.
+
+  After setting up your list with the specific functionality you need,
+  use the following command for D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/:
+
+
+               % ezmlm-make -ePrIAl ~/list/mod ~/.qmail-list-mod joe-list-mod host
+
+
+
+
+  The '-l' flag is not necessary, but makes it easier to administrate
+  your moderator database by permitting the ``supermoderator'' to see
+  who is on the list.
+
+  The new list does not have a key. Using the key from the main list is
+  inadvisable. Instead, create a dummy list, copy the key from this list
+  to your ``moderator'' list:
+
+
+               % cp ~/DUMMY/key ~/DIR/mod/key
+
+
+
+
+  Erase the dummy list. Also, posts to this list should not be allowed.
+  Erase the ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\bt-\b-m\bmo\bod\bd and ~\b~/\b/D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/e\bed\bdi\bit\bto\bor\br.  Then add the remote
+  administrator of the ``moderator'' list:
+
+
+               % ezmlm-sub ~/list/mod/mod supermod@superhost
+
+
+
+
+  The ``supermoderator'' can now remotely administrate the moderators of
+  the main list.
+
+
+  1\b13\b3.\b.2\b2.\b.  M\bMo\bod\bde\ber\bra\bat\bti\bin\bng\bg p\bpo\bos\bst\bts\bs f\bfr\bro\bom\bm a\ba s\bse\bec\bco\bon\bnd\bda\bar\bry\by a\bac\bcc\bco\bou\bun\bnt\bt.\b.
+
+  Request for moderation of posts can be forwarded to any address and
+  acted on from that address. By default, all post moderation requests
+  have subjects starting with ``MODERATE for'' followed by the list
+  name.
+
+  1\b13\b3.\b.3\b3.\b.  M\bMo\bod\bde\ber\bra\bat\bti\bin\bng\bg s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn f\bfr\bro\bom\bm a\ba s\bse\bec\bco\bon\bnd\bda\bar\bry\by a\bac\bcc\bco\bou\bun\bnt\bt.\b.
+
+  Requests for moderator approval of user subscribe requests can be
+  forwarded to any address and acted on from that address.  All
+  subscription moderation requests have subjects starting with
+  ``CONFIRM'' (or ``CONFIRM subscribe to listname'', since ``CONFIRM
+  unsubscribe from listname'' is sent to the moderator only in reply to
+  a moderator-initiated request on a list with remote admin).
+
+  Remote administration (initiation by the moderator of (un)subscribe
+  requests on behalf of a user) CANNOT be initiated from an account that
+  is not listed in the moderator database. If such attempts are made,
+  these will be treated as regular requests, resulting in a confirm
+  request to the user (which includes a copy of the initial request,
+  revealing the moderator's address to the user). The user reply to a
+  confirm request will on a non-moderated list result in the addition of
+  the user address to the subscriber list, and in a moderated list a
+  CONFIRM request to all the moderators. Replies to unsubscribe confirm
+  requests always result in the removal of the address, without
+  moderator intervention (except in some cases when the ezmlm-manage -U
+  switch is used (see below)).  With this caveat, moderation and remote
+  administration can be done from a secondary address.
+
+  For the subscription moderator to temporarily use a different address,
+  s/he needs to forward all ``CONFIRM'' messages to the new address. For
+  a permanent move, it is better to remove the old moderator address and
+  add the new SENDER address to allow moderator-initiated (un)subscribes
+  without user intervention from the new address (of course, the list
+  has to be configured for remote administration with D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be).
+
+
+  1\b13\b3.\b.4\b4.\b.  A\bAu\but\bto\bom\bma\bat\bti\bic\bca\bal\bll\bly\by a\bap\bpp\bpr\bro\bov\bvi\bin\bng\bg p\bpo\bos\bst\bts\bs o\bor\br s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bns\bs.\b.
+
+  Sometimes, it may be desirable for the moderator to automatically
+  approve all moderation requests. This may be appropriate for a single
+  moderator of a ``civilized'' list when away for the week.
+
+  Set up your client to auto-reply to the ``Reply-To:'' address for all
+  messages with subjects ``CONFIRM subscribe to listname'' or ``MODERATE
+  for listname''. Beware that this can be used by malicious people to
+  trick your account to send mail anywhere.  In practice, this should
+  not be a problem.  If you are worried, forward the messages to a
+  (trusted) friend and ask him/her to appropriately reply to the
+  requests.
+
+
+  1\b13\b3.\b.5\b5.\b.  A\bAl\bll\blo\bow\bwi\bin\bng\bg r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bto\bor\brs\bs t\bto\bo g\bge\bet\bt a\ba s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br l\bli\bis\bst\bt.\b.
+
+  Access to the subscriber list is sensitive. Thus, this option is
+  disabled by default. The ezmlm-manage(1) ``-l'' command line switch
+  enables this option, but will send a subscriber list only to a
+  moderator's address. This allows a moderator to also initiate a
+  subscriber list retrieval from a secondary account (i.e.  one to which
+  the moderator's mail is delivered, but for which SENDER is not a
+  moderator). The latter option does not decrease security, as it is
+  trivial to fake SENDER (see ``Ezmlm-idx security'' for a discussion of
+  ezmlm-idx security aspects).
+
+  For maximum subscriber list security, do not enable this feature. To
+  enable this feature by default, just modify e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) (see
+  ``Customizing ezmlm-make operation'').
+
+
+
+
+
+  1\b13\b3.\b.6\b6.\b.  A\bAl\bll\blo\bow\bwi\bin\bng\bg r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bto\bor\brs\bs t\bto\bo r\bre\bet\btr\bri\bie\bev\bve\be o\bor\br s\bse\bea\bar\brc\bch\bh a\ba s\bsu\bub\bb-\b-
+  s\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn l\blo\bog\bg.\b.
+
+  This is restricted and works as the subscriber list, since it contains
+  information of equal sensitivity. To receive the entire log, mail
+  list-log@listhost.  See ``Howto get a subscription log'' for more
+  details on the reply format.  As of ezmlm-idx-0.32, the subscription
+  log also contains the From: line contents from the user's subscribe
+  confirmation. This usually contains the user's name and can be helpful
+  if the user cannot recall or determine the subscription address. To
+  make life easier for the remote admin, ezmlm-idx-0.32 also supports
+  searching the log, using exact matches for alphanumerics and ``_'' as
+  a wild card character. Thus, to find records matching ``Keith John*'',
+  the remote admin can mail list-log.Keith_John.  See ``Howto get a
+  subscription log'' for more information.
+
+
+  1\b13\b3.\b.7\b7.\b.  A\bAl\bll\blo\bow\bwi\bin\bng\bg u\bus\bse\ber\brs\bs t\bto\bo g\bge\bet\bt a\ba s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br l\bli\bis\bst\bt.\b.
+
+  If you want any user to be able to get a subscriber list, you can set
+  up a separate link to D\bDI\bIR\bR/\b/l\bli\bis\bst\bt and then put in a script using ezmlm-
+  list (See ``adding your own commands'' for more info.)  . The authors
+  strongly urge against this, since a common method for spammers to get
+  valid E-mail addresses from mailing lists is to exploit unrestricted
+  -list commands.  A subscriber with questions about who is on the list
+  should contact the list-owner@host. A subscriber wishing to confirm
+  that they are still on the list can just send a message to list-
+  subscribe@listhost, and reply to the confirm request. The following
+  message will be a ``ezmlm response'' if the user was already a
+  subscriber, and a ``WELCOME to listname'' if s/he was not.
+
+
+  1\b13\b3.\b.8\b8.\b.  C\bCh\bha\ban\bng\bgi\bin\bng\bg t\bth\bhe\be t\bti\bim\bme\beo\bou\but\bt f\bfo\bor\br m\bme\bes\bss\bsa\bag\bge\bes\bs i\bin\bn t\bth\bhe\be m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn q\bqu\bue\beu\bue\be.\b.
+
+  Put the time, in hours, into D\bDI\bIR\bR/\b/m\bmo\bod\bdt\bti\bim\bme\be. This value may not exceed
+  the range of 24-120 h set at compile time by the defines in i\bid\bdx\bx.\b.h\bh.
+
+
+  1\b13\b3.\b.9\b9.\b.  F\bFi\bin\bnd\bdi\bin\bng\bg o\bou\but\bt h\bho\bow\bw m\bma\ban\bny\by m\bme\bes\bss\bsa\bag\bge\bes\bs a\bar\bre\be w\bwa\bai\bit\bti\bin\bng\bg f\bfo\bor\br m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn.\b.
+
+
+
+       % ls -l DIR/mod/pending
+
+
+
+
+  and count lines with the owner execute bit set (rwx------).  Others
+  are remnants from failed ezmlm-store runs (ignore - ezmlm-clean(1)
+  will remove them).
+
+  There is currently no way to see this remotely, although you could
+  easily install a script mailing the 'ls' output in response to a
+  message to e.g. l\bli\bis\bst\bt-\b-c\bch\bhk\bkq\bqu\bue\beu\bue\be@\b@h\bho\bos\bst\bt.  (See ezmlm-check(1) and ``adding
+  your own commands'' for examples.)
+
+
+  1\b13\b3.\b.1\b10\b0.\b.  U\bUs\bsi\bin\bng\bg t\bth\bhe\be s\bsa\bam\bme\be m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs f\bfo\bor\br m\bmu\bul\blt\bti\bip\bpl\ble\be l\bli\bis\bst\bts\bs.\b.
+
+  Set up a moderator dir:
+
+
+
+
+
+
+  % mkdir /path/moddir /path/moddir/subscribers
+  % touch /path/moddir/lock
+  % chown -R user /path/moddir
+
+
+
+
+  For alias:
+
+
+
+        # chown -R alias /path/moddir
+
+
+
+
+  For example:
+
+
+
+       % mkdir ~joe/mods ~joe/mods/subscribers
+       % touch ~joe/mods/lock
+
+
+
+
+  Then for the lists, put /\b/p\bpa\bat\bth\bh/\b/m\bmo\bod\bdd\bdi\bir\br into D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb (for moderation
+  of subscribes), D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be (for remote admin if D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb does not
+  exist), and D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt (for moderation of messages).
+
+  For example:
+
+
+
+       % echo "/home/joe/mods" > ~joe/DIR/modsub
+
+
+
+
+  _\bN_\bO_\bT_\bE_\b: The path must start with a '/'.
+
+
+  1\b13\b3.\b.1\b11\b1.\b.  U\bUs\bsi\bin\bng\bg d\bdi\bif\bff\bfe\ber\bre\ben\bnt\bt m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs f\bfo\bor\br m\bme\bes\bss\bsa\bag\bge\be a\ban\bnd\bd s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn m\bmo\bod\bde\ber\br-\b-
+  a\bat\bti\bio\bon\bn.\b.
+
+  Proceed as in the previous point, but set up two different moddirs.
+  Naturally, one of these can be D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ (preferably the one for posts,
+  to keep it cleaner). Then modify the appropriate files (D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt
+  and D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb) to contain absolute paths to the correct moddir.
+
+
+  1\b13\b3.\b.1\b12\b2.\b.  t\bth\bhe\be `\b``\b`s\bsu\bup\bpe\ber\br m\bmo\bod\bde\ber\bra\bat\bto\bor\br'\b''\b' a\bab\bbl\ble\be t\bto\bo a\bad\bdd\bd/\b/r\bre\bem\bmo\bov\bve\be m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs
+  r\bre\bem\bmo\bot\bte\bel\bly\by.\b.  S\bSe\bet\btt\bti\bin\bng\bg u\bup\bp m\bmo\bod\bde\ber\bra\bat\bte\bed\bd l\bli\bis\bst\bts\bs w\bwi\bit\bth\bh t\bth\bhe\be l\bli\bis\bst\bt o\bow\bwn\bne\ber\br a\bas\bs
+
+  This is done by crating a list that has D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ as it's main list
+  directory, then adding the ``super moderator'' to D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/m\bmo\bod\bd/\b/ (see
+  ``remotely adding moderators'').
+
+  If this is a common setup for you, you can write a simple script
+  creating both lists (plus a digest list, if desired) with one simple
+  action (see ezmlm-both(1) for an example).
+
+
+
+
+
+  1\b13\b3.\b.1\b13\b3.\b.  C\bCu\bus\bst\bto\bom\bmi\biz\bzi\bin\bng\bg e\bez\bzm\bml\blm\bm a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\biv\bve\be m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  Subject lines, and other ezmlm output for moderation are controlled by
+  defines in i\bid\bdx\bx.\b.h\bh and by files in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt. To customize these, change
+  i\bid\bdx\bx.\b.h\bh and recompile or for D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt files, edit e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) (see
+  ``Customizing ezmlm-make operation'').
+
+  You can also configure the list to allow remote administrators to edit
+  files in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ via E-mail (see ``How text file editing works'').
+
+
+  1\b13\b3.\b.1\b14\b4.\b.  M\bMa\ban\bnu\bua\bal\bll\bly\by a\bap\bpp\bpr\bro\bov\bvi\bin\bng\bg a\ba m\bme\bes\bss\bsa\bag\bge\be a\baw\bwa\bai\bit\bti\bin\bng\bg m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn.\b.
+
+  All you have to do is to pipe the corresponding message to ``ezmlm-
+  send DIR''. Messages awaiting moderation are kept in D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/p\bpe\ben\bnd\bdi\bin\bng\bg/\b/.
+  To find a particular file, grep the contents.  Thus, to find a file
+  from user@host.dom, try:
+
+
+
+       % grep 'user@host\.dom' DIR/mod/pending/*
+
+
+
+
+  (Depending on your setup, you may not have to escape the period.)
+  Check the files for the owner execute (``x'') bit. It is set on all
+  messages queued successfully. Ignore other files!
+
+  To then accept the message (change the ezmlm-send(1) path if you've
+  installed in a non-default directory):
+
+
+
+       % cat DIR/mod/pending/filename \
+       % /usr/local/bin/ezmlm/ezmlm-send DIR
+
+
+
+
+  Alternatively, use ezmlm-accept(1).  It checks the 'x' bit, ezmlm-
+  send(1) return codes, removes the file, etc.
+
+  For example:
+
+
+
+       % ezmlm-accept ~joe/SOS ~joe/SOS/pending/*
+
+
+
+
+  will accept all messages in the queue of the list in ~\b~j\bjo\boe\be/\b/S\bSO\bOS\bS/\b/.
+
+
+  1\b13\b3.\b.1\b15\b5.\b.  M\bMa\ban\bnu\bua\bal\bll\bly\by r\bre\bej\bje\bec\bct\bti\bin\bng\bg a\ba m\bme\bes\bss\bsa\bag\bge\be a\baw\bwa\bai\bit\bti\bin\bng\bg m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn.\b.
+
+  Simply deleting the file from D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/p\bpe\ben\bnd\bdi\bin\bng\bg/\b/ will do it. If you want
+  to notify the sender, just send him/her an E-mail.  There is an easy
+  way to get ezmlm-idx programs to do it for you: just wait and let
+  ezmlm-clean(1) take care of it for you, once the message has timed out
+  (number of hours settable within 24-240 in D\bDI\bIR\bR/\b/m\bmo\bod\bdt\bti\bim\bme\be; default 120).
+
+
+
+
+  1\b14\b4.\b.  S\bSu\bub\bbl\bli\bis\bst\bts\bs.\b.
+
+  A sublist is a list that receives its input from another mailing list,
+  rather than from users directly. The sublist is just a regular
+  subscriber of the main list. A sublist in e.g. Tasmania is very useful
+  since only one message is sent from the main list and then the
+  sublists servers all subscribers in Tasmania. Bounces and all
+  administration is handled locally. The local sublist can have a
+  digest, even though the main list may not.  (See ``How sublists work''
+  for more info on how sublists work).
+
+
+  1\b14\b4.\b.1\b1.\b.  S\bSu\bub\bbl\bli\bis\bst\bts\bs o\bof\bf e\bez\bzm\bml\blm\bm l\bli\bis\bst\bts\bs.\b.
+
+  To set up a sublist to an ezmlm list, just use the ezmlm-make ``-5
+  mainlist@mainhost'' switch. This will configure your list as a sublist
+  to the mainlist@mainhost mailing list.
+
+
+  1\b14\b4.\b.2\b2.\b.  S\bSu\bub\bbl\bli\bis\bst\bts\bs o\bof\bf n\bno\bon\bn-\b-e\bez\bzm\bml\blm\bm l\bli\bis\bst\bts\bs.\b.
+
+  To set up a sublist to an ezmlm list, just use the ezmlm-make ``-5
+  mainlist@mainhost'' switch. This will configure your list as a sublist
+  to the mainlist@mainhost mailing list. Since the main list may not use
+  the ``Mailing-List'' header, you must identify another header that the
+  main list adds to all messages. See the ezmlm-reject(1) man page for
+  examples. Next, edit D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br of your sublist and add a ``-h
+  _\bL_\bi_\bs_\bt_\bp_\br_\bo_\bc_\be_\bs_\bs_\bo_\br_\b-_\bV_\be_\br_\bs_\bi_\bo_\bn_\b:'' option to the ezmlm-send(1) line, but
+  replacing ``_\bL_\bi_\bs_\bt_\bp_\br_\bo_\bc_\be_\bs_\bs_\bo_\br_\b-_\bV_\be_\br_\bs_\bi_\bo_\bn_\b:'' with your mainlist header.
+
+  Now your list will accept only messages from mainlist@mainhost and
+  with the header specified.
+
+
+  1\b14\b4.\b.3\b3.\b.  H\bHo\bow\bw t\bto\bo s\bse\bet\bt u\bup\bp a\ba c\bcl\blu\bus\bst\bte\ber\br o\bof\bf l\bli\bis\bst\bt a\ban\bnd\bd s\bsu\bub\bbl\bli\bis\bst\bts\bs w\bwi\bit\bth\bh s\bst\bta\ban\bnd\bda\bar\brd\bd
+  d\bda\bat\bta\bab\bba\bas\bse\bes\bs.\b.
+
+  ezmlm-0.53 allows sublists. The difference between a sublist and a
+  main list is that the sublist requires that the SENDER of the message
+  is the main list and that the message has a ``Mailing-List:'' header.
+  Sublist messages have their own subscriber database and subscription
+  mechanism, and use their own message number. This is very convenient
+  if you want to create a private sublist.  Since the subscribers have
+  to interact with the appropriate sublist, it is difficult to
+  administrate if you want to use it to distribute the load of a very
+  large list, since users will have to address administrative requests
+  such as unsubscribe to the correct sublist. Also, bounce messages
+  refer to the sublist archive with sublist message numbers.
+
+  ezmlm-idx modifies this in several ways: First, the message number of
+  the incoming message is used also for the outgoing message so that
+  subscribers see the same message number no matter which sublist they
+  get it from. For security reasons, this is enabled only if the sublist
+  is NOT ARCHIVED. With this feature, bounce messages can refer the user
+  to the main list archive instead, obviating multiple archives.
+
+  Second, ezmlm-split(1) can be used to forward administrative requests
+  sent to the main list, to the appropriate sublist. Thus, subscribers
+  interact only with the main list, and do not need to know which
+  sublist that servers them. With bounce and administrative messages
+  referring them to the main list, subscribers will usually be unaware
+  of the sublisting.
+
+  To set this up:
+
+
+  +\bo
+
+     c\bcr\bre\bea\bat\bte\be t\bth\bhe\be m\bma\bai\bin\bn l\bli\bis\bst\bt
+
+
+                  ezmlm-make dir dot local host
+
+
+
+
+  +\bo
+
+     a\bad\bdd\bd a\ban\bn e\bez\bzm\bml\blm\bm-\b-s\bsp\bpl\bli\bit\bt(\b(1\b1)\b) i\bin\bnv\bvo\boc\bca\bat\bti\bio\bon\bn
+        Before the ezmlm-manage(1) line in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br add:
+
+
+                  |/path/ezmlm-split dir
+
+
+
+
+  +\bo
+
+     d\bde\bec\bci\bid\bde\be h\bho\bow\bw t\bto\bo s\bsp\bpl\bli\bit\bt t\bth\bhe\be l\blo\boa\bad\bd
+        The main list sends to sublists and to any addresses not covered
+        by the split table. You can split the load by domain
+        (``geographically''), and any domain (including '') can be
+        subdivided by ``hash'' by using different parts of the 0-52
+        range. Of course, you can also use hash alone.  The request will
+        go to the first row that matches, so although overlaps are not
+        advisable (in case you later want to add sublists of switch to
+        an SQL server-based system (see ``'')), they have no negative
+        effects. The domain for ezmlm-split can be the last TWO parts,
+        i.e. ``edu.wustl'' to handle all *.wustl.edu subscribers.  This
+        is useful, but remember that the SQL version supports only one
+        level.
+
+        An example:
+
+
+             domain:hash_lo:hash_hi:sublistname
+             edu:0:52:sub1@here.edu
+             com:0:26:sub2@there.net
+             com:27:52:sub3@some.com
+             :0:13:sub4@what.org
+             :14:39:sub5@what.org
+
+
+
+
+     As you can see, the entire ``edu'' domain is handled by
+     sub1@here.edu.  The ``com'' domain is about evenly split between
+     sub2@there.net and sub3@some.com.  Everything else is split so that
+     approximately 1/4 goes to sub4@what.org, 1/2 to sub5@what.org and
+     the rest falls through, i.e. is handled by the main list.
+
+     Why are there 2 sublists on the same host? This is in preparation
+     of adding a host. It easy to just move the entire sub5@what.org
+     list to a new host.  All we have to do it to set up the new list,
+     copy over the subscribers, and change the name in the split table
+     entry.
+
+     To split the split the sub5@what.org load onto 2 lists requires a
+     little more work. First, create a dummy split table in a directory
+     ``temp'':
+
+        :14:26:new1@new.net
+        :27:39:new1@other.net
+
+
+
+
+     Next, split the subscribers of sub5@what.org into these 2 groups,
+     as detailed in the ezmlm-split(1) man page. Create the two new
+     lists, add the respective subscribers, and replace the
+     sub5@what.org line with the two lines above.
+
+     To add a totally new domain, e.g. jp:0:52:sub6@niko.jp requires
+     collection or subscribers from all lists that currently handle
+     these subscribers, (the ones with blank domain in the example), re-
+     splitting them, and adjusting the subscribers. Easiest here is to
+     just unsubscribe the sub6@niko.jp subscribers to be from the other
+     list with ezmlm-sub(1).  Since that program will silently ignore
+     any addresses that are not on the respective list, it will work
+     fine.
+
+  +\bo
+
+     C\bCr\bre\bea\bat\bte\be t\bth\bhe\be s\bsu\bub\bbl\bli\bis\bst\bts\bs
+        Use ezmlmsubrc which sets up a minimal non-archived sublist with
+        bounce texts pointing to the main list:
+
+
+
+                  % ezmlm-make -Cezmlmsubrc -3mainlocal -4mainhost \
+                          DIR dot sub1local sub1host
+
+
+
+
+  +\bo
+
+     s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\be t\bth\bhe\be r\bre\bes\bsp\bpe\bec\bct\bti\biv\bve\be s\bsu\bub\bbl\bli\bis\bst\bts\bs t\bto\bo t\bth\bhe\be m\bma\bai\bin\bn l\bli\bis\bst\bt
+        If you forget, the sublist will not get any messages to
+        distribute. Add these addresses with ezmlm-sub(1) as subscribers
+        to the main list.
+
+  A strong point of this system is that it is relatively simple and that
+  only a fraction of the addresses are available to any given sublist.
+  Thus, compromised security at a sublist threatens only the addresses
+  and functions handled by that sublist.
+
+  As you can see, this works quite well, but it's not trivial to change
+  the setup.  If you modify it while the list is running, some
+  subscribers may get duplicate messages or miss messages. Therefore,
+  you should disable deliveries to the main list before the final step
+  of the changes (removal of subscribers from old lists and adding new
+  lists as subscribers to the main list). For most lists, this should
+  work flawlessly, and some minimal planning and extra lines in
+  ``split'' can markedly facilitate future expansion.
+
+  Another weak point is the authentication of messages between list and
+  sublist.  The requirements the sublist places on the message can be
+  easily faked. This allows injection of messages at the sublist level
+  as a way to circumvent moderation or other access control.
+
+  An associated disadvantage is that not even the main list has access
+  to all the addresses. Thus, SENDER checks for archive access
+  (relatively secure) and posts (relatively insecure) cannot directly be
+  used. Also, sublist cooperation is required to determine the number of
+  subscribers, or to access subscriber addresses for a purpose other
+  than distribution of list messages.
+  1\b15\b5.\b.  M\bMi\big\bgr\bra\bat\bti\bio\bon\bn t\bto\bo E\bEz\bzm\bml\blm\bm f\bfr\bro\bom\bm o\bot\bth\bhe\ber\br M\bMa\bai\bil\bli\bin\bng\bg L\bLi\bis\bst\bt M\bMa\ban\bna\bag\bge\ber\brs\bs.\b.
+
+  This section describes differences and similarities between ezmlm and
+  other mailing list managers. It also details functions of ezmlm-idx
+  that allow you to configure ezmlm to respond to commands utilized by
+  such other mailing list managers so the command syntax will be
+  familiar to such users.  Contributions to complete this sections are
+  welcome.
+
+
+  1\b15\b5.\b.1\b1.\b.  B\bBa\bas\bsi\bic\bc C\bCo\bon\bnc\bce\bep\bpt\bts\bs.\b.
+
+  Ezmlm is different from other mailing list managers in that it is
+  _\bl_\bi_\bs_\bt_\b-_\bc_\be_\bn_\bt_\br_\bi_\bc rather than _\bh_\bo_\bs_\bt_\b-_\bc_\be_\bn_\bt_\br_\bi_\bc. With a _\bl_\bi_\bs_\bt_\b-_\bc_\be_\bn_\bt_\br_\bi_\bc interface,
+  you address the list directly with administrative commands. With
+  ezmlm, the command is embedded in the list address thus becoming part
+  of it (i.e., the ``command address''.)  With smartlist, again you
+  address the list, but send all administrative commands to the list-
+  request address. Ezmlm lists can support this if you use the ezmlm-
+  make(1) ``-q'' switch to configure ezmlm-request(1) in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br.
+
+  Other mailing list managers are _\bh_\bo_\bs_\bt_\b-_\bc_\be_\bn_\bt_\br_\bi_\bc, i.e.  administrative
+  commands for any list on that particular host are addressed to a
+  central address such as majordomo@host, listserv@host, or
+  listproc@host. Then the user is required to place the command in
+  either the subject header or more commonly in the body text of the
+  message. The listname has to be included with the command. [_\bN_\bo_\bt_\be_\b: The
+  above concept is not universally applicable to all host-centric
+  mailing lists.  While intended to to used in a host-centric manner,
+  many such mailing list managers also support listname-request@host
+  addressing. See the applicable list manger documentation for details.
+  Coverage of this aspect of other mailing list manager functionality is
+  beyond the scope of this FAQ.]  To make the migration to ezmlm easier,
+  support for a _\bh_\bo_\bs_\bt_\b-_\bc_\be_\bn_\bt_\br_\bi_\bc style mailing list manger is available.
+  This is based on the use of ezmlm-request(1) with the ``-f
+  c\bco\bon\bnf\bfi\big\bg_\b_f\bfi\bil\ble\be'' switch.
+
+
+  1\b15\b5.\b.2\b2.\b.  S\bSe\bet\btt\bti\bin\bng\bg u\bup\bp e\bez\bzm\bml\blm\bm t\bto\bo r\bre\bes\bsp\bpo\bon\bnd\bd t\bto\bo h\bho\bos\bst\bt-\b-c\bce\ben\bnt\btr\bri\bic\bc c\bco\bom\bmm\bma\ban\bnd\bds\bs.\b.
+
+  ezmlm-request(1) can be used a a ``majordomo/listserv-emulator''. You
+  can create the necessary accessory files manually. However, ezmlm-
+  idx>=0.32 contains ezmlmglrc(5) which makes is very easy for you:
+
+
+               % su
+               # su alias
+               # ezmlm-make -C/usr/local/bin/ezmlmglrc dir dot local host
+
+
+
+
+  where ``local'' may be e.g. ``majordomo''. Even easier is to set it up
+  under a virtual domain ``host'' controlled by a user ``user''. Just
+  put ``user'' in place of ``alias'' in the example.
+
+  If you use a character set other than US-ASCII, put it's name,
+  optionally followed by ``:'' and the desired content-transfer-encoding
+  character (``Q'' for quoted-printable and ``B'' for base64) into
+  e\bez\bzd\bdo\bom\bmo\bo/\b/c\bch\bha\bar\brs\bse\bet\bt.
+
+  All that remains is to set up D\bDI\bIR\bR/\b/e\bez\bzd\bdo\bom\bmo\bo.\b.c\bcf\bf with information on the
+  lists (local and/or remote) that you want to make accessible via this
+  interface. Another script, ezmlm-glconf(1) can help you with this for
+  your local lists. To configure for all your lists:
+
+          ezmlm-glmake ~/ > ~/dir/ezdomo.cf
+
+
+
+
+  See man page for details. Alternatively, do it manually:
+
+  The D\bDI\bIR\bR/\b/e\bez\bzd\bdo\bom\bmo\bo.\b.c\bcf\bf contains a list of mailing lists which the
+  ``majordomo'' (in this case) can provide information about in the
+  following syntax:
+
+
+         list@host:listdir:description
+
+
+
+
+  To show a list in ``lists'', but not include it in a ``which'' search,
+  simply omit the ``listdir'' for that line:
+
+
+         list@host::description
+
+
+
+
+  For the ``which'' command to work, the D\bDI\bIR\bR/\b/, which contains the
+  subscriber database, must be readable by the user under which mail is
+  delivered. This means that ``which'' is usually limited to lists owned
+  by the user or virtual domain under which the ``ezdomo'' interface is
+  set up.
+
+
+  1\b15\b5.\b.3\b3.\b.  C\bCo\bom\bmm\bma\ban\bnd\bds\bs o\bof\bf o\bot\bth\bhe\ber\br m\bma\bai\bil\bli\bin\bng\bgl\bli\bis\bst\bt m\bma\ban\bna\bag\bge\ber\brs\bs r\bre\bec\bco\bog\bgn\bni\biz\bze\bed\bd b\bby\by e\bez\bzm\bml\blm\bm.\b.
+
+
+  1\b15\b5.\b.3\b3.\b.1\b1.\b.  L\bLi\bis\bst\btp\bpr\bro\boc\bc/\b/L\bLi\bis\bst\bts\bse\ber\brv\bv.\b.
+
+  When set up as above, substituting ``listproc'' or ``listserv'' for
+  ``majordomo'' as appropriate, ezmlm will recognize and respond to the
+  following commands placed in the body of the e-mail with the syntax
+  below.  N\bNo\bot\bte\be:\b: e\bez\bzm\bml\blm\bm w\bwi\bil\bll\bl o\bon\bnl\bly\by r\bre\bes\bsp\bpo\bon\bnd\bd t\bto\bo o\bon\bne\be c\bco\bom\bmm\bma\ban\bnd\bd p\bpe\ber\br m\bme\bes\bss\bsa\bag\bge\be.\b.
+
+  s\bsy\byn\bnt\bta\bax\bx:\b: c\bco\bom\bmm\bma\ban\bnd\bd l\bli\bis\bst\btn\bna\bam\bme\be [\b[s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br@\b@h\bho\bos\bst\bt]\b]
+
+
+     S\bSu\bup\bpp\bpo\bor\brt\bte\bed\bd c\bco\bom\bmm\bma\ban\bnd\bds\bs
+        subscribe, sub, unsubscribe, unsub, list, help, review.
+
+     A\bAd\bdd\bdi\bit\bti\bio\bon\bna\bal\bl s\bsu\bup\bpp\bpo\bor\brt\bte\bed\bd c\bco\bom\bmm\bma\ban\bnd\bds\bs
+        All ezmlm commands, such as ``thread'', ``index'' and ``get'' as
+        well as the list owner's commands.
+
+  This interfaced makes information available via command messages to
+  the appropriate mailing list.  Thus, ``list'' and ``review'' will send
+  a subscriber list only to remote administrators and only if
+  specifically allowed by the list owner.
+
+
+  1\b15\b5.\b.3\b3.\b.2\b2.\b.  M\bMa\baj\bjo\bor\brd\bdo\bom\bmo\bo.\b.
+
+  s\bsy\byn\bnt\bta\bax\bx:\b: c\bco\bom\bmm\bma\ban\bnd\bd l\bli\bis\bst\btn\bna\bam\bme\be [\b[s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br@\b@h\bho\bos\bst\bt]\b]
+
+
+     S\bSu\bup\bpp\bpo\bor\brt\bte\bed\bd c\bco\bom\bmm\bma\ban\bnd\bds\bs
+        lists, subscribe, unsubscribe, help, which, who.
+     A\bAd\bdd\bdi\bit\bti\bio\bon\bna\bal\bl s\bsu\bup\bpp\bpo\bor\brt\bte\bed\bd c\bco\bom\bmm\bma\ban\bnd\bds\bs
+        All ezmlm user and ezmlm owner commands.
+
+  This interfaced makes information available via command messages to
+  the appropriate mailing list.  Thus, ``who'' will send a subscriber
+  list only to remote administrators and only if specifically allowed by
+  the list owner.
+
+
+  1\b15\b5.\b.3\b3.\b.3\b3.\b.  S\bSm\bma\bar\brt\btl\bli\bis\bst\bt.\b.
+
+  Unlike ``listproc/listserv'' or ``majordomo'', ``smart-list'' does not
+  provide ``host-centric'' services. Rather, commands are addressed to
+  listname-request@host and the command placed on the ``Subject:'' line:
+
+
+         To: listname-request@host
+         Subject: command [subscriber@host]
+
+
+
+
+  The body of the message is normally ignored.  If the subject is empty,
+  the first body line that starts with a letter is interpreted.
+
+
+     S\bSu\bup\bpp\bpo\bor\brt\bte\bed\bd c\bco\bom\bmm\bma\ban\bnd\bds\bs
+        subscribe, unsubscribe.
+
+     A\bAd\bdd\bdi\bit\bti\bio\bon\bna\bal\bl S\bSu\bup\bpp\bpo\bor\brt\bte\bed\bd C\bCo\bom\bmm\bma\ban\bnd\bds\bs
+        All ezmlm user and ezmlm owner commands.
+
+
+  1\b16\b6.\b.  O\bOp\bpt\bti\bim\bmi\biz\bzi\bin\bng\bg l\bli\bis\bst\bt p\bpe\ber\brf\bfo\bor\brm\bma\ban\bnc\bce\be.\b.
+
+  Ezmlm-idx is designed to make it as easy as possible to set up mailing
+  lists.  The default setup works well for small and medium-sized lists.
+  For large lists, the lists can be made more efficient with a few
+  simple changes.
+
+
+  1\b16\b6.\b.1\b1.\b.  C\bCr\bro\bon\bnd\bd-\b-g\bge\ben\bne\ber\bra\bat\bte\bed\bd d\bdi\big\bge\bes\bst\bts\bs f\bfo\bor\br b\bbe\bet\btt\bte\ber\br p\bpe\ber\brf\bfo\bor\brm\bma\ban\bnc\bce\be.\b.
+
+  With the default setup, ezmlm-tstdig(1) in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br tests if a
+  digest should be sent out. On lists with a lot of traffic this is
+  inefficient.  Also, you may want digests to be delivered as a specific
+  time. To do this, use crond(8) to execute ezmlm-get(1) directly, as
+  described elsewhere.
+
+
+  1\b16\b6.\b.2\b2.\b.  O\bOp\bpt\bti\bim\bmi\biz\bzi\bin\bng\bg e\bex\bxe\bec\bcu\but\bti\bio\bon\bn o\bof\bf e\bez\bzm\bml\blm\bm-\b-w\bwa\bar\brn\bn(\b(1\b1)\b).\b.
+
+  ezmlm-idx>=0.32 comes with much improved bounce handling. Modification
+  as described below should be considered only when you expect thousands
+  of bouncing addresses (virtually never). The description remains, for
+  users of ezmlm-0.53 or earlier versions of ezmlm-idx. For users of
+  ezmlm-0.53 alone, we recommend a patch (
+  <ftp://ftp.id.wustl.edu/pub/patches/ezmlm-return.diff> which fixes a
+  bug in ezmlm-0.53 bounce handling. The patch is superseded by ezmlm-
+  idx.
+
+  To redistribute the load of bounce warning and probe addresses to off-
+  peak hours, you may want to set up the list without ezmlm-warn(1) by
+  using the ezmlm-make ``-w'' switch, and instead execute ``ezmlm-warn
+  DIR'' via crond(8). You also need to run ``ezmlm-warn -d DIR'' for
+  digest bounces if your list is configured with digests. Normal ezmlm
+  list with ezmlm-idx>=0.32 will have an insignificant bounce load,
+  except if you bulk add addresses, e.g. from a MLM without bounce
+  handling. In the latter case, the load will be higher for the first
+  2-4 weeks, then decrease drastically. If you feel you need to run
+  ezmlm-warn(1) from crond(8), you should seriously consider sublisting
+  your lists.
+
+  _\bN_\bo_\bt_\be_\b: the ezmlm-make(1) ``-w'' switch has a special meaning if used at
+  the same time as enabling SQL-support (``-6''; see man pages).
+
+
+  1\b16\b6.\b.3\b3.\b.  D\bDe\bec\bcr\bre\bea\bas\bsi\bin\bng\bg e\bez\bzm\bml\blm\bm-\b-w\bwa\bar\brn\bn t\bti\bim\bme\be o\bou\but\bt t\bto\bo i\bin\bnc\bcr\bre\bea\bas\bse\be p\bpe\ber\brf\bfo\bor\brm\bma\ban\bnc\bce\be.\b.
+
+  With ezmlm-idx, you may alter the ezmlm-warn(1) timeout to a number of
+  seconds with the ``-t seconds'' switch.  The default is 1,000,000
+  seconds or about 11.6 days. This is the time from the first bounce
+  until ezmlm-warn(1) sends a warning message and the time from the
+  warning message bounce until ezmlm-warn(1) sends a probe (which if
+  bounced leads to removal of the address from the subscriber list).  If
+  you have a digest list, remember to execute ezmlm-warn(1) with the
+  ``-d'' switch as well.
+
+  Decreasing the default to e.g. 5 days will cut in half the average
+  number of files in the bounce directory and the number of messages
+  sent at each crond(8)-directed invocation of ezmlm-warn(1). The trade-
+  off is that worst case, a subscriber may be unsubscribed if his/her
+  mail path is defective for more than twice the timeout. Removing a
+  subscriber after 10 days seems reasonable on a busy list. Do this by
+  adding the ``-t'' switch to all the ezmlm-warn(1) invocations. This
+  timeout should be larger than the interval between ezmlm-warn(1)
+  invocation.
+
+  To be aggressive, use ``ezmlm-warn -t0''. This will minimize the time
+  your lists spends servicing bounces, but will for some errors lead to
+  subscribers to be also lead to subscribers being removed if messages
+  to them bounce for two consecutive ezmlm-warn(1) runs. This is useful
+  to rapidly clean up a low quality address collection.
+
+
+  1\b16\b6.\b.4\b4.\b.  U\bUs\bse\be e\bez\bzm\bml\blm\bm w\bwi\bit\bth\bho\bou\but\bt e\bez\bzm\bml\blm\bm-\b-i\bid\bdx\bx f\bfo\bor\br m\bma\bax\bxi\bim\bmu\bum\bm p\bpe\ber\brf\bfo\bor\brm\bma\ban\bnc\bce\be.\b.
+
+  ezmlm-idx adds a number of functions to ezmlm. It indexes the archive,
+  and adds an index entry for each message, it can remove MIME parts, it
+  can add a subject prefix and message trailer, decode rfc2047-encoded
+  subjects, etc.  Although designed to impact minimally on performance,
+  these options when used take time. Even when they are not used, time
+  is spent looking for e.g. the prefix. However, the performance penalty
+  is small, as the absolutely dominating cost of a mailing list is the
+  work qmail does to deliver the messages to subscribers.
+
+  In bench marking, we have not found a significant difference in
+  performance between ezmlm-0.53 and ezmlm-0.53+ezmlm-idx-0.32 when
+  ezmlm-idx features are not used. Thus, a non-indexed list with ezmlm-
+  idx-0.32 performs the same as the corresponding ezmlm-0.53 list.
+  Adding an index adds the overhead of another safe write (the index
+  file). Use of other features adds very marginally to execution time.
+  For virtually all lists, the ezmlm execution time is negligible
+  compared to the resources needed by qmail to disseminate the message
+  to the subscribers.
+
+
+  1\b16\b6.\b.5\b5.\b.  N\bNo\bot\bt a\bar\brc\bch\bhi\biv\bvi\bin\bng\bg t\bto\bo m\bma\bax\bxi\bim\bmi\biz\bze\be p\bpe\ber\brf\bfo\bor\brm\bma\ban\bnc\bce\be.\b.
+
+  An archived list needs to write the message to the archive. If you
+  don't need an archive, don't archive. However, the archive is very
+  useful to allow users to catch up on messages that they didn't receive
+  due to delivery problems.
+
+
+  1\b16\b6.\b.6\b6.\b.  S\bSu\bub\bbl\bli\bis\bst\bts\bs t\bto\bo m\bma\bax\bxi\bim\bmi\biz\bze\be p\bpe\ber\brf\bfo\bor\brm\bma\ban\bnc\bce\be.\b.
+
+  Consider splitting your list into sublists, ideally geographically.
+  The main list deals only with a subset of subscribers (or only the
+  sublists), and each sublist deals with a subset of subscribers,
+  bounces, etc. This is the most rational way to scale ezmlm to large
+  lists (see ``How sublists work'' for more info on how sublists work
+  and ``Sublists'' on how to set up sublists).
+
+
+  1\b17\b7.\b.  M\bMi\bis\bsc\bce\bel\bll\bla\ban\bne\beo\bou\bus\bs.\b.
+
+
+  1\b17\b7.\b.1\b1.\b.  H\bHo\bow\bw d\bdo\bo I\bI q\bqu\bui\bic\bck\bkl\bly\by c\bch\bha\ban\bng\bge\be t\bth\bhe\be p\bpr\bro\bop\bpe\ber\brt\bti\bie\bes\bs o\bof\bf m\bmy\by l\bli\bis\bst\bt?\b?
+
+
+
+               ezmlm-make -+ [changed_switches] dir
+
+
+
+
+  ezmlm-make(1) stores configuration info in D\bDI\bIR\bR/\b/c\bco\bon\bnf\bfi\big\bg and uses that
+  info as the default when you use the ``-+'' switch. If the list was
+  created with a very old version or ezmlm-0.53 ezmlm-make(1) you have
+  to restate all arguments the first time you edit the list.
+
+  The ``-e'' switch works the same, without stickiness for switches.
+
+  A message arriving during reconfiguration may be handled incorrectly.
+  The prudent user will set the sticky bit on the home directory to
+  prevent delivery, then clear it after the list has been changed.
+
+
+  1\b17\b7.\b.2\b2.\b.  O\bOp\bpe\ben\bn a\bar\brc\bch\bhi\biv\bve\bed\bd l\bli\bis\bst\bt w\bwi\bit\bth\bh d\bda\bai\bil\bly\by d\bdi\big\bge\bes\bst\bts\bs.\b.
+
+  This is the default setup. The main list generates digests in response
+  to a mailed request or when a message arrives and the amount of
+  messages since the last digest exceeds set limits (see ezmlm-
+  tstdig(1)).  Alternatively, ezmlm-get(1) can be invoked from the
+  command line. In both cases, the generated digest message is
+  disseminated to the subscribers stored in D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs/\b/,
+  i.e. the subscriber database with the base directory D\bDI\bIR\bR/\b/d\bdi\big\bge\bes\bst\bt/\b/.
+
+  +\bo  See ``setting up a digest list'' on how to set up the lists.
+
+
+  1\b17\b7.\b.3\b3.\b.  V\bVa\bar\bri\bia\bat\bti\bio\bon\bns\bs i\bin\bn m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn
+
+  You can set up lists with combinations of message moderation,
+  subscription moderation, and remote administration, easiest by
+  combining ezmlm-make(1) ``-m'' ,``-s'', and ``-r'' switches. You can
+  use a non-default moderator db, by specifying a directory starting
+  with a slash in D\bDI\bIR\bR/\b/m\bmo\bod\bds\bsu\bub\bb or D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be (for remote admin and
+  subscription moderation - always the same db for both functions) or in
+  D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt for message moderation. You can point several lists to the
+  same moderator db, thus using the same moderators for several lists.
+  _\bN_\bO_\bT_\bE_\b: The user controlling the list must have read/write access to the
+  files (specifically, must be able to write the lock file).
+
+  Some of these setups are not trivial. However, you can make them
+  trivial by modifying ezmlmrc(5) so that ezmlm-make(1) can set up the
+  desired lists by default or when the user uses e.g. the ``-y'' or
+  ``-z'' switches (see ``Customizing ezmlm-make operation'').
+
+
+  1\b17\b7.\b.4\b4.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt a\bal\bll\blo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bn,\b, b\bbu\but\bt n\bno\bot\bt u\bus\bse\ber\br i\bin\bni\bit\bti\bia\bat\bte\bed\bd s\bsu\bub\bbs\bsc\bcr\bri\bip\bp-\b-
+  t\bti\bio\bon\bn o\bor\br a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl.\b.
+
+  Create a regular remote admin list, but remove D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc.  This
+  allows moderators to (un)subscribe users and have archive access, but
+  rejects all user requests. Posts work as usual.  Naturally, this can
+  be combined with message moderation or ezmlm-issub SENDER checks (see
+  ``Restricting message posting to the list'').
+
+
+  1\b17\b7.\b.5\b5.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt a\bal\bll\blo\bow\bw r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bn,\b, u\bus\bse\ber\br a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl,\b, b\bbu\but\bt n\bno\bot\bt
+  u\bus\bse\ber\br-\b-i\bin\bni\bit\bti\bia\bat\bte\bed\bd s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn.\b.
+
+  Create a regular remote admin list, remove D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc, and add the
+  ``-p'' [public] switch to the ezmlm-get(1) command line in
+  D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. This overrides the normal D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc effect on ezmlm-
+  get(1) and archive retrieval, allowing full archive access to anyone,
+  but rejecting user -help and subscription commands.  It is assumed
+  that the users know archive retrieval commands without help. If you
+  want to provide specific help, just link ~\b~/\b/.\b.q\bqm\bma\bai\bil\bl-\b-l\bli\bis\bst\btn\bna\bam\bme\be-\b-h\bhe\bel\blp\bp to
+  D\bDI\bIR\bR/\b/h\bhe\bel\blp\bp, and invoke a script that copies help info from there. See
+  ezmlm-check(1) for an example.
+
+
+  1\b17\b7.\b.6\b6.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt r\bre\bes\bst\btr\bri\bic\bct\bt a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl t\bto\bo s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs.\b.
+
+  Use a standard list, but add the ezmlm-get(1) ``-s'' command line
+  switch in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. Only subscribers can receive archive excerpts.
+  Digests work as usual. This can be set up using the ezmlm-make(1)
+  ``-g'' switch.
+
+
+  1\b17\b7.\b.7\b7.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt d\bdo\bo n\bno\bot\bt a\bal\bll\blo\bow\bw a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl a\bat\bt a\bal\bll\bl.\b.
+
+  Use a standard list, but add the ``-C'' switch to both the ezmlm-
+  get(1) and ezmlm-manage(1) command lines in D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. No archive
+  retrieval commands will be honored. Digest can be created as usual
+  (See ``Restricting archive retrieval'').
+
+
+  1\b17\b7.\b.8\b8.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt d\bdo\bo n\bno\bot\bt a\bal\bll\blo\bow\bw a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl a\ban\bnd\bd d\bdo\bo n\bno\bot\bt a\bal\bll\blo\bow\bw
+  d\bdi\big\bge\bes\bst\bt t\btr\bri\big\bgg\bge\ber\bri\bin\bng\bg p\bpe\ber\br m\bma\bai\bil\bl.\b.
+
+  For maximal archive security, set up a normal indexed and archived
+  list, then remove the ezmlm-get(1) line from D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br and add the
+  ``-C'' switch to the ezmlm-manage(1) command line. You can still
+  create digests by direct invocation of ezmlm-get(1) from a script or
+  crontab entry.
+
+
+  1\b17\b7.\b.9\b9.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt a\bal\bll\blo\bow\bw a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl o\bon\bnl\bly\by t\bto\bo m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs,\b, b\bbu\but\bt
+  a\bal\bll\blo\bow\bw u\bus\bse\ber\br-\b-i\bin\bni\bit\bti\bia\bat\bte\bed\bd s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn.\b.
+
+  Create a normal remote admin (+ subscription moderated) list, and add
+  the ``-P'' (not public) switch to the ezmlm-get(1) command line in
+  D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br. Subscription will not be affected, but ezmlm-get(1) will
+  send archive excerpts only to moderators.  Digests are unaffected.
+
+
+  1\b17\b7.\b.1\b10\b0.\b.  L\bLi\bis\bst\bts\bs t\bth\bha\bat\bt d\bdo\bo n\bno\bot\bt r\bre\beq\bqu\bui\bir\bre\be u\bus\bse\ber\br c\bco\bon\bnf\bfi\bir\brm\bma\bat\bti\bio\bon\bn f\bfo\bor\br (\b(u\bun\bn)\b)s\bsu\bub\bbs\bsc\bcr\bri\bip\bp-\b-
+  t\bti\bio\bon\bn.\b.
+
+
+  The need for a user handshake can be eliminated by the ezmlm-manage(1)
+  ``-S'' (subscribe) and/or ``-U'' (unsubscribe) switches. Alone, this
+  is very insecure. However, there may be some use for it in local lists
+  with subscription moderation, or alone for notifications where ease of
+  use is more important than preventing users from (un)subscribing
+  others. If the list has subscription moderation or remote
+  administration, any user subscribe or unsubscribe request is forwarded
+  to the moderators if the SENDER and target address do not match, even
+  if the ``-U/-S'' switches are specified. This is put in place to make
+  a ``-U/-S'' list similar to other list managers, not for security
+  (it's not secure, since a malicious outsider can easily fake the
+  SENDER address). Unsubscribe confirmations are sent also to the target
+  in this case, to avoid situations where the user needs moderator
+  ``permission'' to get off the list.
+
+
+  1\b17\b7.\b.1\b11\b1.\b.  A\bAn\bnn\bno\bou\bun\bnc\bce\bem\bme\ben\bnt\bt l\bli\bis\bst\bts\bs f\bfo\bor\br a\ba s\bsm\bma\bal\bll\bl s\bse\bet\bt o\bof\bf t\btr\bru\bus\bst\bte\bed\bd p\bpo\bos\bst\bte\ber\brs\bs
+
+  Set up the list with ezmlm-make ``-om'' and add the ``trusted E-mail
+  addresses'' to D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ with
+
+
+       % ezmlm-sub DIR/mod address@host
+
+
+
+
+  A post from a ``trusted address'' is sent back to that address for
+  approval, assuring that the user at that address really sent the post.
+  Posts from other e-mail addresses are rejected.
+
+
+  1\b17\b7.\b.1\b12\b2.\b.  A\bAn\bnn\bno\bou\bun\bnc\bce\bem\bme\ben\bnt\bt l\bli\bis\bst\bts\bs a\bal\bll\blo\bow\bwi\bin\bng\bg m\bmo\bod\bde\ber\bra\bat\bte\bed\bd p\bpo\bos\bst\bts\bs f\bfr\bro\bom\bm a\ban\bny\byo\bon\bne\be.\b.
+
+  This is useful in many circumstances. A list announcing new programs
+  for a system, where both the main developers and other users may have
+  contributed programs.
+
+  Set up the list with ezmlm-make ``-m'' and the main developers as
+  moderators. When any of these posts, that user alone is asked to
+  confirm. Posts from other E-mail addresses are sent to all
+  moderators/developers.  To use a different set of E-mail addresses as
+  ``trusted e-mail addresses'' and moderators for other posts, use the
+  ezmlm-store(1) ``-S'' switch and make a separate address database for
+  the ``trusted E-mail addresses''.  Put the name of the basedir for the
+  ``trusted e-mail addresses'' database in D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt (needs leading
+  ``/''), and add the post moderator(s) to D\bDI\bIR\bR/\b/m\bmo\bod\bd/\b/ using ezmlm-sub(1)
+  as shown above.
+
+
+  1\b17\b7.\b.1\b13\b3.\b.  A\bAn\bnn\bno\bou\bun\bnc\bce\bem\bme\ben\bnt\bt l\bli\bis\bst\bts\bs w\bwi\bit\bth\bh l\ble\bes\bss\bs s\bse\bec\bcu\bur\bri\bit\bty\by a\ban\bnd\bd m\bmo\bor\bre\be c\bco\bon\bnv\bve\ben\bni\bie\ben\bnc\bce\be.\b.
+
+  A general solution for SENDER checking is to configure list with
+  ezmlm-gate(1).  ezmlm-gate(1) takes as arguments any number of
+  basedirs for subscriber lists. Posts from SENDERs that are found are
+  posted. For others ezmlm-store(1) is invoked. If D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt exists,
+  ezmlm-store(1) will send out other messages for moderation.  To bounce
+  such messages, create D\bDI\bIR\bR/\b/m\bmo\bod\bdp\bpo\bos\bst\bt, and use the ezmlm-gate(1) ``-P''
+  switch (will be passed on to ezmlm-store(1) to bounce any posts not
+  from a moderator).
+
+  By default, ezmlm-gate(1) accepts messages from subscribers. However,
+  this is overridden if any ``basedirs'' are put on the ezmlm-gate(1)
+  command line. Common would be to create a address list and put its
+  ``basedir'' on the ezmlm-gate(1) command line. Trusted E-mail
+  addresses can then be added with:
+       % ezmlm-sub basedir trusted@host
+
+
+
+
+  As this relies on SENDER checks it is less secure than the ezmlm-store
+  based confirmation-requiring setup.
+
+
+  1\b18\b8.\b.  E\bEz\bzm\bml\blm\bm-\b-i\bid\bdx\bx c\bco\bom\bmp\bpi\bil\ble\be t\bti\bim\bme\be o\bop\bpt\bti\bio\bon\bns\bs.\b.
+
+
+  1\b18\b8.\b.1\b1.\b.  L\bLo\boc\bca\bat\bti\bio\bon\bn o\bof\bf b\bbi\bin\bna\bar\bri\bie\bes\bs.\b.
+
+  This is configured via c\bco\bon\bnf\bf-\b-b\bbi\bin\bn as for other ezmlm programs.  The
+  default is /\b/u\bus\bsr\br/\b/l\blo\boc\bca\bal\bl/\b/b\bbi\bin\bn/\b/e\bez\bzm\bml\blm\bm.
+
+
+  1\b18\b8.\b.2\b2.\b.  L\bLo\boc\bca\bat\bti\bio\bon\bn o\bof\bf m\bma\ban\bn p\bpa\bag\bge\bes\bs.\b.
+
+  This is configured via c\bco\bon\bnf\bf-\b-m\bma\ban\bn as for other ezmlm programs.  The
+  default is /\b/u\bus\bsr\br/\b/l\blo\boc\bca\bal\bl/\b/m\bma\ban\bn.
+
+
+  1\b18\b8.\b.3\b3.\b.  B\bBa\bas\bse\be d\bdi\bir\bre\bec\bct\bto\bor\bry\by o\bof\bf q\bqm\bma\bai\bil\bl-\b-i\bin\bns\bst\bta\bal\bll\bla\bat\bti\bio\bon\bn.\b.
+
+  This is configured via c\bco\bon\bnf\bf-\b-q\bqm\bma\bai\bil\bl as for other ezmlm programs.  The
+  default is /\b/v\bva\bar\br/\b/q\bqm\bma\bai\bil\bl.
+
+
+  1\b18\b8.\b.4\b4.\b.  S\bSh\bho\bor\brt\bt h\bhe\bea\bad\bde\ber\br t\bte\bex\bxt\bts\bs,\b, e\bet\btc\bc.\b.
+
+  Ezmlm-idx text (short lines, such as ``Administrivia'' for digests),
+  command names, etc, are defined in i\bid\bdx\bx.\b.h\bh, used at compile time. You
+  can change them by changing the defines in this file.
+
+
+  1\b18\b8.\b.5\b5.\b.  A\bAr\brb\bbi\bit\btr\bra\bar\bry\by l\bli\bim\bmi\bit\bts\bs.\b.
+
+  i\bid\bdx\bx.\b.h\bh contains defines for some ezmlm-idx arbitrary limits, such as
+  the maximum number of messages per ``-get'' request. They can be
+  changed here.
+
+
+  1\b18\b8.\b.6\b6.\b.  C\bCo\bom\bmm\bma\ban\bnd\bd n\bna\bam\bme\bes\bs.\b.
+
+  There is support for one alias per user command for
+  internationalization.  (See ``Multiple language support''.)
+
+
+  1\b18\b8.\b.7\b7.\b.  E\bEr\brr\bro\bor\br m\bme\bes\bss\bsa\bag\bge\bes\bs.\b.
+
+  All ezmlm-idx error messages are defines in e\ber\brr\brt\btx\bxt\bt.\b.h\bh, used at compile
+  time. These can be changed for special situations, but we would advise
+  against doing so. If you do for some reason produce such a translated
+  file, we would appreciate if you sent a copy to the authors. NOTE:
+  These do not affect error messages from programs that are not part of
+  the ezmlm-idx package, nor of some subroutines used by ezmlm-idx
+  programs (getconf_line.c comes to mind).
+
+  Hopefully, the error messages for all parts will be synchronized in
+  later versions of ezmlm, and possibly handled from a run-time
+  changeable separate file (maybe as a .cdb database).
+
+
+
+  1\b18\b8.\b.8\b8.\b.  P\bPa\bat\bth\bhs\bs a\ban\bnd\bd o\bot\bth\bhe\ber\br o\bod\bdd\bd c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn i\bit\bte\bem\bms\bs.\b.
+
+  idx.h also has defines for /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bmr\brc\bc, default formats for
+  moderation enclosures, default character set, default digest format,
+  etc. Since most of these items are easily changed at run time, there
+  is usually no need to change the compiled-in defaults. If you do need
+  to, this is where they are.
+
+
+  1\b19\b9.\b.  M\bMu\bul\blt\bti\bip\bpl\ble\be l\bla\ban\bng\bgu\bua\bag\bge\be s\bsu\bup\bpp\bpo\bor\brt\bt.\b.
+
+
+  1\b19\b9.\b.1\b1.\b.  C\bCo\bom\bmm\bma\ban\bnd\bd n\bna\bam\bme\bes\bs.\b.
+
+  ezmlm commands can have aliases for use in translations for non-
+  English use.  Due to the use of commands in mail e-mail addresses, the
+  character set is limited by rfc822 to us-ascii. To enable the command
+  aliases, remove the comment marks around the INTL_CMDS define in
+  idx.h. Also, remove the comments from the define corresponding to one
+  language (currently, only LANG_FR - French) available.
+
+  The INTL_CMDS define results in the compilation of all ezmlm programs
+  with support for alias commands for those commands listed in the INTL
+  section (all that are used directly by users). All aliases MUST be
+  defined, but should be the normal English commands. The language-
+  specific sections un-define and redefine the commands for which
+  alternative names should be used. This allows use of e.g.
+  ``inscription'' as an alias in addition to the standard ``subscribe''.
+
+
+  1\b19\b9.\b.2\b2.\b.  T\bTe\bex\bxt\bt f\bfi\bil\ble\bes\bs.\b.
+
+  Most ezmlm responses are made from text files in D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/. These are
+  created from the template file ``ezmlmrc''. Thanks to Frank Denis, and
+  Masashi Fujita, Wanderlei Antonio Cavassin, Sergiusz Pawlowicz, Frank
+  Tegtmeyer, Torben Fjerdingstad, Jan Kasprzak, and Sebastian Andersson,
+  French, Japanese, Portuguese (var. Brazil), Polish, German, Danish,
+  Czech, and Swedish versions are available. Just:
+
+
+               % make jp
+
+
+
+
+  before
+
+
+               # make setup
+
+
+
+
+  or just copy e\bez\bzm\bml\blm\bmr\brc\bc.\b.j\bjp\bp to /\b/e\bet\btc\bc/\b/e\bez\bzm\bml\blm\bmr\brc\bc, where it will override the
+  copy installed in the ezmlm binary directory. For rpm packages, the
+  en_US version is installed, but the other versions are available in
+  the /\b/u\bus\bsr\br/\b/d\bdo\boc\bc/\b/ hierarchy.
+
+  If you have made an e\bez\bzm\bml\blm\bmr\brc\bc(\b(5\b5)\b) version for another language, please
+  make it public domain and E-mail it as an attachment to
+  lindberg@id.wustl.edu. It will then be put into the e\bez\bzm\bml\blm\bmr\brc\bc directory
+  of the distribution site. Please take advantage of the ``Content-
+  transfer-encoding'' capability of ezmlm-idx>=0.30, if needed, as this
+  avoids problems when messages are sent via non-8-bit MUAs.
+
+
+  Other ezmlm responses, such as words in subject lines, are defines in
+  i\bid\bdx\bx.\b.h\bh and can be changed there. Error messages should ideally not be
+  altered. However, it may make sense to change a few of them which are
+  used as messages to e.g. remote administrators. The defines for all
+  error messages are in e\ber\brr\brt\btx\bxt\bt.\b.h\bh.
+
+
+  1\b19\b9.\b.3\b3.\b.  M\bMu\bul\blt\bti\bi-\b-b\bby\byt\bte\be c\bch\bha\bar\bra\bac\bct\bte\ber\br c\bco\bod\bde\be s\bsu\bup\bpp\bpo\bor\brt\bt.\b.
+
+  ezmlm, as far as we know, places no restrictions on character sets.
+  The configurable default character set allows you to use other
+  character sets for out going ezmlm messages. ezmlm-make does not _\bp_\be_\br
+  _\bs_\be support other character sets. However, any single-byte character
+  set is supported, as long as the us-ascii character sequence ``</''
+  does not occur anywhere as the first characters of the line, and the
+  character sequence ``<#x#>'' (where ``x'' is any number, or A, B, C,
+  D, F, H, L, R, T) does not occur anywhere is text (if it does, it
+  risks being substituted). Also, any occurrence or ``<#A#>'' and
+  ``<#R#>'' that is the first on any text line will be substituted by
+  ezmlm-manage and ezmlm-store. Any occurrence of ``!A'' and ``!R'' as
+  the first characters on a line will be substituted by ezmlm-manage and
+  ezmlm-store.
+
+  For multi-byte character codes, the same restrictions apply.  Thus,
+  ``</'' at the start of a line will confuse ezmlm-make, and any
+  ``<#x#>'' sequence within the text risks substitution. In practice,
+  both of these should be very rare and easily avoidable when setting up
+  an ezmlmrc(5).
+
+
+  2\b20\b0.\b.  S\bSu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br n\bno\bot\bti\bif\bfi\bic\bca\bat\bti\bio\bon\bn o\bof\bf m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn e\bev\bve\ben\bnt\bts\bs.\b.
+
+
+  2\b20\b0.\b.1\b1.\b.  G\bGe\ben\bne\ber\bra\bal\bl o\bop\bpi\bin\bni\bio\bon\bns\bs.\b.
+
+  This is a collection of the authors opinions and an explanation of
+  ezmlm-idx moderation design, which you may or may not agree with.
+
+
+  2\b20\b0.\b.2\b2.\b.  U\bUs\bse\ber\brs\bs s\bsh\bho\bou\bul\bld\bd k\bkn\bno\bow\bw t\bth\bha\bat\bt t\bth\bhe\be l\bli\bis\bst\bt i\bis\bs s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn m\bmo\bod\bde\ber\bra\bat\bte\bed\bd.\b.
+
+  List subscribers should be informed that subscriptions to the list are
+  controlled by a moderator.  ezmlm-idx in its default setup handles
+  this notification during and after the subscribe handshake. Most of
+  this can be disabled by manipulation of the D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/ files.
+
+
+  2\b20\b0.\b.3\b3.\b.  S\bSu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\brs\bs s\bsh\bho\bou\bul\bld\bd k\bkn\bno\bow\bw t\bth\bha\bat\bt p\bpo\bos\bst\bts\bs a\bar\bre\be m\bmo\bod\bde\ber\bra\bat\bte\bed\bd.\b.
+
+  List subscribers should be informed that posts to the list are
+  moderated. ezmlm-idx does this by adding the ``Delivered-To: moderator
+  for ...'' header, but IOHO, the list owner should make the fact of
+  list moderation plain in introductory messages, or other means, to the
+  list subscribers.
+
+
+  2\b20\b0.\b.4\b4.\b.  S\bSe\ben\bnd\bde\ber\brs\bs o\bof\bf p\bpo\bos\bst\bts\bs s\bsh\bho\bou\bul\bld\bd b\bbe\be n\bno\bot\bti\bif\bfi\bie\bed\bd o\bof\bf r\bre\bej\bje\bec\bct\bti\bio\bon\bns\bs.\b.
+
+  With normal use of ezmlm-idx, the sender of a rejected post is
+  notified that the post has been rejected and if the moderators chooses
+  to comment, the sender receives this comment, usually describing why
+  the post was rejected.  This ezmlm behavior cannot be disabled at run
+  time.
+
+  If post are neither accepted or rejected, they time out. ezmlm-
+  clean(1) notifies the sender when this happens. This behavior can be
+  disabled with the ezmlm-clean(1) ``-R'' (not return) switch, which has
+  to be placed on the command line of all invocations of ezmlm-clean(1)
+  (normally in D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br and D\bDI\bIR\bR/\b/m\bmo\bod\bde\ber\bra\bat\bto\bor\br).  If you for some reason do
+  not wish to inform the sender of your editorial decision, you can use
+  this switch and let undesirable posts time out, rather than actively
+  rejecting them. IOHO, it is better to be "above board" and use the
+  normal notification mechanisms, together with active rejection and
+  informative rejection comments.
+
+  The ezmlm-make(1) ``-u'' switch uses moderation in a slightly
+  different way. Here, posts are restricted to subscribers, but posts
+  from non-subscribers are sent to the moderator(s) rather that being
+  ignored. This to help the subscriber that posts from an alias of the
+  subscribed address, or the occasional non-subscriber. In this case it
+  is perfectly acceptable to just ignore non-accepted posts. Thus, using
+  the ezmlm-make(1) ``-u'' switch configures the ezmlm-clean(1)
+  invocations with the ``-R'' switch.
+
+
+  2\b21\b1.\b.  E\bEz\bzm\bml\blm\bm-\b-i\bid\bdx\bx s\bse\bec\bcu\bur\bri\bit\bty\by.\b.
+
+
+  2\b21\b1.\b.1\b1.\b.  G\bGe\ben\bne\ber\bra\bal\bl a\bas\bss\bsu\bum\bmp\bpt\bti\bio\bon\bns\bs.\b.
+
+  This document discusses security aspects of ezmlm-idx addition to the
+  ezmlm-0.53 mailing list manager. This is the authors' understanding of
+  security aspects of ezmlm-idx functions and not to be taken as a
+  warranty. If you find any errors in this document or the ezmlm-idx
+  package in general, please inform the authors.
+
+  In general, ezmlm with or without the ezmlm-idx package is more secure
+  and less resource hungry than most other mailing list managers. Better
+  security than afforded by ezmlm +/- ezmlm-idx would require encryption
+  or PGP/digital signatures. Such an addition would make it difficult,
+  if not impossible, to run the mailing list from a standard MUA. The
+  ezmlm-idx package adds a number of functions and options, which under
+  some conditions may decrease security. The purpose of this document is
+  to discuss security aspects of using/enabling these different
+  functions.
+
+
+  2\b21\b1.\b.2\b2.\b.  S\bSE\bEN\bND\bDE\bER\bR m\bma\ban\bni\bip\bpu\bul\bla\bat\bti\bio\bon\bn.\b.
+
+  We assume that the cost of manipulating/falsifying the SENDER address
+  of a message is zero. Thus, any mechanism relying on SENDER alone is
+  insecure. However, such a mechanism may help in case of simple mailer
+  or user errors. We also assume that the "cookies" used by ezmlm are
+  secure, i.e.  that it is very hard for someone to generate a valid
+  cookie for a given address. SENDER is used to identify a moderator for
+  remote administration of subscriptions. The result of the action or
+  the confirmation request are sent back to that moderator address.
+  Thus, providing a false SENDER is useless, unless the attacker can
+  also read that moderator's mail.
+
+
+  2\b21\b1.\b.3\b3.\b.  e\bez\bzm\bml\blm\bm c\bco\boo\bok\bki\bie\bes\bs.\b.
+
+  Since ezmlm doesn't rely on the SENDER, the security lies entirely
+  within the action-time-cookie-address combination.  Anyone obtaining a
+  valid "combination" can do whatever the combination is meant to do,
+  but nothing else. Also, the cookie times out 1000000 seconds
+  (approximately 11.6 days) after it was issued. Since the
+  "combinations" are specific for a particular action and address, they
+  can only be reused for that particular purpose, and within 11.6 days.
+  Ezmlm (un)subscriptions for a given address are usually pointless to
+  repeat. Message moderation "combinations" are useless after they've
+  been used, since the message is no longer in the moderation queue.
+
+
+  2\b21\b1.\b.4\b4.\b.  L\bLi\bis\bst\bts\bs w\bwi\bit\bth\bho\bou\but\bt r\bre\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bn/\b/s\bsu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn.\b.
+
+  Maliciously (un)subscribing an address with ezmlm-0.53 requires that
+  the attacker is able to read mail sent to the subscription address.
+
+  With the ezmlm-idx add-on, a non-moderated list works exactly the same
+  way. Ezmlm-idx introduces the moderator for moderated and remote admin
+  lists. For any moderator functions, an attacker needs to be able to
+  read mail sent to a moderator's address. If s/he can do this, the
+  attacker can affect anything the moderator is allowed to do (since
+  falsifying SENDER is trivial). To minimize risks, give moderators only
+  the power they need, do not use more moderators than necessary, and
+  use moderators whose mail is hard to intercept (on the same
+  machine/same internal/secure network or by encryption via e.g. ssh).
+
+
+  2\b21\b1.\b.5\b5.\b.  M\bMe\bes\bss\bsa\bag\bge\be m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn.\b.
+
+  A basic message moderated list keeps ezmlm subscriber security, but
+  interpolates the moderator(s) between the address of the list and the
+  list itself. An attacker able to read moderator mail can accept/reject
+  a post, if s/he can do it before a regular moderator has taken action.
+  The potential for abuse can be minimized by using few and local
+  moderators. Mail logs are needed to trace which moderator address was
+  misused.
+
+
+  2\b21\b1.\b.6\b6.\b.  S\bSu\bub\bbs\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn m\bmo\bod\bde\ber\bra\bat\bti\bio\bon\bn.\b.
+
+  A basic subscription moderated list retains ezmlm subscriber security,
+  but adds a moderator handshake. An attacker would need to be able to
+  both read mail to the subscriber address and to at least one
+  moderator.
+
+
+  2\b21\b1.\b.7\b7.\b.  R\bRe\bem\bmo\bot\bte\be a\bad\bdm\bmi\bin\bni\bis\bst\btr\bra\bat\bti\bio\bon\bn.\b.
+
+  A remote admin (-r) list adds the ability of the moderator to
+  (un)subscribe any address. The price of this is that an attacker able
+  to read moderator mail can (un)subscribe any address. The moderator
+  handshake message will be delivered to the abused moderator address,
+  which will alert that moderator and reveal the compromise. Another
+  basic assumption is that action-date-cookie-address combinations are
+  only sent to the target address or a moderator and that moderator
+  action "combinations" are never sent to non-moderators.
+
+
+  2\b21\b1.\b.8\b8.\b.  R\bRe\bem\bmo\bot\bte\be e\bed\bdi\bit\bti\bin\bng\bg o\bof\bf e\bez\bzm\bml\blm\bm t\bte\bex\bxt\bt f\bfi\bil\ble\bes\bs.\b.
+
+  ezmlm-manage(1) can allow remote administrators to edit files in
+  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt.  First, this option is disabled by default. Second, the
+  ``-edit'' command is accepted only when the target (the recipient) is
+  a remote administrator.  Third, only existing files within D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt
+  are editable.  It is not possible to create files.
+
+  ezmlm replies to a valid request with an informative message and the
+  contents of the file. In addition, the ``Reply-To:'' address contains
+  a cookie based on the file name and contents, as well as the current
+  time.  Anyone possessing this cookie can save a new version of the
+  text file. As with other ezmlm security, the security of this process
+  depends on only the remote administrator receiving remote
+  administrator mail. If this is not sufficiently secure for you, do not
+  enable this option. As always, an increase in accessibility results
+  results in a decrease in security.
+
+  Cookies for editing expire in approximately 27 hours. Also, as soon as
+  a file is changed, the cookie is invalidated since the file contents
+  change.  This also means that an outstanding edit request cannot be
+  completed if the files has been updated in the interim.
+
+  A potential attacker obtaining a valid cookie has a window of
+  opportunity while you edit the file, or for at most 27 hours. S/he can
+  overwrite and existing text file with potentially offensive material.
+  Usually, this can be achieved more easily by posting to the list. S/he
+  can also potentially fill your disk with a large amount of data (up to
+  two times 10240 bytes (limited by MAXEDIT in i\bid\bdx\bx.\b.h\bh)) and could put
+  part of this data onto messages leaving the list. Again, this is much
+  more easily achieved by e.g. sending the equivalently sized message to
+  your list.
+
+
+  2\b21\b1.\b.9\b9.\b.  D\bDi\big\bge\bes\bst\bt g\bge\ben\bne\ber\bra\bat\bti\bio\bon\bn a\ban\bnd\bd a\bar\brc\bch\bhi\biv\bve\be r\bre\bet\btr\bri\bie\bev\bva\bal\bl.\b.
+
+  The archive retrieval functions added by ezmlm-idx are digests
+  (protected by a "code") and other functions. Anyone who knows the
+  digest code (through reading mail logs, reading D\bDI\bIR\bR/\b/m\bma\ban\bna\bag\bge\ber\br of the
+  list, or reading any scripts used to send digest triggering messages)
+  can trigger a digest. Protect these locations accordingly!  For
+  default lists with digests triggered from D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br via ezmlm-
+  tstdig(1) and ezmlm-get(1), you do not need the digest code and can
+  thus disable the possibility to trigger digest by mail.  For other
+  functions, the output is sent to SENDER and can be restricted to
+  subscribers (the ``-s'' switch). ezmlm-get(1) functions (apart from
+  digest) can be entirely disabled with the i``-C'' switch, or
+  restricted to moderators with the ``-P'' switch or by removing
+  D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc. Other sections of this document discuss several other
+  options. All switches are documented in the man pages.
+
+  The moderator support functions added by the ezmlm-idx package
+  (extended help and subscriber list) are sent only to a moderator
+  address, i.e. an attacker again needs to be able to read moderator
+  mail to read the output. The help info (D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/m\bmo\bod\bd-\b-h\bhe\bel\blp\bp) should not
+  contain secrets. The ``-list'' function is normally disabled, but can
+  be enabled with the ezmlm-manage -l switch to aid the remote
+  administrator(s).
+
+
+  2\b21\b1.\b.1\b10\b0.\b.  C\bCo\bon\bnv\bve\ben\bni\bie\ben\bnc\bce\be f\bfo\bor\br s\bse\bec\bcu\bur\bri\bit\bty\by:\b: t\bth\bhe\be e\bez\bzm\bml\blm\bm-\b-m\bma\ban\bna\bag\bge\be `\b``\b`-\b-S\bS'\b''\b' a\ban\bnd\bd `\b``\b`-\b-U\bU'\b''\b'
+  s\bsw\bwi\bit\btc\bch\bhe\bes\bs.\b.
+
+  ezmlm-manage(1) functions can be made more convenient, at the expense
+  of security. There have been many requests for these options, so they
+  have been added, although we recommend against using them:
+
+  The ezmlm-manage(1) ``-S'' switch eliminates the subscriber handshake
+  from subscribe requests. Thus, it is no longer necessary for the
+  subscriber to confirm the subscription. This is not secure, but may be
+  convenient for some moderated lists.  Use only with extreme caution.
+  The ezmlm-manage(1) ``-U'' switch similarly eliminates subscriber
+  confirmation from unsubscribe requests. Again, this is insecure and
+  useful only under special circumstances. If the list has any
+  moderators (remote or modsub), requests to (un)subscribe an address
+  other than sender are still routed to a moderator. This is similar to
+  how some other lists work. Naturally, this is insecure because it
+  relies on SENDER.  Unsubscribe requests are always non-moderated,
+  since, IOHO, it seems un-ethical to force a subscriber to remain on a
+  list. Where an unsubscribe confirm request is sent out it is (also)
+  sent to the target, except when the request was initiated by a
+  moderator on a list with remote administration (D\bDI\bIR\bR/\b/r\bre\bem\bmo\bot\bte\be exists).
+  The (un)subscription target is always informed about completed
+  (un)subscribe request, whether initiated by that address, another
+  address, or by a moderator. Thus, attempts of a user or moderator to
+  subscribe an address will be brought to the attention of the user
+  receiving mail at that address.
+
+
+  2\b21\b1.\b.1\b11\b1.\b.  D\bDe\ben\bni\bia\bal\bl o\bof\bf s\bse\ber\brv\bvi\bic\bce\be.\b.
+
+  ezmlm-get(1) archive retrieval functions can be used to deplete system
+  resources. However, this can also be done by posting messages to
+  lists, mail bombing, etc. If you are worried about this, you can use a
+  combination of ezmlm-manage/ezmlm-get ``-C'', ``-s'', and ``-P''
+  switches, removal of D\bDI\bIR\bR/\b/p\bpu\bub\bbl\bli\bic\bc, and removal of the mail-triggered
+  digest function (by removing the digest code from the ezmlm-get(1)
+  command line) to decrease availability of these functions (see man
+  pages). Digest can also be triggered by direct execution of ezmlm-get
+  from within a script from D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br as in the default setup with the
+  ezmlm-make(1) ``-d'' switch.
+
+
+  2\b21\b1.\b.1\b12\b2.\b.  M\bMo\bod\bde\ber\bra\bat\bto\bor\br a\ban\bno\bon\bny\bym\bmi\bit\bty\by.\b.
+
+  Anyone getting messages from the list can see the ``Delivered-To:
+  Moderator for ...'' header and realize that the list is moderated.  In
+  the authors opinion, this is fair and appropriate. If this bothers
+  you, edit the source of e\bez\bzm\bml\blm\bm-\b-s\bst\bto\bor\bre\be.\b.c\bc.
+
+  While the fact that the list is moderated will be disclosed by the
+  headers, the moderator(s)' identity will not be disclosed by the
+  header. Moderators are anonymous to anyone who cannot directly read
+  the mail log, the moderator list, or monitor your outgoing and
+  incoming mail. Anyone intercepting the acting moderators' mail or able
+  to read the mail log can determine who took a particular action.
+
+  Moderator E-mail addresses are not (to our knowledge) disclosed by any
+  ezmlm mechanism. Thus, the poster does not know who rejected/accepted
+  the message. Other moderators can find out that the message was
+  accepted (by seeing it on the list or by themselves committing to a
+  reject/accept reply) or rejected (by being informed by the poster or
+  by themselves committing to a reject/accept reply). If no moderator
+  takes any action for a given time (120 h - configurable to anything
+  24-240 h via D\bDI\bIR\bR/\b/m\bmo\bod\bdt\bti\bim\bme\be - and the parameters are likewise
+  configurable at compile time via i\bid\bdx\bx.\b.h\bh) the message times out, an act
+  for which no particular moderator can be held accountable.
+
+  Subscription requests are acted upon only if a moderator completes the
+  transaction by approving the requests. Requests can not be directly
+  disapproved, but the associated cookie becomes invalid after
+  approximately 11.6 days. Neither the subscriber nor the other
+  moderators know which moderator accepted the subscription request.
+  Requests to unsubscribe from the list are never moderated or otherwise
+  controlled, except by requiring confirmation from the subscriber
+  (normal unsubscribe) or the moderator that initiated the request
+  (remote administration). If several moderators approve the same
+  subscribe request, the user gets multiple notifications.
+
+  The triggering message (the moderation approval or the moderator's
+  completion of the subscription request) are not returned or logged.
+  This protects moderator anonymity, but makes it harder to track down
+  the offender in case of abuse. Only a good mail log will help. IOHO,
+  abuse of these mechanisms requires considerably more effort that it is
+  worth to (un)subscribe someone to a list.  Also, IOHO, moderator
+  anonymity is more important. If this increased difficulty in tracking
+  down abusive behavior bothers you, don't use the remote administration
+  and moderated subscription features.
+  2\b21\b1.\b.1\b13\b3.\b.  C\bCo\bon\bnf\bfi\bid\bde\ben\bnt\bti\bia\bal\bli\bit\bty\by o\bof\bf s\bsu\bub\bbs\bsc\bcr\bri\bib\bbe\ber\br E\bE-\b-m\bma\bai\bil\bl a\bad\bdd\bdr\bre\bes\bss\bse\bes\bs.\b.
+
+  The optional ``-list'' command enabled by the ``-l'' ezmlm-manage(1)
+  command line switch returns a subscriber list to the moderator. Again,
+  anyone who can intercept a moderators' mail can fake SENDER and use
+  this command to obtain a subscriber list. The use of local moderators
+  minimize the risk. If the risk of subscriber disclosure is not worth
+  this convenience, do not enable this feature.
+
+
+  2\b21\b1.\b.1\b14\b4.\b.  H\bHe\bel\blp\bp m\bme\bes\bss\bsa\bag\bge\be f\bfo\bor\br m\bmo\bod\bde\ber\bra\bat\bto\bor\brs\bs.\b.
+
+  ezmlm-manage sends D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/m\bmo\bod\bd-\b-h\bhe\bel\blp\bp, rather than D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/h\bhe\bel\blp\bp in
+  reply to messages to list-help@host if the target address is a
+  moderator.  D\bDI\bIR\bR/\b/t\bte\bex\bxt\bt/\b/m\bmo\bod\bd-\b-h\bhe\bel\blp\bp should not contain secrets or other
+  confidential information.
+
+
+  2\b21\b1.\b.1\b15\b5.\b.  S\bSu\bub\bbl\bli\bis\bst\bts\bs.\b.
+
+  ezmlm sublists require that the message envelope sender is the main
+  list, and that the message has a ``Mailing-List:'' header. Both are
+  easy to fake, allowing an attacker to inject messages at the sublist
+  level. Other than the possible ramifications of only a subset of
+  subscribers seeing the message, this is of no concern for open lists.
+  For a ``subscriber-only'' list based on SENDER checks, it is no harder
+  to set SENDER to the address of a subscriber than to fake the headers
+  required by the sublist. However, for a moderated list the mainlist to
+  sublist communication becomes the weakest link. Sublists using a SQL
+  database also use better authentication in this step (see ``SQL-
+  enabled ezmlm lists'').
+
+  A sublist user can unsubscribe a normal ezmlm sublist from the main
+  list. To guard against this, you need to prevent propagation of
+  unsubscribe confirm requests by the sublist. Easiest is to add a line
+  to D\bDI\bIR\bR/\b/e\bed\bdi\bit\bto\bor\br before the ezmlm-send(1) line:
+
+
+               |grep -i '^Subject: CONFIRM' >/dev/null 2>&1 && exit 99; exit 0
+
+
+
+
+  Another option would be to take advantage of the fact that D\bDI\bIR\bR/\b/h\bhe\bea\bad\bde\ber\br-\b-
+  a\bad\bdd\bd headers at the main list are added to normal messages, but not to
+  administrative messages. Thus, one could discard messages that lack
+  the default ``Precedence: bulk'' header:
+
+
+               |grep -i '^Precedence: bulk' >/dev/null 2>&1 || exit 99; exit 0
+
+
+
+
+  For lists with SQL-support, users cannot unsubscribe sublists (see
+  ``SQL-enabled ezmlm lists'').
+
+  Break-in at a sublist host for normal ezmlm lists leads to
+  loss/compromise of the addresses handled by the sublist. For MySQL-
+  enabled lists, the sublist access credentials give DELETE and SELECT
+  access to all addresses serviced by the list. Thus, a successful
+  sublist attacker can completely disable the list. The MySQL log (if
+  used) will reveal from which host the attack was done. Although the
+  potential damage to a SQL-enabled list is greater, the results are of
+  the same order of magnitude. The risk in minimized by keeping control
+  over all sublist hosts. A successful sublist attacker cannot normally
+  add addresses, since the sublist users by default are set up without
+  INSERT privileges to the address database.
+
+
+  2\b21\b1.\b.1\b16\b6.\b.  S\bSQ\bQL\bL d\bda\bat\bta\bab\bba\bas\bse\bes\bs.\b.
+
+  For SQL-enabled lists, the database contains all list information.
+  Subversion of your database server allows an attacker to add/remove
+  addresses at will.  This is also true for normal ezmlm lists. In
+  addition, modification of the ``*_name'', ``*_cookie'', and ``*_mlog''
+  tables can cause the list to misbehave in a manner that doesn't
+  immediately suggest a security breach.  Keep your ezmlm list and
+  database servers secure.
+
+
+  2\b21\b1.\b.1\b17\b7.\b.  R\bRe\bep\bpo\bor\brt\bti\bin\bng\bg s\bse\bec\bcu\bur\bri\bit\bty\by p\bpr\bro\bob\bbl\ble\bem\bms\bs.\b.
+
+  Please send private E-mail about any security problems with the ezmlm-
+  idx additions to Fred Lindberg, lindberg@id.wustl.edu.  For ezmlm,
+  please send them via private E-mail to Dan J. Bernstein, the author of
+  ezmlm proper.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FILES.idx b/FILES.idx
new file mode 100644 (file)
index 0000000..3c2095d
--- /dev/null
+++ b/FILES.idx
@@ -0,0 +1,165 @@
+CHANGES.idx
+DOWNGRADE.idx
+FAQ.idx
+FILES.idx
+INSTALL.idx
+LICENCE.TXT
+README.idx
+TARGETS
+UPGRADE.idx
+author.c
+auto_cron.h
+case_diffs.c
+case_starts.c
+checktag.c
+concatHDR.c
+conf-cron
+conf-sqlcc
+conf-sqlld
+copy.c
+copy.h
+date2yyyymm.c
+dateline.c
+decodeB.c
+decodeHDR.c
+decodeQ.c
+encodeB.c
+encodeQ.c
+env.c
+errtxt.h
+ezcgi.css
+ezcgirc
+ezmlm-accept.1
+ezmlm-accept.sh
+ezmlm-archive.1
+ezmlm-archive.c
+ezmlm-cgi.1
+ezmlm-cgi.c
+ezmlm-check.1
+ezmlm-check.sh
+ezmlm-clean.1
+ezmlm-clean.c
+ezmlm-cron.1
+ezmlm-cron.c
+ezmlm-gate.1
+ezmlm-gate.c
+ezmlm-get.1
+ezmlm-get.c
+ezmlm-glconf.1
+ezmlm-glconf.sh
+ezmlm-idx.1
+ezmlm-idx.c
+ezmlm-issubn.1
+ezmlm-issubn.c
+ezmlm-limit.1
+ezmlm-limit.c
+ezmlm-list.c
+ezmlm-make.1
+ezmlm-make.c
+ezmlm-manage.1
+ezmlm-manage.c
+ezmlm-mktab
+ezmlm-mktab.1
+ezmlm-moderate.1
+ezmlm-moderate.c
+ezmlm-receipt.1
+ezmlm-receipt.c
+ezmlm-reject.1
+ezmlm-reject.c
+ezmlm-request.1
+ezmlm-request.c
+ezmlm-return.c
+ezmlm-send.c
+ezmlm-split.1
+ezmlm-split.c
+ezmlm-store.1
+ezmlm-store.c
+ezmlm-sub.c
+ezmlm-test.1
+ezmlm-test.sh
+ezmlm-tstdig.1
+ezmlm-tstdig.c
+ezmlm-unsub.c
+ezmlm-warn.c
+ezmlmglrc
+ezmlmglrc.5
+ezmlmrc.5
+ezmlmrc.cs
+ezmlmrc.da
+ezmlmrc.de
+ezmlmrc.en_US
+ezmlmrc.es
+ezmlmrc.fr
+ezmlmrc.id
+ezmlmrc.it
+ezmlmrc.jp
+ezmlmrc.pl
+ezmlmrc.pt_BR
+ezmlmrc.ru
+ezmlmrc.sv
+ezmlmsubrc
+ezmlmsubrc.5
+idx.h
+idx.patch
+idxthread.c
+idxthread.h
+issub.c
+logmsg.c
+makehash.c
+makehash.h
+mime.h
+opensql.c
+putsubs.c
+qmail-qmqpc.tar.gz
+qmail-verh.tar.gz
+qmail.c
+qmail.h
+searchlog.c
+subscribe.c
+subscribe.h
+tagmsg.c
+unfoldHDR.c
+yyyymm.h
+
+sub_mysql:
+README
+checktag.c
+conf-sqlcc
+conf-sqlld
+ezmlm-mktab
+issub.c
+logmsg.c
+opensql.c
+putsubs.c
+searchlog.c
+subscribe.c
+tagmsg.c
+to40x
+
+sub_pgsql:
+README
+checktag.c
+conf-sqlcc
+conf-sqlld
+ezmlm-mktab
+issub.c
+logmsg.c
+opensql.c
+putsubs.c
+searchlog.c
+subscribe.c
+tagmsg.c
+
+sub_std:
+README
+checktag.c
+conf-sqlcc
+conf-sqlld
+ezmlm-mktab
+issub.c
+logmsg.c
+opensql.c
+putsubs.c
+searchlog.c
+subscribe.c
+tagmsg.c
diff --git a/INSTALL.idx b/INSTALL.idx
new file mode 100644 (file)
index 0000000..6a7d899
--- /dev/null
@@ -0,0 +1,274 @@
+$Id: INSTALL.idx,v 1.49 1999/12/24 20:12:57 lindberg Exp $
+$Name: ezmlm-idx-040 $
+
+Like any other piece of software (and information generally), ezmlm-idx
+comes with NO WARRANTY.
+
+This file is for installing ezmlm-idx for the first time on a system
+that may have ezmlm-0.53. If you're already using ezmlm-idx, see
+UPGRADE.idx instead.
+
+Things you have to decide before starting:
+
+   Common for ezmlm-0.53:
+   Put the desired ezmlm bin path into conf-bin. Default "/usr/local/bin/ezmlm",
+   but for e.g. rpm packages it's "/usr/bin". Adjust conf-man accordingly.
+   For installations (e.g. Debian) where qmail is not in "/var/qmail", adjust
+   conf-qmail.
+
+   NOTE: If you follow the test instructions in INSTALL of ezmlm-0.53 after
+   adding ezmlm-idx, step 6 will fail. Before this step, edit
+   ~/testlist/editor and remove the ezmlm-reject line.
+
+HOW TO BUILD, TEST, AND INSTALL:
+
+ 1. Expand the ezmlm-0.53.tar.gz archive. expand the ezmlm-idx-0.xx.tar.gz
+   archive:
+       % zcat ezmlm-0.53.tar.gz | tar -xvf
+       % zcat ezmlm-idx-0.xx.tar.gz | tar -xvf
+
+ 2. Copy the contents of the archive to your ezmlm-0.53 directory.
+       % mv ezmlm-idx-0.xx/* ezmlm-0.53/
+
+ 3. Patch the ezmlm-0.53 source:
+       % cd ezmlm-0.53
+       % patch < idx.patch
+
+    If you patch utility failes with this, get GNU patch.
+    [ezmlm-issubn, an enhanced version of ezmlm-issub is part of this package.
+    The patch for the ezmlm-return bug is also part of this package.]
+
+ 4. If your 'crontab' binary does not live in '/usr/bin' edit 'conf-cron'
+    now to reflect the correct path.
+
+ 5. RDBM Support.
+
+    MySQL:
+    If you want to compile ezmlm with MySQL support (http://www.tcx.se),
+    edit sub_mysql/conf-sqlcc (include files) and mysql/conf-sqlld (libraries)
+    to reflect your MySQL installation (see MySQL documentation). The files
+    are preset for RedHat Linux-i386. On some systems, the ``-lnsl'' should
+    be removed from conf-sqlld. The package has been tested with MySQL 3.22.
+
+    (Programs compiled with MySQL support will work like
+    their non-MySQL counterparts for lists that are not specifically
+    set up to take advantage of MySQL support.) Do:
+       % make mysql
+
+    PostgresSQL:
+    If you want to compile ezmlm with PostgreSQL support 
+    (http://www.postgreSQL.org), edit sub_pgsql/conf-sqlcc (include files) 
+    and pgsql/conf-sqlld (libraries) to reflect your PostgreSQL installation 
+    (see PostgreSQL documentation). Do:
+       % make pgsql
+
+    Others:
+    If you're familiar with C programming for the particular RDBMS, it will
+    take you no more than a few hours to adapt the files in sub_mysql (see
+    docs there). Create a new sub_????, tar and gzip it and send it to
+    lindberg#@id.wustl.edu for inclusion into the package.
+
+ 6. Compile the programs and man pages:
+       % make clean
+       % make; make man
+
+ 7. To use a language other than US English as the default for list texts:
+       % make ISO
+
+    where ``iso'' is the ISO language designation. Currently supported
+    are: cz, da, de, en_US, fr, jp, pl, pt_BR, sv. NOTE: A normal ``make'' sets
+    up the en_US version (as before). ezmlmrc files for your language
+    may be available via ftp://id.wustl.edu/pub/patches/ezmlmrc. If not,
+    please feel free to contribute one (translate ezmlmrc.en_US, but leave
+    comments intact for "diff").
+
+ 8. Test the programs:
+       a. Create a user ``eztest'' or edit ezmlm-test to use another user name.
+          This user should be able to execute the new binaries and also needs
+          to have read access to ezmlm-test (chmod 755 ezmlm-test).
+       b. Change to that user.
+       c. From the build directory, execute ezmlm-test:
+               % ./ezmlm-test
+
+       ezmlm-test will set up a test list, execute the various programs, and
+       test most functions of most programs. It works only if your qmail
+       installation works and allows sending mail to the local user. If you
+       use another user name, add ``-u other_user_name''. NOTE that the
+       arguments must be separated by a space from the switches.
+
+       Occasionally, ezmlm-test fails. This is usually due to problems with
+       ezmlm-test on your particular platform/installation and not due to
+       problems in ezmlm-idx. Please report problems with ezmlm-test, and if
+       you can, patches for correcting it.
+
+
+ 9. To test the SQL functions, set up a mysql database ``ezmlm'' accessible
+    to a user at this host (see MySQL/PostgreSQL docs; the ezmlm-mktab script
+    creates the necessary tables (see man page) but you must first create a
+    database and a user with sufficient access.
+
+    The following command creates a database for use with ezmlm-test.
+    NOTE that ezmlm-mktab and ezmlm-test options must be separated from the
+    switch, whereas the passwd argument for mysql -p must immediately
+    follow the switch.
+       % ./ezmlm-mktab -d list | mysql -hhost -uuser -ppasswd -f ezmlm
+
+    or for PostgresSQL:
+       % ./ezmlm-mktab -d list | pgsql [...] ezmlm
+
+    Now, as the ``eztest'' user, execute:
+       % ./ezmlm-test -l user -p passwd -h host
+
+    This will test the SQL part of the binaries. ``host'' defaults to
+    ``localhost'' and ``user'' defaults to ``ezmlm''. There is no default
+    for passwd and indeed ezmlm-test uses this switch to know to work
+    with SQL support. To execute under a user other than ``eztest'',
+    add a ``-u testuser'' switch. Note that -p has to be specified even if
+    the database has no password. In this case, use -p ''.
+
+10. If you for some reason want to rebuild binaries without MySQL support, do:
+       % make std
+       % make
+
+11. Copy binaries and man pages to the correct locations.
+       # make setup
+       (or copy manually).
+    If you'd like to retest the installation, change uid to the test user
+    ``eztest'' and change to the ezmlm binary directory. Now run ezmlm-test
+    as before.
+
+
+12. Your lists will run as before. To enable ezmlm-idx features like
+    threaded archive access, digest, etc, use:
+
+       % ezmlm-make -e [switches] DIR dot local host
+
+    where ``DIR dot local host'' are the arguments used to create the list,
+    and ``switches'' are desired options (see ezmlm-make man page). Future
+    adjustments can be made with:
+
+       % ezmlm-make -+ [switches] DIR
+
+    where ``switches'' are desired _changes_ from the previous configuration.
+
+------ OPTIONAL ------
+
+13. If you want qmail to add a subscriber-adapted List-Unsubscribe header to
+    outgoing messages, apply the enclosed qmail-verh-0.03.tar.gz patch to
+    qmail-1.03 and follow the documentation in that archive. This is a
+    failsafe way in which to unsubscribe, even if subscriber or list have
+    changed address.
+
+14. If you want to use large lists with custom QMQP servers, apply the
+    qmail-qmqp.tar.gz patch per instructions in the archive. You need this
+    only if you want per-[sub]list control over the QMQP servers used.
+15. (This can be done later if you decide to use ezmlm-cron(1). It is not
+    needed for normal lists and mainly for ``legacy installations''.)
+    The ezmlm-cron(1) program can be run SUID/SGID a special user with crond
+    access. This allows your users to use ezmlm-cron to generate digest
+    trigger messages, without being able to directly use crond. To enable
+    this feature create a special user, e.g. "ezmlm". Then:
+
+       # chown ezmlm /usr/local/bin/ezmlm-cron
+        # chmod 4555 /usr/local/bin/ezmlm-cron
+
+    and create ~ezmlm/ezcronrc as described in the ezmlm-cron(1) man
+    page. You may need to modify the path in the commands above if
+    you have installed ezmlm in a non-default location. ezmlm-cron refuses
+    to run SUID root.
+
+    This user can read its crontab file which may contain digest codes from
+    other users. Thus, this should be a reserved user name, not one of an 
+    ordinary user.
+
+
+16. If you would like to make your archived lists available via the World
+    Wide Web, you must install the ezmlm-cgi program which comes with
+    ezmlm-idx versions starting with ezmlm-idx-0.40. When ezmlm-idx is compiled
+    with the 'make' command, ezmlm-cgi is compiled also, however, it is not
+    installed. Installation of the program allows one to view the archives by
+    date, thread and author.  See, ezmlm-cgi.1 for more details.
+
+17. ezmlm-cgi must be installed where all other common gateway interface
+    ("CGI") programs are installed on your system. For most Un*x based system,
+    this will be in a directory titled 'cgi-bin' which is also, generally
+    speaking, in the root directory for your web server. For example, for apache
+    installations where /usr/local/apache is the root directory for the web
+    server, the directory /usr/local/apache/cgi-bin is where globally availably
+    CGI programs are located. You must copy the ezmlm-cgi program to this
+    location:
+
+       % cp /ezmlm-0.53/ezmlm-cgi /usr/local/apache/cgi-bin
+
+18. ezmlm-cgi should be installed SUID root. Examine the source code to make
+    yourself comfortable that the program is safe. After copying the program to
+    the 'cgi-bin' directory, change the ownerships and permissions as follows:
+
+       % chown root.root ezmlm-cgi
+       % chmod 4755 ezmlm-cgi
+
+    If you are using ezmlm-cgi for a single user, you can install it SUID that
+    user and place the config file (see below) as .ezcgirc in the same directory
+    as the program. If the list archive is readable to the httpd user, you do
+    not have to install it SUID at all (see man page for details).
+
+19. ezmlm-cgi uses a configuration files called 'ezcgirc' which must reside
+    in the /etc/ezmlm directory. First create the directory:
+
+       % mkdir /etc/ezmlm
+
+    Then use your favorite text editor to create the ezcgirc file. 
+
+    The file parameters are set forth on the first line. Comments are
+    allowed if preceded by the '#' in position 1. Lists are input by number
+    which is an arbitrary identifier with the exception of list '0' which is the
+    default list shown on the web page. As an example, the following utilizes a
+    list 'test@example.com' which is owned by the 'alias' user with a UID of
+    7827. The list resides in the directory '/var/qmail/alias/test' and its home
+    page is at 'http://www.example.com/test'. With the foregoing setup, the
+    ezcgirc file's contents are as follows:
+
+# Format for ezcgirc file
+#listno;uid;listdir;listaddr;buttonbar;charset;style;bannerprog
+0;7827;/var/qmail/alias/test;test@example.com;[Home]=http://www.example.com/test
+
+    Note there are no entries for 'charset', 'style' and 'bannerprog' Where
+    no entries are made, the default variables are assumed. The above
+    configuration assumes that the character set 'iso-8859-1' and that no
+    style sheet is used. Since formatting is largely controlled by the
+    style sheet, the output doesn't look exciting on a GUI browser. Start with
+    ezcgi.css in the distribution, and modify to taste. See www.ezmlm.org
+    for URLs to archives using different style sheets/banners.
+
+20. Finally, before accessing the list via the web, you must archive any
+    existing list and add an entry to listdir/editor to archive future posts.
+    You must also run ezmlm-idx (first see man pages for both programs):
+       
+       % ezmlm-idx DIR
+       % ezmlm-archive -c DIR
+
+21. For any existing lists which you would like to archive,
+    add the following line after the call to ezmlm-send in listdir/editor:
+
+       | /usr/local/ezmlm/ezmlm-archive listdir/DIR || exit 0
+
+    This is automatically done when running:
+
+       % ezmlm-make -+i DIR
+
+22. To display your web based archive, open your browser as follows:
+
+       %lynx http://localhost/cgi-bin/ezmlm-cgi
+
+ ------------- End Optional items -----------   
+
+23. That's it! To report success (helps to track platform-specific problems):
+
+       % ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
+         | mail cfl-src@id.wustl.edu
+
+Replace First M. Last with your name.
+
+Send bugs reports, ideally with patch, to 'lindberg@id.wustl.edu'.
+
diff --git a/LICENCE.TXT b/LICENCE.TXT
new file mode 100644 (file)
index 0000000..ee0775e
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software Foundation,
+    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/MAN b/MAN
index df7b2a7..06f2e71 100644 (file)
--- a/MAN
+++ b/MAN
@@ -1,27 +1,67 @@
 d:::755:::
 d:::755:/man1::
 d:::755:/man5::
+c:::644:/man1/:ezmlm-accept.1:
+c:::644:/man1/:ezmlm-archive.1:
+c:::644:/man1/:ezmlm-issubn.1:
 c:::644:/man5/:ezmlm.5:
+c:::644:/man5/:ezmlmrc.5:
+c:::644:/man5/:ezmlmglrc.5:
+c:::644:/man5/:ezmlmsubrc.5:
 c:::644:/man1/:ezmlm-list.1:
+c:::644:/man1/:ezmlm-glconf.1:
 c:::644:/man1/:ezmlm-make.1:
+c:::644:/man1/:ezmlm-mktab.1:
 c:::644:/man1/:ezmlm-manage.1:
+c:::644:/man1/:ezmlm-moderate.1:
 c:::644:/man1/:ezmlm-reject.1:
+c:::644:/man1/:ezmlm-request.1:
 c:::644:/man1/:ezmlm-return.1:
 c:::644:/man1/:ezmlm-send.1:
+c:::644:/man1/:ezmlm-split.1:
+c:::644:/man1/:ezmlm-store.1:
 c:::644:/man1/:ezmlm-sub.1:
 c:::644:/man1/:ezmlm-unsub.1:
 c:::644:/man1/:ezmlm-warn.1:
 c:::644:/man1/:ezmlm-weed.1:
+c:::644:/man1/:ezmlm-idx.1:
+c:::644:/man1/:ezmlm-gate.1:
+c:::644:/man1/:ezmlm-tstdig.1:
+c:::644:/man1/:ezmlm-get.1:
+c:::644:/man1/:ezmlm-check.1:
+c:::644:/man1/:ezmlm-clean.1:
+c:::644:/man1/:ezmlm-limit.1:
+c:::644:/man1/:ezmlm-cron.1:
 d:::755:/cat1::
 d:::755:/cat5::
 c:::644:/cat5/:ezmlm.0:
+c:::644:/cat5/:ezmlmrc.0:
+c:::644:/cat5/:ezmlmglrc.0:
+c:::644:/cat5/:ezmlmsubrc.0:
 c:::644:/cat1/:ezmlm-list.0:
+c:::644:/cat1/:ezmlm-glconf.0:
 c:::644:/cat1/:ezmlm-make.0:
+c:::644:/cat1/:ezmlm-mktab.0:
 c:::644:/cat1/:ezmlm-manage.0:
+c:::644:/cat1/:ezmlm-moderate.0:
+c:::644:/cat1/:ezmlm-request.0:
 c:::644:/cat1/:ezmlm-reject.0:
 c:::644:/cat1/:ezmlm-return.0:
 c:::644:/cat1/:ezmlm-send.0:
+c:::644:/cat1/:ezmlm-store.0:
+c:::644:/cat1/:ezmlm-split.0:
 c:::644:/cat1/:ezmlm-sub.0:
 c:::644:/cat1/:ezmlm-unsub.0:
 c:::644:/cat1/:ezmlm-warn.0:
 c:::644:/cat1/:ezmlm-weed.0:
+c:::644:/cat1/:ezmlm-idx.0:
+c:::644:/cat1/:ezmlm-gate.0:
+c:::644:/cat1/:ezmlm-tstdig.0:
+c:::644:/cat1/:ezmlm-get.0:
+c:::644:/cat1/:ezmlm-check.0:
+c:::644:/cat1/:ezmlm-clean.0:
+c:::644:/cat1/:ezmlm-limit.0:
+c:::644:/cat1/:ezmlm-cron.0:
+c:::644:/cat1/:ezmlm-accept.0:
+c:::644:/cat1/:ezmlm-archive.0:
+c:::644:/cat1/:ezmlm-issubn.0:
index 111a542..e3c7ab3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,14 @@
+#$Id: Makefile,v 1.117 1999/12/23 02:42:12 lindberg Exp $
+#$Name: ezmlm-idx-040 $
 SHELL=/bin/sh
-
+SQLCC=`head -1 conf-sqlcc`
+SQLLD=`head -1 conf-sqlld`
 default: it
 
+clean: \
+TARGETS
+       rm -f `cat TARGETS`
+
 alloc.0: \
 alloc.3
        nroff -man alloc.3 > alloc.0
@@ -18,6 +25,10 @@ alloc_re.o: \
 compile alloc_re.c alloc.h alloc_re.c byte.h alloc_re.c
        ./compile alloc_re.c
 
+author.o: \
+compile author.c mime.h
+       ./compile author.c
+
 auto-ccld.sh: \
 conf-cc conf-ld warn-auto.sh
        ( cat warn-auto.sh; \
@@ -34,6 +45,14 @@ compile auto-str.c substdio.h auto-str.c readwrite.h auto-str.c \
 exit.h auto-str.c
        ./compile auto-str.c
 
+auto_cron.c: \
+auto-str conf-cron
+       ./auto-str auto_cron `head -1 conf-cron` > auto_cron.c
+
+auto_cron.o: \
+compile auto_cron.c
+       ./compile auto_cron.c
+
 auto_bin.c: \
 auto-str conf-bin
        ./auto-str auto_bin `head -1 conf-bin` > auto_bin.c
@@ -79,13 +98,18 @@ case.3
        nroff -man case.3 > case.0
 
 case.a: \
-makelib case_diffb.o case_lowerb.o case_startb.o
-       ./makelib case.a case_diffb.o case_lowerb.o case_startb.o
+makelib case_diffb.o case_diffs.o case_starts.o case_lowerb.o case_startb.o
+       ./makelib case.a case_diffb.o case_lowerb.o case_startb.o \
+       case_diffs.o case_starts.o
 
 case_diffb.o: \
 compile case_diffb.c case.h case_diffb.c
        ./compile case_diffb.c
 
+case_diffs.o: \
+compile case_diffs.c case.h
+       ./compile case_diffs.c
+
 case_lowerb.o: \
 compile case_lowerb.c case.h case_lowerb.c
        ./compile case_lowerb.c
@@ -94,6 +118,15 @@ case_startb.o: \
 compile case_startb.c case.h case_startb.c
        ./compile case_startb.c
 
+checktag.o: \
+compile checktag.c stralloc.h scan.h fmt.h strerr.h cookie.h \
+       errtxt.h subscribe.h conf-sqlcc
+       ./compile checktag.c ${SQLCC}
+
+case_starts.o: \
+compile case_starts.c case.h
+       ./compile case_starts.c
+
 compile: \
 make-compile warn-auto.sh systype
        ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \
@@ -110,6 +143,19 @@ compile cookie.c cookie.h cookie.c str.h cookie.c uint32.h cookie.c \
 surfpcs.h uint32.h surfpcs.h cookie.c
        ./compile cookie.c
 
+copy.o: \
+compile copy.c copy.h stralloc.h substdio.h str.h readwrite.h open.h qmail.h \
+strerr.h getln.h case.h errtxt.h mime.h error.h quote.h
+       ./compile copy.c
+
+date2yyyymm.o:\
+compile date2yyyymm.c yyyymm.h
+       ./compile date2yyyymm.c
+
+dateline.o:\
+compile dateline.c yyyymm.h stralloc.h fmt.h
+       ./compile dateline.c
+
 date822fmt.o: \
 compile date822fmt.c datetime.h date822fmt.c fmt.h date822fmt.c \
 date822fmt.h date822fmt.c
@@ -133,13 +179,46 @@ compile trydrent.c direntry.h1 direntry.h2
        && cat direntry.h2 || cat direntry.h1 ) > direntry.h
        rm -f trydrent.o
 
+concatHDR.o: \
+compile concatHDR.c mime.h stralloc.h strerr.h byte.h errtxt.h
+       ./compile concatHDR.c
+
+decodeB.o: \
+compile decodeB.c mime.h uint32.h stralloc.h strerr.h errtxt.h
+       ./compile decodeB.c
+
+decodeHDR.o: \
+compile decodeHDR.c mime.h stralloc.h strerr.h error.h case.h byte.h \
+uint32.h errtxt.h
+       ./compile decodeHDR.c
+
+decodeQ.o: \
+compile decodeQ.c mime.h stralloc.h strerr.h errtxt.h
+       ./compile decodeQ.c
+
+encodeB.o: \
+compile encodeB.c mime.h uint32.h stralloc.h strerr.h errtxt.h
+       ./compile encodeB.c
+
+encodeQ.o: \
+compile encodeQ.c mime.h stralloc.h strerr.h errtxt.h
+       ./compile encodeQ.c
+
+unfoldHDR.o: \
+compile unfoldHDR.c mime.h stralloc.h strerr.h errtxt.h
+       ./compile unfoldHDR.c
+
 env.0: \
 env.3
        nroff -man env.3 > env.0
 
 env.a: \
-makelib envread.o
-       ./makelib env.a envread.o
+makelib env.o envread.o
+       ./makelib env.a env.o envread.o
+
+env.o: \
+compile env.c env.h str.h
+       ./compile env.c
 
 envread.o: \
 compile envread.c env.h envread.c str.h envread.c
@@ -169,28 +248,281 @@ error_temp.0: \
 error_temp.3
        nroff -man error_temp.3 > error_temp.0
 
+ezmlm-accept: \
+ezmlm-accept.sh warn-auto.sh conf-bin
+       (cat warn-auto.sh; \
+       echo EZPATH=\'`head -1 conf-bin`\'; \
+       cat ezmlm-accept.sh ) > ezmlm-accept
+
+ezmlm-accept.0: \
+ezmlm-accept.1
+       nroff -man ezmlm-accept.1 > ezmlm-accept.0
+
+ezmlm-archive: \
+load ezmlm-archive.o getconf.o slurpclose.o slurp.o getln.a sig.a \
+strerr.a substdio.a stralloc.a alloc.a error.a str.a fs.a open.a \
+lock.a fd.a getopt.a idxthread.o yyyymm.a
+       ./load ezmlm-archive getconf.o slurpclose.o slurp.o getln.a sig.a \
+       idxthread.o yyyymm.a strerr.a substdio.a stralloc.a alloc.a \
+       error.a str.a fs.a open.a lock.a fd.a getopt.a
+
+ezmlm-archive.0: \
+ezmlm-archive.1
+       nroff -man ezmlm-archive.1 > ezmlm-archive.0
+
+ezmlm-archive.o: \
+compile ezmlm-archive.c alloc.h error.h stralloc.h gen_alloc.h str.h \
+sig.h slurp.h getconf.h strerr.h getln.h substdio.h readwrite.h \
+makehash.h fmt.h strerr.h errtxt.h idx.h idxthread.h sgetopt.h subgetopt.h
+       ./compile ezmlm-archive.c
+
+ezmlm-cgi: \
+load ezmlm-cgi.o getconf.o slurpclose.o slurp.o constmap.o getln.a sig.a \
+mime.a strerr.a substdio.a stralloc.a alloc.a error.a str.a fs.a open.a \
+lock.a fd.a getopt.a env.a case.a datetime.o now.o mime.a wait.a yyyymm.a
+       ./load ezmlm-cgi getconf.o slurpclose.o slurp.o constmap.o getln.a \
+       mime.a sig.a env.a case.a datetime.o now.o mime.a wait.a yyyymm.a \
+       strerr.a substdio.a stralloc.a alloc.a error.a str.a fs.a open.a \
+       lock.a fd.a getopt.a
+
+ezmlm-cgi.0: \
+ezmlm-cgi.1
+       nroff -man ezmlm-cgi.1 > ezmlm-cgi.0
+
+ezmlm-cgi.o: \
+compile ezmlm-cgi.c alloc.h error.h stralloc.h gen_alloc.h str.h \
+sig.h slurp.h getconf.h strerr.h getln.h substdio.h readwrite.h env.h \
+makehash.h fmt.h strerr.h errtxt.h idx.h idxthread.h mime.h \
+constmap.h sgetopt.h subgetopt.h datetime.h now.h fork.h wait.h
+       ./compile ezmlm-cgi.c
+
+ezmlm-check: \
+ezmlm-check.sh warn-auto.sh conf-bin
+       (cat warn-auto.sh; \
+       echo EZPATH=\'`head -1 conf-bin`\'; \
+       echo QMPATH=\'`head -1 conf-qmail`\'; \
+       cat ezmlm-check.sh ) > ezmlm-check
+
+ezmlm-check.0: \
+ezmlm-check.1
+       nroff -man ezmlm-check.1 > ezmlm-check.0
+
+ezmlm-clean: \
+load ezmlm-clean.o auto_qmail.o getconf.o copy.o mime.a \
+now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
+getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a surf.a \
+error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a cookie.o getopt.a
+       ./load ezmlm-clean auto_qmail.o getconf.o copy.o mime.a \
+       now.o datetime.o date822fmt.o slurpclose.o \
+       slurp.o qmail.o quote.o getln.a env.a sig.a strerr.a \
+       substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
+       open.a seek.a wait.a lock.a fd.a cookie.o getopt.a surf.a
+
+ezmlm-clean.0: \
+ezmlm-clean.1
+       nroff -man ezmlm-clean.1 > ezmlm-clean.0
+
+ezmlm-clean.o: \
+compile ezmlm-clean.c error.h stralloc.h gen_alloc.h str.h \
+env.h sig.h slurp.h getconf.h strerr.h byte.h getln.h case.h copy.h mime.h \
+qmail.h substdio.h readwrite.h seek.h quote.h datetime.h now.h cookie.h \
+date822fmt.h direntry.h fmt.h strerr.h errtxt.h idx.h sgetopt.h subgetopt.h
+       ./compile ezmlm-clean.c
+
+ezmlm-cron: \
+load ezmlm-cron.o strerr.a stralloc.a alloc.a error.a open.a auto_qmail.o \
+getopt.a getln.a str.a substdio.a sig.a fs.a open.a fd.a lock.a wait.a \
+case.a auto_cron.o
+       ./load ezmlm-cron getopt.a getln.a strerr.a substdio.a \
+       stralloc.a alloc.a sig.a fs.a open.a fd.a lock.a error.a \
+       wait.a case.a str.a auto_qmail.o auto_cron.o
+
+ezmlm-cron.0: \
+ezmlm-cron.1
+       nroff -man ezmlm-cron.1 > ezmlm-cron.0
+
+ezmlm-cron.o: \
+compile ezmlm-cron.c strerr.h substdio.h stralloc.h error.h str.h \
+fork.h readwrite.h wait.h errtxt.h idx.h sgetopt.h auto_qmail.h \
+fmt.h auto_cron.h
+       ./compile ezmlm-cron.c
+
+ezmlm-gate: \
+load ezmlm-gate.o subdb.a auto_bin.o getopt.a getln.a env.a sig.a strerr.a \
+stralloc.a alloc.a error.a str.a case.a wait.a substdio.a open.a lock.a \
+fs.a getconf.o slurpclose.o slurp.o seek.a conf-sqlld
+       ./load ezmlm-gate subdb.a getconf.o slurpclose.o slurp.o \
+       getopt.a getln.a auto_bin.o env.a sig.a fs.a \
+       strerr.a substdio.a stralloc.a alloc.a error.a str.a case.a wait.a \
+       open.a lock.a seek.a ${SQLLD}
+
+ezmlm-gate.0: \
+ezmlm-gate.1
+       nroff -man ezmlm-gate.1 > ezmlm-gate.0
+
+ezmlm-gate.o: \
+compile ezmlm-gate.c idx.h errtxt.h subscribe.h auto_bin.h \
+sgetopt.h subgetopt.h substdio.h getconf.h \
+env.h sig.h strerr.h stralloc.h alloc.h error.h str.h case.h \
+fork.h wait.h exit.h getln.h open.h
+       ./compile ezmlm-gate.c
+
+ezmlm-get: \
+load ezmlm-get.o idxthread.o subdb.a auto_qmail.o getopt.a now.o getconf.o \
+datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o makehash.o \
+cookie.o surf.a yyyymm.a \
+constmap.o getln.a env.a sig.a strerr.a substdio.a mime.a stralloc.a alloc.a \
+error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a copy.o conf-sqlld
+       ./load ezmlm-get idxthread.o subdb.a auto_qmail.o getopt.a getconf.o \
+       now.o datetime.o date822fmt.o cookie.o makehash.o slurpclose.o slurp.o \
+       yyyymm.a \
+       constmap.o substdio.a copy.o mime.a strerr.a stralloc.a alloc.a \
+       qmail.o quote.o surf.a getln.a env.a sig.a \
+       error.a str.a fs.a case.a \
+       open.a seek.a wait.a lock.a fd.a ${SQLLD}
+
+ezmlm-get.o: \
+compile ezmlm-get.c idx.h errtxt.h error.h getconf.h stralloc.h gen_alloc.h \
+str.h cookie.h env.h sig.h slurp.h strerr.h byte.h getln.h case.h qmail.h \
+substdio.h readwrite.h seek.h quote.h sgetopt.h subgetopt.h datetime.h now.h \
+date822fmt.h fmt.h strerr.h copy.h errtxt.h idx.h idxthread.h mime.h \
+constmap.h makehash.h
+       ./compile ezmlm-get.c
+
+ezmlm-get.0: \
+ezmlm-get.1
+       nroff -man ezmlm-get.1 > ezmlm-get.0
+
+ezmlm-greturn: \
+load ezmlm-greturn.o quote.o getconf.o subdb.a log.o \
+slurpclose.o slurp.o now.o cookie.o surf.a lock.a env.a sig.a \
+strerr.a getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a \
+case.a open.a conf-sqlld
+       ./load ezmlm-greturn quote.o getconf.o subdb.a \
+       log.o slurpclose.o slurp.o now.o cookie.o surf.a lock.a \
+       env.a sig.a strerr.a getln.a substdio.a stralloc.a alloc.a \
+       error.a str.a fs.a case.a open.a ${SQLLD}
+
+ezmlm-greturn.0: \
+ezmlm-greturn.1
+       nroff -man ezmlm-greturn.1 > ezmlm-greturn.0
+
+ezmlm-greturn.o: \
+compile ezmlm-greturn.c stralloc.h gen_alloc.h stralloc.h str.h env.h sig.h \
+slurp.h getconf.h strerr.h byte.h case.h getln.h substdio.h error.h \
+quote.h readwrite.h fmt.h datetime.h now.h cookie.h \
+strerr.h subscribe.h
+       ./compile ezmlm-greturn.c
+
+ezmlm-gwarn: \
+load ezmlm-gwarn.o auto_qmail.o getconf.o mime.a cookie.o subdb.a now.o \
+slurpclose.o slurp.o quote.o datetime.o date822fmt.o qmail.o surf.a \
+case.a strerr.a sig.a getln.a substdio.a stralloc.a alloc.a error.a \
+open.a lock.a str.a fs.a fd.a wait.a copy.o getopt.a conf-sqlld
+       ./load ezmlm-gwarn auto_qmail.o getconf.o mime.a \
+       cookie.o subdb.a getopt.a \
+       now.o slurpclose.o slurp.o quote.o datetime.o date822fmt.o \
+       qmail.o surf.a case.a strerr.a sig.a getln.a substdio.a \
+       stralloc.a alloc.a error.a open.a lock.a str.a fs.a fd.a \
+       wait.a copy.o ${SQLLD}
+
+ezmlm-gwarn.0: \
+ezmlm-gwarn.1
+       nroff -man ezmlm-gwarn.1 > ezmlm-gwarn.0
+
+ezmlm-gwarn.o: \
+compile ezmlm-gwarn.c direntry.h readwrite.h getln.h \
+substdio.h stralloc.h gen_alloc.h slurp.h getconf.h byte.h error.h str.h \
+sig.h now.h datetime.h  date822fmt.h fmt.h cookie.h qmail.h substdio.h \
+qmail.h copy.h mime.h idx.h errtxt.h sgetopt.h subgetopt.h
+       ./compile ezmlm-gwarn.c
+
+ezmlm-idx: \
+load ezmlm-idx.o slurp.o slurpclose.o mime.a wait.a getopt.a \
+getln.a strerr.a sig.h sig.a open.a lock.a substdio.a stralloc.a \
+alloc.a error.a str.a fd.a case.a fs.a getconf.o makehash.o surf.o mime.a
+       ./load ezmlm-idx \
+       mime.a slurp.o slurpclose.o wait.a getln.a strerr.a sig.a open.a \
+       lock.a mime.a substdio.a stralloc.a alloc.a error.a str.a fd.a \
+       getopt.a case.a fs.a getconf.o makehash.o surf.o
+
+ezmlm-idx.o: \
+compile ezmlm-idx.c stralloc.h getconf.h \
+substdio.h subfd.h strerr.h error.h sgetopt.h \
+lock.h sig.h slurp.h open.h getln.h case.h \
+str.h fmt.h readwrite.h exit.h idx.h mime.h errtxt.h uint32.h
+       ./compile ezmlm-idx.c
+
+ezmlm-idx.0: \
+ezmlm-idx.1
+       nroff -man ezmlm-idx.1 > ezmlm-idx.0
+
+ezmlm-glconf: \
+ezmlm-glconf.sh warn-auto.sh conf-bin
+       (cat warn-auto.sh; \
+       echo EZPATH=\'`head -1 conf-bin`\'; \
+       cat ezmlm-glconf.sh ) > ezmlm-glconf
+
+ezmlm-glconf.0: \
+ezmlm-glconf.1
+       nroff -man ezmlm-glconf.1 > ezmlm-glconf.0
+
+ezmlm-issubn: \
+load ezmlm-issubn.o subdb.a getconf.o slurpclose.o slurp.o \
+env.a fs.a strerr.a getln.a getopt.a conf-sqlld \
+substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
+       ./load ezmlm-issubn subdb.a getconf.o slurpclose.o slurp.o \
+       getopt.a env.a fs.a strerr.a \
+       getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+       open.a lock.a ${SQLLD}
+
+ezmlm-issubn.0: \
+ezmlm-issubn.1
+       nroff -man ezmlm-issubn.1 > ezmlm-issubn.0
+
+ezmlm-issubn.o: \
+compile ezmlm-issubn.c strerr.h subscribe.h env.h errtxt.h sgetopt.h idx.h
+       ./compile ezmlm-issubn.c
+
+ezmlm-limit: \
+load ezmlm-limit.o getconf.o slurpclose.o slurp.o substdio.a stralloc.a \
+alloc.a error.a str.a case.a open.a lock.a getopt.a fs.a sig.a now.o
+       ./load ezmlm-limit getconf.o slurpclose.o slurp.o getopt.a \
+       strerr.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+       open.a lock.a fs.a sig.a now.o
+
+ezmlm-limit.0: \
+ezmlm-limit.1
+       nroff -man ezmlm-limit.1 > ezmlm-limit.0
+
+ezmlm-limit.o: \
+compile ezmlm-limit.c stralloc.h strerr.h substdio.h readwrite.h sig.h lock.h \
+getconf.h fmt.h now.h sgetopt.h error.h errtxt.h idx.h datetime.h
+       ./compile ezmlm-limit.c
+
 ezmlm-list: \
-load ezmlm-list.o strerr.a getln.a substdio.a stralloc.a alloc.a \
-error.a open.a str.a
-       ./load ezmlm-list strerr.a getln.a substdio.a stralloc.a \
-       alloc.a error.a open.a str.a 
+load ezmlm-list.o subdb.a fs.a getconf.o slurpclose.o slurp.o \
+strerr.a getln.a substdio.a stralloc.a alloc.a \
+error.a open.a str.a case.a getopt.a conf-sqlld
+       ./load ezmlm-list subdb.a fs.a getconf.o slurpclose.o slurp.o \
+       strerr.a getln.a getopt.a substdio.a stralloc.a \
+       alloc.a error.a open.a str.a case.a ${SQLLD}
 
 ezmlm-list.0: \
 ezmlm-list.1
        nroff -man ezmlm-list.1 > ezmlm-list.0
 
 ezmlm-list.o: \
-compile ezmlm-list.c stralloc.h gen_alloc.h stralloc.h ezmlm-list.c \
-substdio.h ezmlm-list.c getln.h ezmlm-list.c strerr.h ezmlm-list.c \
-error.h ezmlm-list.c readwrite.h ezmlm-list.c exit.h ezmlm-list.c \
-open.h ezmlm-list.c
+compile ezmlm-list.c stralloc.h gen_alloc.h substdio.h getln.h strerr.h \
+error.h readwrite.h exit.h open.h errtxt.h subscribe.h exit.h sgetopt.h \
+idx.h fmt.h
        ./compile ezmlm-list.c
 
 ezmlm-make: \
-load ezmlm-make.o auto_bin.o open.a getopt.a substdio.a strerr.a \
-stralloc.a alloc.a error.a str.a
-       ./load ezmlm-make auto_bin.o open.a getopt.a substdio.a \
-       strerr.a stralloc.a alloc.a error.a str.a 
+load ezmlm-make.o auto_bin.o open.a getln.a getopt.a substdio.a strerr.a \
+stralloc.a alloc.a error.a lock.a str.a
+       ./load ezmlm-make auto_bin.o open.a getln.a getopt.a substdio.a \
+       strerr.a stralloc.a alloc.a error.a lock.a str.a
 
 ezmlm-make.0: \
 ezmlm-make.1
@@ -202,19 +534,20 @@ sgetopt.h ezmlm-make.c stralloc.h gen_alloc.h stralloc.h ezmlm-make.c \
 strerr.h ezmlm-make.c exit.h ezmlm-make.c readwrite.h ezmlm-make.c \
 open.h ezmlm-make.c substdio.h ezmlm-make.c str.h ezmlm-make.c \
 auto_bin.h ezmlm-make.c ezmlm-make.c ezmlm-make.c ezmlm-make.c \
-ezmlm-make.c
+errtxt.h idx.h getln.h lock.h
        ./compile ezmlm-make.c
 
 ezmlm-manage: \
-load ezmlm-manage.o auto_qmail.o getconf.o subscribe.o log.o cookie.o \
+load ezmlm-manage.o auto_qmail.o getconf.o subdb.a log.o cookie.o \
 now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
 surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
-error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a
-       ./load ezmlm-manage auto_qmail.o getconf.o subscribe.o \
-       log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
+error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a getopt.a \
+mime.a copy.o conf-sqlld
+       ./load ezmlm-manage subdb.a auto_qmail.o getconf.o copy.o \
+       mime.a log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
        slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
        substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
-       open.a seek.a wait.a lock.a fd.a 
+       open.a seek.a wait.a lock.a fd.a getopt.a ${SQLLD}
 
 ezmlm-manage.0: \
 ezmlm-manage.1
@@ -231,35 +564,84 @@ ezmlm-manage.c readwrite.h ezmlm-manage.c seek.h ezmlm-manage.c \
 quote.h ezmlm-manage.c datetime.h ezmlm-manage.c now.h datetime.h \
 datetime.h now.h ezmlm-manage.c date822fmt.h ezmlm-manage.c fmt.h \
 ezmlm-manage.c subscribe.h strerr.h strerr.h subscribe.h \
-ezmlm-manage.c cookie.h ezmlm-manage.c
+sgetopt.h subgetopt.h cookie.h idx.h errtxt.h copy.h
        ./compile ezmlm-manage.c
 
+ezmlm-mktab.0: \
+ezmlm-mktab.1
+       nroff -man ezmlm-mktab.1 > ezmlm-mktab.0
+
+ezmlm-moderate: \
+load ezmlm-moderate.o auto_qmail.o getconf.o auto_bin.o copy.o mime.a \
+cookie.o now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
+surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
+error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a getopt.a
+       ./load ezmlm-moderate auto_qmail.o getconf.o copy.o mime.a \
+       cookie.o now.o datetime.o date822fmt.o slurpclose.o \
+       slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
+       substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
+       auto_bin.o open.a seek.a wait.a lock.a fd.a getopt.a
+
+ezmlm-moderate.0: \
+ezmlm-moderate.1
+       nroff -man ezmlm-moderate.1 > ezmlm-moderate.0
+
+ezmlm-moderate.o: \
+compile ezmlm-moderate.c error.h stralloc.h gen_alloc.h str.h \
+env.h sig.h slurp.h getconf.h strerr.h byte.h getln.h case.h \
+qmail.h substdio.h readwrite.h seek.h quote.h datetime.h now.h \
+date822fmt.h fmt.h strerr.h cookie.h errtxt.h idx.h copy.h mime.h \
+subgetopt.h sgetopt.h auto_bin.h fork.h wait.h
+       ./compile ezmlm-moderate.c
+
+ezmlm-request: \
+load ezmlm-request.o subdb.a getconf.o constmap.o getln.a auto_qmail.o qmail.o \
+strerr.a slurpclose.o slurp.o getopt.a env.a open.a fd.a sig.a case.a \
+substdio.a error.a stralloc.a alloc.a str.a case.a fs.a wait.a seek.a \
+date822fmt.o now.o datetime.o quote.o copy.o mime.a conf-sqlld
+       ./load ezmlm-request subdb.a getconf.o constmap.o getln.a auto_qmail.o \
+       qmail.o date822fmt.o datetime.o now.o quote.o \
+       slurpclose.o slurp.o env.a open.a sig.a wait.a getopt.a \
+       strerr.a substdio.a error.a copy.o stralloc.a alloc.a substdio.a \
+       str.a case.a fs.a fd.a sig.a wait.a seek.a mime.a ${SQLLD}
+
+ezmlm-request.0:
+       nroff -man ezmlm-request.1 > ezmlm-request.0
+
+ezmlm-request.o: \
+compile ezmlm-request.c stralloc.h subfd.h strerr.h error.h qmail.h env.h \
+sig.h open.h getln.h case.h str.h readwrite.h exit.h substdio.h quote.h \
+getconf.h constmap.h fmt.h byte.h errtxt.h idx.h datetime.h date822fmt.h \
+subscribe.h now.h copy.h
+       ./compile ezmlm-request.c
+
 ezmlm-reject: \
-load ezmlm-reject.o getln.a strerr.a substdio.a error.a stralloc.a \
-alloc.a str.a getopt.a case.a
-       ./load ezmlm-reject getln.a strerr.a substdio.a error.a \
-       stralloc.a alloc.a str.a getopt.a case.a 
+load ezmlm-reject.o getln.a strerr.a substdio.a error.a stralloc.a open.a \
+qmail.o env.a seek.a fd.a wait.a auto_qmail.o \
+alloc.a getconf.o slurp.o slurpclose.o str.a getopt.a case.a constmap.o fs.a
+       ./load ezmlm-reject qmail.o getln.a strerr.a substdio.a error.a fs.a \
+       env.a constmap.o getconf.o slurp.o slurpclose.o stralloc.a alloc.a \
+       seek.a str.a getopt.a case.a open.a fd.a wait.a auto_qmail.o
 
 ezmlm-reject.0: \
 ezmlm-reject.1
        nroff -man ezmlm-reject.1 > ezmlm-reject.0
 
 ezmlm-reject.o: \
-compile ezmlm-reject.c strerr.h ezmlm-reject.c substdio.h \
-ezmlm-reject.c readwrite.h ezmlm-reject.c stralloc.h gen_alloc.h \
-stralloc.h ezmlm-reject.c getln.h ezmlm-reject.c sgetopt.h \
-subgetopt.h sgetopt.h ezmlm-reject.c
+compile ezmlm-reject.c strerr.h substdio.h readwrite.h stralloc.h gen_alloc.h \
+stralloc.h getln.h sgetopt.h subgetopt.h constmap.h getconf.h errtxt.h \
+scan.h fmt.h idx.h qmail.h env.h seek.h
        ./compile ezmlm-reject.c
 
 ezmlm-return: \
-load ezmlm-return.o quote.o getconf.o issub.o subscribe.o log.o \
+load ezmlm-return.o quote.o getconf.o subdb.a log.o \
 slurpclose.o slurp.o now.o cookie.o surf.a lock.a env.a sig.a \
 strerr.a getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a \
-case.a open.a
-       ./load ezmlm-return quote.o getconf.o issub.o subscribe.o \
+case.a open.a conf-sqlld
+       ./load ezmlm-return quote.o getconf.o subdb.a \
        log.o slurpclose.o slurp.o now.o cookie.o surf.a lock.a \
        env.a sig.a strerr.a getln.a substdio.a stralloc.a alloc.a \
-       error.a str.a fs.a case.a open.a 
+       error.a str.a fs.a case.a open.a ${SQLLD}
 
 ezmlm-return.0: \
 ezmlm-return.1
@@ -270,43 +652,134 @@ compile ezmlm-return.c stralloc.h gen_alloc.h stralloc.h \
 ezmlm-return.c str.h ezmlm-return.c env.h ezmlm-return.c sig.h \
 ezmlm-return.c slurp.h ezmlm-return.c getconf.h ezmlm-return.c \
 strerr.h ezmlm-return.c byte.h ezmlm-return.c case.h ezmlm-return.c \
-getln.h ezmlm-return.c substdio.h ezmlm-return.c error.h \
+getln.h ezmlm-return.c substdio.h ezmlm-return.c error.h direntry.h \
 ezmlm-return.c quote.h ezmlm-return.c readwrite.h ezmlm-return.c \
 fmt.h ezmlm-return.c now.h datetime.h now.h ezmlm-return.c cookie.h \
 ezmlm-return.c subscribe.h strerr.h strerr.h subscribe.h \
-ezmlm-return.c issub.h strerr.h strerr.h issub.h ezmlm-return.c
+strerr.h strerr.h
        ./compile ezmlm-return.c
 
 ezmlm-send: \
 load ezmlm-send.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
-slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a \
-substdio.a stralloc.a alloc.a error.a str.a fd.a case.a fs.a
-       ./load ezmlm-send auto_qmail.o getconf.o qmail.o \
-       constmap.o slurp.o slurpclose.o wait.a getln.a strerr.a \
-       sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a \
-       error.a str.a fd.a case.a fs.a 
+slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a conf-sqlld \
+substdio.a cookie.o stralloc.a alloc.a error.a str.a fd.a case.a fs.a surf.a \
+getopt.a copy.o mime.a subdb.a makehash.o surf.o makehash.o str.a quote.o
+       ./load ezmlm-send subdb.a cookie.o surf.a auto_qmail.o getconf.o \
+       getopt.a qmail.o quote.o constmap.o slurp.o slurpclose.o \
+       wait.a getln.a strerr.a \
+       sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a error.a \
+       fd.a case.a fs.a getopt.a copy.o mime.a makehash.o str.a ${SQLLD}
 
 ezmlm-send.0: \
 ezmlm-send.1
        nroff -man ezmlm-send.1 > ezmlm-send.0
 
 ezmlm-send.o: \
-compile ezmlm-send.c stralloc.h gen_alloc.h stralloc.h ezmlm-send.c \
-subfd.h substdio.h subfd.h ezmlm-send.c strerr.h ezmlm-send.c error.h \
-ezmlm-send.c qmail.h substdio.h substdio.h qmail.h ezmlm-send.c env.h \
-ezmlm-send.c lock.h ezmlm-send.c sig.h ezmlm-send.c open.h \
-ezmlm-send.c getln.h ezmlm-send.c case.h ezmlm-send.c scan.h \
-ezmlm-send.c str.h ezmlm-send.c fmt.h ezmlm-send.c readwrite.h \
-ezmlm-send.c exit.h ezmlm-send.c substdio.h substdio.h ezmlm-send.c \
-getconf.h ezmlm-send.c constmap.h ezmlm-send.c
+compile ezmlm-send.c stralloc.h gen_alloc.h copy.h \
+subfd.h substdio.h strerr.h error.h qmail.h env.h makehash.h sgetopt.h \
+lock.h sig.h open.h getln.h case.h scan.h str.h fmt.h readwrite.h quote.h \
+exit.h getconf.h constmap.h byte.h errtxt.h idx.h mime.h subscribe.h \
+uint32.h
        ./compile ezmlm-send.c
 
+ezmlm-master: \
+load ezmlm-master.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
+slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a conf-sqlld \
+substdio.a cookie.o stralloc.a alloc.a error.a str.a fd.a case.a fs.a surf.a\
+getopt.a copy.o mime.a subdb.a makehash.o surf.o makehash.o str.a quote.o
+       ./load ezmlm-master subdb.a cookie.o surf.a auto_qmail.o getconf.o \
+       getopt.a qmail.o quote.o constmap.o slurp.o slurpclose.o \
+       wait.a getln.a strerr.a \
+       sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a error.a \
+       fd.a case.a fs.a getopt.a copy.o mime.a makehash.o str.a ${SQLLD}
+
+ezmlm-master.0: \
+ezmlm-master.1
+       nroff -man ezmlm-master.1 > ezmlm-master.0
+
+ezmlm-master.o: \
+compile ezmlm-master.c stralloc.h gen_alloc.h copy.h \
+subfd.h substdio.h strerr.h error.h qmail.h env.h makehash.h sgetopt.h \
+lock.h sig.h open.h getln.h case.h scan.h str.h fmt.h readwrite.h quote.h \
+exit.h getconf.h constmap.h byte.h errtxt.h idx.h mime.h subscribe.h \
+uint32.h
+       ./compile ezmlm-master.c
+
+ezmlm-slave: \
+load ezmlm-slave.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
+slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a conf-sqlld \
+substdio.a cookie.o stralloc.a alloc.a error.a str.a fd.a case.a fs.a surf.a \
+getopt.a copy.o mime.a subdb.a makehash.o surf.o makehash.o str.a quote.o
+       ./load ezmlm-slave subdb.a cookie.o surf.a auto_qmail.o getconf.o \
+       getopt.a qmail.o quote.o constmap.o slurp.o slurpclose.o \
+       wait.a getln.a strerr.a \
+       sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a error.a \
+       fd.a case.a fs.a getopt.a copy.o mime.a makehash.o str.a ${SQLLD}
+
+ezmlm-slave.0: \
+ezmlm-slave.1
+       nroff -man ezmlm-slave.1 > ezmlm-slave.0
+
+ezmlm-slave.o: \
+compile ezmlm-slave.c stralloc.h gen_alloc.h copy.h \
+subfd.h substdio.h strerr.h error.h qmail.h env.h makehash.h sgetopt.h \
+lock.h sig.h open.h getln.h case.h scan.h str.h fmt.h readwrite.h quote.h \
+exit.h getconf.h constmap.h byte.h errtxt.h idx.h mime.h subscribe.h \
+uint32.h
+       ./compile ezmlm-slave.c
+
+ezmlm-split: \
+load ezmlm-split.o auto_qmail.o getconf.o \
+slurpclose.o slurp.o qmail.o quote.o wait.a \
+getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
+error.a str.a fs.a case.a open.a fd.a
+       ./load ezmlm-split auto_qmail.o getconf.o slurpclose.o \
+       slurp.o qmail.o quote.o getln.a env.a sig.a strerr.a \
+       substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
+       open.a fd.a wait.a
+
+ezmlm-split.0: \
+ezmlm-split.1
+       nroff -man ezmlm-split.1 > ezmlm-split.0
+
+ezmlm-split.o: \
+compile ezmlm-split.c error.h stralloc.h gen_alloc.h str.h \
+env.h sig.h getconf.h strerr.h byte.h getln.h case.h \
+qmail.h substdio.h  readwrite.h quote.h \
+fmt.h errtxt.h idx.h uint32.h
+       ./compile ezmlm-split.c
+
+ezmlm-store: \
+load ezmlm-store.o auto_qmail.o getconf.o subdb.a log.o auto_bin.o mime.a \
+cookie.o now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
+surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a conf-sqlld \
+error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a getopt.a copy.o
+       ./load ezmlm-store auto_qmail.o getconf.o subdb.a copy.o mime.a \
+       log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
+       slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
+       substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
+       open.a seek.a wait.a lock.a fd.a getopt.a auto_bin.o ${SQLLD}
+
+ezmlm-store.0: \
+ezmlm-store.1
+       nroff -man ezmlm-store.1 > ezmlm-store.0
+
+ezmlm-store.o: \
+compile ezmlm-store.c error.h stralloc.h gen_alloc.h str.h \
+sgetopt.h subgetopt.h fork.h wait.h auto_bin.h lock.h mime.h \
+env.h sig.h slurp.h getconf.h strerr.h byte.h getln.h case.h \
+qmail.h substdio.h readwrite.h seek.h quote.h datetime.h now.h \
+date822fmt.h fmt.h subscribe.h strerr.h cookie.h errtxt.h idx.h copy.h
+       ./compile ezmlm-store.c
+
 ezmlm-sub: \
-load ezmlm-sub.o subscribe.o log.o now.o fs.a strerr.a getln.a \
+load ezmlm-sub.o subdb.a getconf.o slurpclose.o slurp.o \
+log.o now.o fs.a strerr.a getln.a getopt.a fs.a conf-sqlld \
 substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
-       ./load ezmlm-sub subscribe.o log.o now.o fs.a strerr.a \
+       ./load ezmlm-sub subdb.a getconf.o slurpclose.o slurp.o \
+       log.o now.o fs.a strerr.a getopt.a fs.a \
        getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
-       open.a lock.a 
+       open.a lock.a ${SQLLD}
 
 ezmlm-sub.0: \
 ezmlm-sub.1
@@ -314,57 +787,86 @@ ezmlm-sub.1
 
 ezmlm-sub.o: \
 compile ezmlm-sub.c strerr.h ezmlm-sub.c subscribe.h strerr.h \
-strerr.h subscribe.h ezmlm-sub.c log.h ezmlm-sub.c
+getln.h substdio.h stralloc.h readwrite.h \
+strerr.h subscribe.h log.h errtxt.h sgetopt.h scan.h idx.h
        ./compile ezmlm-sub.c
 
+ezmlm-test: \
+ezmlm-test.sh warn-auto.sh conf-bin
+       (cat warn-auto.sh; \
+       echo QMPATH=\'`head -1 conf-qmail`\'; \
+       cat ezmlm-test.sh ) > ezmlm-test; \
+       chmod 755 ezmlm-test
+
+ezmlm-test.0: \
+ezmlm-test.1
+       nroff -man ezmlm-test.1 > ezmlm-test.0
+
+ezmlm-tstdig: \
+load ezmlm-tstdig.o getopt.a getconf.o now.o fs.a strerr.a getln.a \
+lock.a \
+substdio.a stralloc.a alloc.a error.a str.a case.a sig.a \
+open.a slurpclose.o slurp.o env.a
+       ./load ezmlm-tstdig getopt.a getconf.o env.a now.o fs.a strerr.a \
+       lock.a getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+       sig.a slurpclose.o slurp.o open.a 
+
+ezmlm-tstdig.0: \
+ezmlm-tstdig.1
+       nroff -man ezmlm-tstdig.1 > ezmlm-tstdig.0
+
+ezmlm-tstdig.o: \
+compile ezmlm-tstdig.c strerr.h sgetopt.h getconf.h \
+sig.h now.h errtxt.h stralloc.h sig.h env.h fmt.h substdio.h readwrite.h \
+now.h idx.h
+       ./compile ezmlm-tstdig.c
+
 ezmlm-unsub: \
-load ezmlm-unsub.o subscribe.o log.o now.o fs.a strerr.a getln.a \
+load ezmlm-unsub.o subdb.a getconf.o slurpclose.o slurp.o \
+log.o now.o fs.a strerr.a getln.a getopt.a fs.a conf-sqlld \
 substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
-       ./load ezmlm-unsub subscribe.o log.o now.o fs.a strerr.a \
+       ./load ezmlm-unsub subdb.a getopt.a getconf.o slurpclose.o slurp.o \
+       log.o now.o fs.a strerr.a fs.a \
        getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
-       open.a lock.a 
+       open.a lock.a ${SQLLD}
 
 ezmlm-unsub.0: \
 ezmlm-unsub.1
        nroff -man ezmlm-unsub.1 > ezmlm-unsub.0
 
 ezmlm-unsub.o: \
-compile ezmlm-unsub.c strerr.h ezmlm-unsub.c subscribe.h strerr.h \
-strerr.h subscribe.h ezmlm-unsub.c log.h ezmlm-unsub.c
+compile ezmlm-unsub.c strerr.h subscribe.h \
+log.h errtxt.h sgetopt.h scan.h idx.h readwrite.h stralloc.h substdio.h 
        ./compile ezmlm-unsub.c
 
 ezmlm-warn: \
-load ezmlm-warn.o auto_qmail.o getconf.o cookie.o issub.o now.o \
+load ezmlm-warn.o auto_qmail.o getconf.o mime.a cookie.o subdb.a now.o \
 slurpclose.o slurp.o quote.o datetime.o date822fmt.o qmail.o surf.a \
 case.a strerr.a sig.a getln.a substdio.a stralloc.a alloc.a error.a \
-open.a lock.a str.a fs.a fd.a wait.a
-       ./load ezmlm-warn auto_qmail.o getconf.o cookie.o issub.o \
+open.a lock.a str.a fs.a fd.a wait.a copy.o getopt.a conf-sqlld
+       ./load ezmlm-warn auto_qmail.o getconf.o mime.a \
+       cookie.o subdb.a getopt.a \
        now.o slurpclose.o slurp.o quote.o datetime.o date822fmt.o \
        qmail.o surf.a case.a strerr.a sig.a getln.a substdio.a \
        stralloc.a alloc.a error.a open.a lock.a str.a fs.a fd.a \
-       wait.a 
+       wait.a copy.o ${SQLLD}
 
 ezmlm-warn.0: \
 ezmlm-warn.1
        nroff -man ezmlm-warn.1 > ezmlm-warn.0
 
 ezmlm-warn.o: \
-compile ezmlm-warn.c ezmlm-warn.c ezmlm-warn.c direntry.h direntry.h \
-direntry.h ezmlm-warn.c readwrite.h ezmlm-warn.c getln.h ezmlm-warn.c \
-substdio.h ezmlm-warn.c stralloc.h gen_alloc.h stralloc.h \
-ezmlm-warn.c slurp.h ezmlm-warn.c getconf.h ezmlm-warn.c byte.h \
-ezmlm-warn.c error.h ezmlm-warn.c str.h ezmlm-warn.c strerr.h \
-ezmlm-warn.c sig.h ezmlm-warn.c now.h datetime.h now.h ezmlm-warn.c \
-datetime.h datetime.h ezmlm-warn.c date822fmt.h ezmlm-warn.c fmt.h \
-ezmlm-warn.c cookie.h ezmlm-warn.c qmail.h substdio.h substdio.h \
-qmail.h ezmlm-warn.c
+compile ezmlm-warn.c direntry.h readwrite.h getln.h \
+substdio.h stralloc.h gen_alloc.h slurp.h getconf.h byte.h error.h str.h \
+sig.h now.h datetime.h  date822fmt.h fmt.h cookie.h qmail.h substdio.h \
+qmail.h copy.h mime.h idx.h errtxt.h sgetopt.h subgetopt.h
        ./compile ezmlm-warn.c
 
 ezmlm-weed: \
-load ezmlm-weed.o getln.a strerr.a substdio.a error.a stralloc.a \
+load ezmlm-weed.o getln.a strerr.a substdio.a error.a case.a stralloc.a \
 alloc.a str.a
        ./load ezmlm-weed getln.a strerr.a substdio.a error.a \
-       stralloc.a alloc.a str.a 
+       case.a stralloc.a alloc.a str.a 
 
 ezmlm-weed.0: \
 ezmlm-weed.1
@@ -380,6 +882,18 @@ ezmlm.0: \
 ezmlm.5
        nroff -man ezmlm.5 > ezmlm.0
 
+ezmlmglrc.0: \
+ezmlmglrc.5
+       nroff -man ezmlmglrc.5 > ezmlmglrc.0
+
+ezmlmrc.0: \
+ezmlmrc.5
+       nroff -man ezmlmrc.5 > ezmlmrc.0
+
+ezmlmsubrc.0: \
+ezmlmsubrc.5
+       nroff -man ezmlmsubrc.5 > ezmlmsubrc.0
+
 fd.a: \
 makelib fd_copy.o fd_move.o
        ./makelib fd.a fd_copy.o fd_move.o
@@ -496,16 +1010,23 @@ install.c open.h install.c error.h install.c strerr.h install.c \
 byte.h install.c
        ./compile install.c
 
+idxthread.o: \
+compile idxthread.c idxthread.h alloc.h error.h stralloc.h str.h lock.h idx.h \
+substdio.h fmt.h readwrite.h idx.h errtxt.h substdio.h byte.h yyyymm.h
+       ./compile idxthread.c
+
 issub.o: \
-compile issub.c stralloc.h gen_alloc.h stralloc.h issub.c getln.h \
-issub.c readwrite.h issub.c substdio.h issub.c open.h issub.c byte.h \
-issub.c case.h issub.c lock.h issub.c error.h issub.c issub.h \
-strerr.h issub.h issub.c uint32.h issub.c
-       ./compile issub.c
+compile issub.c stralloc.h gen_alloc.h getln.h readwrite.h substdio.h \
+open.h byte.h case.h lock.h error.h subscribe.h strerr.h uint32.h fmt.h \
+conf-sqlcc
+       ./compile issub.c ${SQLCC}
 
 it: \
+ezmlm-idx ezmlm-accept ezmlm-archive ezmlm-check ezmlm-gate ezmlm-get \
+ezmlm-clean ezmlm-glconf ezmlm-moderate ezmlm-store ezmlm-tstdig \
 ezmlm-make ezmlm-manage ezmlm-send ezmlm-reject ezmlm-return \
-ezmlm-warn ezmlm-weed ezmlm-list ezmlm-sub ezmlm-unsub
+ezmlm-warn ezmlm-weed ezmlm-list ezmlm-sub ezmlm-unsub ezmlm-cgi ezmlm-limit \
+ezmlm-issubn ezmlm-cron ezmlm-request ezmlm-test ezmlm-split ezmlmrc
 
 load: \
 make-load warn-auto.sh systype
@@ -527,6 +1048,10 @@ gen_alloc.h stralloc.h log.c log.h log.c now.h datetime.h now.h log.c \
 fmt.h log.c open.h log.c
        ./compile log.c
 
+logmsg.o: \
+compile logmsg.c stralloc.h fmt.h conf-sqlcc
+       ./compile logmsg.c ${SQLCC}
+
 make-compile: \
 make-compile.sh auto-ccld.sh
        cat auto-ccld.sh make-compile.sh > make-compile
@@ -542,6 +1067,10 @@ make-makelib.sh auto-ccld.sh
        cat auto-ccld.sh make-makelib.sh > make-makelib
        chmod 755 make-makelib
 
+makehash.o: \
+makehash.c makehash.h surf.h uint32.h stralloc.h
+       ./compile makehash.c
+
 makelib: \
 make-makelib warn-auto.sh systype
        ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \
@@ -549,12 +1078,23 @@ make-makelib warn-auto.sh systype
        chmod 755 makelib
 
 man: \
-ezmlm.0 ezmlm-make.0 ezmlm-manage.0 ezmlm-send.0 ezmlm-reject.0 \
+ezmlm.0 ezmlm-gate.0 ezmlm-idx.0 ezmlm-get.0 ezmlm-check.0 ezmlm-tstdig.0 \
+ezmlm-make.0 ezmlm-manage.0 ezmlm-send.0 ezmlm-reject.0 ezmlm-accept.0 \
 ezmlm-return.0 ezmlm-warn.0 ezmlm-weed.0 ezmlm-list.0 ezmlm-sub.0 \
 ezmlm-unsub.0 alloc.0 case.0 datetime.0 direntry.0 env.0 error.0 \
 error_str.0 error_temp.0 ezmlm.0 fd_copy.0 fd_move.0 getln.0 getln2.0 \
+ezmlm-issubn.0 ezmlm-cron.0 ezmlm-glconf.0 ezmlmglrc.0 ezmlm-test.0 \
+ezmlmsubrc.0 ezmlm-mktab.0 ezmlm-split.0 ezmlm-archive.0 ezmlm-cgi.0 \
 getopt.0 now.0 sgetopt.0 stralloc.0 subfd.0 subgetopt.0 substdio.0 \
-substdio_copy.0 substdio_in.0 substdio_out.0 surf.0 surfpcs.0 wait.0
+substdio_copy.0 substdio_in.0 substdio_out.0 surf.0 surfpcs.0 wait.0 \
+ezmlm-clean.0 ezmlm-moderate.0 ezmlm-store.0 ezmlm-request.0 ezmlmrc.0 \
+ezmlm-limit.0
+
+mime.a: \
+makelib concatHDR.o decodeHDR.o unfoldHDR.o \
+decodeQ.o encodeQ.o decodeB.o encodeB.o author.o
+       ./makelib mime.a concatHDR.o decodeHDR.o decodeQ.o encodeQ.o \
+       decodeB.o encodeB.o unfoldHDR.o author.o
 
 now.0: \
 now.3
@@ -582,6 +1122,16 @@ open_trunc.o: \
 compile open_trunc.c open_trunc.c open_trunc.c open.h open_trunc.c
        ./compile open_trunc.c
 
+opensql.o: \
+compile opensql.c error.h strerr.h errtxt.h \
+       str.h case.h stralloc.h subscribe.h conf-sqlcc
+       ./compile opensql.c ${SQLCC}
+
+putsubs.o: \
+compile putsubs.c error.h substdio.h strerr.h readwrite.h \
+str.h open.h case.h errtxt.h stralloc.h subscribe.h qmail.h fmt.h conf-sqlcc
+       ./compile putsubs.c ${SQLCC}
+
 qmail.o: \
 compile qmail.c substdio.h qmail.c readwrite.h qmail.c wait.h qmail.c \
 exit.h qmail.c fork.h qmail.c fd.h qmail.c qmail.h substdio.h \
@@ -601,6 +1151,12 @@ scan_ulong.o: \
 compile scan_ulong.c scan.h scan_ulong.c
        ./compile scan_ulong.c
 
+searchlog.o: \
+compile searchlog.c case.h stralloc.h scan.h open.h datetime.h errtxt.h str.h \
+       datetime.h date822fmt.h substdio.h readwrite.h strerr.h error.h \
+       subscribe.h conf-sqlcc
+       ./compile searchlog.c ${SQLCC}
+
 seek.a: \
 makelib seek_set.o
        ./makelib seek.a seek_set.o
@@ -800,6 +1356,12 @@ strerr_sys.o: \
 compile strerr_sys.c error.h strerr_sys.c strerr.h strerr_sys.c
        ./compile strerr_sys.c
 
+subdb.a: \
+makelib checktag.o issub.o logmsg.o subscribe.o opensql.o putsubs.o \
+       tagmsg.o searchlog.o
+       ./makelib subdb.a checktag.o issub.o logmsg.o subscribe.o \
+       opensql.o putsubs.o tagmsg.o searchlog.o
+
 subfd.0: \
 subfd.3
        nroff -man subfd.3 > subfd.0
@@ -818,12 +1380,10 @@ compile subgetopt.c subgetopt.h subgetopt.h subgetopt.c
        ./compile subgetopt.c
 
 subscribe.o: \
-compile subscribe.c stralloc.h gen_alloc.h stralloc.h subscribe.c \
-getln.h subscribe.c readwrite.h subscribe.c substdio.h subscribe.c \
-strerr.h subscribe.c open.h subscribe.c byte.h subscribe.c case.h \
-subscribe.c lock.h subscribe.c error.h subscribe.c uint32.h \
-subscribe.c subscribe.h strerr.h strerr.h subscribe.h subscribe.c
-       ./compile subscribe.c
+compile subscribe.c stralloc.h gen_alloc.h stralloc.h \
+getln.h readwrite.h substdio.h strerr.h open.h byte.h case.h \
+lock.h error.h uint32.h subscribe.h idx.h fmt.h conf-sqlcc
+       ./compile subscribe.c ${SQLCC}
 
 substdi.o: \
 compile substdi.c substdio.h substdi.c byte.h substdi.c error.h \
@@ -889,6 +1449,10 @@ systype: \
 find-systype trycpp.c
        ./find-systype > systype
 
+tagmsg.o: \
+compile tagmsg.c stralloc.h slurp.h scan.h fmt.h strerr.h cookie.h conf-sqlcc
+       ./compile tagmsg.c ${SQLCC}
+
 uint32.h: \
 tryulong32.c compile load uint32.h1 uint32.h2
        ( ( ./compile tryulong32.c && ./load tryulong32 && \
@@ -907,3 +1471,124 @@ makelib wait_pid.o
 wait_pid.o: \
 compile wait_pid.c wait_pid.c wait_pid.c error.h wait_pid.c
        ./compile wait_pid.c
+
+yyyymm.a: \
+makelib date2yyyymm.o dateline.o
+       ./makelib yyyymm.a date2yyyymm.o dateline.o
+
+ch: \
+ezmlmrc.ch
+       cp -f ezmlmrc.ch_GB ezmlmrc
+
+ch_GB: \
+ezmlmrc.ch_GB
+       cp -f ezmlmrc.ch_GB ezmlmrc
+
+cs: \
+ezmlmrc.cs
+       cp -f ezmlmrc.cs ezmlmrc
+
+da: \
+ezmlmrc.da
+       cp -f ezmlmrc.da ezmlmrc
+
+de: \
+ezmlmrc.de
+       cp -f ezmlmrc.de ezmlmrc
+
+en_US: \
+ezmlmrc.en_US
+       cp -f ezmlmrc.en_US ezmlmrc
+
+en: \
+ezmlmrc.en_US
+       cp -f ezmlmrc.en_US ezmlmrc
+
+es: \
+ezmlmrc.es
+       cp -f ezmlmrc.es ezmlmrc
+
+us: \
+ezmlmrc.en_US
+       cp -f ezmlmrc.en_US ezmlmrc
+
+ezmlmrc: \
+ezmlmrc.en_US
+       cp -f ezmlmrc.en_US ezmlmrc
+
+fr: \
+ezmlmrc.fr
+       cp -f ezmlmrc.fr ezmlmrc
+
+id: \
+ezmlmrc.id
+       cp -f ezmlmrc.id ezmlmrc
+
+ita: \
+ezmlmrc.it
+       cp -f ezmlmrc.it ezmlmrc
+
+jp: \
+ezmlmrc.jp
+       cp -f ezmlmrc.jp ezmlmrc
+
+pl: \
+ezmlmrc.pl
+       cp -f ezmlmrc.pl ezmlmrc
+
+pt: \
+ezmlmrc.pt
+       cp -f ezmlmrc.pt ezmlmrc
+
+pt_BR: \
+ezmlmrc.pt_BR
+       cp -f ezmlmrc.pt_BR ezmlmrc
+
+ru: \
+ezmlmrc.ru
+       cp -f ezmlmrc.ru ezmlmrc
+
+sv: \
+ezmlmrc.sv
+       cp -f ezmlmrc.sv ezmlmrc
+
+mysql:
+       ln -sf sub_mysql/ezmlm-mktab ezmlm-mktab
+       ln -sf sub_mysql/checktag.c checktag.c; rm -f checktag.o
+       ln -sf sub_mysql/issub.c issub.c; rm -f issub.o
+       ln -sf sub_mysql/logmsg.c logmsg.c; rm -f logmsg.o
+       ln -sf sub_mysql/subscribe.c subscribe.c; rm -f subscribe.o
+       ln -sf sub_mysql/opensql.c opensql.c; rm -f opensql.o
+       ln -sf sub_mysql/putsubs.c putsubs.c; rm -f putsubs.o
+       ln -sf sub_mysql/tagmsg.c tagmsg.c; rm -f tagmsg.o
+       ln -sf sub_mysql/searchlog.c searchlog.c; rm -f searchlog.o
+       ln -sf sub_mysql/conf-sqlld conf-sqlld; touch conf-sqlld
+       ln -sf sub_mysql/conf-sqlcc conf-sqlcc; touch conf-sqlcc
+
+pgsql:
+       ln -sf sub_pgsql/ezmlm-mktab ezmlm-mktab
+       ln -sf sub_pgsql/checktag.c checktag.c; rm -f checktag.o
+       ln -sf sub_pgsql/issub.c issub.c; rm -f issub.o
+       ln -sf sub_pgsql/logmsg.c logmsg.c; rm -f logmsg.o
+       ln -sf sub_pgsql/subscribe.c subscribe.c; rm -f subscribe.o
+       ln -sf sub_pgsql/opensql.c opensql.c; rm -f opensql.o
+       ln -sf sub_pgsql/putsubs.c putsubs.c; rm -f putsubs.o
+       ln -sf sub_pgsql/tagmsg.c tagmsg.c; rm -f tagmsg.o
+       ln -sf sub_pgsql/searchlog.c searchlog.c; rm -f searchlog.o
+       ln -sf sub_pgsql/conf-sqlld conf-sqlld; touch conf-sqlld
+       ln -sf sub_pgsql/conf-sqlcc conf-sqlcc; touch conf-sqlcc
+
+std:
+       ln -sf sub_std/ezmlm-mktab ezmlm-mktab
+       ln -sf sub_std/checktag.c checktag.c; rm -f checktag.o
+       ln -sf sub_std/issub.c issub.c; rm -f issub.o
+       ln -sf sub_std/logmsg.c logmsg.c; rm -f logmsg.o
+       ln -sf sub_std/subscribe.c subscribe.c; rm -f subscribe.o
+       ln -sf sub_std/opensql.c opensql.c; rm -f opensql.o
+       ln -sf sub_std/putsubs.c putsubs.c; rm -f putsubs.o
+       ln -sf sub_std/tagmsg.c tagmsg.c; rm -f tagmsg.o
+       ln -sf sub_std/searchlog.c searchlog.c; rm -f searchlog.o
+       ln -sf sub_std/conf-sqlld conf-sqlld; touch conf-sqlld
+       ln -sf sub_std/conf-sqlcc conf-sqlcc; touch conf-sqlcc
+
+
diff --git a/README.idx b/README.idx
new file mode 100644 (file)
index 0000000..7ffd895
--- /dev/null
@@ -0,0 +1,337 @@
+$Id: README.idx,v 1.70 1999/12/24 04:20:45 lindberg Exp $
+$Name: ezmlm-idx-040 $
+
+(c) 1997, 1998, 1999 Fred Lindberg, lindberg@id.wustl.edu (code+docs+faq)
+                     Fred B. Ringel, fredr@rivertown.net (faq)
+
+This software is distributed under the GNU General Public Licence as
+published by the Free Software Foundation.  See the file LICENCE.TXT for
+the conditions under which this software is made available.
+
+Like any other piece of software (and information generally), ezmlm-idx
+comes with NO WARRANTY.
+
+This is an addition and patch to ezmlm-0.53 [(c) Dan J. Bernstein].
+Ezmlm-idx uses Dan's libraries and heavily relies on ezmlm-0.53 code.
+ezmlm-idx does not function without ezmlm-0.53.
+
+ezmlm-0.53 provides basic message distribution, subscriber address handling,
+message archiving (single quoted message retrieval only), and bounce
+handling. ezmlm-idx adds multi-message threaded archive retrieval, digests,
+remote administration, message and subscription moderation, subscriber-only
+restrictions, message trailer, subject prefix, subscriber name storage,
+customizable setup, multi-language and MIME support and more. In addition,
+improved bounce handling, case-insensitive subscriber address storage,
+and support for distributed lists (main + sublist transparent to the
+subscriber). Ezmlm-idx can now be compiled with MySQL or Postgres support.
+
+See INSTALL.idx for installation instructions.
+See UPGRADE.idx to upgrade from a previous version of ezmlm-idx.
+See CHANGES.idx to see what's new in this version.
+See FILES.idx for a list of files in this package.
+See FAQ.idx for more info on ezmlm(-idx) functions and setup (see below on
+           how to get the latest version).
+See ezman-0.xx* for a user's/administrator's manual to ezmlm/idx. Available
+           on line at http://www.ezmlm.org/ and for download at
+           ftp://ftp.ezmlm.org/pub/patches/ezman/ and mirrors.
+
+NOTE: Some ezmlmrc translations may not be up-to-date. ezmlm-make will
+complain and the results may not match the documentation. Changes required
+are small. If you correct a translation, please submit it for inclusion
+in the next ezmlm-idx version.
+
+*.rpm will put ezmlm-cgi in the bin directory. To use it, you must manually
+move it to a cgi-bin directory, set ownership and SUID (if needed), and
+set up /ezmlm/ezcgirc.
+
+See http://pobox.com/~djb/ezmlm.html for the latest information about
+ezmlm.
+
+See ftp://ftp.id.wustl.edu/pub/patches for the latest version of ezmlm-idx and
+downloadable versions of the FAQ.
+
+See http://www.ezmlm.org/ for ezmlm FAQ and docs on line.
+
+Mail ``ezmlm-subscribe@list.cr.yp.to'' to join the ezmlm mailing
+list (averages 3 messages per day). This list is run by Dan J. Bernstein
+using ezmlm-0.53.
+
+Send general comments and questions to:
+lindberg@id.wustl.edu or fredr@rivertown.net.
+
+Send bug reports and patches to:
+lindberg@id.wustl.edu.
+
+Below, acknowledgements and a list of systems where ezmlm-idx has been reported
+to work.
+
+ACKNOWLEDGEMENTS
+- Fred B. Ringel, first and foremost, for a great collaboration, ideas, testing,
+  docs clarification. This makes it so much more fun.
+- Dan J. Bernstein for qmail, ezmlm, and great libraries.
+- Toshinori Maeno (TM; tmaeno@hpcl.titech.ac.jp) for finding an ezmlm-idx-0.20
+  bug, and MIME suggestions, suggestions about hopcount and received headers,
+  return-path header in archive, sublist loop detection, ezmlm-limit suggestion,
+  many other suggestions and explanations, help to get ezmlm-cgi Japanese
+  support to work, as well as pre-release testing.
+- Frank Tegtmeyer for inciting digests and format info/suggestions, and for
+  ezmlmrc.de.
+- Magnus Stålåker (MAS; stalaker@umc.se) for the PostgresSQL interface.
+- Mark Delany for the original ezmlm-issub and for suggesting reordering
+  unlink() and doit() in ezmlm-warn.
+- Raul Miller and Chris Garrigues for format info/suggestions.
+- Thomas Erskine (TEE; tom@crc.doc.ca) for fix to clean sunos-4.1.3 compiles
+  and pre-release compilation on many platforms, fixes for bash-isms in
+  ezmlm-check, and testing.
+- Shuhei Kobayashi (SK; shuhei-k@jaist.ac.jp) for MIME corrections/suggestions,
+  X-sequence suggestions, ezmlm-check/make corrections, and pre-release testing.
+- Yusuf Goolamabbas (YG; yusufg@krdl.org.sg) and the Mutt developers for
+  pointing out outdated MIME, ezmlmrc improvements, outformat bug, suggestion
+  about ezmlm-moderate -r switch, and pre-release testing.
+- Brian Gentry (BG; gentry@usaccess-inc.com) for reporting the
+  ezmlm-moderate-0.12 exit code bug.
+- Anand R. Buddhev (ARB; arb@iconnect.co.ke) for fix to clean BSDI 2.1 compiles
+  and pre-release testing.
+- Masashi Fujita (MF; objectx@polyphony.scei.co.jp) for fix to clean SGI
+  compiles, lint advice, MIME suggestions, pre-release testing, a patch for
+  the ezmlm-make-0.21 -c bug, ezmlmrc.jp, and finding postmsg bug in 0.301.
+- Matthew D. Stock (MDS; stock@perdix.acsu.buffalo.edu) for pre-release testing.
+- Ximenes Zalteca for finding an ezmlm-send-0.21 bug.
+- Jukka Suomela (JS; jukka@narnia.tky.hut.fi) for inciting the ezmlm-send -cC
+  switch.
+- John White (johnjohn@triceratops.com) for questions leading to ezmlm-tstdig,
+  and for testing the example script.
+- Torben Fjerdingstad (TF; unitfj@tfj.uni-c.dk) for testing case-insensitive
+  issub.c and subscribe.c, for TARGETS, for suggesting a no-copy-to-SENDER
+  option, for ezmlmrc.da, for reporting missing MIME end for base64/QP
+  moderated [un]sub confirms, and for pre-release testing.
+- Shinya O'Hira (SOH; Shinya_Oohira@justsystem.co.jp) for reporting the
+  ezmlm-make-0.22 lock file name bug and for testing the fix. Also for many
+  helpful suggestions and hard work testing rfc2047 subject support.
+- Sadhu(sadhu@aloha.net) & Tracy Reed (TR; treed@ultraviolet.org) for reporting
+  the ezmlm-send-0.22 undefined SENDER bug, and testing the fix.
+- Scott Balantyne (SDB; sdb@ssr.com) for inciting the modifications for digest
+  out of dir/editor.
+- Frank Denis (j@industrie.capgemini.fr) for ezmlmrc.fr.
+- Marc Evans (marc@destek.net) for reporting the ezmlm-make-0.221 64-bit bug
+  and pre-release testing, and inciting, improving, and testing ezmlm-test.
+- Vince Vielhaber (VV; vev@michvhf.com) for reporting ezmlm-check problems with
+  non-bash and testing fixes.
+- Andrew Pam (AP; xanni@xanadu.net) for suggesting the ezmlm-idx -d switch and
+  reporting the ezmlm-gate failure on NULL $SENDER bug.
+- Glen Stewart (GS; glen_stewart@associate.com) for reminding me about command
+  case insensitivity and ezmlmrc typo fixes.
+- Kenji Ikeda (KI; noroi@nt.is.dnp.co.jp) for a patch to ezmlm-idx-0.23 to get
+  message numbers in subjects. I've added code to ezmlm-idx-0.30 implementing
+  this is a similar manner. Sen Nagata (sen_ml@eccosys.com) for suggesting this
+  earlier. Bugfix for missing MIME boundary in -index reply.
+- Sebastian Andersson (SA; sa@hogia.net) for ezmlmrc.sv, MIME suggestions,
+  ezmlm-weed patch, ezmlmrc version check suggestion and pre-release testing.
+- Giorgos Stathakopoulos (GS; stathako@cti.gr) for pre-release testing.
+- David Summers (DS; david@summersoft.fay.ar.us) for pre-release testing,
+  reporting a ezmlm-check bug and SPEC files for rpm:s.
+- Steinar Haug (STH; sthaug@nethelp.no) for pointing out missing ';' in
+  ezmlmrc (caused problems with /bin/sh on FreeBSD), and the trigger message
+  received lines in the digest header.
+- Peter Hunter (PH) for suggesting dir/headeradd and list address in digest
+  headers and for reporting a "cosmetic" bug in subscription logging.
+- Jim Simmons (JS) for finding the n/d switch misnaming in ezmlmrc for text file
+  editing.
+- Sergiusz Pawlowicz (SP; ser@arch.pwr.wroc.pl) for ezmlmrc.pl, and many entries
+  for mimeremove.
+- Wanderlei Antonio Cavassin (WAC; cavassin@connectiva.com.br) for
+  ezmlmrc.pt_BR.
+- Jim Simmons (     ) for finding a missing-newline-mime bug in
+  ezmlm-moderate/store/clean.
+- Monte Mitzelfelt (MM; monte@gonefishing.org) for suggesting alternative
+  From: for ezmlm-manage help messages to break responder loops.
+- Louis Larry (LL; luois@kediri.webindonesia.com) for reporting a 0.311 prefix
+  handling bug.
+- Sigi Remsmurr (SR; service@isk.de), Bill Nugent (whn@topelo.lopi.com), James
+  Smallacombe (JS; up@3am) for ezmlmrc corrections/suggestions.
+- Matthew Saunders (MS; matts@easynet.net) for ezmlm-glconf.sh corrections and
+  reporting crashability of ezmlm-get by abnormal use (fixed).
+- Petr Novotny (PN; Petr.Novotny@antek.cz) for ezdomo.tar.gz corrections.
+- Evan Champion (EC; evanc@synapse.net) for ezmlm-request bug report.
+- Jeff Hill (JH; jhill@hronline.com) for ezmlm-reject bug report.
+- Bruno Wolff (BW; bruno@cerberus.umn.edu) for Ultrix fixes.
+- Butch Evans (BE; butch@###.com) for reporting trailer problems with
+  multipart/alternative messages and testing fix.
+- Kragen Sitaker (KS; kragen@pobox.com) for reporting ezmlm-manage -get bug.
+- Lars B. Rasmusson (LBR; lbr@mjolner.dk) for prerelease testing with sun cc.
+- Mike McLeish (MMcL; Mike.Mcleish@chatsoft.com) for reporting problems
+  using the 'x' format and testing the fix.
+- Jan Kasprzak (JK; kas@informatics.muni.cz) for ezmlmrc.cs and headerremove
+  suggestions.
+- Matt McGlynn for reporting 0.32 ezmlmrc -return omission.
+- Roman V Isaev (RVI; rm@techno.ru) for ezmlmrc.ru as well as suggestions
+  on -allow automation.
+- Aria Prima Novianto (APN; aria@isnet.org) for ezmlmrc.id.
+- Roberto De Carlo (RDC; rodeca@flashnet.it) for ezmlmrc.it.
+- Vicent Mas, Francesc Alted, Sonia Lorente, and Cyndy DePoy for ezmlmrc.es.
+- HaiFeng Guo (HFG; haifeng@ms.lawton.com.cn) for ezmlmrc.cn_GB.
+- Benjamin Pflugman (BPF; philemon@spin.de) for reporting multipart/signed
+  problems and testing fix.
+- Rik Myers (RM; rik@sumthin.nu), Daniel Mattos (DM; daniel@tiii.com) and others
+  for helping make ezmlm-test less platform-sensitive.
+- Michael Hirohama (MH; michael@sooth.com) for reporting Outlook problems and
+  testing a work-around.
+- Mate Wierdl (MW; mw@wierdlpc.msci.memphis.edu) for SPEC file kit and
+  suggestions on ezmlm-test, "it", etc, and pre-release testing.
+- Numerous users for suggestions/corrections for the documentation.
+- I'm sure I've forgotten others who have contributed ideas, questions,
+  comments. Thanks!
+
+EZMLM-IDX HAS BEEN REPORTED TO WORK ON (With ezmlm-0.53):
+0.12: linux-2.0.18-:i386-:-:i486-:- (Thanks Dileep Agrawal)
+0.12: linux-2.0.23-:i386-:-:i486-:- (Thanks TV)
+0.12: linux-2.0.31-:i386-:-:i486-:- (Thanks Nathan D. Faber)
+0.20: linux-2.0.23-:i386-:-:i486:-:-
+0.20: linux-2.0.31-:i386-:-:pentium:-:- (Thanks FBR)
+0.20: linux-2.0.23-:i386-:-:pentium:-:- (Thanks YG)
+0.20: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.20: bsd.os-2.1-:i386-:-:pentium-:- (Thanks ARB)
+0.20: sunos-4.1.4-2-:sparc-:sun4:sun4m- (Thanks SK)
+0.20: freebsd-2.2.2-release-:i386-:-:pentium.pro-:- (Thanks MDS)
+0.21: linux-2.0.23-:i386-:-:i486-:-
+0.21: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.21: sunos-4.1.3-3-:unknown-:sun4-:sun4m-:sun4m- (Thanks TEE)
+0.21: bsd.os-2.1-:i386-:-:pentium-:- (Thanks ARB)
+0.21: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (Thanks SOH)
+0.22: aix-4-2-:-:-:000720704c00-:- (Thanks TF)
+0.22: linux-2.0.23-:i386-:-:i486-:-
+0.22: linux-2.0.31-:i386-:-:ppro-:- (Thanks YG)
+0.22: linux-2.0.32-:i386-:-:pentium-:- (Thanks FBR)
+0.22: linux-2.0.32-:i386-:-:pentium-:- (Thanks TF)
+0.22: freebsd-2.2.1-release-:i386-:-:-:- (Thanks TM)
+0.22: freebsd-2.2.5-stable-:i386-:-:pentium.pro-:- (Thanks MDS)
+0.22: osf1-v4.0-386-:-:-:alpha-:- (Thanks TEE)
+0.22: sunos-4.1.3-3-:unknown-:sun4-:sun4m-:sun4m- (Thanks TEE)
+0.22: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4u-:sun4u- (Thanks YG)
+0.22: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (Thanks TEE)
+0.22: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (Thanks SOH)
+0.23: aix-4-2-:-:-:000720704c00-:- (Thanks TF)
+0.23: bsd.os-2.1-:i386-:-:pentium-:- (Thanks ARB)
+0.23: freebsd-2.2.1-release-:i386-:-:-:- (Thanks TM)
+0.23: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.23: linux-2.0.30-:i386-:-:i486-:-
+0.23: linux-2.0.32-:i386-:-:i486-:- (Thanks BCL; Bruce C. Law)
+0.23: linux-2.0.32-:i386-:-:pentium-:- (Thanks FBR)
+0.23: osf1-v4.0-386-:-:-:alpha-:- (Thanks TEE)
+0.23: osf1-v4.0-564-:-:-:alpha-:- (Thanks ME)
+0.23: sunos-4.1.3-3-:unknown-:sun4-:sun4m-:sun4m- (Thanks TEE)
+0.23: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (Thanks TEE)
+0.23: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (Thanks SOH)
+0.30: aix-4-2-:-:-:000720704c00-:- (Thanks TF)
+0.30: bsd.os-2.1-:i386-:-:pentium-:- (Thanks ARB)
+0.30: freebsd-2.2.5-release-:i386-:-:-:- (Thanks TM)
+0.30: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.30: linux-2.0.30-:i386-:-:i486-:-
+0.30: linux-2.0.31-:i386-:-:ppro:-:- (Thanks YG)
+0.30: linux-2.0.32-:i386-:-:pentium-:- (Thanks FBR)
+0.30: osf1-v4.0-564-:-:-:alpha-:- (Thanks ME)
+0.30: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (Thanks SOH)
+0.301: freebsd-2.2.5-release-:i386-:-:pentium.pro-:- (Thanks Matthew S. Soffen)
+0.301: linux-2.0.30-:-:-:sparc-:- (Thanks Bill Himmelstoss)
+0.301: linux-2.0.30-:i386-:-:i486-:-
+0.301: linux-2.0.32-:i386-:-:pentium-:- (Thanks FBR)
+0.301: linux-2.0.33-:i386-:-:i486-:- (Thanks BCL)
+0.301: linux-2.0.32-:i386-:-:ppro-:-
+0.302: aix-4-2-:-:-:000720704c00-:- (Thanks TF)
+0.302: linux-2.0.31-:i386-:-:ppro:-:- (Thanks YG)
+0.302: linux-2.0.32-:i386-:-:i486-:-
+0.302: linux-2.0.34-:i386-:-:pentium-:- (Thanks FBR)
+0.31: freebsd-2.2.6-release-:i386-:-:-:- (Thanks TM)
+0.31: linux-2.0.32-:i386-:-:ppro-:-
+0.31: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.311: linux-2.0.31-:i386-:-:ppro-:- (Thanks YG)
+0.311: linux-2.0.32-:i386-:-:ppro-:-
+0.311: linux-2.0.33-:i386-:-:i486-:- (Thanks BCL)
+0.311: linux-2.0.35-:i386-:-:i486-:- (Thanks PN)
+0.312: aix-4-2-:-:-:000720704c00-:- (Thanks TF)
+0.312: freebsd-3.0-current-:i386-:-:pentium/p54c-:- (Thanks MS)
+0.312: irix-5.3-08031224-:-:-:ip12-:- (Thanks Rob Stone)
+0.312: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.312: irix64-6.4-02121744-:-:-:ip30-:- (Thanks MF)
+0.312: linux-2.0.32-:i386-:-:ppro-:-
+0.312: linux-2.1.115-:i386-:-:pentium-:- (Thanks Uwe Ohse)
+0.312: sunos-5.6-generic_105181-06-:sparc-:sun4-:sun4u-:sun4u- (Thanks GS)
+0.312: sunos-5.6-generic_105182-08-:i386-:i86pc-:i86pc-:i86pc- (Thanks LBR)
+0.313: aix-4-2-:-:-:00720704c00-:- (Thanks TF)
+0.313: freebsd-2.2.6-release-:i386-:-:-:- (Thanks TM)
+0.313: freebsd-2.2.6-release-:i386-:-:pentium-:- (Thanks Dave Walton)
+0.313: freebsd-3.0-release-:i386-:-:pentium.ii.(qtr-micr)-:- (Tx Erik Gault)
+0.313: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.313: irix64-6.4-02121744-:-:-:ip30-:- (Thanks MF)
+0.313: linux-2.0.32-:i386-:-:ppro-:-
+0.313: linux-2.0.35-:i386-:-:pentium-:- (Thanks crt@ice.degan.si)
+0.313: linux-2.0.36-:i386-:-:pentium-:- (Thanks FBR)
+0.313: linux-2.2.0-pre4-:i386-:-:i486-:- (Thanks Chris Van Meter)
+0.313: sunos-5.6-generic_105181-04-:sparc-:sun4-:sun4u-:sun4u- (Thanks SOH)
+0.313: sunos-5.6-generic_105181-08-:sparc-:sun4-:sun4u-:sun4u- (Thanks LBR)
+0.313: ultrix-4.3-0-:-:-:risc-:- (Thanks BW)
+0.314: amigaos-3.1-40.60-:-:-:m68k-:- (Thanks Kirk Strauser)
+0.314: freebsd-2.2.8-stable-:i386-:-:pentium.ii-:-
+0.314: linux-2.0.32-:i386-:-:ppro-:-
+0.32: bsd.os-4.0-:i386-:-:-:- (Thanks ME)
+0.32: freebsd-2.2.7-stable-:i386-:-:pentium.pro-:- (Thanks Chris Johnson)
+0.32: freebsd-3.0-release-:i386-:-:pentium.ii.(qtr-micr)-:- (Thanks MS)
+0.32: irix-5.3-11091812-:-:-:ip22-:- (Thanks MF)
+0.32: linux-2.0.34-:i386-:-:i486-:- (Thanks Benjamin T. Brillat)
+0.32: linux-2.0.34-:i386-:-:ppro-:- (Thanks Troy N. Poppe)
+0.32: linux-2.0.36-:i386-:-:pentium-:- (Thanks Peter J. Hunter)
+0.32: linux-2.0.36-:i386-:-:ppro-:- (Thanks FT)
+0.32: linux-2.2.0-pre4-:i386-:-:pentium-:- (Thanks FBR)
+0.32: linux-2.2.1-:i386-:-:pentium-:- (Thanks FBR)
+0.32: netbsd-1.3i-:i386-:-:intel.pentium.(p54c).(586-class)-:- (Thanks GC Wing)
+0.321: bsd.os-4.0-:i386-:-:pentium.ii-:- (Thanks RV Isaev)
+0.321: freebsd-3.1-release-:i386-:-:pentium.ii-:- (Thanks AR Buddhdev)
+0.321: freebsd-3.1-release-:i386-:-:pentium.ii/xeon/celeron-:- (Tx A Iijima)
+0.321: freebsd-3.1-stable-:i386-:-:pentium.ii/xeon/celeron-:- (Tx B Fuerst)
+0.321: linux-2.0.34-:i386-:-:pentium-:- (Thanks root@issaries.com.au)
+0.321: linux-2.0.36-:i386-:-:pentium-:- (Thanks MC Yoon)
+0.321: linux-2.0.36-:i386-:-:i486-:- (Thanks K Ralph)
+0.321: linux-2.0.36-:i386-:-:ppro-:- (Thanks JD Mitchell)
+0.321: linux-2.2.2-:i386-:-:pentium-:- (Thanks R Siemer)
+0.321: linux-2.2.4-:i386-:-:pentium-:- (Thanks JM Charette)
+0.321: linux-2.2.5-:i386-:-:ppro-:- (Thanks SM Moret)
+0.321: sunos-5.6-generic_105181-10-:sparc-:sun4-:sun4u-:sun4u- (Tx JA Marshall)
+0.322: aix-4-2-:-:-:000720704c00-:- (Thanks TF)
+0.322: linux-2.0.35-:i386-:-:i486-:- (Thanks Robert Siemer)
+0.322: linux-2.0.36-:i386-:-:i486-:- (Thanks Ryan C. Hughes)
+0.322: linux-2.0.36-:i386-:-:pentium-:- (Thanks Kengo Nakajima)
+0.322: linux-2.2.10-:i386-:-:pentium-:- (Thanks Peter Green)
+0.322: linux-2.0.36-:i386-:-:ppro-:- (Thanks Jeff Hill)
+0.322: linux-2.2.5-15-:i386-:-:pentium-:- (Thanks Raj H.)
+0.322: linux-2.2.5-15-:i386-:-:ppro-:- (Thanks peter@mail.gradwell.com)
+0.322: linux-2.2.5-:i386-:-:ppro-:- (Thanks Achim Gosse)
+0.322: linux-2.2.9-:i386-:-:pentium-:- (Thanks FBR)
+0.322: netbsd-1.4-:sparc-:-:mb86900/1a.or.l64801.@.25.mhz,.wtl3170/2.fpu-:- (Tx R Nurges)
+0.322: sunos-5.6-generic_105181-05-:sparc-:sun4-:sun4u-:sun4u- (Tx Ralf Weber)
+0.322: sunos-5.6-generic_105181-13-:sparc-:sun4-:sun4u-:sun4u- (Tx MN Boyiazis)
+0.322: sunos-5.7-generic-:sparc-:sun4-:sun4u-:sun4u- (Tx Paul Theodoropoulos)
+0.322: sunos-5.7-generic_106541-02-:sparc-:sun4-:sun4u-:sun4u- (Tx Bo Fussing)
+0.323: linux-2.0.36-:i386-:-:pentium-:- (TX RVI)
+0.323: linux-2.2.3-:i386-:-:pentium-:- (TX Ludovico Magocavallo)
+0.323: linux-2.2.7-:i386-:-:ppro-:- (TX Achim Gosse)
+0.324: bsd.os-4.0.1-:i386-:-:-:- (Tx Peeter Pirn)
+0.324: freebsd-3.3-stable-:i386-:-:pentium/p54c-:- (Tx J B Bell)
+0.324: freebsd-4.0-19990816-current-:i386-:-:pentium.ii/xeon/celeron-:- (Tx MS)
+0.324: irix-6.5-04151556-:-:-:ip22-:- (Tx Claudio Nieder)
+0.324: linux-2.0.36-:i386-:-:pentium-:- (Tx Michael P. McMillan)
+0.324: linux-2.0.38-:i386-:-:pentium-:- (Tx Anton V. Bobykin)
+0.324: linux-2.2.1-:-:-:armv4l-:- (Tx Jim Zajkowski)
+0.324: linux-2.2.12-20-:i386-:-:ppro-:- (Tx Michael D. Mooney)
+0.324: linux-2.2.12-:i386-:-:p2-:- (TX Oden Eriksson)
+0.324: linux-2.2.13-:i386-:-:pentium-:- (Tx Tibor Szentmarjay)
+0.324: linux-2.2.5-22-:alpha-:-:alpha-:- (Tx Steve J. Borho)
+0.324: linux-2.2.5-22-:i386-:-:ppro-:- (Tx Yann M. Aubert)
+0.324: linux-2.2.7-:i386-:-:ppro-:- (Tx Brad W. Baker)
+0.324: openbsd-2.5-generic#172-:openbsd.sparc-:-:sparc-:- (Tx Grant L Miller)
+0.324: openbsd-2.5-generic#243-:openbsd.i386-:-:i386-:- (Tx Grant L Miller)
+0.324: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (Tx D Andrew Reynhout)
+0.40: freebsd-3.3-release-:i386-:-:prentium.iii-:- (Tx MAS)
+0.40: linux-2.2.11-:i386-:-:pentium-:- (Tx FBR)
+0.40: linux-2.2.5-22-:i386-:-:ppro-:- [RH6.0]
+0.40: linux-2.2.13-:i386-:-:ppro-:- [Debian] (Tx MAS)
+  
diff --git a/TARGETS b/TARGETS
new file mode 100644 (file)
index 0000000..6ae1653
--- /dev/null
+++ b/TARGETS
@@ -0,0 +1,191 @@
+alloc.a
+alloc.o
+alloc_re.o
+author.o
+auto-ccld.sh
+auto-str
+auto-str.o
+auto_bin.c
+auto_bin.o
+auto_cron.c
+auto_cron.o
+auto_qmail.c
+auto_qmail.o
+byte_chr.o
+byte_copy.o
+byte_cr.o
+byte_diff.o
+byte_rchr.o
+byte_zero.o
+case.a
+case_diffb.o
+case_diffs.o
+case_lowerb.o
+case_startb.o
+case_starts.o
+checktag.o
+compile
+concatHDR.o
+constmap.o
+cookie.o
+copy.o
+date2yyyymm.o
+date822fmt.o
+dateline.o
+datetime.o
+decodeB.o
+decodeHDR.o
+decodeQ.o
+direntry.h
+encodeB.o
+encodeQ.o
+env.a
+env.o
+envread.o
+error.a
+error.o
+error_str.o
+ezmlm-accept
+ezmlm-archive
+ezmlm-archive.o
+ezmlm-cgi
+ezmlm-cgi.o
+ezmlm-check
+ezmlm-clean
+ezmlm-clean.o
+ezmlm-cron
+ezmlm-cron.o
+ezmlm-gate
+ezmlm-gate.o
+ezmlm-get
+ezmlm-get.o
+ezmlm-glconf
+ezmlm-idx
+ezmlm-idx.o
+ezmlm-issubn
+ezmlm-issubn.o
+ezmlm-limit
+ezmlm-limit.o
+ezmlm-list
+ezmlm-list.o
+ezmlm-make
+ezmlm-make.o
+ezmlm-manage
+ezmlm-manage.o
+ezmlm-moderate
+ezmlm-moderate.o
+ezmlm-reject
+ezmlm-reject.o
+ezmlm-request
+ezmlm-request.o
+ezmlm-return
+ezmlm-return.o
+ezmlm-send
+ezmlm-send.o
+ezmlm-split
+ezmlm-split.o
+ezmlm-store
+ezmlm-store.o
+ezmlm-sub
+ezmlm-sub.o
+ezmlm-test
+ezmlm-tstdig
+ezmlm-tstdig.o
+ezmlm-unsub
+ezmlm-unsub.o
+ezmlm-warn
+ezmlm-warn.o
+ezmlm-weed
+ezmlm-weed.o
+ezmlmrc
+fd.a
+fd_copy.o
+fd_move.o
+find-systype
+fmt_str.o
+fmt_uint.o
+fmt_uint0.o
+fmt_ulong.o
+fork.h
+fs.a
+getconf.o
+getln.a
+getln.o
+getln2.o
+getopt.a
+hasflock.h
+hassgact.h
+idxthread.o
+issub.o
+load
+lock.a
+lock_ex.o
+log.o
+logmsg.o
+make-compile
+make-load
+make-makelib
+makehash.o
+makelib
+mime.a
+now.o
+open.a
+open_append.o
+open_read.o
+open_trunc.o
+opensql.o
+putsubs.o
+qmail.o
+quote.o
+scan_8long.o
+scan_ulong.o
+searchlog.o
+seek.a
+seek_set.o
+sgetopt.o
+sig.a
+sig_catch.o
+sig_pipe.o
+slurp.o
+slurpclose.o
+str.a
+str_chr.o
+str_cpy.o
+str_diff.o
+str_diffn.o
+str_len.o
+str_rchr.o
+str_start.o
+stralloc.a
+stralloc_arts.o
+stralloc_cat.o
+stralloc_catb.o
+stralloc_cats.o
+stralloc_copy.o
+stralloc_eady.o
+stralloc_opyb.o
+stralloc_opys.o
+stralloc_pend.o
+strerr.a
+strerr.o
+strerr_die.o
+strerr_sys.o
+subdb.a
+subfderr.o
+subgetopt.o
+subscribe.o
+substdi.o
+substdio.a
+substdio.o
+substdio_copy.o
+substdo.o
+surf.a
+surf.o
+surfpcs.o
+systype
+tagmsg.o
+uint32.h
+unfoldHDR.o
+wait.a
+wait_pid.o
+yyyymm.a
diff --git a/UPGRADE.idx b/UPGRADE.idx
new file mode 100644 (file)
index 0000000..3e18bc2
--- /dev/null
@@ -0,0 +1,190 @@
+$Id: UPGRADE.idx,v 1.39 1999/12/19 16:47:30 lindberg Exp $
+$Name: ezmlm-idx-040 $
+
+Like any other piece of software (and information generally), ezmlm-idx
+comes with NO WARRANTY.
+
+This file is for upgrading from earlier version of ezmlm-idx/mod. If you use
+ezmlm-0.53 but have not installed a previous version of ezmlm-idx,
+see INSTALL.idx. For details on what's new in this version, see CHANGES.idx.
+
+1. Proceed as per INSTALL.idx.
+   NOTE: If you follow the test instructions in INSTALL of ezmlm-0.53 after
+   adding ezmlm-idx, step 6 will fail. Before this step, edit
+   ~/testlist/editor and remove the ezmlm-reject line. 
+
+
+Steps required for upgrading from ezmlm-idx-0.31x/32x
+-----------------------------------------------------
+In the unlikely event that you have lists that have been manually configured
+to use ezmlm-gate (sender check/moderation combination) using only a single
+directory, add the same directory to the command line a second time. If you
+do not do this, all messages will be moderated, i.e. no major problem. No
+other changes are required, but you may want to use some of the new features
+(see CHANGES.idx).
+
+See below to migrate customized ezmlmrc files.
+
+Run ezmlm-idx on your lists. The index file format has changed. The old
+format will still work, but only the new format can be used with WWW archive
+access.
+
+To enable WWW archive access for your list(s) see the "Optional" section in
+INSTALL.idx.
+
+If you used MySQL support, you need to update the tables. A few new fields
+have been added for performance moitoring and billing. sub_mysql/to40x will
+generate the necessary SQL. For each list, do:
+
+sh sub_mysql/to40x list | mysql ...
+
+where ``list'' is the tableroot for the list.
+
+Additional steps for upgrading from ezmlm-idx-0.30x
+---------------------------------------------------
+
+If your old lists use SENDER restriction on posts with aliases (DIR/extra)
+and blacklisted addresses (DIR/blacklist) you need to change the name of
+these directories to DIR/allow and DIR/deny, respectively, and rebuild the list
+(see below). Without this change the list will still function normally, but
+addresses cannot be added/removed from these addresses remotely (e.g. via
+list-allow-subscribe). Other than this, your lists will function without
+modifications if you are upgrading from ezmlm-idx>=0.30.
+
+See below for changing the names of the extra databases.
+
+
+Additional steps for upgrading from ezmlm-idx<0.30
+--------------------------------------------------
+1.  Reindex the subject index for existing lists:
+
+       % ezmlm-idx DIR
+
+    for each list directory 'DIR'.
+
+2.  For existing digest lists, assuming the list "joe-sos" and the digest list
+    "joe-sos-digest".
+
+    a. Remove links to the digest list:
+
+       % rm -f ~joe/.qmail-sos-digest*
+
+    b. edit the main list to include a digest list:
+
+       % ezmlm-make -edxxx ~joe/SOS ~joe/.qmail joe-sos id.com gaga
+
+       where 'xxx' are switches used in creating the original list. For lists
+       created with ezmlm-idx-0.23 or later, the arguments after the list
+       directory may be omitted.
+
+    c. Move the digest subscriber info:
+
+       % mv ~joe/SOS-digest/subscribers/* ~joe/SOS/digest/subscribers
+
+    The digest list bounce info will be lost, as the key used for the new
+    digest is the same as for the list, and different for that from the old
+    digest. At worst, this will make the info for a digest user on missed
+    digests incomplete. It may also slightly delay the removal
+    of a permanently bouncing subscriber addresses.
+
+
+Additional steps for upgrading from ezmlm-idx<0.23x
+---------------------------------------------------
+For subscription-moderated lists created with ezmlm-idx<0.23:
+
+1.  If you have subscription moderated lists created with ezmlm-idx<0.23,
+    they likely lack dir/text/mod-sub-confirm and dir/text/mod-unsub-confirm.
+    Ezmlm-idx-0.23 substituted dir/text/(un)sub-confirm if the above files were
+    missing, but ezmlm-idx>=0.30 will not work without these files. Again,
+    ezmlm-make -e is the easiest way to update the list. Alternatively,
+    just copy dir/text/(un)sub-confirm to dir/text/mod-(un)sub-confirm.
+
+------------------------------------------------------------------------------
+That's it! To report success (this helps to track platform-specific problems):
+
+       % ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
+         | mail cfl-src@id.wustl.edu
+
+Replace First M. Last with your name.
+
+Send bugs reports, ideally with patch, to 'lindberg@id.wustl.edu'.
+
+
+------------------------------------------------------------------------------
+
+
+ Additional information
+------------------------
+
+Migrating customized ezmlmrc files
+==================================
+
+ezmlmrc is upgraded from ezmlm-idx-0.31x/32x, but the changes are not required
+for using the list.
+
+The changes from ezmlm-0.30x are minimal, but fix a couple of bugs, see
+CHANGES.idx). If you have custom ezmlmrc files and wish to update them,
+do the following:
+
+    First create a diff between the old ezmlmrc distribution and the
+    custom ezmlmrc file:
+
+       % cp /usr/local/bin/ezmlm/ezmlmrc ~/ezmlmrc.orig
+       % cp ~/.ezmlmrc ~/ezmlmrc
+       % diff -c ~/ezmlmrc.orig ~/ezmlmrc ~/ezmlmrc.diff
+
+    Next, apply the changes to the new ezmlmrc:
+
+       % cp .../ezmlm-idx-0.33/ezmlmrc ~/ezmlmrc
+       % patch ~/ezmlmrc < ~/ezmlmrc.diff
+
+    Next check for rejected parts (you need to apply them manually):
+
+       % cat ~/ezmlmrc.rej
+
+Any existing list can be edited with 'ezmlm-make -e dir dot local host [code]'
+with the appropriate switches to take advantage of new ezmlm functions. If
+you make ezmlmrc files in other languages, please make them public domain and
+mail them to lindberg@id.wustl.edu for inclusion in future versions of
+ezmlm-idx.
+
+Adjusting the name of the extra databases (from ezmlm-idx<0.30):
+===============================================================
+
+       % mv DIR/extra DIR/allow
+       % mv DIR/blacklist DIR/deny
+       % ezmlm-make -+ DIR
+
+Additional steps required when upgrading from ezmlm-idx<0.30:
+
+1.  Reindex the subject index for existing lists:
+
+       % ezmlm-idx DIR
+
+    for each list directory 'DIR'.
+
+2.  For existing digest lists, assuming the list "joe-sos" and the digest list
+    "joe-sos-digest".
+
+    a. Remove links to the digest list:
+
+       % rm -f ~joe/.qmail-sos-digest*
+
+    b. edit the main list to include a digest list:
+
+       % ezmlm-make -edxxx ~joe/SOS ~joe/.qmail joe-sos id.com gaga
+
+       where 'xxx' are switches used in creating the original list. For lists
+       created with ezmlm-idx-0.23 or later, the arguments after the list
+       directory may be omitted.
+
+    c. Move the digest subscriber info:
+
+       % mv ~joe/SOS-digest/subscribers/* ~joe/SOS/digest/subscribers
+
+    The digest list bounce info will be lost, as the key used for the new
+    digest is the same as for the list, and different for that from the old
+    digest. At worst, this will make the info for a digest user on missed
+    digests incomplete. It may also slightly delay the removal
+    of a permanently bouncing subscriber addresses.
+
diff --git a/VERSION b/VERSION
index 6049539..a01a1fd 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1,2 @@
 ezmlm 0.53
+$Name: ezmlm-idx-040 $
diff --git a/author.c b/author.c
new file mode 100644 (file)
index 0000000..c5bf0dc
--- /dev/null
+++ b/author.c
@@ -0,0 +1,103 @@
+/*$Id: author.c,v 1.3 1999/09/29 03:11:44 lindberg Exp $*/
+/*$Name:*/
+
+unsigned int author_name(char **sout,char *s,unsigned int l)
+/* s is a string that contains a from: line argument\n. We parse */
+/* s as follows: If there is an unquoted '<' we eliminate everything after */
+/* it else if there is a unquoted ';' we eliminate everything after it.    */
+/* Then, we eliminate LWSP and '"' from the beginning and end. Note that   */
+/* this is not strict rfc822, but all we need is a display handle that     */
+/* doesn't show the address. If in the remaining text there is a '@' we put*/
+/* in a '.' instead. Also, there are some non-rfc822 from lines out there  */
+/* and we still want to maximize the chance of getting a handle, even if it*/
+/* costs a little extra work.*/
+{
+  int squote = 0;
+  int dquote = 0;
+  int level = 0;
+  int flagdone;
+  unsigned int len;
+  char ch;
+  char *cpfirst,*cp;
+  char *cpcomlast = 0;
+  char *cpquotlast = 0;
+  char *cpquot = 0;
+  char *cpcom = 0;
+  char *cplt = 0;
+
+  if (!s || !l) {      /* Yuck - pass the buck */
+    *sout = s;
+    return 0;
+  }
+  cp = s; len = l;
+
+  while (len--) {
+    ch = *(cp++);
+    if (squote) {
+      squote = 0;
+      continue;
+    }
+    if (ch == '\\') {
+      squote = 1;
+      continue;
+    }
+    if (ch == '"') {           /* "name" <address@host> */
+      if (dquote) {
+       cpquotlast = cp - 2;
+        break;
+      } else {
+       cpquot = cp;
+        dquote = 1;
+      }
+      continue;
+    } else if (dquote) continue;
+    if (ch == '(') {
+       if (!level) cpcom = cp;
+       level++;
+    } else if (ch == ')') {
+       level--;
+       if (!level)
+         cpcomlast = cp - 2;   /* address@host (name) */
+    } else if (!level) {
+      if (ch == '<') {         /* name <address@host> */
+       cplt = cp - 2;
+       break;
+      } else if (ch == ';') break;     /* address@host ;garbage */
+    }
+  }
+  if (cplt) {                  /* non-comment '<' */
+    cp = cplt;
+    cpfirst = s;
+  } else if (cpquot && cpquotlast >= cpquot) {
+    cpfirst = cpquot;
+    cp = cpquotlast;
+  } else if (cpcom && cpcomlast >= cpcom) {
+    cpfirst = cpcom;
+    cp = cpcomlast;
+  } else {
+    cp = s + l - 1;
+    cpfirst = s;
+  }
+  flagdone = 0;
+  for (;;) {           /* e.g. LWSP <user@host> */
+    while (cpfirst <= cp &&
+       (*cpfirst == ' ' || *cpfirst == '\t' || *cpfirst == '<')) cpfirst++;
+    while (cp >= cpfirst &&
+       (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '>')) cp--;
+    if (cp >= cpfirst || flagdone)
+      break;
+    cp = s + l - 1;
+    cpfirst = s;
+    flagdone = 1;
+  }
+
+  *sout = cpfirst;
+  len = cp - cpfirst + 1;
+  while (cpfirst <= cp) {
+    if (*cpfirst == '@')
+      *cpfirst = '.';
+    cpfirst++;
+  }
+  return len;
+}
+
diff --git a/auto_cron.h b/auto_cron.h
new file mode 100644 (file)
index 0000000..bdd9387
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef AUTO_CRON_H
+#define AUTO_CRON_H
+
+extern char auto_cron[];
+
+#endif
diff --git a/case_diffs.c b/case_diffs.c
new file mode 100644 (file)
index 0000000..212c645
--- /dev/null
@@ -0,0 +1,19 @@
+#include "case.h"
+
+int case_diffs(s,t)
+register char *s;
+register char *t;
+{
+  register unsigned char x;
+  register unsigned char y;
+
+  for (;;) {
+    x = *s++ - 'A';
+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+    y = *t++ - 'A';
+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+    if (x != y) break;
+    if (!x) break;
+  }
+  return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+}
diff --git a/case_starts.c b/case_starts.c
new file mode 100644 (file)
index 0000000..2278a0a
--- /dev/null
@@ -0,0 +1,18 @@
+#include "case.h"
+
+int case_starts(s,t)
+register char *s;
+register char *t;
+{
+  register unsigned char x;
+  register unsigned char y;
+
+  for (;;) {
+    x = *s++ - 'A';
+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+    y = *t++ - 'A';
+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+    if (!y) return 1;
+    if (x != y) return 0;
+  }
+}
diff --git a/checktag.c b/checktag.c
new file mode 120000 (symlink)
index 0000000..fe4e410
--- /dev/null
@@ -0,0 +1 @@
+sub_std/checktag.c
\ No newline at end of file
diff --git a/concatHDR.c b/concatHDR.c
new file mode 100644 (file)
index 0000000..604892c
--- /dev/null
@@ -0,0 +1,45 @@
+/*$Id: */
+/*$Name: ezmlm-idx-040 $*/
+
+#include "stralloc.h"
+#include "strerr.h"
+#include "case.h"
+#include "byte.h"
+#include "mime.h"
+#include "errtxt.h"
+
+void concatHDR(indata,n,outdata,fatal)
+char *indata;
+unsigned int n;
+stralloc *outdata;
+char *fatal;
+/* takes a concatenated string of line and continuation line, trims leading */
+/* and trailing LWSP and collapses line breaks and surrounding LWSP to ' '. */
+/* indata has to end in \n or \0 or this routine will write beyond indata!  */
+/* if indata ends with \0, this will be changed to \n. */
+
+{
+  register char *cp,*cpout;
+  char *cplast;
+  if (!stralloc_copys(outdata,"")) die_nomem(fatal);
+  if (!stralloc_ready(outdata,n)) die_nomem(fatal);
+  cpout = outdata->s;
+  if (n == 0) return;
+  cplast = indata + n - 1;
+  cp = cplast;
+  while (*cplast == '\0' || *cplast == '\n') --cplast;
+  if (cp == cplast) die_nomem(fatal);          /* just in case */
+  *(++cplast) = '\n';                          /* have terminal '\n' */
+  cp = indata;
+  while (cp <= cplast) {
+    while (*cp == ' ' || *cp == '\t') ++cp;    /* LWSP before */
+    while (*cp != '\n') *(cpout++) = *(cp++);  /* text */
+    ++cp;                                      /* skip \n */ 
+    --cpout;                                   /* last char */
+    while (*cpout == ' ' || *cpout == '\t') --cpout;   /* LWSP after */
+    *(++cpout) = ' ';                          /* replace with single ' ' */
+    ++cpout;                                   /* point to free byte */
+  }
+  outdata->len = cpout - outdata->s;
+}
+
diff --git a/conf-cron b/conf-cron
new file mode 100644 (file)
index 0000000..612a7e3
--- /dev/null
+++ b/conf-cron
@@ -0,0 +1,3 @@
+/usr/bin
+
+This is the directory housing the crontab binary
diff --git a/conf-sqlcc b/conf-sqlcc
new file mode 120000 (symlink)
index 0000000..6d13424
--- /dev/null
@@ -0,0 +1 @@
+sub_std/conf-sqlcc
\ No newline at end of file
diff --git a/conf-sqlld b/conf-sqlld
new file mode 120000 (symlink)
index 0000000..7432a47
--- /dev/null
@@ -0,0 +1 @@
+sub_std/conf-sqlld
\ No newline at end of file
index 722e3b8..8c742c2 100644 (file)
@@ -18,6 +18,39 @@ int len;
   return h;
 }
 
+/* Returns index of string in constmap. 1 = first string, 2 = second ... */
+/* 0 not found. Use for commands */ 
+int constmap_index(cm,s,len)
+struct constmap *cm;
+char *s;
+int len;
+{
+  constmap_hash h;
+  int pos;
+  h = hash(s,len);
+  pos = cm->first[h & cm->mask];
+  while (pos != -1) {
+    if (h == cm->hash[pos])
+      if (len == cm->inputlen[pos])
+        if (!case_diffb(cm->input[pos],len,s))
+         return pos + 1;
+    pos = cm->next[pos];
+  }
+  return 0;
+}
+
+/* returns pointer to sz of string with index "idx". 1 = first, 2 = second...*/
+char *constmap_get(cm,idx)
+struct constmap *cm;
+int idx;
+
+{
+  if (idx <= 0 || idx > cm->num)
+    return 0;
+  else
+    return cm->input[idx-1];
+}
+
 char *constmap(cm,s,len)
 struct constmap *cm;
 char *s;
@@ -38,6 +71,9 @@ int len;
 }
 
 int constmap_init(cm,s,len,flagcolon)
+/* if flagcolon is true, we process only the stuff before the colon on */
+/* each line. Otherwise, it's the entire line. Still, the entire line */
+/* is stored! */
 struct constmap *cm;
 char *s;
 int len;
index 3f29179..66be99f 100644 (file)
@@ -16,5 +16,6 @@ struct constmap {
 extern int constmap_init();
 extern void constmap_free();
 extern char *constmap();
-
+extern char *constmap_get();
+extern int constmap_index();
 #endif
diff --git a/copy.c b/copy.c
new file mode 100644 (file)
index 0000000..24f0034
--- /dev/null
+++ b/copy.c
@@ -0,0 +1,211 @@
+/*$Id: copy.c,v 1.10 1999/08/07 19:28:16 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+/* Copies a file relative the current directory and substitutes    */
+/* !A at the beginning of a line for the target,                   */
+/* !R at the beginning of a line for the confirm reply address,    */
+/* The following substitutions are also made. If not set, ?????    */
+/* will be printed:  <#l#> outlocal                                */
+/* will be printed:  <#h#> outhost                                 */
+/* will be printed:  <#n#> outmsgnum                               */
+/* Other tags are killed, e.g. removed. A missing file is a        */
+/* permanent error so owner finds out ASAP. May not have access to */
+/* maillog. Content transfer encoding is done for 'B' and 'Q'. For */
+/* 'H' no content transfer encoding is done, but blank lines are   */
+/* suppressed. Behavior for other codes is undefined. This includes*/
+/* lower case 'q'/'b'! If code is 'H' substitution of target and   */
+/* verptarget is prevented as it may create illegal headers.       */
+
+#include "stralloc.h"
+#include "substdio.h"
+#include "strerr.h"
+#include "str.h"
+#include "getln.h"
+#include "case.h"
+#include "readwrite.h"
+#include "qmail.h"
+#include "errtxt.h"
+#include "error.h"
+#include "quote.h"
+#include "copy.h"
+#include "mime.h"
+                       /* for public setup functions only */
+#define FATAL "copy: fatal: "
+
+static stralloc line = {0};
+static stralloc outline = {0};
+static stralloc qline = {0};
+static stralloc outlocal = {0};
+static stralloc outhost = {0};
+static substdio sstext;
+static char textbuf[256];
+static char *target = "?????";
+static char *verptarget = "?????";
+static char *confirm = "?????";
+static char *szmsgnum = "?????";
+
+void set_cpoutlocal(ln)
+stralloc *ln;
+{      /* must be quoted for safety. Note that substitutions that use */
+       /* outlocal within an atom may create illegal addresses */
+  if (!quote(&outlocal,ln))
+        strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
+void set_cpouthost(ln)
+stralloc *ln;
+{
+  if (!stralloc_copy(&outhost,ln))
+        strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
+void set_cptarget(tg)
+char *tg;
+{
+  target = tg;
+}
+
+void set_cpverptarget(tg)
+char *tg;
+{
+  verptarget = tg;
+}
+
+void set_cpconfirm(cf)
+char *cf;
+{
+  confirm = cf;
+}
+
+void set_cpnum(cf)
+char *cf;
+{
+  szmsgnum = cf;
+}
+
+static struct qmail *qq;
+
+static void codeput(l,n,code,fatal)
+char *l;
+unsigned int n;
+char code;
+char *fatal;
+
+{
+  if (!code || code == 'H')
+    qmail_put(qq,l,n);
+  else {
+    if (code == 'Q')
+      encodeQ(l,n,&qline,fatal);
+    else
+      encodeB(l,n,&qline,0,fatal);
+    qmail_put(qq,qline.s,qline.len);
+  }
+}
+
+static void codeputs(l,code,fatal)
+char *l;
+char code;
+char *fatal;
+{
+  codeput(l,str_len(l),code,fatal);
+}
+
+void copy(qqp,fn,q,fatal)
+struct qmail *qqp;
+char *fn;              /* text file name */
+char q;                        /* = '\0' for regular output, 'B' for base64, */
+                       /* 'Q' for quoted printable,'H' for header    */
+char *fatal;           /* FATAL error string */
+
+{
+  int fd;
+  int match, done;
+  unsigned int pos,nextpos;
+
+  qq = qqp;
+  if ((fd = open_read(fn)) == -1)
+    if (errno != error_noent)
+      strerr_die4sys(111,fatal,ERR_OPEN,fn,": ");
+    else
+      strerr_die4sys(100,fatal,ERR_OPEN,fn,": ");
+  substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+  for (;;) {
+    if (getln(&sstext,&line,&match,'\n') == -1)
+      strerr_die4sys(111,fatal,ERR_READ,fn,": ");
+    if (match) {       /* suppress blank line for 'H'eader mode */
+      if (line.len == 1 && q == 'H') continue;
+      if (line.s[0] == '!') {
+       if (line.s[1] == 'R') {
+         codeput("   ",3,q,fatal);
+         codeputs(confirm,q,fatal);
+         codeput("\n",1,q,fatal);
+         continue;
+       }
+       if (line.s[1] == 'A') {
+         codeput("   ",3,q,fatal);
+         codeputs(target,q,fatal);
+         codeput("\n",1,q,fatal);
+         continue;
+       }
+      }
+               /* Find tags <#x#>. Replace with for x=R confirm, for x=A */
+               /* target, x=l outlocal, x=h outhost. For others, just    */
+               /* skip tag. If outlocal/outhost are not set, the tags are*/
+               /* skipped. If confirm/taget are not set, the tags are    */
+               /* replaced by "???????" */
+      pos = 0;
+      nextpos = 0;
+      done = 0;
+      outline.len = 0;                 /* zap outline */
+      while ((pos += byte_chr(line.s+pos,line.len-pos,'<')) != line.len) {
+        if (pos + 4 < line.len &&
+            line.s[pos+1] == '#' &&
+            line.s[pos+3] == '#' &&
+            line.s[pos+4] == '>') {    /* tag. Copy first part of line */
+          done = 1;                            /* did something */
+          if (!stralloc_catb(&outline,line.s+nextpos,pos-nextpos))
+                        die_nomem(fatal);
+          switch(line.s[pos+2]) {
+            case 'A':
+             if (q == 'H') strerr_die(111,ERR_SUBST_UNSAFE);
+              if (!stralloc_cats(&outline,target)) die_nomem(fatal);
+              break;
+            case 'R':
+              if (!stralloc_cats(&outline,confirm)) die_nomem(fatal);
+              break;
+            case 'l':
+              if (!stralloc_cat(&outline,&outlocal)) die_nomem(fatal);
+              break;
+            case 'h':
+              if (!stralloc_cat(&outline,&outhost)) die_nomem(fatal);
+              break;
+            case 't':
+             if (q == 'H') strerr_die(111,ERR_SUBST_UNSAFE);
+              if (!stralloc_cats(&outline,verptarget)) die_nomem(fatal);
+              break;
+            case 'n':
+              if (!stralloc_cats(&outline,szmsgnum)) die_nomem(fatal);
+              break;
+            default:
+              break;                   /* unknown tags killed */
+          }
+          pos += 5;
+          nextpos = pos;
+        } else
+          ++pos;                               /* try next position */
+      }
+      if (!done)
+        codeput(line.s,line.len,q,fatal);
+      else {
+        if (!stralloc_catb(&outline,line.s+nextpos,line.len-nextpos))
+               die_nomem(fatal);               /* remainder */
+        codeput(outline.s,outline.len,q,fatal);
+      }
+
+    } else
+      break;
+  }
+  close(fd);
+}
+
diff --git a/copy.h b/copy.h
new file mode 100644 (file)
index 0000000..3a642eb
--- /dev/null
+++ b/copy.h
@@ -0,0 +1,11 @@
+#ifndef COPY_H
+#define COPY_H
+
+/* copy (qq,fn,fatal) */
+extern void copy();
+extern void set_cpoutlocal();
+extern void set_cpouthost();
+extern void set_cptarget();
+extern void set_cpconfirm();
+extern void set_cpnum();
+#endif
diff --git a/date2yyyymm.c b/date2yyyymm.c
new file mode 100644 (file)
index 0000000..dd6ea87
--- /dev/null
@@ -0,0 +1,71 @@
+
+/*$Id: date2yyyymm.c,v 1.1 1999/10/09 16:45:43 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "yyyymm.h"
+
+unsigned int date2yyyymm(s)
+char *s;
+/* expects a qmail date string s and returns yyyymm */
+/* if there are problems, it returns 0. If there is no terminating char */
+/* we may segfault if the syntax is bad. Assure that the ';' is there   */
+/* or add '\0' */
+{
+  unsigned int mo;
+  unsigned int year;   /* must hold yyyymm - ok to year 65K */
+  char ch,ch1,ch2;
+
+/* jan feb mar apr may jun jul aug sep oct nov dec */
+/* - strictly qmail datefmt dependent*/
+  for (;;s++) {
+    ch = *s;
+    if (ch != ' ' && (ch < '0' || ch > '9')) break;
+  }
+  mo = 0;
+  if (!(ch = *(s++))) return 0;
+  if (ch >= 'a')  ch -= ('a' - 'A');   /* toupper */
+  if (!(ch1 = *(s++))) return 0;       /* rfc822 hrds are case-insens */
+  if (ch1 >= 'a')  ch1 -= ('a' - 'A');
+  if (!(ch2 = *(s++))) return 0;
+  if (ch2 >= 'a')  ch2 -= ('a' - 'A');
+
+  switch (ch) {
+    case 'J':
+       if (ch1 == 'A' && ch2 == 'N') { mo = 1; break; }
+       if (ch1 == 'U') {
+         if (ch2 == 'N') mo = 6;
+         else if (ch2 == 'L') mo = 7;
+       }
+       break;
+    case 'F': if (ch1 == 'E' && ch2 == 'B') mo = 2; break;
+    case 'A':
+       if (ch1 == 'P' && ch2 == 'R') mo = 4;
+       else if (ch1 == 'U' && ch2 == 'G') mo = 8;
+       break;
+    case 'M':
+       if (ch1 != 'A') break;
+       if (ch2 == 'R') mo = 3;
+       else if (ch2 == 'Y') mo = 5;
+       break;
+    case 'S': if (ch1 == 'E' && ch2 == 'P') mo = 9; break;
+    case 'O': if (ch1 == 'C' && ch2 == 'T') mo = 10; break;
+    case 'N': if (ch1 == 'O' && ch2 == 'V') mo = 11; break;
+    case 'D': if (ch1 == 'E' && ch2 == 'C') mo = 12; break;
+    default:
+       break;
+  }
+  if (!mo || *(s++) != ' ')
+    return 0L;         /* mo true means s[0-2] valid */
+  year = 0L;
+  for (;;) {
+    register unsigned char chy;
+    chy = (unsigned char) *(s++);
+    if (chy < '0' || chy > '9') {
+      if (year) break;
+      else return 0;
+    }
+    year = year * 10 + (chy - '0');
+  }
+  return year * 100 + mo;
+}
+
+
diff --git a/dateline.c b/dateline.c
new file mode 100644 (file)
index 0000000..77a11bd
--- /dev/null
@@ -0,0 +1,41 @@
+/*$Id: dateline.c,v 1.2 1999/10/09 17:44:37 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include "fmt.h"
+#include "yyyymm.h"
+#include "stralloc.h"
+
+static char strnum[FMT_ULONG];
+
+int dateline(dt,d)
+stralloc *dt; unsigned long d;
+/* converts yyyymm from unsigned long d to text dt */
+{
+  char *mo;
+  switch (d % 100) {
+    case 1: mo = "January"; break;
+    case 2: mo = "February"; break;
+    case 3: mo = "March"; break;
+    case 4: mo = "April"; break;
+    case 5: mo = "May"; break;
+    case 6: mo = "June"; break;
+    case 7: mo = "July"; break;
+    case 8: mo = "August"; break;
+    case 9: mo = "September"; break;
+    case 10: mo = "October"; break;
+    case 11: mo = "November"; break;
+    case 12: mo = "December"; break;
+    case 0: mo = "????"; break;
+    default: cgierr("I don't know any month > 12",
+               "","");
+  }
+  if (!stralloc_copys(dt,mo)) return -1;
+  if (!stralloc_cats(dt," ")) return -1;
+  if ((d/100)) {
+    if (!stralloc_catb(dt,strnum,fmt_ulong(strnum,d/100))) return -1;
+  } else
+    if (!stralloc_cats(dt,"????")) return 0;
+  return 1;
+}
+
+
diff --git a/decodeB.c b/decodeB.c
new file mode 100644 (file)
index 0000000..78406bc
--- /dev/null
+++ b/decodeB.c
@@ -0,0 +1,71 @@
+#include "stralloc.h"
+#include "strerr.h"
+#include "uint32.h"
+#include "errtxt.h"
+
+       /* Characters and translation as per rfc2047. */
+static char char64table[128] = {
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+
+#define char64enc(c)  (((c) & 0x80) ? -1 : char64table[(c)])
+
+static void die_nomem(fatal)
+  char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+void decodeB(cpfrom,n,outdata,fatal)
+char *cpfrom;
+unsigned int n;
+stralloc *outdata;
+char *fatal;
+/* does B decoding of the string pointed to by cpfrom up to the character */
+/* before the one pointed to by cpnext, and appends the results to mimeline*/
+{
+  uint32 hold32;
+  char holdch[4] = "???";
+  int i,j;
+  char c;      /* needs to be signed */
+  char *cp, *cpnext;
+
+  cp = cpfrom;
+  cpnext = cp + n;
+  i = 0;
+  hold32 = 0L;
+  if (!stralloc_readyplus(outdata,n)) die_nomem(fatal);
+  for (;;) {
+    if (i == 4) {
+      for (j = 2; j >= 0; --j) {
+        holdch[j] = hold32 & 0xff;
+        hold32 = hold32 >> 8;
+      }
+      if (!stralloc_cats(outdata,holdch)) die_nomem(fatal);
+      if (cp >= cpnext)
+        break;
+      hold32 = 0L;
+      i = 0;
+    }
+    if (cp >= cpnext) {        /* pad */
+      c = 0;
+    } else {
+      c = char64enc(*cp);
+      ++cp;
+    }
+    if (c < 0)         /* ignore illegal characters */
+      continue;
+    else {
+      hold32 = (hold32 << 6) | (c & 0x7f);
+      ++i;
+    }
+  }
+}
+
diff --git a/decodeHDR.c b/decodeHDR.c
new file mode 100644 (file)
index 0000000..4693d3d
--- /dev/null
@@ -0,0 +1,92 @@
+/*$Id: decodeHDR.c,v 1.2 1998/02/28 19:03:02 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include "stralloc.h"
+#include "strerr.h"
+#include "error.h"
+#include "case.h"
+#include "byte.h"
+#include "uint32.h"
+#include "mime.h"
+#include "errtxt.h"
+
+static void die_nomem(fatal)
+  char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+void decodeHDR(indata,n,outdata,charset,fatal)
+char *indata;
+unsigned int n;
+stralloc *outdata;
+char *charset;
+char *fatal;
+
+/* decodes indata depending on charset. May put '\n' and '\0' into out */
+/* data and can take them as indata. */
+{
+  unsigned int pos;
+  char *cp,*cpnext,*cpstart,*cpenc,*cptxt,*cpend,*cpafter;
+
+  cpnext = indata;
+  cpafter = cpnext + n;
+  cpstart = cpnext;
+  if (!stralloc_copys(outdata,"")) die_nomem(fatal);
+  if (!stralloc_ready(outdata,n)) die_nomem(fatal);
+  for (;;) {
+    cpstart = cpstart + byte_chr(cpstart,cpafter-cpstart,'=');
+    if (cpstart == cpafter)
+      break;
+    ++cpstart;
+    if (*cpstart != '?')
+      continue;
+    ++cpstart;
+    cpenc = cpstart + byte_chr(cpstart,cpafter-cpstart,'?');
+    if (cpenc == cpafter)
+      continue;
+    cpenc++;
+    cptxt = cpenc + byte_chr(cpenc,cpafter-cpenc,'?');
+    if (cptxt == cpafter)
+      continue;
+    cptxt++;
+    cpend = cptxt + byte_chr(cptxt,cpafter-cptxt,'?');
+    if (cpend == cpafter || *(cpend + 1) != '=')
+      continue;
+       /* We'll decode anything. On lists with many charsets, this may */
+       /* result in unreadable subjects, but that's the case even if   */
+       /* no decoding is done. This way, the subject will be optimal   */
+       /* for threading, but charset info is lost. We aim to correctly */
+       /* decode us-ascii and all iso-8859/2022 charsets. Exacly how   */
+       /* these will be displayed depends on dir/charset.              */
+    cp = cpnext;
+                       /* scrap lwsp between coded strings */
+    while (*cp == ' ' || *cp == '\t')
+      cp++;
+    if (cp != cpstart - 2)
+      if (!stralloc_catb(outdata,cpnext, cpstart - cpnext - 2))
+               die_nomem(fatal);
+   cpnext = cp + 1;
+   cpstart = cpnext;
+          switch (*cpenc) {
+            case 'b':
+            case 'B':
+              pos = outdata->len;
+              decodeB(cptxt,cpend-cptxt,outdata,2,fatal);
+              cpnext = cpend + 2;
+              cpstart = cpnext;
+              break;
+            case 'q':
+            case 'Q':
+              decodeQ(cptxt,cpend-cptxt,outdata,fatal);
+              cpnext = cpend + 2;
+              cpstart = cpnext;
+              break;
+            default:           /* shouldn't happen, but let's be reasonable */
+              cpstart = cpend + 2;
+              break;
+          }
+  }
+  if (!stralloc_catb(outdata,cpnext,indata-cpnext+n)) die_nomem(fatal);
+}
+
diff --git a/decodeQ.c b/decodeQ.c
new file mode 100644 (file)
index 0000000..7e5d388
--- /dev/null
+++ b/decodeQ.c
@@ -0,0 +1,77 @@
+/*$Id: decodeQ.c,v 1.3 1998/10/29 21:48:24 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include "stralloc.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "mime.h"
+
+/* takes a string pointed to by cpfrom and adds the next 'n' bytes to        */
+/* outdata, replacing any Quoted-Printable codes with the real characters.   */
+/* NUL and LF in the input are allowed, but anything that decodes to these   */
+/* values is ignored. */
+
+static char char16table[128] = {
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+     0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
+    -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
+};
+
+#define char16enc(c)  (((c) & 0x80) ? -1 : char16table[(c)])
+
+static void die_nomem(fatal)
+  char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+void decodeQ(cpfrom,n,outdata,fatal)
+char *cpfrom;
+unsigned int n;
+stralloc *outdata;
+char *fatal;
+/* does Q decoding of the string pointed to by cpfrom up to the character */
+/* before the one pointed to by cpnext, and appends the results to mimeline*/
+{
+  char *cp,*cpnext,*cpmore;
+  char holdch[2];
+  char ch1,ch2;                /* need to be signed */
+
+  cpmore = cpfrom;
+  cp = cpfrom;
+  cpnext = cp + n;
+  if (!stralloc_readyplus(outdata,n)) die_nomem(fatal);
+
+  while (cp < cpnext) {
+    if (*cp == '_') *cp = ' ';         /* '_' -> space */
+    else if (*cp == '=') {             /* "=F8" -> '\xF8' */
+                                       /* copy stuff before */
+      if (!stralloc_catb(outdata,cpmore,cp-cpmore)) die_nomem(fatal);
+      cpmore = cp;
+      ++cp;
+      if (*cp == '\n') {               /* skip soft line break */
+        ++cp;
+        cpmore = cp;
+        continue;
+      }
+      ch1 = char16enc(*cp);
+      if (++cp >= cpnext)
+        break;
+      ch2 = char16enc(*cp);
+      if (ch1 >= 0 && ch2 >= 0) {      /* ignore illegals */
+        holdch[0] = (ch1 << 4 | ch2) & 0xff;
+        if (!stralloc_catb(outdata,holdch,1)) die_nomem(fatal);
+        cpmore += 3;
+      }
+    }
+    ++cp;
+  }                                    /* copy stuff after */
+  if (!stralloc_catb(outdata,cpmore,cpnext-cpmore)) die_nomem(fatal);
+}      
+
+
diff --git a/encodeB.c b/encodeB.c
new file mode 100644 (file)
index 0000000..1672de9
--- /dev/null
+++ b/encodeB.c
@@ -0,0 +1,99 @@
+/* $Id: encodeB.c,v 1.3 1998/03/21 18:30:27 lindberg Exp $*/
+/* $Name: ezmlm-idx-040 $*/
+
+#include "stralloc.h"
+#include "uint32.h"
+#include "mime.h"
+#include "strerr.h"
+#include "errtxt.h"
+
+static void die_nomem(fatal)
+  char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static unsigned char base64char[] =
+   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static unsigned int pos = 0;
+static unsigned int i = 0;
+static uint32 hold32;
+static unsigned char *cpout;
+
+static void addone(ch)
+unsigned char ch;
+{
+ if (!(pos++))
+    hold32 = (uint32) ch;
+  else
+    hold32 = (hold32 << 8) | ch;
+  if (pos == 3) {
+    *cpout++ = base64char[(hold32 >> 18) & 0x3f];
+    *cpout++ = base64char[(hold32 >> 12) & 0x3f];
+    *cpout++ = base64char[(hold32 >>  6) & 0x3f];
+    *cpout++ = base64char[hold32 & 0x3f];
+    if (++i == 18) {
+      *cpout++ = '\n';
+      i = 0;
+    }
+    pos = 0;
+  }
+}
+
+static void dorest()
+{
+  switch (pos) {
+    case 2:
+      hold32 = hold32 << 2;
+      *cpout++ = base64char[(hold32 >> 12) & 0x3f];
+      *cpout++ = base64char[(hold32 >> 06) & 0x3f];
+      *cpout++ = base64char[hold32 & 0x3f];
+      *cpout++ = '=';
+      break;
+    case 1:
+      hold32 = hold32 << 4;
+      *cpout++ = base64char[(hold32 >> 06) & 0x3f];
+      *cpout++ = base64char[hold32 & 0x3f];
+      *cpout++ = '=';
+      *cpout++ = '=';
+      break;
+    default:
+      break;
+  }
+  *cpout++ = '\n';
+}   
+
+void encodeB(indata,n,outdata,control,fatal)
+unsigned char *indata;
+unsigned int n;
+stralloc *outdata;
+int control;   /* 1 = init, 2 = flush */
+char *fatal;
+       /* converts any character with the high order bit set to */
+       /* base64. In: n chars of indata, out: stralloc outdata  */
+       /* as '=' is not allowed within the block, we cannot flush after */
+       /* each line, so we carry over data from call to call. The last  */
+       /* call to encodeB should have control = 2 to do the flushing.   */
+       /* control = 0 resets, and the routine starts out reset. */
+{
+  register unsigned char ch;
+
+  if (control == 1) {
+    pos = 0;
+    i = 0;
+  }
+  if (!stralloc_copys(outdata,"")) die_nomem(fatal);
+  if (!stralloc_ready(outdata,n*8/3 + n/72 + 5)) die_nomem(fatal);
+  cpout = (unsigned char *) outdata->s;
+  while (n--) {
+    ch = *indata++;
+    if (ch == '\n')
+      addone('\r');
+    addone(ch);
+  }
+  if (control == 2)
+    dorest();
+  outdata->len = (unsigned int) (cpout - (unsigned char *) outdata->s);
+}
+
diff --git a/encodeQ.c b/encodeQ.c
new file mode 100644 (file)
index 0000000..080b875
--- /dev/null
+++ b/encodeQ.c
@@ -0,0 +1,58 @@
+/* $Id: encodeQ.c,v 1.2 1998/02/28 19:03:02 lindberg Exp $*/
+/* $Name: ezmlm-idx-040 $*/
+
+#include "errtxt.h"
+#include "mime.h"
+#include "stralloc.h"
+#include "strerr.h"
+
+static void die_nomem(fatal)
+  char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static char *hexchar = "0123456789ABCDEF";
+
+void encodeQ(indata,n,outdata,fatal)
+char *indata;
+unsigned int n;
+stralloc *outdata;
+char *fatal;
+
+       /* converts any character with the high order bit set to */
+       /* quoted printable. In: n chars of indata, out: stralloc outdata*/
+
+{
+  register char *cpout;
+  register char ch;
+  unsigned int i;
+  char *cpin;
+
+  cpin = indata;
+  i = 0;
+       /* max 3 outchars per inchar  & 2 char newline per 72 chars */
+  if (!stralloc_copys(outdata,"")) die_nomem(fatal);
+  if (!stralloc_ready(outdata,n * 3 + n/36)) die_nomem(fatal); /* worst case */
+  cpout = outdata->s;
+  while (n--) {
+    ch = *cpin++;
+    if (ch != ' ' && ch != '\n' && ch != '\t' &&
+          (ch > 126 || ch < 33 || ch == 61)) {
+      *(cpout++) = '=';
+      *(cpout++) = hexchar[(ch >> 4) & 0xf];
+      *(cpout++) = hexchar[ch & 0xf];
+      i += 3;
+    } else {
+      if (ch == '\n')
+        i = 0;
+      *(cpout++) = ch;
+    }
+    if (i >= 72) {
+      *(cpout++) = '=';
+      *(cpout++) = '\n';
+      i = 0;
+    }
+  }
+  outdata->len = (unsigned int) (cpout - outdata->s);
+}
diff --git a/env.c b/env.c
new file mode 100644 (file)
index 0000000..05d527b
--- /dev/null
+++ b/env.c
@@ -0,0 +1,113 @@
+/* env.c, envread.c, env.h: environ library
+Daniel J. Bernstein, djb@silverton.berkeley.edu.
+Depends on str.h, alloc.h.
+Requires environ.
+19960113: rewrite. warning: interface is different.
+No known patent problems.
+*/
+
+#include "str.h"
+#include "alloc.h"
+#include "env.h"
+
+int env_isinit = 0; /* if env_isinit: */
+static int ea; /* environ is a pointer to ea+1 char*'s. */
+static int en; /* the first en of those are ALLOCATED. environ[en] is 0. */
+
+static void env_goodbye(i) int i;
+{
+ alloc_free(environ[i]);
+ environ[i] = environ[--en];
+ environ[en] = 0;
+}
+
+static char *null = 0;
+
+void env_clear()
+{
+ if (env_isinit) while (en) env_goodbye(0);
+ else environ = &null;
+}
+
+static void env_unsetlen(s,len) char *s; int len;
+{
+ int i;
+ for (i = en - 1;i >= 0;--i)
+   if (!str_diffn(s,environ[i],len))
+     if (environ[i][len] == '=')
+       env_goodbye(i);
+}
+
+int env_unset(s) char *s;
+{
+ if (!env_isinit) if (!env_init()) return 0;
+ env_unsetlen(s,str_len(s));
+ return 1;
+}
+
+static int env_add(s) char *s;
+{
+ char *t;
+ t = env_findeq(s);
+ if (t) env_unsetlen(s,t - s);
+ if (en == ea)
+  {
+   ea += 30;
+   if (!alloc_re(&environ,(en + 1) * sizeof(char *),(ea + 1) * sizeof(char *)))
+    { ea = en; return 0; }
+  }
+ environ[en++] = s;
+ environ[en] = 0;
+ return 1;
+}
+
+int env_put(s) char *s;
+{
+ char *u;
+ if (!env_isinit) if (!env_init()) return 0;
+ u = alloc(str_len(s) + 1);
+ if (!u) return 0;
+ str_copy(u,s);
+ if (!env_add(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_put2(s,t) char *s; char *t;
+{
+ char *u;
+ int slen;
+ if (!env_isinit) if (!env_init()) return 0;
+ slen = str_len(s);
+ u = alloc(slen + str_len(t) + 2);
+ if (!u) return 0;
+ str_copy(u,s);
+ u[slen] = '=';
+ str_copy(u + slen + 1,t);
+ if (!env_add(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_init()
+{
+ char **newenviron;
+ int i;
+ for (en = 0;environ[en];++en) ;
+ ea = en + 10;
+ newenviron = (char **) alloc((ea + 1) * sizeof(char *));
+ if (!newenviron) return 0;
+ for (en = 0;environ[en];++en)
+  {
+   newenviron[en] = alloc(str_len(environ[en]) + 1);
+   if (!newenviron[en])
+    {
+     for (i = 0;i < en;++i) alloc_free(newenviron[i]);
+     alloc_free(newenviron);
+     return 0;
+    }
+   str_copy(newenviron[en],environ[en]);
+  }
+ newenviron[en] = 0;
+ environ = newenviron;
+ env_isinit = 1;
+ return 1;
+}
diff --git a/error.c b/error.c
index d51304f..f9b617d 100644 (file)
--- a/error.c
+++ b/error.c
@@ -17,7 +17,7 @@ ENOMEM;
 -2;
 #endif
 
-int error_noent = 
+int error_noent =
 #ifdef ENOENT
 ENOENT;
 #else
@@ -93,3 +93,10 @@ EACCES;
 #else
 -13;
 #endif
+
+int error_notdir =
+#ifdef ENOTDIR
+ENOTDIR;
+#else
+-14;
+#endif
diff --git a/error.h b/error.h
index 01bd3dc..42fdc81 100644 (file)
--- a/error.h
+++ b/error.h
@@ -16,6 +16,7 @@ extern int error_again;
 extern int error_pipe;
 extern int error_perm;
 extern int error_acces;
+extern int error_notdir;
 
 extern char *error_str();
 extern int error_temp();
diff --git a/errtxt.h b/errtxt.h
new file mode 100644 (file)
index 0000000..ae82bf9
--- /dev/null
+++ b/errtxt.h
@@ -0,0 +1,156 @@
+/*$Id: errtxt.h,v 1.46 1999/11/29 04:54:01 lindberg Exp $*/
+/*Name: $*/
+
+#ifndef ERRTXT_H
+#define ERRTXT_H
+
+/* Error messages. If you translate these, I would urge you to keep the */
+/* English version as well. I'm happy to include any bilingual versions */
+/* of this file with future versions of ezmlm-idx.                      */
+
+#define ERR_NOMEM "out of memory"
+#define ERR_NOCMD "command not available"
+#define ERR_CLOSE "unable to close "
+#define ERR_WRITE "unable to write "
+#define ERR_READ "unable to read "
+#define ERR_READ_KEY "unable to read key"
+#define ERR_FLUSH "unable to flush "
+#define ERR_SEEK "unalble to seek "
+#define ERR_SYNC "unable to sync "
+#define ERR_CHMOD "unable to chmod "
+#define ERR_STAT "unable to stat "
+#define ERR_DELETE "unable to delete "
+#define ERR_READ_INPUT "unable to read input: "
+#define ERR_SEEK_INPUT "unable to seek input: "
+#define ERR_CREATE "unable to create "
+#define ERR_MOVE "unable to move "
+#define ERR_OPEN "unable to open "
+#define ERR_OBTAIN "unable to obtain "
+#define ERR_OPEN_LOCK "unable to open lock: "
+#define ERR_OBTAIN_LOCK "unable to obtain lock: "
+#define ERR_NOLOCAL "LOCAL not set"
+#define ERR_NODTLINE "DTLINE not set"
+#define ERR_NOSENDER "SENDER not set"
+#define ERR_NOHOST "HOST not set"
+#define ERR_NOEXIST " does not exist"
+#define ERR_NOEXIST_KEY " key does not exist"
+#define ERR_SWITCH "unable to switch to "
+#define ERR_BOUNCE "I do not reply to bounce messages (#5.7.2)"
+#define ERR_ANONYMOUS "I do not reply to senders without host names (#5.7.2)"
+#define ERR_NOT_PUBLIC "Sorry, I've been told to reject all requests (#5.7.2)"
+#define ERR_NOT_ARCHIVED "Sorry, this list is not archived (#5.1.1)"
+#define ERR_NOT_INDEXED "Sorry, this list is not indexed (#5.1.1)"
+#define ERR_NOT_AVAILABLE "Command not available (#5.1.1)"
+#define ERR_NOT_ALLOWED "Command allowed only to moderators (#5.7.1)"
+#define ERR_BAD_ADDRESS "I don't accept messages at this address (inlocal and/or inhost don't match) (#5.1.1)"
+#define ERR_BAD_RETURN_ADDRESS "Invalid bounce or receipt address format (#5.1.1)"
+#define ERR_BAD_REQUEST "Illegal request format (#5.7.1)"
+#define ERR_QMAIL_QUEUE "unable to run qmail-queue: "
+#define ERR_TMP_QMAIL_QUEUE "temporary qmail-queue error: "
+#define ERR_NOT_PARENT "this message is not from my parent list (#5.7.2)"
+#define ERR_SUBLIST "sublist messages must have a Mailing-List header (#5.7.2)"
+#define ERR_MAILING_LIST "message already has a Mailing-List header (maybe I should be a sublist) (#5.7.2)"
+#define ERR_LOOPING "this message is looping: it already has my Delivered-To line (#5.4.6)"
+#define ERR_SUBSCRIBER_CAN "only subscribers can "
+#define ERR_571 " (#5.7.1)"
+#define ERR_EMPTY_DIGEST "nothing to digest"
+#define ERR_EMPTY_LIST "no messages in archive"
+#define ERR_NOINDEX "Sorry, I can't find the index for this message"
+#define ERR_BAD_INDEX "Old format or corrupted index. Run ezmlm-idx! (#5.3.0)"
+#define ERR_BAD_DIGCODE "incorrect digest code (#5.7.1)"
+#define ERR_UNEXPECTED "program logic error (#5.3.0)"
+#define ERR_BAD_ALL "Sorry, after removing unacceptable MIME parts from your message I was left with nothing (#5.7.0)"
+#define ERR_MIME_QUOTE "MIME boundary lacks end quote"
+#define ERR_SUBST_UNSAFE "Sorry, substitution of target addresses into headers with <#A#> or <#T#> is unsafe and not permitted."
+
+/* ezmlm-request unique */
+#define ERR_REQ_LISTNAME "This command requires a mailing list name (#5.1.1)"
+#define ERR_REQ_LOCAL "the local part of the command string does not match this list (#5.1.1)"
+
+/* ezmlm-reject unique */
+#define ERR_MAX_SIZE "Sorry, I don't accept messages larger than "
+#define ERR_MIN_SIZE "Sorry, I don't accept messages shorter than "
+#define ERR_SIZE_CODE " (#5.2.3)"
+#define ERR_NO_ADDRESS "List address must be in To: or Cc: (#5.7.0)"
+#define ERR_NO_SUBJECT "Sorry, I don't accept message with empty Subject (#5.7.0)"
+#define ERR_SUBCOMMAND "Sorry, I don't accept commands in the subject line. Please send a message to the -help address shown in the the ``Mailing-List:'' header for command info (#5.7.0)"
+#define ERR_BODYCOMMAND "Sorry, as the message starts with ``[un]subscribe'' it looks like an adminstrative request. Please send a message to the -help address shown in the ``Mailing-List:'' header for [un]subscribe info (#5.7.0)"
+#define ERR_BAD_TYPE "Sorry, I don't accept messages of MIME Content-Type '"
+#define ERR_BAD_PART "Sorry, a message part has an unacceptable MIME Content-Type: "
+#define ERR_JUNK "Precedence: junk - message ignored"
+
+/* ezmlm-manage unique */
+#define ERR_SUB_NOP "target is already a subscriber"
+#define ERR_UNSUB_NOP "target is not a subscriber"
+#define ERR_BAD_NAME "only letters and underscore allowed in file name (#5.6.0)"
+#define ERR_NO_MARK "missing start-of-text or end-of-text mark (#5.6.0)"
+#define ERR_EDSIZE "Maximum edit file size exceeded (#5.6.0)"
+#define ERR_BAD_CHAR "NUL or other illegal character in input (#5.6.0)"
+#define ERR_EXTRA_SUB "Processed SENDER check addition request for: "
+#define ERR_EXTRA_UNSUB "Processed SENDER check removal request for: "
+
+
+/* ezmlm-moderation functions unique */
+#define ERR_MOD_TIMEOUT "I'm sorry, I no longer have this message"
+#define ERR_MOD_ACCEPTED "I'm sorry, I've already accepted this message"
+#define ERR_MOD_REJECTED "I'm sorry, I've already rejected this message"
+#define ERR_MOD_COOKIE "Illegal or outdated moderator request (#5.7.1)"
+#define ERR_FORK "unable to fork: "
+#define ERR_EXECUTE "unable to execute "
+#define ERR_CHILD_CRASHED "child crashed"
+#define ERR_CHILD_FATAL "fatal error from child"
+#define ERR_CHILD_TEMP "temporary error from child"
+#define ERR_CHILD_UNKNOWN "unknown error from child"
+#define ERR_UNIQUE "unable to create unique message file name"
+#define ERR_NO_POST "I'm sorry, you are not allowed to post messages to this list (#5.7.2)"
+
+/* ezmlm-make unique */
+#define ERR_VERSION "ezmlmrc version mismatch. Behavior may not match docs."
+#define ERR_ENDTAG "tag lacks /> end marker: "
+#define ERR_LINKDIR "linktag lacks /dir: "
+#define ERR_FILENAME "continuation tag without defined file name: "
+#define ERR_PERIOD "periods not allowed in tags: "
+#define ERR_SLASH "dir and dot must start with slash"
+#define ERR_NEWLINE "newlines not allowed in dir"
+#define ERR_QUOTE "quotes not allowed in dir"
+#define ERR_SYNTAX " syntax error: "
+
+/* ezmlm-limit unique */
+#define ERR_EXCESS_MOD "excess traffic: moderating"
+#define ERR_EXCESS_DEFER "excess traffic: deferring"
+
+/* ezmlm-cron unique */
+#define ERR_SAME_HOST "list and digest must be on same host"
+#define ERR_DOW "single comma-separated digits only for day-of-week"
+#define ERR_NOT_CLEAN "Bad character in address components"
+#define ERR_SUID "Sorry, I won't run as root"
+#define ERR_UID "user id not found"
+#define ERR_EUID "effective user id not found"
+#define ERR_BADUSER "user not allowed"
+#define ERR_BADHOST "list host not allowed"
+#define ERR_BADLOCAL "list local not allowed"
+#define ERR_LISTNO "max number of list entries exceeded"
+#define ERR_NO_MATCH "no matching entry found"
+#define ERR_SETUID "unable to set uid: "
+#define ERR_CFHOST "bounce-host required on first line of "
+#define ERR_EXCLUSIVE "action-controlling switches are mutually exclusive"
+#define ERR_CRONTAB "crontab update failed. Contact you sysadmin with the above error information"
+
+/* ezmlm-gate */
+#define ERR_REJECT "Sorry, I've been told to reject this message (#5.7.0)"
+
+/* issub/subscribe ... */
+#define ERR_ADDR_AT "address does not contain @"
+#define ERR_ADDR_LONG "address is too long"
+#define ERR_ADDR_NL "address contains newline"
+
+/* sql */
+#define ERR_COOKIE "message does not have valid authentication token"
+#define ERR_NOT_ACTIVE "this sublist is not active"
+#define ERR_PARSE "unable to parse "
+#define ERR_DONE "message already successfully processed by this list"
+#define ERR_MAX_BOUNCE "max bounces exceeded: bounce will not be saved"
+#define ERR_NO_TABLE "no table specified in database connect data"
+
+#endif
+
diff --git a/ezcgi.css b/ezcgi.css
new file mode 100644 (file)
index 0000000..f4c4f87
--- /dev/null
+++ b/ezcgi.css
@@ -0,0 +1,24 @@
+BODY { background-color: #ffffe0; margin-left: 5%; margin-right: 5% }
+
+  a:link { color: black }
+  a:active { color: black }
+
+  H1 { color: red; font-size: 200% ; text-align: center}
+  H2 { color: green; font-size: 150%; text-align: left }
+
+  HR { border: medium; color: #00426B }
+
+  DIV.idx { margin-left: 5%; margin-right: 5% }
+  a.alk { color: green; font-style: italic }
+
+  LI.subjbody { margin-left: 15% }
+  LI.authbody { margin-left: 10% }
+
+  DIV.rfc822hdr { text-align: left; font-size: 100% }
+    EM.rfc822hdr { font-weight: bold; font-family: arial, sans-serif }
+      SPAN.subject { font-weight: bold; font-size: 150% }
+       a.relk { color: red }
+
+DIV.copyright { font-size: 50%; text-align: right }
+
+
diff --git a/ezcgirc b/ezcgirc
new file mode 100644 (file)
index 0000000..13e101f
--- /dev/null
+++ b/ezcgirc
@@ -0,0 +1,15 @@
+# listno;[-]UID;DIR;[-]listaddr;button[,button ...];style;banner
+
+# Default list. "alias" UID=7771. One extra button pointing to my home page ;-)
+0;7771;/var/qmail/alias/QMAIL;qmail@id.wustl.edu;[Home]=http://id.wustl.edu/~lindberg
+
+# the real qmail archive. Because of '-' before the list name, the from
+# address is not shown, only the associated "handle". More buttons here.
+1;7771;/var/qmail/alias/QMAIL;-qmail@id.wustl.edu;[Home]=http://id.wustl.edu/~lindberg,[qmail.org]=http://www.qmail.org,[djb]=http://www.pobox.com/~djb/qmail.html
+
+# This is another alias list. The '-' before the 7771 suppresses the chroot(),
+# which is needed since banner program "./niles" uses /bin/sh, cat, etc. The
+# style sheet is specified as well. Blank lines are ok, but all chars on config
+# lines are significant.
+2;-7771;/var/qmail/alias/SAMBA;samba@id.wustl.edu;[Home]=http://id.wustl.edu/~lindberg;iso-8859-1;/ezcgi.css;./niles
+
diff --git a/ezmlm-accept.1 b/ezmlm-accept.1
new file mode 100644 (file)
index 0000000..bbb86f3
--- /dev/null
@@ -0,0 +1,30 @@
+.TH ezmlm-accept 1
+.SH NAME
+ezmlm-accept \- accept messages in moderation queue
+.SH SYNOPSIS
+.B ezmlm-accept
+.I dir
+.I file
+[
+.I file1 ...
+]
+.SH DESCRIPTION
+.B ezmlm-accept
+copies
+.IR file ,
+.IR file1 ,\ ...
+to
+.B ezmlm-send
+.IR dir .
+.B ezmlm-accept
+removes each file that was successfully processed by
+.BR ezmlm-send .
+
+This can be used to manually ``accept'' messages in a
+.I ezmlm
+moderation queue.
+.SH "SEE ALSO"
+ezmlm(5),
+ezmlm-moderate(1),
+ezmlm-send(1),
+ezmlm-store(1)
diff --git a/ezmlm-accept.sh b/ezmlm-accept.sh
new file mode 100644 (file)
index 0000000..b723259
--- /dev/null
@@ -0,0 +1,30 @@
+# [should have a bin/sh line and EZPATH added above by make]
+#
+# script to accept files in DIR/mod/pending. Use as:
+# ezmlm-accept DIR file1 [file2 ...]
+# where ``DIR'' is the list directory and ``file1'' is a file to be
+# accepted. ``ezmlm-accept DIR DIR/mod/pending/*'' will accept all
+# pending files. Files that are successfully sent to the list are
+# deled. See man page for details.
+
+EZSEND="${EZPATH}/ezmlm-send"
+FATAL='ezmlm-accept: fatal:'
+if [ ! -x "$EZSEND" ]; then
+  echo "$FATAL please edit script to the correct ezmlm-send path"
+  exit 100;
+fi
+
+DIR="$1"
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+  echo "$FATAL usage: ezmlm-accept DIR file1 [file2 ...]"
+  exit 100;
+fi
+
+while [ -n "$2" ]; do
+  if [ -x "$2" ]; then
+    $EZSEND $DIR < "$2" && rm -f "$2"
+  fi
+  shift
+done
+exit 0;
diff --git a/ezmlm-archive.1 b/ezmlm-archive.1
new file mode 100644 (file)
index 0000000..ca764f8
--- /dev/null
@@ -0,0 +1,178 @@
+.TH ezmlm-archive 1
+.SH NAME
+ezmlm-archive \- create thread and author index for a mailing list archive
+.SH SYNOPSIS
+.B ezmlm-archive
+[
+.B \-cCFTvV
+][
+.B \-f\fI msg1
+]
+][
+.B \-t\fI msg2
+]
+.I dir
+.SH DESCRIPTION
+.B ezmlm-archive
+reads the index files from a message archive, and creates a subject index, a
+collection of subject files, and a collection of author files. These
+files are suitable as an index for WWW access to, and navigation through
+a mailing list archive by
+.BR ezmlm-cgi(1) .
+
+The index files read are created by
+.B ezmlm-idx(1)
+on a per-list basis and by
+.B ezmlm-send(1)
+on a per-message archive for a indexed list.
+
+The output files created are:
+.TP
+.I dir\fB/archive/threads/yyyymm
+The thread index. It contains one line per subject, starting with the
+number of the first message with that subject within the set
+investigated, ``:'', a 20 character
+subject hash, blank, ``\[n\]'' where ``n'' is the number of messages in the
+thread, blank, and the subject.
+The file ``yyyymm'' contains
+entries for all threads that have messages in the month ``yyyymm''
+or that have messages both before and after that month.
+The subject hash is a key to the subject files; the message number is
+a key to the index file.
+The lines are in ascending order by message number when the index is
+created
+.I de novo
+on an existing archive. When the messages are added one-by-one as in normal
+archive operation, ``n'' is the number of message in the thread
+.I for the particular month
+and the order is in reverse of latest message, i.e. the last extended thread
+is shown last. The message number accompanying a thread is
+always a message within the thread. It is the first in
+archives created
+on existing lists, and the last message in incrementally created archives.
+Use the corresponding subject index file to get a list of all
+messages in the thread in ascending order.
+.TP
+.I dir\fB/archive/subjects/xx/yyyyyyyyyyyyyyyyyy
+A subject file. The first line is the subject hash, a space, and the subject.
+This is followed by one line per message with this subject, in the format
+message number, ``:'', date (yyyymm), ``:'',
+author hash, blank, author from line. The lines are
+sorted by message number. The author hash is a key to the author files;
+the message number is a key to the index file. The file in the example
+would be for the subject hash ``xxyyyyyyyyyyyyyyyyyy''.
+.TP
+.I dir\fB/archive/authors/xx/yyyyyyyyyyyyyyyyyy
+An author file. The first line is the author hash, a space, and the author
+from line.
+This is followed by one line per message with this author, in the format
+message number, ``:'', date (yyyymm), ``:'',
+subject hash, blank, subject. The lines are
+sorted by message number. The subject hash is a key to the subject files;
+the message number is a key to the index file. The file in the example
+would be for the author hash ``xxyyyyyyyyyyyyyyyyyy''.
+
+.I dir\fB/archnum
+keeps track of the last message processed. Normally,
+.B ezmlm-archive
+will process entries for messages from one above the contents of this file
+up to an including the message number in
+.IR dir\fB/num .
+.SH OPTIONS
+.B ezmlm-archive
+writes messages in a crash-proof manner when run in normal mode. When overriding
+the normal message range with any of the options listed, the normal
+.B sync(3)
+of the output files is suppressed for efficiency. Should the computer crash
+during this time the state of the indices is not defined. Use the
+.B \-s
+option in the (extremely rare) cases where this would be a problem.
+.TP
+.B \-c
+Create a new index. This  overrides
+.I dir\fB/archnum
+causing
+.B ezmlm-archive
+to start with the first message in the archive. Synonym for
+.BR \-f\fI0 .
+.B NOTE:
+.B ezmlm-archive
+does not remove files in the index. While it will overwrite/update old files
+it will not remove files that are obsolete for other reasons.
+.TP
+.B \-C
+(Default.)
+Process entries starting with the message after the message listed in
+.IR dir\fB/archnum .
+.TP
+.B \-f\fI msg1
+Process messages from the archive section (set of 100 messages)
+containing message
+.IR msg1 .
+This is useful if you have removed part of the archive, as it will shorten
+processing time and decrease memory use.
+.B NOTE:
+.B ezmlm-archive
+does not remove files in the index. While it will overwrite/update old files
+it will not remove files that are obsolete for other reasons. The number of
+messages per thread will be incorrect when using of the
+.B \-f
+and
+.B \-t
+switches leads to partial re-indexing of already indexed messages.
+.TP
+.B \-F
+(Default.)
+Do not change the starting message from the default
+(see
+.BR \-C ).
+.TP
+.B \-s
+Always sync files.
+.TP
+.B \-S
+(Default.)
+Sync files, except when on of the message range modifying options is
+used.
+.TP
+.B \-t\fI msg2
+Process messages to message
+.I msg2
+instead of the last message in the archive. Again, files written are
+corrected, but other files are not explicitly removed.
+.TP
+.B \-T
+(Default.)
+Process entries for messages up to the last message in the archive.
+.TP
+.B \-v
+Display
+.B ezmlm-archive
+version info.
+.TP
+.B \-V
+Display
+.B ezmlm-archive
+version info.
+.SH "MEMORY USAGE"
+.B ezmlm-archive
+stores its linked lists in memory. On at 32-bit architecture, it uses
+12 bytes per message, 28 bytes per thread (plus one copy of the subject),
+and 20 bytes per author (plus one copy of the author from line).
+
+In normal list use, it processes only at most a few messages at a time,
+but for initial processing of a large archive, considerable amounts of
+memory may be used. Assuming
+40 bytes for subject/from line, 5 messages per thread, 100,000 messages,
+and 1000 authors, this is 2.5 MB. For 1,000,000 messages this is about 20 MB.
+
+Thus, for large archives, it may be useful to use the
+.I \-t
+switch to process the archive in multiple subsets, starting with e.g. the first
+100,000, then the next, and so on.
+.SH "SEE ALSO"
+ezmlm-cgi(1),
+ezmlm-idx(1),
+ezmlm-send(1),
+ezmlm(5)
+
diff --git a/ezmlm-archive.c b/ezmlm-archive.c
new file mode 100644 (file)
index 0000000..9026789
--- /dev/null
@@ -0,0 +1,479 @@
+/*$Id: ezmlm-archive.c,v 1.13 1999/11/28 20:13:32 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include "alloc.h"
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "sig.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "getln.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "fmt.h"
+#include "sgetopt.h"
+#include "idxthread.h"
+#include "makehash.h"
+#include "idx.h"
+#include "errtxt.h"
+
+#define FATAL "ezmlm-archive: fatal: "
+#define WARNING "ezmlm-archive: warning: inconsistent index: "
+
+substdio ssin;
+char inbuf[1024];
+substdio ssout;
+char outbuf[1024];
+substdio ssnum;
+char numbuf[16];
+
+stralloc line = {0};
+stralloc num = {0};
+stralloc fn = {0};
+stralloc fnn = {0};
+
+char strnum[FMT_ULONG];
+int flagerror = 0;
+int flagsync = 1;      /* sync() by default, not for -c or -f or -t */
+char *dir;
+
+struct ca {
+  char *s;             /* start */
+  unsigned int l;      /* length */
+} ca;
+
+void die_usage() {
+  strerr_die1x(100,
+    "ezmlm-archive: usage: "
+       "ezmlm-archive [-cCFsSTvV] [-f min_msg] [-t max_msg] dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void close_proper(ss,s,sn)
+/* flush,sync,close,move sn->s) */
+substdio *ss;
+char *s, *sn;
+{
+   if (substdio_flush(ss) == -1)
+     strerr_die6sys(111,FATAL,ERR_FLUSH,dir,"/",s,": ");
+  if (flagsync)
+    if (fsync(ss->fd) == -1)
+       strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",s,": ");
+  if (close(ss->fd) == -1)
+     strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",s,": ");
+  if (rename(sn,s) == -1)
+     strerr_die6sys(111,FATAL,ERR_MOVE,dir,"/",sn,": ");
+}
+
+void write_threads(msgtable,subtable,authtable,datetable,from,to)
+/* Add the current threading data to the thread database without dups */
+/* Writes the subject index first, then processes the individual files */
+msgentry *msgtable; subentry *subtable; authentry *authtable;
+dateentry *datetable;
+unsigned long from,to;
+{
+  msgentry *pmsgt;
+  subentry *psubt,*psubtm, *psubtlast;
+  subentry *presubt = (subentry *)0;
+  authentry *pautht;
+  dateentry *pdatet;
+  char *cp,*cp1;
+  unsigned long msg;
+  unsigned long ulmsginthread;
+  unsigned long subnum;
+  unsigned long authnum;
+  unsigned long msgnum;
+  unsigned int pos,l;
+  unsigned int startdate,nextdate;
+  unsigned int startmsg,nextmsg;
+  int fd = -1;
+  int fdn = -1;
+  int match;
+  int ffound;
+  int lineno;
+  int res;
+
+  psubtm = subtable;           /* now for new threads */
+  pdatet = datetable;
+  nextmsg = 0L;
+  nextdate = pdatet->date;
+  while (psubtm->sub) {                /* these are in msgnum order */
+    if (!presubt)              /* for rewind */
+      if (psubtm->lastmsg >= nextmsg)
+       presubt = psubtm;       /* this thread extends beyond current month */
+    if (psubtm->firstmsg >= nextmsg) { /* done with this month */
+      if (fdn != -1) close_proper(&ssout,fn.s,fnn.s);
+      psubtlast = psubtm;              /* last thread done */
+      if (presubt)                     /* need to rewind? */
+       psubtm = presubt;               /* do it */
+      psubt = psubtm;                  /* tmp pointer to reset done flag */
+      presubt = (subentry *)0;         /* reset rewind pointer */
+      pdatet++;                                /* next month */
+      startdate = nextdate;            /* startdate */
+      nextdate = pdatet->date;         /* end date */
+      startmsg = nextmsg;              /* first message in month */
+      nextmsg = pdatet->msg;           /* first message in next month */
+      if (!stralloc_copys(&fn,"archive/threads/")) die_nomem();
+      if (!stralloc_catb(&fn,strnum,fmt_uint(strnum,startdate))) die_nomem();
+      if (!stralloc_copy(&fnn,&fn)) die_nomem();
+      if (!stralloc_0(&fn)) die_nomem();
+      if (!stralloc_cats(&fnn,"n")) die_nomem();
+      if (!stralloc_0(&fnn)) die_nomem();
+      if ((fdn = open_trunc(fnn.s)) == -1)
+       strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fnn.s,": ");
+      substdio_fdbuf(&ssout,write,fdn,outbuf,sizeof(outbuf));
+      if ((fd = open_read(fn.s)) == -1) {
+      if (errno != error_noent)
+             strerr_die6sys(111,FATAL,ERR_OPEN,dir,"/",fn.s,": ");
+      } else {
+       substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+      for (;;) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+             strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fn.s,": ");
+      if (!match) break;
+      pos = scan_ulong(line.s,&msgnum);
+      pos++;                   /* skip ':' */
+      if (msgnum >= from)
+       continue;               /* ignore entries from threading range */
+      if (line.len < pos + HASHLEN) {
+       flagerror = -1;         /* and bad ones */
+       continue;
+      }
+      psubt = subtable;
+      cp = line.s + pos;
+      ffound = 0;              /* search among already known subjects */
+      for (;;) {
+       res = str_diffn(psubt->sub,cp,HASHLEN);
+       if (res < 0) {
+         if (psubt->higher)
+           psubt = psubt->higher;
+        else
+          break;
+       } else if (res > 0) {
+         if (psubt->lower)
+           psubt = psubt->lower;
+         else
+           break;
+       } else {
+         ffound = 1;
+         break;
+       }
+      }
+      if (!ffound) {
+       if (substdio_put(&ssout,line.s,line.len) == -1)
+         strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+      } else {                 /* new # of msg in thread */
+       cp += HASHLEN;          /* HASHLEN [#] Subject always \n at end */
+       if (*(cp++) == ' ' && *(cp++) == '[') {
+         cp += scan_ulong(cp,&ulmsginthread);
+         if (*cp == ']') {
+           psubt->msginthread += (unsigned char) (ulmsginthread & 0xff);
+         }
+       } else
+         flagerror = -5;
+      }
+    }
+    close(fd);
+  }
+  continue;
+  }
+
+    if (psubtm->firstmsg < nextmsg && psubtm->lastmsg >= startmsg) {
+    if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,psubtm->lastmsg)))
+               die_nomem();
+    if (!stralloc_cats(&line,":")) die_nomem();
+    if (!stralloc_catb(&line,psubtm->sub,HASHLEN)) die_nomem();
+    if (!stralloc_cats(&line," [")) die_nomem();
+    if (!stralloc_catb(&line,strnum,
+       fmt_ulong(strnum,(unsigned long) psubtm->msginthread)))
+               die_nomem();
+    if (!stralloc_cats(&line,"]")) die_nomem();
+    if (!stralloc_catb(&line,psubtm->sub + HASHLEN,psubtm->sublen - HASHLEN))
+                        die_nomem();   /* has \n */
+    if (substdio_put(&ssout,line.s,line.len) == -1)
+       strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+    }
+  psubtm++;
+  }
+  if (fdn != -1)
+    close_proper(&ssout,fn.s,fnn.s);
+
+  psubt = subtable;
+  while (psubt->sub) {         /* now the threads */
+    if (!stralloc_copys(&fn,"archive/subjects/")) die_nomem();
+    if (!stralloc_catb(&fn,psubt->sub,2)) die_nomem();
+    if (!stralloc_0(&fn)) die_nomem();
+    if (mkdir(fn.s,0755) == -1)
+    if (errno != error_exist)
+      strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fn.s,": ");
+    fn.s[fn.len - 1] = '/';
+    if (!stralloc_catb(&fn,psubt->sub+2,HASHLEN-2)) die_nomem();
+    if (!stralloc_copy(&fnn,&fn)) die_nomem();
+    if (!stralloc_cats(&fnn,"n")) die_nomem();
+    if (!stralloc_0(&fn)) die_nomem();
+    if (!stralloc_0(&fnn)) die_nomem();
+    if ((fdn = open_trunc(fnn.s)) == -1)
+      strerr_die4sys(111,FATAL,ERR_CREATE,fnn.s,": ");
+    substdio_fdbuf(&ssout,write,fdn,outbuf,sizeof(outbuf));
+    if ((fd = open_read(fn.s)) == -1) {
+      if (errno != error_noent)
+         strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+      if (substdio_puts(&ssout,psubt->sub) == -1)      /* write subject */
+            strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+    } else {                                   /* copy data */
+       substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+       lineno = 0;
+       for (;;) {
+         if (getln(&ssin,&line,&match,'\n') == -1)
+             strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fn.s,": ");
+          if (!match) break;
+         if (!lineno) {                        /* write subject */
+           if (line.len < HASHLEN + 1 || line.s[HASHLEN] != ' ')
+               flagerror = -3;
+           if (substdio_put(&ssout,line.s,line.len) == -1)
+              strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+           lineno = 1;
+           continue;
+         }
+         (void) scan_ulong(line.s,&msgnum);
+         if (msgnum >= from) break;
+         if (substdio_put(&ssout,line.s,line.len) == -1)
+            strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+       }
+       (void) close(fd);       /* close old index */
+      }
+
+    subnum = (unsigned long) (psubt - subtable + 1);   /* idx of this subj */
+    pmsgt = msgtable + psubt->firstmsg - from; /* first message entry */
+    for (msg = psubt->firstmsg; msg <= psubt->lastmsg; msg++) {
+      if (pmsgt->subnum == subnum) {
+        if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem();
+        if (!stralloc_cats(&line,":")) die_nomem();
+       if (!stralloc_catb(&line,strnum,fmt_uint(strnum,pmsgt->date)))
+               die_nomem();
+       if (!stralloc_cats(&line,":")) die_nomem();
+        if (pmsgt->authnum) {
+         pautht = authtable + pmsgt->authnum - 1;
+         cp = pautht->auth;
+         cp1 = cp + str_chr(cp,' ');
+         if (cp + HASHLEN != cp1)
+           strerr_die1x(100,ERR_BAD_INDEX);
+         if (!stralloc_cats(&line,cp))
+               die_nomem();                            /* hash */
+       } else
+          if (!stralloc_cats(&line,"\n")) die_nomem();
+       if (substdio_put(&ssout,line.s,line.len) == -1)
+         strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+      }
+      pmsgt++;
+    }
+    close_proper(&ssout,fn.s,fnn.s);
+    psubt++;
+  }
+
+                                       /* (no master author index) */
+  pautht = authtable;
+  while (pautht->auth) {               /* now the authors */
+    if (!stralloc_copys(&fn,"archive/authors/")) die_nomem();
+    if (!stralloc_catb(&fn,pautht->auth,2)) die_nomem();
+    if (!stralloc_0(&fn)) die_nomem();
+    if (mkdir(fn.s,0755) == -1)
+    if (errno != error_exist)
+      strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fn.s,": ");
+    fn.s[fn.len - 1] = '/';
+    if (!stralloc_catb(&fn,pautht->auth+2,HASHLEN-2)) die_nomem();
+    if (!stralloc_copy(&fnn,&fn)) die_nomem();
+    if (!stralloc_cats(&fnn,"n")) die_nomem();
+    if (!stralloc_0(&fn)) die_nomem();
+    if (!stralloc_0(&fnn)) die_nomem();
+    if ((fdn = open_trunc(fnn.s)) == -1)
+      strerr_die4sys(111,FATAL,ERR_CREATE,fnn.s,": ");
+    substdio_fdbuf(&ssout,write,fdn,outbuf,sizeof(outbuf));
+      if ((fd = open_read(fn.s)) == -1) {
+       if (errno != error_noent)
+         strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+        else {                 /* didn't exist before: write author */
+          if (substdio_put(&ssout,pautht->auth,pautht->authlen) == -1)
+            strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+       }
+      } else {                                 /* copy data */
+       substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+       lineno = 0;
+       for (;;) {
+         if (getln(&ssin,&line,&match,'\n') == -1)
+             strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fn.s,": ");
+          if (!match) break;
+         if (!lineno) {                        /* write author */
+           if (line.len < HASHLEN + 1 || line.s[HASHLEN] != ' ')
+               flagerror = - 4;
+           if (substdio_put(&ssout,line.s,line.len) == -1)
+              strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+           lineno = 1;
+           continue;
+         }
+         (void) scan_ulong(line.s,&msgnum);
+         if (msgnum >= from) break;
+         if (substdio_put(&ssout,line.s,line.len) == -1)
+            strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+       }
+       (void) close(fd);                       /* close old index */
+      }
+
+    authnum = (unsigned long) (pautht - authtable + 1);        /* idx of this auth */
+    pmsgt = msgtable + pautht->firstmsg - from;        /* first message entry */
+    for (msg = pautht->firstmsg; msg <= to; msg++) {
+      if (pmsgt->authnum == authnum) {
+        if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem();
+        if (!stralloc_cats(&line,":")) die_nomem();
+       if (!stralloc_catb(&line,strnum,fmt_uint(strnum,pmsgt->date)))
+               die_nomem();
+       if (!stralloc_cats(&line,":")) die_nomem();
+        if (pmsgt->subnum) {
+         psubt = subtable + pmsgt->subnum - 1;
+          if (!stralloc_catb(&line,psubt->sub,psubt->sublen))
+               die_nomem();
+       }
+       if (substdio_put(&ssout,line.s,line.len) == -1)
+         strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+      }
+      pmsgt++;
+    }
+    close_proper(&ssout,fn.s,fnn.s);
+    pautht++;
+  }
+}
+
+int main(argc,argv)
+int argc;
+char **argv;
+{
+  unsigned long archnum = 0L;
+  unsigned long to = 0L;
+  unsigned long max;
+  int fd;
+  int fdlock;
+  int flagcreate = 0;
+  int flagsyncall = 0;
+  int opt;
+  msgentry *msgtable;
+  subentry *subtable;
+  authentry *authtable;
+  dateentry *datetable;
+
+  (void) umask(022);
+  sig_pipeignore();
+
+  while ((opt = getopt(argc,argv,"cCf:FsSt:TvV")) != opteof)
+    switch (opt) {
+      case 'c':        flagcreate = 1;
+               flagsync = 0;
+               break;                  /* start at beginning of archive */
+      case 'C': flagcreate = 0;
+               break;  /* Do only archnum+1 => num */
+      case 'f': if (optarg) {
+                 (void) scan_ulong(optarg,&archnum);
+                 archnum = (archnum / 100) * 100;
+               }
+               flagsync = 0;
+               break;
+      case 'F': archnum = 0; break;
+      case 's': flagsyncall = 1; break;
+      case 'S': flagsyncall = 0; break;
+      case 't': if (optarg) {
+                 (void) scan_ulong(optarg,&to);
+               }
+               flagsync = 0;
+               break;
+      case 'T': to = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-archive version: ",EZIDX_VERSION);
+      default:
+        die_usage();
+    }
+
+  if (flagsyncall) flagsync = 1;       /* overrides */
+  dir = argv[optind++];
+  if (!dir) die_usage();
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  if (mkdir("archive/threads",0755) == -1)
+    if (errno != error_exist)
+      strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archive/threads: ");
+  if (mkdir("archive/subjects",0755) == -1)
+    if (errno != error_exist)
+      strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archive/subjects: ");
+  if (mkdir("archive/authors",0755) == -1)
+    if (errno != error_exist)
+      strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archive/authors: ");
+
+       /* Lock list to assure that no ezmlm-send is working on it */
+       /* and that the "num" message is final */
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    strerr_die2sys(111,FATAL,ERR_OPEN_LOCK);
+  if (lock_ex(fdlock) == -1) {
+    (void) close(fdlock);
+    strerr_die2sys(111,FATAL,ERR_OBTAIN_LOCK);
+  }
+                                       /* get num */
+  if (!getconf_line(&num,"num",0,FATAL,dir))
+    strerr_die1x(100,ERR_EMPTY_LIST);
+  (void) close(fdlock);
+
+  if (!stralloc_0(&num)) die_nomem();  /* parse num */
+  (void) scan_ulong(num.s,&max);
+  if (!to || to > max) to = max;
+
+  fdlock = open_append("archive/lock");        /* lock index */
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/archive/lock: ");
+  if (lock_ex(fdlock) == -1) {
+    (void) close(fdlock);
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/archive/lock: ");
+  }
+  if (!flagcreate && !archnum) {       /* adjust archnum (from) / to */
+    if (getconf_line(&num,"archnum",0,FATAL,dir)) {
+      if (!stralloc_0(&num)) die_nomem();
+      (void) scan_ulong(num.s,&archnum);
+      archnum++;
+    }
+  }
+
+  if (archnum > to)
+    _exit(0);                          /* nothing to do */
+
+                                       /* do the subject threading */
+  idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
+       archnum,to,max,0,FATAL);
+                                       /* update the index */
+  write_threads(msgtable,subtable,authtable,datetable,archnum,to);
+                                       /* update archnum */
+  if ((fd = open_trunc("archnumn")) == -1)
+    strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archnumn: ");
+  substdio_fdbuf(&ssnum,write,fd,numbuf,sizeof(numbuf));
+  if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,to)) == -1)
+     strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+  if (substdio_puts(&ssnum,"\n") == -1)
+     strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": ");
+  close_proper(&ssnum,"archnum","archnumn");
+  switch (flagerror) {
+    case 0:
+       _exit(0);                               /* go bye-bye */
+    case -1:
+       strerr_die2x(99,WARNING,"threads entry with illegal format");
+    case -2:
+       strerr_die2x(99,WARNING,"thread in index, but threadfile missing");
+    case -3:
+       strerr_die2x(99,WARNING,"a subject file lacks subject");
+    case -4:
+       strerr_die2x(99,WARNING,"an author file lacks author/hash");
+    case -5:
+       strerr_die2x(99,WARNING,"threads entry lacks message count");
+    default:
+       strerr_die2x(99,WARNING,"something happened that isn't quite right");
+  }
+}
+
diff --git a/ezmlm-cgi.1 b/ezmlm-cgi.1
new file mode 100644 (file)
index 0000000..6103ed8
--- /dev/null
@@ -0,0 +1,349 @@
+.TH ezmlm-cgi 1
+.SH NAME
+ezmlm-cgi \- provide WWW access to the list archive
+.SH SYNOPSIS
+.B ezmlm-cgi
+.SH DESCRIPTION
+.B ezmlm-cgi
+is executed by the httpd daemon and generates HTTP/CGI/html 4.0-compliant
+self-referencing output of index pages for threads in a given month,
+messages in a thread, messages by a given author, messages by date,
+and messages themselves with full navigation controls. It uses the
+archive directly, aided by index files created by
+.BR ezmlm-idx(1) ,
+and
+.B ezmlm-send(1)
+as part of normal archive access and digest indexing, and
+by
+.BR ezmlm-archive(1) .
+
+.B ezmlm-cgi
+uses the httpd-supplied variables
+.B PATH_INFO
+to obtain the list number,
+.B QUERY_STRING
+to obtain the command, as well as
+.BR SERVER_NAME ,
+.BR SERVER_PORT ,
+and
+.B SCRIPT_NAME
+to create a self-referencing URL.
+
+When
+.B ezmlm-cgi
+is invoked without a command, it shows the threads for the
+current month.
+If no list number is supplied, the default list is shown (see below).
+.SH CONFIGURATION
+.B ezmlm-cgi
+expects to find configuration info in
+.B /etc/ezmlm/ezcgirc
+when run SUID root, or
+.B .ezcgirc
+otherwise. The entries in this file describe one list per line. Blank lines
+and comments
+starting with a ``#'' in position 1 are allowed and ignored. No extra
+blanks, tab, etc, are allowed. Entries must be
+of the following format:
+
+.EX
+.I listno;uid;listdir;listaddr;buttonbar;charset;style;bannerprog
+.EE
+
+.B where:
+.TP 5
+.I listno
+is the list number using ``0'' for the default list if desired;
+.TP 5
+.I uid
+the user id to switch to if installed SUID root (default invoking user id) and
+if preceded by ``-'' chroot() is suppressed for SUID root installations;
+.TP 5
+.I listdir
+ the absolute path to the list base directory (required);
+.TP 5
+.I listaddr
+the list address as local@host (required) and if preceded by ``-'' the
+``From:'' E-mail address is replaced by the posters name/handle as a
+further precaution against address harvesting;
+.TP 5
+.I buttonbar
+a set of comma-separated fields of the type
+.IR ``[Home]=http://example.com/list.html''.
+The text before the ``='' is the exact text displayed and the subsequent
+text should be the URL linked to that button. Use the braces to make the
+buttons be consistent with preexisting navigation buttons. It is desirable
+to add a ``[Help]'' button with a link to an explanation of the various
+displays generated by
+.BR ezmlm-cgi .
+.TP 5
+.I charset
+the character set used for the main pages (default ``iso-8859-1''); 
+.TP 5
+.I style
+the style sheet used (default none, which doesn't look pretty);
+.TP 5
+.I bannerprog
+the path to a banner program which is given
+the name of the script and the list as arguments (default none). The path
+is relative to ``listdir'' and can point anywhere in the file system. However,
+for SUID root installations access is normally restricted via
+.BR chroot(3) .
+(See SECURITY.)
+If
+.I ``bannerprog''
+starts with a less-than character (''<'') it is assumed to
+be a URL which is inserted as is, rather than executed.
+.TP 5
+.I ``;''
+the separator can be any non-numeric character and can be different for
+different
+.I ezcgirc
+lines. There
+is no quoting/escaping mechanism. Thus, choose a character not present in
+any of the arguments. ``bannerprog'' as the last argument is an exception,
+and may contain any characters except LF and NUL.
+.SH OPTIONS
+.TP 5
+If ``uid'' is preceded by a minus sign (``-''),
+.B ezmlm-cgi
+will not call
+.B chroot(3) .
+This potentially decreases security, but may be needed to
+execute ``bannerprog''.
+.TP 5
+If ``listaddr'' is preceded by a minus sign (``-''),
+.B ezmlm-cgi
+will, as a precaution against address harvesting robots,
+remove the sender's E-mail address also in the message view. This is
+already done in all other views. The archive user can still obtain the address
+by requesting the message by E-mail.
+.SH OUTPUT
+.B ezmlm-cgi
+outputs 5 different views.
+.TP
+.I thread index
+shows the threads which have messages in a given month. The subject is
+prefixed with the number of messages in the thread for the given month. When
+.B ezmlm-archive(1)
+is first run against an existing archive, the number is the total number of
+messages in the thread. The subject and author are links to the respective
+thread or author index. The threads are ordered in reverse order of latest
+message, i.e. the thread that last received a message is listed last. When
+.B ezmlm-archive(1)
+is run against an existing archive, the initial sort is in order of the
+first message in the thread.
+
+The subject in the
+.I thread index
+is a link to the last message in the thread.
+.TP
+.I thread
+shows the messages in the respective thread in date order. For each message
+the author is shown linked to the message.
+.TP
+.I author index
+shows the subject of all messages posted from a given address in order of
+arrival at the list. Links are to the messages.
+.TP
+.I message by date
+shows entries in order of arrival of sets of 100 messages. Links are to
+the message and to the author.
+.TP
+.I message
+shows the message itself. The message has links to the previous and next
+message by time, in the thread, or by the same author. There are also links
+to the other views, as well as links to subscribe, or request FAQ,
+the message or the thread by E-mail. The navigation bar is very concise
+to optimize appearance in
+.BR lynx .
+It is self-explanatory to anyone daring to experiment. For others, you may
+wish to supply a ``help'' button.
+The message subject is a
+.I mailto:
+link for a follow-up post to the list. 
+.SH "OUTPUT FORMATTING"
+.B ezmlm-cgi
+outputs html 4.0 in a format suitable for
+.I Lynx
+and other text-mode browsers. The format is designed for easy optional
+enhancement
+via CSS1/2 type
+style sheets in the format ``text/css''.
+.B ezmlm-cgi
+is self-documenting in this respect. Simply review the output in the different
+views and the sample style sheet to see the class structure.
+.SH "EXTERNAL LINKS TO MESSAGES"
+.B ezmlm-cgi
+will accept a PATH_INFO of the following format:
+
+.EX
+.I /listno/message
+.EE
+
+.B where:
+.TP 5
+.I listno
+is the list number per config file;
+.TP 5
+.I message
+is the message number.
+
+Thus,
+.B ezmlm-cgi\fI/2/20000
+will return message 20000 from list 2.
+
+.B ezmlm-cgi
+uses a second syntax based on QUERY_STRING for internal links. This
+command set is implemented only as far as required for normal
+.B ezmlm-cgi
+function. Useful are:
+.TP
+.B ezmlm-cgi\fB?listno?ams:message
+which will return in order the list of messages posted by the author of message
+.I message
+on list
+.IR listno ,
+and
+.TP
+.B ezmlm-cgi\fB?listno?sms:message
+which will return in order the list of messages with the same subject as message
+.I message
+.I message
+on list
+.IR listno ,
+i.e. the ``thread''.
+.SH ROBOTS
+There are many possible URLs for the same message.
+To still allow external indexing,
+.B ezmlm-cgi
+supports the command
+.I ezmlm-cgi/index
+which returns a page with links to all lists, except the default list. These
+links indirectly lead exactly once to each message.
+None of the links used contain
+a ``?''. Thus, to index the archives, allow access to scripts in the
+(separate)
+.I directory
+where
+.B ezmlm-cgi
+is installed, but deny access to
+.IR directory\fB/ezmlm-cgi\fI? .
+Any message will have a ``nofollow'' robot META tag, and any view reached by
+a URL based on QUERY_STRING will in addition have a ``noindex'' robot META tag
+to avoid trapping robots in the archive.
+.SH EXECUTION
+.B ezmlm-cgi
+can operate in three modes,
+.IR SUID\ root ,
+.IR SUID\ user ,
+and
+.IR normal .
+
+In
+.I normal
+and
+.I SUID user
+mode,
+.B ezmlm-cgi
+will read the configuration file
+.B .ezcgirc
+from the working directory set by the httpd daemon
+(per
+.B cgi
+definition this should be the same directory as
+.B ezmlm-cgi
+is in), then
+change directory to the list directory. ``uid'' is ignored.
+.I SUID user
+may be required to read the particular archive if it is not owned by the
+httpd user. For user installations or systems where
+the httpd user has access to all the lists,
+.I normal
+mode usually gives sufficient access.
+
+In
+.I SUID\ root
+mode,
+.B ezmlm-cgi
+will read the configuration info from
+.B /etc/ezmlm/ezcgirc
+then change directory to that directory, then
+change root to that directory, then change
+userid to ``uid''. If ``uid'' is not specified, it will change to the
+uid of the process invoking
+.B ezmlm-cgi
+(normally the httpd user). If the archive files are world-readable, but the list
+directory is not, it is safest to leave ``uid'' blank. The httpd user will still
+be able to read the files.
+.SH "EXECUTION OF BANNER PROGRAMS"
+A banner program can be specified in the config file. It is executed
+immediately before the end of the text. The formatting for
+``<BODY>'' is active and the banner program output is encapsulated in
+a ``<DIV class=banner>'' segment to allow additional formatting.
+The banner program is called for all summary views, but not for the message
+view itself.
+
+The banner program is give the list local name as argument 1, and the host
+name as argument 2. It is expected to exit 0 on success. The return code is
+checked, but the archive page (and whatever the banner program has already
+produced) is output even if the banner program fails.
+
+.B chroot(3)
+may make it difficult to run banner programs that depend on e.g. ``sh''
+or ``perl''. For this reason, the chroot call can be suppressed by prefixing
+the ``uid'' with a ``-''.
+.SH SECURITY
+.B ezmlm-cgi
+will refuse to run as root.
+
+.B ezmlm-cgi
+does not write or lock any files.
+
+.B ezmlm-cgi
+has a short well commented segment of code that potentially runs SUID root.
+Read the source to convince yourself that this is safe. If possible, install
+it SUID user, or not SUID at all, if that meets your needs (single list
+user, httpd user is list user, or httpd user has sufficient access to all
+list directories and archives).
+
+.B ezmlm-cgi
+will allow execution of banner programs that are located outside of the list
+directory. These are executed with the privileges of the userid set in the
+config file. If the program is installed SUID root, banner programs outside
+of the list directory are not normally accessible. Even when this is overridden,
+.B ezmlm-cgi
+will never execute the program with root permissions.
+
+Input to the CGI script is not propagated to the banner program.
+.SH BUGS
+.B ezmlm-send(1)
+updates the list message counter once a message is safely archived, but
+before it is accepted by
+.BR qmail(7) .
+Also, the
+.I index
+file is updated before the message is accepted by
+.BR qmail(7) .
+If
+.B qmail(7)
+fails,
+.B ezmlm-send(1)
+resets the counter before terminating. It is possible that in such a situation
+the message would be replaced by a different one.
+If
+.B ezmlm-cgi
+accesses a message that ultimately fails and in that time interval,
+it may expose a message that ultimately is replaced, especially when doing it
+via the ``Messages by date'' view which is based on the
+.I index
+file. In practice, this is relatively harmless. Avoiding it would require
+locking the list with significant implications for security and performance.
+.SH "SEE ALSO"
+ezmlm-archive(1),
+ezmlm-get(1),
+ezmlm-idx(1),
+ezmlm-send(1),
+ezmlm(5),
+qmail(7)
+
diff --git a/ezmlm-cgi.c b/ezmlm-cgi.c
new file mode 100644 (file)
index 0000000..9549aed
--- /dev/null
@@ -0,0 +1,2238 @@
+/*$Id: ezmlm-cgi.c,v 1.17 1999/12/24 04:21:26 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+/* Please leave. Will hopefully help pay for further improvement. */
+#define EZ_CRIGHT "<a href=\"http://www.lindeinc.com\">(c) 1999 Lin-De, Inc</a>"
+/******/
+#include <sys/types.h>
+#include "direntry.h"
+#include "datetime.h"
+#include "now.h"
+#include "stralloc.h"
+#include "strerr.h"
+#include "error.h"
+#include "env.h"
+#include "sig.h"
+#include "open.h"
+#include "getln.h"
+#include "case.h"
+#include "scan.h"
+#include "str.h"
+#include "fmt.h"
+#include "readwrite.h"
+#include "fork.h"
+#include "wait.h"
+#include "exit.h"
+#include "substdio.h"
+#include "getconf.h"
+#include "gen_alloc.h"
+#include "gen_allocdefs.h"
+#include "constmap.h"
+#include "byte.h"
+#include "subscribe.h"
+#include "errtxt.h"
+#include "makehash.h"
+#include "mime.h"
+#include "idx.h"
+#include "yyyymm.h"
+
+#define FATAL "ezmlm-cgi: fatal: "
+#define GET "-getv"
+#define THREAD "-threadv"
+#define SUBSCRIBE "-subscribe"
+#define FAQ "-faq"
+#define TXT_CGI_SUBSCRIBE "\">[eSubscribe]</a>\n"
+#define TXT_CGI_FAQ "\">[eFAQ]</a>\n"
+
+int flagshowhtml = 1;  /* show text/html parts. This leads to duplication */
+                       /* when both text/plain and text/html are in a     */
+                       /* multipart/alternative message, but it is assumed*/
+                       /* that text/html is not frivolous, but only used  */
+                       /* when the formatting is important. */
+int flagobscure = 0;   /* Don't remove Sender's E-mail address in message */
+                       /* view. Overridden by config file (- before list */
+                       /* name). */
+
+/**************** Header processing ***********************/
+char headers_used[] = "Subject\\From\\Date\\content-type\\"
+               "content-transfer-encoding\\mime-version";
+/* index of headers displayed (shown in order listed above) */
+int headers_shown[] = {1,1,1,0,0,0};
+/* index of specific headers */
+#define NO_HDRS 6
+#define HDR_SUBJECT 1
+#define HDR_FROM 2
+#define HDR_CT 4
+#define HDR_CTENC 5
+#define HDR_VERSION 6
+
+/* Need to add inits if you increase NO_HDRS */
+stralloc hdr[NO_HDRS] = { {0},{0},{0},{0},{0},{0} };
+/**************** Header processing ***********************/
+
+
+/* index of subject in above, first = 1 */
+
+/* TODO: Sort headers before display. Find a way to display the body with the*/
+/* correct charset, ideally letting the browser do the work (should really */
+/* be able to specify charset for DIV ! */
+
+/* ulong at least 32 bits. (Creating a Year 0xffffff problem ;-) */
+#define MAXULONG 0xffffffff
+
+char cmdstr[5] = "xxx:";
+#define ITEM "-msadiz"
+#define ITEM_MESSAGE 1
+#define ITEM_SUBJECT 2
+#define ITEM_AUTHOR 3
+#define ITEM_DATE 4
+#define ITEM_INDEX 5
+
+#define DIRECT "psnpn"
+#define DIRECT_SAME 0
+#define DIRECT_NEXT 1
+#define DIRECT_PREV -1
+/* use only as the argument for some functions. Terrible hack for date links */
+#define DIRECT_FIRST 3
+#define DIRECT_LAST 2
+
+char *dir = 0;
+char *local = 0;
+char *host = 0;
+char *home = 0;
+char *banner = 0;
+char *charset = 0;
+char *stylesheet = 0;
+char *cmd;
+char strnum[FMT_ULONG];
+/* these are the only headers we really care about for message display */
+/* one can always retrieve the complete message by E-mail */
+stralloc charg = {0};
+stralloc url = {0};
+stralloc author = {0};
+stralloc subject = {0};
+stralloc base = {0};
+stralloc line = {0};
+stralloc decline = {0};                /* for rfc2047-decoded headers and QP/base64 */
+stralloc cfline = {0};         /* from config file */
+stralloc fn = {0};
+stralloc dtline = {0};
+stralloc headers = {0};
+stralloc encoding = {0};
+stralloc content = {0};
+stralloc charsetbase = {0};
+stralloc curcharset = {0};
+stralloc sainit = {0};
+struct constmap headermap;
+unsigned long uid,euid;
+int recursion_level;
+int so = 0;
+int ss23 = 0;
+int state = 0;
+int newlevel;
+int match;     /* used everywhere and no overlap */
+int fd;                /* same; never >1 open */
+int cache;     /* 0 = don't; 1 = don't know; 2 = do */
+int child,wstat;
+int flagtoplevel;
+unsigned int flagmime;
+unsigned int cs,csbase;
+int flagrobot;
+int flagpre;
+int precharcount;
+char cn1 = 0;
+char cn2 = 0;
+char lastjp[] = "B";   /* to get back to the correct JP after line break */
+char *bannerargs[4];
+
+
+mime_info *mime_current = 0;
+mime_info *mime_tmp = 0;
+
+datetime_sec when;
+struct datetime dt;
+
+char inbuf[4096];
+substdio ssin;
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_syntax(char *s)
+{
+  strerr_die4x(100,FATAL,ERR_SYNTAX,"config file: ",s);
+}
+
+char outbuf[4096];
+substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof(outbuf));
+
+void oput(register char *s, register unsigned int l)
+/* unbuffered. Avoid extra copy as httpd buffers */
+{
+  if (substdio_put(&ssout,s,l) == -1)
+    strerr_die3sys(111,FATAL,ERR_WRITE,"stdout: ");
+}
+
+void oputs(register char *s)
+{
+  oput(s,str_len(s));
+}
+
+/* this error is for things that happen only if program logic is screwed up */
+void die_prog(char *s) { strerr_die5x(100,FATAL,"program error (please send bug report to bugs@ezmlm.org): ",s," Command: ",cmd); }
+
+/* If we already issued a header than this will look ugly */
+void cgierr(char *s,char *s1,char *s2)
+{
+  strerr_warn4(FATAL,s,s1,s2,(struct strerr *)0);
+  oputs("Content-type: text/plain\n");
+  oputs("Status: 500 Couldn't do it\n\n");
+  oputs("I tried my best, but:\n\n");
+  if (s) oputs(s);
+  if (s1) oputs(s1);
+  if (s2) oputs(s2);
+  oputs("\n");
+  substdio_flush(&ssout);
+  _exit(0);
+}
+
+unsigned long msgnav[5]; /* 0 prev prev 1 prev 2 this 3 next 4 next-next */
+
+struct msginfo {       /* clean info on the target message */
+  char item;           /* What we want */
+  char direction;      /* Relation to current msg */
+  char axis;           /* Axis of desired movement [may be calculated] */
+  unsigned long source;        /* reference message number */
+  unsigned long target;
+  unsigned long date;
+  unsigned long *authnav;      /* msgnav structure */
+  unsigned long *subjnav;      /* msgnav structure */
+  char *author;
+  char *subject;
+  char *cgiarg;                        /* sub/auth as expected from axis */
+} msginfo;
+
+void toggle_flagpre(int flag)
+{
+  flagpre = flag;
+  precharcount = 0;
+  cn1 = 0; cn2 = 0;            /* just in case */
+}
+
+unsigned int decode_charset(register char *s, register unsigned int l)
+/* return charset code. CS_BAD means that base charset should be used, i.e. */
+/* that charset is empty or likely invalid. CS_NONE are charsets for which  */
+/* we don't need to do anything special. */
+{
+  unsigned int r;
+
+  if (case_startb(s,l,"iso-8859") || case_startb(s,l,"us-ascii") ||
+       case_startb(s,l,"utf")) /* at the moment, we can do utf-8 right */
+    return CS_NONE;            /* what is utf-7 (used by OE)? */
+  if (case_startb(s,l,"x-cp") ||
+       case_startb(s,l,"cp") ||
+       case_startb(s,l,"x-mac") ||
+       case_startb(s,l,"koi8")) return CS_NONE;
+  if (!l || *s == 'x' || *s == 'X') return CS_BAD;
+  if (case_startb(s,l,"iso-2022")) {
+    if (case_startb(s+8,l-8,"-cn"))
+      return CS_2022_CN;
+    if (case_startb(s+8,l-8,"-jp"))
+      return CS_2022_JP;
+    return CS_2022_KR;
+  }
+  if (case_startb(s,l,"cn-") ||
+               case_startb(s,l,"hz-gb") ||
+               case_startb(s,l,"gb") ||
+               case_startb(s,l,"big5"))
+    return CS_CN;              /* Only consideration for linebreak */
+  if (case_startb(s,l,"iso_8859") ||
+       case_startb(s,l,"latin") ||
+       case_startb(s,l,"windows")) return CS_NONE;
+/* Add other charsets here. Later we will add code to replace a detected */
+/* charset name with another, and to connect conversion routines, such as */
+/* between windows-1251/koi-8r/iso-8859-5 */
+  return CS_BAD;
+}
+
+void htmlencode_put (register char *s,register unsigned int l)
+/* At this time, us-ascii, iso-8859-? create no problems. We just encode  */
+/* some html chars. iso-2022 may have these chars as character components.*/
+/* cs is set for these, 3 for CN, 2 for others. Bit 0 set means 2 byte    */
+/* chars for SS2/SS3 shiftouts (JP doesn't use them, KR has single byte.  */
+/* If cs is set and we're shifted out (so set) we don't substitute. We    */
+/* also look for SI/SO to adjust so, and ESC to detect SS2/SS3. Need to   */
+/* ignore other ESC seqs correctly. JP doesn't use SI/SO, but uses        */
+/* ESC ( B/J and ESC $ B/@ analogously, so we use these to toggle so.     */
+/* "Roman", i.e. ESC ( J is treated as ascii - no differences in html-    */
+/* relevant chars. Together, this allows us to deal with all iso-2022-*   */
+/* as a package. see rfc1468, 1554, 1557, 1922 for more info.             */
+/* line break at 84 to avoid splits with lines just a little too long. */
+{
+  if (!cs) {           /* us-ascii & iso-8859- & unrecognized */
+    for (;l--;s++) {
+      precharcount++;
+      switch (*s) {
+        case '>': oputs("&gt;"); break;
+        case '<': oputs("&lt;"); break;
+        case '"': oputs("&quot;"); break;
+        case '&': oputs("&amp;"); break;
+       case '\n': precharcount = 0; oput(s,1); break;
+       case ' ':
+         if (precharcount >= 84 && flagpre) {
+           oput("\n",1);                       /* in place of ' ' */
+           precharcount = 0;
+         } else
+           oput(s,1);                          /* otherwise out with it. */
+         break;
+        default: oput(s,1); break;
+      }
+    }
+  } else if (cs == CS_CN) {                    /* cn-, gb*, big5 */
+    for (;l--;s++) {
+      precharcount++;
+      if (cn1) { cn2 = cn1; cn1 = 0; }         /* this is byte 2 */
+      else { cn2 = 0; cn1 = *s & 0x80; }       /* this is byte 1/2 or ascii */
+      if (!cn1 && !cn2) {                      /* ascii */
+       switch (*s) {
+          case '>': oputs("&gt;"); break;
+          case '<': oputs("&lt;"); break;
+          case '"': oputs("&quot;"); break;
+          case '&': oputs("&amp;"); break;
+         case '\n': precharcount = 0; oput(s,1); break;
+          case ' ':
+               if (precharcount >= 84 && flagpre) {
+                 oput("\n",1);         /* break in ascii sequence */
+                 precharcount = 0;
+               } else
+                 oput(s,1);
+               break;
+         default: oput(s,1); break;
+       }
+      } else if (precharcount >= 84 && flagpre && cn2) {
+         oput("\n",1);                 /* break after 2-byte code */
+         precharcount = 0;
+      }
+    }
+  } else {                                     /* iso-2022 => PAIN! */
+    for (;l--;s++) {
+      precharcount++;
+      if (ss23) {                              /* ss2/ss3 character */
+       ss23--;
+       oput(s,1);
+        continue;
+      }
+      if (so) {                                        /* = 0 ascii, = 1 SO charset */
+        if (!(*s & 0xe0)) {                    /* ctrl-char */
+         switch (*s) {
+           case ESC: state = 1; break;
+           case SI: so = 0; break;
+           case '\n': precharcount = 0; break;
+           default: break;
+         }
+       }
+       oput(s,1);
+      } else {                                 /* check only ascii */
+       switch (*s) {
+         case '>': oputs("&gt;"); break;
+         case '<': oputs("&lt;"); break;
+         case '"': oputs("&quot;"); break;
+         case '&': oputs("&amp;"); break;
+          case ' ':
+               if (precharcount >= 84 && flagpre) {
+                 oput("\n",1);         /* break in ascii sequence */
+                 precharcount = 0;
+               } else
+                 oput(s,1);
+               break;
+         default:
+                 oput(s,1);
+                 if (!(*s & 0xe0)) {
+                   switch (*s) {
+                     case SO: so = 1; break;
+                     case ESC: state = 1; break;
+                     case SI: so = 0; break;   /* shouldn't happen */
+                     case '\n': precharcount = 0; break;
+                     default: break;
+                   }
+                 }
+       }
+      }                /* by now all output is done, now ESC interpretation */
+      if (state) {
+               /* ESC code - don't count */
+         if (precharcount) precharcount--;
+         state++;
+         switch (state) {
+           case 2: break;                      /* this was the ESC */
+           case 3: switch (*s) {
+                       case 'N': ss23 = (cs & 1) + 1; state = 0; break;
+                       case 'O': ss23 = 2; state = 0; break;
+                       case '(': state = 20; so = 0; break;    /* JP ascii */
+                       case '$': break;                /* var S2/SS2/SS3 des*/
+                       case '.': state = 10;   /* g3 settings, one more char */
+                       default: state = 0; break;      /* or JP */
+               }
+               break;
+           case 4: switch (*s) {       /* s2/ss2/ss3 or JP 2 byte shift */
+                  case 'B':
+                  case '@': lastjp[0] = *s;
+                            so = 1; state = 0; break;  /* JP */
+                  default: break;                      /* other SS2/3 des */
+                }
+                break;
+           case 5:  state = 0; break;          /* 4th char of ESC $ *|+|) X */
+           case 11: state = 0; break;          /* 3nd char of ESC . */
+           case 21: state = 0; break;          /* ESC ( X for JP */
+           default: die_prog("bad state in htmlencode_put"); break;
+         }
+      } else if (so && flagpre && precharcount >= 84) {
+               /* 84 is nicer than 78/80 since most use GUI browser */
+               /* iso-2022-* line splitter here. SO only, SI done above */
+               /* For JP need even precharcount, add ESC ( B \n ESC $B */
+       if (so && !(precharcount & 1)) {        /* even */
+         precharcount = 0;                     /* reset */
+         if (cs == CS_2022_JP) {               /* JP uses ESC like SI/SO */
+           oputs(TOASCII);
+           oput("\n",1);
+           oputs(TOJP);
+           oput(lastjp,1);
+         } else {
+           if (so) {
+               /* For iso-2022-CN: nothing if SI, otherwise SI \n SO */
+               /* For iso-2022-KR same */
+             oputs(SI_LF_SO);
+           } else
+             oput("\n",1);
+         }
+       }
+      }
+    }
+  }
+}
+
+char hexchar[] = "0123456789ABCDEF";
+char enc_url[] = "%00";
+
+void urlencode_put (register char *s,register unsigned int l)
+{
+  for (;l--;s++) {
+    register unsigned char ch;
+    ch = (unsigned char) *s;
+    if (ch <= 32 || ch > 127 || byte_chr("?<>=/:%+#\"",10,ch) != 10) {
+      enc_url[2] = hexchar[ch & 0xf];
+      enc_url[1] = hexchar[(ch >> 4) & 0xf];
+      oput(enc_url,3);
+    } else
+      oput(s,1);
+  }
+}
+
+void urlencode_puts(register char *s)
+{
+  urlencode_put(s,str_len(s));
+}
+
+int checkhash(register char *s)
+{
+  register int l = HASHLEN;
+  while (l--) {
+    if (*s < 'a' || *s > 'p') return 0;        /* illegal */
+    s++;
+  }
+  if (*s) return 0;                    /* extraneous junk */
+  return 1;
+}
+
+int makefn(stralloc *sa,char item, unsigned long n, char *hash)
+{
+  if (!stralloc_copys(sa,"archive/")) die_nomem();
+  if (item == ITEM_MESSAGE) {
+    if (!stralloc_catb(sa,strnum,fmt_ulong(strnum, n / 100))) die_nomem();
+    if (!stralloc_cats(sa,"/")) die_nomem();
+    if (!stralloc_catb(sa,strnum,fmt_uint0(strnum,(unsigned int) (n % 100),2)))
+                       die_nomem();
+  } else if (item == ITEM_DATE) {
+    if (!stralloc_cats(sa,"threads/")) die_nomem();
+    if (!stralloc_catb(sa,strnum,fmt_ulong(strnum,n)))
+       die_nomem();
+  } else if (item == ITEM_INDEX) {
+    if (!stralloc_catb(sa,strnum,fmt_ulong(strnum, n / 100))) die_nomem();
+    if (!stralloc_cats(sa,"/index")) die_nomem();
+  } else {
+    if (item == ITEM_AUTHOR) {
+      if (!stralloc_cats(sa,"authors/")) die_nomem();
+    } else {
+      if (!stralloc_cats(sa,"subjects/")) die_nomem();
+    }
+    if (!hash) return 0;
+    if (!stralloc_catb(sa,hash,2)) die_nomem();
+    if (!stralloc_cats(sa,"/")) die_nomem();
+    if (!stralloc_catb(sa,hash+2,HASHLEN-2)) die_nomem();
+  }
+  if (!stralloc_0(sa)) die_nomem();
+  return 1;
+}
+
+void link(struct msginfo *infop,char item,char axis,unsigned long msg,
+               char *data,unsigned int l)
+/* links with targets other msg -> msg. If the link is for author, we    */
+/* still supply subject, since most navigation at the message level will */
+/* be along threads rather than author and we don't have an author index.*/
+{
+  char *cp;
+
+  cp = (char *) 0;
+       /* this should be separate routine. Works because all index views */
+       /* have at least a subject link */
+  if (axis == ITEM_SUBJECT && infop->target == msg)
+    oputs("<a name=b></a>");
+  oput(url.s,url.len);
+  cmdstr[0] = ITEM[item];
+  cmdstr[1] = ITEM[axis];
+  cmdstr[2] = DIRECT[DIRECT_SAME + 1];
+  if (item == ITEM_MESSAGE && axis == ITEM_AUTHOR) {
+    if (infop->subject) {
+      cmdstr[1] = ITEM[ITEM_SUBJECT];
+      cp = infop->subject;     /* always HASLEN in length due to decode_cmd */
+    }
+  }
+  oputs(cmdstr);               /* e.g. map: */
+  oput(strnum,fmt_ulong(strnum,msg));
+  if (!cp && l >= HASHLEN)
+    cp = data;
+  if (infop->date) {
+    oput(":",1);
+    oput(strnum,fmt_ulong(strnum,infop->date));
+  }
+  if (cp) {
+    oput(":",1);
+    oput(cp,HASHLEN);
+  }
+  switch (item) {
+    case ITEM_MESSAGE: oputs("\" class=mlk>"); break;
+    case ITEM_AUTHOR: oputs("#b\" class=alk>"); break;
+    case ITEM_SUBJECT: oputs("#b\" class=slk>"); break;
+    default: oputs("#b\">"); break;
+  }
+  if (HASHLEN + 1 < l)
+    htmlencode_put(data + HASHLEN + 1,l - HASHLEN - 1);
+  else
+    oputs("(none)");
+  oputs("</A>");
+}
+
+void linktoindex(struct msginfo *infop,char item)
+/* for links from message view back to author/subject/threads index */
+{
+  oput(url.s,url.len);
+  cmdstr[0] = ITEM[item];
+  cmdstr[1] = ITEM[item];
+  cmdstr[2] = DIRECT[DIRECT_SAME + 1];
+  oputs(cmdstr);               /* e.g. map: */
+  oput(strnum,fmt_ulong(strnum,infop->target));
+  if (infop->date) {
+    oput(":",1);
+    oput(strnum,fmt_ulong(strnum,infop->date));
+  }
+  switch (item) {
+    case ITEM_AUTHOR:
+      if (infop->author) {
+       oput(":",1);
+       oputs(infop->author);
+      }
+      break;
+    case ITEM_SUBJECT:
+      if (infop->subject) {
+       oput(":",1);
+       oputs(infop->subject);
+      }
+      break;
+    default:
+      break;
+  }
+  oputs("#b\"");
+}
+
+void link_msg(struct msginfo *infop,char axis,char direction)
+/* Creates <a href="mapa:123:aaaaa...."> using a maximum of available */
+/* info only for links where the target is a message */
+{
+  unsigned long msg;
+  char *acc;
+  oput(url.s,url.len);
+  cmdstr[0] = ITEM[ITEM_MESSAGE];
+  cmdstr[1] = ITEM[axis];
+  cmdstr[2] = DIRECT[direction + 1];
+  msg = infop->target;
+  acc = 0;
+      switch(axis) {
+       case ITEM_SUBJECT:
+         if (infop->subject)
+           acc = infop->subject;
+         if (infop->subjnav)   /* translate to message navigation */
+           if (infop->subjnav[direction]) {
+             msg = infop->subjnav[direction];
+             cmdstr[2] = DIRECT[DIRECT_SAME + 1];
+         }
+         acc = infop->subject;
+         break;
+       case ITEM_AUTHOR:
+         if (infop->author)
+           acc = infop->author;
+         if (infop->authnav)   /* translate to message navigation */
+           if (infop->authnav[direction]) {
+             msg = infop->authnav[direction];
+             cmdstr[2] = DIRECT[DIRECT_SAME + 1];
+           }
+         acc = infop->author;
+         break;
+       default:
+         break;
+       }
+       oputs(cmdstr);
+       oput(strnum,fmt_ulong(strnum,msg));
+       if (acc) {
+         oputs(":");
+         oputs(acc);
+       }
+       oputs("\">");
+}
+
+void justpress()
+{
+  oputs("?subject=");
+  urlencode_puts("Just Click \"SEND\"!");
+}
+
+void homelink()
+{
+  register char *cp,*cp1,*cp2;
+
+  if (home && *home) {
+    cp = home;
+    for(;;) {
+      cp1 = cp;
+      while(*cp1 && *cp1 != '=') cp1++;
+      if (!*cp1) break;
+      cp2 = cp1;
+      while(*cp2 && *cp2 != ',') cp2++;
+      oputs("<a href=\"");
+      oput(cp1 + 1,cp2 - cp1 - 1);
+      oputs("\">");
+      oput(cp,cp1 - cp);
+      oputs("</a>\n");
+      if (!*cp2) break;
+      cp = cp2 + 1;
+    }
+  }
+}
+
+void subfaqlinks()
+{
+  oputs("<a href=\"mailto:");
+  oputs(local);
+  oputs(SUBSCRIBE);
+  oputs("@");
+  oputs(host);
+  justpress();
+  oputs(TXT_CGI_SUBSCRIBE);
+  oputs("<a href=\"mailto:");
+  oputs(local);
+  oputs(FAQ);
+  oputs("@");
+  oputs(host);
+  justpress();
+  oputs(TXT_CGI_FAQ);
+}
+
+void msglinks(struct msginfo *infop)
+/* Creates the html for all links from one message view */
+{
+  oputs("<DIV class=msglinks><STRONG>Msg by: ");
+  link_msg(infop,ITEM_SUBJECT,DIRECT_PREV);
+  oputs("[&lt;-</A> ");
+  linktoindex(infop,ITEM_SUBJECT);
+  oputs(">thread</A> ");
+  link_msg(infop,ITEM_SUBJECT,DIRECT_NEXT);
+  oputs("-&gt;]</A> \n");
+  link_msg(infop,ITEM_MESSAGE,DIRECT_PREV);
+  oputs("[&lt;-</A> ");
+  linktoindex(infop,ITEM_INDEX);
+  oputs(">time</A> ");
+  link_msg(infop,ITEM_MESSAGE,DIRECT_NEXT);
+  oputs("-&gt;]</A> \n");
+  link_msg(infop,ITEM_AUTHOR,DIRECT_PREV);
+  oputs("[&lt;-</A> ");
+  linktoindex(infop,ITEM_AUTHOR);
+  oputs(">author</A> ");
+  link_msg(infop,ITEM_AUTHOR,DIRECT_NEXT);
+  oputs("-&gt;]</A> |\n");
+  linktoindex(infop,ITEM_DATE);
+  oputs(">[Threads]</A>\n");
+  homelink();
+  oputs("\n<a href=\"mailto:");
+  oputs(local);
+  oputs(GET);
+  strnum[fmt_ulong(strnum,infop->target)] = '\0';
+  oputs(strnum);
+  oputs("@");
+  oputs(host);
+  justpress();
+  oputs("\">[eMsg]</A>\n");
+  oputs("<a href=\"mailto:");
+  oputs(local);
+  oputs(THREAD);
+  oputs(strnum);
+  oputs("@");
+  oputs(host);
+  justpress();
+  oputs("\">[eThread]</A>\n");
+  subfaqlinks();
+  oputs("</STRONG></DIV>\n");
+}
+
+#define SPC_BASE 1
+#define SPC_BANNER 2
+
+void html_header(char *t,char *s, unsigned int l,char *class,int flagspecial)
+/* flagspecial: 0x1 => robot index; no style sheet, no BASE */
+/* flagspecial: 0x2 => banner, if available */
+{
+  oputs("Content-Type: text/html; charset=");
+  oput(curcharset.s,curcharset.len);
+
+  oputs("\nCache-Control: ");
+  switch (cache) {
+    case 0:
+       oputs("no-cache");              /* known upper border */
+       break;
+    case 1:
+       oputs("max-age=300");           /* 5 min - most lists aren't that fast*/
+       break;
+    case 2:
+       oputs("max-age=1209600");       /* 14 days is a long time */
+       break;
+  }
+  oputs("\n\n");
+  oputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n");
+  oputs("<HTML><HEAD>\n<TITLE>");
+  if (local) {
+    oputs(local);
+    oputs("@");
+    oputs(host);
+    oputs(": ");
+  }
+  if (t) oputs(t);
+  if (s) htmlencode_put(s,l);
+  oputs("</TITLE>\n");
+  if (class && *class && stylesheet && *stylesheet) {
+    oputs("<LINK href=\"");
+    oputs(stylesheet);
+    oputs("\" rel=\"stylesheet\" type=\"text/css\">\n");
+  }
+  if (!flagrobot)      /* robot access allowed to follow */
+    oputs("<META NAME=\"robots\" CONTENT=\"noindex\">\n");
+  if (flagrobot < 2)
+    oputs("<META NAME=\"robots\" CONTENT=\"nofollow\">\n");
+  if (flagspecial & SPC_BASE)
+    oput(base.s,base.len);
+  oputs("</HEAD>\n");
+  if (class && *class) {
+    oputs("<BODY class=");
+    oputs(class);
+    oputs(">\n");
+  } else
+    oputs("<BODY>\n");
+
+}
+
+void html_footer(int flagspecial)
+{
+  oputs("<HR><DIV class=copyright>");
+  oputs(EZ_CRIGHT);
+  oputs("</DIV>");
+  if ((flagspecial & SPC_BANNER) && banner && *banner) {
+    oputs("<DIV class=banner>\n");
+    if (*banner == '<') oputs(banner);
+    else {
+      substdio_flush(&ssout);
+      sig_pipeignore();
+      bannerargs[0] = banner;
+      bannerargs[1] = host;
+      bannerargs[2] = local;
+      bannerargs[3] = 0;
+       /* We log errors but just complete the page anyway, since we're */
+       /* already committed to output something. */
+      switch(child = fork()) {
+        case -1:
+          strerr_warn3(FATAL,ERR_FORK,"banner program: ",&strerr_sys);
+          break;
+        case 0:
+          execv(*bannerargs,bannerargs);
+          strerr_die3x(100,FATAL,ERR_EXECUTE,"banner program: ");
+         break;
+      }
+         /* parent */
+      wait_pid(&wstat,child);
+      if (wait_crashed(wstat))
+        strerr_warn2(FATAL,ERR_CHILD_CRASHED,(struct strerr *) 0);
+      if (wait_exitcode(wstat))
+        strerr_warn2(FATAL,ERR_CHILD_UNKNOWN,(struct strerr *) 0);
+    }
+    oputs("</DIV>\n");
+  }
+  oputs("</BODY>\n</HTML>\n");
+  substdio_flush(&ssout);
+}
+
+/* DATE functions */
+
+void datelink(struct msginfo *infop,unsigned long d,char direction)
+/* output a date with link back to thread index */
+{
+  oput(url.s,url.len);
+  cmdstr[0] = ITEM[ITEM_DATE];
+  cmdstr[1] = ITEM[ITEM_DATE];
+  cmdstr[2] = DIRECT[direction + 1];
+  oputs(cmdstr);
+  if (direction == DIRECT_LAST)
+    oput("0",1);       /* suppress msgnum to avoid going there */
+  else
+    oput(strnum,fmt_ulong(strnum,infop->target));
+  oputs(":");
+  oput(strnum,fmt_ulong(strnum,d));
+  oputs("#b\">");
+  switch (direction) {
+    case DIRECT_SAME:
+       if (dateline(&dtline,d) < 0) die_nomem();
+       oput(dtline.s,dtline.len);
+       break;
+    case DIRECT_PREV:
+       oputs("[&lt;-]");
+       break;
+    case DIRECT_NEXT:
+       oputs("[-&gt;]");
+       break;
+    case DIRECT_FIRST:
+       oputs("[&lt;&lt;-]");
+       break;
+    case DIRECT_LAST:
+       oputs("[-&gt;&gt;]");
+       break;
+  }
+  oputs("</A>");
+}
+
+void finddate(struct msginfo *infop)
+/* DIRECT_SAME works as DIRECT_PREV, dvs returns previous date or last date */
+{
+  DIR *archivedir;
+  direntry *d;
+  unsigned long ddate, startdate;
+  unsigned long below, above;
+
+  below = 0L;
+  above = MAXULONG;    /* creating a Y 0xffffff problem */
+  startdate = infop->date;
+  archivedir = opendir("archive/threads/");
+  if (!archivedir)
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/archive/threads: ");
+    else
+      strerr_die4sys(100,FATAL,ERR_OPEN,dir,"/archive/threads: ");
+
+  while ((d = readdir(archivedir))) {          /* dxxx/ */
+    if (str_equal(d->d_name,".")) continue;
+    if (str_equal(d->d_name,"..")) continue;
+    scan_ulong(d->d_name,&ddate);
+    if (!ddate) continue;      /* just in case some smart guy ... */
+    if (startdate) {
+      if (ddate > startdate && ddate < above) above = ddate;
+      if (ddate < startdate && ddate > below) below = ddate;
+    } else {
+      if (ddate < above) above = ddate;
+      if (ddate > below) below = ddate;
+    }
+  }
+  closedir(archivedir);
+
+  if (infop->direction == DIRECT_NEXT && above != MAXULONG || !below)
+       /* we always give a valid date as long as there is at least one */
+    infop->date = above;
+  else
+    infop->date = below;
+  return;
+}
+
+void latestdate(struct msginfo *infop,int flagfail)
+{
+  if (!flagfail) {
+    datetime_tai(&dt,now());
+    infop->date = ((unsigned long) dt.year + 1900) * 100 + dt.mon + 1;
+  } else {
+    infop->date = 0;
+    infop->direction = DIRECT_PREV;
+    finddate(infop);
+  }
+}
+
+void firstdate(struct msginfo *infop,int flagfail)
+{
+    infop->date = 0;
+    infop->direction = DIRECT_NEXT;
+    finddate(infop);
+}
+
+void getdate(struct msginfo *infop,int flagfail)
+/* infop->date has to be 0 or valid on entry. Month outside of [1-12] on */
+/* entry causes GIGO */
+{
+  if (!flagfail) {                             /* guess */
+    if (infop->direction == DIRECT_NEXT) {
+      infop->date++;
+      if (infop->date % 100 > 12) infop->date += (100 - 12);
+    } else if (infop->direction == DIRECT_PREV) {
+      infop->date--;
+      if (!infop->date % 100) infop->date -= (100 - 12);
+    }
+  } else
+    finddate(infop);
+  return;
+}
+
+indexlinks(struct msginfo *infop)
+{
+  unsigned long tmpmsg;
+
+  tmpmsg = infop->target;
+  infop->target = 1;
+  oputs("<DIV class=idxlinks><STRONG>");
+  linktoindex(infop,ITEM_INDEX);
+  oputs(">[&lt;&lt;-]</A>\n");
+  if (tmpmsg >= 100) infop->target = tmpmsg - 100;
+  linktoindex(infop,ITEM_INDEX);
+  oputs(">[&lt;-]</A>\n");
+  infop->target = tmpmsg + 100;
+  linktoindex(infop,ITEM_INDEX);
+  oputs(">[-&gt;]</A>\n");
+  infop->target = MAXULONG;
+  linktoindex(infop,ITEM_INDEX);
+  oputs(">[-&gt;&gt;]</A> |\n");
+  infop->target = tmpmsg;
+  linktoindex(infop,ITEM_DATE);
+  oputs(">[Threads by date]</A>\n");
+  subfaqlinks();
+  homelink();
+  oputs("</STRONG></DIV>\n");
+}
+
+int show_index(struct msginfo *infop)
+{
+  unsigned long thismsg;
+  unsigned int pos,l;
+  char ch;
+
+  (void) makefn(&fn,ITEM_INDEX,msginfo.target,"");
+  if ((fd = open_read(fn.s)) == -1)
+    if (errno == error_noent)
+      return 0;
+    else
+      strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+  if (!stralloc_copyb(&line,strnum,
+       fmt_ulong(strnum,(unsigned long) (infop->target / 100))))
+               die_nomem();
+  if (!stralloc_cats(&line,"xx")) die_nomem();
+  html_header("Messages ",line.s,line.len,"idxbody",SPC_BANNER | SPC_BASE);
+  indexlinks(infop);
+  oputs("<HR><H1 id=\"idxhdr\">");
+  oputs("Messages ");
+  oput(line.s,line.len);
+  oputs("</H1>\n");
+  oputs("<HR><DIV class=idx>\n");
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match)
+      break;
+    pos = scan_ulong(line.s,&thismsg);
+    l = pos;
+    ch = line.s[pos++];
+    pos++;
+    if (line.len < pos + 1 + HASHLEN)
+       strerr_die2x(100,FATAL,"index line with truncated subject entry");
+    if (!stralloc_copyb(&subject,line.s+pos,HASHLEN)) die_nomem();
+    if (!stralloc_0(&subject)) die_nomem();
+    infop->axis = ITEM_SUBJECT;
+    infop->subject = subject.s;
+    oput(strnum,fmt_uint0(strnum,(unsigned int) thismsg % 100,2));
+    oputs(": ");
+    link(infop,ITEM_MESSAGE,ITEM_SUBJECT,thismsg,line.s+pos,line.len - pos - 1);
+    oputs("\n");
+    if (ch == ':') {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+      if (!match)
+        break;
+      pos = byte_chr(line.s,line.len,';');
+      if (pos != line.len) {
+       infop->date = date2yyyymm(line.s);
+       oputs("(");
+       link(infop,ITEM_AUTHOR,ITEM_AUTHOR,thismsg,line.s+pos+1,
+               line.len - pos - 2);
+       oputs(")<BR>\n");
+      }
+    }
+  }
+  close(fd);
+  oputs("\n</DIV><HR>\n");
+  indexlinks(infop);
+  html_footer(SPC_BANNER);
+  return 1;
+}
+
+void objectlinks(struct msginfo *infop, char item)
+{
+  oputs("<DIV class=objlinks><STRONG>\n");
+  if (item == ITEM_DATE) {
+    datelink(infop,0,DIRECT_FIRST);
+    datelink(infop,infop->date,DIRECT_PREV);
+    datelink(infop,infop->date,DIRECT_NEXT);
+    datelink(infop,0,DIRECT_LAST);
+    oputs("\n");
+  } else {
+    if (!infop->target) infop->axis = ITEM_DATE;
+    linktoindex(infop,ITEM_DATE);
+    oputs(">[Threads by date]</A>\n");
+  }
+  if (item != ITEM_INDEX) {
+    linktoindex(infop,ITEM_INDEX);
+    oputs(">[Messages by date]</A>\n");
+  }
+  homelink();
+  subfaqlinks();
+  oputs("</STRONG></DIV>\n");
+}
+
+int show_object(struct msginfo *infop,char item)
+/* shows thread, threads, author */
+/* infop has the info needed to access the author/subject/thread file */
+{
+  unsigned long lastdate,thisdate,thismsg;
+  char linkitem;
+  char targetitem;
+  unsigned int pos;
+
+  lastdate = 0L;
+  targetitem = ITEM_MESSAGE;                   /* default message is target */
+  switch (item) {
+    case ITEM_SUBJECT:
+       if (!makefn(&fn,ITEM_SUBJECT,0L,infop->subject)) return 0;
+       break;
+    case ITEM_AUTHOR:
+       if (!makefn(&fn,ITEM_AUTHOR,0L,infop->author)) return 0;
+       break;
+    case ITEM_DATE:
+       if (!makefn(&fn,ITEM_DATE,infop->date,"")) return 0;
+       break;
+    default:
+       die_prog("Bad object type in show_object");
+  }
+if ((fd = open_read(fn.s)) == -1)
+    if (errno == error_noent)
+      return 0;
+    else
+      strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+  if (item != ITEM_DATE) {
+    if (getln(&ssin,&line,&match,'\n') == -1)  /* read subject */
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match || line.len < HASHLEN + 2)
+      strerr_die4x(111,FATAL,ERR_READ,fn.s,": nothing there");
+  }
+  switch (item) {
+    case ITEM_SUBJECT:
+       html_header("Thread on: ",line.s + HASHLEN + 1,
+               line.len - HASHLEN - 2,"subjbody",SPC_BANNER | SPC_BASE);
+       objectlinks(infop,item);
+       oputs("<HR><H1>On: ");
+       oput(line.s+HASHLEN+1,line.len-HASHLEN-2);
+       oputs("</H1>\n");
+       break;
+    case ITEM_AUTHOR:
+       html_header("Posts by: ",line.s + HASHLEN + 1,
+               line.len - HASHLEN - 2,"authbody",SPC_BANNER | SPC_BASE);
+       objectlinks(infop,item);
+       oputs("<HR><H1>By: ");
+       oput(line.s+HASHLEN+1,line.len-HASHLEN-2);
+       oputs("</H1>\n");
+       break;
+    case ITEM_DATE:
+/*     targetitem = ITEM_SUBJECT;*/    /* thread index is target */
+       thisdate = infop->date;
+       if (dateline(&dtline,infop->date) < 0) die_nomem();
+       html_header("Threads for ",
+               dtline.s,dtline.len,"threadsbody",SPC_BANNER | SPC_BASE);
+       objectlinks(infop,item);
+       oputs("<HR><H1>Threads for ");
+       oput(dtline.s,dtline.len);
+       oputs("</H1>\n");
+       break;
+    default: die_prog("unrecognized object type in show_object");
+  }
+
+  oputs("<DIV class=obj>\n");
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)  /* read subject */
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match)
+      break;
+    pos = scan_ulong(line.s,&thismsg);
+    if (line.s[pos++] != ':')
+       strerr_die4x(100,FATAL,"entry in ",fn.s," lacks message number");
+    if (item != ITEM_DATE) {           /* no date for threads by date */
+      pos += scan_ulong(line.s+pos,&thisdate);
+      infop->date = thisdate;
+      if (line.s[pos++] != ':')
+       strerr_die4x(100,FATAL,"entry in ",fn.s," lacks date");
+    }
+    if (line.len < pos + HASHLEN + 2)
+       strerr_die4x(100,FATAL,"entry in ",fn.s," lacks hash");
+    if (thisdate != lastdate) {
+      if (!lastdate)
+       oputs("<UL>\n");
+      else
+        oputs("<P>");
+      oputs("<LI><H2>");
+      datelink(infop,thisdate,DIRECT_SAME);
+      lastdate = thisdate;
+      oputs("</H2>\n");
+    }
+    if (item == ITEM_SUBJECT)
+      linkitem = ITEM_AUTHOR;
+    else
+      linkitem = ITEM_SUBJECT;
+    link(infop,targetitem,linkitem,thismsg,line.s+pos,line.len - pos - 1);
+    oputs("<BR>\n");
+  }
+  close(fd);
+  oputs("</UL>\n");
+  if (!infop->target)
+    oputs("<a name=b></a>");
+  oputs("<HR></DIV>\n");
+  objectlinks(infop,item);
+  html_footer(SPC_BANNER);
+  return 1;
+}
+
+void clear_mime()
+{
+  mime_current->charset.len = 0;       /* exist but need emptying */
+  mime_current->boundary.len = 0;
+  mime_current->ctype.len = 0;
+  mime_current->mimetype = MIME_NONE;
+  mime_current->ctenc = CTENC_NONE;
+  mime_current->cs = CS_NONE;
+}
+
+void new_mime()
+{
+    mime_tmp = mime_current;
+    if (mime_current)
+      mime_current = mime_current->next;
+    if (!mime_current) {
+      if (!(mime_current = (mime_info *) alloc(sizeof (mime_info))))
+       die_nomem();
+      mime_current->charset = sainit;          /* init */
+      mime_current->boundary = sainit;
+      mime_current->ctype = sainit;
+      mime_current->next = (mime_info *) 0;
+      mime_current->previous = mime_tmp;
+    }
+    clear_mime();
+    if (mime_tmp)
+      mime_current->level = mime_tmp->level + 1;
+    else
+      mime_current->level = 1;
+}
+
+void mime_getarg(stralloc *sa,char **s, unsigned int *l)
+/* copies next token or "token" into sa and sets s & l appropriately */
+/* for continuing the search */
+{
+  char *cp, *cpafter, *cpnext;
+
+      if (!*l || !**s) return;
+      if (**s == '"') {
+       (*s)++; (*l)--;
+       cp = *s; cpnext = cp + *l; cpafter = cpnext;
+       while (cp < cpafter) {
+         if (*cp == '"') {
+           break;
+         }
+         cp++;
+       }
+       cpnext = cp;
+      } else {
+       cp = *s; cpnext = cp + *l; cpafter = cpnext;
+       while (cp < cpafter) {
+         if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == ';') {
+           break;
+         }
+         cp++;
+       }
+       cpnext = cp;
+      }
+      if (!stralloc_copyb(sa,*s,cp - *s)) die_nomem();
+      *l = cpafter - cpnext;           /* always >= 0 */
+      *s = cpnext;
+      return;
+}
+
+void decode_mime_type(char *s,unsigned int l,unsigned int flagmime)
+{
+  char *st;
+  unsigned int r,lt;
+  if (!flagmime || !l) {               /* treat non-MIME as plain text */
+    mime_current->mimetype = MIME_TEXT_PLAIN;
+    if (!stralloc_copys(&curcharset,charset)) die_nomem();
+       /* should be us-ascii, but this is very likely better */
+    return;
+  }
+  r = MIME_APPLICATION_OCTETSTREAM;
+  while (l && (*s == ' ' || *s == '\t')) { s++; l--; } /* skip LWSP */
+  mime_getarg(&(mime_current->ctype),&s,&l);
+  st = mime_current->ctype.s;
+  lt = mime_current->ctype.len;
+  if (case_startb(st,lt,"text")) {                     /* text types */
+    r = MIME_TEXT; st+= 4; lt-= 4;
+    if (case_startb(st,lt,"/plain")) {
+      r = MIME_TEXT_PLAIN; st+= 6; lt-= 6;
+    } else if (case_startb(st,lt,"/html")) {
+      r = MIME_TEXT_HTML; st+= 5; lt-= 5;
+    } else if (case_startb(st,lt,"/enriched")) {
+      r = MIME_TEXT_ENRICHED; st+= 9; lt-= 9;
+    } else if (case_startb(st,lt,"/x-vcard")) {
+      r = MIME_TEXT_ENRICHED; st+= 8; lt-= 8;
+    }
+  } else if (case_startb(st,lt,"multipart")) {         /* multipart types */
+    r = MIME_MULTI; st += 9; lt-= 9;
+    if (case_startb(st,lt,"/alternative")) {
+      r = MIME_MULTI_ALTERNATIVE; st+= 12; lt-= 12;
+    } else if (case_startb(st,lt,"/mixed")) {
+      r = MIME_MULTI_MIXED; st+= 6; lt-= 6;
+    } else if (case_startb(st,lt,"/digest")) {
+      r = MIME_MULTI_DIGEST; st+= 7; lt-= 7;
+    } else if (case_startb(st,lt,"/signed")) {
+      r = MIME_MULTI_SIGNED; st+= 7; lt-= 7;
+    }
+  } else if (case_startb(st,lt,"message")) {           /* message types */
+    r = MIME_MESSAGE; st += 7; lt -= 7;
+    if (case_startb(st,lt,"/rfc822")) {
+      r = MIME_MESSAGE_RFC822; st+= 7; lt-= 7;
+    }
+  }
+  mime_current->mimetype = r;
+  while (l) {
+    while (l && (*s == ' ' || *s == '\t' || *s == ';' || *s == '\n')) {
+        s++; l--; }                                    /* skip ;LWSP */
+    if (case_startb(s,l,"boundary=")) {
+      s += 9; l-= 9;
+      mime_getarg(&(mime_current->boundary),&s,&l);
+    } else if (case_startb(s,l,"charset=")) {
+      s += 8; l-= 8;
+      mime_getarg(&(mime_current->charset),&s,&l);
+      cs = decode_charset(mime_current->charset.s,
+               mime_current->charset.len);
+      if (cs == CS_BAD) cs = csbase;                   /* keep base cs */
+      else
+       if (!stralloc_copy(&curcharset,&mime_current->charset)) die_nomem();
+    } else {                                           /* skip non LWSP */
+      for (;;) {
+       if (!l) break;
+       if (*s == '"') {
+         s++, l--;
+         while (l && *s != '"') { s++, l--; }
+         if (l) { s++, l--; }
+         break;
+       } else {
+         if (!l || *s == ' ' || *s == '\t' || *s == '\n') break;
+         s++; l--;
+       }
+      }
+    }
+  }
+  return;
+}
+
+void decode_transfer_encoding(register char *s,register unsigned int l)
+{
+  unsigned int r;
+  mime_current->ctenc = CTENC_NONE;
+  if (!l || (mime_current->mimetype & MIME_MULTI)) return;
+                       /* base64/QP ignored for multipart */
+  r = CTENC_NONE;
+  while (l && (*s == ' ' || *s == '\t')) { s++; l--; } /* skip LWSP */
+  if (case_startb(s,l,"quoted-printable")) {
+    r = CTENC_QP;
+  } else if (case_startb(s,l,"base64")) {
+    r = CTENC_BASE64;
+  }
+  mime_current->ctenc = r;
+  return;
+}
+
+int check_boundary()
+/* return 0 if no boundary, 1 if start, 2 if end */
+{
+  mime_info *tmp;
+
+  if (*line.s != '-' || line.s[1] != '-') return 0;
+  tmp = mime_current;
+  while (tmp) {
+    if (tmp->boundary.len) {
+    if (line.len > tmp->boundary.len + 2 &&
+       !case_diffb(line.s+2,tmp->boundary.len,tmp->boundary.s)) {
+      if (line.s[tmp->boundary.len + 2] == '-' &&
+               line.s[tmp->boundary.len + 3] == '-') { /* end */
+       mime_current = tmp;
+       clear_mime();
+       return 2;
+
+      } else {                                         /* start */
+       mime_current = tmp;
+       new_mime();
+       return 1;
+      }
+    }
+    }
+    tmp = tmp->previous;
+  }
+  if (!stralloc_copys(&curcharset,charset)) die_nomem();
+                       /* suprtfluous since header done by now */
+  cs = csbase;
+  return 0;
+}
+
+void start_message_page(struct msginfo *infop)
+/* header etc for message. Delayed to collect subject so that we can put */
+/* that in TITLE. This in turn needed for good looking robot index.      */
+/* Yep, not pretty, but it works and it's abhorrent to seek()/rewind     */
+/* and another hack: it's hard to mix charsets within a doc. So, we disp */
+/* messages entirely in the charset of the message. This is ok, since    */
+/* headers will be us-ascii or have encoded segments usually matching    */
+/* the charset in the message. Of course, we should be able to used e.g. */
+/* <DIV charset=iso-2022-jp> with internal resources as well as internal */
+/* ones. One might make other-charset messages external resources as well*/
+/* Now, the problem is that we need to "preview" MIME info _before_      */
+/* seeing the start boundary. */
+{
+  if (!stralloc_copyb(&decline,strnum,fmt_ulong(strnum,infop->target)))
+       die_nomem();
+  if (!stralloc_cats(&decline,":")) die_nomem();
+  if (!stralloc_0(&decline)) die_nomem();
+  decodeHDR(hdr[HDR_SUBJECT - 1].s,hdr[HDR_SUBJECT - 1].len,&line,"",FATAL);
+  if (!mime_current)
+    new_mime();                        /* allocate */
+  else
+    clear_mime();
+  decode_mime_type(hdr[HDR_CT - 1].s,hdr[HDR_CT - 1].len,
+       hdr[HDR_VERSION - 1].len);
+  html_header(decline.s,line.s,line.len - 1,
+               "msgbody",SPC_BASE);
+  msglinks(infop);
+  oputs("<DIV class=message>\n");
+}
+
+void show_part(struct msginfo *infop,int flagshowheaders,
+       int flagskip,int flagstartseen)
+/* if flagshowheaders we display headers, otherwise not */
+/* if flagstartseen we've already see the start boundary for this part, */
+/* if not we'll ignore what's there up to it */
+/* if flagskip we skip this part */
+{
+  char *cp;
+  int flaginheader;
+  int whatheader;
+  int flaggoodfield;
+  int flaghtml;
+  int btype,i;
+  unsigned int colpos,l,pos;
+  char linetype;
+
+  flaginheader = 1;
+  for (i = 0; i < NO_HDRS; i++) hdr[i].len = 0;
+  flaggoodfield = 1;
+  match = 1;
+  recursion_level++;                   /* one up */
+  for (;;) {
+    if (!match) return;
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match) return;
+    if ((btype = check_boundary())) {
+      if (flagpre) {
+       oputs("</PRE>");
+       toggle_flagpre(0);
+      }
+      if (mime_current->level < recursion_level) {
+        return;
+      }
+      if (btype == 1) {
+       flagstartseen = 1;
+       flaggoodfield = 1;
+       flaginheader = 1;
+      } else
+       flagstartseen = 0;
+      continue;
+    }
+    if (!flagstartseen) continue;      /* skip to start */
+    if (flaginheader) {
+      if (line.len == 1) {
+       if (flagshowheaders) {          /* rfc822hdr only */
+         if (flagtoplevel)
+           start_message_page(infop);  /* so we can put subj in TITLE */
+         oputs("<DIV class=rfc822hdr><HR>\n");
+         for (i = 0; i < NO_HDRS; i++) {
+           if (!hdr[i].len || !headers_shown[i]) continue;
+           if (i == HDR_SUBJECT - 1 && flagtoplevel)
+             oputs("<SPAN class=subject>");
+           oputs("<EM>");
+           oputs(constmap_get(&headermap,i + 1));
+           oputs(":</EM>");
+           decodeHDR(hdr[i].s,hdr[i].len,&line,"",FATAL);
+           if (i == HDR_SUBJECT - 1 && flagtoplevel) {
+             oputs("<A class=relk href=\"mailto:");
+             oputs(local);
+             oput("@",1);
+             oputs(host);
+             oputs("?subject=");
+             urlencode_put(line.s + 1,line.len - 2);
+             oputs("\">");
+           }
+           if (flagobscure && i == HDR_FROM - 1) {
+             oputs(" ");
+             decodeHDR(cp,author_name(&cp,line.s,line.len),&decline,"",FATAL);
+             htmlencode_put(decline.s,decline.len);
+           } else {
+             decodeHDR(hdr[i].s,hdr[i].len,&decline,"",FATAL);
+              htmlencode_put(decline.s,decline.len - 1);
+           }
+           if (i == HDR_SUBJECT - 1 && flagtoplevel)
+             oputs("</A></SPAN>");
+           oputs("\n<BR>");
+         }
+         oputs("</DIV>\n");
+       }
+        flaginheader = 0;
+       flagtoplevel = 0;
+        flaggoodfield = 1;
+       flaghtml = 0;
+       if (!flagmime)
+         flagmime = hdr[HDR_VERSION - 1].len;  /* MIME-Version header */
+       decode_mime_type(hdr[HDR_CT - 1].s,hdr[HDR_CT - 1].len,flagmime);
+       decode_transfer_encoding(hdr[HDR_CTENC - 1].s,hdr[HDR_CTENC - 1].len);
+       content.len = 0; encoding.len = 0;
+       switch (mime_current->mimetype) {
+         case MIME_MULTI_SIGNED:
+         case MIME_MULTI_MIXED:
+         case MIME_MULTI_ALTERNATIVE:
+         case MIME_MULTI_DIGEST:
+               show_part(infop,0,0,0);
+               recursion_level--;
+               flagstartseen = 0;
+               flaginheader = 1;
+               continue;
+         case MIME_MESSAGE_RFC822:
+               oputs("\n<PRE>");
+               toggle_flagpre(1);
+               flagshowheaders = 1;
+               flaginheader = 1;
+               flagmime = 0;           /* need new MIME-Version header */
+               continue;
+         case MIME_TEXT_HTML:
+               if (flagshowhtml) {
+                 oputs("<HR>\n");
+                 flaghtml = 1;
+               } else {
+                 oputs("<strong>[\"");
+                 oput(mime_current->ctype.s,mime_current->ctype.len);
+                 oputs("\" not shown]</strong>\n");
+                 flaggoodfield = 0;    /* hide */
+               }
+               continue;
+         case MIME_TEXT_PLAIN:
+         case MIME_TEXT:               /* in honor of Phil using "text" on */
+         case MIME_NONE:               /* the qmail list and rfc2045:5.2 */
+               oputs("<HR>\n<PRE>\n");
+               toggle_flagpre(1);
+               continue;
+         case MIME_TEXT_VCARD:
+         default:              /* application/octetstream...*/
+               oputs("<HR><strong>[\"");
+               oput(mime_current->ctype.s,mime_current->ctype.len);
+               oputs("\" not shown]</strong>\n");
+               flaggoodfield = 0;      /* hide */
+               continue;
+       }
+      } else if (line.s[0] != ' ' && line.s[0] != '\t') {
+       linetype = ' ';
+        flaggoodfield = 0;
+       colpos = byte_chr(line.s,line.len,':');
+       if ((whatheader = constmap_index(&headermap,line.s,colpos))) {
+          flaggoodfield = 1;
+         if (!stralloc_copyb(&hdr[whatheader - 1],line.s + colpos + 1,
+               line.len - colpos - 1)) die_nomem();
+       }
+      } else {
+       if (whatheader)
+         if (!stralloc_catb(&hdr[whatheader - 1],line.s,line.len))
+               die_nomem();
+      }
+    } else {
+      if (flaggoodfield) {
+       if (mime_current->ctenc) {
+         if (!stralloc_copy(&decline,&line)) die_nomem();
+         line.len = 0;
+         if (mime_current->ctenc == CTENC_QP)
+           decodeQ(decline.s,decline.len,&line);
+         else
+           decodeB(decline.s,decline.len,&line);
+       }
+       if (flaghtml)
+         oput(line.s,line.len);
+       else {
+          htmlencode_put(line.s,line.len);             /* body */
+       }
+      }
+    }
+  }
+}
+
+int show_message(struct msginfo *infop)
+{
+  char *psz;
+
+  if(!stralloc_copys(&headers,(char *) headers_used)) die_nomem();
+  if (!stralloc_0(&headers)) die_nomem();
+  psz = headers.s;
+  while (*psz) {
+    if (*psz == '\\') *psz = '\0';
+    ++psz;
+  }
+  if (!constmap_init(&headermap,headers.s,headers.len,0))
+       die_nomem();
+
+  (void) makefn(&fn,ITEM_MESSAGE,msginfo.target,"");
+  if ((fd = open_read(fn.s)) == -1)
+    if (errno == error_noent)
+      return 0;
+    else
+      strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+  toggle_flagpre(0);
+  recursion_level = 0; /* recursion level for show_part */
+  flagmime = 0;                /* no active mime */
+  flagtoplevel = 1;    /* top message/rfc822 get special rx */
+  new_mime();          /* initiate a MIME info storage slot */
+
+  show_part(infop,1,0,1);      /* do real work, including html header etc */
+  if (flagpre)
+    oputs("</PRE>\n");
+  close(fd);
+  oputs("<HR></DIV>\n");
+  msglinks(infop);
+  html_footer(0);
+  return 1;
+}
+
+char decode_item(char ch)
+{
+  switch (ch) {
+       case 'm': return ITEM_MESSAGE;
+       case 'a': return ITEM_AUTHOR ;
+       case 's': return ITEM_SUBJECT;
+       case 'd': return ITEM_DATE   ;
+       case 'i': return ITEM_INDEX  ;
+       default: cgierr("Navigation command contains ",
+               "illegal item code","");
+  }
+  return 0;    /* never reached */
+}
+
+char decode_direction(char ch)
+{
+  switch (ch) {
+       case 's': return DIRECT_SAME;
+       case 'n': return DIRECT_NEXT;
+       case 'p': return DIRECT_PREV;
+       default: cgierr("Navigation command contains ",
+               "illegal direction code","");
+  }
+  return 0;    /* never reached */
+}
+
+int decode_cmd(char *s,struct msginfo *infop)
+/* decodes s into infop. Assures that no security problems slip through by */
+/* checking everything */
+/* commands xyd:123[:abc]. x what we want, y is the axis, d the direction. */
+/* 123 is the current message number. abc is a date/subject/author hash,   */
+/* depending on axis, or empty if not available. */
+/* returns: 0 no command+msgnum. */
+/*          1 empty or at least cmd + msgnum. */
+/*            Guarantee: Only legal values accepted */
+{
+  register char ch;
+
+  infop->source = 0L;
+  infop->date = 0L;
+  infop->author = (char *)0;
+  infop->subject = (char *)0;
+  infop->cgiarg = (char *)0;
+
+  if (!s || !*s) {     /* main index */
+    infop->item = ITEM_DATE;
+    infop->axis = ITEM_DATE;
+    infop->direction = DIRECT_SAME;
+    latestdate(&msginfo,0);
+    infop->target = MAXULONG;
+    return 1;
+  }
+  ch = *(s++);
+  if (ch >= '0' && ch <= '9') {        /* numeric - simplified cmd: msgnum ... */
+    s--;
+    infop->item = ITEM_MESSAGE;
+    infop->axis = ITEM_MESSAGE;
+    infop->direction = DIRECT_SAME;
+  } else {                     /* what:axis:direction:msgnum ... */
+    infop->item = decode_item(ch);
+    ch = *(s++);
+    infop->axis = decode_item(ch);
+    ch = *(s++);
+    infop->direction = decode_direction(ch);
+    if (*(s++) != ':') return 0;
+  }
+  s+= scan_ulong(s,&(infop->source));
+  if (*(s++) != ':') return 0;
+  if (*s >= '0' && *s <= '9') {        /* numeric nav hint [date] */
+    s+= scan_ulong(s,&(infop->date));
+    if (!*s++) return 1;       /* skip any char - should be ':' unless NUL */
+  }
+  if (checkhash(s)) {          /* Ignore if illegal rather than complaining*/
+    if (!stralloc_copyb(&charg,s,HASHLEN)) die_nomem();
+    if (!stralloc_0(&charg)) die_nomem();
+    infop->cgiarg = charg.s;
+  }
+  return 1;
+}
+
+int msg2hash(struct msginfo *infop)
+{
+  unsigned int pos;
+  unsigned long tmpmsg;
+
+  if (!infop->source) die_prog("source is 0 in msg2hash");
+  (void) makefn(&fn,ITEM_INDEX,infop->source,"");
+  if ((fd = open_read(fn.s)) == -1) {
+    if (errno == error_noent)
+      return 0;
+    else
+      strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+  }
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+  for (;;) {
+        if (getln(&ssin,&line,&match,'\n') == -1)
+          strerr_die3sys(111,FATAL,ERR_READ,"index: ");
+        if (!match)
+         return 0;                             /* didn't find message */
+       if (*line.s == '\t') continue;          /* author line */
+        pos = scan_ulong(line.s,&tmpmsg);
+       if (tmpmsg == infop->source) {
+          if (line.s[pos++] != ':' || line.s[pos++] != ' ')
+           strerr_die3x(100,ERR_SYNTAX,fn.s,": missing subject separator");
+         if (line.len < HASHLEN + pos)
+           strerr_die3x(100,ERR_SYNTAX,fn.s,": missing subject hash");
+         if (!stralloc_copyb(&subject,line.s+pos,HASHLEN)) die_nomem();
+         if (!stralloc_0(&subject)) die_nomem();
+         infop->subject = subject.s;
+          if (getln(&ssin,&line,&match,'\n') == -1)
+            strerr_die3sys(111,FATAL,ERR_READ,"index: ");
+          if (!match)
+           strerr_die3x(100,ERR_SYNTAX,fn.s,
+               ": author info missing. Truncated?");
+         pos = byte_chr(line.s,line.len,';');
+         if (pos == line.len)
+           strerr_die3x(100,ERR_SYNTAX,fn.s,"missing ';' after date");
+         if (pos > 1)
+           infop->date = date2yyyymm(line.s+1);        /* ';' marks end ok */
+         pos++;
+         if (line.len < HASHLEN + pos)
+           strerr_die3x(100,ERR_SYNTAX,fn.s,": missing author hash");
+         if (!stralloc_copyb(&author,line.s+pos,HASHLEN)) die_nomem();
+         if (!stralloc_0(&author)) die_nomem();
+         infop->author = author.s;
+         close(fd);
+         return 1;     /* success */
+        }
+  }
+  close(fd);
+  return 0;            /* failed to match */
+}
+
+void setmsg(struct msginfo *infop)
+/* Reads the file corresponding to infop->axis and assumes fn.s is set */
+/* correctly for this. Sets up a msgnav structure and links it in      */
+/* correction for axis=author/subject. For axis=date it supports also  */
+/* direction=DIRECT_FIRST which will return the first message of the   */
+/* first thread in the date file. DIRECT_LAST is not supported.        */
+/* DIRECT_FIRST is supported ONLY for date. */
+{
+  if (infop->direction == DIRECT_SAME) {
+    infop->target = infop->source;
+    return;
+  }
+  if ((fd = open_read(fn.s)) == -1) {
+    if (errno == error_noent)
+      strerr_die4x(100,FATAL,ERR_OPEN,fn.s,
+       " in listmsgs. Rerun ezmlm-archive!");
+    else
+      strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+  }
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+  if (infop->source != ITEM_DATE) {
+    if (getln(&ssin,&line,&match,'\n') == -1)  /* first line */
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match)
+      strerr_die3x(100,ERR_SYNTAX,fn.s,": first line missing");
+  }
+  msgnav[3] = 0L;              /* next */
+  msgnav[4] = 0L;              /* after */
+  infop->target = 0L;
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match) break;
+    msgnav[0] = msgnav[1];
+    msgnav[1] = msgnav[2];
+    (void) scan_ulong(line.s,&(msgnav[2]));
+    if (infop->direction == DIRECT_FIRST) break;
+    if (msgnav[2] == infop->source) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+      if (!match) break;
+      (void) scan_ulong(line.s,&(msgnav[3]));
+      if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+      if (!match) break;
+      (void) scan_ulong(line.s,&(msgnav[4]));
+      break;
+    }
+  }
+  close(fd);
+  switch (infop->axis) {
+    case ITEM_AUTHOR:
+      infop->authnav = msgnav + 2 + infop->direction;
+      infop->target = *(infop->authnav);
+      infop->subject = (char *)0;      /* what we know is not for this msg */
+      infop->date = 0;
+      break;
+    case ITEM_SUBJECT:
+      infop->subjnav = msgnav + 2 + infop->direction;
+      infop->target = *(infop->subjnav);
+      infop->author = (char *)0;       /* what we know is not for this msg */
+      infop->date = 0;
+      break;
+    case ITEM_DATE:
+      infop->target = msgnav[2];
+      infop->subject = (char *)0;      /* what we know is not for this msg */
+      infop->author = (char *)0;       /* what we know is not for this msg */
+    default:
+      die_prog("Bad item in setmsg");
+  }
+  return;
+}
+
+void auth2msg(struct msginfo *infop)
+{
+  if (!infop->author) die_prog("no such author in authmsg");
+  if (!makefn(&fn,ITEM_AUTHOR,0L,infop->author)) die_prog("auth2msg");
+  setmsg(infop);
+}
+
+void subj2msg(struct msginfo *infop)
+{
+  if (!infop->subject) die_prog("no such subject in subj2msg");
+  if (!makefn(&fn,ITEM_SUBJECT,0L,infop->subject)) die_prog("subj2msg");
+  setmsg(infop);
+}
+
+void date2msg(struct msginfo *infop)
+{
+  (void) makefn(&fn,ITEM_DATE,infop->date,"");
+  setmsg(infop);
+}
+
+void findlastmsg(struct msginfo *infop)
+{
+  if (!getconf_line(&line,"num",dir,0,FATAL))
+    cgierr("Sorry, there are no messages in the archive","","");
+  if (!stralloc_0(&line)) die_nomem();
+  (void) scan_ulong(line.s,&(infop->target));
+}
+
+int do_cmd(struct msginfo *infop)
+/* interprets msginfo to create msginfo. Upon return, msginfo can be trusted */
+/* to have all info needed, and that all info is correct. There may be more */
+/* info than needed. This can be used to build more specific links. NOTE:   */
+/* there is no guarantee that a message meeting the criteria actually exists*/
+{
+  infop->target = infop->source;
+
+  switch (infop->item) {
+       case ITEM_MESSAGE:      /* we want to get a message back */
+         {
+           switch (infop->axis) {
+             case ITEM_MESSAGE:
+               if (infop->direction == DIRECT_SAME)
+                 break;
+               else if (infop->direction == DIRECT_NEXT)
+                 (infop->target)++;
+               else {          /* previous */
+                 cache = 2;
+                 if (infop->target >= 2)
+                   (infop->target)--;
+                 else
+                   infop->target = 1;
+               }
+               break;
+             case ITEM_AUTHOR:
+                 infop->author = infop->cgiarg;
+               if (!infop->author)      /* we don't know author hash */
+                 if (!msg2hash(infop)) return 0;
+               auth2msg(infop);
+               break;
+             case ITEM_SUBJECT:
+                 infop->subject = infop->cgiarg;
+               if (!infop->subject)     /* we don't know Subject hash */
+                 if (!msg2hash(infop)) return 0;
+               subj2msg(infop);
+               break;
+           }
+           break;
+         }
+       case ITEM_AUTHOR:
+         switch (infop->axis) {
+           case ITEM_MESSAGE:
+             if (!infop->author)
+               if (!msg2hash(infop)) return 0;
+             break;
+           case ITEM_AUTHOR:
+             infop->author = infop->cgiarg;
+             if (!infop->author)
+               if (!msg2hash(infop)) return 0;
+               auth2msg(infop);
+             break;
+           case ITEM_SUBJECT:
+             infop->subject = infop->cgiarg;
+             if (!infop->subject)       /* we don't know Subject hash */
+               if (!msg2hash(infop)) return 0;
+             subj2msg(infop);
+             break;
+           }
+           break;
+       case ITEM_SUBJECT:
+         switch (infop->axis) {
+           case ITEM_MESSAGE:
+             if (!msg2hash(infop)) return 0;
+             break;
+           case ITEM_AUTHOR:
+             infop->author = infop->cgiarg;
+             if (!infop->author)
+               if (!msg2hash(infop)) return 0;
+             auth2msg(infop);
+             break;
+           case ITEM_SUBJECT:
+             infop->subject = infop->cgiarg;
+             if (!infop->subject)       /* we don't know Subject hash */
+               if (!msg2hash(infop)) return 0;
+             subj2msg(infop);
+             break;
+           }
+           break;
+         case ITEM_DATE:       /* want a date reference */
+           switch (infop->axis) {
+             case ITEM_MESSAGE:
+             case ITEM_AUTHOR:
+             case ITEM_SUBJECT:
+             case ITEM_DATE:
+               if (!infop->date && infop->source)
+                 if (!msg2hash(infop)) return 0;
+                 getdate(infop,0);
+               break;
+           }
+           break;
+         case ITEM_INDEX:      /* ignore direction etc - only for index */
+           if (!infop->target)
+             infop->target = infop->source;
+           if (!infop->target)
+             findlastmsg(infop);
+           break;
+  }
+  return 1;
+}
+
+void list_lists()
+{
+  unsigned long lno;
+  cache = 2;
+  flagrobot = 2;
+  html_header("Robot index of lists",0,0,0,0);
+  for (;;) {
+    if (getln(&ssin,&cfline,&match,'\n') == -1)                /* read line */
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match)
+      break;
+    if (cfline.s[0] == '#') continue;                  /* skip comment */
+    cfline.s[cfline.len - 1] = '\0';                   /* so all are sz */
+    (void) scan_ulong(cfline.s,&lno);                  /* listno for line */
+    if (lno) {                         /* don't expose default list */
+      oputs("<A href=\"");
+      oput(strnum,fmt_ulong(strnum,lno));
+      oputs("/index\">[link]</a>\n");
+   }
+  }
+  html_footer(0);
+}
+
+void list_list(unsigned long listno)
+/* Make one link [for list_set()] per set of 100 archive messages. */
+/* Assumption: Any directory DIR/archive/xxx where 'xxx' is a numeric,*/
+/* is part of the list archive and has in it an index file and one    */
+/* or more messages. */
+{
+  DIR *archivedir;
+  direntry *d;
+  unsigned long msgset;
+
+  flagrobot = 2;
+  strnum[fmt_ulong(strnum,listno)] = '\0';
+  archivedir = opendir("archive/");
+  if (!archivedir)
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/archive: ");
+    else
+      strerr_die4sys(100,FATAL,ERR_OPEN,dir,"/archive: ");
+
+  cache = 1;
+  html_header("Robot index for message sets in list",0,0,0,0);
+
+  while ((d = readdir(archivedir))) {
+    if (d->d_name[scan_ulong(d->d_name,&msgset)])
+       continue;               /* not numeric */
+    oputs("<a href=\"../");    /* from /ezcgi/0/index to /ezcgi/listno/index*/
+    oputs(strnum);
+    oputs("/index/");
+    oputs(d->d_name);
+    oputs("\">[link]</a>\n");
+  }
+  closedir(archivedir);
+  html_footer(0);
+}
+
+void list_set(unsigned long listno,unsigned long msgset)
+{
+  unsigned int msgfirst,msgmax;
+  unsigned long lastset;
+
+  flagrobot = 2;
+  findlastmsg(&msginfo);
+  if (!stralloc_copys(&line,"<a href=\"../")) die_nomem();
+  if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgset))) die_nomem();
+  lastset = msginfo.target / 100;
+  cache = 2;
+  msgfirst = 0;
+  if (!msgset)
+    msgfirst = 1;
+  msgmax = 99;
+  if (msgset > lastset) {              /* assure empty list */
+    msgmax = 0;
+    msgfirst = 1;
+  } else if (msgset == lastset) {
+    cache = 0;                         /* still changing */
+    msgmax = msginfo.target % 100;
+  }
+  html_header("Robot index for messages in set",0,0,0,0);
+  while (msgfirst <= msgmax) {
+    oput(line.s,line.len);
+    oput(strnum,fmt_uint0(strnum,msgfirst,2));
+    oputs("\">[link]</a>\n");
+    msgfirst++;
+  }
+  html_footer(0);
+}
+
+/**************** MAY BE SUID ROOT HERE ****************************/
+void drop_priv(int flagchroot)
+{
+  if (!uid) strerr_die2x(100,FATAL,ERR_SUID);          /* not as root */
+  if (!euid) {
+    if (flagchroot)
+      if (chroot(dir) == -1)                           /* chroot listdir */
+        strerr_die4sys(111,FATAL,"failed to chroot ",dir,": ");
+    if (setuid(uid) == -1)                             /* setuid */
+      strerr_die2sys(111,FATAL,ERR_SETUID);
+  }
+  euid = (unsigned long) geteuid();
+  if (!euid) strerr_die2x(100,FATAL,ERR_SUID);         /* setuid didn't do it*/
+}
+/*******************************************************************/
+
+int main(argc,argv)
+int argc;
+char **argv;
+{
+  char *cp,*cppath;
+  unsigned long listno,thislistno,tmpuid,msgset;
+  unsigned long msgnum = 0;
+  unsigned long port = 0L;
+  unsigned long tmptarget;
+  unsigned int pos,l;
+  int flagindex = 0;
+  int flagchroot = 1;          /* chroot listdir if SUID root */
+  int ret;
+  char sep;
+
+/******************** we may be SUID ROOT ******************************/
+  uid = (unsigned long) getuid();                      /* should be http */
+  euid = (unsigned long) geteuid();                    /* chroot only if 0 */
+
+  if (!euid) {
+    if ((fd = open_read(EZ_CGIRC)) == -1)              /* open config */
+      strerr_die4sys(111,FATAL,ERR_OPEN,EZ_CGIRC,": ");
+  } else {
+    if ((fd = open_read(EZ_CGIRC_LOC)) == -1)          /* open local config */
+      strerr_die4sys(111,FATAL,ERR_OPEN,EZ_CGIRC_LOC,": ");
+  }
+
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));   /* set up buffer */
+       /* ##### tainted info #####*/
+
+  cmd = env_get("QUERY_STRING");                       /* get command */
+  cppath = env_get("PATH_INFO");                       /* get path_info */
+
+  if (!cppath || !*cppath) {
+    if (cmd && *cmd) {
+      cmd += scan_ulong(cmd,&thislistno);
+      if (*cmd == ':') cmd++;                          /* allow ':' after ln*/
+    } else
+      thislistno = 0L;
+  } else {
+    cppath++;
+      cppath += scan_ulong(cppath,&thislistno);                /* this listno */
+      if (!thislistno || *cppath++ == '/') {
+       if (str_start(cppath,"index")) {
+          cppath += 5;
+         flagindex = 1;
+         if (!thislistno) {                            /* list index */
+           drop_priv(0);       /* <---- dropping privs */
+           list_lists();
+           close(fd);
+           _exit(0);
+         }
+       }
+      }                                                        /* rest done per list */
+    }
+
+  for (;;) {
+    if (getln(&ssin,&cfline,&match,'\n') == -1)                /* read line */
+      strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+    if (!match)
+      break;
+    if (*cfline.s == '#' || cfline.len == 1) continue; /* skip comment/blank */
+    cfline.s[cfline.len - 1] = '\0';                   /* so all are sz */
+    pos = scan_ulong(cfline.s,&listno);                        /* listno for line */
+    if (thislistno != listno) continue;
+    sep = cfline.s[pos++];
+    if (cfline.s[pos] == '-') {                                /* no chroot if -uid*/
+      flagchroot = 0;
+      pos++;
+    }
+    pos += scan_ulong(cfline.s+pos,&tmpuid);           /* listno for line */
+    if (tmpuid) uid = tmpuid;                          /* override default */
+    if (!cfline.s[pos++] == sep)
+      die_syntax("missing separator after user id");
+    if (cfline.s[pos] != '/')
+       die_syntax("dir");                              /* absolute path */
+    l = byte_chr(cfline.s + pos, cfline.len - pos,sep);
+    if (l == cfline.len - pos)                         /* listno:path:...*/
+      die_syntax("missing separator after path");
+    dir = cfline.s + pos;
+    pos += l;
+    cfline.s[pos++] = '\0';                            /* .../dir\0 */
+    break;     /* do rest after dropping priv */
+  }
+  close(fd);                                           /* don't accept uid 0*/
+  if (!dir) {
+    drop_priv(0);      /* don't trust cgierr. No dir, no chroot */
+    cgierr("list ",ERR_NOEXIST,"");
+  }
+  if (chdir(dir) == -1)                                        /* chdir listdir */
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+  drop_priv(flagchroot);
+
+/******************************* RELAX **********************************/
+
+/********************* continue to process config line ******************/
+
+  flagrobot = 0;
+  if (cfline.s[pos] == '-') {
+    flagobscure = 1;
+    pos++;
+  }
+  local = cfline.s + pos;
+  l = byte_chr(cfline.s + pos, cfline.len - pos,sep);  /* ... home */
+  if (l < cfline.len - pos) {                          /* optional */
+    pos += l;
+    cfline.s[pos++] = '\0';
+    home = cfline.s + pos;
+    l = byte_chr(cfline.s + pos, cfline.len - pos,sep);        /* ... charset */
+    if (l < cfline.len - pos) {                                /* optional */
+      pos += l;
+      cfline.s[pos++] = '\0';
+      charset = cfline.s + pos;
+      l = byte_chr(cfline.s+pos,cfline.len - pos,sep); /* ... stylesheet */
+      if (l < cfline.len - pos) {                      /* optional */
+        pos += l;
+        cfline.s[pos++] = '\0';
+        stylesheet = cfline.s + pos;
+        l = byte_chr(cfline.s+pos,cfline.len-pos,sep); /* ... bannerURL */
+       if (l < cfline.len - pos) {                     /* optional */
+         pos += l;
+         cfline.s[pos++] = '\0';
+         banner = cfline.s + pos;
+       }
+      }
+    }
+  }
+  if (!charset || !*charset)                           /* rfc822 default */
+    charset = EZ_CHARSET;
+  if (!stralloc_copys(&curcharset,charset)) die_nomem();
+  csbase = decode_charset(curcharset.s,curcharset.len);
+  if (csbase == CS_BAD) csbase = CS_NONE;
+  cs = csbase;
+  pos = + str_rchr(local,'@');
+  if (!local[pos])
+    die_syntax("listaddress lacks '@'");               /* require host */
+  local[pos++] = '\0';
+  host = local + pos;
+
+/********************* Accomodate robots and PATH_INFO ****************/
+
+  if (flagindex) {
+    if (*(cppath++) == '/') {          /* /2/index/123 */
+      cppath += scan_ulong(cppath,&msgset);
+      list_set(thislistno,msgset);
+    } else                             /* /2/index */
+      list_list(thislistno);
+    _exit(0);
+  }
+
+  if (cppath && *cppath) {             /* /2/msgnum */
+    flagrobot = 1;                     /* allow index, but "nofollow" */
+    scan_ulong(cppath,&msgnum);
+  }                                    /* dealt with normally */
+
+/********************* Get info from server on BASE etc ****************/
+
+  if (!stralloc_copys(&base,"<BASE href=\"http://")) die_nomem();
+  cp = env_get("SERVER_PORT");
+  if (cp) {                    /* port */
+    (void) scan_ulong(cp,&port);
+    if ((unsigned int) port == 443) {          /* https: */
+      if (!stralloc_copys(&base,"<BASE href=\"https://")) die_nomem();
+    }
+  }
+  if (port && (unsigned int) port != 80 && (unsigned int) port != 443) {
+      if (!stralloc_cats(&base,":")) die_nomem();
+      if (!stralloc_catb(&base,strnum,fmt_ulong(strnum,port))) die_nomem();
+  }
+  if (!(cp = env_get("HTTP_HOST")))
+    if (!(cp = env_get("SERVER_NAME")))
+      strerr_die2x(100,FATAL,"both HTTP_HOST and SERVER_NAME are empty");
+  if (!stralloc_cats(&base,cp)) die_nomem();
+  if (!(cp = env_get("SCRIPT_NAME")))
+    strerr_die2x(100,FATAL,"empty SCRIPT_NAME");
+  if (!stralloc_cats(&base,cp)) die_nomem();
+  if (!stralloc_cats(&base,"\">\n")) die_nomem();
+  if (!stralloc_copys(&url,"<A HREF=\"")) die_nomem();
+  pos = str_rchr(cp,'/');
+  if (cp[pos])
+    if (!stralloc_cats(&url,cp + pos + 1)) die_nomem();
+  if (!stralloc_cats(&url,"?")) die_nomem();
+  if (thislistno) {
+    if (!stralloc_catb(&url,strnum,fmt_ulong(strnum,thislistno))) die_nomem();
+    if (!stralloc_cats(&url,":")) die_nomem();
+  }
+
+  cache = 1;                           /* don't know if we want to cache */
+
+/****************************** Get command ****************************/
+
+  if (msgnum) {                                /* to support /listno/msgno */
+   msginfo.target = msgnum;
+   msginfo.item = ITEM_MESSAGE;
+   cache = 2;
+  } else {
+      (void) decode_cmd(cmd,&msginfo);
+      if (!do_cmd(&msginfo))
+       cgierr("I'm sorry, Dave ... I can't do that, Dave ...","","");
+  }
+
+  switch (msginfo.item) {
+    case ITEM_MESSAGE:
+       if (!show_message(&msginfo)) {          /* assume next exists ... */
+         cache = 0;                            /* border cond. - no cache */
+         msginfo.target = msginfo.source;      /* show same */
+         msginfo.subjnav = 0;
+         msginfo.authnav = 0;
+         ret = show_message(&msginfo);
+       }
+       break;
+    case ITEM_AUTHOR:
+       if (!show_object(&msginfo,ITEM_AUTHOR))
+         cgierr ("I couldn't find the author for that message","","");
+       break;
+    case ITEM_SUBJECT:
+       if (!show_object(&msginfo,ITEM_SUBJECT))
+         cgierr ("I couldn't find the subject for that message","","");
+       break;
+    case ITEM_DATE:
+       if (!show_object(&msginfo,ITEM_DATE)) {
+         finddate(&msginfo);
+         ret = show_object(&msginfo,ITEM_DATE);
+       }
+       break;
+    case ITEM_INDEX:
+       if (!show_index(&msginfo)) {
+         tmptarget = msginfo.target;
+         findlastmsg(&msginfo);
+         cache = 0;                    /* latest one - no cache */
+         if (!msginfo.target || msginfo.target > tmptarget) {
+           cache = 2;                  /* won't change */
+            firstdate(&msginfo,1);     /* first thread index */
+           msginfo.direction = DIRECT_FIRST;
+           date2msg(&msginfo);         /* (may not be 1 if parts removed) */
+         }
+         ret = show_index(&msginfo);
+       }
+       break;
+    default:
+       strerr_die2x(100,FATAL,"bad item in main");
+  }
+  if (!ret) {
+    findlastmsg(&msginfo);             /* as last resort; last msgindex */
+    cache = 0;
+    ret = show_message(&msginfo);
+  }
+
+ _exit(0);
+}
diff --git a/ezmlm-check.1 b/ezmlm-check.1
new file mode 100644 (file)
index 0000000..e2e07d7
--- /dev/null
@@ -0,0 +1,52 @@
+.TH ezmlm-check 1
+.SH NAME
+ezmlm-check \- Mails back results of ezmlm list check
+.SH SYNOPSIS
+.B ezmlm-check
+[
+.B \-sS
+]
+.I [dir]
+.SH DESCRIPTION
+.B ezmlm-check
+mails useful environment variables to SENDER via
+.BR qmail-inject .
+It is usually invoked for debugging purposes in a
+.I .qmail
+file to determine the value of relevant environment variables.
+If
+.I dir
+is specified,
+.B ezmlm-check
+attempts to evaluate the
+.I ezmlm
+mailing list set up in
+.I dir
+and return information about the setup as well as possible
+errors.
+
+.B ezmlm-check
+will flag errors with
+.BR ``???\ ERROR'' ,
+likely errors with
+.BR ``???'' ,
+and potential errors/problems with
+.BR ``!!!'' .
+
+If
+.B ezmlm-check
+is invoked from the shell, it will print to stdout.
+In this case, diagnostics are more limited,
+since important environment variables
+normally supplied by qmail are not available.
+.SH OPTIONS
+.TP 5
+.B \-s
+(Default.) List subscribers.
+.TP 5
+.B \-S
+Do not list subscribers.
+.SH "SEE ALSO"
+ezmlm(5),
+qmail(7),
+qmail-inject(8)
diff --git a/ezmlm-check.sh b/ezmlm-check.sh
new file mode 100644 (file)
index 0000000..b844d1b
--- /dev/null
@@ -0,0 +1,670 @@
+
+# [should have a bin/sh line and EZPATH/QMPATH added above by make]
+#
+# script to diagnose ezmlm lists
+# call as is for environment only, or as ezmlm-check 'DIR' for more info
+
+listsubscribers=yes
+if [ "$1" = '-S' ] ; then
+  listsubscribers=no
+  shift
+else
+    if [ "$1" = '-s' ] ; then
+      shift
+    fi
+fi
+
+QINJECT="${QMPATH}/bin/qmail-inject"
+EZLIST="${EZPATH}/ezmlm-list"
+MYNAME='ezmlm-check'
+MYDTLINE="Delivered-To: ${MYNAME}"
+FATAL="${MYNAME}: fatal:"
+EZERR="??? ERROR:"
+EZWARN="!!! Warning:"
+# This should be a ``grep'' that does regexps. Needs to recognize ^ and $.
+GREP='grep'
+CAT='cat'
+CUT='cut'
+ECHO='echo'
+# Needed to isolate some lines in the DIR/editor ... files
+HEAD='head'
+TAIL='tail'
+# needed to count characters in $USER
+WC='wc'
+# should mark executables with '*' and list one file per line
+LS='ls -F1'
+# should list links as: "... symlink -> file"
+LLS='ls -l'
+if [ ! -x "$QINJECT" ]; then
+  ${ECHO} "$FATAL edit script to for path to qmail-inject"
+  exit 100
+fi
+
+if [ -z "$SENDER" ] ; then
+       RCP="${CAT}"
+else
+       RCP="$QINJECT"
+       if ${CAT} - | ${GREP} "${MYDTLINE}" >/dev/null 2>&1
+         then
+           ${ECHO} "FATAL this message is looping: it already has my Delivered-To line (#5.4.6)"
+           exit 100
+       fi
+fi
+
+if [ ! -z "$1" -a ! -d "$1" ]; then
+  ${ECHO} "$FATAL $1 is not a directory"; exit 100
+fi
+
+# reset variables
+GET=''; DIGEST=''; FLAGARCH=''; FLAGIND=''; INLOCAL=''; INLOCALOK=''; INHOST=''
+MANAGE=''; OUTLOCAL=''; OUTHOST=''; SPEC=''; SRESTRICT=''; STORE=''; REMOTE=''
+MODSUB=''; MODPOST=''; SPEC=''
+
+(
+       ${ECHO} "Delivered-To: ezmlm-check"
+       ${ECHO} "To: $SENDER"
+       ${ECHO} "Subject: ${MYNAME} results"
+       ${ECHO}
+       ${ECHO} "Important environment variables:"
+       ${ECHO}
+       ${ECHO} "SENDER ='$SENDER'"
+       ${ECHO} "NEWSENDER ='$NEWSENDER'"
+       ${ECHO} "RECIPIENT ='$RECIPIENT'"
+       ${ECHO} "USER ='$USER'"
+       ${ECHO} "DEFAULT ='$DEFAULT'"
+       ${ECHO} "LOCAL ='$LOCAL'"
+       ${ECHO} "HOST ='$HOST'"
+       ${ECHO} "---------------------------------------------------"
+       if [ ! -z "$1" ]; then
+         ${ECHO} "Checking basic list setup:"
+         ${ECHO}
+         if [ ! -r "$1/mailinglist" ]; then
+           ${ECHO} "$EZERR $1/mailinglist does not exist"
+         fi
+         if [ -e "$1/num" ]; then
+           if [ ! -w "$1/num" ]; then
+             ${ECHO} "$EZERR $1/num is not writable to $USER"
+           else
+             NUM=`${CAT} "$1/num" | ${HEAD} -1`
+             ${ECHO} "... latest message was message # $NUM"
+            fi
+         else
+           ${ECHO} "... no num. This must be a new list ..."
+         fi
+         ${ECHO}
+         if [ -e "$1/lock" -a ! -w "$1/lock" ]; then
+           ${ECHO} "$EZERR User $USER does not have write premission to $1/lock"
+         fi
+         if [ -e "$1/lockbounce" -a ! -w "$1/lockbounce" ]; then
+           ${ECHO} "$EZERR User $USER does not have write premission to $1/lockbounce"
+         fi
+         if [ ! -r "$1/inlocal" ]; then
+           ${ECHO} "$EZERR $1/inlocal does not exist"
+         elif [ ! -r "$1/inhost" ]; then
+             ${ECHO} "$EZERR $1/inhost does not exist"
+         else
+           INLOCAL=`${CAT} "$1/inlocal"| ${HEAD} -1`
+            INHOST=`${CAT} "$1/inhost"| ${HEAD} -1`
+           if [ -z "$HOST" ]; then
+             ${ECHO} "$EZERR HOST is empty. Likely running from the command"
+             ${ECHO} "    line. Run from $1/editor to check if HOST matches"
+             ${ECHO} "    $1/inhost and LOCAL matches $1/inlocal."
+             ${ECHO} "    Mismatches here are the most common setup error."
+             ${ECHO}
+           else
+             if [ "$HOST" != "$INHOST" ]; then
+               ${ECHO} "$EZERR HOST does not match $1/inhost"
+             else
+               ${ECHO} "... $1/inhost OK"
+             fi
+             if ${ECHO} "$LOCAL" | ${GREP} -G "^$INLOCAL" >/dev/null 2>&1
+              then
+               ${ECHO} "... $1/inlocal OK"
+             else
+               ${ECHO} \
+                "$EZERR LOCAL does not begin with contents of $1/inlocal"
+             fi
+           fi
+         fi
+         if [ ! -r "$1/outlocal" ]; then
+           ${ECHO} "$EZERR $1/outlocal does not exist"
+         else
+           OUTLOCAL=`${CAT} "$1/outlocal"| ${HEAD} -1`
+         fi
+         if [ ! -r "$1/outhost" ]; then
+             ${ECHO} "$EZERR $1/outhost does not exist"
+          else
+             OUTHOST=`${CAT} "$1/outhost"| ${HEAD} -1`
+         fi
+         ${ECHO} "... The list is named ${OUTLOCAL}@${OUTHOST}"
+         if [ "$OUTHOST" != "$INHOST" ]; then
+           HOSTMATCH='1';
+           ${ECHO}
+           ${ECHO} "??? $1/inhost and $1/outhost do not"
+           ${ECHO} "    match. This is very unusual ..."
+         fi
+         if ${ECHO} "$INLOCAL" | ${GREP} "^${USER}" >/dev/null ; then
+           USERSTART='1'; INLOCALOK='1'
+         fi
+         if [ -z "$USERSTART" ]; then
+           ${ECHO}
+           ${ECHO} "??? $1/inlocal does not start with the user name."
+           ${ECHO} "    This is an error, unless $INLOCAL starts with"
+           ${ECHO} "    an alias of \"$USER\"."
+         fi
+         if [ "$INLOCAL" = "$OUTLOCAL" ]; then
+           ${ECHO} "... $1/inlocal matches $1/outlocal"
+           ${ECHO} "    suggesting that this is a regular user-owned list."
+           CHARS=`${ECHO} " $USER" | ${WC} -c`
+           LIST=`${ECHO} "$OUTLOCAL" | cut -c$CHARS-`
+         else
+           if  ${ECHO} "$INLOCAL" | ${GREP} "$OUTLOCAL$" >/dev/null ; then
+             if [ ! -z "$USERSTART" ]; then
+               ${ECHO} "... It appears that $OUTHOST is a virtual domain"
+               ${ECHO} "    controlled by $USER."
+               LIST="$OUTLOCAL"
+               if [ ! -z "$HOSTMATCH" ]; then
+                 ${ECHO} "   This part of the setup appears correct."
+               fi
+             else
+               ${ECHO}
+               ${ECHO} "$EZWARN $1/inlocal ends with the contents"
+               ${ECHO} "    of $1/outlocal, but does not start with"
+               ${ECHO} "    $USER. If this message persists when you"
+               ${ECHO} "    run this program from $1/editor,"
+               ${ECHO} "    there is a setup error."
+             fi
+           else
+             ${ECHO}
+             ${ECHO} "$EZWARN $1/inlocal does not end with the contents"
+             ${ECHO} "    of $1/outlocal. This is almost always wrong."
+           fi
+          fi
+         if [ ! -r "$1/editor" ]; then
+             ${ECHO} "$EZERR $1/editor does not exist"
+         else
+           ${ECHO}
+           ${ECHO} "$1/editor:"
+           ${ECHO} "============================"
+           ${CAT} "$1/editor"
+           ${ECHO} "============================"
+           ${ECHO}
+         fi
+         if [ ! -r "$1/manager" ]; then
+             ${ECHO} "$EZERR $1/manager does not exist"
+         else
+           ${ECHO} "$1/manager:"
+           ${ECHO} "============================"
+           ${CAT} "$1/manager"
+           ${ECHO} "============================"
+           ${ECHO}
+         fi
+         if [ ! -r "$1/bouncer" ]; then
+             ${ECHO} "$EZERR $1/bouncer does not exist"
+         else
+           ${ECHO} "$1/bouncer:"
+           ${ECHO} "============================"
+           ${CAT} "$1/bouncer"
+           ${ECHO} "============================"
+           ${ECHO}
+         fi
+         if [ ! -r "$1/owner" ]; then
+             ${ECHO} "$EZERR $1/owner does not exist"
+         else
+           ${ECHO} "$1/owner:"
+           ${ECHO} "============================"
+           ${CAT} "$1/owner"
+           ${ECHO} "============================"
+           ${ECHO}
+           OWNER=`${GREP} "@" < $1/owner`
+         fi
+         if [ ! -r "$1/headeradd" ]; then
+             ${ECHO} "$EZERR $1/headeradd does not exist"
+         else
+           ${ECHO} "$1/headeradd:"
+           ${ECHO} "============================"
+           ${CAT} "$1/headeradd"
+           ${ECHO} "============================"
+           ${ECHO}
+         fi
+         if [ ! -r "$1/headerremove" ]; then
+             ${ECHO} "$EZERR $1/headerremove does not exist"
+         else
+           ${ECHO} "$1/headerremove:"
+           ${ECHO} "============================"
+           ${CAT} "$1/headerremove"
+           ${ECHO} "============================"
+           ${ECHO}
+         fi
+         ${ECHO} "---------------------------------------------------"
+         ${ECHO} "Checking standard options:"
+         ${ECHO}
+         if [ -r "$1/public" ]; then
+           ${ECHO} "... public"
+         else
+           ${ECHO} "... not public"
+         fi
+         if [ -r "$1/archived" ]; then
+           FLAGARCH='1'
+           ${ECHO} "... archived"
+         else
+           ${ECHO} "... not archived"
+         fi
+         if [ -r "$1/indexed" ]; then
+           FLAGARCH='1'
+           FLAGIND='1'
+           ${ECHO} "... indexed"
+         else
+           ${ECHO} "... not indexed"
+         fi
+         if [ ! -z "$FLAGARCH" ]; then
+            if [ ! -d "$1/archive" ]; then
+             ${ECHO} "$EZERR $1/archive is not a directory"
+           else
+             if [ ! -z "$NUM" -a ! -r "$1/archive/0/index" \
+                       -a ! -z "$FLAGIND" ]; then
+               ${ECHO} "$EZWARN list is archived, but there is no index."
+               ${ECHO} "    Please run ezmlm-idx!"
+             fi
+            fi
+          fi
+          if [ ! -d "$1/bounce" ]; then
+           ${ECHO} "$EZERR $1/bounce is not a directory"
+         fi
+         if [ -r "$1/prefix" ]; then
+           PREFIX=`${HEAD} -1 "$1/prefix"`
+           ${ECHO} "... using $1/prefix as subject prefix: $PREFIX"
+           ${ECHO}
+         fi
+         if [ -r "$1/sublist" ]; then
+           ${ECHO} "... this is a sublist for:"
+           ${HEAD} -1 < "$1/sublist"
+         else
+           ${ECHO} "... not a sublist"
+         fi
+          if [ ! -d "$1/text" ]; then
+           ${ECHO} "$EZERR $1/text is not a directory"
+         fi
+         ${ECHO} "... Contents of $1/text not checked"
+         ${ECHO}
+         if [ ! -z "$OWNER" ]; then
+           ${ECHO} "... Mail to owner goes to: $OWNER"
+         else
+           ${ECHO} "$EZWARN Mail to owner seems not to be forwarded."
+           ${ECHO} "    Remember to check the mailbox once in a while!"
+         fi
+         ${ECHO}
+         ${ECHO} "--------------------------------------------------"
+         ${ECHO}
+         ${ECHO} "... Links should be:"
+         ${ECHO} "    ~/.qmail-{list} -> $1/editor"
+         ${ECHO} "    ~/.qmail-{list}-default -> $1/manager"
+         ${ECHO} "    ~/.qmail-{list}-owner -> $1/owner"
+         ${ECHO} "    ~/.qmail-{list}-return-default -> $1/bouncer"
+         if [ ! -z "$LIST" -a ! -z "$INLOCALOK" ]; then
+           ${ECHO}
+           ${ECHO} "    As far as I can see, '{list}' should be '$LIST'."
+           ${ECHO} "    If so and if .qmail files should be in $HOME ..."
+           BN="$HOME/.qmail-$LIST"
+           FN="$BN"
+           if ${LLS} "$FN" 2>/dev/null | ${GREP} "$1/editor$" >/dev/null ; then
+             ${ECHO} "      $FN is OK"
+           else
+             ${ECHO} "???    $FN is BAD"
+           fi
+           FN="$BN-default"
+           if ${LLS} "$FN" | ${GREP} "$1/manager$" >/dev/null ; then
+             ${ECHO} "      $FN OK"
+           else
+             ${ECHO} "???    $FN is BAD"
+           fi
+           FN="$BN-owner"
+           if ${LLS} "$FN" | ${GREP} "$1/owner$" >/dev/null ; then
+             ${ECHO} "      $FN is OK"
+           else
+             ${ECHO} "???    $FN is BAD"
+           fi
+           FN="$BN-return-default"
+           if ${LLS} "$FN" | ${GREP} "$1/bouncer$" >/dev/null ; then
+             ${ECHO} "      $FN is OK"
+           else
+             ${ECHO} "???    $FN is BAD"
+           fi
+         fi
+         ${ECHO} "--------------------------------------------------"
+         ${ECHO} "Checking subscribers:"
+         ${ECHO}
+          if [ ! -d "$1/subscribers" ]; then
+           ${ECHO} "$EZERR $1/subscribers is not a directory"
+         else
+            if [ ! -x "$EZLIST" ]; then
+             ${ECHO} "$EZLIST is not available for listing"
+           else
+             if [ "$listsubscribers" = "yes" ] ; then
+               ${ECHO} "... Subscribers are:"
+                if ${EZLIST} "$1" | ${GREP} '@' ; then
+                 :
+               else
+                 ${ECHO} "$EZWARN no subscribers!"
+                fi
+             else
+               if ${EZLIST} "$1" | ${GREP} '@' >/dev/null 2>&1 ; then
+                 ${ECHO} "... There are subscribers."
+               else
+                 ${ECHO} "$EZWARN no subscribers!"
+               fi
+             fi
+            fi
+         fi
+         ${ECHO}
+         ${ECHO} "--------------------------------------------------"
+         ${ECHO} "Checking for digest:"
+         ${ECHO}
+         if ${GREP} 'ezmlm-tstdig' < $1/editor >/dev/null 2>&1 ; then
+            if ${GREP} -1 'ezmlm-tstdig' < $1/editor \
+               | ${TAIL} -1 | ${GREP} 'ezmlm-get' >/dev/null; then
+             ${ECHO} "... integrated digest via $1/editor"
+             DIGEST='1'
+             ${ECHO}
+           fi
+         fi
+         if [ -z "$DIGEST" ]; then
+           ${ECHO} "... no digest via $1/editor"
+         else
+           ${ECHO} "... links should be:"
+           ${ECHO} "    ~/.qmail-{list}-digest-return-default -> $1/bouncer"
+           ${ECHO} "    ~/.qmail-{list}-digest-owner -> $1/owner"
+           if [ ! -z "$LIST" -a ! -z "$INLOCALOK" ]; then
+             ${ECHO}
+             ${ECHO} "    As far as I can see, '{list}' should be '$LIST'."
+             ${ECHO} "    If so and if .qmail files should be in $HOME ..."
+             BN="$HOME/.qmail-$LIST"
+             FN="$BN-digest-return-default"
+             if ${LLS} "$FN" 2>/dev/null | \
+                       ${GREP} "$1/bouncer$" >/dev/null ; then
+               ${ECHO} "      $FN is OK"
+             else
+               ${ECHO} "???    $FN is BAD"
+             fi
+             FN="$BN-digest-owner"
+             if ${LLS} "$FN" 2>/dev/null | \
+                       ${GREP} "$1/owner$" >/dev/null ; then
+               ${ECHO} "      $FN is OK"
+             else
+               ${ECHO} "???    $FN is BAD"
+             fi
+           fi
+         fi
+         if [ -d "$1/digest" ]; then
+           if [ ! -d "$1/digest/subscribers" ]; then
+             ${ECHO} "$EZERR $1/digest exists, but $1/digest/subscribers"
+             ${ECHO} "$EZERR is not a directory"
+             ${ECHO}
+           else
+             if [ ! -x "$EZLIST" ]; then
+               ${ECHO} "$EZLIST is not available for listing"
+             else
+               if [ "$listsubscribers" = "yes" ] ; then
+                 ${ECHO}
+                 ${ECHO} "... Digest subscribers are:"
+                  if ${EZLIST} "$1" | ${GREP} '@' ; then
+                   :
+                 else
+                   ${ECHO} "$EZWARN no subscribers!"
+                  fi
+               else
+                 if ${EZLIST} "$1" | ${GREP} '@' >/dev/null 2>&1 ; then
+                   ${ECHO} "... There are digest subscribers."
+                 else
+                   ${ECHO} "$EZWARN no subscribers!"
+                 fi
+               fi
+              fi
+             ${ECHO}
+            fi
+         fi
+         ${ECHO} "---------------------------------------------------"
+         ${ECHO} "Checking for subscription moderation/remote admin:"
+         ${ECHO}
+         if [ -r "$1/remote" ]; then
+           FLAGMOD='1'
+           ${ECHO} "... set up for remote administration"
+           REMOTE=`${CAT} "$1/remote"| ${HEAD} -1`
+            if ${ECHO} "$REMOTE" | ${GREP} -G "^/" >/dev/null 2>&1
+              then
+               MODDIR="$REMOTE"
+             else
+               MODDIR="$1/mod"
+           fi
+           REMOTE='1'
+         else
+           ${ECHO} "... no remote admin"
+          fi
+         if [ -r "$1/modsub" ]; then
+           FLAGMOD='1'
+           ${ECHO} "... subscription moderated"
+           MODSUB=`${CAT} "$1/modsub"| ${HEAD} -1`
+            if ${ECHO} "$MODSUB" | ${GREP} -G "^/" >/dev/null 2>&1
+           then
+              MODDIR="$MODSUB"
+           elif [ -z "$MODDIR" ]; then
+             MODDIR="$1/mod"
+           fi
+         else
+           ${ECHO} "... no subscription moderation"
+         fi
+         if [ "$FLAGMOD" = '1' ]; then
+           ${ECHO}
+           ${ECHO} "Mods/remote admins stored based in $MODDIR:"
+           ${ECHO}
+           if [ ! -d "$MODDIR" ]; then
+             ${ECHO} "$EZERR moderator dir $MODDIR doesn't exist!"
+           elif [ -e "$MODDIR/lock" -a ! -w "$MODDIR/lock" ]; then
+             ${ECHO} "$EZERR $MODDIR/lock is not writable to user $USER"
+           elif [ ! -x "$EZLIST" ]; then
+             ${ECHO} "${EZLIST} not available for listing"
+           else
+             if ${EZLIST} "$MODDIR" | ${GREP} '@' ; then
+                 :
+             else
+                 ${ECHO} "$EZERR no subscription moderators/remote admins!"
+             fi
+           fi
+           ${ECHO}
+         fi
+         ${ECHO} "---------------------------------------------------"
+         ${ECHO} "Checking for message moderation:"
+         ${ECHO}
+         if ${GREP} 'ezmlm-gate' < "$1/editor" > /dev/null 2>&1; then
+            GATE='1'
+         fi
+         if ${GREP} 'ezmlm-store' < "$1/editor" > /dev/null 2>&1; then
+            STORE='1'
+         fi
+         FLAGMOD=''
+         if [ -r "$1/modpost" ]; then
+           FLAGMOD='1'
+           MODPOST=`${CAT} "$1/modpost" | ${HEAD} -1`
+            if ${ECHO} "$MODPOST" | ${GREP} -G "^/" >/dev/null 2>&1
+              then
+             MODDIR="$MODPOST"
+           else
+             MODDIR="$1/mod"
+           fi
+         fi
+         if [ "$STORE" = '1' -a -z "$FLAGMOD" ]; then
+             ${ECHO} "??? it looks from $1/editor like the list is set up"
+             ${ECHO} "    for message moderation. However, since $1/modpost"
+              ${ECHO} "    doesn't exist, ezmlm-store posts them directly. If"
+              ${ECHO} "    this is not intended, please create $1/modpost."
+             ${ECHO}
+             FLAGMOD='1'
+             MODDIR="$1/mod"
+         elif [ -z "$STORE" -a -z "$GATE" -a "$FLAGMOD" = '1' ]; then 
+             ${ECHO} "??? $1/modpost exists, leading me to think you'd like"
+             ${ECHO} "    message moderation, but I can't find any call to"
+             ${ECHO} "    ezmlm-store in $1/editor."
+             ${ECHO}
+         elif [ -z "$STORE" -a "$GATE" = '1' ]; then
+             if [ -z "$FLAGMOD" ]; then
+               ${ECHO} "??? The list is set up with ezmlm-gate in $1/editor."
+               ${ECHO} "    However, since $1/modpost does not exist all"
+               ${ECHO} "    messages will be accepted!"
+               FLAGMOD='1'
+               MODDIR="$1/mod"
+             else
+               ${ECHO} "... The list is set up with ezmlm-gate in $1/editor."
+               ${ECHO} "    Since $1/modpost exists, subscriber messages"
+               ${ECHO} "    will be accepted and others will be send for"
+               ${ECHO} "    moderation."
+             fi
+         fi
+         if [ "$FLAGMOD" = '1' ]; then
+           ${ECHO} "... message moderated"
+           ${ECHO}
+           ${ECHO} "Message moderators based in $MODDIR:"
+           ${ECHO}
+           if [ ! -d "$MODDIR" ]; then
+             ${ECHO} "$EZERR moderator dir $MODDIR doesn't exist!"
+           elif [ -e "$MODDIR/lock" -a ! -w "$MODDIR/lock" ]; then
+             ${ECHO} "$EZERR $MODDIR/lock is not writable to user $USER"
+           elif [ ! -x "$EZLIST" ]; then
+             ${ECHO} "${EZLIST} not available for listing"
+           else
+             if ${EZLIST} "$MODDIR" | ${GREP} '@' ; then
+                 :
+             else
+                 ${ECHO} "$EZERR no message moderators!"
+             fi
+           fi
+           ${ECHO}
+           MT="120"
+           if [ -r "$1/modtime" ]; then
+             MODTIME=`${CAT} "$1/modtime" | ${HEAD} -1`
+             if [ "$MODTIME" -eq 0 ]; then
+               MT="120"
+             elif [ "$MODTIME" -lt 24 ]; then
+               MT="24"
+             elif [ "$MODTIME" -gt 240 ]; then
+               MT="240"
+             else
+               MT="${MODTIME}"
+             fi
+           fi
+           ${ECHO} "... Messages awaiting moderation time out after $MT hours"
+           if [ ! -d "$1/mod/pending" ]; then
+             ${ECHO} "$EZERR $MODDIR/pending is not a directory"
+           else
+             MODNUM=`${LS} "$1/mod/pending" | ${GREP} -c '*'`
+             ${ECHO} "... there are $MODNUM messages awaiting moderator action"
+           fi
+           if [ ! -d "$1/mod/accepted" ]; then
+             ${ECHO} "$EZERR $MODDIR/accepted is not a directory"
+           fi
+           if [ ! -d "$1/mod/rejected" ]; then
+             ${ECHO} "$EZERR $MODDIR/rejected is not a directory"
+           fi
+           if [ ! -r "$1/moderator" ]; then
+             ${ECHO} "$EZERR $1/moderator is not readable to user $USER"
+           else
+             if ${GREP} 'ezmlm-moderate' < "$1/moderator" >/dev/null 2>&1
+             then
+               :
+             else
+               ${ECHO} "$EZERR $1/moderator lacks ezmlm-moderate entry"
+             fi
+             ${ECHO}
+             ${ECHO} "$1/moderator:"
+             ${ECHO} "============================"
+             ${CAT} "$1/moderator"
+             ${ECHO} "============================"
+             ${ECHO} 
+           fi
+           ${ECHO}
+           ${ECHO} "... Links should be:"
+           ${ECHO} "    ~/.qmail-{list}-accept-default -> $1/moderator"
+           ${ECHO} "    ~/.qmail-{list}-reject-default -> $1/moderator"
+           ${ECHO}
+           if [ ! -z "$LIST" -a ! -z "$INLOCALOK" ]; then
+             ${ECHO}
+             ${ECHO} "    As far as I can see, '{list}' should be '$LIST'."
+             ${ECHO} "    If so and if .qmail files should be in $HOME ..."
+             BN="$HOME/.qmail-$LIST"
+             FN="$BN-accept-default"
+             if ${LLS} "$FN" 2>/dev/null | \
+                       ${GREP} "$1/moderator$" >/dev/null ; then
+               ${ECHO} "      $FN is OK"
+             else
+               ${ECHO} "???    $FN is BAD"
+             fi
+             FN="$BN-reject-default"
+             if ${LLS} "$FN" 2>/dev/null | \
+                       ${GREP} "$1/moderator$" >/dev/null ; then
+               ${ECHO} "      $FN is OK"
+             else
+               ${ECHO} "???    $FN is BAD"
+             fi
+           fi
+          else
+           ${ECHO} "... no message moderation"
+          fi   
+         ${ECHO}
+         ${ECHO} "---------------------------------------------------"
+         ${ECHO} "Checking for SENDER checks:"
+         ${ECHO}
+         if ${GREP} 'ezmlm-issubn -n' < "$1/editor" >/dev/null 2>&1 ; then
+           ${ECHO} "... Some type of blacklisting in use"
+           SRESTRICT='1'
+         fi
+         if ${GREP} 'ezmlm-issubn' < "$1/editor" |\
+           ${GREP} -v -- '-n' >/dev/null 2>&1 ; then
+           ${ECHO} "... Some type of SENDER check in use for posts"
+           SRESTRICT='1'
+         fi
+         if [ -z "$SRESTRICT" ]; then
+           ${ECHO} "... no SENDER restrictions found for posts"
+         fi
+         ${ECHO}
+         GET=` ${GREP} 'ezmlm-get' < "$1/manager" | \
+               ${CUT} -d' ' -f2- | ${CUT} -d\' -f1`
+         if ${ECHO} "$GET" | ${GREP} 's' >/dev/null ; then
+           ${ECHO} "... Only subscribers may access the archive"
+         else
+           ${ECHO} "... no SENDER restrictions for archive access"
+         fi
+         ${ECHO}
+         ${ECHO} "---------------------------------------------------"
+         ${ECHO} "Checking for special options:"
+         ${ECHO}
+         MANAGE=` ${GREP} 'ezmlm-manage' < "$1/manager" | \
+               ${CUT} -d' ' -f2- | ${CUT} -d\' -f1`
+         if ${ECHO} "$MANAGE" | ${GREP} 'e' >/dev/null ; then
+           ${ECHO} "... remote editing of $1/text/ files enabled"
+           SPEC='1'
+         fi
+         if ${ECHO} "$MANAGE" | ${GREP} 'l' >/dev/null ; then
+           ${ECHO} "... remote listing of subscribers enabled"
+           SPEC='1'
+         fi
+         if [ "$SPEC" = '1' -a -z "$REMOTE" ] ; then
+           ${ECHO} \
+             "$EZERR but remote admin is not enabled, so this will not work!"
+         fi
+         ${ECHO} "---------------------------------------------------"
+       fi
+       ${ECHO}
+       ${ECHO} "EXT ='$EXT'"
+       ${ECHO} "EXT1 ='$EXT1'"
+       ${ECHO} "EXT2 ='$EXT2'"
+       ${ECHO} "EXT3 ='$EXT3'"
+       ${ECHO} "EXT4 ='$EXT4'"
+       ${ECHO} "DTLINE = $DTLINE"
+       ${ECHO} "RPLINE = $RPLINE"
+       ${ECHO} "UFLINE = $UFLINE"
+       ${ECHO} "---------------------------------------------------"
+       ${ECHO}
+       ${ECHO} "Hope that helps!"
+) | "$RCP" || exit 100
+
+exit 99
+
diff --git a/ezmlm-clean.1 b/ezmlm-clean.1
new file mode 100644 (file)
index 0000000..652507e
--- /dev/null
@@ -0,0 +1,117 @@
+.TH ezmlm-clean 1
+.SH NAME
+ezmlm-clean \- clean moderation directory
+.SH SYNOPSIS
+.B ezmlm-clean [mMrRvV]
+.I dir
+.SH DESCRIPTION
+If
+.I dir\fB/modpost
+exists,
+.B ezmlm-clean
+sends out messages for timed-out posts in the moderation directory
+and removes stubs for rejected and accepted posts
+for the mailing list stored in
+.IR dir .
+If
+.I dir\fB/modpost
+does not exist,
+.B ezmlm-clean
+does nothing and exits.
+
+.B ezmlm-clean
+reads
+.I dir\fB/modtime
+and extracts a time-out ``time'' in hours from it. If ``time'' is 0 or
+.I dir\fB/modtime
+is empty or doesn't exist,
+a default of 120 h is used. If a time is given, it is limited to
+the range 24 h to 240 h.
+
+.B ezmlm-clean
+then looks through
+.I dir\fB/mod/accepted/
+and
+.I dir\fB/mod/rejected/
+and removes message stubs older than ``time''. ``Time'' is
+a minimum retention time. Since the files are processed only
+when
+.B ezmlm-clean
+is run, the delay before a message is timed-out may
+be substantially longer if the list does not receive many messages.
+
+Message age
+determined by the time parsed
+.I from the file name,
+not from the creation time.
+Thus, there is no good way to extend the life of
+the file by e.g. touching it. Also, files in these directories are not
+checked for the proper format. Thus, most non-message files in these
+directories will be deleted the first time
+.B ezmlm-clean
+is run.
+
+For messages in
+.I dir\fB/mod/pending/
+no action is taken on read-only files. Messages without the owner execute
+bit set are silently removed, as they are the result of incomplete
+.B ezmlm-store(1)
+executions. For other messages, a notification of the time out is sent
+to the sender, before the file is removed.
+
+.B ezmlm-clean
+logs errors to the mail log.
+Re-delivery should be avoided by suffixing any
+.I \.qmail
+line invoking
+.B ezmlm-clean
+with '|| exit 0'.
+.SH OPTIONS
+.TP
+.B \-m
+(Default.)
+The timed-out post is sent as a MIME enclosure.
+.TP
+.B \-M
+The timed-out post is appended to the message.
+.TP
+.B \-r
+(Default.)
+The timed-out post is returned to sender.
+.TP
+.B \-R
+The timed-out post is discarded without sender notification.
+.B Note:
+.B ezmlm-clean
+is normally run from both
+.I dir\fB/editor
+and
+.IR dir\fB/moderator .
+To suppress sender notification, the switch needs to be specified
+for all invocations of
+.BR ezmlm-clean .
+.TP
+.B \-v
+Display
+.B ezmlm-clean
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-clean
+version information.
+.SH "CHARACTER SETS"
+If
+.I dir\fB/charset
+exists,
+.B ezmlm-clean
+will use the character set listed for all messages. Otherwise, the
+default ``us-ascii'' will be used. The character set can be suffixed
+by ``:'' followed by a code. If the code is ``Q'', outgoing messages are 
+sent as ``Quoted-Printable'', if it is ``B'' they are sent ``base64'' encoded.
+Otherwise, text is sent as is.
+.SH "SEE ALSO"
+ezmlm-make(1),
+ezmlm-moderate(1),
+ezmlm-store(1),
+ezmlm(5)
diff --git a/ezmlm-clean.c b/ezmlm-clean.c
new file mode 100644 (file)
index 0000000..ce8c04f
--- /dev/null
@@ -0,0 +1,355 @@
+/*$Id: ezmlm-clean.c,v 1.30 1999/05/12 22:15:26 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "sig.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "getln.h"
+#include "case.h"
+#include "qmail.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "seek.h"
+#include "quote.h"
+#include "datetime.h"
+#include "now.h"
+#include "date822fmt.h"
+#include "direntry.h"
+#include "cookie.h"
+#include "sgetopt.h"
+#include "fmt.h"
+#include "errtxt.h"
+#include "copy.h"
+#include "idx.h"
+#include "mime.h"
+
+int flagmime = MOD_MIME;       /* default is message as attachment */
+int flagreturn = 1;            /* default return timed-out messages */
+char flagcd = '\0';            /* default: no transferencoding */
+stralloc fnmsg = {0};
+
+/* When ezmlm-clean is run, messages and message stubs in pending/      */
+/* rejected/accepted are erased if they are older than delay hours.     */
+/* Timeouts in h for messages. If modtime has a number, it is made to be*/
+/* in the range DELAY_MIN..DELAY_MAX. If the number is 0 or there is no */
+/* number, DELAY_DEFAULT is used. Messages that are read-only are       */
+/* ignored. Messages in 'pending' that have the execute bit set result  */
+/* in an informative reply to the poster. Any defects in the message    */
+/* format, inability to open the file, etc, result in a maillog entry   */
+/* whereafter the message is erased. */
+
+/* The defines are in "idx.h" */
+
+#define FATAL "ezmlm-clean: fatal: "
+
+void die_read()
+{
+  strerr_die4x(111,FATAL,ERR_READ,fnmsg.s,": ");
+}
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-clean: usage: ezmlm-clean [-mMrRvV] dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+datetime_sec when;
+unsigned int older;
+struct datetime dt;
+
+char textbuf[1024];
+substdio sstext;
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+
+char *dir;
+char strnum[FMT_ULONG];
+char date[DATE822FMT];
+char boundary[COOKIE];
+datetime_sec hashdate;
+
+stralloc outhost = {0};
+stralloc outlocal = {0};
+stralloc mailinglist = {0};
+stralloc listid = {0};
+stralloc quoted = {0};
+stralloc line = {0};
+stralloc modtime = {0};
+stralloc to = {0};
+stralloc charset = {0};
+
+int flagconf;
+int fd;
+int match;
+unsigned long msgnum = 0;
+                       /* counter to make message-id unique, since we may */
+                       /* send out several msgs. This is not bullet-proof.*/
+                       /* Duplication occurs if we do x>1 msg && another  */
+                       /* ezmlm started within x seconds, and with the    */
+                       /* same pid. Very unlikely.                        */
+
+void transferenc()
+{
+       if (flagcd) {
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"Quoted-Printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        } else
+          qmail_puts(&qq,"\n\n");
+}
+void readconfigs()
+/* gets outlocal, outhost, etc. This is done only if there are any timed-out*/
+/* messages found, that merit a reply to the author. */
+{
+
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  getconf_line(&listid,"listid",0,FATAL,dir);
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  set_cpouthost(&outlocal);
+  set_cpoutlocal(&outlocal);
+}
+
+void sendnotice(d)
+char *d;
+/* sends file pointed to by d to the address in the return-path of the  */
+/* message. */
+{
+  unsigned int x,y;
+  char *err;
+
+      if (!flagconf) {
+        readconfigs();
+      }
+      if (qmail_open(&qq, (stralloc *) 0) == -1)
+        strerr_die2x(111,FATAL,ERR_QMAIL_QUEUE);
+
+      fd = open_read(d);
+      if (fd == -1)
+        strerr_die4sys(111,FATAL,ERR_OPEN,d,": ");
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      if (getln(&sstext,&line,&match,'\n') == -1) die_read();
+      if (!match) die_read();
+      if (!case_startb(line.s,line.len,"return-path:")) die_read();
+      x = 12 + byte_chr(line.s + 12,line.len-12,'<');
+      y = byte_rchr(line.s + x,line.len-x,'>');
+      if (x != line.len && x+y != line.len) {
+        if (!stralloc_copyb(&to,line.s+x+1, y-1)) die_nomem();
+        if (!stralloc_0(&to)) die_nomem();
+      } else
+        die_read();
+      qmail_puts(&qq,"Mailing-List: ");
+      qmail_put(&qq,mailinglist.s,mailinglist.len);
+      qmail_puts(&qq,"\nList-ID: ");
+      qmail_put(&qq,listid.s,listid.len);
+      qmail_puts(&qq,"\nDate: ");
+      datetime_tai(&dt,when);
+      qmail_put(&qq,date,date822fmt(date,&dt));
+      qmail_puts(&qq,"Message-ID: <");
+      if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,
+               (unsigned long) when + msgnum++))) die_nomem();
+      if (!stralloc_append(&line,".")) die_nomem();
+      if (!stralloc_catb(&line,strnum,
+               fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+      if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
+      if (!stralloc_cat(&line,&outhost)) die_nomem();
+      if (!stralloc_0(&line)) die_nomem();
+      qmail_puts(&qq,line.s);
+               /* "unique" MIME boundary as hash of messageid */
+      cookie(boundary,"",0,"",line.s,"");
+      qmail_puts(&qq,">\nFrom: ");
+      if (!quote(&quoted,&outlocal)) die_nomem();
+      qmail_put(&qq,quoted.s,quoted.len);
+      qmail_puts(&qq,"-help@");
+      qmail_put(&qq,outhost.s,outhost.len);
+      qmail_puts(&qq,"\nSubject: ");
+      qmail_puts(&qq,TXT_RETURNED_POST);
+      qmail_put(&qq,quoted.s,quoted.len);
+      qmail_puts(&qq,"@");
+      qmail_put(&qq,outhost.s,outhost.len);
+      qmail_puts(&qq, "\nTo: ");
+      qmail_puts(&qq,to.s);
+      if (flagmime) {
+        if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+          if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+            if (charset.s[charset.len - 1] == 'B' ||
+               charset.s[charset.len - 1] == 'Q') {
+              flagcd = charset.s[charset.len - 1];
+              charset.s[charset.len - 2] = '\0';
+            }
+          }
+        } else
+          if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+        if (!stralloc_0(&charset)) die_nomem();
+        qmail_puts(&qq,"\nMIME-Version: 1.0\n");
+        qmail_puts(&qq,"Content-Type: multipart/mixed;\n\tboundary=");
+        qmail_put(&qq,boundary,COOKIE);
+        qmail_puts(&qq,"\n\n--");
+        qmail_put(&qq,boundary,COOKIE);
+        qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+        qmail_puts(&qq,charset.s);
+        transferenc();
+      } else
+      qmail_puts(&qq,"\n\n");
+
+      copy(&qq,"text/top",flagcd,FATAL);
+      copy(&qq,"text/mod-timeout",flagcd,FATAL);
+      if (flagcd == 'B') {
+        encodeB("",0,&line,2,FATAL);
+        qmail_put(&qq,line.s,line.len);
+      }
+
+      if (flagmime) {
+        qmail_puts(&qq,"\n--");
+        qmail_put(&qq,boundary,COOKIE);
+        qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n");
+      }
+
+      if (seek_begin(fd) == -1)
+        strerr_die4sys(111,FATAL,ERR_SEEK,d,": ");
+
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      if (substdio_copy(&ssqq,&sstext) != 0) die_read();
+      close (fd);
+
+      if (flagmime) {
+        qmail_puts(&qq,"\n--");
+        qmail_put(&qq,boundary,COOKIE);
+        qmail_puts(&qq,"--\n");
+      }
+
+      if (!stralloc_copy(&line,&outlocal)) die_nomem();
+      if (!stralloc_cats(&line,"-return-@")) die_nomem();
+      if (!stralloc_cat(&line,&outhost)) die_nomem();
+      if (!stralloc_0(&line)) die_nomem();
+      qmail_from(&qq,line.s);          /* sender */
+        qmail_to(&qq,to.s);
+
+     if (*(err = qmail_close(&qq)) != '\0')
+       strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE, err + 1);
+
+     strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+     strerr_warn2("ezmlm-clean: info: qp ",strnum,0);
+}
+
+void dodir(dirname,reply)
+char *dirname; int reply;
+/* parses file names in directory 'dirname'. Files that are not owner */
+/* writable (w) are ignored. If the files are older (by name!) than   */
+/* now-delay, action is taken:                                        */
+/* If the owner x bit is not set, the file is erased.                 */
+/* If it is set and reply is not set, the file is erased. If both are */
+/* set, a notice about the timeout is sent to the poster. If this     */
+/* fails due to a message-related error (format, etc) the file is     */
+/* erased even though no notice is sent. For temporary errors (like   */
+/* out-of-memory) the message is left intact for the next run. If the */
+/* notice is sent successfully, the file is erased. All this is to    */
+/* do the best possible without risking a rerun of the .qmail file,   */
+/* which could result in a redelivery of the action request and a     */
+/* second (incorrect) reply to the moderator's request.               */
+
+/* NOTE: ALL non-hidden files in this dir are processed and merci-    */
+/* lessly deleted. No checks for proper file name. E.g. 'HELLO'       */
+/* => time 0 => will be deleted on the next ezmlm-clean run.          */
+{
+  DIR *moddir;
+  direntry *d;
+  unsigned long modtime;
+  struct stat st;
+
+  moddir = opendir(dirname);
+  if (!moddir)
+    strerr_die6sys(0,FATAL,ERR_OPEN,dir,"/",dirname,": ");
+  while ((d = readdir(moddir))) {
+    if (d->d_name[0] == '.') continue;
+    scan_ulong(d->d_name,&modtime);
+    if (modtime < older) {
+      if (!stralloc_copys(&fnmsg,dirname)) die_nomem();
+      if (!stralloc_cats(&fnmsg,d->d_name)) die_nomem();
+      if (!stralloc_0(&fnmsg)) die_nomem();
+      if((stat(fnmsg.s,&st) != -1) && (st.st_mode & 0200)) {
+        if(reply && (st.st_mode & 0100)) {
+                       /* unlink unless there was a TEMPORARY */
+                       /* not message-related error notifying */
+                       /* poster and msg x bit set.  Leave r/o*/
+                       /* messages alone. Non-x bit msg are   */
+                       /* trash. Just unlink, don't notify    */
+          sendnotice(fnmsg.s);
+          unlink(fnmsg.s);
+        } else
+          unlink(fnmsg.s);
+      }
+    }
+  }
+  closedir(moddir);
+}
+
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int fdlock;
+  int delay;
+  int opt;
+  (void) umask(022);
+  sig_pipeignore();
+  when = now();
+
+  while ((opt = getopt(argc,argv,"mMrRvV")) != opteof)
+    switch(opt) {
+      case 'm': flagmime = 1; break;
+      case 'M': flagmime = 0; break;
+      case 'r': flagreturn = 1; break;
+      case 'R': flagreturn = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-clean version: ", EZIDX_VERSION);
+                /* not reached */
+      default:
+       die_usage();
+    }
+
+  dir = argv[optind];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  getconf_line(&modtime,"modtime",0,FATAL,dir);
+  if (!stralloc_0(&modtime)) die_nomem();
+  scan_ulong(modtime.s,&delay);
+  if (!delay) delay = DELAY_DEFAULT;
+  else if (delay < DELAY_MIN) delay = DELAY_MIN;
+  else if (delay > DELAY_MAX) delay = DELAY_MAX;
+  older = (unsigned long) when - 3600L * delay;        /* delay is in hours */
+
+  fdlock = open_append("mod/lock");
+  if (fdlock == -1)
+    strerr_die4sys(0,FATAL,ERR_OPEN,dir,"/mod/lock: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(0,FATAL,ERR_OBTAIN,dir,"/mod/lock: ");
+
+  flagconf = 0;
+  dodir("mod/pending/",flagreturn);
+  dodir("mod/accepted/",0);
+  dodir("mod/rejected/",0);
+  _exit(0);
+}
+
diff --git a/ezmlm-cron.1 b/ezmlm-cron.1
new file mode 100644 (file)
index 0000000..98fa3e2
--- /dev/null
@@ -0,0 +1,245 @@
+.TH ezmlm-cron 1
+.SH NAME
+ezmlm-cron \- Sets up digest request messages generation via crond
+.SH SYNOPSIS
+.B ezmlm-cron
+[
+.B \-cCdDlL
+][
+.B \-w \fIdow
+][
+.B \-t \fIhh:mm
+][
+.B \-i \fIhrs
+]
+.I listadr code[f]
+.SH DESCRIPTION
+.B ezmlm-cron
+is a very restrictive interface to
+.BR crond(8) .
+It edits the effective user's ~/crontab (see crontab(5))
+file. It then executes
+.B crontab(1)
+to update crond(8) with the changes.
+
+.B ezmlm-cron
+sets up the generation of trigger messages to the list
+.I listadr
+and the digest code
+.IR code .
+A optional digest format specifier
+.I f
+can be added to
+.IR code .
+
+.B ezmlm-cron
+reads
+.IR dir\fB/ezcronrc ,
+where
+.I dir
+is the home directory of the effective user. The first line of this file is the
+host name, 'host', to which bounces should be sent. Any bounces for lists
+set up by 'user' will go to 'user@host'. Subsequent lines are entries
+for users made up of:
+.IR user:local:host:num:[list1[,list2...]] .
+The ':'-separated parameters are:
+.TP
+.I user
+the user name to which this line corresponds.
+.TP
+.I local
+the list address must start with exactly these characters (case sensitive).
+If this field is empty, any list local address is allowed.
+.TP
+.I host
+the list host name must exactly match this parameter (case insensitive).
+If this field is empty, any list host address is allowed. (Host names for
+the list and the digest list must still match.)
+.TP
+.I num
+the user is permitted a maximum of
+.I num
+entries.
+.TP
+.I list1 [,list2...]
+a set of complete list names separated by commas. The user may edit
+entries for these lists, even if they do not match the criteria set
+above. If these lists exist, they are counted in determining
+.IR num .
+No while space is allowed before or between list names. If no list names
+are specified, the final ':' can be omitted.
+
+The first line matching the executing user will be used.
+
+If
+.B ezmlm-cron
+is installed SUID
+.IR euser ,
+the configuration and crontab files from that user's home directory
+will be used for all
+.B ezmlm-cron
+actions. This way, users on a system can be given limited
+.B crond(8)
+access via
+.B ezmlm-cron
+restricted by
+.I dir\fB/ezcronrc
+and to the generation of digest trigger messages. This is especially
+useful for users without shell access or access to
+.BR crond(8) .
+.I euser
+is usually 'ezmlm'.
+To install
+.B ezmlm-cron
+SUID ezmlm:
+
+.EX
+  # chown ezmlm /usr/local/bin/ezmlm/ezmlm-cron
+  # chmod 4555 /usr/local/bin/ezmlm/ezmlm-cron
+.EE
+
+.B ezmlm-cron
+refuses to run if installed SUID root.
+.B ezmlm-cron
+when executed by 'root',
+will still use the files in ~root.
+
+To allow
+.B crond(8)
+access, you may need to list the effective user (all users allowed access
+or ~ezmlm if
+.B ezmlm-cron
+is installed SUID ezmlm) in
+.BR /etc/cron.allow .
+See
+.B crontab(1)
+for further information.
+.SH OPTIONS
+The
+.BR \-c ,
+.BR \-d ,
+and
+.B \-l
+switches are mutually exclusive.
+
+.TP
+.B \-c
+List user entry from
+.IR ezcronrc .
+.TP
+.B \-C
+(Default.)
+Do not list user entry.
+.TP
+.B \-d
+Delete entry.
+.B ezmlm-cron
+will search
+.I ~euser\fB/crontab
+for an entry belonging to the executing user, permitted by
+.I ~euser\fB/ezcronrc
+and matching the command line arguments supplied.
+.I code
+is ignored and may be omitted.
+.TP
+.B \-D
+(Default.)
+Do not delete entry.
+.TP
+.B \-i\fI hrs
+Generate trigger message with
+.I hrs
+hours interval. Accepted intervals are 0, 1 ,2, 3, 6, 12, 24, 48, and 72 hours.
+Other numbers will be silently adjusted upwards to the nearest accepted
+interval
+(intervals above 72 hours will result in weekly trigger messages).
+.TP
+.B \-l
+List entries. If no other command line arguments are given,
+.B ezmlm-cron
+lists the entries created in the name of the user. If
+.I listadr
+is given,
+.B ezmlm-cron
+will list the entries for all the matching lists, even if the entries
+were not set up by the current user. Arguments, if given, still have to
+comply with the rules set in
+.IR ezcronrc .
+
+Crude
+.B crontab(5)
+lines are listed. These are taken from the ~/crontab file. Usually,
+these are active entries, although if the last execution of
+.B crontab(1)
+failed, they may not be.
+.TP
+.B \-L
+(Default.)
+Do not list entry.
+.TP
+.B \-t\fI hh:mm
+The time for the trigger message. Other trigger messages will be sent
+.I hrs
+hours before and after this time.
+.TP
+.B \-w\fI dow
+The days of the week on which trigger messages are sent. day 0 and 7 are
+Sunday, 1 is Monday, etc (see crontab(5)). The string specified for
+.I dow
+must consists of single comma-separated digits in the range '0'-'7'
+only. The default is every day, except for
+.I hrs
+of 48 (default Monday, Wednesday, Friday), 72 (default Monday and Thursday),
+or greater than 72 (default Monday).
+Both
+.I hrs
+and
+.I dow
+can be specified. In this case, trigger messages are sent
+on the day specified by
+.I dow
+at the interval
+specified by
+.IR hrs .
+If
+.I hrs
+is greater than 24 h, it is ignored and
+trigger messages are generated daily or as specified by
+.IR dow .
+.SH FILES
+.TP
+.I ~euser/ezcronrc
+The configuration file for
+.BR ezmlm-cron .
+.I euser
+is the effective user id. This is the executing user, unless
+.B ezmlm-cron
+is installed SUID
+.IR otheruser ,
+in which case it is
+.IR otheruser .
+.TP
+.I ~euser/crontab
+The file edited by
+.BR ezmlm-cron .
+.I euser
+is the effective user.
+.TP
+.I ~euser/crontabl
+The lock file used to assure that only one process at a time is editing the
+.B crond(8)
+settings.
+.I euser
+is the effective user.
+.SH BUGS
+.B ezmlm-cron
+should use the output of 'crontab -l' to list crontab lines, rather than
+parse the crontab file (and assume that the last execution of
+.B crontab(1)
+was successful).
+.SH "SEE ALSO"
+crond(8),
+crontab(1),
+crontab(5),
+ezmlm(5),
+ezmlm-get(1)
diff --git a/ezmlm-cron.c b/ezmlm-cron.c
new file mode 100644 (file)
index 0000000..e35f7ea
--- /dev/null
@@ -0,0 +1,507 @@
+#include <sys/types.h>
+#include <pwd.h>
+#include "strerr.h"
+#include "stralloc.h"
+#include "sgetopt.h"
+#include "substdio.h"
+#include "error.h"
+#include "str.h"
+#include "fmt.h"
+#include "fork.h"
+#include "wait.h"
+#include "readwrite.h"
+#include "auto_qmail.h"
+#include "auto_cron.h"
+#include "errtxt.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-cron: fatal: "
+
+void die_usage()
+{
+ strerr_die2x(100,FATAL,
+  "usage: ezmlm-cron [-cCdDlLvV] [-w dow] [-t hh:mm] [-i hrs] listadr code");
+}
+
+void die_dow()
+{
+  strerr_die2x(100,FATAL,ERR_DOW);
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+unsigned long deltah = 24L;    /* default interval 24h */
+unsigned long hh = 4L;         /* default time 04:12 */
+unsigned long mm = 12L;
+char *dow = "*";               /* day of week */
+char *qmail_inject = "/bin/qmail-inject ";
+char strnum[FMT_ULONG];
+unsigned long uid,euid;
+
+stralloc line = {0};
+stralloc rp = {0};
+stralloc addr = {0};
+stralloc user = {0};
+stralloc euser = {0};
+stralloc dir = {0};
+stralloc listaddr = {0};
+
+struct passwd *ppasswd;
+
+int opt,match;
+int hostmatch;
+int localmatch;
+unsigned long dh,t;
+int founduser = 0;
+int listmatch = 0;
+int flagconfig = 0;
+int flagdelete = 0;
+int flaglist = 0;
+int flagdigit = 0;
+int flagours;
+int foundlocal;
+int foundmatch = 0;
+int nolists = 0;
+int maxlists;
+unsigned int pos,pos2,poslocal,len;
+unsigned int lenhost,lenlocal;
+unsigned int part0start,part0len;
+int fdlock,fdin,fdout;
+
+char *local = (char *) 0;      /* list = local@host */
+char *host = (char *) 0;
+char *code = (char *) 0;       /* digest code */
+char *cp;
+
+void die_syntax()
+{
+  if (!stralloc_0(&line)) die_nomem();
+  strerr_die5x(100,FATAL,TXT_EZCRONRC," ",ERR_SYNTAX,line.s);
+}
+
+void die_argument()
+{
+  strerr_die2x(100,FATAL,ERR_NOT_CLEAN);
+}
+
+int isclean(addr,flagaddr)
+       /* assures that addr has only letters, digits, "-_" */
+       /* also checks allows single '@' if flagaddr = 1 */
+       /* returns 1 if clean, 0 otherwise */
+  char *addr;
+  int flagaddr;                /* 1 for addresses with '@', 0 for other args */
+{
+  unsigned int pos;
+  register char ch;
+  register char *cp;
+  if (flagaddr) {              /* shoud have one '@' */
+    pos = str_chr(addr,'@');
+    if (!pos || !addr[pos])
+      return 0;                        /* at least 1 char for local */
+    if (!addr[pos+1])
+      return 0;                        /* host must be at least 1 char */
+    pos++;
+    case_lowerb(addr+pos,str_len(addr)-pos);
+  } else
+    pos = 0;
+  pos +=  str_chr(addr + pos,'@');
+  if (addr[pos])               /* but no more */
+    return 0;
+  cp = addr;
+  while ((ch = *(cp++)))
+    if (!(ch >= 'a' && ch <= 'z') &&
+        !(ch >= 'A' && ch <= 'Z') &&
+        !(ch >= '0' && ch <= '9') &&
+        ch != '.' && ch != '-' && ch != '_' && ch != '@')
+      return 0;
+  return 1;
+}
+
+char inbuf[512];
+substdio ssin;
+
+char outbuf[512];
+substdio ssout;
+
+void main(argc,argv)
+int argc;
+char **argv;
+
+{
+  int child;
+  char *sendargs[4];
+  int wstat;
+
+  (void) umask(077);
+  sig_pipeignore();
+
+  while ((opt = getopt(argc,argv,"cCdDi:lLt:w:vV")) != opteof)
+    switch (opt) {
+      case 'c': flagconfig = 1; break;
+      case 'C': flagconfig = 0; break;
+      case 'd': flagdelete = 1; break;
+      case 'D': flagdelete = 0; break;
+      case 'i': scan_ulong(optarg,&deltah); break;
+      case 'l': flaglist = 1; break;
+      case 'L': flaglist = 0; break;
+      case 't':
+                pos = scan_ulong(optarg,&hh);
+                if (!optarg[pos++] == ':') die_usage();
+                pos = scan_ulong(optarg + pos,&mm);
+                break;
+      case 'w':
+                dow = optarg;
+                cp = optarg - 1;
+                while (*(++cp)) {
+                  if (*cp >= '0' && *cp <= '7') {
+                    if (flagdigit) die_dow();
+                    flagdigit = 1;
+                  } else if (*cp == ',') {
+                    if (!flagdigit) die_dow();
+                    flagdigit = 0;
+                  } else
+                    die_dow();
+                }
+                break;
+      case 'v':
+      case 'V': strerr_die2x(100,"ezmlm-cron version: ",EZIDX_VERSION);
+      default:
+                die_usage();
+    }
+  if (flaglist + flagdelete + flagconfig > 1)
+    strerr_die2x(100,FATAL,ERR_EXCLUSIVE);
+  uid = getuid();
+  if (uid && !(euid = geteuid()))
+    strerr_die2x(100,FATAL,ERR_SUID);
+  if (!(ppasswd = getpwuid(uid)))
+    strerr_die2x(100,FATAL,ERR_UID);
+  if (!stralloc_copys(&user,ppasswd->pw_name)) die_nomem();
+  if (!stralloc_0(&user)) die_nomem();
+  if (!(ppasswd = getpwuid(euid)))
+    strerr_die2x(100,FATAL,ERR_EUID);
+  if (!stralloc_copys(&dir.s,ppasswd->pw_dir)) die_nomem();
+  if (!stralloc_0(&dir)) die_nomem();
+  if (!stralloc_copys(&euser,ppasswd->pw_name)) die_nomem();
+  if (!stralloc_0(&euser)) die_nomem();
+
+  if (chdir(dir.s) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir.s,": ");
+
+  local = argv[optind++];      /* list address, optional for -c & -l */
+  if (!local) {
+    if (!flagconfig && !flaglist)
+      die_usage();
+    lenlocal = 0;
+    lenhost = 0;
+  } else {
+    if (!stralloc_copys(&listaddr,local)) die_nomem();
+    if (!isclean(local,1))
+      die_argument();
+    pos = str_chr(local,'@');
+    lenlocal = pos;
+    local[pos] = '\0';
+    host = local + pos + 1;
+    lenhost = str_len(host);
+    code = argv[optind];
+    if (!code) {               /* ignored for -l, -c, and -d */
+      if (flagdelete || flaglist || flagconfig)
+                               /* get away with not putting code for delete */
+        code = "a";    /* a hack - so what! */
+      else
+        die_usage();
+    } else
+      if (!isclean(code,0))
+        die_argument();
+  }
+  if ((fdin = open_read(TXT_EZCRONRC)) == -1)
+    strerr_die6sys(111,FATAL,ERR_OPEN,dir.s,"/",TXT_EZCRONRC,": ");
+       /* first line is special */
+  substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf));
+  if (getln(&ssin,&line,&match,'\n') == -1)
+    strerr_die6sys(111,FATAL,ERR_READ,dir.s,"/",TXT_EZCRONRC,": ");
+
+  if (!match)
+    strerr_die6sys(111,FATAL,ERR_READ,dir.s,"/",TXT_EZCRONRC,": ");
+       /* (since we have match line.len has to be >= 1) */
+  line.s[line.len - 1] = '\0';
+  if (!isclean(line.s,0))       /* host for bounces */
+    strerr_die4x(100,ERR_CFHOST,dir.s,"/",TXT_EZCRONRC);
+  if (!stralloc_copys(&rp,line.s)) die_nomem();
+
+  match = 1;
+  for(;;) {
+    if (!match) break;         /* to allow last line without '\n' */
+    if (getln(&ssin,&line,&match,'\n') == -1)
+    strerr_die6sys(111,FATAL,ERR_READ,dir.s,"/",TXT_EZCRONRC,": ");
+    if (!line.len)
+      break;
+    line.s[line.len-1] = '\0';
+    if (!case_startb(line.s,line.len,user.s))
+      continue;
+    pos = user.len - 1;
+    if (pos >= line.len || line.s[pos] != ':')
+      continue;
+    founduser = 1;              /* got user line */
+    break;
+  }
+  close(fdin);
+  if (!founduser)
+    strerr_die2x(100,FATAL,ERR_BADUSER);
+  
+  if (flagconfig) {
+    line.s[line.len-1] = '\n'; /* not very elegant ;-) */
+    substdio_fdbuf(&ssout,write,1,outbuf,sizeof(outbuf));
+    if (substdio_put(&ssout,line.s,line.len) == -1)
+      strerr_die3sys(111,FATAL,ERR_WRITE,"stdout: ");
+    if (substdio_flush(&ssout) == -1)
+      strerr_die3sys(111,FATAL,ERR_WRITE,"stdout: ");
+    _exit(0);
+  }
+  ++pos;                               /* points to first ':' */
+  len = str_chr(line.s+pos,':');       /* second ':' */
+    if (!line.s[pos + len])
+      die_syntax();
+  if (!local) {                                /* only -d and std left */
+    localmatch = 1;
+    hostmatch = 1;
+  } else {
+    hostmatch = 0;
+    if (len <= str_len(local))
+      if (!str_diffn(line.s+pos,local,len))
+        localmatch = 1;
+  }
+  pos += len + 1;
+  len = str_chr(line.s + pos,':');     /* third */
+  if (!line.s[pos + len])
+    die_syntax();
+  if (local) {                         /* check host */
+    if (len == 0)                      /* empty host => any host */
+      hostmatch = 1;
+    else
+      if (len == str_len(host))
+        if (!case_diffb(line.s+pos,len,host))
+          hostmatch = 1;
+  }
+  pos += len + 1;
+  pos += scan_ulong(line.s+pos,&maxlists);
+  if (line.s[pos]) {                   /* check additional lists */
+    if (line.s[pos] != ':')
+      die_syntax();
+    if (line.s[pos+1+str_chr(line.s+pos+1,':')])
+      die_syntax();    /* reminder lists are not separated by ':'  */
+                       /* otherwise a ':' or arg miscount will die */
+                       /* silently */
+    if (local) {
+      while (++pos < line.len) {
+        len = str_chr(line.s + pos,'@');
+        if (len == lenlocal && !str_diffn(line.s + pos,local,len)) {
+          pos += len;
+          if (!line.s[pos]) break;
+          pos++;
+          len = str_chr(line.s+pos,',');
+            if (len == lenhost && !case_diffb(line.s+pos,len,host)) {
+              listmatch = 1;
+              break;
+            }
+        }
+        pos += len;
+      }
+    }
+  }
+  if (!listmatch) {
+    if (!hostmatch)
+      strerr_die2x(100,FATAL,ERR_BADHOST);
+    if (!localmatch)
+      strerr_die2x(100,FATAL,ERR_BADLOCAL);
+  }
+       /* assemble correct line */
+  if (!flaglist) {
+    if (!stralloc_copyb(&addr,strnum,fmt_ulong(strnum,mm))) die_nomem();
+    if (!stralloc_cats(&addr," ")) die_nomem();
+    dh = 0L;
+    if (deltah <= 3L) dh = deltah;
+    else if (deltah <= 6L) dh = 6L;
+    else if (deltah <= 12L) dh = 12L;
+    else if (deltah <= 24L) dh = 24L;
+    else if (deltah <= 48L) {
+      if (dow[0] == '*') dow = "1,3,5";
+    } else if (deltah <= 72L) {
+      if (dow[0] == '*') dow = "1,4";
+    } else
+    if (dow[0] == '*') dow = "1";
+
+    if (!dh) {
+      if (!stralloc_cats(&addr,"*")) die_nomem();
+    } else {
+      if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,hh))) die_nomem();
+      for (t = hh + dh; t < hh + 24L; t+=dh) {
+        if (!stralloc_cats(&addr,",")) die_nomem();
+        if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,t % 24L))) die_nomem();
+      }
+    }
+    if (!stralloc_cats(&addr," * * ")) die_nomem();
+    if (!stralloc_cats(&addr,dow)) die_nomem();
+    if (!stralloc_cats(&addr," ")) die_nomem();
+    part0start = addr.len;             /* /var/qmail/bin/qmail-inject */
+    if (!stralloc_cats(&addr,auto_qmail)) die_nomem();
+    if (!stralloc_cats(&addr,qmail_inject)) die_nomem();
+    part0len = addr.len - part0start;
+    if (!stralloc_cats(&addr,local)) die_nomem();
+    if (!stralloc_cats(&addr,"-dig-")) die_nomem();
+    if (!stralloc_cats(&addr,code)) die_nomem();
+    if (!stralloc_cats(&addr,"@")) die_nomem();
+    if (!stralloc_cats(&addr,host)) die_nomem();
+               /* feed 'Return-Path: <user@host>' to qmail-inject */
+    if (!stralloc_cats(&addr,"%Return-path: <")) die_nomem();
+    if (!stralloc_cats(&addr,user.s)) die_nomem();
+    if (!stralloc_cats(&addr,"@")) die_nomem();
+    if (!stralloc_cat(&addr,&rp)) die_nomem();
+    if (!stralloc_cats(&addr,">\n")) die_nomem();
+  }
+  if (!stralloc_0(&addr)) die_nomem();
+
+  if (!flaglist) {
+       /* now to rewrite crontab we need to lock */
+    fdlock = open_append("crontabl");
+    if (fdlock == -1)
+      strerr_die4sys(111,FATAL,ERR_OPEN,dir.s,"/crontabl: ");
+    if (lock_ex(fdlock) == -1) {
+      close(fdlock);
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,dir.s,"/crontabl: ");
+    }
+  } /* if !flaglist */
+  if ((fdin = open_read("crontab")) == -1) {
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_READ,dir.s,"/crontab: ");
+  } else
+    substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf));
+  if (flaglist)
+    substdio_fdbuf(&ssout,write,1,outbuf,sizeof(outbuf));
+  else {
+    if ((fdout = open_trunc("crontabn")) == -1)
+      strerr_die4sys(111,FATAL,ERR_WRITE,dir.s,"/crontabn: ");
+    substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof(outbuf));
+  }
+  line.len = 0;
+
+  if (fdin != -1) {
+    for (;;) {
+      if (!flaglist && line.len) {
+        line.s[line.len-1] = '\n';
+        if (substdio_put(&ssout,line.s,line.len) == -1)
+          strerr_die4sys(111,FATAL,ERR_WRITE,dir.s,"/crontabn: ");
+      }
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die4sys(111,FATAL,ERR_READ,dir.s,"/crontab: ");
+      if (!match)
+        break;
+      flagours = 0;                    /* assume entry is not ours */
+      foundlocal = 0;
+      line.s[line.len - 1] = '\0';     /* match so at least 1 char */
+      pos = 0;
+      while (line.s[pos] == ' ' && line.s[pos] == '\t') ++pos;
+      if (line.s[pos] == '#')
+        continue;                      /* cron comment */
+      pos = str_chr(line.s,'/');
+      if (!str_start(line.s+pos,auto_qmail)) continue;
+      pos += str_len(auto_qmail);
+      if (!str_start(line.s+pos,qmail_inject)) continue;
+      pos += str_len(qmail_inject);
+      poslocal = pos;
+      pos = byte_rchr(line.s,line.len,'<');    /* should be Return-Path: < */
+      if (pos == line.len)
+        continue;                      /* not ezmlm-cron line */
+      pos++;
+     len = str_chr(line.s+pos,'@');
+      if (len == user.len - 1 && !str_diffn(line.s+pos,user.s,len)) {
+        flagours = 1;
+        ++nolists;             /* belongs to this user */
+      }
+      if (!local) {
+        foundlocal = 1;
+      } else {
+        pos = poslocal + str_chr(line.s+poslocal,'@');
+        if (pos + lenhost +1 >= line.len) continue;
+        if (case_diffb(line.s+pos+1,lenhost,host)) continue;
+        if (line.s[pos+lenhost+1] != '%') continue;
+                               /* check local */
+        if (poslocal + lenlocal + 5 >= line.len) continue;
+        if (!str_start(line.s+poslocal,local)) continue;
+        pos2 = poslocal+lenlocal;
+        if (!str_start(line.s+pos2,"-dig-")) continue;
+        foundlocal = 1;
+      }
+      if (foundlocal) {
+        foundmatch = 1;
+        if (flaglist && (local || flagours)) {
+          if (substdio_put(&ssout,line.s,line.len) == -1)
+            strerr_die3sys(111,FATAL,ERR_WRITE,"stdout: ");
+          if (substdio_put(&ssout,"\n",1) == -1)
+            strerr_die3sys(111,FATAL,ERR_WRITE,"stdout: ");
+        }
+        line.len = 0;          /* same - kill line */
+        if (flagours)
+          --nolists;
+      }
+    }
+    close(fdin);
+  }
+  if (flaglist) {
+    if (substdio_flush(&ssout) == -1)
+      strerr_die3sys(111,FATAL,ERR_FLUSH,"stdout: ");
+    if (foundmatch)            /* means we had a match */
+      _exit(0);
+    else
+      strerr_die2x(100,FATAL,ERR_NO_MATCH);
+  }
+       /* only -d and regular use left */
+
+  if (nolists >= maxlists && !flagdelete)
+    strerr_die2x(100,FATAL,ERR_LISTNO);
+  if (!flagdelete)
+    if (substdio_put(&ssout,addr.s,addr.len-1) == -1)
+      strerr_die4sys(111,FATAL,ERR_WRITE,dir.s,"/crontabn: ");
+  if (flagdelete && !foundlocal)
+    strerr_die2x(111,FATAL,ERR_NO_MATCH);
+  if (substdio_flush(&ssout) == -1)
+    strerr_die4sys(111,FATAL,ERR_FLUSH,dir.s,"/crontabn: ");
+  if (fsync(fdout) == -1)
+    strerr_die4sys(111,FATAL,ERR_SYNC,dir.s,"/crontabn++: ");
+  if (close(fdout) == -1)
+    strerr_die4sys(111,FATAL,ERR_CLOSE,dir.s,"/crontabn: ");
+  if (rename("crontabn","crontab") == -1)
+    strerr_die4sys(111,FATAL,ERR_MOVE,dir.s,"/crontabn: ");
+  sendargs[0] = "sh";
+  sendargs[1] = "-c";
+
+  if (!stralloc_copys(&line,auto_cron)) die_nomem();
+  if (!stralloc_cats(&line,"/crontab '")) die_nomem();
+  if (!stralloc_cats(&line,dir.s)) die_nomem();
+  if (!stralloc_cats(&line,"/crontab'")) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  sendargs[2] = line.s;
+  sendargs[3] = 0;
+  switch(child = fork()) {
+      case -1:
+        strerr_die2sys(111,FATAL,ERR_FORK);
+      case 0:
+        if (setreuid(euid,euid) == -1)
+          strerr_die2sys(100,FATAL,ERR_SETUID);
+        execvp(*sendargs,sendargs);
+        if (errno == error_txtbsy || errno == error_nomem ||
+            errno == error_io)
+          strerr_die4sys(111,FATAL,ERR_EXECUTE,sendargs[2],": ");
+        else
+          strerr_die4sys(100,FATAL,ERR_EXECUTE,sendargs[2],": ");
+  }
+         /* parent */
+  wait_pid(&wstat,child);
+  if (wait_crashed(wstat))
+    strerr_die2x(111,FATAL,ERR_CHILD_CRASHED);
+  switch(wait_exitcode(wstat)) {
+      case 0:
+        _exit(0);
+      default:
+        strerr_die2x(111,FATAL,ERR_CRONTAB);
+  }
+}
diff --git a/ezmlm-gate.1 b/ezmlm-gate.1
new file mode 100644 (file)
index 0000000..cd45cd5
--- /dev/null
@@ -0,0 +1,116 @@
+.TH ezmlm-gate 1
+.SH NAME
+ezmlm-gate \- Gate posts depending on message SENDER
+.SH SYNOPSIS
+.B ezmlm-gate [-cCmMpPrRsSvV] [-q file]
+.I dir [moddir1] [moddir2 ...]
+.SH DESCRIPTION
+.B ezmlm-gate
+checks if SENDER is in it least one of the subscriber lists
+with base directory
+.IR moddir1 ,
+.IR moddir2 ,
+etc.
+If it is, the message is posted via
+.B ezmlm-send 
+to the list in
+.IR dir .
+If not, the message is sent for moderation via
+.BR ezmlm-store .
+
+The default is to send the message for moderation.
+.SH OPTIONS
+.TP
+.B \-cCmMpPrRsS
+Passed on to ezmlm-store(1) and ezmlm-send(1).
+.TP
+.B \-cCrR
+Passed on to ezmlm-send(1).
+.TP
+.B \-q\fI file
+Execute arbitration programs in
+.IR file .
+Lines in
+.I file
+are executed just like in regular
+.IR .qmail
+files with the difference that all lines are assumed to be program
+names. See
+.BR dot-qmail(5) .
+Programs are executed by /bin/sh and the message is on stdin of the
+executed program.
+The leading ``|'' is optional. Comments and blank lines are allowed. If a
+program exits 111, delivery is deferred. If it exits 99, the message is
+sent to the list. If it exits 0, the next line is executed. If it exits
+with any other exit code, the message is sent for moderation. Subscriber
+status per
+.IR moddir1 ,
+.IR moddir2 ,
+etc, is tested only if all the programs have been executed and the final
+exit code is 0. Thus, programs can cause moderation (100), posting (99), or
+defer the decision to the next program and ultimately to subscriber status.
+.TP
+.B \-v
+Display version information.
+.TP
+.B \-V
+Display version information.
+
+.SH USAGE
+.B ezmlm-gate
+is best used if you want to restrict posts to a set of addresses using
+SENDER checks. Obviously, this is not secure, but it 
+can help quite a bit to keep
+garbage off the list. For more secure setups, see
+.BR ezmlm-store(1) .
+For other arbitration such as SPAM protection, use the
+.B \-q
+option. For instance, invoking
+.B ezmlm-reject(1)
+here would cause failing messages to be sent for moderation rather that being
+rejected.
+
+Set up the list with a
+.B ezmlm-gate
+line in
+.I dir\fB/editor
+and touch
+.IR dir\fB/modpost .
+Add the moderator(s) (usually the list owner):
+
+.EX
+.B ezmlm-sub
+.I dir\fB/mod
+moderator@host
+.EE
+
+This will via
+.B ezmlm-send
+directly distribute
+all posts from subscriber addresses and send out the rest for moderation
+to the moderator(s) via
+.BR ezmlm-store .
+To test several subscriber databases, e.g. the list and the list-digest
+subscribers, add the corresponding list directories to the
+.B ezmlm-gate
+command line.
+
+This can be expanded to include users that post from addresses other than
+the one they are subscribed as: just create another directory 'addl', and
+a 'subscribers' subdirectory of it, and add 'addl' to the
+.B ezmlm-gate
+command line. Now just add all problem addresses to the 'addl' address
+database using
+.BR ezmlm-sub .
+The entire point is that post from subscribers of any union
+of list go through, posts from identified 'aliases' go through and all
+others go for approval to the list owner. Thus, legitimate users are never
+rejected and 'aliases' rapidly identified by the owner/moderator and
+their handling automated one by one, by simply adding them to 'addl'.
+.SH "SEE ALSO"
+dot-qmail(5),
+ezmlm(5),
+ezmlm-issubn(1),
+ezmlm-reject(1),
+ezmlm-send(1),
+ezmlm-store(1)
diff --git a/ezmlm-gate.c b/ezmlm-gate.c
new file mode 100644 (file)
index 0000000..25d9b59
--- /dev/null
@@ -0,0 +1,213 @@
+/*$Id: ezmlm-gate.c,v 1.18 1999/10/09 16:49:56 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include "stralloc.h"
+#include "strerr.h"
+#include "error.h"
+#include "env.h"
+#include "sig.h"
+#include "str.h"
+#include "seek.h"
+#include "fork.h"
+#include "wait.h"
+#include "exit.h"
+#include "getconf.h"
+#include "auto_bin.h"
+#include "sgetopt.h"
+#include "errtxt.h"
+#include "idx.h"
+#include "subscribe.h"
+
+#define FATAL "ezmlm-gate: fatal: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-gate: usage: ezmlm-gate [-cCmMpPqrRsSvV] "
+                       "dir [moddir [...]]");
+}
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+stralloc line = {0};
+stralloc cmds = {0};
+stralloc send = {0};
+stralloc sendopt = {0};
+stralloc storeopt = {0};
+void *psql = (void *) 0;
+
+char szchar[2] = "-";
+  char *sendargs[4];
+  int child;
+  int wstat;
+  char *pmod;
+
+int mailprog(s)
+  char *s;
+{
+    int r;
+
+    sendargs[0] = "/bin/sh";   /* 100 perm error, 111 temp, 99 dom ok */
+    sendargs[1] = "-c";                /* 0 rec ok, others bounce */
+    sendargs[2] = s;
+    sendargs[3] = (char *)0;
+    switch(child = fork()) {
+      case -1:
+       strerr_die2sys(111,FATAL,ERR_FORK);
+      case 0:
+       execv(*sendargs,sendargs);
+       if (errno == error_txtbsy || errno == error_nomem ||
+         errno == error_io)
+               strerr_die5sys(111,FATAL,ERR_EXECUTE,
+                 "/bin/sh -c ",sendargs[2],": ");
+        else
+               strerr_die5sys(100,FATAL,ERR_EXECUTE,
+                 "/bin/sh -c ",sendargs[2],": ");
+    }
+         /* parent */
+    wait_pid(&wstat,child);
+    if (wait_crashed(wstat))
+       strerr_die2x(111,FATAL,ERR_CHILD_CRASHED);
+    switch((r = wait_exitcode(wstat))) {
+      case 0: case 99: case 100: break;
+      case 111:                                        /* temp error */
+        strerr_die2x(111,FATAL,ERR_CHILD_TEMP);
+      default:
+        strerr_die2x(100,FATAL,ERR_REJECT);    /* other errors => bounce */
+    }
+    if (seek_begin(0) == -1)                   /* rewind */
+      strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+    return r;
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *sender;
+  char *moddir;
+  char *queryext = (char *) 0;
+  int opt;
+  int ret = 0;
+  unsigned int i,j,k;
+
+  umask(022);
+  sig_pipeignore();
+       /* storeopts to ezmlm-store only. Others to both (ezmlm-store may */
+       /* pass them on to ezmlm-send. */
+  if (!stralloc_copys(&sendopt," -")) die_nomem();
+  if (!stralloc_copys(&storeopt," -")) die_nomem();
+
+  while ((opt = getopt(argc,argv,
+      "cCmMpPq:Q:sSrRt:T:vV")) != opteof)
+    switch(opt) {      /* pass on unrecognized options */
+      case 'c':                        /* ezmlm-send flags */
+      case 'C':
+      case 'r':
+      case 'R':
+        szchar[0] = opt;
+        if (!stralloc_append(&sendopt,szchar)) die_nomem();
+        break;
+      case 'm':                        /* ezmlm-store flags */
+      case 'M':
+      case 'p':
+      case 'P':
+      case 's':
+      case 'S':
+        szchar[0] = opt;
+        if (!stralloc_append(&storeopt,szchar)) die_nomem();
+        break;
+      case 'q':                        /* allow both qQ to be nice */
+      case 'Q': if (optarg) queryext = optarg; break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-gate version: ",EZIDX_VERSION);
+      default:                 /* ezmlm-store flags */
+        die_usage();
+    }
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  sender = env_get("SENDER");
+
+  pmod = (char *) 0;
+
+  if (queryext) {
+    getconf(&cmds,queryext,1,FATAL,dir);
+    i = 0;
+    for (j = 0;j < cmds.len; ++j)
+      if (!cmds.s[j]) {
+       switch (cmds.s[i]) {
+         case '\0': case '#': break;   /* ignore blank/comment */
+         case '|':
+               ret = mailprog(cmds.s + i + 1); break;
+         default:
+               ret = mailprog(cmds.s + i); break;
+       }
+       if (ret) break;
+       i = j + 1;
+    }
+    if (!ret || ret == 99)             /* 111 => temp error */
+      pmod = "";                       /* 0, 99 => post */
+                                       /* other => moderate */
+  }
+  moddir = argv[optind++];
+  if (moddir && !ret) {                        /* if exit 0 and moddir, add issub */
+    pmod = (char *) 0;
+    while (moddir && !pmod && sender) {
+      pmod = issub(moddir,sender,(char *) 0,FATAL);
+      closesql();
+      moddir = argv[optind++];
+    }
+  }
+
+  sendargs[0] = "sh";
+  sendargs[1] = "-c";
+  if (!stralloc_copys(&send,auto_bin)) die_nomem();
+  if (pmod) {
+    if (!stralloc_cats(&send,"/ezmlm-send")) die_nomem();
+    if (sendopt.len > 2)
+      if (!stralloc_cat(&send,&sendopt)) die_nomem();
+
+  } else {
+    if (!stralloc_cats(&send,"/ezmlm-store")) die_nomem();
+    if (storeopt.len > 2)
+      if (!stralloc_cat(&send,&storeopt)) die_nomem();
+    if (sendopt.len > 2)
+      if (!stralloc_cat(&send,&sendopt)) die_nomem();
+  }
+  if (!stralloc_cats(&send," '")) die_nomem();
+  if (!stralloc_cats(&send,dir)) die_nomem();
+  if (!stralloc_cats(&send,"'")) die_nomem();
+  if (!stralloc_0(&send)) die_nomem();
+  sendargs[2] = send.s;
+  sendargs[3] = 0;
+
+  switch(child = fork()) {
+    case -1:
+      strerr_die2sys(111,FATAL,ERR_FORK);
+    case 0:
+      execvp(*sendargs,sendargs);
+      if (errno == error_txtbsy || errno == error_nomem ||
+          errno == error_io)
+        strerr_die4sys(111,FATAL,ERR_EXECUTE,sendargs[2],": ");
+      else
+        strerr_die4sys(100,FATAL,ERR_EXECUTE,sendargs[2],": ");
+   }
+         /* parent */
+   wait_pid(&wstat,child);
+   if (wait_crashed(wstat))
+     strerr_die2x(111,FATAL,ERR_CHILD_CRASHED);
+   switch(wait_exitcode(wstat)) {
+     case 100:
+       strerr_die2x(100,FATAL,ERR_CHILD_FATAL);
+     case 111:
+        strerr_die2x(111,FATAL,ERR_CHILD_TEMP);
+     case 0:
+       _exit(0);
+     default:
+       strerr_die2x(111,FATAL,ERR_CHILD_UNKNOWN);
+   }
+}
+
diff --git a/ezmlm-get.1 b/ezmlm-get.1
new file mode 100644 (file)
index 0000000..12c4e3b
--- /dev/null
@@ -0,0 +1,464 @@
+.TH ezmlm-get 1
+.SH NAME
+ezmlm-get \- handles mailing list archive retrieval and digests
+.SH SYNOPSIS
+.B ezmlm-get
+[
+.B \-bBcCpPsSvV
+][
+.B \-f
+.I format
+]
+.I dir
+[
+.I digestcode[f]
+]
+.SH OPTIONS
+.TP
+.B \-b
+(Default.)
+Copy administrative information and the request to the bottom of replies.
+This informs the recipient of other commands, and allows some error tracking
+in case the recipient did not originate the request.
+.TP
+.B \-B
+Suppress the normal administrative information and request copy. This may make
+it harder for the recipient to diagnose problems and learn commands.
+.TP
+.B \-c
+(Default.)
+Process and reply to commands (does not affect digests).
+.TP
+.B \-C
+Ignore all commands except digest.
+.TP
+.B \-f \fIformat
+.B ezmlm-get
+will use
+.I format
+as the default format for all returned message collections. The default
+is 'm' for MIME with a header subset (see below). Format specifiers
+send with individual requests override the default set with the
+.B \-f
+switch.
+.TP
+.B \-p
+\-get, \-index, and \-thread commands are available to all users,
+provided other flags are permissive. This overrides normal behavior,
+which is to allow archive retrieval only to moderators, when
+.I dir\fB/public
+does not exist. This is useful to set up non-public lists that still give
+users archive access.
+.TP
+.B \-P
+\-get, \-index, and \-thread commands are available
+only to moderators, even if
+.I dir\fB/public
+exists. The
+.B \-C
+and
+.B \-s
+flags can restrict this further. This is useful for public lists with
+archive retrieval restricted to a subset of users (moderators).
+.TP
+.B \-s
+\-get, \-index, and \-thread requests are processed only if
+.B SENDER
+is a subscriber.
+.TP
+.B \-S
+(Default.)
+Anyone can issue \-get, \-index, and \-thread requests.
+.TP
+.B \-v
+Print version info.
+.TP
+.B \-V
+Print version info.
+.SH DESCRIPTION
+.B ezmlm-get
+handles archive retrieval and optionally makes and sends out
+digests for the mailing list
+stored in
+.IR dir .
+Subscribers of the digest list are stored in
+.IR dir\fB/digest/subscribers/ .
+
+The contents of
+.I dir\fB/headeradd
+are added to the header of outgoing messages.
+
+.B ezmlm-get
+is normally invoked from a
+.B .qmail(7)
+file.
+
+It reads a mail message from its standard input,
+and a mail envelope from the
+.BR SENDER ,
+.BR LOCAL ,
+and
+.BR HOST
+environment variables.
+
+.B ezmlm-get
+uses
+.B LOCAL
+to determine where it is invoked. If
+.B LOCAL
+is the list local name only,
+.B ezmlm-get
+assumes it is run from
+.I dir\fB/editor
+to produce a digest.
+The digest is sent directly to the digest list subscribers.
+
+If
+.B LOCAL
+is empty or undefined,
+.B ezmlm-get
+assumes it is run from the command line or a script. In this case
+it behaves as if run from
+.I dir\fB/editor
+and sends out a digest to the digest subscribers.
+
+Otherwise,
+.B ezmlm-get
+expects
+.B LOCAL
+to be of the form
+.IR list\fB-\fIaction .
+Here
+.I list
+is the first line of
+.IR dir\fB/inlocal
+and
+.I action
+is a request.
+The output is sent to the envelope sender.
+
+.BR ezmlm-get
+checks
+.I action
+for
+.BR dig\.\fIdigestcode ,
+.BR index ,
+.BR thread ,
+and
+.BR get .
+If 
+.I action
+is one of these,
+.B ezmlm-get
+handles the request and sends a reply. If successful, it
+exits 99 (ignore remaining
+.B .qmail(7)
+file entries).
+If
+.I action
+is not one of these,
+.B ezmlm-get
+exits 0 (success) to pass the message on to later handlers,
+such as
+.BR ezmlm-manage(1) .
+
+.B ezmlm-get
+expects
+.B HOST
+to match the first line of
+.IR dir\fB/inhost .
+
+.BR ezmlm-dig\.\fIdigestcode
+returns a digest of messages received since the last digest, unless
+numerical arguments are given.
+.I digestcode
+must be alphanumeric, and match (case-insensitive)
+.I digestcode
+on the
+.B ezmlm-get
+command line. Otherwise, the request will be ignored. This is to restrict
+digest creation. The body of the requesting message up to the first line
+starting with '-' is copied into the
+.I administrivia 
+section of the digest. This is followed by the contents of
+.IR dir\fB/text/digest ,
+if this file exists.
+
+.B Note:
+Anyone who can read your
+.I dir\fB/manager
+file, digest-requesting scripts, or mail log knows the
+.I digestcode
+and can trigger digests.
+
+.B ezmlm-get
+copies
+.I dir\fB/mailinglist
+into a
+.B Mailing-List
+field in its response.
+If the incoming message has a
+.B Mailing-List
+field,
+.B ezmlm-get
+refuses to respond.
+.B ezmlm-get
+also refuses to respond to bounce messages.
+
+If
+.I dir\fB/listid
+exists,
+.B ezmlm-get
+will assume that the format is correct and
+create a ``List-ID:'' header by placing the contents after the
+text ``List-ID: ''. 
+
+If
+.I dir\fB/qmqpservers
+exists,
+.B ezmlm-get will use
+.B qmail-qmqp(1)
+to send digests. Other messages are sent by the normal qmail mechanism.
+
+If
+.I dir\fB/public
+does not exist,
+.B ezmlm-get
+rejects all archive retrieval attempts, unless the
+.B \-p
+command line switch is used.
+
+Archive retrieval actions can be of the form
+.BR action[f] , 
+.BR action[f].\fInum 
+or 
+.BR action[f].\fInum_num2 ,
+where 
+.I num
+is the message number for the action or
+.I num_num2
+the range of message numbers for the action.
+
+.B f
+is an optional format specifier for
+.IR \-get ,
+.IR \-thread ,
+and
+.I \-dig
+requests. It is allowed, but ignored for
+.I \-index
+requests. Currently, the following are allowed:
+
+.TP
+.B r
+rfc1153. This is a ``plain'' non-MIME format for dumb clients.
+.TP
+.B m
+(Default.) MIME
+.I multipart/digest 
+with a subset of ordered headers sorted.
+Currently, the following headers are
+included in the order listed:
+Date:,
+To:,
+From:,
+Reply-To:,
+Cc:,
+MIME-Version:,
+Content-Type:,
+Message-ID:,
+and Keywords:.
+This can be customized with the optional file
+.IR dir\fB/digheaders ,
+which should contain the desired headers up to but not including the colon.
+
+The format is no longer compliant
+with rfc1153, as the rfc1153 format is incompatible with rfc2046, which
+which the format is (should be) compatible.
+.TP
+.B x
+MIXED: This is the same as the default MIME
+format, except that the Content-Type is
+.IR multipart/mixed .
+This helps circumnavigate a Pine bug: when the digest is
+content-transfer-encoded, Pine will refuse to display the initial
+text/plain part of a 
+.I multipart/digest
+message, but display the same part of a
+.I multipart/mixed
+message. Some MUAs for some strange reason treat the two multipart formats
+differently. In some cases, ``x'' works better than ``m''.
+.TP
+.B v
+VIRGIN: This is MIME
+.I multipart/digest 
+with messages returned without any header filtering.
+.TP
+.B n
+NATIVE: This is VIRGIN format without threading, i.e. messages are
+presented in numerical order and the message index is suppressed.
+
+.PP
+For flexibility and backwards compatibility, the '.' separating the action from
+the first argument can be replaced by '\-',
+or omitted.
+Any non-alphanumeric character can separate
+.I num2
+from
+.IR num .
+.PP
+
+If
+.I action
+is
+.IR dig.digestcode ,
+.B ezmlm-get
+returns a digest of the messages received since the last digest, and updates
+the digest issue counter.
+
+If
+.I action
+is
+.IR get ,
+.B ezmlm-get
+sends back message(s)
+.I num
+or
+.I num
+through
+.IR num2 .
+from
+.IR dir\fB/archive/ .
+If
+.I num
+is omitted and
+.I dir\fB/dignum
+does not exist or is 0, the latest HISTGET message (default 30) are
+returned. Otherwise,
+the messages since the latest digest are returned including the last
+message in that digest, so that always at least 1 message is send. If the
+number of messages
+exceeds MAXGET (default 100), only the MAXGET last messages are returned.
+if
+.I num
+is greater than the latest message in the archive _and_
+.I num2
+is specified, the latest messages back to HISTGET before the end of the
+latest digest up to MAXGET messages are returned. This is a good way of
+always getting at least the latest 30 messages without knowing the latest
+message number. A link with such a command could be put into e.g.
+.IR dir\fB/text/sub-ok .
+
+.I num
+and
+.I num2
+are adjusted to make both > 0, and
+.I num2
+>=
+.IR num .
+If either is greater than
+the largest message number processed, it is silently
+set to the largest message number.
+At most 100 messages are
+returned.
+
+If
+.I action
+is
+.BI index ,
+.B ezmlm-get
+sends back the subjects and authors of the message(s)
+.I num
+or
+.IR num
+through
+.I num2
+in sets of 100 from
+.IR dir\fB/archive/ .
+.I num
+and
+.I num2
+are reasonable adjusted as for 'get'. No warnings are
+sent. At most 20 sets of 100 message entries are returned per request. If
+.I num
+is omitted,
+.B ezmlm-get
+returns the last 100-200 message entries, which automatically gives
+information about the last message number.
+
+If
+.I action
+is
+.BI thread ,
+.B ezmlm-get
+sends back the message(s) that have an index subject entry identical to
+that of message
+.I num 
+from
+.IR dir\fB/archive/ .
+
+If
+.I num2
+is given it is ignored. If
+.I num
+is out of range, and error
+message is returned. The message range scanned for the subject is limited
+to 2000 messages before and after the master message, i.e. the
+.BR thread
+argument.
+This limit protects very large archives.
+Most threads are expected to be considerably more short-lived.
+In the unlikely event that there are further messages,
+these can be retrieved by a second request for the 
+highest/lowest message returned in the first request.
+.SH "CHARACTER SETS"
+If
+.I dir\fB/charset
+exists,
+.B ezmlm-get
+will use the character set listed for all messages. Otherwise, the
+default ``us-ascii'' will be used. The character set can be suffixed
+by ``:'' followed by a code. If the code is ``Q'', outgoing messages are 
+sent as ``Quoted-Printable'', if it is ``B'' they are sent ``base64'' encoded.
+Otherwise, text is sent as is.
+.SH "FILES"
+.TP
+.I dir\fB/dignum
+The last message included in the latest normal mode digest.
+.TP
+.I dir\fB/digissue
+The issue number of the latest normal mode digest.
+.TP
+.I dir\fB/text/get-bad
+Returned if a/the message cannot be found.
+.TP
+.I dir\fB/text/digest
+Copied into the
+.I Administrivia
+section of digests after the body of the requesting message.
+.TP
+.I dir\fB/charset
+The character set used for all
+.B ezmlm-get
+messages (see above).
+If not present, the default, ``us-ascii'', is used without encoding.
+.SH BUGS
+The digest format per rfc2046
+should (but is not required to) be multipart/mixed
+with the table-of-contents a text/plain part, and the entire remainder of
+the digest a multipart/digest part. The multipart/digest in turn should 
+contain all the messages. Many
+MUA's fail to split out the individual messages from such a hierarchy, so the
+format used by
+.B ezmlm-get
+is a simple multipart/digest, explicitly typing the table-of-contents
+to text/plain, with the ``x'' format changing the mail content-type to
+multipart/mixed.
+.SH "SEE ALSO"
+ezmlm-make(1),
+ezmlm-manage(1),
+ezmlm-send(1),
+ezmlm(5),
+qmail-command(8),
+qmail-qmqp(1)
+
diff --git a/ezmlm-get.c b/ezmlm-get.c
new file mode 100644 (file)
index 0000000..f8146ac
--- /dev/null
@@ -0,0 +1,1459 @@
+/*$Id: ezmlm-get.c,v 1.113 1999/11/22 01:47:45 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "alloc.h"
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "sig.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "getln.h"
+#include "case.h"
+#include "qmail.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "seek.h"
+#include "quote.h"
+#include "datetime.h"
+#include "now.h"
+#include "date822fmt.h"
+#include "fmt.h"
+#include "sgetopt.h"
+#include "cookie.h"
+#include "makehash.h"
+#include "copy.h"
+#include "constmap.h"
+#include "subscribe.h"
+#include "idxthread.h"
+#include "idx.h"
+#include "mime.h"
+#include "errtxt.h"
+
+int flagdo = 1;                        /* React to commands (doesn't affect -dig)*/
+int flagbottom = 1;            /* copy text/bottom + request */
+int flagpublic = 2;            /* 0 = non-public, 1 = public, 2 = respect*/
+                               /* dir/public. */
+char flagcd = '\0';            /* default: don't use quoted-printable */
+int flagsub = 0;               /* =1 subscribers only for get/index/thread */
+char *digsz =
+               "from\\to\\subject\\reply-to\\date\\message-id\\cc\\"
+               "mime-version\\content-type\\content-transfer-encoding";
+
+#define FATAL "ezmlm-get: fatal: "
+
+void die_usage() {
+  strerr_die1x(100,
+    "ezmlm-get: usage: "
+       "ezmlm-get [-bBcClLpPsSvV] [-f fmt] [digestcode]");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_badaddr()
+{
+  strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+
+stralloc inhost = {0};
+stralloc outhost = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+stralloc listname = {0};
+stralloc mailinglist = {0};
+stralloc qmqpservers = {0};
+stralloc fn = {0};
+stralloc moddir = {0};
+stralloc charset = {0};
+stralloc mydtline = {0};
+stralloc digheaders = {0};
+stralloc seed = {0};
+struct constmap digheadersmap;
+
+char schar[] = "00_";
+stralloc listno = {0};
+void *psql = (void *) 0;
+
+datetime_sec when;
+struct datetime dt;
+unsigned long cumsize = 0L;    /* cumulative msgs / 256 */
+unsigned long cumsizen = 0L;   /* new cumulative msgs / 256 */
+unsigned long max = 0L;                /* Last message in archive */
+unsigned long msgsize = 0L;    /* for digest accounting */
+datetime_sec digwhen;          /* last digest */
+
+char strnum[FMT_ULONG];
+char szmsgnum[FMT_ULONG];
+char date[DATE822FMT];
+char boundary[COOKIE];
+char hashout[COOKIE];
+stralloc line = {0};
+stralloc line2 = {0};
+stralloc qline = {0};
+stralloc quoted = {0};
+stralloc msgnum = {0};
+stralloc num = {0};
+stralloc subject = {0};
+
+/* for copy archive */
+stralloc archdate = {0};
+stralloc archfrom = {0};
+stralloc archto = {0};
+stralloc archcc = {0};
+stralloc archsubject = {0};
+stralloc archmessageid = {0};
+stralloc archkeywords = {0};
+stralloc archblanklines = {0};
+char archtype=' ';
+
+/* for mods on non-public lists (needed for future fuzzy sub dbs) */
+stralloc mod = {0};            /* moderator addr for non-public lists */
+char *pmod = (char *) 0;       /* pointer to above */
+
+/* for digest */
+stralloc ddir = {0};
+stralloc edir = {0};
+
+int act = AC_NONE;             /* Action we do */
+int flageditor = 0;            /* if we're invoked for within dir/editor */
+struct stat st;
+
+int flaglocked = 0;            /* if directory is locked */
+int flagarchived;              /* if list is archived */
+int flagindexed;               /* if list is indexed */
+int flagq = 0;                 /* don't use 'quoted-printable' */
+
+char *dir;
+char *workdir;
+char *sender;
+char *digestcode;
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+
+int subto(s,l)
+char *s;
+unsigned int l;
+{
+  qmail_put(&qq,"T",1);
+  qmail_put(&qq,s,l);
+  qmail_put(&qq,"",1);
+  return (int) l;
+}
+
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+
+substdio ssnum;
+char numbuf[16];
+
+substdio sstext;
+char textbuf[1024];
+
+substdio ssindex;
+char indexbuf[1024];
+
+int fdlock;
+
+void lockup()
+/* lock unless locked */
+{
+  if(!flaglocked) {
+    fdlock = open_append("lock");
+    if (fdlock == -1)
+      strerr_die2sys(111,FATAL,ERR_OPEN_LOCK);
+    if (lock_ex(fdlock) == -1) {
+      close(fdlock);
+      strerr_die2sys(111,FATAL,ERR_OBTAIN_LOCK);
+    }
+    flaglocked = 1;
+  }
+}
+
+void unlock()
+/* unlock if locked */
+{
+  if (flaglocked) {
+    close(fdlock);
+    flaglocked = 0;
+  }
+}
+
+void code_qput(s,n)
+char *s;
+unsigned int n;
+{
+    if (!flagcd)
+      qmail_put(&qq,s,n);
+    else {
+      if (flagcd == 'B')
+        encodeB(s,n,&qline,0,FATAL);
+      else
+        encodeQ(s,n,&qline,FATAL);
+      qmail_put(&qq,qline.s,qline.len);
+      msgsize += qline.len;
+    }
+}
+
+void transferenc()
+{
+       if (flagcd) {
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"Quoted-printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        } else
+          qmail_puts(&qq,"\n\n");
+}
+
+void zapnonsub(szerr)
+/* fatal error if flagsub is set and sender is not a subscriber */
+/* expects the current dir to be the list dir. Error is szerr */
+/* added check for undefined sender as a precaution */
+char *szerr;
+{
+  if (sender && *sender) {     /* "no sender" is not a subscriber */
+    if (!flagsub)
+      return;
+    if (issub(dir,sender,(char *) 0,FATAL))
+      return;          /* subscriber */
+    if (issub(ddir.s,sender,(char *) 0,FATAL))
+      return;          /* digest subscriber */
+    if (issub(edir.s,sender,(char *) 0,FATAL))
+      return;          /* allow addresses */
+  }
+  strerr_die4x(100,FATAL,ERR_SUBSCRIBER_CAN,szerr,ERR_571);
+}
+
+void tosender()
+{
+  qmail_puts(&qq,"To: ");
+  if (!quote2(&quoted,sender)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"\n");
+}
+
+void get_num()
+{
+/* read dir/num -> max. max/cumsizen left alone if not present */
+/* Both of these should have been initialized to 0L */
+
+  unsigned int pos;
+  if (getconf_line(&num,"num",0,FATAL,dir)) {
+    if(!stralloc_0(&num)) die_nomem();
+    pos = scan_ulong(num.s,&max);
+    if (num.s[pos] == ':') pos++;
+    scan_ulong(num.s+pos,&cumsizen);
+  }
+}
+
+unsigned long dignum()
+{
+/* return dignum if exists, 0 otherwise. */
+
+  unsigned long retval;
+  if (!stralloc_copys(&num,"")) die_nomem();   /* zap */
+  getconf_line(&num,"dignum",0,FATAL,dir);
+  if(!stralloc_0(&num)) die_nomem();
+  scan_ulong(num.s,&retval);
+  return retval;
+}
+
+void write_ulong(num,cum,dat,fn,fnn)
+/* write num to "fnn" add ':' & cum if cum <>0, then move "fnn" to "fn" */
+char *fn, *fnn;
+unsigned long num,cum,dat;
+{
+  int fd;
+
+  fd = open_trunc(fnn);
+  if (fd == -1)
+     strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fnn,": ");
+  substdio_fdbuf(&ssnum,write,fd,numbuf,sizeof(numbuf));
+  if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,num)) == -1)
+     strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": ");
+  if (substdio_puts(&ssnum,":") == -1)
+     strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": ");
+  if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,cum)) == -1)
+     strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": ");
+  if (dat) {
+    if (substdio_puts(&ssnum,":") == -1)
+       strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": ");
+    if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,dat)) == -1)
+       strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": ");
+  }
+  if (substdio_puts(&ssnum,"\n") == -1)
+     strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": ");
+  if (substdio_flush(&ssnum) == -1)
+     strerr_die6sys(111,FATAL,ERR_FLUSH,dir,"/",fnn,": ");
+  if (fsync(fd) == -1)
+     strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",fnn,": ");
+  if (close(fd) == -1)
+     strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",fnn,": ");
+  if (rename(fnn,fn) == -1)
+     strerr_die4sys(111,FATAL,ERR_MOVE,fnn,": ");
+}
+
+void normal_bottom(format)
+char format;
+/* Copies bottom text and the original message to the new message */
+{
+  if (flagbottom) {
+    copy(&qq,"text/bottom",flagcd,FATAL);
+    if (flagcd && format != RFC1153) {
+      if (flagcd == 'B') {
+       encodeB("",0,&line,2,FATAL);    /* flush */
+       qmail_put(&qq,line.s,line.len);
+      }
+      qmail_puts(&qq,"\n--");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"\nContent-Type: message/rfc822");
+      qmail_puts(&qq,"\nContent-Disposition: inline; filename=request.msg\n\n");
+    }
+    qmail_puts(&qq,"Return-Path: <");
+    if (!quote2(&quoted,sender)) die_nomem();
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,">\n");
+    if (seek_begin(0) == -1)
+      strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+    if (substdio_copy(&ssqq,&ssin2) != 0)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+  } else {
+    if (flagcd == 'B' && format != RFC1153) {
+      encodeB("",0,&line,2,FATAL);     /* flush */
+      qmail_put(&qq,line.s,line.len);
+    }
+  }
+}
+
+void presub(from,to,subject,factype,format)
+/* Starts within header, outputs "subject" and optional headers, terminates*/
+/* header and handles output before table-of-contents                      */
+unsigned long from,to;
+stralloc *subject;
+int factype;           /* action type (AC_THREAD, AC_GET, AC_DIGEST) */
+char format;           /* output format type (see idx.h) */
+{
+  qmail_puts(&qq,"MIME-Version: 1.0\n");
+  switch(format) {
+    case MIME:
+    case VIRGIN:
+    case NATIVE:
+    case MIXED:
+        qmail_puts(&qq,"Content-Type: multipart/");
+        if (format == MIXED)
+         qmail_puts(&qq,"mixed");
+        else
+          qmail_puts(&qq,"digest");
+       qmail_puts(&qq,"; boundary=");
+        qmail_put(&qq,boundary,COOKIE);
+       qmail_puts(&qq,"\nSubject: ");
+       qmail_put(&qq,subject->s,subject->len);
+       qmail_puts(&qq,"\n\n\n--");
+        qmail_put(&qq,boundary,COOKIE);
+        qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+       qmail_puts(&qq,charset.s);
+       transferenc();  /* content-transfer-enc header if needed */
+        qmail_puts(&qq,"\n");
+       break;
+    case RFC1153:
+       qmail_puts(&qq,"Content-type: text/plain; charset=");
+       qmail_puts(&qq,charset.s);
+       qmail_puts(&qq,"\nSubject: ");
+       qmail_put(&qq,subject->s,subject->len);
+       qmail_puts(&qq,"\n\n");
+       flagcd = '\0';  /* We make 8-bit messages, not QP/bas64 for rfc1153 */
+        break;         /* Since messages themselves aren't encoded */
+    }
+    if (!stralloc_cats(subject,"\n\n")) die_nomem();
+    code_qput(subject->s,subject->len);
+    if (format != NATIVE && factype != AC_THREAD && factype != AC_INDEX) {
+      if (!stralloc_copys(&line,TXT_TOP_TOPICS)) die_nomem();
+      if (!stralloc_cats(&line,TXT_TOP_MESSAGES)) die_nomem();
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,from))) die_nomem();
+      if (!stralloc_cats(&line,TXT_TOP_THROUGH)) die_nomem();
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,to))) die_nomem();
+      if (!stralloc_cats(&line,TXT_TOP_LAST)) die_nomem();
+      code_qput(line.s,line.len);
+    }
+}
+
+void postsub(factype,format)
+/* output after TOC and before first message. */
+int factype;           /* action type (AC_THREAD, AC_GET, AC_DIGEST) */
+char format;           /* output format type (see idx.h) */
+{
+    code_qput(TXT_ADMINISTRIVIA,str_len(TXT_ADMINISTRIVIA));
+    if(factype == AC_DIGEST) {
+      copy(&qq,"text/digest",flagcd,FATAL);
+      if (flagcd == 'B') {
+        encodeB("",0,&line,2,FATAL);   /* flush */
+        qmail_put(&qq,line.s,line.len);
+      }
+     } else
+      normal_bottom(format);
+    if (!flagcd || format == RFC1153)
+      qmail_puts(&qq,"\n----------------------------------------------------------------------\n");
+    else
+      qmail_puts(&qq,"\n");
+}
+
+void postmsg(format)
+char format;
+{
+    switch(format) {
+       case MIME:
+       case VIRGIN:
+       case NATIVE:
+        case MIXED:
+                qmail_puts(&qq,"\n--");
+                qmail_put(&qq,boundary,COOKIE);                /* digest boundary */
+                qmail_puts(&qq,"--\n");
+               break;
+       case RFC1153:
+               qmail_puts(&qq,"End of ");
+               qmail_put(&qq,listname.s,listname.len);
+               qmail_puts(&qq," Digest");
+               qmail_puts(&qq,"\n***********************************\n");
+               break;
+    }
+}
+
+void copymsg(msg,fd,format)
+/* Copy archive message "msg" itself from open file handle fd, in "format" */
+unsigned long msg; int fd; char format;
+{
+  int match;
+  int flaginheader;
+  int flagskipblanks;
+  int flaggoodfield;
+
+  switch(format) {
+    case VIRGIN:
+    case NATIVE:
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      for (;;) {
+        if (getln(&sstext,&line,&match,'\n') == -1)
+           strerr_die4sys(111,FATAL,ERR_READ,line.s,": ");
+        if (match) {
+           qmail_put(&qq,line.s,line.len);
+          msgsize += line.len;
+        } else
+           break;
+      }
+      break;
+    case MIME:
+    case MIXED:
+      flaginheader = 1;
+      flaggoodfield = 0;
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      for (;;) {
+        if (getln(&sstext,&line,&match,'\n') == -1)
+           strerr_die4sys(111,FATAL,ERR_READ,line.s,": ");
+        if (match) {
+          if (flaginheader) {
+            if (line.len == 1) {
+              flaginheader = 0;
+              flaggoodfield = 1;
+            } else if (line.s[0] != ' ' && line.s[0] != '\t') {
+              flaggoodfield = 0;
+              if (constmap(&digheadersmap,line.s,
+                       byte_chr(line.s,line.len,':')))
+                flaggoodfield = 1;
+            }
+            if (flaggoodfield) {
+              qmail_put(&qq,line.s,line.len);          /* header */
+             msgsize += line.len;
+           }
+          } else {
+            qmail_put(&qq,line.s,line.len);            /* body */
+           msgsize += line.len;
+         }
+        } else
+          break;
+      }
+      break;
+    case RFC1153:              /* Not worth optimizing. Rarely used */
+      flaginheader = 1;
+      flagskipblanks = 1;      /* must skip terminal blanks acc to rfc1153 */
+      archtype = ' ';          /* rfc1153 requires ordered headers */
+      if (!stralloc_copys(&archblanklines,"")) die_nomem();
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      for (;;) {
+        if (getln(&sstext,&line,&match,'\n') == -1)
+           strerr_die4sys(111,FATAL,ERR_READ,line.s,": ");
+        if (match) {
+          if (flaginheader) {
+            if (line.len == 1) {
+              flaginheader = 0;
+              if (archdate.len) {
+                qmail_put(&qq,archdate.s,archdate.len);
+                archdate.len = 0;
+               msgsize += archdate.len;
+              }
+              if (archto.len) {
+                qmail_put(&qq,archto.s,archto.len);
+               msgsize += archto.len;
+                archto.len = 0;
+              }
+              if (archfrom.len) {
+                qmail_put(&qq,archfrom.s,archfrom.len);
+               msgsize += archfrom.len;
+                archfrom.len = 0;
+              }
+              if (archcc.len) {
+                qmail_put(&qq,archcc.s,archcc.len);
+               msgsize += archcc.len;
+                archcc.len = 0;
+              }
+              if (archsubject.len) {
+                qmail_put(&qq,archsubject.s,archsubject.len);
+               msgsize += archsubject.len;
+                archsubject.len = 0;
+              }
+              if (archmessageid.len) {
+                qmail_put(&qq,archmessageid.s,archmessageid.len);
+               msgsize += archmessageid.len;
+                archmessageid.len = 0;
+              }
+              if (archkeywords.len) {
+                qmail_put(&qq,archkeywords.s,archkeywords.len);
+               msgsize += archkeywords.len;
+                archkeywords.len = 0;
+              }
+              qmail_puts(&qq,"\n");
+            } else if (line.s[0] == ' ' || line.s[0] == '\t') {
+              switch (archtype) {      /* continuation lines */
+                case ' ':
+                  break;
+                case 'D':
+                  if (!stralloc_cat(&archdate,&line)) die_nomem(); break;
+                case 'F':
+                  if (!stralloc_cat(&archfrom,&line)) die_nomem(); break;
+                case 'T':
+                  if (!stralloc_cat(&archto,&line)) die_nomem(); break;
+                case 'C':
+                  if (!stralloc_cat(&archcc,&line)) die_nomem(); break;
+                case 'S':
+                  if (!stralloc_cat(&archsubject,&line)) die_nomem(); break;
+                case 'M':
+                  if (!stralloc_cat(&archmessageid,&line)) die_nomem(); break;
+                case 'K':
+                  if (!stralloc_cat(&archkeywords,&line)) die_nomem(); break;
+                default:
+                  strerr_die2x(111,FATAL,
+                      "Program error: Bad archive header type");
+              }
+            } else {
+              archtype = ' ';
+              if (case_startb(line.s,line.len,"cc:")) {
+                archtype='C';
+                if (!stralloc_copy(&archcc,&line)) die_nomem();
+              }
+              else if (case_startb(line.s,line.len,"date:")) {
+                archtype='D';
+                if (!stralloc_copy(&archdate,&line)) die_nomem();
+              }
+              else if (case_startb(line.s,line.len,"from:")) {
+                archtype='F';
+                if (!stralloc_copy(&archfrom,&line)) die_nomem();
+              }
+              else if (case_startb(line.s,line.len,"keywords:")) {
+                archtype='K';
+                if (!stralloc_copy(&archkeywords,&line)) die_nomem();
+              }
+              else if (case_startb(line.s,line.len,"message-id:")) {
+                archtype='M';
+                if (!stralloc_copy(&archmessageid,&line)) die_nomem();
+              }
+              else if (case_startb(line.s,line.len,"subject:")) {
+                archtype='S';
+                if (!stralloc_copy(&archsubject,&line)) die_nomem();
+              }
+              else if (case_startb(line.s,line.len,"to:")) {
+                archtype='T';
+                if (!stralloc_copy(&archto,&line)) die_nomem();
+              }
+            }
+          } else if (line.len == 1) {
+            if (!flagskipblanks)
+              if (!stralloc_copys(&archblanklines,"\n")) die_nomem();
+          } else {
+            if (archblanklines.len) {
+              qmail_put(&qq,archblanklines.s,archblanklines.len);
+              archblanklines.len = 0;
+            }
+            flagskipblanks = 0;
+            qmail_put(&qq,line.s,line.len);
+           msgsize += line.len;
+          }
+        } else
+          break;
+      }
+      break;
+    default:
+      strerr_die2x(100,FATAL,"Program error: bad format in copymsg()");
+  }
+}
+
+void mime_getbad(msg)
+/* Message not found as a MIME multipart */
+unsigned long msg;
+{
+   qmail_puts(&qq,"\n--");
+   qmail_put(&qq,boundary,COOKIE);
+   qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+   qmail_puts(&qq,charset.s);
+   qmail_puts(&qq,"\nContent-Disposition: inline; filename=\"");
+   qmail_put(&qq,listname.s,listname.len);
+   qmail_puts(&qq,"_");
+   qmail_put(&qq,strnum,fmt_ulong(strnum,msg));
+   qmail_puts(&qq,".ezm\"\n");
+   transferenc();
+   copy(&qq,"text/get-bad",flagcd,FATAL);
+}
+
+void msgout(msg,format)
+/* Outputs message (everything that's needed per message) */
+unsigned long msg; char format;
+{
+  int fd;
+  unsigned int len;
+
+    if (!stralloc_copys(&fn,"archive/")) die_nomem();
+
+    len = fmt_ulong(strnum, msg / 100);
+    if (!stralloc_catb(&fn,strnum,len)) die_nomem();
+    if (!stralloc_cats(&fn,"/")) die_nomem();
+    len = fmt_uint0(strnum, (unsigned int) (msg % 100),2);
+    if (!stralloc_catb(&fn,strnum,len)) die_nomem();
+    if (!stralloc_0(&fn)) die_nomem();
+
+    switch(format) {
+      case MIME:
+      case VIRGIN:
+      case NATIVE:
+      case MIXED:
+       fd = open_read(fn.s);
+       if (fd == -1) {
+         if (errno != error_noent)
+           strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+          else
+            mime_getbad(msg);
+        } else if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) {
+         close(fd);
+          mime_getbad(msg);
+        } else {
+          qmail_puts(&qq,"\n--");
+          qmail_put(&qq,boundary,COOKIE);
+          qmail_puts(&qq,"\nContent-Type: message/rfc822");
+          qmail_puts(&qq,"\nContent-Disposition: inline; filename=\"");
+         qmail_put(&qq,listname.s,listname.len);
+         qmail_puts(&qq,"_");
+         qmail_put(&qq,strnum,fmt_ulong(strnum,msg));
+         qmail_puts(&qq,".ezm\"\n\n");
+          copymsg(msg,fd,format);
+         close(fd);
+        }
+       break;
+      case RFC1153:
+       fd = open_read(fn.s);
+       if (fd == -1) {
+         if (errno != error_noent)
+           strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+         else {
+           qmail_puts(&qq,"\n== ");
+           qmail_put(&qq,strnum,fmt_ulong(strnum,msg));
+           qmail_puts(&qq," ==\n\n");
+           copy(&qq,"text/get-bad",flagcd,FATAL);
+          }
+        } else {
+          if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) {
+           close(fd);
+           qmail_puts(&qq,"\n== ");
+           qmail_put(&qq,strnum,fmt_ulong(strnum,msg));
+           qmail_puts(&qq," ==\n\n");
+           copy(&qq,"text/get-bad",flagcd,FATAL);
+         } else {
+           copymsg(msg,fd,format);
+           close(fd);
+          }
+       }
+       qmail_puts(&qq,"\n------------------------------\n\n");
+       break;
+      default:
+        strerr_die2x(100,FATAL,"Program error: Unrecognized format in msgout");
+        break;
+    }
+}
+
+void digest(msgtable,subtable,authtable,from,to,subj,factype,format)
+/* Output digest range from-to as per msgtable/subtable (from mkthread(s)). */
+/* "Subject is the subject of the _entire_ digest/set. */
+msgentry *msgtable; subentry *subtable; authentry *authtable;
+unsigned long from,to; stralloc *subj; int factype; char format;
+{
+  msgentry *pmsgt;
+  subentry *psubt;
+  char *cp;
+  int ffirstmsg;
+  unsigned int len;
+  unsigned long msg;
+  unsigned long subnum;
+
+  psubt = subtable;
+  presub(from,to,subj,factype,format);
+
+  if (format != NATIVE) {
+    while (psubt->sub) {
+      ffirstmsg = 1;
+               /* ptr to first message with this subject */
+      pmsgt = msgtable + psubt->firstmsg - from;
+      subnum = (unsigned long) (psubt - subtable +1);
+      for (msg=psubt->firstmsg; msg<=to; msg++) {
+        if (pmsgt->subnum == subnum) {
+          if(ffirstmsg) {
+            ffirstmsg = 0;
+            if (!stralloc_copys(&line,"\n")) die_nomem();
+           if (psubt->sublen <= HASHLEN + 2) {
+              if (!stralloc_cats(&line,"(null)\n")) die_nomem();
+           } else
+              if (!stralloc_catb(&line,psubt->sub + HASHLEN + 1,
+               psubt->sublen - HASHLEN - 1)) die_nomem();
+          } else
+            if (!stralloc_copys(&line,"")) die_nomem();
+          if (!stralloc_cats(&line,"\t")) die_nomem();
+          if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem();
+          if (!stralloc_cats(&line,TXT_BY)) die_nomem();
+          if (pmsgt->authnum) {
+           cp = authtable[pmsgt->authnum - 1].auth;
+           len = authtable[pmsgt->authnum - 1].authlen;
+           if (len > HASHLEN) {
+              if (!stralloc_catb(&line,cp + HASHLEN + 1,
+               len - HASHLEN - 1)) die_nomem();
+           } else
+             if (!stralloc_catb(&line,cp,len)) die_nomem();
+         } else
+            if (!stralloc_cats(&line,"\n")) die_nomem();
+          code_qput(line.s,line.len);
+        }
+        pmsgt++;
+      }
+      psubt++;
+    }
+  }
+  postsub(factype,format);
+
+  psubt = subtable;
+  while (psubt->sub) {
+    pmsgt = msgtable + psubt->firstmsg - from;
+    subnum = (unsigned long) (psubt - subtable +1);
+    for (msg=psubt->firstmsg; msg<=to; msg++) {
+      if (pmsgt->subnum == subnum)
+        msgout(msg,format);
+      pmsgt++;
+    }
+    psubt++;
+  }
+  postmsg(format);
+  idx_destroythread(msgtable,subtable,authtable);
+}
+
+void doheaders()
+{
+  int flaggoodfield,match;
+
+  if (act == AC_DIGEST)
+    copy(&qq,"headeradd",'H',FATAL);
+
+  qmail_puts(&qq,"Mailing-List: ");
+  qmail_put(&qq,mailinglist.s,mailinglist.len);
+  if (getconf_line(&line,"listid",0,FATAL,dir)) {
+    qmail_puts(&qq,"\nList-ID: ");
+    qmail_put(&qq,line.s,line.len);
+  }
+  qmail_puts(&qq,"\nDate: ");
+  qmail_put(&qq,date,date822fmt(date,&dt));
+  qmail_puts(&qq,"Message-ID: <");
+  if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
+     die_nomem();
+  if (!stralloc_append(&line,".")) die_nomem();
+  if (!stralloc_catb(&line,strnum,
+               fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  qmail_puts(&qq,line.s);
+               /* "unique" MIME boundary as hash of messageid */
+  makehash(line.s,line.len,boundary);
+  qmail_puts(&qq,">\nFrom: ");
+  if (!quote(&quoted,&outlocal)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"-help@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,"\n");
+  if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem();
+  if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"@")) die_nomem();
+  if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+
+  qmail_put(&qq,mydtline.s,mydtline.len);
+
+  flaggoodfield = 0;
+  if (act != AC_DIGEST)
+    for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (!match) break;
+    if (line.len == 1) break;
+    if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+      flaggoodfield = 0;
+      if (case_startb(line.s,line.len,"mailing-list:"))
+        if (flageditor)                        /* we may be running from a sublist */
+          flaggoodfield = 0;
+        else
+          strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+      if (line.len == mydtline.len)
+       if (byte_equal(line.s,line.len,mydtline.s))
+          strerr_die2x(100,FATAL,ERR_LOOPING);
+      if (case_startb(line.s,line.len,"delivered-to:"))
+        flaggoodfield = 1;
+      if (case_startb(line.s,line.len,"received:"))
+        flaggoodfield = 1;
+    }
+    if (flaggoodfield)
+      qmail_put(&qq,line.s,line.len);
+  }
+}
+
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *def;
+  char *local;
+  char *action = "";
+  char *psz;
+  char *err;
+  int fd;
+  unsigned int i,j;
+  int flagremote;
+  int flagqmqp = 0;
+  int match;
+  int goodexit = 0;                    /* exit code for normal exit */
+                                       /* in manager this will be set to 0 */
+  unsigned long from,u,to,issue,prevmax,mno;
+  unsigned long chunk;
+  unsigned long subs;
+  unsigned int pos,pos1;
+  unsigned int len;
+  int opt;
+  char outformat = DEFAULT_FORMAT;     /* default output format */
+  msgentry *msgtable;
+  subentry *subtable;
+  authentry *authtable;
+  dateentry *datetable;
+
+  (void) umask(022);
+  sig_pipeignore();
+  when = now();
+  datetime_tai(&dt,when);
+
+  while ((opt = getopt(argc,argv,"bBcCf:pPsSt:vV")) != opteof)
+    switch (opt) {
+      case 'b': flagbottom = 1; break; /* add text/bottom (default) */
+      case 'B': flagbottom = 0; break; /* suppress text/bottom */
+      case 'c': flagdo = 1; break;     /* do commands */
+      case 'C': flagdo = 0; break;     /* ignore commands X dig */
+      case 'f': if (FORMATS[str_chr(FORMATS,optarg[0])])
+                   outformat = optarg[0];
+                break;
+      case 'p': flagpublic = 1; break; /* always public */
+      case 'P': flagpublic = 0; break; /* never public = only mods do cmd*/
+                                       /* def = 2: respect DIR/public */
+      case 's': flagsub = 1; break;    /* only subs have archive access */
+      case 'S': flagsub = 0; break;    /* everyone has archive access */
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-get version: ",EZIDX_VERSION);
+      default:
+        die_usage();
+    }
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+  if (chdir(dir) == -1)
+    strerr_die4x(111,FATAL,ERR_SWITCH,dir,": ");
+
+  digestcode = argv[optind];   /* code to activate digest (-digest-code)*/
+                               /* ignore any extra args */
+
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  if (!stralloc_copy(&subject,&outlocal)) die_nomem(); /* for subjects */
+  if (!stralloc_copy(&listname,&outlocal)) die_nomem();        /* for content disp */
+
+  local = env_get("LOCAL");
+  def = env_get("DEFAULT");
+  sender = env_get("SENDER");
+  if (local && *local) {       /* in editor local = inlocal */
+    if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
+    if (!*sender)
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
+    if (str_equal(sender,"#@[]"))
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
+    if (!sender[str_chr(sender,'@')])
+      strerr_die2x(100,FATAL,ERR_ANONYMOUS);
+    if (def) {                 /* qmail>=1.02 support only */
+      if (*def) {
+       action = def;
+       goodexit = 99;
+      } else
+       _exit(0);               /* list-@host should do -help from manager */
+    } else {                   /* editor */
+      act = AC_DIGEST;         /* on list-@host ! */
+      flageditor = 1;          /* to avoid Mailing-list error on sublists */
+                               /* when running out of dir/editor. */
+    }
+    if (case_starts(action,"dig")) {
+      action += 3;
+      if (action[0] == '-' || action [0] == '.') {
+        action++;
+       if (!digestcode)
+            strerr_die2x(100,FATAL,ERR_BAD_DIGCODE);
+        len = str_len(digestcode);
+        if (len <= str_len(action) && case_startb(action,len,digestcode)) {
+          if (FORMATS[str_chr(FORMATS,*(action+len))])
+            outformat = *(action+len);
+          act = AC_DIGEST;
+        } else
+          strerr_die2x(100,FATAL,ERR_BAD_DIGCODE);
+      }
+    }
+  } else                       /* Command line operation */
+    act = AC_DIGEST;
+
+       /* Things we deal with. If anything else just die with success!   */
+       /* At the moment this is -index, -thread, and -get.               */
+       /* If flagdo = 0 we only service -dig commands. This is to support*/
+       /* "secret" lists that are still archived and digested. -c on     */
+       /* cmd line. */
+
+  if (act == AC_NONE) {
+    if (case_equals(action,ACTION_DIGEST)) {
+      act = AC_GET;            /* list-digest@ => msg since last digest */
+      action = ACTION_GET;
+    } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET))
+      act = AC_GET;
+    else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX))
+      act = AC_INDEX;
+    else if (case_starts(action,ACTION_THREAD) ||
+        case_starts(action,ALT_THREAD))
+      act = AC_THREAD;
+  }
+  if (act == AC_NONE)                  /* not for us. Pass the buck. */
+    _exit(0);
+  if (act != AC_INDEX) {               /* need to do header processing */
+    if(!getconf(&digheaders,"digheaders",0,FATAL,dir)) {
+      if(!stralloc_copys(&digheaders,digsz)) die_nomem();
+      if (!stralloc_0(&digheaders)) die_nomem();
+      psz = digheaders.s;
+      while (*psz) {
+        if (*psz == '\\') *psz = '\0';
+        ++psz;
+      }
+    }
+    if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0))
+       die_nomem();
+  }
+  if (act != AC_DIGEST) {
+    if (!flagdo)                       /* only do digests */
+      strerr_die2x(100,FATAL,ERR_NOCMD);
+    if (flagpublic == 2)
+      flagpublic = getconf_line(&line,"public",0,FATAL,dir);
+    if (!flagpublic) {
+               /* This all to take care of non-public lists. They should*/
+               /* still do digests, but do other things only for        */
+               /* moderators that have remote access. Since this is rare*/
+               /* efforts have been made to keep everything that's not  */
+               /* needed elsewhere in here.                   */
+      getconf_line(&moddir,"modsub",0,FATAL,dir);
+      flagremote = getconf_line(&line,"remote",0,FATAL,dir);
+      if (!flagremote)
+        strerr_die2x(100,FATAL,ERR_NOT_PUBLIC);
+      if (!moddir.len || moddir.s[0] != '/') {
+        if (line.len && line.s[0] == '/') {
+          if (!stralloc_copy(&moddir,&line)) die_nomem();
+        } else {
+          if (!stralloc_copys(&moddir,dir)) die_nomem();
+          if (!stralloc_cats(&moddir,"/mod")) die_nomem();
+        }
+      }
+      if (!stralloc_0(&moddir)) die_nomem();
+      pmod = issub(moddir.s,sender,(char *) 0,FATAL);
+      if (!pmod)                       /* sender = moderator? */
+        strerr_die2x(100,FATAL,ERR_NOT_PUBLIC);
+      else {
+        if (!stralloc_copys(&mod,pmod)) die_nomem();
+        if (!stralloc_0(&mod)) die_nomem();
+        pmod = mod.s;          /* send to address in list not matching bait */
+      }
+    }
+  }
+
+  flagindexed = getconf_line(&line,"indexed",0,FATAL,dir);
+  flagarchived = getconf_line(&line,"archived",0,FATAL,dir);
+
+  if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+    if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+      if (charset.s[charset.len - 1] == 'B' ||
+                charset.s[charset.len - 1] == 'Q') {
+        flagcd = charset.s[charset.len - 1];
+        charset.s[charset.len - 2] = '\0';
+      }
+    }
+  } else
+    if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+  if (!stralloc_0(&charset)) die_nomem();
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  set_cpouthost(&outhost);
+
+    if (!stralloc_copys(&ddir,dir)) die_nomem();
+    if (!stralloc_cats(&ddir,"/digest")) die_nomem();
+    if (!stralloc_0(&ddir)) die_nomem();
+  if (act == AC_DIGEST) {
+    workdir = ddir.s;
+    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
+    if (getconf_line(&line,"chunk",0,FATAL,dir)) {
+      if (!stralloc_0(&line)) die_nomem();
+      (void) scan_ulong(line.s,&chunk);                /* same chunk as main list */
+      if (chunk == 0)                          /* limit range to 1-53 */
+       chunk = 1L;
+      else if (chunk > 52)
+       chunk = 52L;
+    } else {
+      chunk = 0L;                              /* maybe direct qmqp? */
+      if (!stralloc_copys(&line,QMQPSERVERS)) die_nomem();
+      if (!stralloc_cats(&line,"/0")) die_nomem();
+      if (!stralloc_0(&line)) die_nomem();
+      flagqmqp = getconf(&qmqpservers,line.s,0,FATAL,dir);
+    }
+  } else
+    workdir = dir;
+
+  if (!stralloc_copys(&edir,dir)) die_nomem(); /* not needed for -dig, but */
+  if (!stralloc_cats(&edir,"/allow")) die_nomem();     /* be safe */
+  if (!stralloc_0(&edir)) die_nomem();
+  set_cpoutlocal(&outlocal);           /* needed for copy */
+
+  if (flagqmqp) {
+    if (qmail_open(&qq,&qmqpservers) == -1)            /* open qmail */
+      strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+  } else if (qmail_open(&qq,(stralloc *) 0) == -1)     /* open qmail */
+      strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+
+  set_cpnum("");       /* default for <#n#> replacement */
+
+  if (act == AC_DIGEST) {
+/* -dig{.|-}'digestcode'[f] returns an rfc1153 digest                        */
+/* of messages from the archive. Messages                                    */
+/* dignum+1 through the last message received by the list are processed and  */
+/* dignum is updated to the last message processed. digissue is advanced.    */
+
+    if (!flagarchived)
+      strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED);
+
+    get_num();                         /* max = last successful message */
+    to = max;
+    lockup();                  /* another digest could corrupt dignum */
+                               /* but will be saved only if flagdigrange==0 */
+    if(getconf_line(&num,"dignum",0,FATAL,dir)) {
+      if(!stralloc_0(&num)) die_nomem();
+      pos = scan_ulong(num.s,&prevmax);
+      if (num.s[pos] == ':') pos++;
+      pos += 1 + scan_ulong(num.s+pos,&cumsize);       /* last cumsize */
+      if (num.s[pos] == ':') pos++;
+      scan_ulong(num.s+pos,&digwhen);                  /* last reg dig */
+    } else {
+      prevmax = 0L;
+      cumsize = 0L;
+      digwhen = 0L;
+    }
+    mno = prevmax + 1L;
+    if(!max || mno > max)      /* if a digest-list is "sending" the request, */
+                               /* don't make noise: errors go to postmaster!*/
+      strerr_die2x(goodexit,FATAL,ERR_EMPTY_DIGEST);
+    szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0';
+    set_cpnum(szmsgnum);       /* for copy */
+                               /* prepare subject to get entropy for tagmsg*/
+    if (!stralloc_cats(&subject," Digest ")) die_nomem();
+    if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1))
+          die_nomem();         /* skip trailing in date '\n' */
+    if (!stralloc_cats(&subject," Issue ")) die_nomem();
+    if (getconf_line(&num,"digissue",0,FATAL,dir)) {
+      if(!stralloc_0(&num)) die_nomem();
+      scan_ulong(num.s,&issue);
+      issue++;
+    } else {
+      issue = 1;
+    }
+    if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue)))
+      die_nomem();
+                                       /* use the subject as entropy */
+    if (!stralloc_copy(&line,&subject)) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+
+    if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem();
+    seed.len = HASHLEN + 1;
+    seed.s[HASHLEN] = '\0';
+    makehash(line.s,line.len,seed.s);
+    if (chunk) {                       /* only if slaves are used */
+      qmail_puts(&qq,"Ezauth: ");
+      qmail_put(&qq,seed.s,HASHLEN);
+      qmail_puts(&qq,"\n");
+    }
+
+    doheaders();
+    qmail_puts(&qq,"To: ");
+    if (!quote(&quoted,&listname)) die_nomem();
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,"@");
+    qmail_put(&qq,outhost.s,outhost.len);
+    qmail_puts(&qq,"\n");
+    if (flagindexed && (outformat != NATIVE))
+      idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
+       mno,to,max,flaglocked,FATAL);
+    else
+      idx_mklist(&msgtable,&subtable,&authtable,mno,to,FATAL);
+    digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat);
+
+    write_ulong(issue,0L,0L,"digissue","digissuen");
+    write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn");
+  }
+
+  else if (act == AC_GET) {
+
+/* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted   */
+/* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET.  */
+
+    if (!flagarchived)
+      strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED);
+    zapnonsub(ACTION_GET);             /* restrict to subs if requested */
+    tosender();
+                               /* for rfc1153 */
+    if (!stralloc_cats(&subject," Digest of: ")) die_nomem();
+    if (!stralloc_cats(&subject,action)) die_nomem();
+
+    to = 0;
+    pos = str_len(ACTION_GET);
+    if (!case_starts(action,ACTION_GET))
+      pos = str_len(ALT_GET);
+    if (FORMATS[str_chr(FORMATS,action[pos])]) {
+       outformat = action[pos];
+       ++pos;
+    }
+                                       /* optional - or . after '-get' */
+    if (action[pos] == '-' || action[pos] == '.') pos++;
+    get_num();                         /* max = last successful message */
+                                       /* accept any separator. It may be  */
+                                       /* the terminal '\n', but then      */
+                                       /* scan will = 0 on the \0 so should*/
+                                       /* be safe                          */
+    if (!max)
+      strerr_die2x(100,FATAL,ERR_EMPTY_LIST);
+    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
+    set_cpnum(szmsgnum);       /* for copy this is the latest message arch'd*/
+    doheaders();
+    if(action[pos += scan_ulong(action + pos,&u)])
+      scan_ulong(action + pos + 1, &to);
+    if (u == 0 && to == 0) {           /* default: messages since last */
+                                       /* digest, or last MAXGET if too many */
+      to= max;
+      u = dignum();
+      if (u == 0) {            /* no digest => last up to HISTGET msgs */
+       to = max;
+       if (max > HISTGET) u = max - HISTGET; else u = 1;
+      }
+      if (to - u >= MAXGET) u = to - MAXGET + 1;       /* max MAXGET */
+    } else if (u > max) {
+      if (to) {                        /* -get.999999_x returns 30 and msg since last*/
+       to = max;               /* digest 30*/
+        u = dignum();
+       if (u > HISTGET) u -= HISTGET; else u = 1;
+        if (to - u >= MAXGET) u = to - MAXGET + 1;
+      } else
+       u = max;
+    }
+    if (u == 0) u = 1;                 /* -get.5 => 1-5 */
+    if (to < u) to = u;                        /* -get23_2 => 23 */
+    if (to >= u + MAXGET) to = u + MAXGET - 1;
+                                       /* no more than MAXGET at a time */
+    if (to > max) to = max;
+    if (flagindexed && (outformat != NATIVE))  /* fake out threading */
+      idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
+       u,to,max,0,FATAL);
+    else
+      idx_mklist(&msgtable,&subtable,&authtable,u,to,FATAL);
+    digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat);
+  }
+
+  else if (act == AC_INDEX) {
+
+/* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/
+/* sets of 100.                                                              */
+/* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */
+/* limited to num+MAXINDEX to limit the amount of data sent.                 */
+
+    if (!flagindexed)
+      strerr_die2x(100,FATAL,ERR_NOT_INDEXED);
+    if (flagsub)
+    zapnonsub(ACTION_INDEX);   /* restrict to subs if requested */
+    to = 0;
+    pos = str_len(ACTION_INDEX);
+    if (!case_starts(action,ACTION_INDEX))
+      pos = str_len(ALT_INDEX);
+    if (FORMATS[str_chr(FORMATS,action[pos])]) {
+       outformat = action[pos];                /* ignored, but be nice ... */
+       ++pos;
+    }
+    get_num();                         /* max = last successful message */
+    if (!max)
+      strerr_die2x(100,FATAL,ERR_EMPTY_LIST);
+    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
+    set_cpnum(szmsgnum);       /* for copy this is the latest message arch'd*/
+
+    doheaders();
+    tosender();
+    if (!stralloc_cats(&subject," Result of: ")) die_nomem();
+    if (!stralloc_cats(&subject,action)) die_nomem();
+    presub(1,1,&subject,AC_INDEX,outformat);
+
+    if (action[pos] == '-' || action[pos] == '.') pos++;
+    if(action[pos += scan_ulong(action + pos,&u)])
+      scan_ulong(action + pos + 1, &to);
+
+    if (u == 0 && to == 0) { to = max; u = max - 100; }
+    if (u <= 0) u = 1;
+    if (u > max) u = max;
+    if (to < u) to = u;
+    if (to > u + MAXINDEX) to = u+MAXINDEX;    /* max MAXINDEX index files */
+    if (to > max) to = max;
+    u /= 100;
+    to /= 100;
+    while (u <= to) {
+      if (!stralloc_copys(&fn,"archive/")) die_nomem();
+      if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem();
+      if (!stralloc_cats(&fn,"/index")) die_nomem();
+      if (!stralloc_0(&fn)) die_nomem();
+
+      if (u == max/100)        /* lock if last index file in archive */
+        lockup();
+
+      fd = open_read(fn.s);
+      if (fd == -1)
+        if (errno != error_noent)
+          strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+        else
+          code_qput(TXT_NOINDEX,str_len(TXT_NOINDEX));
+      else {
+        substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+        for (;;) {
+          if (getln(&sstext,&line,&match,'\n') == -1)
+            strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+          if (match) {
+            if (line.s[0] != '\t') {   /* subject line */
+              pos = byte_chr(line.s,line.len,' ');
+             if (pos && pos != line.len && line.s[pos - 1] == ':')
+                pos1 = pos + HASHLEN + 1;      /* after hash */
+              if (pos1 >= line.len) {  /* bad! */
+                pos = 0;
+                pos1 = 0;              /* output as is */
+              }
+              if (!stralloc_copyb(&line2,line.s,pos)) die_nomem();
+              if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem();
+            } else {
+             pos = byte_chr(line.s,line.len,';');
+             if (pos + HASHLEN + 1 < line.len && pos > 15 &&
+                               line.s[pos + 1] != ' ') {
+                 if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem();
+                 pos++;
+                 if (!stralloc_catb(&line2,line.s + pos + HASHLEN,
+                       line.len - pos - HASHLEN)) die_nomem();
+             } else                    /* old format - no author hash */
+                if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem();
+           }
+            code_qput(line2.s,line2.len);
+          } else
+            break;
+        }
+        close(fd);
+      }
+
+      if (u == max/100)        /* unlock if last index in archive file */
+        unlock();
+
+      u++;
+    }
+    normal_bottom(outformat);
+    postmsg(outformat);
+  }
+
+  else if (act == AC_THREAD) {
+
+/* -thread[f][-|.]num returns messages with subject matching message        */
+/* 'num' in the subject index. If 'num' is not in[1..last_message] an error */
+/* message is returned.                                                     */
+
+    if (!flagarchived)
+      strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED);
+    if (!flagindexed)
+      strerr_die2x(100,FATAL,ERR_NOT_INDEXED);
+
+    zapnonsub(ACTION_THREAD);          /* restrict to subs if requested*/
+
+    get_num();                         /* max = last successful message */
+    if (!max)
+      strerr_die2x(100,FATAL,ERR_EMPTY_LIST);
+    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
+    set_cpnum(szmsgnum);       /* for copy this is the latest message arch'd*/
+
+    doheaders();
+    tosender();
+                               /* for rfc1153 */
+    if (!stralloc_cats(&subject," Digest of: ")) die_nomem();
+    if (!stralloc_cats(&subject,action)) die_nomem();
+
+    to = 0;
+    pos = str_len(ACTION_THREAD);
+    if (!case_starts(action,ACTION_THREAD))
+      pos = str_len(ALT_THREAD);
+    if (FORMATS[str_chr(FORMATS,action[pos])]) {
+       outformat = action[pos];
+       ++pos;
+    }
+    if (action[pos] == '-' || action[pos] == '.') pos++;
+    if(action[pos += scan_ulong(action + pos,&u)])
+      scan_ulong(action + pos + 1, &to);
+
+    if(u == 0 || u > max) {
+      if (!stralloc_cats(&subject,"\n\n")) die_nomem();
+      qmail_puts(&qq,"Subject: ");
+      qmail_put(&qq,subject.s,subject.len);
+      copy(&qq,"text/get-bad",flagcd,FATAL);
+    } else {   /* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */
+      if (u > THREAD_BEFORE)
+        from = u-THREAD_BEFORE;
+      else
+        from = 1L;
+      if (u + THREAD_AFTER > max) {
+        idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0,FATAL);
+        digest(msgtable,subtable,authtable,from,max,&subject,
+               AC_THREAD,outformat);
+      } else {
+        idx_mkthread(&msgtable,&subtable,&authtable,
+               from,u+THREAD_AFTER,u,max,0,FATAL);
+        digest(msgtable,subtable,authtable,from,u+THREAD_AFTER,
+                       &subject,AC_THREAD,outformat);
+      }
+    }
+  }
+
+  else
+       /* This happens if the initial check at the beginning of 'main'    */
+       /* matches something that isn't matched here. Conversely, just     */
+       /* adding an action here is not enough - it has to be added to the */
+       /* initial check as well.                                          */
+
+    strerr_die2x(100,FATAL,
+      "Program error: I'm supposed to deal with this but I didn't");
+
+  if (!stralloc_copy(&line,&outlocal)) die_nomem();
+  if (act == AC_DIGEST) {
+    if (chunk) {
+      if (!stralloc_cats(&line,"-return-g-")) die_nomem();
+    } else
+      if (!stralloc_cats(&line,"-return-")) die_nomem();
+    strnum[fmt_ulong(strnum,mno)] = '\0';
+    if (!stralloc_cats(&line,strnum)) die_nomem();
+    if (!stralloc_cats(&line,"-@")) die_nomem();
+
+    if (!stralloc_cat(&line,&outhost)) die_nomem();
+    if (!stralloc_cats(&line,"-@[]")) die_nomem();
+  } else {
+    if (!stralloc_cats(&line,"-return-@")) die_nomem();
+    if (!stralloc_cat(&line,&outhost)) die_nomem();
+  }
+  if (!stralloc_0(&line)) die_nomem();
+
+  qmail_from(&qq,line.s);
+  if (act == AC_DIGEST) {       /* Do recipients */
+    tagmsg(workdir,mno,seed.s,"d",hashout,qq.msgbytes,chunk,FATAL);
+    if (chunk) {
+      if (!stralloc_copys(&line,"T")) die_nomem();
+      if (!stralloc_cat(&line,&outlocal)) die_nomem();
+      if (!stralloc_cats(&line,"-s-d-")) die_nomem();
+      if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem();
+      if (!stralloc_cats(&line,"-")) die_nomem();
+      if (!stralloc_cats(&line,strnum)) die_nomem();
+      if (!stralloc_cats(&line,"-")) die_nomem();
+      if (!stralloc_copys(&line2,"@")) die_nomem();
+      if (!stralloc_cat(&line2,&outhost)) die_nomem();
+      if (!stralloc_0(&line2)) die_nomem();
+      j = 0;
+      for (i = 0; i <= 52; i += chunk) {               /* To slaves */
+        qmail_put(&qq,line.s,line.len);
+        schar[0] = '0' + i / 10;
+        schar[1] = '0' + (i % 10);
+        qmail_put(&qq,schar,3);
+        j += (chunk - 1);
+        if (j > 52) j = 52;
+        schar[0] = '0' + j / 10;
+        schar[1] = '0' + (j % 10);
+        qmail_put(&qq,schar,2);
+        qmail_put(&qq,line2.s,line2.len);
+      }
+    } else
+      subs = putsubs(workdir,0L,52L,&subto,1,FATAL);
+  } else {                     /* if local is set, sender is checked */
+    if (pmod)
+      qmail_to(&qq,pmod);
+    else
+      qmail_to(&qq,sender);
+  }
+
+  if (*(err = qmail_close(&qq)) == '\0') {     /* Done. Skip rest. */
+    if (act == AC_DIGEST) {
+      if (chunk)
+       (void) logmsg(workdir,mno,0L,0L,2);
+      else
+        (void) logmsg(workdir,mno,0L,subs,4);
+    }
+    closesql();                        /* close db connection */
+    unlock();                  /* NOP if nothing locked */
+    strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+    strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum);
+  } else {                     /* failed. Reset last msg & issue for digest */
+    if(act == AC_DIGEST) {
+      issue--;
+      write_ulong(issue,0L,0L,"digissue","digissuen");
+      write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn");
+    }
+    unlock();                  /* NOP if nothing locked */
+    strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
+  }
+}
diff --git a/ezmlm-glconf.1 b/ezmlm-glconf.1
new file mode 100644 (file)
index 0000000..df054be
--- /dev/null
@@ -0,0 +1,54 @@
+.TH ezmlm-glconf 1
+.SH NAME
+ezmlm-glconf \- create config file for global ezmlm-request address
+.SH SYNOPSIS
+.B ezmlm-glconf
+[
+.B \-n
+]
+.IR dotdir [dotdir1 ...] >
+.I config_file
+.SH DESCRIPTION
+.B ezmlm-glconf
+scans
+.IR dotdir ,
+usually the users home directory,
+for files of type
+.BR .qmail-return-*-default .
+For normal ezmlm-lists, these are links to
+.B bouncer
+files, which on their last line have a call to
+.B ezmlm-return(1)
+or
+.BR ezmlm-warn(1).
+.B ezmlm-glconf
+extracts the list directory from these,
+and builds a
+line for
+.I config_file
+from the information in the list directory. The first line of
+the
+.B /text/info
+file of the list is assumed to contain a brief description of the list. If
+not found ``no information available'' is used.
+
+.B ezmlm-glconf
+output is sorted alphabetically by list name and if a digest list
+exists, the digest list entry immediately follows the list entry.
+
+.SH OPTIONS
+.TP
+.B \-n
+Add information on the current number of subscribers to the list information.
+.SH CONFIGURATION
+The descriptive text
+.B ezmlm-glconf
+uses for digest lists and the text used if
+.B /text/info
+is not readable/available can be changed by editing the script.
+.SH "SEE ALSO"
+ezmlm-glmake(1),
+ezmlm-request(1),
+ezmlm-return(1),
+ezmlm-warn(1),
+ezmlm(5)
diff --git a/ezmlm-glconf.sh b/ezmlm-glconf.sh
new file mode 100644 (file)
index 0000000..73f2e0d
--- /dev/null
@@ -0,0 +1,107 @@
+# above should be a definition of EZPATH and the /bin/sh line
+# automatically added at build time.
+#########################################################################
+# Script to build ezmlm-request config file for the global interface
+#      Lists are presented in alphabetical order and digest lists are
+#      directly after the main list. Digest list info is only that
+#      they are digest of the main list. If the -n switch is used,
+#      the current number of subscribers is added to the info text.
+#
+# Usage: ezmlm-glconf [-n] dotdir [dotdir1 ...] > config
+#      where ``dotdir'' is the directory where the users .qmail files
+#      reside (usually ``~''), and ``config'' is the config file for
+#      ezmlm-request servicing the global interface.
+#
+#      Note: This config file will provide info on all lists available
+#      and allow ``which'' for all lists. You may want to edit the
+#      output before using it.
+##########################################################################
+
+# Change if you change the name of the command
+NAME='ezmlm-glconf'
+
+# Info text used for list-digest
+DIGTXT='Digest of'
+# ``subscribers'' for stating subscriber number
+SUBTXT='subscribers'
+# Info text if dir/text/info doesn't exist/is not readable
+INFOTXT='No information available'
+
+# Set full path if you use the script as root or if the commands
+# are not in your path.
+HEAD='head'
+TAIL='tail'
+CAT='cat'
+CUT='cut'
+GREP='grep'
+ECHO='echo'
+LS='ls'
+SORT='sort'
+SED='sed'
+WC='wc'
+
+# Nothing more to configure
+##########################################################################
+
+SUB=''
+EZLIST="${EZPATH}/ezmlm-list"
+FATAL="${NAME}: fatal: "
+WARN="${NAME}: warning: "
+USAGE="${NAME} [-n] dotdir [dotdir1 ...] > config"
+
+if [ "$1" = "-n" ]; then
+       shift
+       if [ ! -x "${EZLIST}" ]; then
+               echo "${WARN} ezmlm-list not found. Edit ezmlm binary path."
+       else
+               SUB='yes'
+       fi
+fi
+
+if [ -z "$1" ]; then
+       ${ECHO} "$USAGE"
+       exit 100
+fi
+
+
+(
+while [ -n "$1" ]; do
+  for i in `${LS} $1/.qmail-*-return-default` ; do
+       INFO=''
+                                       # get list directory
+       if [ -r "$i" ] ; then
+               DIR=`${TAIL} -1 "$i" | ${CUT} -d\' -f2`
+                                       # only dir that exists
+               if [ -d "$DIR" ] ; then
+                       INFOFN="${DIR}/text/info"
+                       if [ -r "${INFOFN}" ]; then
+                               INFO=`${HEAD} -1 ${INFOFN}`
+                       else
+                               INFO="$INFOTXT"
+                               ${ECHO} "$WARN No info for list in $DIR" 1>&2
+                       fi
+
+                       OUTLOCAL=`${CAT} $DIR/outlocal`
+                       OUTHOST=`${CAT} $DIR/outhost`
+                       if [ -z "$SUB" ]; then
+                               SUBN=''
+                       else
+                               NO=`${EZLIST} ${DIR} | ${WC} -l |${SED} 's/ //g'`
+                               SUBN=" (${NO} ${SUBTXT})"
+                       fi
+                       ${ECHO} "$i" | ${GREP} 'digest-return' >/dev/null && \
+                               { INFO="${DIGTXT} $OUTLOCAL@$OUTHOST."; \
+                               OUTLOCAL="${OUTLOCAL}~digest" ; }
+                       ${ECHO} "$OUTLOCAL@$OUTHOST:$DIR:${INFO}${SUBN}"
+               else
+                       ${ECHO} "$WARN $DIR not readable - list ignored" 1>&2
+               fi
+       else
+               ${ECHO} "$WARN $i ignored: doesn't point to readable file" 1>&2
+       fi
+  done ;
+  shift
+done;
+) | ${SORT} | ${SED} 's/~digest@/-digest@/'
+                                       # list-digest after list
+                                       # "~"-cludge needed to get order right
diff --git a/ezmlm-idx.1 b/ezmlm-idx.1
new file mode 100644 (file)
index 0000000..60a07ea
--- /dev/null
@@ -0,0 +1,97 @@
+.TH ezmlm-idx 1
+.SH NAME
+ezmlm-idx \- create index for mailing list archive
+.SH SYNOPSIS
+.B ezmlm-idx
+[
+.B \-dDF
+][
+.B \-f\I msg
+]
+.I dir
+.SH DESCRIPTION
+.B ezmlm-idx
+reads all archived messages for the list in
+.I dir
+from
+.IR dir\fB/archive/
+and creates a message index. The index file format is identical to
+that produced by
+.B ezmlm-send(1)
+(when
+.IR dir\fB/indexed
+is present).
+
+.B ezmlm-idx
+will ignore messages that do not have the owner execute bit set.
+
+.B ezmlm-idx
+will create the the index file under a different name and then
+move it into place.
+
+If 
+.IR dir\fB/indexed
+does not exist,
+.B ezmlm-idx
+will create it to enable the use of the newly created subject and author index.
+
+.B ezmlm-idx
+will remove reply-indicators and the prefix from the subject before
+entry into the index, as described for
+.BR ezmlm-send(1) .
+.B ezmlm-idx
+will decode rfc2047 encoded subject and author headers. When unfolding
+split lines,
+.B ezmlm-idx
+will remove redundant escape sequences for the character
+set specified in
+.IR dir\fB/charset .
+
+.B ezmlm-idx
+calculates a subject hash based on the normalized subject header. Normalization
+removes linear whitespace, case information, and message reply indicators.
+The subject hash is used by
+.B ezmlm-get(1)
+for message threading.
+.SH OPTIONS
+.TP
+.B \-d
+Use the ``Date:'' header for date information, rather than the top ``Received:''
+header. This is unreliable, but useful when messages have been transferred from
+another type of archive by mailing them to ezmlm.
+.B ezmlm-idx
+converts dates with two-digit years ``xx'' to 19xx if xx is >= 70. Otherwise,
+20xx is assumed.
+.TP
+.B \-D
+(Default.)
+Use the ``Received:'' header, not the ``Date:'' header. This is more reliable
+since this is the header added by the receiving qmail. Also, this results
+in always correctly formatted dates in the same time zone (usually UTC).
+.TP
+.B \-f\fI msg
+Start indexing with the archive section containing message
+.IR msg .
+This is useful if you have removed part of the archive and do not want to
+create (empty) directories and index files for these. It also decreases
+.B ezmlm-idx
+memory use.
+.TP
+.B \-F
+(Default.)
+Start indexing from message 1.
+.TP
+.B \-v
+Display
+.B ezmlm-idx
+version info.
+.TP
+.B \-V
+Display
+.B ezmlm-idx
+version info.
+.SH "SEE ALSO"
+ezmlm-archive(1),
+ezmlm-get(1),
+ezmlm-send(1),
+ezmlm(5)
diff --git a/ezmlm-idx.c b/ezmlm-idx.c
new file mode 100644 (file)
index 0000000..c552357
--- /dev/null
@@ -0,0 +1,337 @@
+/*$Id: ezmlm-idx.c,v 1.29 1999/10/29 02:49:14 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "stralloc.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "error.h"
+#include "lock.h"
+#include "slurp.h"
+#include "open.h"
+#include "getln.h"
+#include "sgetopt.h"
+#include "case.h"
+#include "scan.h"
+#include "str.h"
+#include "fmt.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "substdio.h"
+#include "idx.h"
+#include "mime.h"
+#include "errtxt.h"
+#include "getconf.h"
+#include "makehash.h"
+
+#define FATAL "ezmlm-idx: fatal: "
+
+char strnum[FMT_ULONG];
+char hash[HASHLEN];
+
+stralloc fnadir = {0};
+stralloc fnif = {0};
+stralloc fnifn = {0};
+stralloc fnaf = {0};
+
+stralloc line = {0};
+stralloc lines = {0};
+stralloc dummy = {0};
+
+int fdindexn;
+int fdlock;
+int fd;
+int flagdate = 0;      /* use 'Received:' header by default, =1 -> 'Date:' */
+
+       /* for reading index and in ezmlm-idx for reading message */
+static substdio ssin;
+static char inbuf[1024];
+
+substdio ssindex;
+char indexbuf[1024];
+
+struct stat st;
+
+stralloc subject = {0};
+stralloc author = {0};
+stralloc authmail = {0};
+stralloc received = {0};
+stralloc prefix = {0};
+stralloc charset = {0};
+
+struct strerr index_err;
+
+stralloc num = {0};
+
+char buf0[256];
+substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-idx: usage: ezmlm-idx [-dDF] [-f msg] dir");
+
+}
+
+void die_nomem()
+{
+  strerr_die2x(100,FATAL,ERR_NOMEM);
+}
+
+int idx_get_trimsubject()
+
+/* reads an open message from 'fd', extracts the subject (if any), and    */
+/* returns the subject in 'sub', the author in 'author', and the received */
+/* rfc822 date to 'received'. 'fatal' is a program-specific error string. */
+/* returns: 0 - no reply no prefix */
+/*          1 - reply no prefix */
+/*          2 - prefix no reply */
+/*          3 - reply & prefix */
+/* No terminal '\n' in any of the strallocs! */
+{
+char *cp;
+int foundsubject = 0;
+int issubject = 0;
+int isfrom = 0;
+int foundreceived = 0;
+int foundfrom = 0;
+int match;
+int r;
+unsigned int pos,pos1;
+
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2x(111,FATAL,ERR_READ_INPUT);
+    if (match) {
+      if (line.len == 1)
+        break;
+      if (*line.s == ' ' || *line.s == '\t') {
+                               /* continuation */
+        if (issubject) {
+          if (!stralloc_cat(&subject,&line)) die_nomem();
+        } else if (isfrom)
+          if (!stralloc_cat(&author,&line)) die_nomem();
+      } else {
+        issubject = 0;
+        isfrom = 0;
+        if (!foundsubject && case_startb(line.s,line.len,"Subject:")) {
+          if (!stralloc_copyb(&subject,line.s+8,line.len-8)) die_nomem();
+          foundsubject = 1;
+          issubject = 1;
+        } else if (!foundfrom && case_startb(line.s,line.len,"From:")) {
+          if (!stralloc_copyb(&author,line.s+5,line.len-5)) die_nomem();
+          foundfrom = 1;
+          isfrom = 1;
+        } else if (!flagdate && !foundreceived &&
+            case_startb(line.s,line.len,"Received:")) {
+          pos = byte_chr(line.s,line.len,';');
+          if (pos != line.len)
+            if (!stralloc_copyb(&received,line.s+pos+2,line.len - pos - 3))
+              die_nomem();
+          foundreceived = 1;
+        } else if (flagdate && !foundreceived &&
+            case_startb(line.s,line.len,"Date:")) {
+          if (line.len < 22) continue;                         /* illegal */
+          pos = 6 + byte_chr(line.s+6,line.len-6,',');
+          if (pos == line.len)
+            pos = 5;
+          ++pos;
+          while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos;     /* dd */
+          pos1 = pos + 3;
+          while (++pos1 < line.len && line.s[pos1] != ' ');            /* mo */
+          ++pos1;
+          if (!stralloc_copyb(&received,line.s+pos,pos1 - pos))
+              die_nomem();                                     /* '01 Jun ' */
+          if (pos1 + 2 < line.len) {
+            if (line.s[pos1 + 2] == ' ') {                     /* 2-digit */
+              if (line.s[pos1] >= '7') {                       /* >= 70 */
+              if (!stralloc_cats(&received,"19")) die_nomem();
+              } else if (!stralloc_cats(&received,"20")) die_nomem();
+              pos = pos1 + 3;                                  /* 2 digit */
+            } else
+              pos = pos1 + 5;                                  /* 4 digit */
+            if (pos < line.len) {
+              pos += byte_chr(line.s+pos,line.len-pos,' ');    /* after time */
+              if (pos < line.len) {
+                ++pos;                                         /* zone */
+                while (line.s[pos] != ' ' && line.s[pos] != '\n') ++pos;
+              } else
+                pos = line.len - 1;    /* no zone. Illegal; better than 0 */
+              if (!stralloc_catb(&received,line.s+pos1,pos - pos1))
+                       die_nomem();
+              foundreceived = 1;
+              continue;
+            }
+          }
+          received.len = 0;            /* bad format - scrap */
+        }
+      }
+    } else
+      break;
+  }
+
+  if (foundsubject) {
+    concatHDR(subject.s,subject.len,&lines,FATAL);     /* make 1 line */
+    decodeHDR(lines.s,lines.len,&line,charset.s,FATAL);        /* decode mime */
+    r= unfoldHDR(line.s,line.len,&subject,charset.s,&prefix,1,FATAL);
+                                                /* trim mime */
+  }
+  else {
+    r = 0;
+    subject.len = 0;
+  }
+  return r;
+}
+
+int main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir,*cp;
+  unsigned long msgnum = 0L;
+  unsigned long msgmax;
+  int opt,r;
+
+  while ((opt = getopt(argc,argv,"dDf:FvV")) != opteof)
+    switch (opt) {
+      case 'd': flagdate = 1; break;
+      case 'D': flagdate = 0; break;
+      case 'f': if (optarg) (void) scan_ulong(optarg,&msgnum); break;
+      case 'F': msgnum = 0L;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-archive version: ",EZIDX_VERSION);
+      default: die_usage();
+  }
+  dir = argv[optind];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(100,FATAL,ERR_SWITCH,dir,": ");
+
+  (void) umask(022);
+  sig_pipeignore();
+                       /* obtain lock to write index files */
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    strerr_die2sys(100,FATAL,ERR_OPEN_LOCK);
+  if (lock_ex(fdlock) == -1)
+    strerr_die2sys(100,FATAL,ERR_OBTAIN_LOCK);
+
+  getconf_line(&charset,"charset",0,FATAL,dir);
+  if (!stralloc_0(&charset)) die_nomem();
+
+  getconf_line(&prefix,"prefix",0,FATAL,dir);
+                                       /* support rfc2047-encoded prefix */
+  decodeHDR(prefix.s,prefix.len,&line,charset.s,FATAL);
+  unfoldHDR(line.s,line.len,&prefix,charset.s,&dummy,0,FATAL);
+                                       /* need only decoded one */
+
+                       /* Get message number */
+  switch(slurp("num",&num,32)) {
+    case -1:
+      strerr_die4sys(100,FATAL,ERR_READ,dir,"/num: ");
+    case 0:
+      strerr_die4x(100,FATAL,dir,"/num",ERR_NOEXIST);
+  }
+  if (!stralloc_0(&num)) die_nomem();
+  scan_ulong(num.s,&msgmax);
+  if (msgnum > msgmax) _exit(0);
+  if (msgnum) {
+    msgnum = (msgnum / 100) * 100 - 1;
+  }
+  while (++msgnum <= msgmax) {
+    if (msgnum == 1 || !(msgnum % 100)) {
+      if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
+      if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,msgnum / 100)))
+       die_nomem();
+      if (!stralloc_copy(&fnifn,&fnadir)) die_nomem();
+      if (!stralloc_copy(&fnif,&fnadir)) die_nomem();
+      if (!stralloc_cats(&fnif,"/index")) die_nomem();
+      if (!stralloc_cats(&fnifn,"/indexn")) die_nomem();
+      if (!stralloc_0(&fnadir)) die_nomem();
+      if (!stralloc_0(&fnifn)) die_nomem();
+      if (!stralloc_0(&fnif)) die_nomem();
+
+                       /* May not exist, so be nice and make it */
+      if (mkdir(fnadir.s,0755) == -1)
+       if (errno != error_exist)
+         strerr_die4sys(100,FATAL,ERR_CREATE,fnadir.s,": ");
+
+                       /* Open index */
+      fdindexn = open_trunc(fnifn.s);
+      if (fdindexn == -1)
+        strerr_die4sys(100,FATAL,ERR_WRITE,fnifn.s,": ");
+
+                       /* set up buffers for index */
+      substdio_fdbuf(&ssindex,write,fdindexn,indexbuf,sizeof(indexbuf));
+
+                       /* Get subject without the 'Subject: ' */
+                       /* make sure there is one */
+    }
+
+    if (!stralloc_copys(&fnaf,fnadir.s)) die_nomem();
+    if (!stralloc_cats(&fnaf,"/")) die_nomem();
+    if (!stralloc_catb(&fnaf,strnum,
+       fmt_uint0(strnum,(unsigned int) (msgnum % 100),2))) die_nomem();
+    if (!stralloc_0(&fnaf)) die_nomem();
+    fd = open_read(fnaf.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(100,FATAL,ERR_READ,fnaf.s,": ");
+    } else if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100)))
+        close(fd);
+    else {
+      subject.len = 0;         /* clear in case they're missing in msg */
+      author.len = 0;
+      received.len = 0;
+      r = idx_get_trimsubject();
+      close(fd);
+      if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem();
+      if (!stralloc_cats(&line,": ")) die_nomem();
+      makehash(subject.s,subject.len,hash);
+      if (!stralloc_catb(&line,hash,HASHLEN)) die_nomem();
+      if (!stralloc_cats(&line," ")) die_nomem();
+      if (r & 1)       /* reply */
+       if (!stralloc_cats(&line,"Re: ")) die_nomem();
+      if (!stralloc_cat(&line,&subject)) die_nomem();
+      if (!stralloc_cats(&line,"\n\t")) die_nomem();
+      if (!stralloc_cat(&line,&received)) die_nomem();
+      if (!stralloc_cats(&line,";")) die_nomem();
+
+      concatHDR(author.s,author.len,&lines,FATAL);
+      mkauthhash(lines.s,lines.len,hash);
+      if (!stralloc_catb(&line,hash,HASHLEN)) die_nomem();
+
+      decodeHDR(cp,author_name(&cp,lines.s,lines.len),&author,charset.s,FATAL);
+      (void) unfoldHDR(author.s,author.len,&lines,charset.s,&prefix,0,FATAL);
+
+      if (!stralloc_cats(&line," ")) die_nomem();
+      if (!stralloc_cat(&line,&lines)) die_nomem();
+      if (!stralloc_cats(&line,"\n")) die_nomem();
+      if (substdio_put(&ssindex,line.s,line.len) == -1)
+          strerr_die4sys(100,FATAL,ERR_WRITE,fnifn.s, ": ");
+    }
+
+    if (!((msgnum + 1) % 100) ||
+               (msgnum == msgmax)) {   /* last in this set */
+      if (substdio_flush(&ssindex) == -1)
+        strerr_die4sys(100,FATAL,ERR_FLUSH,fnifn.s, ": ");
+      if (fsync(fdindexn) == -1)
+        strerr_die4sys(100,FATAL,ERR_SYNC,fnifn.s, ": ");
+      if (fchmod(fdindexn,MODE_ARCHIVE | 0700) == -1)
+        strerr_die4sys(100,FATAL,ERR_WRITE,fnifn.s, ": ");
+      if (close(fdindexn) == -1)
+        strerr_die4sys(100,FATAL,ERR_CLOSE,fnifn.s,": ");
+      if (rename(fnifn.s,fnif.s) == -1)
+        strerr_die4x(111,FATAL,ERR_MOVE,fnifn.s,": ");
+    }
+  }
+  fd = open_append("indexed");
+  if (fd == -1)
+    strerr_die4sys(100,FATAL,ERR_CREATE,dir,"/indexed: ");
+  close(fd);
+  close(fdlock);
+  _exit(0);
+}
+
diff --git a/ezmlm-issubn.1 b/ezmlm-issubn.1
new file mode 100644 (file)
index 0000000..59affa0
--- /dev/null
@@ -0,0 +1,74 @@
+.TH ezmlm-issubn 1
+.SH NAME
+ezmlm-issubn \- test to see if an address is subscribed to a mailing list
+.SH SYNOPSIS
+.B ezmlm-issubn
+[
+.B \-nN
+]
+.I dir
+.I [ dir1 ... ]
+.SH DESCRIPTION
+.B ezmlm-issubn
+checks to see if the address obtained from the environment variable
+.I SENDER
+is subscribed to the mailing list stored in
+.I dir 
+or the mailing list in
+.I dir1 
+or ...
+
+If
+.I SENDER
+is not defined
+.B ezmlm-issubn
+exits with an error.
+
+If
+.I SENDER
+is on [any of] the mailing list[s],
+.B ezmlm-issubn
+exits with a zero exit code.
+
+If
+.I box\fB@\fIdomain
+is not on the mailing list,
+.B ezmlm-issubn
+exits 99. This exit code is non-success from a shell point of view, but to
+qmail it means "success and skip remaining lines in the .qmail file). Thus,
+a simple way to execute a delivery if the
+.ezmlm-issubn
+criteria are met is to place the
+.B ezmlm-issubn
+line first, followed by the action line. If SENDER is a subscriber, the
+action line is executed, if not, the line is ignored without the generation
+of an error condition. To generate a fatal error, just:
+
+.EX
+|/path/ezmlm-issubn
+.I dir1 ...
+|| (echo "err msg"; exit 100)
+
+|/path/action_for_subscribers
+|/path/more_for_subscribers
+.EE
+
+.B ezmlm-issubn
+exits 100 on permanent and 111 on temporary errors.
+.SH OPTIONS
+.TP
+.B \-n
+Negate exit code. Exit 99 if SENDER is a subscriber and exit 0 if not.
+This is useful when trying to exclude SENDERs.
+.TP
+.B \-N
+(Default.)
+Normal exit codes: 0 is the address is in any of the lists, 99 if not.
+.SH "SEE ALSO"
+ezmlm-list(1),
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm(5)
diff --git a/ezmlm-issubn.c b/ezmlm-issubn.c
new file mode 100644 (file)
index 0000000..3440b23
--- /dev/null
@@ -0,0 +1,64 @@
+/*$Id: ezmlm-issubn.c,v 1.13 1999/08/07 20:45:16 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "strerr.h"
+#include "env.h"
+#include "subscribe.h"
+#include "sgetopt.h"
+#include "errtxt.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-issubn: fatal: "
+
+void *psql = (void *) 0;
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-issubn: usage: ezmlm-issubn [-nN] dir [dir1 ...]");
+}
+
+void die_sender()
+{
+  strerr_die2x(100,FATAL,ERR_NOSENDER);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *addr;
+  int flagsub = 0;
+  int opt;
+
+  addr = env_get("SENDER");
+  if (!addr) die_sender();     /* REQUIRE sender */
+
+  while ((opt = getopt(argc,argv,"nNvV")) != opteof)
+    switch(opt) {
+      case 'n': flagsub = 99; break;
+      case 'N': flagsub = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-issubn version: ezmlm-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+  dir = argv[optind];
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  while ((dir = argv[optind++])) {
+    if (dir[0] != '/')
+      strerr_die2x(100,FATAL,ERR_SLASH);
+    if (issub(dir,addr,(char *) 0,FATAL)) {
+      closesql();
+      _exit(flagsub);          /* subscriber */
+    }
+  }
+  closesql();
+  if (flagsub)                 /* not subscriber anywhere */
+    _exit(0);
+  else
+    _exit(99);
+}
diff --git a/ezmlm-limit.1 b/ezmlm-limit.1
new file mode 100644 (file)
index 0000000..e977a10
--- /dev/null
@@ -0,0 +1,127 @@
+.TH ezmlm-limit 1
+.SH NAME
+ezmlm-limit \- Limits traffic to list
+.SH SYNOPSIS
+.B ezmlm-limit
+[
+.B -f\fI file
+][
+.B -dDF
+][
+.B \-t
+.I secs
+][
+.B -n
+.I msgs
+]
+.I dir
+
+.B ezmlm-limit
+is intended to limit traffic to a list in case an outside
+source sends excessive messages. Traffic is limited by converting the list
+to a message moderated list, or by deferring messages.
+
+.B ezmlm-limit
+reads
+.IR dir\fB/loopnum .
+If it does not exist or contains a time stamp more than
+.I secs
+seconds old, it is updated.
+If it exists, contains a time stamp less than
+.I secs
+seconds old, and
+.I msgs
+messages or more have passed since
+.I dir\fB/loopnum
+was created,
+.B ezmlm-limit
+creates
+.IR dir\fB/modpost .
+For lists set up with
+.B ezmlm-store(1)
+this results in future messages being moderated.
+
+As long as
+.I dir\fB/modpost
+does not exist,
+.B ezmlm-store(1)
+will simply forward the message to
+.BR ezmlm-send(1).
+Once
+.I dir\fB/modpost
+exists, messages will be queued for moderation. Thus, in case of error (such
+as misconfigured auto responders) subscribers are spared
+messages in excess of the limit. However, no messages are lost, since the
+moderator(s) can (selectively) approve queued messages. 
+
+If
+.I dir\fB/modpost
+already exists,
+.B ezmlm-limit
+will not take any action.
+
+Resetting the list requires manual
+removal of
+.IR dir\fB/modpost . Queued messages can be managed with regular moderation
+or approved in bulk with
+.BR ezmlm-accept(1) .
+
+.B ezmlm-limit
+is not required for normal lists, since qmail and
+ezmlm loop detection prevent problems caused by regular bounces. However,
+it affords some protection against malice, and severely misconfigured
+subscribers.
+
+.SH OPTIONS
+.TP
+.B \-d
+Defer excess traffic. Excess messages are deferred and qmail will re-deliver. As
+a consequence, traffic will be throttled to the maximum allowed. Due to qmail
+delivery back-off, messages may not be disseminated in the order received. If
+traffic is continuously above or close to the limit, some messages may be
+delayed for a long time or even bounced.
+.TP
+.B \-D
+(Default.)
+Make the list moderated once excess traffic is detected. This requires the
+list to be configured up with
+.B ezmlm-store(1)
+rather than
+.BR ezmlm-send(1) ,
+which is easiest done by first creating a message moderated list, then removing
+.IR dir\fB/modpost .
+.TP
+.B \-n\fI msgs
+Trigger after
+.I msgs
+messages (default 30).
+.TP
+.B \-f\fI file
+Instead of
+.I dir\fB/loopnum
+use
+.I file
+as the time stamp. This allows the use of multiple instances of
+.B ezmlm-limit
+for the same list. For instance, you may allow 60 messages per hour with one
+instance and 150 messages per day with another. If
+.I file
+is relative, this is relative to
+.IR dir .
+.TP
+.B \-F
+(Default.)
+Use
+.I dir\fB/loopnum
+as the time stamp.
+.TP
+.B \-t\fI secs
+Check the number of messages within period of
+.I secs
+seconds (default 3600 seconds).
+.SH "SEE ALSO"
+ezmlm-accept(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-store(1),
+ezmlm(5)
diff --git a/ezmlm-limit.c b/ezmlm-limit.c
new file mode 100644 (file)
index 0000000..9e21774
--- /dev/null
@@ -0,0 +1,134 @@
+/*$Id: ezmlm-limit.c,v 1.2 1999/10/31 18:58:48 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include "stralloc.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "sig.h"
+#include "lock.h"
+#include "getconf.h"
+#include "fmt.h"
+#include "now.h"
+#include "sgetopt.h"
+#include "error.h"
+#include "errtxt.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-limit: fatal: "
+#define INFO "ezmlm-limit: info: "
+
+unsigned long deltasecs = LIMSECS;     /* interval to test over (seconds) */
+unsigned long deltanum = LIMMSG;       /* max no messages in interval */
+                                       /* see idx.h. Usually 30 msg/3600 secs*/
+int flagd = 0;                         /* =0 create modpost, =1 ignore */
+                                       /* excess, =2 defer excess */
+int flagmod;                           /* list moderated */
+int flagloop;
+char *fn = TXT_LOOPNUM;
+
+void die_usage()
+{
+  strerr_die1x(100,
+       "ezmlm-limit: usage: ezmlm-limit [-f file] [-dDF] [-n messages]"
+       " [-t secs] dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_new() { strerr_die4sys(111,FATAL,ERR_WRITE,fn,": "); }
+
+stralloc line = {0};
+
+substdio ssnew;
+char newbuf[16];
+
+char strnum[FMT_ULONG];
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  int opt;
+  unsigned int pos;
+  unsigned long num, loopnum, when;
+  unsigned long loopwhen = 0L;
+  unsigned long numwhen = 0L;
+  int fd,fdlock;
+
+  (void) umask(022);
+  sig_pipeignore();
+  when = (unsigned long) now();
+
+  while ((opt = getopt(argc,argv,"dDf:Fn:t:")) != opteof)
+    switch(opt) {
+      case 'd': flagd = 1; break;
+      case 'D': flagd = 0; break;
+      case 'f': if (optarg && *optarg) fn = optarg; break;
+      case 'F': fn = TXT_LOOPNUM;
+      case 'n':
+                if (optarg)
+                  scan_ulong(optarg,&deltanum);
+                break;
+      case 't':
+                if (optarg)
+                  scan_ulong(optarg,&deltasecs);
+                break;
+      default:
+       die_usage();
+  }
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  if (argv[optind])
+    die_usage();       /* avoid common error of putting options after dir */
+  if ((flagmod = getconf_line(&line,"modpost",0,FATAL,dir)))
+    _exit(0);          /* already mod */
+                       /* lock for num and for writing loopnum */
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: ");
+
+  if (!getconf_line(&line,"num",0,FATAL,dir))
+    _exit(99);                                         /* no msgs */
+  if(!stralloc_0(&line)) die_nomem();
+  pos = scan_ulong(line.s,&num);                       /* current msg */
+  if ((flagloop = getconf_line(&line,fn,0,FATAL,dir))) {
+    if(!stralloc_0(&line)) die_nomem();
+    pos = scan_ulong(line.s,&loopnum);                 /* msg when written */
+    if (line.s[pos] == ':')
+      scan_ulong(line.s+pos+1,&loopwhen);              /* time written */
+  }
+  if (!flagloop || loopwhen + deltasecs < when || loopwhen > when) {
+                                       /* loopnum too old, bad or not there */
+      fd = open_trunc(fn);             /* no need to write crash-proof */
+      if (fd == -1) die_new();
+      substdio_fdbuf(&ssnew,write,fd,newbuf,sizeof(newbuf));
+      if (substdio_put(&ssnew,strnum,fmt_ulong(strnum,num)) == -1) die_new();
+      if (substdio_puts(&ssnew,":") == -1) die_new();
+      if (substdio_put(&ssnew,strnum,fmt_ulong(strnum,when)) == -1) die_new();
+      if (substdio_puts(&ssnew,"\n") == -1) die_new();
+      if (substdio_flush(&ssnew) == -1) die_new();
+      close(fd);
+  } else if (num >= loopnum + deltanum) {      /* excess messages */
+    if (!flagd) {
+      if ((fd = open_append("modpost")) == -1) /* create dir/modpost */
+         strerr_die3sys(111,FATAL,ERR_WRITE,"subpost:");
+      else {
+        close(fd);
+        unlink(fn);
+        strerr_die2x(0,INFO,ERR_EXCESS_MOD);
+      }
+    } else
+        strerr_die2x(111,FATAL,ERR_EXCESS_DEFER);
+  }
+  _exit(0);
+}
index 38e140a..352989a 100644 (file)
@@ -3,6 +3,11 @@
 ezmlm-list \- show the addresses on a mailing list
 .SH SYNOPSIS
 .B ezmlm-list
+[
+.B \-n\fI msgnum
+] [
+.B \-aAmMnNvV
+]
 .I dir
 .SH DESCRIPTION
 .B ezmlm-list
@@ -16,6 +21,40 @@ the addresses in a mailing list are under the control
 of a possibly malicious remote user.
 .B ezmlm-list
 does not strip control characters.
+.SH "GENERAL OPTIONS"
+.TP
+.B \-m
+(Default.)
+Use SQL support if available.
+.TP
+.B \-M
+Do not use SQL support even if available.
+This option can be used to manipulate
+a normal local subscriber database even for lists with SQL support.
+.TP
+.B \-n
+Print only the number of subscribers.
+.TP
+.B \-N
+(Default.)
+List subscriber addresses, one per line.
+.TP
+.B \-v
+Display
+.B ezmlm-list(1)
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-list(1)
+version information.
+.SH "SEE ALSO"
+ezmlm-list(1),
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm(5)
 .SH "SEE ALSO"
 ezmlm-sub(1),
 ezmlm-unsub(1),
index c6ec801..644c514 100644 (file)
@@ -1,62 +1,88 @@
-#include "stralloc.h"
-#include "substdio.h"
-#include "getln.h"
+/*$Id: ezmlm-list.c,v 1.15 1999/09/12 20:25:33 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
 #include "strerr.h"
-#include "error.h"
 #include "readwrite.h"
+#include "substdio.h"
+#include "subscribe.h"
 #include "exit.h"
-#include "open.h"
+#include "fmt.h"
+#include "sgetopt.h"
+#include "errtxt.h"
+#include "idx.h"
 
 #define FATAL "ezmlm-list: fatal: "
+
+int flagnumber = 0;    /* default list subscribers, not number of */
+
+void *psql = (void *) 0;
+
+char strnum[FMT_ULONG];
+
 void die_write()
 {
-  strerr_die2sys(111,FATAL,"unable to write: ");
+  strerr_die3sys(111,FATAL,ERR_WRITE,"stdout: ");
+}
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-list: usage: ezmlm-list [-mMnNvV] dir");
 }
 
-char outbuf[1024];
-substdio out = SUBSTDIO_FDBUF(write,1,outbuf,sizeof(outbuf));
-char inbuf[1024];
-substdio in;
+static char outbuf[512];
+static substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof(outbuf));
 
-stralloc line = {0};
+int subwrite(s,l)
+char *s;
+unsigned int l;
+{
+  return substdio_put(&ssout,s,l) | substdio_put(&ssout,"\n",1);
+}
 
-char fn[14] = "subscribers/?";
+int dummywrite(s,l)
+char *s;               /* ignored */
+unsigned int l;
+{
+  return (int) l;
+}
 
 void main(argc,argv)
 int argc;
 char **argv;
 {
   char *dir;
-  int fd;
-  int match;
+  int flagmysql = 1;   /* use if supported */
+  unsigned long n;
+  int opt;
 
-  dir = argv[1];
-  if (!dir) strerr_die1x(100,"ezmlm-list: usage: ezmlm-list dir");
+  while ((opt = getopt(argc,argv,"mMnNvV")) != opteof)
+    switch(opt) {
+      case 'm': flagmysql = 1; break;
+      case 'M': flagmysql = 0; break;
+      case 'n': flagnumber = 1; break;
+      case 'N': flagnumber = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+       "ezmlm-list version: ezmlml-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
 
-  if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+  dir = argv[optind++];
+  if (!dir) die_usage();
 
-  for (fn[12] = 64;fn[12] < 64 + 53;++fn[12]) {
-    fd = open_read(fn);
-    if (fd == -1) {
-      if (errno != error_noent)
-       strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
-    }
-    else {
-      substdio_fdbuf(&in,read,fd,inbuf,sizeof(inbuf));
-      for (;;) {
-        if (getln(&in,&line,&match,'\0') == -1)
-         strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
-       if (!match) break;
-       if (line.s[str_chr(line.s,'\n')])
-         strerr_die3x(111,FATAL,"newline in ",fn);
-       if (substdio_puts(&out,line.s + 1)) die_write();
-       if (substdio_put(&out,"\n",1) == -1) die_write();
-      }
-    }
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
 
-  }
+  if (dir[0] != '/')
+    strerr_die2x(100,FATAL,ERR_SLASH);
 
-  if (substdio_flush(&out) == -1) die_write();
+  if (flagnumber) {
+    n = putsubs(dir,0L,52L,dummywrite,flagmysql,FATAL);
+    if (substdio_put(&ssout,strnum,fmt_ulong(strnum,n)) == -1) die_write(FATAL);
+    if (substdio_put(&ssout,"\n",1) == -1) die_write(FATAL);
+  } else
+    (void) putsubs(dir,0L,52L,subwrite,flagmysql,FATAL);
+  if (substdio_flush(&ssout) == -1) die_write(FATAL);
+  closesql();
   _exit(0);
 }
index 834feed..ef898d8 100644 (file)
@@ -4,12 +4,19 @@ ezmlm-make \- create a new mailing list
 .SH SYNOPSIS
 .B ezmlm-make
 [
-.B \-aApP
+.B \-+
+][
+.B \-a..zABD..Z
+][
+.B \-C03..9 arg
 ]
 .I dir
+[
 .I dot
 .I local
 .I host
+.I [digestcode]
+]
 .SH DESCRIPTION
 .B ezmlm-make
 sets up a new mailing list,
@@ -20,6 +27,45 @@ All mailing list information is stored in a new directory,
 .IR dir .
 .I dir
 must be an absolute pathname, starting with a slash.
+.I dot
+must be an absolute file name starting with a slash. Arguments other than
+.I dir
+may be omitted when editing an existing list, using the
+.B \-e
+or
+.B \-+
+options (see below).
+
+.B ezmlm-make
+is controlled by a template,
+.BR .ezmlmrc .
+Described here is the behavior with the default template file.
+.B ezmlm-make
+will print a warning message before continuing,
+if the ezmlmrc version does not match the
+.B ezmlm-make
+version.
+
+.B ezmlm-make also creates
+.IR dir\fB/config ,
+where it stores all configuration information. By reading this file, you
+can rapidly get information about how the list is set up.
+.B ezmlm-make
+when used with the
+.B \-e
+switch will read information from this file. Thus, when using
+.B ezmlm-make
+.BR \-e ,
+you only need to specify the desired switches and switch arguments and
+.IR dir .
+With the
+.B \-+
+switch all switches become sticky, i.e. the default for all switches (and
+command line arguments) becomes the switches and arguments active for the
+list to be edited. Note that the choice of config file also is sticky,
+except when running
+.B ezmlm-make
+as root.
 
 .B ezmlm-make
 sets up four
@@ -38,12 +84,49 @@ these
 .B .qmail
 files.
 
+For message moderated lists,
+.B ezmlm-make
+sets up two additional
+.B .qmail
+files:
+.IR dot\fB-accept-default
+and
+.IR dot\fB-reject-default .
+
+For digested lists,
+.B ezmlm-make
+sets up another two
+.B .qmail
+file:
+.IR dot\fB-digest-return-default
+and
+.IR dot\fB-digest-owner .
+
+If
+.I digestcode
+is specified, digest creation by
+.B ezmlm-get(1)
+via trigger messages to the
+.I local\fB/@\fIhost\fB-dig.\fIdigestcode
+address is enabled.
+
+By default,
+.B ezmlm-make
+sets up lists to add a ``X-No-Archive: yes'' header to outgoing messages.
+Public archiving servers will interpret this header as a
+request not to archive messages from
+the list. It this in not what you desire, remove this header from
+.B ezmlmrc
+for global effects, or from
+.I dir\fB/headeradd
+for the specific list.
+
 Typical use of
 .B ezmlm-make
 by a normal user:
 
 .EX
-   ezmlm-make ~/SOS ~/.qmail-sos joe-sos isp.net
+   ezmlm-make ~joe/SOS ~joe/.qmail-sos joe-sos isp.net
 .EE
 
 Typical use of
@@ -54,34 +137,872 @@ by
 .EX
    ezmlm-make ~alias/SOS ~alias/.qmail-sos sos isp.net
 .EE
+.EX
+   chown -R alias ~alias/SOS 
+.EE
+
+Typical use of
+.B ezmlm-make
+by a normal user enabling automatic digests:
+
+.EX
+   ezmlm-make -d ~joe/SOS ~joe/.qmail-sos joe-sos isp.net
+.EE
+
+Typical use of
+.B ezmlm-make
+to change an existing list in ~joe/SOS to a message moderated list with
+remote administration, and enabling the remote administrator(s) to retrieve
+a subscriber list and to edit
+.I dir\fB/text
+files (digest are still enabled):
+
+.EX
+   ezmlm-make -emrldn ~joe/SOS
+.EE
+
+Mail can arrive at any time!
+For safe editing, turn on the sticky bit of the home directory before
+editing the list setup,
+then turn it off again (see
+.BR dot-qmail(5) ).
+
+Moderator addresses are added with
+
+.EX
+  ezmlm-sub ~joe/SOS/mod mod1@host1 mod2@host2 ...
+.EE
+
+.B ezmlm-make
+also creates the necessary text files in
+.IR dir\fB/text/ .
+
+.B ezmlm-make
+has a large number of switches to control all aspects of list generation.
+Only defaults or a small subset of switches are necessary for most list
+setups. Other options are present primarily to allow a external CGI script
+or other graphical user interface to use
+.B ezmlm-make
+to manipulate ezmlm list setups.
+.SH "VIRTUAL DOMAINS"
+For virtual domains,
+.B qmail(5)
+prefixes the name of the controlling user to the LOCAL part of the recipient
+address.
+.B ezmlm(5)
+needs to be informed of this in order to correctly interpret list commands.
+This is done by adjusting
+.IR dir\fB/inlocal .
+This adaptation is necessary only when ezmlm is used with qmail version 1.01
+or earlier.
+
+To create the list ``tl@virtual.dom'' where ``virtual.dom'' is controlled
+by ``vu'' (virtual.dom:vu), change identity to ``vu'' or chown files to
+that user after:
+
+.EX
+       ezmlm-make ~vu/dir ~vu/.qmail-tl tl virtual.dom
+.EE
+
+.EX
+       echo "vu-tl" > ~vu/inlocal
+.EE
+
+Thus, create the list exactly as for a list under ``alias'', but adjust
+.I dir\fB/inlocal
+to the list local name prefixed with the controlling user name.
 .SH OPTIONS
+All
+.B ezmlm-make
+letter switches except
+.BR \-v
+and
+.B \-V
+are available for interpretation via
+.IR ezmlmrc .
+Switches
+.BR -e ,
+.BR -E ,
+.BR -c ,
+and
+.BR -C
+have special meaning within the program.
+.I ezmlmrc
+customization should respect the function of the switches described here.
+.TP 5
+.B \-+
+Switches currently active for the list
+will be used, as modified by the current command line.
+Thus,
+.B \-+
+makes switches ``sticky''. By default,
+only switches specified on the current command line will be used.
+This switch implies
+.BR \-e 
+as it is meaningless except in edit mode. Note that the config file choice
+(see
+.B \-c
+and
+.BR \-C )
+is also sticky.
+.B ezmlmrc(5)
+is set up so that most text files (and
+.IR DIR\fB/headeradd ,
+.IR DIR\fB/headerremove )
+are not overwritten if they already exist so as to preserve
+manual customizations. If
+.I local
+is specified
+.B ezmlm-make
+overrides this behavior and all files are rewritten. You can also force
+.B ezmlm-make
+to rewrite all files by using
+.BR \-++ .
 .TP 5
 .B \-a
-(Default.) Archived.
+(Default.) Archived and configured with
+.B ezmlm-get(1)
+for archive access.
 .B ezmlm-make
 will touch
-.IR dir\fB/archived ,
+.I dir\fB/archived
+and
+.I dir\fB/indexed
 so that
-.B ezmlm-send
+.B ezmlm-send(1)
 will archive new messages.
 .TP
 .B \-A
 Not archived.
 .TP 5
+.B \-b
+Block archive. Only moderators are allowed to access the archive.
+.TP 5
+.B \-B
+(Default.)
+Archive access is open to anyone or subscribers only, depending
+on the
+.B \-g
+switch.
+.TP 5
+.B \-c
+Config.
+Use
+.I .ezmlmrc
+(see CONFIGURATION) from the directory where
+.I dot
+resides.
+.B ezmlm-make
+otherwise uses the
+system wide ezmlmrc
+file (normally /etc/ezmlmrc and if not found there, the ezmlmrc file
+in the ezmlm binary directory).
+The
+.B \-c
+switch may cause you to execute
+.B ezmlm-make
+based on a
+configuration file controlled by another user.
+.B ezmlm-make
+does not allow periods in any tag to restrict all actions to within
+.IR dir .
+Be careful with this option setting up lists for other users,
+especially when running
+.B ezmlm-make
+as root.
+.TP 5
+.B \-C\fI arg
+Like
+.BR \-c ,
+but use file
+.I arg
+as the ezmlmrc file.
+Use
+.B \-C\fI ''
+to override a default when using
+.B \-+
+or
+.BR \-e .
+.TP 5
+.B \-d
+Digest.
+.B ezmlm-make
+will set up the
+.I local\fB\-digest@host
+digest list to disseminate digest of the list messages. By default, this
+is done when 30 messages, 48 hours, or 64 kbytes of message body text have
+accumulated since the last digest. Use the
+.B \-4
+switch to override these defaults. See
+.B ezmlm-tstdig(1)
+and
+.B ezmlm-get(1)
+for more info.
+.TP 5
+.B \-D
+(Default.)
+No digest.
+Do not set up the digest list.
+.TP 5
+.B \-e
+Edit.
+.B ezmlm-make
+will remove links before creating them and accept
+if directories to be created are already present.
+.b ezmlm-make
+will also (via entries in
+.IR ezmlmrc )
+remove flags that are present but not desired for the current list.
+Thus, this option can be used to reconfigure existing lists without affecting
+moderator and subscriber lists or message archive. All desired
+.B ezmlm-make
+switches
+need to be specified. To make all switches sticky, i.e. only specify the
+ones changed from the previous setup, use
+.BR \-+ .
+Command line arguments other
+than
+.I dir
+can be omitted.
+In the unlikely case where
+.I dot
+is changed, you must manually remove the old links.
+Mail can arrive at any time!
+For safe editing, turn on the sticky bit of the home directory before
+using the edit function,
+then turn it off again (see
+.BR dot-qmail(5) ).
+.B ezmlmrc(5)
+is set up so that most text files (and
+.IR DIR\fB/headeradd ,
+.IR DIR\fB/headerremove )
+are not overwritten if they already exist so as to preserve
+manual customizations. If
+.I local
+is specified
+.B ezmlm-make
+overrides this behavior and all files are rewritten. You can also force
+.B ezmlm-make
+to rewrite all files by using
+.BR \-ee .
+.TP 5
+.B \-E
+(Default.)
+No edit.
+.B ezmlm-make
+will abort if directories or links to be created already exist. This prevents
+accidental reconfiguration of a pre-existing list, since the first action
+is to create the list directory.
+.TP 5
+.B \-f
+Prefix.
+.B ezmlm-make
+will set up the list so that the outgoing subject will be prefixed
+with the list name.
+.TP 5
+.B \-F
+(Default.)
+No prefix.
+.TP 5
+.B \-g
+Guard archive.
+Archive access requests from unrecognized SENDERs will be rejected.
+This restriction is safe, since replies are sent to the SENDER address.
+.TP 5
+.B \-G
+(Default.)
+Do not guard archive.
+Archive access request from any SENDER will be serviced.
+.TP 5
+.B \-h
+Help subscription. Subscriptions do not require confirmation. Strongly
+recommended against, since anyone can subscribe any address,
+but may be useful for some subscription moderated lists.
+.TP 5
+.B \-H
+(Default.)
+Subscription requires confirmation by reply to a message sent to the
+subscription address.
+.TP 5
+.B \-i
+Indexed for WWW archive access.
+.B ezmlm-make
+will create the list so that
+.B ezmlm-archive(1)
+is invoked to maintain an index suitable for use by
+.BR ezmlm-cgi(1) .
+.TP 5
+.B \-I
+(Default.)
+The list is created without
+.BR ezmlm-archive(1) .
+.TP 5
+.B \-j
+Jump off. Unsubscribe does not require confirmation. Strongly recommended
+against, since anyone can unsubscribe any address, but may be useful
+in some situations.
+.TP 5
+.B \-J
+(Default.)
+Unsubscribe requires confirmation by a reply to a message sent to the
+subscription address.
+.TP 5
+.B \-k
+kill.
+.B ezmlm-make
+sets up
+.IR dir\fB/deny/ .
+It sets up the list so that posts from addresses in
+.I dir\fB/deny/
+are rejected. This is useful in combination with the
+.B \-u
+switch to temporarily restrain offenders, such as misconfigured auto-responders
+or automatic spammers.
+It can also be used in combination with
+.B \-m
+to filter out SENDERs from whom the moderators do not want to see
+posts (again, bad
+re-mailers and spammers come to mind).
+
+To add/remove blacklisted addresses:
+
+.EX
+.B ezmlm-sub \fIdir\fB/deny \fIbad@host
+.EE
+
+.EX
+.B ezmlm-unsub \fIdir\fB/deny \fIbad@host
+.EE
+
+.TP 5
+.B \-K
+(Default.)
+Not kill.
+.I dir\fB/deny/
+is not created, and even if it exists, the contents will be ignored.
+.TP 5
+.B \-l
+List subscribers.
+.B ezmlm-make
+sets up the list so that remote administrators can request a subscriber list,
+and search the subscriber log.
+.TP 5
+.B \-L
+(Default.)
+The subscriber list cannot be obtained.
+.TP 5
+.B \-m
+Message moderation. (Please note that the 
+.B \-u switch modifies
+the action of this switch.)
+.B ezmlm-make
+will touch
+.I dir\fB/modpost
+and create
+.I dir\fB/mod/
+and
+.IR dir\fB/mod/subscribers/ ,
+where the moderator addresses are stored.
+.B ezmlm-make
+also creates
+.IR dir\fB/mod/pending/ ,
+.IR dir\fB/mod/accepted/ ,
+and
+.IR dir\fB/mod/rejected/ .
+These directories are used to queue messages awaiting moderation.
+.I dir\fB/editor
+will be set up to run
+.B ezmlm-store(1)
+to store incoming messages in the moderation queue and send moderation
+requests to the moderators.
+.I dir\fB/moderator
+will be set up to run
+.B ezmlm-moderate
+to process moderator
+.I accept
+or
+.I reject
+requests.
+
+To add/remove moderators:
+
+.EX
+.B ezmlm-sub \fIdir\fB/mod \fImoderator@host
+.EE
+
+.EX
+.B ezmlm-unsub \fIdir\fB/mod \fImoderator@host
+.EE
+
+.TP 5
+.B \-M
+(Default.)
+Message posting is not moderated.
+.TP 5
+.B \-n
+New text file.
+.B ezmlm-make
+sets up the list to allow remote administrators to edit files in
+.IR dir\fB/text/ .
+.TP 5
+.B \-N
+(Default.)
+Not new text file.
+Text file editing not allowed.
+.TP 5
+.B \-o
+Others rejected.
+Posts from addresses other than moderators are rejected. This is
+applicable to message moderated lists only
+(see
+.BR \-m ).
+The switch has no effect on other lists.
+.TP 5
+.B \-O
+(Default.)
+Others not rejected.
+For moderated lists, all posts are forwarded to the moderators.
+The switch has effects only on message moderated lists.
+.TP 5
 .B \-p
 (Default.) Public.
 .B ezmlm-make
 will touch
 .IR dir\fB/public ,
 so that
-.B ezmlm-manage
-will respond to administrative requests.
+.B ezmlm-manage(1)
+will respond to administrative requests and
+.B ezmlm-get
+will allow archive retrieval.
 .TP
 .B \-P
 Private.
+.B ezmlm-manage(1)
+and
+.B ezmlm-get(1)
+will allow only digest creation, remote administration, and archive
+retrieval by remote administrators, (if the list is configured with these
+options).
+.TP
+.B \-q
+ReQuest address is serviced.
+.B ezmlm-make
+will configure the list to process commands sent in the subject to
+.IR local\fB-request@\fIhost .
+This is done by adding a
+.B ezmlm-request(1)
+line to
+.IR dir\fB/manager .
+.TP
+.B \-Q
+(Default.)
+Do not process messages sent to the ``request'' address.
+.TP
+.B \-r
+Remote admin.
+.B ezmlm-make
+enables remote administration by touching
+.IR dir\fB/remote .
+Moderator(s) can unsubscribe and subscribe
+any address.
+See the
+.B \-m
+option on how moderator addresses are stored and manipulated.
+.TP
+.B \-R
+(Default.) No remote administration.
+.TP
+.B \-s
+Subscription moderation.
+.B ezmlm-make
+enables subscription moderation by touching
+.IR dir\fB/modsub .
+This affects subscriptions for both the main list and the digest list.
+See the
+.B \-m
+option on how moderator addresses are stored and manipulated.
+.TP
+.B \-S
+(Default.) Subscriptions are not moderated.
+.TP 5
+.B \-t
+Trailer.
+.B ezmlm-make
+will create
+.I dir\fB/text/trailer
+to set up the list to add a trailer to outgoing messages.
+.TP 5
+.B \-T
+No trailer.
+(Default.)
+.TP 5
+.B \-u
+User posts only.
+.B ezmlm-make
+sets up the list
+so that posts and archive access is restricted to subscribers.
+These are addresses subscribed to the main list, the digest, or added
+manually to the address database in
+.I dir\fB/allow/
+which accommodates addresses from e.g. subscribers working from an address
+other than their subscriber address.
+
+Posts from unrecognized SENDER addresses will be rejected.
+This is relatively easily defeated for posts.
+More secure alternatives are message moderated lists configured with the
+.B ezmlm-make \-m
+switch (without the
+.B \-u
+switch).
+
+There is no reason to combine of SENDER checks on posts with message
+moderation. Therefore, the combination of the
+.B \-u
+switch with the
+.B \-m
+switch is used for a configuration with SENDER restrictions (like with
+.B \-u
+alone), with the difference that posts from non-subscribers will be sent for
+moderation instead of being rejected. This allows the list admin to let
+non-subscribers post occasionally, as well as to catch subscribers posting
+from non-subscriber addresses.
+.TP
+.B \-U
+(Default.)
+Do not restrict posts based on SENDER address.
+.TP 5
+.B \-v
+Display
+.B ezmlm-make
+version information.
+.TP 5
+.B \-V
+Display
+.B ezmlm-make
+version information.
+.TP 5
+.B \-w
+Remove the
+.B ezmlm-warn(1)
+invocations from the list setup. It is assumed that
+.B ezmlm-warn(1)
+for both
+.I local@host
+and
+.I local\fB-digest@\fIhost
+will be run by other means, such as crond.
+If the list is set up with SQL support (see
+.BR \-6 ),
+restrict the list to a subset of addresses by adding the list name to
+the
+.I dir\fB/sql ,
+.I dir\fB/allow/sql ,
+.I dir\fB/digest/sql ,
+configuration files. Useful only when setting up the main list
+for a large distributed list supported by a SQL address database.
+Also, bounces will be handled by
+.B ezmlm-receipt(1)
+rather than
+.BR ezmlm-return(1) .
+As the main list will have only sublists as subscribers, it is desirable
+to log bounces and feedback messages rather than to remove a bouncing
+subscriber.
+.TP 5
+.B \-W
+(Default.)
+No address restriction. Normal
+use of
+.B ezmlm-warn(1)
+and
+.BR ezmlm-return(1) .
+.TP 5
+.B \-x
+eXtra.
+.B ezmlm-make
+will configure the list with a few extras:
+.I dir\fB/mimeremove
+will be configured to strip annoying mime parts such as excel spreadsheets,
+rtf text, html text etc from the messages. Messages consisting solely of
+this Content-type will be rejected. See
+.B ezmlm-send(1)
+and
+.B ezmlm-reject(1)
+for more info.
+.TP 5
+.B \-0 \fImainlist@host
+Make the list a sublist of list
+.IR mainlist@host .
+.TP 5
+.B \-3 \fIfromarg
+.B ezmlm-make
+sets up the list to replace the ``From:'' header of the message with
+``From:
+.IR fromarg ''.
+.TP 5
+.B \-4 \fItstdigopts
+.B ezmlm-make
+replaces the
+.B ezmlm-tstdig(1)
+switches used for digest generation with the text in
+.IR tstdigopts .
+This is part of a command line, NOT a specific switch. It should normally
+be placed within single quotes. This switch is mainly for programmatic
+use. For changing list defaults, it is usually easier to create a custom
+.I ~/.ezmlmrc
+file and edit it. The default is '-t24 -m30 -k64'. (See
+.B ezmlm-tstdig(1)
+for more info.)
+.TP
+.B \-5 \fIowner@host
+.B ezmlm-make
+will configure the list to forward mail directed to the list owner to
+.IR owner@host .
+.TP
+.B \-6\fI\ host:port:user:password:datab:table
+SQL connect info. Use the sql
+.IR host
+(default localhost),
+connecting to
+.I port
+(default port for SQL server) as
+.I user
+with
+.I password
+using database
+.I datab
+(default ezmlm)
+and the table root name
+.I table
+(default ezmlm)
+This will have no effect unless the ezmlm programs
+are compiled with SQL support.
+.TP
+.B \-7 \fI/msg_mod_path
+Make
+.I /path
+the path to the database for message moderators, if the list is set up for
+message moderation.
+.I /msg_mod_path
+must be an absolute pathname, starting with a slash. If not, it will be ignored.
+.TP
+.B \-8 \fI/sub_mod_path
+Make
+.I /sub_mod_path
+the path to the database for subscription moderators, if the list is set up for
+subscription moderation.
+.I /sub_mod_path
+must be an absolute pathname, starting with a slash. If not, it will be ignored.
+.TP
+.B \-9 \fI/rem_adm_path
+Make
+.I /path
+the path to the database for remote administrators, if the list is set up for
+remote administration.
+.I /rem_adm_path
+must be an absolute pathname, starting with a slash. If not, it will be ignored.
+.SH "LIST EDITING"
+When
+.B ezmlm-make
+is used with the
+.B \-e
+switch, and the list was previously created or edited with a
+new (ezmlm-idx >= 0.23) version of
+.BR ezmlm-make ,
+all arguments other than
+.I dir
+can be omitted. In this case, arguments will be read from
+.IR dir\fB/config .
+The appropriate flags must always be specified. To override
+.IR dot ,
+.IR local ,
+.IR host ,
+or
+.IR code ,
+all arguments must be specified.
+.SH CONFIGURATION
+This version of
+.B ezmlm-make
+is template driven. The template file consists of plain text with four types
+of tags. Both start in
+the first position of the line.
+No other text is allowed on the same line. For
+security reasons, no periods are allowed anywhere in a tag.
+Any line with a ``#'' in position 1 is ignored,
+as is any text preceding the first tag.
+.TP
+.B </filename#aI/>
+The following text will be copied to
+.IR dir\fB/filename
+if the options specified after the ``#'' are active, in this case
+.I archived
+and not
+.IR indexed .
+Any number of flags can be specified. This
+is used to adapt the files and
+messages to the type of list created. If no flags are
+used, the ``#'' can be omitted. If the file name is the same as the previous
+tag, or if it is omitted, the text will be added to the previous file.
+When a new file is opened the previous file is closed. Attempts to add
+more text to a already closed file overwrites its contents.
+
+An alternative to specify that a flag, e.g. ``4'' should not be active is
+to prefix the switch with ``^'', e.g. use ``^4''.
+The ``E'' flag is treated in a special manner. When the list
+is being edited, it evaluates to false if the file already exists,
+true if it does not. Thus, files using this condition are not overwritten
+when editing. This is useful for files that you frequently customize manually.
+.TP
+.B </-filename#eA/>
+.IR dir\fB/filename
+will be erased, if the options after the ``#'' are active, in this case
+.I not archived
+and
+.IR edit .
+.TP
+.B </+directory#aI/>
+The directory ``directory'' is created if the flags specified are active, in
+this case
+.I archived
+and not
+.IR indexed .
+If no flags are specified, the ``#'' can be
+omitted.
+.TP
+.B </:link/directory#aI/>
+.B dot\fI\-link
+is symlinked to
+.I dir/directory
+if the flags specified are active, in
+this case
+.I archived
+and not
+.IR indexed .
+If no flags are specified, the ``#'' can be
+omitted.
+.PP
+In addition,
+.I local
+is substituted for
+.BR <#L#> ,
+the part of
+.I dot
+between the first 2 hyphens (if any) for
+.BR <#1#> ,
+the part of
+.I dot
+between the second and third hyphen (if any) for
+.BR <#2#> ,
+.I host
+for
+.BR <#H#> ,
+.I dir
+for
+.BR <#D#> ,
+.I dot
+for
+.BR <#T#> ,
+.I digestcode
+for
+.BR <#C#> ,
+the set of all active flags for
+.BR <#F#> ,
+the config file used for
+.BR <#X#> ,
+and the path to the
+.B ezmlm
+binaries for
+.BR <#B#>
+anywhere in the text. Other tags of this format are copied to the files as is.
+
+.BR <#l#> ,
+.BR <#h#> ,
+.BR <#n#> ,
+.BR <#A#> ,
+.BR <#R#> ,
+will be substituted on-the-fly where appropriate for the
+.IR local
+or
+.IR local\fB\-digest
+local part of the list address, the
+.IR host ,
+the subscriber address or the moderation accept address,
+the message number, 
+and the subscription reply address or moderation reject address, respectively.
+The use of
+.BR <#l#>
+is to allow the same text file to be used for requests pertaining to both
+the main list and the digest list.
+.BR <#h#>
+makes it possible to share some files between lists.
+.BR <#n#>
+is defined only by programs where this makes sense, i.e.
+.B ezmlm-send(1)
+and
+.B ezmlm-get(1)
+
+In the absence of
+.B \-e
+and
+.B \-+
+switches,
+.B ezmlm-make
+will create the list directory before processing the template file, and
+create
+.I dir\fB/key
+after all other actions.
+
+.B ezmlm-make
+will use
+.B /etc/ezmlmrc
+and if not found
+.B ezmlmrc
+in the ezmlm binary directory. This can be overridden with the
+.B \-c
+and
+.B \-C
+switches.
+.SH BUGS
+.B ezmlm-make
+deals with the template file as us-ascii.
+Any occurrence of
+the characters ``</'' at the beginning of a line will disrupt
+.B ezmlm-make
+operation.
+Any occurrence of tags with the format ``<#X#>'' with 
+with 'X' being any digit, 'B', 'C', 'D', 'F', 'H', 'L', or 'T'
+will be substituted by
+.BR ezmlm-make .
+Any occurrence of a tag of this format with 'X' being 'h', 'l', 'A',
+or 'R' will be
+substituted by
+.B ezmlm-store
+and
+.B ezmlm-manage
+at run time.
+.B ezmlm-send
+will substitute tags with 'h' and 'l', and tags with 'n' will be replaced
+by the current message number.
+.B ezmlm-get
+will substitute tags ``<#h#>'', ``<#l#>'' in the same way. The
+tag ``<#n#>'' will be replaced by the digest message number which is the
+number of the first message in the digest.
+
+In practice, these character sequences are unlikely to occur in any
+multi-byte character set text. They also will not occur by chance
+in
+single-byte character sets where '<', '/', and '#'
+retain their us-ascii codes.
+.SH BUGS
+.B ezmlm-make
+cannot deal with ezmlmrc lines containing NUL (they will be truncated
+at the NUL). This needs to be fixed to make it 8-bit clean.
 .SH "SEE ALSO"
+ezmlm-clean(1),
+ezmlm-get(1),
 ezmlm-manage(1),
+ezmlm-moderate(1),
 ezmlm-send(1),
+ezmlm-store(1),
 ezmlm-sub(1),
 ezmlm-unsub(1),
 ezmlm(5)
index 09d2b3e..11fc6f4 100644 (file)
@@ -1,3 +1,6 @@
+/*Id: ezmlm-make.c,v 1.31 1997/12/08 23:44:02 lindberg Exp lindberg $*/
+/*$Name: ezmlm-idx-040 $*/
+
 #include <sys/types.h>
 #include <sys/time.h>
 #include "sgetopt.h"
 #include "substdio.h"
 #include "str.h"
 #include "auto_bin.h"
+#include "getln.h"
+#include "error.h"
+#include "lock.h"
+#include "errtxt.h"
+#include "idx.h"
+
+                       /* defaults. All other flags are false = 0 */
+char  *defflags="ap";          /* archived list -a */
+                               /* public list -p */
+                               /* no ezmlm-archive -I */
+                               /* no text edit for remote admin -D */
+                               /* not in edit mode -E */
+                               /* no subs list for remote admin -L */
+                               /* no remote admin -R */
+                               /* no message moderation -M */
+                               /* no subscription moderation -S */
+                               /* don't use .ezmlmrc from dot-file dir -C */
+                               /* no prefix -F */
+                               /* no trailer -T */
+
+#define NO_FLAGS ('z' - 'a' + 1)
+int flags[NO_FLAGS];           /* holds flags */
+
+char *popt[10];
+stralloc dotplus = {0};
+stralloc dirplus = {0};
+stralloc line = {0};
 
 #define FATAL "ezmlm-make: fatal: "
+#define WARNING "ezmlm-make: warning: "
 
 void die_usage()
 {
-  strerr_die1x(100,"ezmlm-make: usage: ezmlm-make [ -aApP ] dir dot local host");
+ strerr_die1x(100,
+  "ezmlm-make: usage: ezmlm-make [-+] [ -a..zA..Z03..9 ] dir dot local host");
 }
 void die_relative()
 {
-  strerr_die2x(100,FATAL,"dir must start with slash");
+  strerr_die2x(100,FATAL,ERR_SLASH);
 }
 void die_newline()
 {
-  strerr_die2x(100,FATAL,"newlines not allowed");
+  strerr_die2x(100,FATAL,ERR_NEWLINE);
 }
 void die_quote()
 {
-  strerr_die2x(100,FATAL,"quotes not allowed");
+  strerr_die2x(100,FATAL,ERR_QUOTE);
 }
 void die_nomem()
 {
-  strerr_die2x(111,FATAL,"out of memory");
+  strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
+void die_read()
+{
+  strerr_die4sys(111,FATAL,ERR_READ,dirplus.s,": ");
 }
 
+stralloc cmdline = {0};
+stralloc outline = {0};
+substdio sstext;
+char textbuf[1024];
+
+stralloc fname = {0};          /* file name */
+stralloc oldfname = {0};       /* file name from prevoius tag */
+stralloc dname = {0};          /* directory name */
+stralloc lname = {0};          /* link name */
+stralloc template = {0};       /* template file name */
+stralloc ext1 = {0};           /* dot = dir/.qmail-ext1-ext2-list */
+stralloc ext2 = {0};
+stralloc f = {0};
 stralloc key = {0};
 struct timeval tv;
+char sz[2] = "?";
 
 void keyadd(u)
 unsigned long u;
 {
   char ch;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem();
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem();
 }
 
 void keyaddtime()
@@ -54,11 +105,8 @@ void keyaddtime()
 
 char *dir;
 char *dot;
-char *local;
-char *host;
-
-stralloc dotplus = {0};
-stralloc dirplus = {0};
+char *local = (char *) 0;
+char *host = (char *) 0;
 
 void dirplusmake(slash)
 char *slash;
@@ -76,8 +124,12 @@ char *slash;
   if (!stralloc_cats(&dotplus,dash)) die_nomem();
   if (!stralloc_0(&dotplus)) die_nomem();
   dirplusmake(slash);
+  if (flags['e' - 'a'])
+    if (unlink(dotplus.s) == -1)
+      if (errno != error_noent)
+        strerr_die4x(111,FATAL,ERR_DELETE,dotplus.s,": ");
   if (symlink(dirplus.s,dotplus.s) == -1)
-    strerr_die4sys(111,FATAL,"unable to create ",dotplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_CREATE,dotplus.s,": ");
   keyaddtime();
 }
 
@@ -86,14 +138,15 @@ char *slash;
 {
   dirplusmake(slash);
   if (mkdir(dirplus.s,0755) == -1)
-    strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
+    if ((errno != error_exist) || !flags['e' - 'a'])
+      strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
   keyaddtime();
 }
 
 substdio ss;
 char ssbuf[SUBSTDIO_OUTSIZE];
 
-void fopen(slash)
+void f_open(slash)
 char *slash;
 {
   int fd;
@@ -101,335 +154,508 @@ char *slash;
   dirplusmake(slash);
   fd = open_trunc(dirplus.s);
   if (fd == -1)
-    strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
 
   substdio_fdbuf(&ss,write,fd,ssbuf,sizeof(ssbuf));
 }
 
-void fput(buf,len)
+void f_put(buf,len)
 char *buf;
 unsigned int len;
 {
   if (substdio_bput(&ss,buf,len) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
 }
-void fputs(buf)
+void f_puts(buf)
 char *buf;
 {
   if (substdio_bputs(&ss,buf) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
 }
 
-void fclose()
+void f_close()
 {
   if (substdio_flush(&ss) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_FLUSH,dirplus.s,": ");
   if (fsync(ss.fd) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_SYNC,dirplus.s,": ");
   if (close(ss.fd) == -1) /* NFS stupidity */
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_CLOSE,dirplus.s,": ");
   keyaddtime();
 }
 
+void frm(slash)
+char *slash;
+{
+  dirplusmake(slash);
+  if (unlink(dirplus.s) == -1)
+    if (errno != error_noent)
+    strerr_die4sys(111,FATAL,ERR_DELETE,dirplus.s,": ");
+}
+
+
 void main(argc,argv)
 int argc;
 char **argv;
 {
+  unsigned long euid;
   int opt;
-  int flagarchived;
-  int flagpublic;
+  int flagdo;
+  int flagnot;
+  int flagover;
+  int flagnotexist;
+  int flagforce = 0;
+  int flagforce_p = 0;
+  int usecfg = 0;
+  int match;
+  unsigned int next,i,j;
+  int last;
+  unsigned int slpos,hashpos,pos;
+  int fdin,fdlock,fdtmp;
+  char *p;
+  char *oldflags = (char *) 0;
+  char *code = (char *) 0;
+  char *cfname = (char *) 0;           /* config file if spec as -C cf_file */
+  char ch;
 
-  keyadd(getpid());
-  keyadd(getppid());
-  keyadd(getuid());
-  keyadd(getgid());
+  keyadd((unsigned long) getpid());
+  keyadd((unsigned long) getppid());
+  euid = (unsigned long) geteuid();
+  keyadd(euid);
+  keyadd((unsigned long) getgid());
   gettimeofday(&tv,(struct timezone *) 0);
   keyadd(tv.tv_sec);
 
-  umask(077);
-
-  flagarchived = 1;
-  flagpublic = 1;
+  (void) umask(077);
+       /* flags with defined use. vV for version. Others free */
 
-  while ((opt = getopt(argc,argv,"aApP")) != opteof)
-    switch(opt) {
-      case 'a': flagarchived = 1; break;
-      case 'A': flagarchived = 0; break;
-      case 'p': flagpublic = 1; break;
-      case 'P': flagpublic = 0; break;
-      default:
-       die_usage();
-    }
+  for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) {
+    flags[pos] = 0;
+  }
+  for (pos = 0; pos < 10; popt[pos++] = (char *) 0);
+
+  while ((opt = getopt(argc,argv,
+   "+aAbBcC:dDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0:3:4:5:6:7:8:9:"))
+           != opteof) {
+    if (opt == 'v' || opt == 'V')
+      strerr_die2x(0,"ezmlm-make version: ezmlm-0.53+",EZIDX_VERSION);
+    if (opt =='C')     /* treat this like nl switch to allow override of -c*/
+      cfname = optarg;
+    if (opt >= 'a' && opt <= 'z') {
+      flags[opt - 'a'] = 3;            /* Dominant "set" */
+      if (opt == 'e') flagforce++;     /* two 'e' => ignore 'E' */
+    } else if (opt >= 'A' && opt <= 'Z')
+      flags[opt - 'A'] = 2;            /* Dominant "unset" */
+    else if (opt >= '0' && opt <= '9')
+      popt[opt-'0'] = optarg;
+    else if (opt == '+') {
+      flagforce_p++;           /* two '+' => ignore 'E' */
+      flags['e' - 'a'] = 3;    /* -+ implies -e */
+      usecfg = 1;
+    } else
+      die_usage();
+  }
   argv += optind;
 
-  if (!(dir = *argv++)) die_usage();
-  if (!(dot = *argv++)) die_usage();
-  if (!(local = *argv++)) die_usage();
-  if (!(host = *argv++)) die_usage();
+  if (flagforce_p > 1 || flagforce > 1)
+    flagforce = 1;
+  else
+    flagforce = 0;
 
+  if (!(dir = *argv++)) die_usage();
   if (dir[0] != '/') die_relative();
   if (dir[str_chr(dir,'\'')]) die_quote();
   if (dir[str_chr(dir,'\n')]) die_newline();
-  if (local[str_chr(local,'\n')]) die_newline();
-  if (host[str_chr(host,'\n')]) die_newline();
 
-  dcreate("");
-  dcreate("/archive");
-  dcreate("/subscribers");
-  dcreate("/bounce");
-  dcreate("/text");
+  if (flags['e' - 'a'] & 1) {  /* lock for edit */
+    dirplusmake("/lock");
+    fdlock = open_append(dirplus.s);
+    if (fdlock == -1)
+      strerr_die4sys(111,FATAL,ERR_OPEN,dirplus.s,": ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,FATAL,ERR_OBTAIN,dirplus.s,": ");
+
+                               /* for edit, try to get args from dir/config */
+    dirplusmake("/config");
+    if ((fdin = open_read(dirplus.s)) == -1) {
+      if (errno != error_noent) die_read();
+    } else {
+      substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
+      for (;;) {
+       if (getln(&sstext,&line,&match,'\n') == -1) die_read();
+       if (!match) break;
+       if (line.s[0] == '#') continue;
+       if (line.len == 1) break;
+       if (line.s[1] != ':') break;
+       line.s[line.len - 1] = '\0';
+             if (!stralloc_cat(&cmdline,&line)) die_nomem();
+      }
+      close(fdin);
+      pos = 0;
+      while (pos < cmdline.len) {
+       ch = cmdline.s[pos];
+       pos += 2;
+       switch (ch) {
+         case 'X': if (euid && !flags['c' - 'a'] && (!cfname))
+                       cfname = cmdline.s + pos;       /* cmdline overrides */
+                   break;      /* for safety: ignore if root */
+          case 'T': dot = cmdline.s + pos; break;
+          case 'L': local = cmdline.s + pos; break;
+          case 'H': host = cmdline.s + pos; break;
+          case 'C': code = cmdline.s + pos; break;
+          case 'D': break;     /* no reason to check */
+          case 'F': oldflags = cmdline.s + pos; break;
+          default:
+                 if (ch == '0' || (ch >= '3' && ch <= '9')) {
+                   if (usecfg && !popt[ch - '0'])
+                     popt[ch - '0'] = cmdline.s + pos;
+                 } else
+                   strerr_die4x(111,FATAL,dirplus.s,ERR_SYNTAX,
+                        cmdline.s+pos);
+                 break;
+        }
+        pos += str_len(cmdline.s + pos) + 1;
+      }
+    }
+  }
 
+  if (p = *argv++) {
+    dot = p;
+    if (p = *argv++) {
+      if (!local || str_diff(local,p))
+       flagforce = 1;          /* must rewrite if list name changed */
+      local = p;
+      if (p = *argv++) {
+       if (!host || str_diff(host,p))
+         flagforce = 1;        /* must rewrite if list name changed */
+        host = p;
+        if (p = *argv++) {
+          code = p;
+        }
+      }
+    }
+  }
+  if (!dot || !local || !host) die_usage();
+  if (dot[0] != '/') die_relative();           /* force absolute dot */
+
+                       /* use flags from config, overridden with new values */
+                       /* if there are old flags, we're in "edit" and "-+" */
+                       /* Previous versions only wrote _set_ flags to */
+                       /* to DIR/confiag. We need to make sure that we */
+                       /* don't apply the defaults for non-specified ones! */
+  if (usecfg && oldflags && flags['e' - 'a']) {
+    while ((ch = *(oldflags++))) {
+      if (ch >= 'a' && ch <= 'z') {            /* unset flags ignored */
+        if (ch != 'e')
+          if (!flags[ch - 'a'])                        /* cmd line overrides */
+           flags[ch - 'a'] = 1;
+      }
+    }
+  }
 
-  linkdotdir("-owner","/owner");
-  linkdotdir("-default","/manager");
-  linkdotdir("-return-default","/bouncer");
-  linkdotdir("","/editor");
+  if (!usecfg) {                               /* apply defaults */
+    while (( ch = *(defflags++))) {            /* gets used up! */
+      if (ch >= 'a' && ch <= 'z') {            /* defensive! */
+       if (!flags[ch - 'a'])                   /* cmdline still overrides */
+         flags[ch - 'a'] = 1;
+      }
+    }
+  }
 
-  fopen("/lock"); fclose();
-  fopen("/lockbounce"); fclose();
-  if (flagpublic) {
-    fopen("/public"); fclose();
+  for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) {        /* set real flags */
+    if (flags[pos] & 2)                                /* 2 = "dominant" 0 */
+      flags[pos] = flags[pos] & 1;             /* 3 = "dominant" 1 */
   }
-  if (flagarchived) {
-    fopen("/archived"); fclose();
+
+  if (local[str_chr(local,'\n')]) die_newline();
+  if (host[str_chr(host,'\n')]) die_newline();
+
+       /* build 'f' for <#F#> */
+  if (!stralloc_ready(&f,28)) die_nomem();
+  if (!stralloc_copys(&f,"-")) die_nomem();
+  for (ch = 0; ch <= 'z' - 'a'; ch++) {                /* build string with flags */
+    if (flags[ch])
+      sz[0] = 'a' + ch;
+    else
+      sz[0] = 'A' + ch;
+    if (!stralloc_append(&f,sz)) die_nomem();
+  }
+
+  fdin = -1;   /* assure failure for .ezmlmrc in case flags['c'-'a'] = 0 */
+  slpos = str_len(dot);
+  while ((--slpos > 0) && dot[slpos] != '/');
+  if (dot[slpos] == '/') {
+    if (!stralloc_copyb(&template,dot,slpos+1)) die_nomem();   /* dot dir */
+    slpos += str_chr(dot+slpos,'-');
+    if (dot[slpos]) {
+      slpos++;
+      pos = slpos + str_chr(dot+slpos,'-');
+      if (dot[pos]) {
+        if (!stralloc_copyb(&ext1,dot+slpos,pos-slpos)) die_nomem();
+        pos++;
+        slpos = pos + str_chr(dot+pos,'-');
+        if (dot[slpos])
+          if (!stralloc_copyb(&ext2,dot+pos,slpos-pos)) die_nomem();
+      }
+    }
+  }
+  if (!stralloc_0(&ext1)) die_nomem();
+  if (!stralloc_0(&ext2)) die_nomem();
+  popt[1] = ext1.s;
+  popt[2] = ext2.s;
+       /* if 'c', template already has the dot directory. If 'C', cfname */
+       /* (if exists and != '') points to the file name to use instead. */
+  if (flags['c'-'a'] || (cfname && *cfname)) {
+    if (!flags['c'-'a']) {     /* i.e. there is a cfname specified */
+      if (!stralloc_copys(&template,cfname)) die_nomem();
+    } else
+      if (!stralloc_cats(&template,TXT_DOTEZMLMRC)) die_nomem();
+  if (!stralloc_0(&template)) die_nomem();
+  if ((fdin = open_read(template.s)) == -1)
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
+    else
+      strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
+  } else {                     /* /etc/ezmlmrc */
+    if (!stralloc_copys(&template,TXT_ETC_EZMLMRC)) die_nomem();
+    if (!stralloc_0(&template)) die_nomem();
+    if ((fdin = open_read(template.s)) == -1)
+      if (errno != error_noent)
+        strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
+      else {                   /* ezbin/ezmlmrc */
+       if (!stralloc_copys(&template,auto_bin)) die_nomem();
+       if (!stralloc_cats(&template,TXT_EZMLMRC)) die_nomem();
+       if (!stralloc_0(&template)) die_nomem();
+       if ((fdin = open_read(template.s)) == -1)
+         if (errno != error_noent)
+           strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
+         else
+           strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
+      }
   }
-  fopen("/num"); fputs("0\n"); fclose();
-  fopen("/inhost"); fputs(host); fputs("\n"); fclose();
-  fopen("/outhost"); fputs(host); fputs("\n"); fclose();
-  fopen("/inlocal"); fputs(local); fputs("\n"); fclose();
-  fopen("/outlocal"); fputs(local); fputs("\n"); fclose();
-
-  fopen("/mailinglist");
-  fputs("contact ");
-  fputs(local); fputs("-help@"); fputs(host); fputs("; run by ezmlm\n");
-  fclose();
-
-  fopen("/owner");
-  fputs(dir); fputs("/Mailbox\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fclose();
-
-  fopen("/manager");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-manage '"); fputs(dir); fputs("'\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fclose();
-
-  fopen("/editor");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-reject\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-send '"); fputs(dir); fputs("'\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fclose();
-
-  fopen("/bouncer");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-weed\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-return '"); fputs(dir); fputs("'\n");
-  fclose();
-
-  fopen("/headerremove");
-  fputs("\
-return-path\n\
-return-receipt-to\n\
-content-length\n\
-");
-  fclose();
-
-  fopen("/headeradd");
-  fclose();
-
-
-  fopen("/text/top");
-  fputs("Hi! This is the ezmlm program. I'm managing the\n");
-  fputs(local); fputs("@"); fputs(host); fputs(" mailing list.\n\n");
-  fclose();
-
-  fopen("/text/bottom");
-  fputs("\n--- Here are the ezmlm command addresses.\n\
-\n\
-I can handle administrative requests automatically.\n\
-Just send an empty note to any of these addresses:\n\n   <");
-  fputs(local); fputs("-subscribe@"); fputs(host); fputs(">:\n");
-  fputs("   Receive future messages sent to the mailing list.\n\n   <");
-  fputs(local); fputs("-unsubscribe@"); fputs(host); fputs(">:\n");
-  fputs("   Stop receiving messages.\n\n   <");
-  fputs(local); fputs("-get.12345@"); fputs(host); fputs(">:\n");
-  fputs("   Retrieve a copy of message 12345 from the archive.\n\
-\n\
-DO NOT SEND ADMINISTRATIVE REQUESTS TO THE MAILING LIST!\n\
-If you do, I won't see them, and subscribers will yell at you.\n\
-\n\
-To specify God@heaven.af.mil as your subscription address, send mail\n\
-to <");
-  fputs(local); fputs("-subscribe-God=heaven.af.mil@"); fputs(host);
-  fputs(">.\n\
-I'll send a confirmation message to that address; when you receive that\n\
-message, simply reply to it to complete your subscription.\n\
-\n");
-  fputs("\n--- Below this line is a copy of the request I received.\n\n");
-  fclose();
-
-  fopen("/text/sub-confirm");
-  fputs("To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-added to this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Your mailer should have a Reply feature that uses this address automatically.\n\
-\n\
-This confirmation serves two purposes. First, it verifies that I am able\n\
-to get mail through to you. Second, it protects you in case someone\n\
-forges a subscription request in your name.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-confirm");
-  fputs("To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-removed from this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Your mailer should have a Reply feature that uses this address automatically.\n\
-\n\
-I haven't checked whether your address is currently on the mailing list.\n\
-To see what address you used to subscribe, look at the messages you are\n\
-receiving from the mailing list. Each message has your address hidden\n\
-inside its return path; for example, God@heaven.af.mil receives messages\n\
-with return path ...-God=heaven.af.mil.\n\
-\n");
-  fclose();
-
-  fopen("/text/sub-ok");
-  fputs("Acknowledgment: I have added the address\n\
-\n\
-!A\n\
-\n\
-to this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-ok");
-  fputs("Acknowledgment: I have removed the address\n\
-\n\
-!A\n\
-\n\
-from this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/sub-nop");
-  fputs("Acknowledgment: The address\n\
-\n\
-!A\n\
-\n\
-is on this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-nop");
-  fputs("Acknowledgment: The address\n\
-\n\
-!A\n\
-\n\
-is not on this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/sub-bad");
-  fputs("Oops, that confirmation number appears to be invalid.\n\
-\n\
-The most common reason for invalid numbers is expiration. I have to\n\
-receive confirmation of each request within ten days.\n\
-\n\
-I've set up a new confirmation number. To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-added to this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Sorry for the trouble.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-bad");
-  fputs("Oops, that confirmation number appears to be invalid.\n\
-\n\
-The most common reason for invalid numbers is expiration. I have to\n\
-receive confirmation of each request within ten days.\n\
-\n\
-I've set up a new confirmation number. To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-removed from this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Sorry for the trouble.\n\
-\n");
-  fclose();
-
-  fopen("/text/get-bad");
-  fputs("Sorry, I don't see that message.\n\n");
-  fclose();
-
-  fopen("/text/bounce-bottom");
-  fputs("\n\
---- Below this line is a copy of the bounce message I received.\n\n");
-  fclose();
-
-  fopen("/text/bounce-warn");
-  fputs("\n\
-Messages to you seem to have been bouncing. I've attached a copy of\n\
-the first bounce message I received.\n\
-\n\
-If this message bounces too, I will send you a probe. If the probe bounces,\n\
-I will remove your address from the mailing list, without further notice.\n\
-\n");
-  fclose();
-
-  fopen("/text/bounce-probe");
-  fputs("\n\
-Messages to you seem to have been bouncing. I sent you a warning\n\
-message, but it bounced. I've attached a copy of the bounce message.\n\
-\n\
-This is a probe to check whether your address is reachable. If this\n\
-probe bounces, I will remove your address from the mailing list, without\n\
-further notice.\n\
-\n");
-  fclose();
-
-  fopen("/text/bounce-num");
-  fputs("\n\
-I've kept a list of which messages bounced from your address. Copies of\n\
-these messages may be in the archive. To get message 12345 from the\n\
-archive, send an empty note to ");
-  fputs(local); fputs("-get.12345@"); fputs(host); fputs(".\n\
-Here are the message numbers:\n\
-\n");
-  fclose();
-
-  fopen("/text/help");
-  fputs("\
-This is a generic help message. The message I received wasn't sent to\n\
-any of my command addresses.\n\
-\n");
-  fclose();
-
-  fopen("/key");
-  fput(key.s,key.len);
-  fclose();
 
+  dcreate("");         /* This is all we do, the rest is up to ezmlmrc */
+                       /* do it after opening template to avoid aborts */
+                       /* with created DIR. Well we also write DIR/key */
+                       /* at the end except in -e[dit] mode.           */
+
+  substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
+  if (!stralloc_0(&oldfname)) die_nomem();             /* init oldfname */
+  flagdo = 0;
+
+  if (getln(&sstext,&line,&match,'\n') == -1)
+    strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
+  if (!match)
+    strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
+  i = str_rchr(EZIDX_VERSION,'-');                     /* check version */
+  if (EZIDX_VERSION[i]) i++;
+  j = 0;
+  while (line.s[j] == EZIDX_VERSION[i] && j < line.len &&
+               EZIDX_VERSION[i] != '.' && EZIDX_VERSION[i]) {
+    i++; j++;                                          /* major */
+  }                                                    /* first minor */
+  if (EZIDX_VERSION[i] != '.' || j + 1 >= line.len ||
+               EZIDX_VERSION[i+1] != line.s[j+1])
+    strerr_warn2(WARNING,ERR_VERSION, (struct strerr *) 0);
+
+  for (;;) {
+    if (getln(&sstext,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
+    if (!match)
+      break;
+    if (line.s[0] == '#')                              /* comment */
+      continue;
+    if (!stralloc_0(&line)) die_nomem();
+    if (line.s[0] == '<' && line.s[1] == '/') {                /* tag */
+    if (line.s[str_chr(line.s,'.')])
+      strerr_die3x(100,FATAL,ERR_PERIOD,line.s);
+      flagdo = 1;
+      flagover = 0;
+      hashpos = 0;
+      pos = str_chr(line.s+2,'#')+2;
+      if (line.s[pos]) {
+        hashpos = pos;
+        pos++;
+        flagnot = 0;
+        while ((ch = line.s[pos]) &&
+              (line.s[pos] != '/' && line.s[pos+1] != '>')) {
+          if (ch == '^') {
+            flagnot = 1;
+            pos++;
+            continue;
+          }
+                       /* E is ignored. For files => create unless exists */
+         if (ch == 'E' && !flagnot ||  ch == 'e' && flagnot) {
+               if (flags['e' - 'a'] && !flagforce)
+           flagover = 1;               /* ignore #E & #^e, but set flagover */
+          } else if (ch >= 'a' && ch <= 'z')
+            flagdo &= (flags[ch - 'a'] ^ flagnot);
+          else if (ch >= 'A' && ch <= 'Z')
+            flagdo &= !(flags[ch - 'A'] ^ flagnot);
+          else if (ch >= '0' && ch <= '9')
+            flagdo &= (popt[ch - '0'] && *popt[ch - '0']) ^flagnot;
+          flagnot = 0;
+          pos++;
+        }
+        if (line.s[pos] != '/' || line.s[pos+1] != '>')
+          strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
+      } else {
+        flagdo = 1;
+        pos = 2;       /* name needs to be >= 1 char */
+        while (line.s[pos = str_chr(line.s+pos,'/')+pos]) {
+          if (line.s[pos+1] == '>')
+            break;
+          pos++;
+        }
+        if (!line.s[pos])
+          strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
+      }
+      if (hashpos)
+        pos = hashpos; /* points to after file name */
+
+      if (line.s[2] == '+') {                  /* mkdir */
+        if (!flagdo)
+          continue;
+        if (!stralloc_copys(&dname,"/")) die_nomem();
+        if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
+        if (!stralloc_0(&dname)) die_nomem();
+        dcreate(dname.s);
+        flagdo = 0;
+        continue;
+      } else if (line.s[2] == ':') {           /* ln -s */
+        if (!flagdo)
+          continue;
+        slpos = str_chr(line.s + 3,'/') + 3;
+        if (slpos >= pos)
+          strerr_die3x(100,FATAL,ERR_LINKDIR,line.s);
+        if (!stralloc_copyb(&dname,line.s+slpos,pos-slpos)) die_nomem();
+        if (!stralloc_copyb(&lname,line.s+3,slpos-3)) die_nomem();
+        if (!stralloc_0(&dname)) die_nomem();
+        if (!stralloc_0(&lname)) die_nomem();
+        linkdotdir(lname.s,dname.s);
+        flagdo = 0;
+        continue;
+      } else if (line.s[2] == '-') {           /* rm */
+        if (!flagdo)
+          continue;
+        if (!stralloc_copys(&dname,"/")) die_nomem();
+        if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
+        if (!stralloc_0(&dname)) die_nomem();
+        frm(dname.s);
+        flagdo = 0;
+        continue;
+      }
+                                               /* only plain files left */
+                                               /* first get file name */
+      if (pos > 2) {                   /* </#ai/> => add to open file */
+        if (!stralloc_copyb(&fname,line.s+1,pos-1)) die_nomem();
+        if (!stralloc_0(&fname)) die_nomem();
+      }
+
+      if (str_diff(fname.s, oldfname.s)) {
+       flagnotexist = 1;
+                       /* Treat special case of #E when editing which _should*/
+                       /* write only if the file does not exist. flagover */
+                       /* is set if we need to check */
+        if (flagover) {        /* skip if exists */
+         dirplusmake(fname.s);         /* decided by FIRST tag for file */
+         fdtmp = open_read(dirplus.s);
+         if (fdtmp == -1) {
+           if (errno != error_noent)
+             strerr_die3sys(111,ERR_OPEN,dirplus.s,": ");
+          } else {
+           flagnotexist = 0;           /* already there - don't do it */
+           close(fdtmp);
+         }
+        }
+        if (oldfname.len > 1) {
+          f_close();
+          if (!stralloc_copys(&oldfname,"")) die_nomem();
+          if (!stralloc_0(&oldfname)) die_nomem();
+          }
+          if (flagdo && flagnotexist) {
+            if (!fname.len)
+              strerr_die3x(100,FATAL,ERR_FILENAME,line.s);
+            f_open(fname.s);
+           if (!stralloc_copy(&oldfname,&fname)) die_nomem();
+          }
+        }
+       if (flagdo) flagdo = flagnotexist;
+        continue;
+    } else if (!flagdo)
+      continue;                        /* part not to go out */
+    last = -1;
+    next = 0;
+    outline.len = 0;
+    for (;;) {
+      pos = next + str_chr(line.s+next,'<');
+      if (line.s[pos] &&
+          line.s[pos+1] == '#' &&
+          line.s[pos+2] &&
+          line.s[pos+3] == '#' &&
+          line.s[pos+4] == '>') {      /* host/local */
+        if (!stralloc_catb(&outline,line.s+last+1,pos-last-1))
+                die_nomem();
+        switch (line.s[pos+2]) {
+          case 'B':            /* path to ezmlm binaries (no trailing /) */
+            if (!stralloc_cats(&outline,auto_bin)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'C':            /* digestcode */
+            if (code && *code)
+              if (!stralloc_cats(&outline,code)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'D':            /* listdir */
+            if (!stralloc_cats(&outline,dir)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'F':            /* flags */
+            if (!stralloc_cat(&outline,&f)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'H':            /* hostname */
+            if (!stralloc_cats(&outline,host)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'L':            /* local */
+            if (!stralloc_cats(&outline,local)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'T':            /* dot */
+            if (!stralloc_cats(&outline,dot)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'X':            /* config file name */
+            if (cfname)
+             if (!stralloc_cats(&outline,cfname)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          default:             /* copy unknown tag as is for e.g. <#A#> and*/
+                               /* <#R#> to be processed by -manage/store   */
+                                /* stuff in args for <#0#> .. <#9#> */
+            if ((line.s[pos+2] >= '0') && (line.s[pos+2] <= '9')) {
+              if (popt[line.s[pos+2] - '0'])
+                if (!stralloc_cats(&outline,popt[line.s[pos+2]-'0']))
+                  die_nomem();
+            } else
+              if (!stralloc_catb(&outline,line.s+pos,5)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+        }
+      } else {                 /* not tag */
+        if (line.s[pos]) {
+          next++;
+        } else {
+          if (!stralloc_catb(&outline,line.s+last+1,line.len-last-1))
+            die_nomem();
+          f_puts(outline.s);
+          break;
+        }
+      }
+    }
+  }
+
+  close(fdin);
+  if (oldfname.len > 1)
+    f_close();
+
+  if (!flags['e' - 'a']) {     /* don't redo key when editing a list */
+    f_open("/key");
+    f_put(key.s,key.len);
+    f_close();
+  }
   _exit(0);
 }
+
index a68438f..940a766 100644 (file)
@@ -2,13 +2,14 @@
 .SH NAME
 ezmlm-manage \- automatically manage a mailing list
 .SH SYNOPSIS
-.B ezmlm-manage
+.B ezmlm-manage [-bBcCdDeEfFlLmMsSqQuUvV]
 .I dir
 .SH DESCRIPTION
 .B ezmlm-manage
 handles administrative requests for the mailing list
 stored in
-.IR dir .
+.IR dir ,
+as well as for the associated digest list.
 
 .B ezmlm-manage
 is normally invoked from a
@@ -52,6 +53,56 @@ expects
 to match the first line of
 .IR dir\fB/inhost .
 
+If
+.I list
+is the first line of
+.IR dir\fB/inlocal
+followed by ``-digest'', the request is assumed to be for the
+associated digest list.
+.B ezmlm-manage
+handles these requests similarly, except that digest list subscriber addresses
+are stored in
+.IR dir\fB/digest/subscribers ,
+rather than in
+.IR dir\fB/subscribers .
+
+If
+.I list
+.IR dir\fB/inlocal
+followed by ``-allow'', the request is assumed to be for the
+associated
+.I dir\fB/allow/
+database. This database is used to store aliases of subscribers for lists
+allowing only posts only if the envelope sender is a subscriber.
+Actions on the
+.I dir\fB/allow/
+database follow the same rules as for the main list. The ezmlm messages are
+the same as those used for normal subscription, but refer to the
+.I list\fB-allow@\fIhost
+list. As this feature is designed for advanced uses and remote administrators
+only, this is not a problem.
+.B NOTE:
+No message is sent out to confirm additions to or removals from this
+database. However, the user can
+verify the change using the
+.I query
+action.
+The
+.I list\fB-deny
+addresses similarly controls
+.I dir\fB/deny/
+database for blocking posts with certain envelope senders.
+This database is available
+to remote administrators only, and only if the list has been set up with
+this feature (see
+.BR ezmlm-manage(1) ).
+.B NOTE:
+No message is sent out to confirm additions to or removals from this database.
+However, the remote admin can
+verify the change using the
+.I query
+action.
+
 .B ezmlm-manage
 copies
 .I dir\fB/mailinglist
@@ -65,6 +116,186 @@ field,
 refuses to respond.
 .B ezmlm-manage
 also refuses to respond to bounce messages.
+.SH OPTIONS
+.TP 5
+.B \-b
+(Default.)
+.B ezmlm-manage
+will add general instructions and the request to the outgoing message.
+.TP 5
+.B \-B
+.B ezmlm-manage
+will not add general instructions and the request to the outgoing message.
+This information gives the recipient of a confirmation request some
+information about the inciting message. Use of this switch will deny the
+recipient that information.
+.TP 5
+.B \-c
+(Default.)
+.B ezmlm-manage
+will reply to
+.I \-get
+commands.
+.TP
+.B \-C
+.B ezmlm-manage
+will not reply to
+.I \-get
+commands. This is useful for closed lists, where the owner for
+some reason wants to keep an archive, without making it available.
+.TP 5
+.B \-d
+Alias for the
+.B \-e
+switch for backwards compatibility.
+.TP 5
+.B \-D
+Alias for the
+.B \-E
+switch for backwards compatibility.
+.TP 5
+.B \-e
+.B ezmlm-manage
+allows remote administrators to edit files in
+.I dir\fB/text/
+via E-mail.
+.TP 5
+.B \-E
+(Default.)
+Text file editing not allowed.
+.TP 5
+.B \-f
+(Default.)
+The information in the ``From:'' is extracted from subscribe confirm
+messages and added to
+.I dir\fB/Log
+together with the subscriber address. This makes it easier for the list owner
+to help a subscriber who cannot determine his/her subscription address. If the
+.B \-S
+switch is used, the information is instead extracted from the subscribe
+request.
+.TP 5
+.B \-F
+Ignore ``From:'' headers.
+.TP 5
+.B \-l
+.B ezmlm-manage
+will send a subscriber list in reply to the 
+.I \-list
+command and
+the number of subscribers in reply to the
+.I \-listn
+comman if
+.I dir\fB/modsub
+or
+.I dir\fB/remote
+exist and target (the address the reply is to be sent to) is a moderator.
+.TP 5
+.B \-L
+(Default.)
+.B ezmlm-manage
+will ignore the
+.I \-list
+and
+.I \-listn
+commands.
+.TP 5
+.B \-m
+For lists with moderated subscription, require moderator approval also
+for unsubscribe requests. Remote admins are normally informed about
+unsuccessful unsubscribes. This creates problems when there is more
+than one moderator. Therefore, when the
+.B \-m
+switch is used, the notification is suppressed. Moderators can still
+determine the result by using the
+.I \-query
+command.
+.TP 5
+.B \-M
+(Default.)
+Requests to unsubscribe from moderated lists do not require moderator approval.
+.TP 5
+.I \-n
+(Default.)
+Target addresses
+will be notified if the are added or removed from the subscriber list.
+.TP 5
+.I \-N
+Target addresses will not be notified if they are added/removed from the
+subscriber list by remote admin or moderator action. Also, the target will
+not be notified if they were successfully added/removed when the
+.B \-S
+and
+.B \-U
+switches, respectively, are used.
+.TP 5
+.B \-q
+(Default.)
+Quiet. The list-owner is not notified of subscription events.
+.TP 5
+.B \-Q
+The list-owner is notified about failed unsubscribe attempts. Usually, these
+are from subscribers that do not remember their subscription address and
+require administrative assistance. Remote admins are notified when a unsubscribe
+request initiated by them fails. Thus, the owner is not notified about these
+events even if the
+.B \-Q
+switch is used.
+.TP 5
+.B \-QQ
+As for
+.BR \-Q ,
+and in addition, the list-owner is notified about all additions to or removals
+from the subscriber database. This is sometimes desired by owners of small
+lists.
+.TP 5
+.B \-s
+(Default.)
+.B ezmlm-manage
+will handle subscriptions with the normal target handshake.
+.TP 5
+.B \-S
+.B ezmlm-manage
+will eliminate the target handshake from the subscription
+process. This allows anyone to subscribe anybody else. DO NOT use this
+option, unless you know what you are doing. This option may be useful for 
+some moderated lists.
+.TP 5
+.B \-u
+(Default.)
+.B ezmlm-manage
+will handle unsubscribe requests with the normal target
+handshake.
+.TP 5
+.B \-U
+.B ezmlm-manage
+will eliminate the target handshake from the unsubscription
+process. This allows anyone to unsubscribe anybody else. DO NOT use this
+option, unless you know what you are doing.
+.TP 5
+.B \-v
+Display
+.B ezmlm-manage
+version information.
+.TP 5
+.B \-V
+Display
+.B ezmlm-manage
+version information.
+.SH "CHARACTER SETS"
+If
+.I dir\fB/charset
+exists,
+.B ezmlm-manage
+will use the character set listed for all messages. Otherwise, the
+default ``us-ascii'' will be used. The character set can be suffixed
+by ``:'' followed by a code. If the code is ``Q'', outgoing messages are 
+sent as ``Quoted-Printable'', if it is ``B'' they are sent ``base64'' encoded.
+Otherwise, text is sent as is.
+
+Incoming text for the
+.I \-edit
+is accepted unencoded or in either of these encodings.
 .SH SUBSCRIPTIONS
 If
 .I action
@@ -75,7 +306,25 @@ where
 is an appropriate code
 (depending on the target, the approximate time, and other factors),
 .B ezmlm-manage
-adds the target to the mailing list.
+adds the target to the mailing list
+if subscriptions are not moderated.
+For subscription moderated lists,
+.B ezmlm-manage
+sends a confirmation request to the moderators with the right
+.BR tc.\fIcookie
+address in its response.
+If
+.I action
+is
+.BR tc.\fIcookie ,
+where
+.I cookie
+is an appropriate code
+(depending on the target, the approximate time, and other factors),
+.B ezmlm-manage
+adds the target to the mailing list. If the target was not already a
+subscriber, a welcome message is sent to the target.
 
 If
 .I action
@@ -97,12 +346,84 @@ Actions of
 and
 .B unsubscribe
 are used in the same way to delete the target from the mailing list.
+Unsubscribes do not require moderator confirmation.
+
+Actions of
+.B vc.\fIcookie
+are used to confirm moderator-initiated unsubscribes for lists configured
+with remote administration (see MODERATION).
+
+If
+.I action
+is
+.BR query ,
+.B ezmlm-manage
+returns a message to the target indicating whether or not the target address
+is a subscriber.
+
+If
+.I action
+is
+.B info
+or
+.BR faq ,
+.B ezmlm-manage
+returns the contents of
+.I dir\fB/text/info
+or
+.IR dir\fB/text/info ,
+respectively.
 
 If
 .I dir\fB/public
 does not exist,
 .B ezmlm-manage
 rejects all subscription and unsubscription attempts.
+However, if the list is configured with remote administration,
+moderator-initiated subscribe and unsubscribe requests will still be
+honored. Also, if
+.I action
+is
+.IR help ,
+.B ezmlm-manage
+will still send help.
+.SH "TEXT FILE EDITING"
+If
+.I action
+is
+.BR edit ,
+the
+.B \-e
+switch is used, and the target address is that of a remote administrator,
+.B ezmlm-manage
+will reply with a list of editable file in
+.I dir\fB/text/
+and instructions for editing. Cookies for editing expire approximately 27.8
+hours after they are issued, or when a file has been changed, whichever is
+sooner. The size of the updated file is limited to 5120 bytes.
+
+If
+.I action
+is
+.BR edit.\fIfile ,
+the
+.B \-e
+switch is used, and the target address is that of a remote administrator,
+.B ezmlm-manage
+will return an editable copy of
+.IR file .
+
+If
+.I action
+is
+.BR ed.\fIcookie ,
+.B ezmlm-manage
+will verify that the edit cookie is still valid and that the file has
+not been modified since the cookie was issued. If the cookie passes
+these tests,
+.B ezmlm-manage
+will update
+.IR dir\fB/text\fI/file .
 .SH "ARCHIVE RETRIEVALS"
 If
 .I action
@@ -112,18 +433,146 @@ is
 sends back message
 .I num
 from
-.IR dir\fB/archive .
+.IR dir\fB/archive/ .
+This can be disabled with the
+.B \-C
+command line switch.
 
 If
 .I dir\fB/public
 does not exist,
 .B ezmlm-manage
 rejects all archive retrieval attempts.
+.SH MODERATION
+If
+.I dir\fB/modsub
+exists, subscriptions are moderated. Users can
+unsubscribe without moderator action, but moderator confirmation is required
+for subscriptions.
+
+If
+.I dir\fB/modsub
+starts with a forward slash, it is assumed that the content this is the base
+directory for the moderator database (
+.IR moddir ).
+Otherwise,
+.I moddir
+is assumed to be
+.IR dir\fB/mod/ .
+
+The moderator names are assumed
+to be stored in a set of files in
+.IR /moddir\fB/subscribers/ .
+
+I to add, remove, and list moderators, use respectively:
+
+.EX
+.B ezmlm-sub
+.I moddir
+.IR user@host
+.EE
+
+.EX
+.B ezmlm-unsub
+.I moddir
+.IR user@host
+.EE
+
+.EX
+.B ezmlm-list
+.I moddir
+.EE
+
+Subscription requests from potential
+subscribers will be sent for a second round of confirmation to all the
+moderators.
+If a moderator approves the request, a message confirming the
+subscription will be sent to the subscriber. The
+subscriber will not know which moderator approved the subscription.
+
+If more than one moderator replies to the confirmation request, the subscriber
+will not receive duplicate messages about being on (or not on) the mailing list.
+
+Unsubscribe requests from users are handled as for non-moderated lists.
+
+All subscribe confirmation requests requiring moderator action have a subject of
+.B CONFIRM subscribe to\fI listname@host.
+All unsubscribe confirmation requests in reply to moderator-initiated
+unsubscribe dialogs have a subject of
+.B CONFIRM unsubscribe from\fI listname@host.
+
+If
+.I dir\fB/remote
+exists (remote administration), moderators can initiate a request to
+subscribe a user 'username@userhost' by sending mail to
+.IR listname-subscribe\fB\-username=userhost\fI@host .
+The moderator (not the subscriber) will receive the confirmation request,
+and can complete the transaction. Moderators' request to unsubscribe
+users are handled analogously. Once an address is successfully added to
+or removed from the subscriber database by a moderator or remote admin,
+the user is notified of the action. If a moderator or remote admin's subscribe
+confirmation does not result in a change, i.e. if the address already was a
+subscriber, no notification is sent. If a remote admin's
+unsubscribe confirmation does not result in a change, i.e. the address was
+not a subscriber, a notification is sent to the remote admin. This is to make
+the remote admin aware that the address unsubscribed most likely is not the
+subscriber's subscription address.
+
+.I dir\fB/remote
+starts with a forward slash, it is assumed that the content this is the base
+directory for the moderator database (
+.IR moddir ).
+The moderator names are assumed
+to be stored in a set of files in
+.IR /moddir\fB/subscribers/ .
+If both
+.I dir\fB/modsub
+and
+.I dir\fB/remote
+exist, and both contain directory names, the directory name in
+.I dir\fB/modsub
+is used, and the
+.I dir\fB/remote
+entry is ignored.
+
+It is possible to set up
+a mailinglist for moderators only by using
+.I dir\fB/mod/
+as the list directory. Make sure that such a list is not public! Otherwise,
+anyone can become a moderator by subscribing to this list.
+
+If action is
+.B \-help
+and target is a moderator,
+.B ezmlm-manage
+will in addition to the usual help send
+.I dir\fB/text/mod-help
+containing instructions for moderators.
+
+If action is
+.B \-list
+and target is a moderator, the list is set up for subscription moderation
+or remote administration, and the
+.I \-l
+command line switch is used,
+.B ezmlm-manage
+will reply with an unsorted subscriber list. Extensions for digest subscribers
+and auxillary databases are supported (see above).
+
+If action is
+.BR \-log ,
+.B ezmlm-manage
+will reply with the contents of the
+.I Log
+file with the same access restrictions as for the
+.B \-list
+action.
 .SH "SEE ALSO"
 ezmlm-make(1),
 ezmlm-return(1),
 ezmlm-send(1),
 ezmlm-sub(1),
 ezmlm-unsub(1),
+ezmlm-list(1),
 ezmlm(5),
 qmail-command(8)
index d40ba0f..134c5a0 100644 (file)
@@ -1,3 +1,6 @@
+/*$Id: ezmlm-manage.c,v 1.86 1999/12/23 02:43:55 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "error.h"
 #include "fmt.h"
 #include "subscribe.h"
 #include "cookie.h"
+#include "sgetopt.h"
+#include "copy.h"
+#include "errtxt.h"
+#include "idx.h"
 
 #define FATAL "ezmlm-manage: fatal: "
-void die_usage() { strerr_die1x(100,"ezmlm-manage: usage: ezmlm-manage dir"); }
-void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+#define INFO "ezmlm-manage: info: "
+
+int flagverbose = 0;   /* default: Owner not informed about subdb changes */
+                       /* 1 => notified for failed unsub, 2 => for all */
+int flagnotify = 1;    /* notify subscriber of completed events. 0 also */
+                       /* suppresses all subscriber communication for */
+                       /* [un]sub if -U/-S is used */
+int flagbottom = 1;    /* default: copy request & admin info to message */
+int flaglist = 0;      /* default: do not reply to -list */
+int flagget = 1;       /* default: service -get requests */
+int flagsubconf = 1;   /* default: require user-confirm for subscribe */
+int flagunsubconf = 1; /* default: require user-confirm for unsubscribe */
+int flagunsubismod = 0;        /* default: do not require moderator approval to */
+                       /* unsubscribe from moderated list */
+int flagedit = 0;      /* default: text file edit not allowed */
+int flagstorefrom = 1; /* default: store from: line for subscribes */
+char flagcd = '\0';    /* default: do not use _Q_uoted printable or _B_ase64 */
+char encin = '\0';     /* encoding of incoming message */
+int flagdig = 0;       /* request is not for digest list */
+
+static const char hex[]="0123456789ABCDEF";
+char urlstr[] = "%00"; /* to build a url-encoded version of a char */
+
+int act = AC_NONE;     /* desired action */
+unsigned int actlen = 0;/* str_len of above */
+char *dir;
+char *workdir;
+char *sender;
+void *psql = (void *) 0;
+
+void die_usage() {
+  strerr_die1x(100,"ezmlm-manage: usage: ezmlm-manage "
+               "[-bBcCdDeEfFlLmMnNqQsSuUvV] dir"); }
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
 void die_badaddr()
 {
-  strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
+  strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+
+void die_cookie()
+{
+  strerr_die2x(100,FATAL,ERR_MOD_COOKIE);
 }
 
 stralloc inhost = {0};
@@ -37,34 +83,200 @@ stralloc inlocal = {0};
 stralloc outlocal = {0};
 stralloc key = {0};
 stralloc mailinglist = {0};
+stralloc mydtline = {0};
+stralloc target = {0};
+stralloc verptarget = {0};
+stralloc confirm = {0};
+stralloc line = {0};
+stralloc qline = {0};
+stralloc quoted = {0};
+stralloc moddir = {0};
+stralloc ddir = {0};
+stralloc modsub = {0};
+stralloc remote = {0};
+stralloc from = {0};
+stralloc to = {0};
+stralloc owner = {0};
+stralloc fromline = {0};
+stralloc text = {0};
+stralloc fnedit = {0};
+stralloc fneditn = {0};
+stralloc charset = {0};
 
 datetime_sec when;
 struct datetime dt;
+int match;
+unsigned int max;
 
 char strnum[FMT_ULONG];
 char date[DATE822FMT];
 char hash[COOKIE];
+char boundary[COOKIE];
 datetime_sec hashdate;
-stralloc target = {0};
-stralloc confirm = {0};
-stralloc line = {0};
-stralloc quoted = {0};
 
-int hashok(action)
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+
+substdio sstext;       /* editing texts and reading "from" */
+char textbuf[512];
+
+substdio ssfrom;       /* writing "from" */
+char frombuf[512];
+
+int fdlock;
+
+void lock()
+{
+    fdlock = open_append("lock");
+    if (fdlock == -1)
+      strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: ");
+}
+
+void unlock()
+{
+    close(fdlock);
+}
+
+void make_verptarget()
+/* puts target with '=' instead of last '@' into stralloc verptarget */
+/* and does set_cpverptarget */
+{
+  unsigned int i;
+
+  i = str_rchr(target.s,'@');
+  if (!stralloc_copyb(&verptarget,target.s,i)) die_nomem();
+  if (target.s[i]) {
+    if (!stralloc_append(&verptarget,"=")) die_nomem();
+    if (!stralloc_cats(&verptarget,target.s + i + 1)) die_nomem();
+  }
+  if (!stralloc_0(&verptarget)) die_nomem();
+  set_cpverptarget(verptarget.s);
+}
+
+void store_from(frl,adr)
+/* rewrites the from file removing all that is older than 1000000 secs  */
+/* and add the curent from line (frl). Forget it if there is none there.*/
+/* NOTE: This is used only for subscribes to moderated lists!           */
+stralloc *frl; /* from line */
+char *adr;
+{
+  int fdin;
+  int fdout;
+  unsigned long linetime;
+
+  if (!flagstorefrom || !frl->len) return;     /* nothing to store */
+  lock();
+  if ((fdout = open_trunc("fromn")) == -1)
+    strerr_die3sys(111,FATAL,ERR_OPEN,"fromn: ");
+  substdio_fdbuf(&ssfrom,write,fdout,frombuf,(int) sizeof(frombuf));
+  if ((fdin = open_read("from")) == -1) {
+    if (errno != error_noent)
+      strerr_die3sys(111,FATAL,ERR_OPEN,"from: ");
+  } else {
+      substdio_fdbuf(&sstext,read,fdin,textbuf,(int) sizeof(textbuf));
+      for (;;) {
+       if (getln(&sstext,&line,&match,'\n') == -1)
+       strerr_die3sys(111,FATAL,ERR_READ,"from: ");
+       if (!match) break;
+       (void) scan_ulong(line.s,&linetime);
+       if (linetime + 1000000 > when && linetime <= when)
+         if (substdio_bput(&ssfrom,line.s,line.len))
+           strerr_die3sys(111,FATAL,ERR_WRITE,"fromn: ");
+      }
+      close(fdin);
+  }                                    /* build new entry */
+  if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,when))) die_nomem();
+  if (!stralloc_append(&line," ")) die_nomem();
+  if (!stralloc_cats(&line,adr)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  if (!stralloc_catb(&line,frl->s,frl->len)) die_nomem();
+  if (!stralloc_append(&line,"\n")) die_nomem();
+  if (substdio_bput(&ssfrom,line.s,line.len) == -1)
+    strerr_die3sys(111,FATAL,ERR_WRITE,"fromn: ");
+  if (substdio_flush(&ssfrom) == -1)
+    strerr_die3sys(111,FATAL,ERR_WRITE,"fromn: ");
+  if (fsync(fdout) == -1)
+    strerr_die3sys(111,FATAL,ERR_SYNC,"fromn: ");
+  if (close(fdout) == -1)
+    strerr_die3sys(111,FATAL,ERR_CLOSE,"fromn: ");
+  if (rename("fromn","from") == -1)
+    strerr_die3sys(111,FATAL,ERR_MOVE,"from: ");
+  unlock();
+}
+
+char *get_from(adr,act)
+/* If we captured a from line, it will be from the subscriber, except   */
+/* when -S is used when it's usually from the subscriber, but of course */
+/* could be from anyone. The matching to stored data is required only   */
+/* to support moderated lists, and in cases where a new -sc is issued   */
+/* because an old one was invalid. In this case, we read through the    */
+/* from file trying to match up a timestamp with that starting in       */
+/* *(act+3). If the time stamp matches, we compare the target address   */
+/* itself. act + 3 must be a legal part of the string returns pointer to*/
+/* fromline, NULL if not found. Since the execution time from when to   */
+/* storage may differ, we can't assume that the timestamps are in order.*/
+
+char *adr;             /* target address */
+char *act;             /* action */
+{
+  int fd;
+  char *fl;
+  unsigned int pos;
+  unsigned long thistime;
+  unsigned long linetime;
+
+  if (!flagstorefrom) return 0;
+  if (fromline.len) {  /* easy! We got it in this message */
+    if (!stralloc_0(&fromline)) die_nomem(FATAL);
+    return fromline.s;
+  }                    /* need to recover it from DIR/from */
+  fl = 0;
+  (void) scan_ulong(act+3,&thistime);
+  if ((fd = open_read("from")) == -1)
+    if (errno == error_noent)
+      return 0;
+    else
+      strerr_die3x(111,FATAL,ERR_READ,"from: ");
+  substdio_fdbuf(&sstext,read,fd,textbuf,(int) sizeof(textbuf));
+  for (;;) {
+    if (getln(&sstext,&fromline,&match,'\n') == -1)
+      strerr_die3sys(111,FATAL,ERR_READ,"from: ");
+    if (!match) break;
+    fromline.s[fromline.len - 1] = (char) 0;
+       /* now:time addr\0fromline\0 read all. They can be out of order! */
+    pos = scan_ulong(fromline.s,&linetime);
+    if (linetime != thistime) continue;
+    if (!str_diff(fromline.s + pos + 1,adr)) {
+      pos = str_len(fromline.s);
+      if (pos < fromline.len) {
+       fl = fromline.s + pos + 1;
+       break;
+      }
+    }
+  }
+  close(fd);
+  return fl;
+}
+
+int hashok(action,ac)
 char *action;
+char *ac;
 {
   char *x;
-  unsigned long u;
+  datetime_sec u;
 
-  x = action + 4;
+  x = action + 3;
   x += scan_ulong(x,&u);
   hashdate = u;
   if (hashdate > when) return 0;
   if (hashdate < when - 1000000) return 0;
 
   u = hashdate;
-  strnum[fmt_ulong(strnum,u)] = 0;
-  cookie(hash,key.s,key.len,strnum,target.s,action + 1);
+  strnum[fmt_ulong(strnum,(unsigned long) u)] = 0;
+  cookie(hash,key.s,key.len - flagdig,strnum,target.s,ac);
 
   if (*x == '.') ++x;
   if (str_len(x) != COOKIE) return 0;
@@ -77,127 +289,528 @@ int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
   qmail_put(&qq,buf,len);
   return len;
 }
+
 char qqbuf[1];
-substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
 
-char inbuf[1024];
-substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
-substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+int code_qput(s,n)
+char *s;
+unsigned int n;
+{
+    if (!flagcd)
+      qmail_put(&qq,s,n);
+    else {
+      if (flagcd == 'B')
+        encodeB(s,n,&qline,0,FATAL);
+      else
+        encodeQ(s,n,&qline,FATAL);
+      qmail_put(&qq,qline.s,qline.len);
+    }
+    return 0;          /* always succeeds */
+}
 
-substdio sstext;
-char textbuf[1024];
+int subto(s,l)
+char *s;
+unsigned int l;
+{
+  qmail_put(&qq,"T",1);
+  qmail_put(&qq,s,l);
+  qmail_put(&qq,"",1);
+  return (int) l;
+}
 
-void copy(fn)
-char *fn;
+int code_subto(s,l)
+char *s;
+unsigned int l;
 {
-  int fd;
-  int match;
+  code_qput(s,l);
+  code_qput("\n",1);
+  return (int) l;
+}
 
-  fd = open_read(fn);
-  if (fd == -1)
-    strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
+int dummy_to(s,l)
+char *s;       /* ignored */
+unsigned int l;
+{
+  return (int) l;
+}
 
-  substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
-  for (;;) {
-    if (getln(&sstext,&line,&match,'\n') == -1)
-      strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
-
-    if (match)
-      if (line.s[0] == '!') {
-       if (line.s[1] == 'R') {
-         qmail_puts(&qq,"   ");
-         qmail_puts(&qq,confirm.s);
-         qmail_puts(&qq,"\n");
-         continue;
-       }
-       if (line.s[1] == 'A') {
-         qmail_puts(&qq,"   ");
-         qmail_puts(&qq,target.s);
-         qmail_puts(&qq,"\n");
-         continue;
-       }
+void transferenc()
+{
+       if (flagcd) {
+          qmail_puts(&qq,"\n--");
+          qmail_put(&qq,boundary,COOKIE);
+          qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+          qmail_puts(&qq,charset.s);
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"quoted-printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        } else
+          qmail_puts(&qq,"\n");
+}
+
+void to_owner()
+{
+       if (!stralloc_copy(&owner,&outlocal)) die_nomem();
+       if (!stralloc_cats(&owner,"-owner@")) die_nomem();
+       if (!stralloc_cat(&owner,&outhost)) die_nomem();
+       if (!stralloc_0(&owner)) die_nomem();
+       qmail_to(&qq,owner.s);
+}
+
+void mod_bottom()
+{
+      copy(&qq,"text/mod-sub",flagcd,FATAL);
+      copy(&qq,"text/bottom",flagcd,FATAL);
+      code_qput(TXT_SUPPRESSED,str_len(TXT_SUPPRESSED));
+      if (flagcd) {
+        qmail_puts(&qq,"\n--");
+        qmail_put(&qq,boundary,COOKIE);
+        qmail_puts(&qq,"--\n");
       }
+      if (flagcd == 'B') {
+        encodeB("",0,&line,2,FATAL);   /* flush */
+        qmail_put(&qq,line.s,line.len);
+      }
+      qmail_from(&qq,from.s);
+}
+void msg_headers()
+               /* Writes all the headers up to but not including subject */
+{
+  int flaggoodfield;
+  int flagfromline;
+  int flaggetfrom;
+  unsigned int pos;
 
+  qmail_puts(&qq,"Mailing-List: ");
+  qmail_put(&qq,mailinglist.s,mailinglist.len);
+  if(getconf_line(&line,"listid",0,FATAL,dir)) {
+    qmail_puts(&qq,"\nList-ID: ");
     qmail_put(&qq,line.s,line.len);
+  }
+  if (!quote(&quoted,&outlocal)) die_nomem();  /* quoted has outlocal */
+  qmail_puts(&qq,"\nList-Help: <mailto:");     /* General rfc2369 headers */
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"-help@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,">\nList-Post: <mailto:");
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,">\nList-Subscribe: <mailto:");
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"-subscribe@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,">\nDate: ");
+  datetime_tai(&dt,when);
+  qmail_put(&qq,date,date822fmt(date,&dt));
+  qmail_puts(&qq,"Message-ID: <");
+  if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
+     die_nomem();
+  if (!stralloc_append(&line,".")) die_nomem();
+  if (!stralloc_catb(&line,strnum,
+               fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  qmail_puts(&qq,line.s);
+               /* "unique" MIME boundary as hash of messageid */
+  cookie(boundary,"",0,"",line.s,"");
+  qmail_puts(&qq,">\nFrom: ");
+  qmail_put(&qq,quoted.s,quoted.len);
+  if (act == AC_HELP)          /* differnt "From:" for help to break auto- */
+    qmail_puts(&qq,"-return-@");       /* responder loops */
+  else
+    qmail_puts(&qq,"-help@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,"\nTo: ");
+  if (!quote2(&quoted,target.s)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"\n");
+  if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem();
+  if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"@")) die_nomem();
+  if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+  qmail_put(&qq,mydtline.s,mydtline.len);
 
-    if (!match)
-      break;
+  flaggoodfield = 0;
+  flagfromline = 0;
+       /* do it for -sc, but if the -S flag is used, do it for -subscribe */
+  flaggetfrom = flagstorefrom &&
+        ((act == AC_SC) || ((act == AC_SUBSCRIBE) && !flagsubconf));
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (!match) break;
+    if (line.len == 1) break;
+    if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+      flagfromline = 0;
+      flaggoodfield = 0;
+      if (case_startb(line.s,line.len,"mailing-list:"))
+        strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+      if (line.len == mydtline.len)
+       if (byte_equal(line.s,line.len,mydtline.s))
+          strerr_die2x(100,FATAL,ERR_LOOPING);
+      if (case_startb(line.s,line.len,"delivered-to:"))
+        flaggoodfield = 1;
+      else if (case_startb(line.s,line.len,"received:"))
+        flaggoodfield = 1;
+      else if (case_startb(line.s,line.len,"content-transfer-encoding:")) {
+        pos = 26;
+        while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos;
+        if (case_startb(line.s+pos,line.len-pos,"base64"))
+          encin = 'B';
+        else if (case_startb(line.s+pos,line.len-pos,"quoted-printable"))
+          encin = 'Q';
+      } else if (flaggetfrom && case_startb(line.s,line.len,"from:")) {
+       flagfromline = 1;               /* for logging subscriber data */
+       pos = 5;
+       while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos;
+        if (!stralloc_copyb(&fromline,line.s + pos,line.len - pos - 1))
+         die_nomem();
+      }
+    } else {
+      if (flagfromline == 1)           /* scrap terminal '\n' */
+        if (!stralloc_catb(&fromline,line.s,line.len - 1)) die_nomem();
+    }
+    if (flaggoodfield)
+      qmail_put(&qq,line.s,line.len);
+  }
+  qmail_puts(&qq,"MIME-Version: 1.0\n");
+  if (flagcd) {
+    qmail_puts(&qq,"Content-Type: multipart/mixed; charset=");
+    qmail_puts(&qq,charset.s);
+    qmail_puts(&qq,";\n\tboundary=");
+    qmail_put(&qq,boundary,COOKIE);
+  } else {
+    qmail_puts(&qq,"Content-type: text/plain; charset=");
+    qmail_puts(&qq,charset.s);
   }
+  qmail_puts(&qq,"\n");
+}
 
-  close(fd);
+int geton(action)
+char *action;
+{
+  char *fl;
+  int r;
+  unsigned int i;
+  unsigned char ch;
+
+  fl = get_from(target.s,action);              /* try to match up */
+  switch((r = subscribe(workdir,target.s,1,fl,"+",1,-1,(char *) 0,FATAL))) {
+    case 1:
+           qmail_puts(&qq,"List-Unsubscribe: <mailto:");       /*rfc2369 */
+           qmail_put(&qq,outlocal.s,outlocal.len);
+           qmail_puts(&qq,"-unsubscribe-");
+               /* url-encode since verptarget is controlled by sender */
+               /* note &verptarget ends in '\0', hence len - 1! */
+           for (i = 0; i < verptarget.len - 1; i++) {
+             ch = verptarget.s[i];
+             if (str_chr("\"?;<>&/:%+#",ch) < 10 ||
+                        (ch <= ' ') || (ch & 0x80)) {
+               urlstr[1] = hex[ch / 16];
+               urlstr[2] = hex[ch & 0xf];
+               qmail_put(&qq,urlstr,3);
+             } else {
+               qmail_put(&qq,verptarget.s + i, 1);
+             }
+           }
+           qmail_puts(&qq,"@");
+           qmail_put(&qq,outhost.s,outhost.len);       /* safe */
+           qmail_puts(&qq,">\n");
+            qmail_puts(&qq,TXT_WELCOME);
+           if (!quote(&quoted,&outlocal)) die_nomem();
+            qmail_put(&qq,quoted.s,quoted.len);
+            qmail_puts(&qq,"@");
+            qmail_put(&qq,outhost.s,outhost.len);
+            qmail_puts(&qq,"\n");
+            transferenc();
+           if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
+           if (!stralloc_append(&confirm,"unsubscribe-")) die_nomem();
+           if (!stralloc_cats(&confirm,verptarget.s)) die_nomem();
+           if (!stralloc_append(&confirm,"@")) die_nomem();
+           if (!stralloc_cat(&confirm,&outhost)) die_nomem();
+           if (!stralloc_0(&confirm)) die_nomem();
+           set_cpconfirm(confirm.s);                   /* for !R in copy */
+            copy(&qq,"text/top",flagcd,FATAL);
+            copy(&qq,"text/sub-ok",flagcd,FATAL);
+            break;
+    default:
+            if (str_start(action,ACTION_TC))
+              strerr_die2x(0,INFO,ERR_SUB_NOP);
+            qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+            transferenc();
+            copy(&qq,"text/top",flagcd,FATAL);
+            copy(&qq,"text/sub-nop",flagcd,FATAL);
+            break;
+  }
+  if (flagdig == FLD_DENY || flagdig == FLD_ALLOW)
+    strerr_die3x(0,INFO,ERR_EXTRA_SUB,target.s);
+  return r;
 }
 
-stralloc mydtline = {0};
+int getoff(action)
+char *action;
+{
+  int r;
+
+  switch((r = subscribe(workdir,target.s,0,"","-",1,-1,(char *) 0,FATAL))) {
+                       /* no comment for unsubscribe */
+    case 1:
+            qmail_puts(&qq,TXT_GOODBYE);
+            if (!quote(&quoted,&outlocal)) die_nomem();
+            qmail_put(&qq,quoted.s,quoted.len);
+            qmail_puts(&qq,"@");
+            qmail_put(&qq,outhost.s,outhost.len);
+            qmail_puts(&qq,"\n\n");
+            transferenc();
+            copy(&qq,"text/top",flagcd,FATAL);
+            copy(&qq,"text/unsub-ok",flagcd,FATAL);
+            break;
+    default:
+            qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+            transferenc();
+            copy(&qq,"text/top",flagcd,FATAL);
+            copy(&qq,"text/unsub-nop",flagcd,FATAL);
+            break;
+  }
+  if (flagdig == FLD_DENY || flagdig == FLD_ALLOW)
+    strerr_die3x(0,INFO,ERR_EXTRA_UNSUB,target.s);
+  return r;
+}
 
-void main(argc,argv)
+void doconfirm(act)
+/* This should only be called with valid act for sub/unsub confirms. If act */
+/* is not ACTION_SC or ACTION_TC, it is assumed to be an unsubscribe conf.*/
+char *act;     /* first letter of desired confirm request only as STRING! */
+{
+  unsigned int i;
+
+  strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
+  cookie(hash,key.s,key.len-flagdig,strnum,target.s,act);
+  if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
+  if (!stralloc_append(&confirm,"-")) die_nomem();
+  if (!stralloc_catb(&confirm,act,1)) die_nomem();
+  if (!stralloc_cats(&confirm,"c.")) die_nomem();
+  if (!stralloc_cats(&confirm,strnum)) die_nomem();
+  if (!stralloc_append(&confirm,".")) die_nomem();
+  if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem();
+  if (!stralloc_append(&confirm,"-")) die_nomem();
+  if (!stralloc_cats(&confirm,verptarget.s)) die_nomem();
+  if (!stralloc_append(&confirm,"@")) die_nomem();
+  if (!stralloc_cat(&confirm,&outhost)) die_nomem();
+  if (!stralloc_0(&confirm)) die_nomem();
+  set_cpconfirm(confirm.s);            /* for copy */
+
+  qmail_puts(&qq,"Reply-To: ");
+  if (!quote2(&quoted,confirm.s)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"\n");
+  if (!stralloc_0(&confirm)) die_nomem();
+
+  qmail_puts(&qq,"Subject: ");
+  if (*act == ACTION_SC[0] || *act == ACTION_UC[0])
+    qmail_puts(&qq,TXT_USRCONFIRM);
+  else
+    qmail_puts(&qq,TXT_MODCONFIRM);
+  if (*act == ACTION_SC[0] || *act == ACTION_TC[0])
+    qmail_puts(&qq,TXT_SUBSCRIBE_TO);
+  else
+    qmail_puts(&qq,TXT_UNSUBSCRIBE_FROM);
+  if (!quote(&quoted,&outlocal)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,"\n");
+  transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+}
+
+void sendtomods()
+{
+  putsubs(moddir.s,0L,52L,subto,1,FATAL);
+}
+
+void copybottom()
+{
+  if (flagbottom || act == AC_HELP) {
+    copy(&qq,"text/bottom",flagcd,FATAL);
+    if (flagcd) {
+      if (flagcd == 'B') {
+       encodeB("",0,&line,2,FATAL);    /* flush */
+       qmail_put(&qq,line.s,line.len);
+      }
+      qmail_puts(&qq,"\n--");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"\nContent-Type: message/rfc822");
+      qmail_puts(&qq,"\nContent-Disposition: inline; filename=request.msg\n\n");
+    }
+    qmail_puts(&qq,"Return-Path: <");
+    if (!quote2(&quoted,sender)) die_nomem();
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,">\n");
+    if (seek_begin(0) == -1)
+      strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+    if (substdio_copy(&ssqq,&ssin2) != 0)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (flagcd) {
+      qmail_puts(&qq,"\n--");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"--\n");
+    }
+  } else {
+    if (flagcd == 'B') {
+      encodeB("",0,&line,2,FATAL);     /* flush even if no bottom */
+      qmail_put(&qq,line.s,line.len);
+    }
+  }
+
+  qmail_from(&qq,from.s);
+}
+
+int main(argc,argv)
 int argc;
 char **argv;
 {
-  char *dir;
-  char *sender;
-  char *host;
   char *local;
+  char *def;
   char *action;
+  char *x, *y;
+  char *fname;
+  char *pmod;
+  char *err;
+  char *cp,*cpfirst,*cplast,*cpnext,*cpafter;
+  int flagmod;
+  int flagremote;
+  int flagpublic;
+  int opt,r;
+  unsigned int i;
+  unsigned int len;
   int fd;
-  int i;
-  int flagconfirm;
-  int flaghashok;
-  int flaggoodfield;
-  int match;
+  int flagdone;
+  register char ch;
 
-  umask(022);
+  (void) umask(022);
   sig_pipeignore();
   when = now();
 
-  dir = argv[1];
+  while ((opt = getopt(argc,argv,"bBcCdDeEfFlLmMnNqQsSuUvV")) != opteof)
+    switch(opt) {
+      case 'b': flagbottom = 1; break;
+      case 'B': flagbottom = 0; break;
+      case 'c': flagget = 1; break;
+      case 'C': flagget = 0; break;
+      case 'd':
+      case 'e': flagedit = 1; break;
+      case 'D':
+      case 'E': flagedit = 0; break;
+      case 'f': flagstorefrom = 1; break;
+      case 'F': flagstorefrom = 0; break;
+      case 'l': flaglist = 1; break;
+      case 'L': flaglist = 0; break;
+      case 'm': flagunsubismod = 1; break;
+      case 'M': flagunsubismod = 0; break;
+      case 'n': flagnotify = 1; break;
+      case 'N': flagnotify = 0; break;
+      case 's': flagsubconf = 1; break;
+      case 'S': flagsubconf = 0; break;
+      case 'q': flagverbose = 0; break;
+      case 'Q': flagverbose++; break;
+      case 'u': flagunsubconf = 1; break;
+      case 'U': flagunsubconf = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-manage version: ezmlm-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+  dir = argv[optind];
   if (!dir) die_usage();
 
   sender = env_get("SENDER");
-  if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
+  if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
   local = env_get("LOCAL");
-  if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
-  host = env_get("HOST");
-  if (!host) strerr_die2x(100,FATAL,"HOST not set");
+  if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+  def = env_get("DEFAULT");
 
   if (!*sender)
-    strerr_die2x(100,FATAL,"I don't reply to bounce messages (#5.7.2)");
+    strerr_die2x(100,FATAL,ERR_BOUNCE);
   if (!sender[str_chr(sender,'@')])
-    strerr_die2x(100,FATAL,"I don't reply to senders without host names (#5.7.2)");
+    strerr_die2x(100,FATAL,ERR_ANONYMOUS);
   if (str_equal(sender,"#@[]"))
-    strerr_die2x(100,FATAL,"I don't reply to bounce messages (#5.7.2)");
+    strerr_die2x(100,FATAL,ERR_BOUNCE);
 
   if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
 
   switch(slurp("key",&key,32)) {
     case -1:
-      strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
+      strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
     case 0:
-      strerr_die3x(100,FATAL,dir,"/key does not exist");
+      strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
   }
   getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
-  getconf_line(&inhost,"inhost",1,FATAL,dir);
-  getconf_line(&inlocal,"inlocal",1,FATAL,dir);
   getconf_line(&outhost,"outhost",1,FATAL,dir);
   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  set_cpouthost(&outhost);
+  if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+    if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+      if (charset.s[charset.len - 1] == 'B' ||
+               charset.s[charset.len - 1] == 'Q') {
+        flagcd = charset.s[charset.len - 1];
+        charset.s[charset.len - 2] = '\0';
+      }
+    }
+  } else
+    if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+  if (!stralloc_0(&charset)) die_nomem();
 
-  if (inhost.len != str_len(host)) die_badaddr();
-  if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
-  if (inlocal.len > str_len(local)) die_badaddr();
-  if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+  if (def)                     /* qmail-1.02 */
+    action = def;              /* .qmail-list-default */
+  else {                       /* older version of qmail */
+    getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+    if (inlocal.len > str_len(local)) die_badaddr();
+    if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+    action = local + inlocal.len;
+    if (*(action++) != '-') die_badaddr();
+                               /* has to be '-' to match link. Check anyway */
+  }
 
-  action = local + inlocal.len;
+  if (!stralloc_copys(&ddir,dir)) die_nomem();
 
-  switch(slurp("public",&line,1)) {
-    case -1:
-      strerr_die4sys(111,FATAL,"unable to read ",dir,"/public: ");
-    case 0:
-      strerr_die2x(100,FATAL,"sorry, I've been told to reject all requests (#5.7.2)");
+  if (case_starts(action,"digest")) {                  /* digest */
+    action += 6;
+    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
+    if (!stralloc_cats(&ddir,"/digest")) die_nomem();
+    flagdig = FLD_DIGEST;
+  } else if (case_starts(action,ACTION_ALLOW)) {       /* allow */
+    action += str_len(ACTION_ALLOW);
+    if (!stralloc_append(&outlocal,"-")) die_nomem();
+    if (!stralloc_cats(&outlocal,ACTION_ALLOW)) die_nomem();
+    if (!stralloc_cats(&ddir,"/allow")) die_nomem();
+    flagdig = FLD_ALLOW;
+  } else if (case_starts(action,ACTION_DENY)) {                /* deny */
+    action += str_len(ACTION_DENY);
+    if (!stralloc_append(&outlocal,"-")) die_nomem();
+    if (!stralloc_cats(&outlocal,ACTION_DENY)) die_nomem();
+    if (!stralloc_cats(&ddir,"/deny")) die_nomem();
+    flagdig = FLD_DENY;
   }
+  if (flagdig)                         /* zap '-' after db specifier */
+    if (*(action++) != '-') die_badaddr();
+
+  if (!stralloc_0(&ddir)) die_nomem();
+  workdir = ddir.s;
+  set_cpoutlocal(&outlocal);
 
   if (!stralloc_copys(&target,sender)) die_nomem();
   if (action[0]) {
-    i = 1 + str_chr(action + 1,'-');
+    i = str_chr(action,'-');
     if (action[i]) {
       action[i] = 0;
       if (!stralloc_copys(&target,action + i + 1)) die_nomem();
@@ -207,138 +820,488 @@ char **argv;
     }
   }
   if (!stralloc_0(&target)) die_nomem();
-  if (!stralloc_copys(&confirm,"")) die_nomem();
+  set_cptarget(target.s);      /* for copy() */
+  make_verptarget();
 
-  if (qmail_open(&qq) == -1)
-    strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
+  flagmod = getconf_line(&modsub,"modsub",0,FATAL,dir);
+  flagremote = getconf_line(&remote,"remote",0,FATAL,dir);
 
-  qmail_puts(&qq,"Mailing-List: ");
-  qmail_put(&qq,mailinglist.s,mailinglist.len);
-  qmail_puts(&qq,"\nDate: ");
-  datetime_tai(&dt,when);
-  qmail_put(&qq,date,date822fmt(date,&dt));
-  qmail_puts(&qq,"Message-ID: <");
-  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) when));
-  qmail_puts(&qq,".");
-  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) getpid()));
-  qmail_puts(&qq,".ezmlm@");
-  qmail_put(&qq,outhost.s,outhost.len);
-  qmail_puts(&qq,">\nFrom: ");
-  if (!quote(&quoted,&outlocal)) die_nomem();
-  qmail_put(&qq,quoted.s,quoted.len);
-  qmail_puts(&qq,"-help@");
-  qmail_put(&qq,outhost.s,outhost.len);
-  qmail_puts(&qq,"\nTo: ");
-  if (!quote2(&quoted,target.s)) die_nomem();
-  qmail_put(&qq,quoted.s,quoted.len);
-  qmail_puts(&qq,"\n");
+  if (case_equals(action,ACTION_LISTN) ||
+               case_equals(action,ALT_LISTN))
+    act = AC_LISTN;
+  else if (case_equals(action,ACTION_LIST) ||
+               case_equals(action,ALT_LIST))
+    act = AC_LIST;
+  else if (case_starts(action,ACTION_GET) ||
+               case_starts(action,ALT_GET))
+    act = AC_GET;
+  else if (case_equals(action,ACTION_HELP) ||
+               case_equals(action,ALT_HELP))
+    act = AC_HELP;
+  else if (case_starts(action,ACTION_EDIT) ||
+               case_starts(action,ALT_EDIT))
+    act = AC_EDIT;
+  else if (case_starts(action,ACTION_LOG))
+   { act = AC_LOG; actlen = str_len(ACTION_LOG); }
+  else if (case_starts(action,ALT_LOG))
+   { act = AC_LOG; actlen = str_len(ALT_LOG); }
 
-  flaghashok = 1;
-  if (str_start(action,"-sc.")) flaghashok = hashok(action);
-  if (str_start(action,"-uc.")) flaghashok = hashok(action);
-
-  flagconfirm = 0;
-  if (str_equal(action,"-subscribe")) flagconfirm = 1;
-  if (str_equal(action,"-unsubscribe")) flagconfirm = 1;
-  if (!flaghashok) flagconfirm = 1;
-  
-  if (flagconfirm) {
-    strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
-    cookie(hash,key.s,key.len,strnum,target.s,action + 1);
-    if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
-    if (!stralloc_cats(&confirm,"-")) die_nomem();
-    if (!stralloc_catb(&confirm,action + 1,1)) die_nomem();
-    if (!stralloc_cats(&confirm,"c.")) die_nomem();
-    if (!stralloc_cats(&confirm,strnum)) die_nomem();
-    if (!stralloc_cats(&confirm,".")) die_nomem();
-    if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem();
-    if (!stralloc_cats(&confirm,"-")) die_nomem();
-    i = str_rchr(target.s,'@');
-    if (!stralloc_catb(&confirm,target.s,i)) die_nomem();
-    if (target.s[i]) {
-      if (!stralloc_cats(&confirm,"=")) die_nomem();
-      if (!stralloc_cats(&confirm,target.s + i + 1)) die_nomem();
+                       /* NOTE: act is needed in msg_headers(). */
+                       /* Yes, this needs to be cleaned up! */
+
+  if (flagmod || flagremote) {
+    if (modsub.len && modsub.s[0] == '/') {
+      if (!stralloc_copy(&moddir,&modsub)) die_nomem();
+    } else if (remote.len && remote.s[0] == '/') {
+      if (!stralloc_copy(&moddir,&remote)) die_nomem();
+    } else {
+      if (!stralloc_copys(&moddir,dir)) die_nomem();
+      if (!stralloc_cats(&moddir,"/mod")) die_nomem();
     }
-    if (!stralloc_cats(&confirm,"@")) die_nomem();
-    if (!stralloc_cat(&confirm,&outhost)) die_nomem();
-    if (!stralloc_0(&confirm)) die_nomem();
+    if (!stralloc_0(&moddir)) die_nomem();
+               /* for these the reply is 'secret' and goes to sender  */
+               /* This means that they can be triggered from a SENDER */
+               /* that is not a mod, but never send to a non-mod */
+    if (act == AC_NONE || flagdig == FLD_DENY) /* None of the above */
+      pmod = issub(moddir.s,sender,(char *) 0,FATAL);
+                               /* sender = moderator? */
+    else
+      pmod = issub(moddir.s,target.s,(char *) 0,FATAL);
+                               /* target = moderator? */
+   } else
+     pmod = 0;                 /* always 0 for non-mod/remote lists */
+                               /* if DIR/public is missing, we still respond*/
+                               /* to requests from moderators for remote    */
+                               /* admin and modsub lists. Since pmod   */
+                               /* is false for all non-mod lists, only it   */
+                               /* needs to be tested. */
+  if ((flagpublic = slurp("public",&line,1)) == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,dir,"/public: ");
+  if (!flagpublic && !(pmod && flagremote) &&
+                !case_equals(action,ACTION_HELP))
+      strerr_die2x(100,FATAL,ERR_NOT_PUBLIC);
 
-    qmail_puts(&qq,"Reply-To: ");
-    if (!quote2(&quoted,confirm.s)) die_nomem();
-    qmail_put(&qq,quoted.s,quoted.len);
-    qmail_puts(&qq,"\n");
+  if (flagdig == FLD_DENY)
+    if (!pmod || !flagremote)  /* only mods can do */
+      strerr_die1x(100,ERR_NOT_ALLOWED);
+
+  if (act == AC_NONE) {                /* none of the above */
+    if (case_equals(action,ACTION_SUBSCRIBE) ||
+               case_equals(action,ALT_SUBSCRIBE))
+      act = AC_SUBSCRIBE;
+    else if (case_equals(action,ACTION_UNSUBSCRIBE)
+               || case_equals(action,ALT_UNSUBSCRIBE))
+      act = AC_UNSUBSCRIBE;
+    else if (str_start(action,ACTION_SC)) act = AC_SC;
   }
-  if (!stralloc_0(&confirm)) die_nomem();
 
-  qmail_puts(&qq,"Subject: ezmlm response\n");
+  if (!stralloc_copy(&from,&outlocal)) die_nomem();
+  if (!stralloc_cats(&from,"-return-@")) die_nomem();
+  if (!stralloc_cat(&from,&outhost)) die_nomem();
+  if (!stralloc_0(&from)) die_nomem();
 
-  if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem();
-  if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
-  if (!stralloc_cats(&mydtline,"@")) die_nomem();
-  if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
-  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+  if (qmail_open(&qq,(stralloc *) 0) == -1)
+    strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+  msg_headers();
 
-  qmail_put(&qq,mydtline.s,mydtline.len);
+  if (act == AC_SUBSCRIBE) {
+    if (pmod && flagremote) {
+      doconfirm(ACTION_TC);
+      copy(&qq,"text/mod-sub-confirm",flagcd,FATAL);
+      copybottom();
+      qmail_to(&qq,pmod);
+    } else if (flagsubconf) {
+      doconfirm(ACTION_SC);
+      copy(&qq,"text/sub-confirm",flagcd,FATAL);
+      copybottom();
+      qmail_to(&qq,target.s);
+    } else {                           /* normal subscribe, no confirm */
+      r = geton(action);               /* should be rarely used. */
+      copybottom();
+      if (flagnotify) qmail_to(&qq,target.s);
+      if (r && flagverbose > 1) to_owner();
+    }
 
-  flaggoodfield = 0;
-  for (;;) {
-    if (getln(&ssin,&line,&match,'\n') == -1)
-      strerr_die2sys(111,FATAL,"unable to read input: ");
-    if (!match) break;
-    if (line.len == 1) break;
-    if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
-      flaggoodfield = 0;
-      if (case_startb(line.s,line.len,"mailing-list:"))
-        strerr_die2x(100,FATAL,"incoming message has Mailing-List (#5.7.2)");
-      if (line.len == mydtline.len)
-       if (byte_equal(line.s,line.len,mydtline.s))
-          strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)");
-      if (case_startb(line.s,line.len,"delivered-to:"))
-        flaggoodfield = 1;
-      if (case_startb(line.s,line.len,"received:"))
-        flaggoodfield = 1;
+  } else if (act == AC_SC) {
+    if (hashok(action,ACTION_SC)) {
+      if (flagmod && !(pmod && str_equal(sender,target.s))) {
+        store_from(&fromline,target.s);        /* save from line, if requested */
+                                       /* since transaction not complete */
+        doconfirm(ACTION_TC);
+        copy(&qq,"text/mod-sub-confirm",flagcd,FATAL);
+        copybottom();
+        sendtomods();
+      } else {
+        r = geton(action);
+        copybottom();
+        qmail_to(&qq,target.s);
+       if (r && flagverbose > 1) to_owner();
+      }
+    } else {
+      doconfirm(ACTION_SC);
+      copy(&qq,"text/sub-bad",flagcd,FATAL);
+      copybottom();
+      qmail_to(&qq,target.s);
     }
-    if (flaggoodfield)
-      qmail_put(&qq,line.s,line.len);
-  }
-  if (seek_begin(0) == -1)
-    strerr_die2sys(111,FATAL,"unable to seek input: ");
 
-  qmail_puts(&qq,"\n");
-  copy("text/top");
-  if (str_equal(action,"-subscribe"))
-    copy("text/sub-confirm");
-  else if (str_equal(action,"-unsubscribe"))
-    copy("text/unsub-confirm");
-  else if (str_start(action,"-sc.")) {
-    if (!flaghashok)
-      copy("text/sub-bad");
-    else
-      switch(subscribe(target.s,1)) {
-        case -1: strerr_die1(111,FATAL,&subscribe_err);
-        case -2: strerr_die1(100,FATAL,&subscribe_err);
-       case 1: log("+",target.s); copy("text/sub-ok"); break;
-       default: copy("text/sub-nop"); break;
+  } else if (str_start(action,ACTION_TC)) {
+    if (hashok(action,ACTION_TC)) {
+      r = geton(action);
+      mod_bottom();
+      if (flagnotify) qmail_to(&qq,target.s);  /* unless suppressed */
+      if (r && flagverbose > 1) to_owner();
+    } else {
+      if (!pmod || !flagremote)        /* else anyone can get a good -tc. */
+        die_cookie();
+      doconfirm(ACTION_TC);
+      copy(&qq,"text/sub-bad",flagcd,FATAL);
+      copybottom();
+      qmail_to(&qq,pmod);
+    }
+
+  } else if (act == AC_UNSUBSCRIBE) {
+    if (flagunsubconf) {
+      if (pmod && flagremote) {
+        doconfirm(ACTION_VC);
+        copy(&qq,"text/mod-unsub-confirm",flagcd,FATAL);
+        copybottom();
+       qmail_to(&qq,pmod);
+      } else {
+        doconfirm(ACTION_UC);
+        copy(&qq,"text/unsub-confirm",flagcd,FATAL);
+        copybottom();
+        qmail_to(&qq,target.s);
       }
-  }
-  else if (str_start(action,"-uc.")) {
-    if (!flaghashok)
-      copy("text/unsub-bad");
-    else
-      switch(subscribe(target.s,0)) {
-        case -1: strerr_die1(111,FATAL,&subscribe_err);
-        case -2: strerr_die1(100,FATAL,&subscribe_err);
-       case 1: log("-",target.s); copy("text/unsub-ok"); break;
-       default: copy("text/unsub-nop"); break;
+    } else if (flagunsubismod && flagmod) {
+        doconfirm(ACTION_VC);
+        copy(&qq,"text/mod-unsub-confirm",flagcd,FATAL);
+        copybottom();
+        sendtomods();
+    } else {
+      r = getoff(action);
+      copybottom();
+      if (!r || flagnotify) qmail_to(&qq,target.s);
+               /* tell owner if problems (-Q) or anyway (-QQ) */
+      if (flagverbose && (!r || flagverbose > 1)) to_owner();
+    }
+
+  } else if (str_start(action,ACTION_UC)) {
+    if (hashok(action,ACTION_UC)) {
+       /* unsub is moderated only on moderated list if -m unless the */
+       /* target == sender == a moderator */
+      if (flagunsubismod && flagmod) {
+        doconfirm(ACTION_VC);
+        copy(&qq,"text/mod-unsub-confirm",flagcd,FATAL);
+        copybottom();
+        sendtomods();
+      } else {
+        r = getoff(action);
+        copybottom();
+        if (!r || flagnotify) qmail_to(&qq,target.s);
+               /* tell owner if problems (-Q) or anyway (-QQ) */
+       if (flagverbose && (!r || flagverbose > 1)) to_owner();
       }
-  }
-  else if (str_start(action,"-get.")) {
+    } else {
+      doconfirm(ACTION_UC);
+      copy(&qq,"text/unsub-bad",flagcd,FATAL);
+      copybottom();
+      qmail_to(&qq,target.s);
+    }
+
+  } else if (str_start(action,ACTION_VC)) {
+    if (hashok(action,ACTION_VC)) {
+      r = getoff(action);
+      if (!r && flagmod)
+        strerr_die2x(0,INFO,ERR_UNSUB_NOP);
+      mod_bottom();
+      if (r) {                         /* success to target */
+       qmail_to(&qq,target.s);
+        if (flagverbose > 1) to_owner();
+      } else                           /* NOP to sender = admin. Will take */
+        qmail_to(&qq,sender);          /* care of it. No need to tell owner */
+               /* if list is moderated skip - otherwise bad with > 1 mod */
+    } else {
+      if (!pmod || !flagremote)        /* else anyone can get a good -vc. */
+        die_cookie();
+      doconfirm(ACTION_VC);
+      copy(&qq,"text/unsub-bad",flagcd,FATAL);
+      copybottom();
+      qmail_to(&qq,pmod);
+    }
+
+  } else if (act == AC_LIST || act == AC_LISTN) {
+
+    if (!flaglist || (!flagmod && !flagremote))
+      strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+    if (!pmod)
+      strerr_die2x(100,FATAL,ERR_NOT_ALLOWED);
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+
+    if (act == AC_LIST) {
+      (void) code_qput(TXT_LISTMEMBERS,str_len(TXT_LISTMEMBERS));
+      i = putsubs(workdir,0L,52L,code_subto,1,FATAL);
+    } else                     /* listn */
+      i = putsubs(workdir,0L,52L,dummy_to,1,FATAL);
+
+    (void) code_qput("\n  ======> ",11);
+    (void) code_qput(strnum,fmt_ulong(strnum,i));
+    (void) code_qput("\n",1);
+    copybottom();
+    qmail_to(&qq,pmod);
+
+  } else if (act == AC_LOG) {
+    action += actlen;
+    if (*action == '.' || *action == '_') ++action;
+    if (!flaglist || !flagremote)
+      strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+    if (!pmod)
+      strerr_die2x(100,FATAL,ERR_NOT_ALLOWED);
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    searchlog(workdir,action,code_subto,FATAL);
+    copybottom();
+    qmail_to(&qq,pmod);
+
+  } else if (act == AC_EDIT) {
+       /* only remote admins and only if -e is specified may edit */
+    if (!flagedit || !flagremote)
+      strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+    if (!pmod)
+      strerr_die2x(100,FATAL,ERR_NOT_ALLOWED);
+    len = str_len(ACTION_EDIT);
+    if (!case_starts(action,ACTION_EDIT))
+      len = str_len(ALT_EDIT);
+    if (action[len]) {                 /* -edit.file, not just -edit */
+      if (action[len] != '.')
+        strerr_die2x(100,FATAL,ERR_BAD_REQUEST);
+      if (!stralloc_copys(&fnedit,"text/")) die_nomem();
+      if (!stralloc_cats(&fnedit,action+len+1)) die_nomem();
+      if (!stralloc_0(&fnedit)) die_nomem();
+      case_lowerb(fnedit.s,fnedit.len);
+      i = 5;   /* after the "text/" */
+      while ((ch = fnedit.s[i++])) {
+        if (((ch > 'z') || (ch < 'a')) && (ch != '_'))
+          strerr_die2x(100,FATAL,ERR_BAD_NAME);
+        if (ch == '_') fnedit.s[i-1] = '-';
+      }
+      switch(slurp(fnedit.s,&text,1024)) {     /* entire file! */
+        case -1:
+          strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fnedit.s,": ");
+        case 0:
+          strerr_die5x(100,FATAL,dir,"/",fnedit.s,ERR_NOEXIST);
+      }
+      if (!stralloc_copy(&line,&text)) die_nomem();
+      {                /* get rid of nulls to use cookie */
+        register char *s; register unsigned int n;
+        s = line.s; n = line.len;
+        while(n--) { if (!*s) *s = '_'; ++s; }
+      }
+      if (!stralloc_cat(&line,&fnedit)) die_nomem();   /* including '\0' */
+      strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
+      cookie(hash,key.s,key.len,strnum,line.s,"-e");
+      if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
+      if (!stralloc_append(&confirm,"-")) die_nomem();
+      if (!stralloc_catb(&confirm,ACTION_ED,LENGTH_ED)) die_nomem();
+      if (!stralloc_cats(&confirm,strnum)) die_nomem();
+      if (!stralloc_append(&confirm,".")) die_nomem();
+               /* action part has been checked for bad chars */
+      if (!stralloc_cats(&confirm,action + len + 1)) die_nomem();
+      if (!stralloc_append(&confirm,".")) die_nomem();
+      if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem();
+      if (!stralloc_append(&confirm,"@")) die_nomem();
+      if (!stralloc_cat(&confirm,&outhost)) die_nomem();
+      if (!stralloc_0(&confirm)) die_nomem();
+      set_cpconfirm(confirm.s);
+
+      qmail_puts(&qq,"Reply-To: ");
+      if (!quote2(&quoted,confirm.s)) die_nomem();
+      qmail_put(&qq,quoted.s,quoted.len);
+      qmail_puts(&qq,"\n");
+      if (!stralloc_0(&confirm)) die_nomem();
+
+      qmail_puts(&qq,TXT_EDIT_RESPONSE);
+      qmail_puts(&qq,action+len+1);    /* has the '_' not '-' */
+      qmail_puts(&qq,TXT_EDIT_FOR);
+      if (!quote(&quoted,&outlocal)) die_nomem();
+      qmail_put(&qq,quoted.s,quoted.len);
+      qmail_puts(&qq,"@");
+      qmail_put(&qq,outhost.s,outhost.len);
+      qmail_puts(&qq,"\n");
+      transferenc();
+      copy(&qq,"text/top",flagcd,FATAL);
+      copy(&qq,"text/edit-do",flagcd,FATAL);
+      (void) code_qput(TXT_EDIT_START,str_len(TXT_EDIT_START));
+      (void) code_qput("\n",1);
+      (void) code_qput(text.s,text.len);
+      (void) code_qput(TXT_EDIT_END,str_len(TXT_EDIT_END));
+      (void) code_qput("\n",1);
+
+    } else {   /* -edit only, so output list of editable files */
+      qmail_puts(&qq,TXT_EDIT_LIST);
+      transferenc();
+      copy(&qq,"text/top",flagcd,FATAL);
+      copy(&qq,"text/edit-list",flagcd,FATAL);
+    }
+    qmail_puts(&qq,"\n\n");
+    copybottom();
+    qmail_to(&qq,pmod);
+
+  } else if (str_start(action,ACTION_ED)) {
+    datetime_sec u;
+    int flaggoodfield;
+    x = action + LENGTH_ED;
+    x += scan_ulong(x,&u);
+    if ((u > when) || (u < when - 100000)) die_cookie();
+    if (*x == '.') ++x;
+    fname = x;
+    x += str_chr(x,'.');
+    if (!*x) die_cookie();
+    *x = (char) 0;
+    ++x;
+    if (!stralloc_copys(&fnedit,"text/")) die_nomem();
+    if (!stralloc_cats(&fnedit,fname)) die_nomem();
+    if (!stralloc_0(&fnedit)) die_nomem();
+    y = fnedit.s + 5;          /* after "text/" */
+    while (*++y) {             /* Name should be guaranteed by the cookie, */
+                               /* but better safe than sorry ... */
+      if (((*y > 'z') || (*y < 'a')) && (*y != '_'))
+          strerr_die2x(100,FATAL,ERR_BAD_NAME);
+      if (*y == '_') *y = '-';
+    }
+
+    lock();                    /* file must not change while here */
+
+    switch (slurp(fnedit.s,&text,1024)) {
+      case -1:
+        strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fnedit.s,": ");
+      case 0:
+        strerr_die5x(100,FATAL,dir,"/",fnedit.s,ERR_NOEXIST);
+    }
+    if (!stralloc_copy(&line,&text)) die_nomem();
+    {          /* get rid of nulls to use cookie */
+      register char *s; register unsigned int n;
+      s = line.s; n = line.len;
+      while(n--) { if (!*s) *s = '_'; ++s; }
+    }
+    if (!stralloc_cat(&line,&fnedit)) die_nomem();     /* including '\0' */
+    strnum[fmt_ulong(strnum,(unsigned long) u)] = 0;
+    cookie(hash,key.s,key.len,strnum,line.s,"-e");
+    if (str_len(x) != COOKIE) die_cookie();
+    if (byte_diff(hash,COOKIE,x)) die_cookie();
+       /* cookie is ok, file exists, lock's on, new file ends in '_' */
+    if (!stralloc_copys(&fneditn,fnedit.s)) die_nomem();
+    if (!stralloc_append(&fneditn,"_")) die_nomem();
+    if (!stralloc_0(&fneditn)) die_nomem();
+    fd = open_trunc(fneditn.s);
+    if (fd == -1)
+      strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": ");
+    substdio_fdbuf(&sstext,write,fd,textbuf,sizeof(textbuf));
+    if (!stralloc_copys(&quoted,"")) die_nomem();      /* clear */
+    if (!stralloc_copys(&text,"")) die_nomem();
+
+    for (;;) {                 /* get message body */
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!match) break;
+      if (!stralloc_cat(&text,&line)) die_nomem();
+    }
+    if (encin) {       /* decode if necessary */
+      if (encin == 'B')
+        decodeB(text.s,text.len,&line,FATAL);
+      else
+        decodeQ(text.s,text.len,&line,FATAL);
+      if (!stralloc_copy(&text,&line)) die_nomem();
+    }
+    cp = text.s;
+    cpafter = text.s+text.len;
+    flaggoodfield = 0;
+    flagdone = 0;
+    len = 0;
+    while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) {
+      i = byte_chr(cp,cpnext-cp,'%');
+      if (i != (unsigned int) (cpnext - cp)) {
+        if (!flaggoodfield) {  /* TXT_EDIT_START/END */
+          if (case_startb(cp+i,cpnext-cp-i,TXT_EDIT_START)) {
+               /* start tag. Store users 'quote characters', e.g. '> ' */
+            if (!stralloc_copyb(&quoted,cp,i)) die_nomem();
+            flaggoodfield = 1;
+            cp = cpnext + 1;
+            cpfirst = cp;
+            continue;
+          }
+        } else
+          if (case_startb(cp+i,cpnext-cp-i,TXT_EDIT_END)) {
+            flagdone = 1;
+            break;
+          }
+      }
+      if (flaggoodfield) {
+        if ((len += cpnext - cp - quoted.len + 1) > MAXEDIT)
+          strerr_die1x(100,ERR_EDSIZE);
+
+        if (quoted.len && cpnext-cp >= (int) quoted.len &&
+                       !str_diffn(cp,quoted.s,quoted.len))
+          cp += quoted.len;    /* skip quoting characters */
+        cplast = cpnext - 1;
+        if (*cplast == '\r')   /* CRLF -> '\n' for base64 encoding */
+          *cplast = '\n';
+        else
+          ++cplast;
+        if (substdio_put(&sstext,cp,cplast-cp+1) == -1)
+            strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": ");
+      }
+      cp = cpnext + 1;
+    }
+    if (!flagdone)
+      strerr_die2x(100,FATAL,ERR_NO_MARK);
+    if (substdio_flush(&sstext) == -1)
+      strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": ");
+    if (fsync(fd) == -1)
+      strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",fneditn.s,": ");
+    if (fchmod(fd, 0600) == -1)
+      strerr_die6sys(111,FATAL,ERR_CHMOD,dir,"/",fneditn.s,": ");
+    if (close(fd) == -1)
+      strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",fneditn.s,": ");
+    if (rename(fneditn.s,fnedit.s) == -1)
+      strerr_die6sys(111,FATAL,ERR_MOVE,dir,"/",fneditn.s,": ");
+
+    unlock();
+    qmail_puts(&qq,TXT_EDIT_SUCCESS);
+    qmail_puts(&qq,fname);
+    qmail_puts(&qq,TXT_EDIT_FOR);
+    if (!quote(&quoted,&outlocal)) die_nomem();
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,"@");
+    qmail_put(&qq,outhost.s,outhost.len);
+    qmail_puts(&qq,"\n");
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+    copy(&qq,"text/edit-done",flagcd,FATAL);
+    copybottom();
+    qmail_to(&qq,sender);      /* not necessarily from mod */
+
+  } else if (act == AC_GET) {
+
     unsigned long u;
     struct stat st;
     char ch;
     int r;
+    unsigned int pos;
+
+    if (!flagget)
+      strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+
+    pos = str_len(ACTION_GET);
+    if (!case_starts(action,ACTION_GET))
+      pos = str_len(ALT_GET);
 
-    scan_ulong(action + 5,&u);
+    if (action[pos] == '.' || action [pos] == '_') pos++;
+    scan_ulong(action + pos,&u);
 
     if (!stralloc_copys(&line,"archive/")) die_nomem();
     if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,u / 100))) die_nomem();
@@ -349,20 +1312,20 @@ char **argv;
     fd = open_read(line.s);
     if (fd == -1)
       if (errno != error_noent)
-       strerr_die4sys(111,FATAL,"unable to open ",line.s,": ");
+       strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
       else
-        copy("text/get-bad");
+        copy(&qq,"text/get-bad",flagcd,FATAL);
     else {
       if (fstat(fd,&st) == -1)
-       copy("text/get-bad");
+        copy(&qq,"text/get-bad",flagcd,FATAL);
       else if (!(st.st_mode & 0100))
-       copy("text/get-bad");
+        copy(&qq,"text/get-bad",flagcd,FATAL);
       else {
         substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
        qmail_puts(&qq,"> ");
        for (;;) {
          r = substdio_get(&sstext,&ch,1);
-         if (r == -1) strerr_die4sys(111,FATAL,"unable to read ",line.s,": ");
+         if (r == -1) strerr_die4sys(111,FATAL,ERR_READ,line.s,": ");
          if (r == 0) break;
          qmail_put(&qq,&ch,1);
          if (ch == '\n') qmail_puts(&qq,"> ");
@@ -371,33 +1334,72 @@ char **argv;
       }
       close(fd);
     }
-  }
-  else
-    copy("text/help");
+    copybottom();
+    qmail_to(&qq,target.s);
 
-  copy("text/bottom");
+  } else if (case_starts(action,ACTION_QUERY) ||
+               case_starts(action,ALT_QUERY)) {
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+    if (pmod) {        /* pmod points to static storage in issub(). Need to do this */
+               /* before calling issub() again */
+      if (!stralloc_copys(&to,pmod)) die_nomem();
+      if (!stralloc_0(&to)) die_nomem();
+    } else {
+      if (!stralloc_copy(&to,&target)) die_nomem();
+    }
+    if (issub(workdir,target.s,(char *) 0,FATAL))
+      copy(&qq,"text/sub-nop",flagcd,FATAL);
+    else
+      copy(&qq,"text/unsub-nop",flagcd,FATAL);
+    copybottom();
+    qmail_to(&qq,to.s);
 
-  qmail_puts(&qq,"Return-Path: <");
-  if (!quote2(&quoted,sender)) die_nomem();
-  qmail_put(&qq,quoted.s,quoted.len);
-  qmail_puts(&qq,">\n");
-  if (substdio_copy(&ssqq,&ssin2) != 0)
-    strerr_die2sys(111,FATAL,"unable to read input: ");
+  } else if (case_starts(action,ACTION_INFO) ||
+               case_starts(action,ALT_INFO)) {
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+    copy(&qq,"text/info",flagcd,FATAL);
+    copybottom();
+    qmail_to(&qq,target.s);
 
-  if (!stralloc_copy(&line,&outlocal)) die_nomem();
-  if (!stralloc_cats(&line,"-return-@")) die_nomem();
-  if (!stralloc_cat(&line,&outhost)) die_nomem();
-  if (!stralloc_0(&line)) die_nomem();
-  qmail_from(&qq,line.s);
+  } else if (case_starts(action,ACTION_FAQ) ||
+               case_starts(action,ALT_FAQ)) {
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+    copy(&qq,"text/faq",flagcd,FATAL);
+    copybottom();
+    qmail_to(&qq,target.s);
 
-  qmail_to(&qq,target.s);
+  } else if (pmod && (act == AC_HELP)) {
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+    copy(&qq,"text/mod-help",flagcd,FATAL);
+    copy(&qq,"text/help",flagcd,FATAL);
+    copybottom();
+    qmail_to(&qq,pmod);
 
-  switch(qmail_close(&qq)) {
-    case 0:
+  } else {
+    act = AC_HELP;
+    qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+    copy(&qq,"text/help",flagcd,FATAL);
+    copybottom();
+    qmail_to(&qq,sender);
+  }
+
+  if (*(err = qmail_close(&qq)) == '\0') {
       strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+      closesql();
       strerr_die2x(0,"ezmlm-manage: info: qp ",strnum);
-    default:
-      /* don't worry about undoing actions; everything is idempotent */
-      strerr_die2x(111,FATAL,"temporary qmail-queue error");
+  } else {
+      closesql();
+      strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
   }
 }
+
diff --git a/ezmlm-mktab b/ezmlm-mktab
new file mode 120000 (symlink)
index 0000000..feb8cc6
--- /dev/null
@@ -0,0 +1 @@
+sub_std/ezmlm-mktab
\ No newline at end of file
diff --git a/ezmlm-mktab.1 b/ezmlm-mktab.1
new file mode 100644 (file)
index 0000000..e593986
--- /dev/null
@@ -0,0 +1,61 @@
+.TH ezmlm-mktab 1
+.SH NAME
+ezmlm-mktab \- create SQL table definition for ezmlm list
+.SH SYNOPSIS
+.B ezmlm-mktab
+[
+.B \-dC
+]
+.I table_root
+.SH DESCRIPTION
+.B ezmlm-mktab
+takes
+.I table_root
+and prints table definitions for
+.IR table_root ,
+.IR table_root\fB_slog ,
+etc. All subscriber
+and log tables are defined, although normally only a subset are used.
+
+.B ezmlm-mktab
+also can also print the SQL drop commands to remove the same tables.
+.SH OPTIONS
+.TP
+.B \-C
+Do not print the table definitions for list creation. By default, table
+definitions are printed.
+.TP
+.B \-d
+Print table drop commands. Normally, they are omitted. When printed, they
+are printed before the table definitions, so that existing tables are removed,
+then recreated.
+.SH USAGE
+.B ezmlm-mktab
+.B \-d
+.I list \fR | mysql -hhost -uuser -p -f db
+
+This connects to the mysql server on ``host'' as ``user'' (prompting for
+the password) and in database ``db'' creates tables
+.IR list ,
+.IR list\fB_slog ,
+.IR list\fB_mlog ,
+the corresponding table sets for address lists
+.IR list\fB_allow ,
+.IR list\fB_deny ,
+.IR list\fB_digest,
+.IR list\fB_mod,
+as well as
+.IR list\fB_name ,
+.IR list\fB_digest_name ,
+.IR list\fB_cookie ,
+and
+.IR list\fB_digest_cookie .
+
+Since the
+.B \-d
+switch is used, any of these tables already existing are first dropped,
+with loss of any data already in them.
+.SH "SEE ALSO"
+ezmlm-send(1),
+ezmlm(5),
+mysql(1)
diff --git a/ezmlm-moderate.1 b/ezmlm-moderate.1
new file mode 100644 (file)
index 0000000..a05c891
--- /dev/null
@@ -0,0 +1,170 @@
+.TH ezmlm-moderate 1
+.SH NAME
+ezmlm-moderate \- process moderator actions for message
+acceptance and rejection
+.SH SYNOPSIS
+.B ezmlm-moderate [-cCmMrRvV]
+.B [-t\fI reply-to@host\fB]
+.I dir ['/path/program args']
+
+.SH DESCRIPTION
+.B ezmlm-moderate
+reads a mail message, expecting it to contain an
+.I -accept
+or 
+.I -reject
+action request for the mailing list stored in
+.I dir.
+
+.B ezmlm-moderate
+verifies the cookie sent as part of the action request, and if correct
+attempts to find the corresponding message in
+.IR dir\fB/mod/pending/ .
+If the message is found, it is either rejected or posted to the list,
+depending on the action request.
+
+Posts to the list are handled by
+piping the message to
+.B ezmlm-send(1)
+located in the ezmlm binary directory, as set at compile time. This is
+usually the directory that ezmlm-moderate resides in.
+.B ezmlm-send(1)
+is provided with
+.I dir
+as the first argument.
+
+If the optional second argument is given,
+.B ezmlm-moderate
+pipes the message to that program, as executed by sh.
+No parameters are supplied.
+
+.I dir
+is passed as an argument to
+.BR ezmlm-send(1) ,
+but NOT to any program specified as the optional
+.B ezmlm-moderate
+command line argument.
+
+.B ezmlm-moderate
+does not bother to correctly set sender.
+.B ezmlm-send(1)
+doesn't care, and any other program that might be used can parse the
+sender from the first line of the message, which is always
+.I Return-Path: <address@host.domain>
+as build from SENDER originally passed to
+.BR ezmlm-store(1) .
+
+If the message is rejected, an optional moderator comment is copied into the
+notification to the message sender.
+A moderator comment is any text in the
+.I -reject
+request found between two lines with ``%%%'' starting in one of the first
+5 positions of the line. The easiest is to use lines consisting of ``%%%''
+only. If the characters preceeding the ``%%%'' are found at the beginning
+of any lines within the comment, the characters are removed. This is to
+appropriately ignore any ``quote marks'' that you mail program might place at
+the beginning of lines in a reply.
+
+.B ezmlm-moderate
+messages are sent ``From:''
+.IR list-owner@listhost .
+This allows the poster to easily complain to the owner of the list, in case
+s/he objects. An optional ``Reply-To:'' header can be added via the
+.BR \-r\fI\ reply-to@host .
+
+Once the message has been successfully accepted or rejected, it is removed from
+.I dir\fB/mod/pending/
+and a stub is created in
+.I dir\fB/mod/accepted/
+or
+.IR dir\fB/mod/rejected/ ,
+respectively.
+This is done in order to be able to notify the senders of later moderation
+requests about the fate of the message.
+
+A failure to find the message in
+.I dir\fB/mod/pending/
+is most often caused by the message already having been accepted or rejected
+by another moderator. Therefore,
+.B ezmlm-moderate
+looks in
+.I dir\fB/mod/accepted/
+and
+.I dir\fB/mod/rejected/
+for a message stub. If found, 
+.B ezmlm-moderate
+notifies the sender in the form of a fatal error
+if the fate of the message was different from
+that intended by the current action request.
+Otherwise, the fate of the message is silently logged.
+
+If the message is not found, it has timed out and the message or
+the message stub has been removed by
+.BR ezmlm-clean(1) .
+In this case,
+.B ezmlm-moderate
+notifies the moderator of the failure, but can no longer discern the fate
+of the original message. Again, notification is in the form of a fatal
+error from qmail.
+
+At the beginning of the message,
+.B ezmlm-moderate
+prints a new
+.B Mailing-List
+field with the contents of
+.IR dir\fB/mailinglist .
+It rejects the message if there is already a
+.B Mailing-List
+field.
+
+.B ezmlm-moderate
+does not distribute bounce messages:
+if the environment variable
+.B SENDER
+is set, and is either empty or
+.BR #@[] ,
+.B ezmlm-moderate
+rejects the message.
+.SH OPTIONS
+.TP
+.B \-cCrR
+Passed on to ezmlm-send(1).
+.TP
+.B \-m
+(Default.)
+The rejected post is sent as a MIME enclosure.
+.TP
+.B \-M
+The rejected post is appended to the message.
+.TP
+.B \-t\fI reply-to@host
+A ``Reply-To:
+.IR reply-to@host ''
+header will be added to the moderation request.
+.TP
+.TP
+.B \-v
+Display
+.B ezmlm-moderate
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-moderate
+version information.
+.SH "CHARACTER SETS"
+If
+.I dir\fB/charset
+exists,
+.B ezmlm-moderate
+will use the character set listed for all messages. Otherwise, the
+default ``us-ascii'' will be used. The character set can be suffixed
+by ``:'' followed by a code. If the code is ``Q'', outgoing messages are 
+sent as ``Quoted-Printable'', if it is ``B'' they are sent ``base64'' encoded.
+Otherwise, text is sent as is.
+.SH "SEE ALSO"
+ezmlm-clean(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-store(1),
+ezmlm(5)
diff --git a/ezmlm-moderate.c b/ezmlm-moderate.c
new file mode 100644 (file)
index 0000000..363d207
--- /dev/null
@@ -0,0 +1,611 @@
+/*$Id: ezmlm-moderate.c,v 1.42 1999/10/09 16:49:56 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "error.h"
+#include "case.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "error.h"
+#include "sig.h"
+#include "fork.h"
+#include "wait.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "getln.h"
+#include "qmail.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "seek.h"
+#include "quote.h"
+#include "datetime.h"
+#include "now.h"
+#include "date822fmt.h"
+#include "fmt.h"
+#include "sgetopt.h"
+#include "auto_bin.h"
+#include "cookie.h"
+#include "errtxt.h"
+#include "copy.h"
+#include "idx.h"
+
+int flagmime = MOD_MIME;       /* default is message as attachment */
+char flagcd = '\0';            /* default: do not use transfer encoding */
+
+#define FATAL "ezmlm-moderate: fatal: "
+#define INFO "ezmlm-moderate: info: "
+
+void die_usage() { strerr_die1x(100,
+    "ezmlm-moderate: usage: ezmlm-moderate [-cCmMrRvV] [-t replyto] "
+    "dir [/path/ezmlm-send]"); }
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_badformat() { strerr_die2x(100,FATAL,ERR_BAD_REQUEST); }
+
+void die_badaddr() {
+  strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+
+stralloc outhost = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+stralloc key = {0};
+stralloc mydtline = {0};
+stralloc mailinglist = {0};
+stralloc accept = {0};
+stralloc reject = {0};
+stralloc to = {0};
+stralloc send = {0};
+stralloc sendopt = {0};
+stralloc comment = {0};
+stralloc charset = {0};
+datetime_sec when;
+struct datetime dt;
+
+char strnum[FMT_ULONG];
+char date[DATE822FMT];
+char hash[COOKIE];
+char boundary[COOKIE];
+stralloc line = {0};
+stralloc qline = {0};
+stralloc text = {0};
+stralloc quoted = {0};
+stralloc fnbase = {0};
+stralloc fnmsg = {0};
+stralloc fnnew = {0};
+stralloc fnsub = {0};
+char subbuf[256];
+substdio sssub;
+
+char *dir;
+
+struct stat st;
+
+struct qmail qq;
+
+void code_qput(s,n)
+char *s;
+unsigned int n;
+{
+    if (!flagcd)
+      qmail_put(&qq,s,n);
+    else {
+      if (flagcd == 'B')
+        encodeB(s,n,&qline,0,FATAL);
+      else
+        encodeQ(s,n,&qline,FATAL);
+      qmail_put(&qq,qline.s,qline.len);
+    }
+}
+
+void transferenc()
+{
+       if (flagcd) {
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"Quoted-printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        } else
+          qmail_puts(&qq,"\n\n");
+}
+
+int checkfile(fn)
+char *fn;
+/* looks for DIR/mod/{pending|rejected|accept}/fn.*/
+/* Returns:                                       */
+/*          1 found in pending                    */
+/*          0 not found                           */
+/*         -1 found in accepted                   */
+/*         -2 found in rejected                   */
+/* Handles errors.                                */
+/* ALSO: if found, fnmsg contains the o-terminated*/
+/* file name.                                     */
+{
+  
+  if (!stralloc_copys(&fnmsg,"mod/pending/")) die_nomem();
+  if (!stralloc_cats(&fnmsg,fn)) die_nomem();
+  if (!stralloc_0(&fnmsg)) die_nomem();
+  if (stat(fnmsg.s,&st) == -1) {
+    if (errno != error_noent)
+      strerr_die6sys(111,FATAL,ERR_STAT,dir,"/",fnmsg.s,": ");
+  } else
+      return 1;
+
+  if (!stralloc_copys(&fnmsg,"mod/accepted/")) die_nomem();
+  if (!stralloc_cats(&fnmsg,fn)) die_nomem();
+  if (!stralloc_0(&fnmsg)) die_nomem();
+  if (stat(fnmsg.s,&st) == -1) {
+    if (errno != error_noent)
+      strerr_die6sys(111,FATAL,ERR_STAT,dir,"/",fnmsg.s,": ");
+  } else
+      return -1;
+
+  if (!stralloc_copys(&fnmsg,"mod/rejected/")) die_nomem();
+  if (!stralloc_cats(&fnmsg,fn)) die_nomem();
+  if (!stralloc_0(&fnmsg)) die_nomem();
+  if (stat(fnmsg.s,&st) == -1) {
+    if (errno != error_noent)
+      strerr_die6sys(111,FATAL,ERR_STAT,dir,"/",fnmsg.s,": ");
+  } else
+      return -2;
+  return 0;
+}
+
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+
+char inbuf[512];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+
+substdio sstext;
+char textbuf[1024];
+
+void maketo()
+/* expects line to be a return-path line. If it is and the format is valid */
+/* to is set to to the sender. Otherwise, to is left untouched. Assuming   */
+/* to is empty to start with, it will remain empty if no sender is found.  */
+{
+  unsigned int x, y;
+
+    if (case_startb(line.s,line.len,"return-path:")) {
+      x = 12 + byte_chr(line.s + 12,line.len-12,'<');
+      if (x != line.len) {
+        y = byte_rchr(line.s + x,line.len-x,'>');
+        if (y + x != line.len) {
+          if (!stralloc_copyb(&to,line.s+x+1,y-1)) die_nomem();
+          if (!stralloc_0(&to)) die_nomem();
+        }              /* no return path-> no addressee. A NUL in the sender */
+      }                        /* is no worse than a faked sender, so no problem */
+    }
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *sender;
+  char *def;
+  char *local;
+  char *action;
+  int flaginheader;
+  int flagcomment;
+  int flaggoodfield;
+  int flagdone;
+  int fd, fdlock;
+  int match;
+  char *err;
+  char encin = '\0';
+  char szchar[2] = "-";
+  char *replyto = (char *) 0;
+  unsigned int start,confnum;
+  unsigned int pos,i;
+  int child;
+  int opt;
+  char *sendargs[4];
+  char *cp,*cpnext,*cpfirst,*cplast,*cpafter;
+  int wstat;
+
+  (void) umask(022);
+  sig_pipeignore();
+  when = now();
+
+  if (!stralloc_copys(&sendopt," -")) die_nomem();
+  while ((opt = getopt(argc,argv,"cCmMrRt:T:vV")) != opteof)
+    switch(opt) {      /* pass on ezmlm-send options */
+      case 'c':                        /* ezmlm-send flags */
+      case 'C':
+      case 'r':
+      case 'R':
+        szchar[0] = (char) opt & 0xff;
+        if (!stralloc_append(&sendopt,szchar)) die_nomem();
+        break;
+      case 'm': flagmime = 1; break;
+      case 'M': flagmime = 0; break;
+      case 't':
+      case 'T': if (optarg) replyto = optarg; break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-moderate version: ",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+
+  sender = env_get("SENDER");
+  if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
+  local = env_get("LOCAL");
+  if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+  def = env_get("DEFAULT");
+
+  if (!*sender)
+    strerr_die2x(100,FATAL,ERR_BOUNCE);
+  if (!sender[str_chr(sender,'@')])
+    strerr_die2x(100,FATAL,ERR_ANONYMOUS);
+  if (str_equal(sender,"#@[]"))
+    strerr_die2x(100,FATAL,ERR_BOUNCE);
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  switch(slurp("key",&key,32)) {
+    case -1:
+      strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
+    case 0:
+      strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
+  }
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  set_cpoutlocal(&outlocal);   /* for copy() */
+  set_cpouthost(&outhost);     /* for copy() */
+
+  if (def) {                   /* qmail>=1.02 */
+       /* local should be >= def, but who knows ... */
+    cp = local + str_len(local) - str_len(def) - 2;
+    if (cp < local) die_badformat();
+    action = local + byte_rchr(local,cp - local,'-');
+    if (action == cp) die_badformat();
+    action++;
+  } else {                     /* older versions of qmail */
+    getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+    if (inlocal.len > str_len(local)) die_badaddr();
+    if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+    action = local + inlocal.len;
+    if (*(action++) != '-') die_badaddr();
+  }
+
+  if (!action[0]) die_badformat();
+  if (!str_start(action,ACTION_ACCEPT) && !str_start(action,ACTION_REJECT))
+    die_badformat();
+  start = str_chr(action,'-');
+  if (!action[start]) die_badformat();
+  confnum = 1 + start + str_chr(action + start + 1,'.');
+  if (!action[confnum]) die_badformat();
+  confnum += 1 + str_chr(action + confnum + 1,'.');
+  if (!action[confnum]) die_badformat();
+  if (!stralloc_copyb(&fnbase,action+start+1,confnum-start-1)) die_nomem();
+  if (!stralloc_0(&fnbase)) die_nomem();
+  cookie(hash,key.s,key.len,fnbase.s,"","a");
+  if (byte_diff(hash,COOKIE,action+confnum+1))
+    die_badformat();
+
+  fdlock = open_append("mod/lock");
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/mod/lock: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/mod/lock: ");
+
+  switch(checkfile(fnbase.s)) {
+    case 0:
+      strerr_die2x(100,FATAL,ERR_MOD_TIMEOUT);
+    case -1:                   /* only error if new request != action taken */
+      if (str_start(action,ACTION_ACCEPT))
+        strerr_die2x(0,INFO,ERR_MOD_ACCEPTED);
+      else
+        strerr_die2x(100,FATAL,ERR_MOD_ACCEPTED);
+    case -2:
+      if (str_start(action,ACTION_REJECT))
+        strerr_die2x(0,INFO,ERR_MOD_REJECTED);
+      else
+        strerr_die2x(100,FATAL,ERR_MOD_REJECTED);
+    default:
+      break;
+  }
+/* Here, we have an existing filename in fnbase with the complete path */
+/* from the current dir in fnmsg. */
+
+  if (str_start(action,ACTION_REJECT)) {
+
+    if (qmail_open(&qq, (stralloc *) 0) == -1)
+      strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+
+
+                               /* Build recipient from msg return-path */
+    fd = open_read(fnmsg.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,FATAL,ERR_OPEN,fnmsg.s,": ");
+      else
+        strerr_die2x(100,FATAL,ERR_MOD_TIMEOUT);
+    }
+    substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+
+    if (getln(&sstext,&line,&match,'\n') == -1 || !match)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    maketo();                  /* extract SENDER from return-path */
+                                               /* Build message */
+    qmail_puts(&qq,"Mailing-List: ");
+    qmail_put(&qq,mailinglist.s,mailinglist.len);
+    if(getconf_line(&line,"listid",0,FATAL,dir)) {
+      qmail_puts(&qq,"\nList-ID: ");
+      qmail_put(&qq,line.s,line.len);
+    }
+    qmail_puts(&qq,"\nDate: ");
+    datetime_tai(&dt,when);
+    qmail_put(&qq,date,date822fmt(date,&dt));
+    qmail_puts(&qq,"Message-ID: <");
+    if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
+       die_nomem();
+    if (!stralloc_append(&line,".")) die_nomem();
+    if (!stralloc_catb(&line,strnum,
+               fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+    if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
+    if (!stralloc_cat(&line,&outhost)) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+    qmail_puts(&qq,line.s);
+               /* "unique" MIME boundary as hash of messageid */
+    cookie(boundary,"",0,"",line.s,"");
+    qmail_puts(&qq,">\nFrom: ");
+    if (!quote(&quoted,&outlocal)) die_nomem();
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,"-owner@");
+    qmail_put(&qq,outhost.s,outhost.len);
+    if (replyto) {
+      qmail_puts(&qq,"\nReply-To: ");
+      qmail_puts(&qq,replyto);
+    }
+    qmail_puts(&qq, "\nTo: ");
+    qmail_puts(&qq,to.s);
+    qmail_puts(&qq,"\nSubject: ");
+    qmail_puts(&qq,TXT_RETURNED_POST);
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,"@");
+    qmail_put(&qq,outhost.s,outhost.len);
+
+    if (flagmime) {
+      if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+        if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+          if (charset.s[charset.len - 1] == 'B' ||
+               charset.s[charset.len - 1] == 'Q') {
+            flagcd = charset.s[charset.len - 1];
+            charset.s[charset.len - 2] = '\0';
+          }
+        }
+      } else
+        if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+      if (!stralloc_0(&charset)) die_nomem();
+      qmail_puts(&qq,"\nMIME-Version: 1.0\n");
+      qmail_puts(&qq,"Content-Type: multipart/mixed;\n\tboundary=");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"\n\n--");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+      qmail_puts(&qq,charset.s);
+      transferenc();
+    }
+    copy(&qq,"text/top",flagcd,FATAL);
+    copy(&qq,"text/mod-reject",flagcd,FATAL);
+
+    flagcomment = 0;
+    flaginheader = 1;
+    if (!stralloc_copys(&text,"")) die_nomem();
+    if (!stralloc_ready(&text,1024)) die_nomem(); 
+    for (;;) {         /* copy moderator's rejection comment */
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!match) break;
+      if (flaginheader) {
+        if (case_startb(line.s,line.len,"Content-Transfer-Encoding:")) {
+          pos = 26;
+          while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos;
+          if (case_startb(line.s+pos,line.len-pos,"base64"))
+            encin = 'B';
+          else if (case_startb(line.s+pos,line.len-pos,"quoted-printable"))
+            encin = 'Q';
+        }
+        if (line.len == 1)
+          flaginheader = 0;
+      } else
+        if (!stralloc_cat(&text,&line)) die_nomem();
+    }  /* got body */
+    if (encin) {
+      if (encin == 'B')
+        decodeB(text.s,text.len,&line,FATAL);
+      else
+        decodeQ(text.s,text.len,&line,FATAL);
+      if (!stralloc_copy(&text,&line)) die_nomem();
+    }
+    cp = text.s;
+    cpafter = text.s + text.len;
+    if (!stralloc_copys(&line,"\n>>>>> -------------------- >>>>>\n"))
+                       die_nomem();
+    flaggoodfield = 0;
+    flagdone = 0;
+    while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) {
+      i = byte_chr(cp,cpnext-cp,'%');
+      if (i <= 5 && cpnext-cp >= 8) {
+                               /* max 5 "quote characters" and space for %%% */
+        if (cp[i+1] == '%' && cp[i+2] == '%') {
+          if (!flaggoodfield) {                                        /* Start tag */
+            if (!stralloc_copyb(&quoted,cp,i)) die_nomem();    /* quote chars*/
+            flaggoodfield = 1;
+            cp = cpnext + 1;
+            cpfirst = cp;
+            continue;
+          } else {                                             /* end tag */
+            if (flagdone)      /* 0 no comment lines, 1 comment line */
+              flagdone = 2;    /* 2 at least 1 comment line & end tag */
+            break;
+          }
+        }
+      }
+      if (flaggoodfield) {
+        cplast = cpnext - 1;
+        if (*cplast == '\r')   /* CRLF -> '\n' for base64 encoding */
+          *cplast = '\n';
+        else
+          ++cplast;
+                       /* NUL is now ok, so the test for it was removed */
+        flagdone = 1;
+        i = cplast - cp + 1;
+        if (quoted.len && quoted.len <= i &&
+               !str_diffn(cp,quoted.s,quoted.len)) {   /* quote chars */
+          if (!stralloc_catb(&line,cp+quoted.len,i-quoted.len)) die_nomem();
+        } else
+          if (!stralloc_catb(&line,cp,i)) die_nomem(); /* no quote chars */
+      }
+      cp = cpnext + 1;
+    }
+    if (flagdone == 2) {
+    if (!stralloc_cats(&line,"<<<<< -------------------- <<<<<\n")) die_nomem();
+      code_qput(line.s,line.len);
+    }
+    if (flagcd == 'B') {
+      encodeB("",0,&line,2,FATAL);
+      qmail_put(&qq,line.s,line.len);
+    }
+    if (flagmime) {
+      qmail_puts(&qq,"\n--");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n");
+    } else
+      qmail_puts(&qq,"\n");
+    if (seek_begin(fd) == -1)
+      strerr_die4sys(111,FATAL,ERR_SEEK,fnmsg.s,": ");
+
+    substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+    if (substdio_copy(&ssqq,&sstext) != 0)
+      strerr_die4sys(111,FATAL,ERR_READ,fnmsg.s,": ");
+    close(fd);
+
+    if (flagmime) {
+      qmail_puts(&qq,"\n--");
+      qmail_put(&qq,boundary,COOKIE);
+      qmail_puts(&qq,"--\n");
+    }
+
+    if (!stralloc_copy(&line,&outlocal)) die_nomem();
+    if (!stralloc_cats(&line,"-return-@")) die_nomem();
+    if (!stralloc_cat(&line,&outhost)) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+    qmail_from(&qq,line.s);
+    if (to.len)
+      qmail_to(&qq,to.s);
+
+    if (!stralloc_copys(&fnnew,"mod/rejected/")) die_nomem();
+    if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem();
+    if (!stralloc_0(&fnnew)) die_nomem();
+
+/* this is strictly to track what happended to a message to give informative */
+/* messages to the 2nd-nth moderator that acts on the same message. Since    */
+/* this isn't vital we ignore errors. Also, it is no big ideal if unlinking  */
+/* the old file fails. In the worst case it gets acted on again. If we issue */
+/*  a temp error the reject will be redone, which is slightly worse.         */
+
+    if (*(err = qmail_close(&qq)) == '\0') {
+        fd = open_trunc(fnnew.s);
+        if (fd != -1)
+          close(fd);
+        unlink(fnmsg.s);
+        strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+        strerr_die2x(0,"ezmlm-moderate: info: qp ",strnum);
+    } else
+        strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
+
+  } else if (str_start(action,ACTION_ACCEPT)) {
+        fd = open_read(fnmsg.s);
+        if (fd == -1)
+          if (errno !=error_noent)
+            strerr_die4sys(111,FATAL,ERR_OPEN,fnmsg.s,": ");
+          else /* shouldn't happen since we've got lock */
+            strerr_die3x(100,FATAL,fnmsg.s,ERR_MOD_TIMEOUT);
+
+    substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+                               /* read "Return-Path:" line */
+    if (getln(&sstext,&line,&match,'\n') == -1 || !match)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    maketo();                  /* extract SENDER to "to" */
+    env_put2("SENDER",to.s);   /* set SENDER */
+    if (seek_begin(fd) == -1)  /* rewind, since we read an entire buffer */
+      strerr_die4sys(111,FATAL,ERR_SEEK,fnmsg.s,": ");
+
+/* ##### NO REASON TO USE SH HERE ##### */
+        sendargs[0] = "/bin/sh";
+        sendargs[1] = "-c";
+        if (argc > optind) {
+          sendargs[2] = argv[optind];
+        } else {
+          if (!stralloc_copys(&send,auto_bin)) die_nomem();
+          if (!stralloc_cats(&send,"/ezmlm-send")) die_nomem();
+          if (sendopt.len > 2)
+            if (!stralloc_cat(&send,&sendopt)) die_nomem();
+          if (!stralloc_cats(&send," '")) die_nomem();
+          if (!stralloc_cats(&send,dir)) die_nomem();
+          if (!stralloc_cats(&send,"'")) die_nomem();
+          if (!stralloc_0(&send)) die_nomem();
+          sendargs[2] = send.s;
+        }
+        sendargs[3] = 0;
+
+    switch(child = fork()) {
+      case -1:
+        strerr_die2sys(111,FATAL,ERR_FORK);
+      case 0:          /* child */
+        close(0);
+        dup(fd);       /* make fnmsg.s stdin */
+        execv(*sendargs,sendargs);
+        if (errno == error_txtbsy || errno == error_nomem ||
+            errno == error_io)
+          strerr_die5sys(111,FATAL,ERR_EXECUTE,"/bin/sh -c ",sendargs[2],": ");
+        else
+          strerr_die5sys(100,FATAL,ERR_EXECUTE,"/bin/sh -c ",sendargs[2],": ");
+       }
+         /* parent */
+      wait_pid(&wstat,child);
+      close(fd);
+      if (wait_crashed(wstat))
+        strerr_die3x(111,FATAL,sendargs[2],ERR_CHILD_CRASHED);
+      switch(wait_exitcode(wstat)) {
+        case 100:
+          strerr_die2x(100,FATAL,"Fatal error from child");
+        case 111:
+           strerr_die2x(111,FATAL,"Temporary error from child");
+        case 0:
+          break;
+        default:
+          strerr_die2x(111,FATAL,"Unknown temporary error from child");
+      }
+      if (!stralloc_copys(&fnnew,"mod/accepted/")) die_nomem();
+
+      if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem();
+      if (!stralloc_0(&fnnew)) die_nomem();
+/* ignore errors */
+      fd = open_trunc(fnnew.s);
+      if (fd != -1)
+        close(fd);
+      unlink(fnmsg.s);
+      _exit(0);
+   }
+}
diff --git a/ezmlm-receipt.1 b/ezmlm-receipt.1
new file mode 100644 (file)
index 0000000..cc6531c
--- /dev/null
@@ -0,0 +1,129 @@
+.TH ezmlm-receipt 1
+.SH NAME
+ezmlm-receipt \- handle sublist replies at the main list
+.SH SYNOPSIS
+.B ezmlm-receipt
+[
+.B \-dD
+]
+.I dir
+.SH DESCRIPTION
+.B ezmlm-receipt
+handles mail from sublists of the main list
+stored in
+.I dir
+and, if it exists, the associated main digest list.
+.B ezmlm-receipt
+is used for main list with only sublists as subscribers. It is not
+useful for normal mailing lists.
+
+.B ezmlm-receipt
+is normally invoked from a
+.B .qmail
+file.
+It reads a mail message from its standard input,
+and a mail envelope from the
+.BR SENDER ,
+.BR LOCAL ,
+and
+.BR HOST
+environment variables.
+
+.B ezmlm-receipt
+has two functions. It takes the place of
+.B ezmlm-receipt(1)
+for main lists that have only sublists as subscribers.
+Bounces do not lead to removal of the bouncing
+address.
+.B emzlm-receipt
+saves the
+bounce, and logs the event to the mail log.
+.B ezmlm-receipt
+also saves the bounce to
+.I dir\fB/bounce/dnnn.ppp
+where
+.B nnn
+is a time stamp and
+.B ppp
+the PID of the process. A maximum of 50 bounces are saved. Further bounces
+are discarded until room is made in the bounce directory by manually removing
+files.
+If the list uses and SQL
+database, the even is logged to the _mlog table instead, and only if the
+distribution cookie matches. The bounce is still saved, but only if it
+contained a matching distribution cookie.
+
+.B ezmlm-receipt
+also handles feedback messages from the sublist. These are used to monitor
+that the sublist is able to send out messages. Receipt of feedback messages
+is logged to the mail log.
+If the list uses and SQL
+database, the even is logged to the _mlog table instead, and only if the
+distribution cookie matches.
+.SH OPTIONS
+.TP
+.B \-d
+.B ezmlm-receipt
+will assume the message is for a digest list.
+Normally,
+.B ezmlm-receipt
+will autodetect this from the bounce address. Autodetection makes
+.B ezmlm-receipt
+less flexible and will be removed in future versions.
+.TP
+.B \-D
+.B ezmlm-receipt
+will assume that the bounce is for a normal (non-digest) list.
+Normally,
+.B ezmlm-receipt
+will autodetect this from the bounce address. Autodetection makes
+.B ezmlm-receipt
+less flexible and will be removed in future versions.
+.B \-D
+will become the default.
+.SH ADDRESSES
+.B ezmlm-receipt
+handles mail sent to any of the following addresses:
+.TP
+.I local\fB\-return\-
+Trash.
+A help message or subscription acknowledgment bounced.
+.TP
+.I local\fB\-return\-\fImsg\fB\-\fIbox\fB=\fIdomain
+Distribution bounce.
+Message number
+.I msg
+was lost on the way to the sublist
+.IR box\fB@\fIdomain .
+.TP
+.I local\fB\-return\-\fImsg\fB\-
+Pre-VERP distribution bounce, in QSBMF format.
+Message number
+.I msg
+was lost on the way to one or more sublists;
+.B ezmlm-receipt
+will parse the bounce to figure out the addresses.
+.TP
+.I local\fB\-return\-receipt
+Feedback message from a sublist.
+.B ezmlm-receipt
+will figure out message number and sublist address from the envelope sender.
+
+For all the above addresses if,
+.I local
+is followed by
+.IR \-digest ,
+messages are assumed to relate to the digest list, and are stored in
+.I dir\fB/digest/bounce
+rather than in
+.I dir \fB/bounce .
+.SH "SEE ALSO"
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-return(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm-weed(1),
+ezmlm(5),
+qmail-command(8)
diff --git a/ezmlm-receipt.c b/ezmlm-receipt.c
new file mode 100644 (file)
index 0000000..7b664f9
--- /dev/null
@@ -0,0 +1,374 @@
+/*$Id: ezmlm-receipt.c,v 1.10 1999/02/05 04:57:44 lindberg Exp $*/
+/*$Name: ezmlm-idx-0324 $*/
+/* Handles receipts and bounces from sublists at the main list */
+/* Set up instead of ezmlm-return in DIR/bouncer of main list */
+
+#include <sys/types.h>
+#include "direntry.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "case.h"
+#include "quote.h"
+#include "getln.h"
+#include "substdio.h"
+#include "error.h"
+#include "readwrite.h"
+#include "fmt.h"
+#include "now.h"
+#include "seek.h"
+#include "idx.h"
+#include "errtxt.h"
+
+#define FATAL "ezmlm-receipt: fatal: "
+#define INFO "ezmlm-receipt: info: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-receipt: usage: ezmlm-receipt [-dD] dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_badaddr()
+{
+  strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+void die_trash()
+{
+  strerr_die2x(0,INFO,"trash address");
+}
+
+stralloc line = {0};
+stralloc quoted = {0};
+stralloc intro = {0};
+stralloc bounce = {0};
+stralloc header = {0};
+stralloc failure = {0};
+stralloc paragraph = {0};
+stralloc ddir = {0};
+stralloc outhost = {0};
+stralloc outlocal = {0};
+stralloc inlocal = {0};
+stralloc tagline = {0};
+stralloc listaddr = {0};
+stralloc fndate = {0};
+stralloc fndir = {0};
+stralloc fndatenew = {0};
+
+void die_datenew()
+{ strerr_die4sys(111,FATAL,ERR_WRITE,fndatenew.s,": "); }
+void die_msgin()
+{ strerr_die2sys(111,FATAL,ERR_READ_INPUT); }
+
+char strnum[FMT_ULONG];
+char inbuf[1024];
+substdio ssin;
+
+char outbuf[256];      /* small - rarely used */
+substdio ssout;
+
+unsigned long when;
+unsigned long addrno = 0L;
+
+char *sender;
+char *dir;
+char *workdir;
+void **psql = (void **) 0;
+stralloc listno = {0};
+
+
+void doit(addr,msgnum,when,bounce)
+/* Just stores address\0nsgnum\0 followed by bounce. File name is          */
+/* dttt.ppp[.n], where 'ttt' is a time stamp, 'ppp' the pid, and 'n' the   */
+/* number when there are more than 1 addresses in a pre-VERP bounce. In    */
+/* this case, the first one is just dttt.ppp, the decond dttt.ppp.2, etc.  */
+/* For a main list, bounces come from sublists. They are rare and serious. */
+char *addr;
+unsigned long msgnum;
+unsigned long when;
+stralloc *bounce;
+{
+  int fd;
+  unsigned int pos;
+  DIR *bouncedir;
+  direntry *d;
+  unsigned int no;
+
+  if (!stralloc_copys(&fndir,workdir)) die_nomem();
+  if (!stralloc_cats(&fndir,"/bounce")) die_nomem();
+  if (!stralloc_0(&fndir)) die_nomem();
+  bouncedir = opendir(fndir.s);
+  if (!bouncedir)
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
+    else
+      strerr_die3x(111,FATAL,fndir.s,ERR_NOEXIST);
+
+  no = MAX_MAIN_BOUNCES;       /* no more than this many allowed */
+  while (no && (d = readdir(bouncedir))) {
+    if (str_equal(d->d_name,".")) continue;
+    if (str_equal(d->d_name,"..")) continue;
+    --no;
+  }
+  closedir(bouncedir);
+  if (!no)                     /* max no of bounces exceeded */
+    strerr_die2x(0,INFO,ERR_MAX_BOUNCE);
+                               /* save bounce */
+  if (!stralloc_copys(&fndate,workdir)) die_nomem();
+  if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
+  pos = fndate.len - 1;
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
+  if (!stralloc_cats(&fndate,".")) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
+        die_nomem();
+  if (addrno) {        /* so that pre-VERP bounces make a d... file per address */
+               /* for the first one we use the std-style fname */
+    if (!stralloc_cats(&fndate,".")) die_nomem();
+    if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,addrno))) die_nomem();
+  }
+  addrno++;    /* get ready for next */
+  if (!stralloc_0(&fndate)) die_nomem();
+  if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
+  fndatenew.s[pos] = 'D';
+
+  fd = open_trunc(fndatenew.s);
+  if (fd == -1) die_datenew();
+  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+  if (substdio_puts(&ssout,addr) == -1) die_datenew();
+  if (substdio_put(&ssout,"",1) == -1) die_datenew();
+  if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1)
+       die_datenew();
+  if (substdio_put(&ssout,"",1) == -1) die_datenew();
+
+  if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
+  if (!quote2(&quoted,sender)) die_nomem();
+  if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
+  if (substdio_puts(&ssout,">\n") == -1) die_datenew();
+  if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
+  if (substdio_flush(&ssout) == -1) die_datenew();
+  if (fsync(fd) == -1) die_datenew();
+  if (close(fd) == -1) die_datenew(); /* NFS stupidity */
+  if (rename(fndatenew.s,fndate.s) == -1)
+    strerr_die6sys(111,FATAL,ERR_MOVE,fndatenew.s," to ",fndate.s,": ");
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *local;
+  char *host;
+  char *action;
+  char *def;
+  int flagdig = 1;
+  int flaghaveintro;
+  int flaghaveheader;
+  int match;
+  unsigned long msgnum;
+  unsigned int i;
+  unsigned int len;
+  char *cp;
+
+  umask(022);
+  sig_pipeignore();
+
+  when = (unsigned long) now();
+
+  dir = argv[1];
+  if (!dir) die_usage();
+  if (*dir == '-') {
+    if (dir[1] == 'd') {
+      flagdig = 2;
+    } else if (dir[1] == 'D') {
+      flagdig = 0;
+    } else
+      die_usage();
+    dir = argv[2];
+    if (!dir) die_usage();
+  }
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  sender = env_get("SENDER");
+  def = env_get("DEFAULT");
+  local = env_get("LOCAL");
+
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  workdir = dir;
+  if (def) {                           /* qmail>=1.02 */
+    action = def;                      /* now see if -digest-return- */
+    if (flagdig == 1) {
+      flagdig = 0;
+      if (str_len(local) >= str_len(def) + 14)
+       if (str_start(local + str_len(local) - 14 - str_len(def),"digest-"))
+         flagdig = 2;
+    }
+  } else {                             /* older version of qmail */
+    getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+    if (inlocal.len > str_len(local)) die_badaddr();
+    if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+    action = local + inlocal.len;
+    if (flagdig == 1) {
+      flagdig = 0;
+      if (str_start(action,"-digest")) {
+       flagdig = 2;
+       action += 7;
+      }
+    }
+    if (!str_start(action,"-return-")) die_badaddr();
+    action += 8;
+  }
+  if (flagdig) {
+    if (!stralloc_copys(&ddir,dir)) die_nomem();
+    if (!stralloc_cats(&ddir,"/digest")) die_nomem();
+    if (!stralloc_0(&ddir)) die_nomem();
+    workdir = ddir.s;
+    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
+  }
+  if (!*action) die_trash();
+
+  substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+  if (!case_diffs(action,"receipt")) {
+    host = sender + str_rchr(sender,'@');
+    if (*host)
+      *(host++) = '\0';
+    cp = sender;
+                               /* check recipient in case it's a bounce*/
+    while (*(cp++)) {          /* decode sender */
+      cp += str_chr(cp,'-');
+      if (case_starts(cp,"-return-")) {
+        if (!scan_ulong(cp + 8,&msgnum))
+          strerr_die2x(100,FATAL,"bad VERP format for receipt");
+         *cp = '\0';
+        if (!stralloc_copys(&listaddr,sender)) die_nomem();
+        if (!stralloc_append(&listaddr,"@")) die_nomem();
+        if (!stralloc_cats(&listaddr,host)) die_nomem();
+        if (!stralloc_0(&listaddr)) die_nomem();
+        break;
+      }
+    }
+    for(;;) {                                          /* Get X-tag from hdr*/
+      if (getln(&ssin,&line,&match,'\n') == -1) die_msgin();
+      if (!match)
+        break;
+
+      if (line.len == 1) break;
+      if (case_startb(line.s,line.len,TXT_TAG)) {
+       len = str_len(TXT_TAG);
+        if (!stralloc_catb(&tagline,line.s +len,line.len - len -1)) die_nomem();
+               /* NOTE: tagline is dirty! We quote it for sql and rely on */
+               /* std log clean for maillog */
+        break;
+      }
+    }
+               /* feedback ok even if not sub. Will be filtered by subreceipt*/
+               /* For instance, main list feedback is ok, but !issub. */
+    subreceipt(workdir,msgnum,&tagline,listaddr.s,2,INFO,FATAL);
+    closesql();
+    _exit(0);
+  }
+                               /* not receipt - maybe bounce */
+                               /* no need to lock. dttt.pid can be assumed */
+                               /* to be unique and if not would be over- */
+                               /* written even with lock */
+  action += scan_ulong(action,&msgnum);
+  if (*action != '-') die_badaddr();
+  ++action;
+               /* scan bounce for tag. It'll be in the BODY! */
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (!match) break;
+    if (case_startb(line.s,line.len,TXT_TAG)) {
+      len = str_len(TXT_TAG);
+      if (!stralloc_catb(&tagline,line.s +len,line.len - len -1)) die_nomem();
+               /* NOTE: tagline is dirty! We quote it for sql and rely on */
+               /* std log clean for maillog */
+      break;
+    }
+  }
+  if (seek_begin(0) == -1)
+    strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+  substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+  if (*action) {       /* normal bounce */
+
+    if (slurpclose(0,&bounce,1024) == -1) die_msgin();
+    i = str_rchr(action,'=');
+    if (!stralloc_copyb(&listaddr,action,i)) die_nomem();
+    if (action[i]) {
+      if (!stralloc_cats(&listaddr,"@")) die_nomem();
+      if (!stralloc_cats(&listaddr,action + i + 1)) die_nomem();
+    }
+    if (!stralloc_0(&listaddr)) die_nomem();
+               /* don't check for sub, since issub() doesn't see sublists */
+    switch (subreceipt(workdir,msgnum,&tagline,listaddr.s,-1,INFO,FATAL)) {
+       case -1: strerr_die2x(0,INFO,ERR_COOKIE);
+       case -2: strerr_die2x(0,INFO,ERR_NOT_ACTIVE);
+       default: doit(listaddr.s,msgnum,when,&bounce);
+    }
+    closesql();
+    _exit(0);
+  }                    /* pre-VERP bounce, in QSBMF format */
+
+  flaghaveheader = 0;
+  flaghaveintro = 0;
+
+  for (;;) {
+    if (!stralloc_copys(&paragraph,"")) die_nomem();
+    for (;;) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!match) die_trash();
+      if (!stralloc_cat(&paragraph,&line)) die_nomem();
+      if (line.len <= 1) break;
+    }
+
+    if (!flaghaveheader) {
+      if (!stralloc_copy(&header,&paragraph)) die_nomem();
+      flaghaveheader = 1;
+      continue;
+    }
+
+    if (!flaghaveintro) {
+      if (paragraph.s[0] == '-' && paragraph.s[1] == '-')
+        continue;              /* skip MIME boundary if it exists */
+      if (paragraph.len < 15) die_trash();
+      if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
+      if (!stralloc_copy(&intro,&paragraph)) die_nomem();
+      flaghaveintro = 1;
+      continue;
+    }
+
+    if (paragraph.s[0] == '-')
+      break;
+
+    if (paragraph.s[0] == '<') {       /* find address */
+      if (!stralloc_copy(&failure,&paragraph)) die_nomem();
+
+      if (!stralloc_copy(&bounce,&header)) die_nomem();
+      if (!stralloc_cat(&bounce,&intro)) die_nomem();
+      if (!stralloc_cat(&bounce,&failure)) die_nomem();
+
+      i = byte_chr(failure.s,failure.len,'\n');
+      if (i < 3) die_trash();
+
+      if (!stralloc_copyb(&listaddr,failure.s + 1,i - 3)) die_nomem();
+      if (byte_chr(listaddr.s,listaddr.len,'\0') == listaddr.len) {
+        if (!stralloc_0(&listaddr)) die_nomem();
+        if (subreceipt(workdir,msgnum,&tagline,listaddr.s,-1,INFO,FATAL) == 0)
+         doit(listaddr.s,msgnum,when,&bounce);
+      }
+    }
+  }
+  closesql();
+  _exit(0);
+}
+
index cb8cb7a..643ceb8 100644 (file)
@@ -4,29 +4,149 @@ ezmlm-reject \- reject messages unsuitable for distribution
 .SH SYNOPSIS
 .B ezmlm-reject
 [
-.B \-cCsS
+.B \-bBcChHqQsStT
+][
+.I dir
 ]
 .SH DESCRIPTION
 .B ezmlm-reject
 reads a mail message from its standard input.
 It rejects the message if it sees something it doesn't like.
+
+If the message has a ``Precedence: junk'' header,
+.B ezmlm-reject
+exits 99 causing the message to be ignored. This causes replies from (some)
+vacation programs to be ignored.
+
+If
+.I dir
+is specified,
+.B ezmlm-reject
+reads
+.I dir\fB/msgsize
+for a maximum:minimum message body size in bytes. Either number will be ignored
+if 0 or omitted.
+.B ezmlm-reject
+will also look for
+.I dir\fB/mimereject
+and 
+.IR dir\fB/mimeremove .
+if
+.IR dir\fB/mimereject
+exists,
+Messages of the Content-Types specified or with MIME parts of these
+types will be rejected.
+If
+.I dir\fB/mimeremove
+exists,
+.B ezmlm-reject
+will reject messages of the Content-Types specified. These content-types will
+be accepted as parts of a composite MIME message. They will be
+stripped out later by
+.BR ezmlm-send(1) .
+
+.B ezmlm-reject
+will exit 100 (permanent error) for rejections. If the list address is not in
+the message ``To:'' or ``Cc:'' headers and the
+.B \-t
+switch is active, the exit code is 100 or 99, depending on the
+.B \-q
+switch (see below).
+If you would like to forward messages instead of rejecting them, you can
+into the appropriate .qmail file put:
+
+.EX
+       |condredirect newaddress except /path/ezmlm-reject DIR
+.EE
+
+Now rejected posts will
+go to this address, and the recipient can take appropriate action.
 .SH OPTIONS
 .TP
+.B \-b
+Reject the message if the message body starts ``subscribe''
+or ``unsubscribe''. If the
+.B \-c
+switch is specified, messages will also be rejected if the subject starts
+with these words.
+.B \-B
+(Default.)
+Do not reject messages starting ``subscribe'' or ``unsubscribe''.
+.TP
 .B \-c
 (Default.)
 Commands are not permitted in the Subject line.
-A Subject line consisting solely of HELP, SUBSCRIBE, or UNSUBSCRIBE
+A Subject line consisting solely of HELP, REMOVE, SUBSCRIBE, or UNSUBSCRIBE
 is rejected.
 .TP
 .B \-C
 Commands are permitted in the subject line.
 .TP
+.B \-h
+Reject message if any of the headers in
+.I dir\fB/headerreject
+occur in the header. Mailing-List headers of other mailing list managers
+can be put into this file, one per line, to prevent processing of messages
+from other mailing lists.
+.I dir
+must be specified.
+
+A reasonable set of headers would be: ``Mailing-List'', ``X-ml-name'',
+\``ml-name'',``X-loop'',``X-listprocessor-version'',``X-mailing-list''. Case
+is not important.
+.TP
+.B \-H
+(Default.)
+Ignore
+.IR dir\fB/headerreject .
+.B \-q
+Quiet. If the message due to the lack of the list address (see
+.BR \-t )
+is not accepted, this is logged but no error message is sent to the sender.
+.TP
+.B \-q
+Quiet. If a message due to the lack of the list address (see
+.BR \-t )
+is not accepted,
+the message is silently ignored.
+.TP
+.B \-Q
+(Default.)
+Not quiet. If a message due to the lack of the list address (see
+.BR \-t )
+is not accepted,
+the sender is notified by an error message.
+.TP
 .B \-s
 (Default.)
 A nonempty Subject line is required.
 .TP
 .B \-S
 A Subject line is not required.
+.TP
+.B \-t
+(Default.)
+Reject messages that do not have the list address in the ``To:'' or ``Cc:''
+header(s).
+.B ezmlm-reject
+needs access to
+.I dir\fB/outhost
+and
+.I dir\fB/outlocal
+to check this. This check is silently omitted if
+.I dir
+is not specified, to assure backwards compatibility with existing ezmlm lists.
+.TP
+.B \-T
+Do not require the list address in the ``To:'' or ``Cc:'' header(s).
+.SH BUGS
+.B ezmlm-reject
+does not handle rfc822 comments in ``Content-Type:'' lines if present
+before the type or boundary.
+This could be used to defeat the
+MIME rejection function.
+OTOH, this function is intended to reduce garbage, not guarantee
+its elimination.
 .SH "SEE ALSO"
 ezmlm-send(1),
 qmail-command(8)
index ffeea99..55195e0 100644 (file)
 #include "stralloc.h"
 #include "getln.h"
 #include "sgetopt.h"
+#include "getconf.h"
+#include "constmap.h"
+#include "fmt.h"
+#include "qmail.h"
+#include "seek.h"
+#include "scan.h"
+#include "env.h"
+#include "errtxt.h"
+#include "idx.h"
 
-int flagrejectcommands = 1;
-int flagneedsubject = 1;
+#define FATAL "ezmlm-reject: fatal: "
 
+int flagrejectcommands = 1;    /* reject if subject is simple command */
+int flagneedsubject = 1;       /* reject if subject is missing */
+int flagtook = 0;              /* reject unless listaddress in To: or Cc: */
+int exitquiet = 100;           /* reject with error (100) rather than exit */
+                               /* quietly (99) if listaddress missing */
+int flagheaderreject = 0;      /* don't reject messages with headers from */
+                               /* other mailing lists. */
+int flagbody = 0;              /* =1 => reject is subject or body starts with*/
+                               /* "subscribe" or "unsubscribe" */
+int flagforward = 0;           /* =1 => forward commands to list-request */
+int flagparsemime = 0;
 int flaghavesubject = 0;
 int flaghavecommand = 0;
+int flagcheck = 0;             /* set after boundary is found in body, */
+                               /* until blank line */
 
+stralloc mimeremove = {0};
+stralloc mimereject = {0};
+stralloc headerreject = {0};
+
+struct constmap mimeremovemap;
+struct constmap mimerejectmap;
+struct constmap headerrejectmap;
+
+char strnum[FMT_ULONG];
 char buf0[256];
-substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
+substdio ssin = SUBSTDIO_FDBUF(read,0,buf0,(int) sizeof(buf0));
+substdio ssin2 = SUBSTDIO_FDBUF(read,0,buf0,(int) sizeof(buf0));
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
+
 stralloc line = {0};
+stralloc to = {0};
+stralloc outhost = {0};
+stralloc outlocal = {0};
+stralloc content = {0};
+stralloc subject = {0};
+stralloc boundary = {0};
+stralloc precd = {0};
+stralloc mydtline = {0};
+
+void die_nomem()
+{
+  strerr_die2x(100,FATAL,ERR_NOMEM);
+}
+
+void die_usage()
+{
+  strerr_die2x(100,FATAL,"usage: ezmlm-reject [-bBcCfFhHqQsStT] [dir]");
+}
+
+unsigned int findlocal(sa,n)
+       /* n is index of '@' within sa. Returns index to last postition */
+       /* of local, n otherwise. */
+stralloc *sa;          /* line */
+unsigned int n;
+{
+  char *first;
+  register char *s;
+  register int level = 0;
+
+  first = sa->s;
+  s = sa->s + n;
+  if (s <= first) return n;
+  while (--s >= first) {
+    switch (*s) {
+      case ' ': case '\t': case '\n': break;
+      case ')':
+        if (--s <= first) return n;
+        if (*s == '\\') break;
+        ++level; ++s;
+        while (level && --s > first) {
+          if (*s == ')') if (*(s-1) != '\\') ++level;
+          if (*s == '(') if (*(s-1) != '\\') --level;
+        }
+        break;
+      case '"':
+        --s;
+        if (s < first) return n;
+        return (unsigned int) (s - first);
+      default:
+        return (unsigned int) (s - first);
+    }
+#include "env.h"
+  }
+}
+
+unsigned int findhost(sa,n)
+       /* s in index to a '@' within sa. Returns index to first pos of */
+       /* host part if there is one, n otherwise. */
+stralloc *sa;          /* line */
+unsigned int n;
+{
+  char *last;
+  register char *s;
+  register int level = 0;
+
+  last = sa->s + sa->len - 1;
+  s = sa->s + n;
+  if (s >= last) return n;
+  while (++s <= last) {
+    switch (*s) {
+      case ' ': case '\t': case '\n': break;
+      case '(':
+        ++level;
+        while (level && (++s < last)) {
+          if (*s == ')') --level; if (!level) break;
+          if (*s == '(') ++level;
+          if (*s == '\\') ++s;
+        }
+        break;
+      case '"':
+        while (++s < last) {
+          if (*s == '"') break;
+          if (*s == '\\') ++s;
+        }
+        break;
+      default:
+        return (unsigned int) (s - sa->s);
+    }
+  }
+}
+
+int getto(sa)
+       /* find list address in line. If found, return 1, else return 0. */
+  stralloc *sa;
+{
+  unsigned int pos = 0;
+  unsigned int pos1;
+
+  if (!sa->len) return 0;              /* no To: or Cc: line */
+  while ((pos += 1 + byte_chr(sa->s+pos+1,sa->len-pos-1,'@')) != sa->len) {
+    pos1 = findhost(sa,pos);
+    if (pos1 == pos) break;
+    if (pos1 + outhost.len <= sa->len)
+      if (!case_diffb(sa->s+pos1,outhost.len,outhost.s)) { /* got host */
+        pos1 = findlocal(sa,pos);
+        if (pos1 == pos) break;
+        ++pos1;                                /* avoids 1 x 2 below */
+        if (pos1 >= outlocal.len)
+        if (!case_diffb(sa->s+pos1-outlocal.len,outlocal.len,outlocal.s))
+          return 1;                    /* got local as well */
+     }
+  }
+  return 0;
+}
 
 void main(argc,argv)
 int argc;
 char **argv;
 {
+  unsigned long maxmsgsize = 0L;
+  unsigned long minmsgsize = 0L;
+  unsigned long msgsize = 0L;
   int opt;
-  char *x;
-  int len;
+  char linetype = ' ';
+  char *cp, *cpstart, *cpafter;
+  char *dir;
+  char *err;
+  char *sender;
+  unsigned int len;
   int match;
 
-  while ((opt = getopt(argc,argv,"cCsS")) != opteof)
+  while ((opt = getopt(argc,argv,"bBcCfFhHqQsStT")) != opteof)
     switch(opt) {
+      case 'b': flagbody = 1; break;
+      case 'B': flagbody = 0; break;
       case 'c': flagrejectcommands = 1; break;
       case 'C': flagrejectcommands = 0; break;
+      case 'f': flagforward = 1; break;
+      case 'F': flagforward = 0; break;
+      case 'h': flagheaderreject = 1; break;
+      case 'H': flagheaderreject = 0; break;
+      case 'q': exitquiet = 99; break;
+      case 'Q': exitquiet = 100; break;
       case 's': flagneedsubject = 1; break;
       case 'S': flagneedsubject = 0; break;
-      default:
-       strerr_die1x(100,"ezmlm-reject: usage: ezmlm-reject [ -cCsS ]");
+      case 't': flagtook = 0; break;
+      case 'T': flagtook = 1; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-reject: version ezmlm-0.53+",EZIDX_VERSION);
+
+      default: die_usage();
+    }
+  dir = argv[optind];
+  if (dir) {
+    if (chdir(dir) == -1)
+      strerr_die4x(111,FATAL,ERR_SWITCH,dir,": ");
+    flagparsemime = 1;         /* only if dir do we have mimeremove/reject */
+    if (getconf_line(&line,"msgsize",0,FATAL,dir)) {
+      if (!stralloc_0(&line)) die_nomem();
+      len = scan_ulong(line.s,&maxmsgsize);
+      if (line.s[len] == ':')
+        scan_ulong(line.s+len+1,&minmsgsize);
     }
+    if (!flagtook || flagforward) {
+      getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+      getconf_line(&outhost,"outhost",1,FATAL,dir);
+    }
+    if (flagforward) {
+      if (!stralloc_copys(&mydtline,"Delivered-To: command forwarder for "))
+       die_nomem();
+      if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
+      if (!stralloc_cats(&mydtline,"@")) die_nomem();
+      if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
+      if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+    }
+  } else {
+    flagtook = 1;              /* if no "dir" we can't get outlocal/outhost */
+    flagforward = 0;           /* nor forward requests */
+  }
 
+  if (flagparsemime) {         /* set up MIME parsing */
+    getconf(&mimeremove,"mimeremove",0,FATAL,dir);
+      constmap_init(&mimeremovemap,mimeremove.s,mimeremove.len,0);
+    getconf(&mimereject,"mimereject",0,FATAL,dir);
+      constmap_init(&mimerejectmap,mimereject.s,mimereject.len,0);
+  }
+  if (flagheaderreject) {
+    if (!dir) die_usage();
+    getconf(&headerreject,"headerreject",1,FATAL,dir);
+    constmap_init(&headerrejectmap,headerreject.s,headerreject.len,0);
+  }
   for (;;) {
-    if (getln(&ss0,&line,&match,'\n') == -1)
-      strerr_die2sys(111,"ezmlm-reject: fatal: ","unable to read input: ");
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
     if (!match) break;
+    if (flagheaderreject)
+      if (constmap(&headerrejectmap,line.s,byte_chr(line.s,line.len,':')))
+        strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+
     if (line.len == 1) break;
+    cp = line.s; len = line.len;
+    if ((*cp == ' ' || *cp == '\t')) {
+      switch(linetype) {
+       case 'T': if (!stralloc_catb(&to,cp,len-1)) die_nomem(); break;
+       case 'S': if (!stralloc_catb(&subject,cp,len-1)) die_nomem(); break;
+       case 'C': if (!stralloc_catb(&content,cp,len-1)) die_nomem(); break;
+       case 'P': if (!stralloc_catb(&precd,cp,len-1)) die_nomem(); break;
+       default: break;
+      }
+    } else {
+      if (!flagtook &&
+               (case_startb(cp,len,"to:") || case_startb(cp,len,"cc:"))) {
+       linetype = 'T';         /* cat so that To/Cc don't overwrite */
+        if (!stralloc_catb(&to,line.s + 3,line.len - 4)) die_nomem();
+      } else if ((flagneedsubject || flagrejectcommands) &&
+                        case_startb(cp,len,"subject:")) {
+       if (!stralloc_copyb(&subject,cp+8,len-9)) die_nomem();
+       linetype = 'S';
+      } else if (case_startb(cp,len,"content-type:")) {
+       if (!stralloc_copyb(&content,cp+13,len-14)) die_nomem();
+       linetype = 'C';
+      } else if (case_startb(cp,len,"precedence:")) {
+       if (!stralloc_copyb(&precd,cp+11,len-12)) die_nomem();
+       linetype = 'P';
+      } else {
+       if (flagforward && line.len == mydtline.len) {
+         if (!byte_diff(line.s,line.len,mydtline.s))
+            strerr_die2x(100,FATAL,ERR_LOOPING);
+        }
+        linetype = ' ';
+      }
+    }
+  }
+  if (precd.len >= 4 &&
+               (!case_diffb(precd.s + precd.len - 4,4,"junk") ||
+               !case_diffb(precd.s + precd.len - 4,4,"bulk")))
+         strerr_die1x(99,ERR_JUNK);    /* ignore precedence junk/bulk */
+  cp = subject.s;
+  len = subject.len;
+  while (len && (cp[len-1] == ' ' || cp[len-1] == '\t')) --len;
+  while (len && ((*cp == ' ') || (*cp == '\t'))) { ++cp; --len; }
+  flaghavesubject = 1;
+
+  if (flagbody)
+    if (len > 9 && case_starts(cp,"subscribe") ||
+       len > 11 && case_starts(cp,"unsubscribe"))
+      flaghavecommand = 1;
 
-    x = line.s; len = line.len - 1;
-    while (len && ((x[len - 1] == ' ') || (x[len - 1] == '\t'))) --len;
+  switch(len) {
+    case 0: flaghavesubject = 0; break;
+    case 4: if (!case_diffb("help",4,cp)) flaghavecommand = 1; break;
+    case 6:    /* Why can't they just leave an empty subject empty? */
+           if (!case_diffb("(null)",6,cp))
+              flaghavesubject = 0;
+            else
+           if (!case_diffb("(none)",6,cp))
+              flaghavesubject = 0;
+            else
+              if (!case_diffb("remove",6,cp))
+               flaghavecommand = 1;
+            break;
+    case 9: if (!case_diffb("subscribe",9,cp)) flaghavecommand = 1; break;
+    case 11: if (!case_diffb("unsubscribe",11,cp)) flaghavecommand = 1; break;
+    case 12: if (!case_diffb("(no subject)",12,cp)) flaghavesubject = 0; break;
+    default: break;
+  }
 
-    if (case_startb(x,len,"subject:")) {
-      x += 8; len -= 8;
-      while (len && ((*x == ' ') || (*x == '\t'))) { ++x; --len; }
-      if (len) {
-        flaghavesubject = 1;
+  if (!flagtook && !getto(&to))
+    strerr_die2x(exitquiet,FATAL,ERR_NO_ADDRESS);
 
-        if (len == 4)
-          if (!case_diffb("help",4,x))
-            flaghavecommand = 1;
+  if (flagneedsubject && !flaghavesubject)
+    strerr_die2x(100,FATAL,ERR_NO_SUBJECT);
 
-        if (len == 9)
-          if (!case_diffb("subscribe",9,x))
-            flaghavecommand = 1;
+  if (flagrejectcommands && flaghavecommand)
+    if (flagforward) {                 /* flagforward => forward */
+      sender = env_get("SENDER");
+      if (!sender || !*sender)         /* can't [won't] forward */
+        strerr_die2x(100,FATAL,ERR_SUBCOMMAND);
+      if (qmail_open(&qq,(stralloc *) 0) == -1)        /* open queue */
+       strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+      qmail_put(&qq,mydtline.s,mydtline.len);
+      if (seek_begin(0) == -1)
+       strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+      if (substdio_copy(&ssqq,&ssin2) != 0)
+       strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!stralloc_copy(&to,&outlocal)) die_nomem();
+      if (!stralloc_cats(&to,"-request@")) die_nomem();
+      if (!stralloc_cat(&to,&outhost)) die_nomem();
+      if (!stralloc_0(&to)) die_nomem();
+      qmail_from(&qq,sender);
+      qmail_to(&qq,to.s);
+      if (*(err = qmail_close(&qq)) == '\0') {
+        strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+        strerr_die2x(99,"ezmlm-request: info: forward qp ",strnum);
+      } else
+        strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
+    } else
+      strerr_die2x(100,FATAL,ERR_SUBCOMMAND);
 
-        if (len == 11)
-          if (!case_diffb("unsubscribe",11,x))
-            flaghavecommand = 1;
+  if (content.len) {                   /* MIME header */
+    cp = content.s;
+    len = content.len;
+    while (len && *cp == ' ' || *cp == '\t') { ++cp; --len; }
+    cpstart = cp;
+    if (*cp == '"') {                  /* might be commented */
+      ++cp; cpstart = cp;
+      while (len && *cp != '"') { ++cp; --len; }
+    } else {
+      while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
+        ++cp; --len;
       }
     }
-  }
 
-  if (flagneedsubject && !flaghavesubject)
-    strerr_die1x(100,"\
-ezmlm-reject: fatal: I need a nonempty Subject line in every message.\n\
-If you are trying to subscribe or unsubscribe, WRONG ADDRESS!\n\
-Do not send administrative requests to the mailing list.\n\
-Send an empty message to ...-help@... for automated assistance.");
+    if (flagparsemime)
+    if (constmap(&mimeremovemap,cpstart,cp-cpstart) ||
+       constmap(&mimerejectmap,cpstart,cp-cpstart)) {
+      *(cp) = (char) 0;
+      strerr_die5x(100,FATAL,ERR_BAD_TYPE,cpstart,"'",ERR_SIZE_CODE);
+    }
 
-  if (flagrejectcommands && flaghavecommand)
-    strerr_die1x(100,"\
-ezmlm-reject: fatal: Your Subject line looks like a command word.\n\
-If you are trying to subscribe or unsubscribe, WRONG ADDRESS!\n\
-Do not send administrative requests to the mailing list.\n\
-Send an empty message to ...-help@... for automated assistance.");
+    cpafter = content.s+content.len;
+    while((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
+      ++cp;
+      while (cp < cpafter && (*cp == ' ') || (*cp == '\t')) ++cp;
+      if (case_startb(cp,cpafter - cp,"boundary=")) {
+        cp += 9;                       /* after boundary= */
+        if (cp < cpafter && *cp == '"') {
+          ++cp;
+          cpstart = cp;
+          while (cp < cpafter && *cp != '"') ++cp;
+         if (cp == cpafter)
+               strerr_die1x(100,ERR_MIME_QUOTE);
+        } else {
+          cpstart = cp;
+          while (cp < cpafter &&
+             *cp != ';' && *cp != ' ' && *cp != '\t') ++cp;
+        }
+        if (!stralloc_copys(&boundary,"--")) die_nomem();
+        if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
+               die_nomem();
+       break;
+      }
+    }          /* got boundary, now parse for parts */
+  }
 
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (!match) break;
+    if (line.len == 1) {
+      flagcheck = 0;
+      continue;
+               /* Doesn't do continuation lines. _very_ unusual, and worst */
+               /* case one slips through that shouldn't have */
+    } else if (flagcheck && case_startb(line.s,line.len,"content-type:")) {
+        cp = line.s + 13;
+       len = line.len - 14;                    /* zap '\n' */
+        while (*cp == ' ' || *cp == '\t') { ++cp; --len; }
+        cpstart = cp;
+       if (*cp == '"') {                       /* quoted */
+         ++cp; cpstart = cp;
+         while (len && *cp != '"') { ++cp; --len; }
+        } else {                               /* not quoted */
+          while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
+           ++cp; --len;
+         }
+        }
+       if (flagparsemime && constmap(&mimerejectmap,cpstart,cp-cpstart)) {
+          *cp = '\0';
+          strerr_die4x(100,FATAL,ERR_BAD_PART,cpstart,ERR_SIZE_CODE);
+        }
+    } else if (boundary.len && *line.s == '-' && line.len > boundary.len &&
+       !str_diffn(line.s,boundary.s,boundary.len)) {
+        flagcheck = 1;
+    } else {
+      if (!msgsize && flagbody)
+       if (case_startb(line.s,line.len,"subscribe") ||
+               case_startb(line.s,line.len,"unsubscribe"))
+         strerr_die2x(100,FATAL,ERR_BODYCOMMAND);
+      if (!flagcheck) {
+         msgsize += line.len;
+         if (maxmsgsize && msgsize > maxmsgsize) {
+           strnum[fmt_ulong(strnum,maxmsgsize)] = 0;
+           strerr_die5x(100,FATAL,ERR_MAX_SIZE,strnum," bytes",ERR_SIZE_CODE);
+         }
+      }
+    }
+  }
+  if (msgsize < minmsgsize) {
+    strnum[fmt_ulong(strnum,minmsgsize)] = 0;
+        strerr_die5x(100,FATAL,ERR_MIN_SIZE,strnum," bytes",ERR_SIZE_CODE);
+  }
   _exit(0);
 }
diff --git a/ezmlm-request.1 b/ezmlm-request.1
new file mode 100644 (file)
index 0000000..4f63e19
--- /dev/null
@@ -0,0 +1,216 @@
+.TH ezmlm-request 1
+.SH NAME
+ezmlm-request \- Process subject line and body ezmlm commands
+.SH SYNOPSIS
+.B ezmlm-request
+[
+.B \-f\fI config
+]
+.I dir
+.SH DESCRIPTION
+.B ezmlm-request
+processes ezmlm commands in the subject line or message body.
+.B ezmlm-request
+enables these uses to send the message to
+.I list\fB\-request\fI@host
+with the complete command address line in the subject field,
+or with commands and arguments separated by white
+space.
+.B ezmlm-request
+uses the text to construct a ezmlm command message to the list.
+If the subject does not start with a letter,
+.B ezmlm-request
+instead uses the first body line that starts with a letter. Processing
+terminates if a line with a hyphen in the first position is encountered.
+
+All commands are expected to be in ezmlm command address format or formatted
+as:
+
+.EX
+.BR command [list@listhost [user@userhost]]
+.EE
+
+.B ezmlm-request
+when invoked with the
+.B \-f
+switch and a configuration file (see below), ignores the subject and processes
+the first body line (per rules above) in conjunction with the configuration
+file. It also services the 
+.B lists
+and
+.B which
+commands. This can be used
+to construct a global list interface, similar to that used by some other
+mailing list managers.
+
+Messages at the 
+.I list\fB\-request\fI@host
+are restricted to the local list. When
+.B ezmlm-request
+is invoked with the
+.B \-f\fI config
+switch, command messages are limited to lists in
+.I config
+or at the local host.
+
+Invalid requests for an existing ezmlm list will
+lead to a ``help'' message from
+.BR ezmlm-manage(1) .
+.SH OPTIONS
+.TP
+.B \-f\fI config
+Function as a global interface to ezmlm lists in accordance with
+.IR config.
+This file consists of lines starting in the first position
+with ``list@host:listdir:description''. Lines that are blank or start
+ with ``#'' are ignored. ``listdir''
+and ``description'' are optional. If only ``list@host'' is given, the list
+is used to restrict commands (see below), but not listed. To allow the list
+to be shown by a ``list'' command, use ``list@host:''. To specify only
+the list name and description, use ``list@host::description''.
+If ``listdir'' is
+present, the
+.B which
+command attempts to determine if the user is a subscriber of the list.
+.B NOTE:
+this will work only if the user running
+.B ezmlm-request
+has read access to the lists subscriber database.
+
+If ``listhost'' is not specified,
+.B ezmlm-request
+will use the ``listhost'' from the first
+.I config
+entry matching ``listlocal''. If ``listhost'' is specified, but not found
+in
+.IR config ,
+it is set to the contents of
+.IR dir\fB/outhost .
+.SH USAGE
+Place an invocation of
+.B ezmlm-request
+in
+.I dir\fB/manager
+anywhere before the
+.B ezmlm-manage(1)
+line.
+
+Alternatively, set up
+.I dir\fB/request
+with an invocation of
+.BR ezmlm-request .
+Make a link from
+.I ~/.qmail-list-request
+to this file.
+
+For the global interface, place 
+.B /path/ezmlm-request -f \fIconfig dir
+into a file.
+Link
+.I ~/.qmail-ezmlm
+and
+.I ~/.qmail-ezmlm-default
+to this file. The latter allows
+.B ezmlm-request
+to handle its own bounces as well as to reply to messages to e.g.
+\``user-ezmlm-lists@listhost''.
+Create
+.IR dir\fB/inlocal
+and
+.IR dir\fB/outlocal
+with ``user-ezmlm'',
+.IR dir\fB/outhost
+with ``listhost'',
+.IR dir\fB/headerremove
+with headers to be stripped (copy from a list),
+.IR dir/text\fB/help ,
+.IR dir/text\fB/top ,
+and
+.I dir/text\fB/bottom
+with the appropriate texts.
+Also, create
+.I config
+with the appropriate contents.
+
+Mail to ``user-ezmlm@listhost'' will now be answered by
+.BR ezmlm-request .
+.SH "RECOGNIZED COMMANDS"
+Any command not recognized by
+.B ezmlm-request
+is assumed to be valid, as long as it consists of only letters, numbers,
+hyphen, underscore, period, and ``+''. This allows
+.B ezmlm-request
+to correctly handle commands added by the list owner.
+
+A number of commands are recognized by
+.B ezmlm-request
+but not processed. Instead they are mapped to
+.B help 
+without arguments. These
+are:
+.BR system ,
+.BR put ,
+and
+.BR set .
+
+.B ezmlm-request
+also handles a number of aliases for ezmlm commands. Since
+.B ezmlm-request
+only passes on requests to the list, local restrictions apply.
+For commands that have aliases, accepted aliases are listed:
+.TP
+.B subscribe
+sub
+.TP
+.B unsubscribe
+unsub, signoff, remove.
+.TP
+.B index
+ind.
+.TP
+.B list
+recipients, showdist, review, rev, who.
+.TP
+
+Some commands are handled differently when used without arguments:
+.TP
+.B query
+Treated like ``which''.
+.TP
+.B list
+Treated like ``lists''.
+.SH BUGS
+.B ezmlm-request
+places stricter requirements on addresses than rfc822. Thus, some addresses
+that are rfc822-compliant cannot be used as
+.B ezmlm-request
+command arguments. If you fix this,
+please send a patch to lindberg@id.wustl.edu. I think qmail has the same
+restriction, though.
+
+.B ezmlm-request
+uses NUL as a line terminator internally. Thus, if will fail if NUL is found
+within the line it tries to interpret as a command. It is harmless, other than
+that the remainder of the line will be ignored.
+
+The
+.B ezmlm-request
+\``which''
+command does not differentiate between a list for which the command is not
+available, a list for which the subscriber db is not accessible, and a list
+for which the address is not a subscriber. This should be considered a feature.
+.SH BUGS
+.B ezmlm-request
+when used as a global interface and receiving multipart messages assumes that
+the first line of the fist part is the command. Further, it assumes that the
+first line starting``--'' is the first MIME boundary. This is virtually
+always true, but it is easy to construct legal messages that do not fit these
+assumptions.
+.B ezmlm-request
+in the global interface role
+will fail if this first part or the entire message is base64 encoded.
+.SH "SEE ALSO"
+ezmlm-get(1),
+ezmlm-manage(1),
+ezmlm-send(1),
+ezmlm(5)
diff --git a/ezmlm-request.c b/ezmlm-request.c
new file mode 100644 (file)
index 0000000..661616d
--- /dev/null
@@ -0,0 +1,826 @@
+/*$Id: ezmlm-request.c,v 1.34 1999/08/18 01:50:04 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "error.h"
+#include "qmail.h"
+#include "env.h"
+#include "sig.h"
+#include "open.h"
+#include "getln.h"
+#include "case.h"
+#include "str.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "now.h"
+#include "quote.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "substdio.h"
+#include "getconf.h"
+#include "constmap.h"
+#include "fmt.h"
+#include "sgetopt.h"
+#include "byte.h"
+#include "seek.h"
+#include "errtxt.h"
+#include "copy.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-request: fatal: "
+#define INFO "ezmlm-request: info: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-request: usage: ezmlm-request [-f lists.cfg] dir");
+}
+
+void die_nomem()
+{
+  strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
+void die_badaddr()
+{
+  strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+
+char strnum[FMT_ULONG];
+
+void *psql = (void *) 0;
+
+char *userlocal = (char *) 0;
+char *userhost = (char *) 0;
+char *listlocal = (char *) 0;
+char *listhost = (char *) 0;
+char *cfname = (char *) 0;
+char *command = "help";
+stralloc line = {0};
+stralloc qline = {0};
+stralloc usr = {0};
+stralloc lhost = {0};
+stralloc subject = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+stralloc listname = {0};
+stralloc hostname = {0};
+stralloc outhost = {0};
+stralloc headerremove = {0};
+stralloc mailinglist = {0};
+stralloc cmds = {0};
+stralloc from = {0};
+stralloc to = {0};
+stralloc charset = {0};
+char *boundary = "zxcaeedrqcrtrvthbdty";       /* cheap "rnd" MIME boundary */
+int flagcd = '\0';                             /* no encoding by default */
+
+struct constmap headerremovemap;
+struct constmap commandmap;
+int flaggotsub = 0;            /* Found a subject */
+       /* cmdstring has all commands seperated by '\'. cmdxlate maps each */
+       /* command alias to the basic command, which is used to construct  */
+       /* the command address (positive numbers) or handled by this       */
+       /* program (negative numbers). Note: Any command not matched is    */
+       /* used to make a command address, so ezmlm request can handle     */
+       /* ("transmit") user-added commands.                               */
+const char *cmdstring =
+               "system\\help\\"                        /* 1,2 */
+               "subscribe\\unsubscribe\\index\\"       /* 3,4,5 */
+               "info\\list\\query\\"                   /* 6,7,8 */
+               "sub\\unsub\\remove\\signoff\\"         /* 9,10,11,12 */
+               "lists\\which\\"                        /* 13,14 */
+               "ind\\rev\\review\\recipients\\"        /* 15,16,17,18 */
+               "who\\showdist\\"                       /* 19,20 */
+               "put\\set";                             /* 21,22 */
+
+       /* map aliases. -> 0 not recognized. -> 1 recognized will be made    */
+       /* help and arguments scrapped. < 0 handled locally. HELP without    */
+       /* args also handled locally */
+                       /* the last are not supported -> help */
+const int cmdxlate[] = { 0,1,2,3,4,5,6,7,8,3,4,4,4,-13,-14,5,7,7,7,7,7,
+                       1,1 };
+
+       /* If there are no arguments (listlocal = 0) then commands are mapped*/
+       /* through this. This way, help, list, query, ... can mean something */
+       /* here even though they have local funcions at the lists if used    */
+       /* with arguments. (Made same lengh as cmdxlate in case of bugs.)    */
+       /* Note: This is used ONLY for the global interface */
+const int noargsxlate[] = { 0,1,-2,3,4,5,-2,-13,-14,9,10,11,12,13,14,15,16,17,
+                       18,19,20,21,22 };
+
+       /* these need to be defined as the index of the corresponding      */
+       /* commands. They are handled by ezmlm-request. NOTE: Help is >0!  */
+#define EZREQ_LISTS 13
+#define EZREQ_WHICH 14
+#define EZREQ_HELP  2
+#define EZREQ_BAD 1
+
+substdio sstext;
+char textbuf[1024];
+datetime_sec when;
+struct datetime dt;
+char date[DATE822FMT];
+
+struct qmail qq;
+
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+
+substdio ssout;
+char outbuf[1];
+
+stralloc mydtline = {0};
+
+void transferenc()
+{
+       if (flagcd) {
+          qmail_puts(&qq,"\n--");
+          qmail_puts(&qq,boundary);
+          qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+          qmail_puts(&qq,charset.s);
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"quoted-printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        }
+}
+
+int code_qput(s,n)
+char *s;
+unsigned int n;
+{
+    if (!flagcd)
+      qmail_put(&qq,s,n);
+    else {
+      if (flagcd == 'B')
+        encodeB(s,n,&qline,0,FATAL);
+      else
+        encodeQ(s,n,&qline,FATAL);
+      qmail_put(&qq,qline.s,qline.len);
+    }
+    return 0;          /* always succeeds */
+}
+
+/* Checks the argument. Only  us-ascii letters, numbers, ".+-_" are ok. */
+/* NOTE: For addresses this is more restrictive than rfc821/822.        */
+void checkarg(s)
+char *s;
+{
+  register char *cp;
+  register char ch;
+  cp = s;
+  if (!cp) return;                             /* undef is ok */
+  while ((ch = *cp++)) {
+    if (ch >= 'a' && ch <= 'z')
+        continue;                              /* lc letters */
+    if (ch >= '0' && ch <='9')                 /* digits */
+       continue;
+    if (ch == '.' || ch == '-' || ch == '_' || ch == '+')
+       continue;                               /* ok chars */
+    if (ch >= 'A' && ch <= 'Z') continue;      /* UC LETTERS */
+    strerr_die4x(100,ERR_NOT_CLEAN,": \"",s,"\"");
+  }
+  return;
+}
+
+/* parses line poited to by cp into sz:s as per:                        */
+/* 1. listlocal-command-userlocal=userhost@listhost                     */
+/* 2. command userlocal@userhost                                        */
+/* 3. command userlocal@userhost listlocal@listhost                     */
+/* 4. command listlocal@listhost                                        */
+/* 5. command listlocal[@listhost] userlocal@userhost                   */
+/* 6. which [userlocal@userhost]                                       */
+/* The first 3 are valid only if !cfname, i.e. -request operation and   */
+/* listlocal and listhost are always set to outlocal@outhost. Options   */
+/* 4-5 are for the global address (cfname is set). Here listhost is     */
+/* taken from the first list in *cfname matching listlocal, or set to   */
+/* outhost, if not specified. If specified, it's accepted if it matches */
+/* a list in *cfname and silently set to outhost otherwise. Pointers to */
+/* unspecified parts are set to NULL in this routine to be dealt with   */
+/* elsewhere. "Which" special argument order (6) is fixed elsewhere.    */
+/* If listhost is not given, "@outhost" is added. Absence of 'userhost' */
+/* is accepted to allow commands that take arguments that are not       */
+/* addresses (e.g. -get12-34).                                          */
+
+void parseline(cp)
+char *cp;
+
+{
+  register char *cp1, *cp2;
+  char *cp3;
+
+  cp1 = cp;
+  while (*cp1) {                               /* make tabs into spaces */
+    if (*cp1 == '\t') *cp1 = ' ';
+    ++cp1;
+  }
+                                       /* NOTE: outlocal has '\0' added! */
+  if (outlocal.len < str_len(cp) && cp[outlocal.len -1] == '-' &&
+       case_starts(cp,outlocal.s))      {      /* normal ezmlm cmd */
+    command = cp + outlocal.len;               /* after the '-' */
+    listlocal = outlocal.s;
+    listhost = outhost.s;
+    cp1 = command;
+    while (*cp1 && *cp1 != '-') ++cp1;         /* find next '-' */
+    if (*cp1) {
+      *cp1 = '\0';
+      userlocal = ++cp1;                       /* after '-' */
+      cp1 = cp1 + str_rchr(cp1,'@');           /* @ _or_ end */
+      *cp1 = '\0';                             /* last '=' in userlocal */
+      cp1 = userlocal + str_rchr(userlocal,'=');
+      if (*cp1) {                              /* found '=' */
+        *cp1 = '\0';                           /* zap */
+        userhost = cp1 + 1;                    /* char after '=' */
+      }
+    }
+  } else {                             /* '@' before ' ' means complete cmd */
+    if (str_chr(cp,'@') < str_chr(cp,' '))     /* addr where inlocal failed */
+       strerr_die2x(100,FATAL,ERR_REQ_LOCAL);
+                                               /* to match */
+    command = cp;
+    cp1 = cp + str_chr(cp,' ');
+    if (*cp1) {
+      *cp1++ = '\0';
+      while (*cp1 && *cp1 == ' ') ++cp1;       /* skip spaces */
+    }
+    cp2 = 0;
+    if (*cp1) {                                        /* argument */
+      cp2 = cp1 + str_chr(cp1,' ');
+      cp3 = cp2;
+      while (*cp2 && *cp2 == ' ') ++cp2;       /* skip spaces */
+      *cp3 = '\0';
+
+      if (!*cp2)
+        cp2 = 0;
+      else {
+        cp3 = cp2 + str_chr(cp2,' ');
+        *cp3 = '\0';
+      }
+    } else
+      cp1 = 0;
+
+    if (!cfname && !cp2) {     /* the single arg is user if we serve a */
+      cp2 = cp1;               /* list. It's list if we serve "domo@" */
+      cp1 = 0;
+    }
+    if (cp2) {
+      userlocal = cp2;
+      cp2 += str_chr(cp2,'@');
+      if (*cp2) {
+        *cp2++ = '\0';
+        userhost = cp2;
+      }
+    }
+    if (cp1) {
+      listlocal = cp1;
+      cp1 += str_chr(cp1,'@');
+      if (*cp1) {
+        *cp1++ = '\0';
+        listhost = cp1;
+      }
+    }
+  }
+  checkarg(command);                   /* better safe than sorry */
+  checkarg(userlocal); checkarg(userhost);
+  checkarg(listlocal); checkarg(listhost);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *local;
+  char *action;
+  char *def;
+  char *sender;
+  char *psz;
+  char *err;
+  int cmdidx;
+  int flagsub;
+  int flagok;
+  int flagnosubject;
+  int match;
+  int flaginheader;
+  int flagbadfield;
+  int flagmultipart = 0;
+  int fd;
+  int opt;
+  unsigned int pos,pos1,len,last;
+
+  (void)umask(022);
+  sig_pipeignore();
+
+  while ((opt = getopt(argc,argv,"f:F:vV")) != opteof)
+    switch(opt) {
+      case 'F':
+      case 'f': if (optarg) cfname = optarg; break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-request version: ",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+  dir = argv[optind];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+       /* do minimum to identify request for this program in case */
+       /* it's invoked in line with e.g. ezmlm-manage */
+
+  def = env_get("DEFAULT");
+  if (def) {                   /* qmail>=1.02 */
+    action = def;
+  } else if (cfname) {         /* older qmail OR just list-mdomo */
+    local = env_get("LOCAL");
+    if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+    len = str_len(local);
+    if (len >= 8 && !case_diffb(local + len - 8,8,"-return-")) {
+      action = "return-";      /* our bounce with qmail<1.02 */
+    } else
+      action = "";             /* list-mdomo-xxx won't work for older lists */
+  } else {                     /* older qmail versions */
+    local = env_get("LOCAL");
+    if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+    getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+    if (inlocal.len > str_len(local)) die_badaddr();
+    if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+    action = local + inlocal.len;
+    if (*action)
+      if (*(action++) != '-') die_badaddr();   /* check anyway */
+  }
+       /* at this point action = "request" or "request-..." for std use; */
+       /* "" for majordomo@ */
+  if (!cfname) {                               /* expect request */
+    if (case_starts(action,ACTION_REQUEST))
+      action += str_len(ACTION_REQUEST);
+    else if (case_starts(action,ALT_REQUEST))
+      action += str_len(ALT_REQUEST);
+    else
+      _exit(0);                                        /* not for us */
+  }
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+
+  if (!stralloc_copy(&listname,&outlocal)) die_nomem();
+  if (!stralloc_copy(&hostname,&outhost)) die_nomem();
+  if (!stralloc_0(&outlocal)) die_nomem();
+  if (!stralloc_0(&outhost)) die_nomem();
+
+  sender = env_get("SENDER");
+  if (!sender) strerr_die2x(99,INFO,ERR_NOSENDER);
+  if (!*sender)
+    strerr_die2x(99,INFO,ERR_BOUNCE);
+  if (!sender[str_chr(sender,'@')])
+    strerr_die2x(99,INFO,ERR_ANONYMOUS);
+  if (str_equal(sender,"#@[]"))
+    strerr_die2x(99,INFO,ERR_BOUNCE);
+
+  getconf(&headerremove,"headerremove",1,FATAL,dir);
+  constmap_init(&headerremovemap,headerremove.s,headerremove.len,0);
+
+  if (!stralloc_copys(&mydtline,
+       "Delivered-To: request processor for ")) die_nomem();
+  if (!stralloc_cats(&mydtline,outlocal.s)) die_nomem();
+  if (!stralloc_cats(&mydtline,"@")) die_nomem();
+  if (!stralloc_cats(&mydtline,outhost.s)) die_nomem();
+  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+
+  flagnosubject = 1;
+  if (action[0]) {     /* mainly to allow ezmlm-lists or ezmlm-which with */
+    flagnosubject = 0; /* a command address rather than a complete msg */
+    command = action;
+    if (str_start(action,"return"))            /* kill bounces */
+      strerr_die2x(0,INFO,ERR_BOUNCE);
+    pos = 1 + str_chr(action + 1,'-');
+    if (action[pos]) {                         /* start of target */
+      action[pos] = '\0';
+      userlocal = action + pos + 1;
+      pos = str_rchr(userlocal,'=');           /* the "pseudo-@" */
+      if (userlocal[pos]) {
+       userlocal[pos] = '\0';
+        userhost = userlocal + pos + 1;
+      }
+    }
+  } else {
+    for (;;) {                                 /* Get Subject: */
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+        if (line.len == 1)
+        break;
+        if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+          flagsub = 0;
+
+          if (case_startb(line.s,line.len,"mailing-list:"))
+            strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+          else if (case_startb(line.s,line.len,"Subject:")) {
+            flaggotsub = 1;
+            pos = 8;
+            last = line.len - 2;               /* skip terminal '\n' */
+            while (line.s[last] == ' ' || line.s[last] == '\t') --last;
+            while (pos <= last &&
+               (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
+            if (!stralloc_copyb(&subject,line.s+pos,last-pos+1)) die_nomem();
+          } else if (case_startb(line.s,line.len,"content-type:")) {
+           pos = 13; last = line.len - 2;      /* not cont-line - ok */
+            while (pos <= last &&
+               (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
+           if (case_startb(line.s+pos,line.len - pos,"multipart/"))
+             flagmultipart = 1;
+         } else if (line.len == mydtline.len)
+            if (!byte_diff(line.s,line.len,mydtline.s))
+               strerr_die2x(100,FATAL,ERR_LOOPING);
+        } else if (flagsub) {  /* Continuation line */
+          pos = 1;
+          len = line.len - 2;  /* skip terminal '\n' */
+          while (line.s[len] == ' ' || line.s[len] == '\t') --len;
+          while (pos < len &&
+               (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
+          if (!stralloc_append(&subject," ")) die_nomem();
+          if (!stralloc_copy(&subject,line.s+pos,len-pos+1)) die_nomem();
+      }
+      if (!match)
+        break;
+    }
+    if (!cfname) {              /* listserv@/majordomo@ ignore */
+      register char ch;
+      if (!stralloc_0(&subject)) die_nomem();
+      ch = *subject.s;         /* valid commands/list names start w letter */
+      if ((ch <= 'z' && ch >= 'a') || (ch <= 'Z' && ch >= 'A')) {
+        parseline(subject.s);
+        flagnosubject = 0;
+      }
+    }
+    if (cfname || flagnosubject) {
+      for (;;) {                                       /* parse body */
+        if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+        if (!match) break;
+       if (line.len == 1 && flagmultipart != 2) continue;
+               /* lazy MIME cludge assumes first '--...' is start border */
+               /* which is virtually always true */
+       if (flagmultipart == 1) {               /* skip to first border */
+         if (*line.s != '-' || line.s[1] != '-') continue;
+         flagmultipart = 2;
+         continue;
+       } else if (flagmultipart == 2) {        /* skip content info */
+         if (line.len != 1) continue;
+         flagmultipart = 3;                    /* may be part within part */
+         continue;                             /* and blank line */
+       } else if (flagmultipart == 3) {
+         if (*line.s == '-' && line.s[1] == '-') {
+           flagmultipart = 2;                  /* part within part */
+           continue;
+         }
+       }
+        {
+         register char ch;
+         ch = *line.s;
+        if (line.len == 1 ||
+          !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
+          continue;                            /* skip if not letter pos 1 */
+        }
+                       /* Here we have a body line with something */
+        if (!stralloc_copy(&subject,&line)) die_nomem();       /* save it */
+        subject.s[subject.len-1] = '\0';
+        parseline(subject.s);
+        break;
+      }
+    }
+  }
+       /* Do command substitution */
+  if (!stralloc_copys(&cmds,cmdstring)) die_nomem();
+  if (!stralloc_0(&cmds)) die_nomem();
+  psz = cmds.s;
+  while (*psz) {
+    if (*psz == '\\') *psz = '\0';
+    ++psz;
+  }
+  if (!constmap_init(&commandmap,cmds.s,cmds.len,0)) die_nomem();
+  cmdidx = cmdxlate[constmap_index(&commandmap,command,str_len(command))];
+  if (cmdidx == EZREQ_BAD) {   /* recognized, but not supported -> help */
+    listlocal = 0;             /* needed 'cause arguments are who-knows-what */
+    listhost = 0;
+    userlocal = 0;
+    userhost = 0;
+    cmdidx = EZREQ_HELP;
+  }
+  if (cfname && !listlocal && !userlocal && cmdidx > 0)
+    cmdidx = noargsxlate[cmdidx];       /* some done differently if no args */
+
+       /* =0 not found. This is treated as a list command! */
+  if (cmdidx < 0 && !cfname) {
+    cmdidx = EZREQ_HELP;
+  }
+  if (qmail_open(&qq,(stralloc *) 0) == -1)
+    strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+
+  if (cmdidx >= 0) {
+       /* Things handled elsewhere. We do want to handle a simple HELP */
+       /* without arguments for e.g. majordomo@ from our own help file */
+
+    if (!stralloc_copys(&from,sender)) die_nomem();
+    if (!stralloc_0(&from)) die_nomem();
+    if (!listlocal) {
+      if (cfname)
+        strerr_die1x(100,ERR_REQ_LISTNAME);
+      else
+       listlocal = outlocal.s; /* This is at the -request address */
+    }
+       /* if !cfname listhost is made outhost. If cfname, listhost=outhost */
+       /* is ok. listhost=0 => first match in config. Other listhost is ok */
+       /* only if match is found. Otherwise it's set to outhost. */
+
+    if (!cfname || (listhost && !case_diffs(listhost,outhost.s)))
+      listhost = outhost.s;
+    else {                      /* Check listhost against config file */
+      pos = str_len(listlocal);
+      fd = open_read(cfname);
+      if (fd == -1)
+        strerr_die4sys(111,FATAL,ERR_OPEN,cfname,": ");
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      flagok = 0;                      /* got listhost match */
+      for (;;) {
+        if (getln(&sstext,&line,&match,'\n') == -1)
+          strerr_die3sys(111,FATAL,ERR_READ,cfname);
+        if (!match)
+          break;
+        if (line.len <= 1 || line.s[0] == '#')
+          continue;
+        if ((pos < line.len) && (line.s[pos] == '@') &&
+               !byte_diff(line.s,pos,listlocal)) {
+          last = byte_chr(line.s,line.len,':');
+          if (!stralloc_copyb(&lhost,line.s+pos+1,last-pos-1)) die_nomem();
+          if (!stralloc_0(&lhost)) die_nomem();
+          if (listhost) {
+            if (!case_diffs(listhost,lhost.s)) {
+              flagok = 1;
+              break;                   /* host did match */
+            } else
+              continue;                        /* host didn't match */
+          } else {                     /* none given - grab first */
+            listhost = lhost.s;
+            flagok = 1;
+            break;
+          }
+        }
+      }
+      if (!flagok)
+        listhost = outhost.s;
+      close(fd);
+    }
+    if (!listhost)
+      listhost = outhost.s;
+    if (!userlocal) {
+      if (!stralloc_copys(&usr,sender)) die_nomem();
+      if (!stralloc_0(&usr)) die_nomem();
+      userlocal = usr.s;
+      userhost = usr.s + byte_rchr(usr.s,usr.len-1,'@');
+      if (!*userhost)
+        userhost = 0;
+      else {
+        *userhost = '\0';
+        ++userhost;
+      }
+    }
+
+    if (!stralloc_copys(&to,listlocal)) die_nomem();
+    if (!stralloc_cats(&to,"-")) die_nomem();
+    if (cmdidx) {                      /* recognized - substitute */
+      if (!stralloc_cats(&to,constmap_get(&commandmap,cmdidx)))
+                die_nomem();
+    } else                             /* not recognized - use as is */
+      if (!stralloc_cats(&to,command)) die_nomem();
+
+    if (!stralloc_cats(&to,"-")) die_nomem();
+    if (!stralloc_cats(&to,userlocal)) die_nomem();
+    if (userhost) {                    /* doesn't exist for e.g. -get */
+      if (!stralloc_cats(&to,"=")) die_nomem();
+      if (!stralloc_cats(&to,userhost)) die_nomem();
+    }
+    if (!stralloc_cats(&to,"@")) die_nomem();
+    if (!stralloc_cats(&to,listhost)) die_nomem();
+    if (!stralloc_0(&to)) die_nomem();
+
+    qmail_put(&qq,mydtline.s,mydtline.len);
+
+    flaginheader = 1;
+    flagbadfield = 0;
+
+    if (seek_begin(0) == -1)
+      strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+    substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+    for (;;) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+
+      if (flaginheader && match) {
+        if (line.len == 1)
+          flaginheader = 0;
+        if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+          flagbadfield = 0;
+          if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
+           flagbadfield = 1;
+        }
+      }
+      if (!(flaginheader && flagbadfield))
+        qmail_put(&qq,line.s,line.len);
+      if (!match)
+        break;
+    }
+  } else {                             /* commands we deal with */
+    cmdidx = - cmdidx;                 /* now positive */
+    if (cmdidx == EZREQ_WHICH) {       /* arg is user, not list */
+      userlocal = listlocal; listlocal = 0;
+      userhost = listhost; listhost = 0;
+    }
+    if (!stralloc_copys(&from,outlocal.s)) die_nomem();
+    if (!stralloc_cats(&from,"-return-@")) die_nomem();
+    if (!stralloc_cats(&from,outhost.s)) die_nomem();
+    if (!stralloc_0(&from)) die_nomem();
+
+    if (userlocal) {
+      if (!stralloc_copys(&to,userlocal)) die_nomem();
+      if (!stralloc_cats(&to,"@")) die_nomem();
+      if (userhost) {
+        if (!stralloc_cats(&to,userhost)) die_nomem();
+       } else {
+        if (!stralloc_cats(&to,outhost.s)) die_nomem();
+      }
+    } else
+      if (!stralloc_copys(&to,sender)) die_nomem();
+    if (!stralloc_0(&to)) die_nomem();
+
+       /* now we need to look for charset and set flagcd appropriately */
+
+    if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+      if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+        if (charset.s[charset.len - 1] == 'B' ||
+               charset.s[charset.len - 1] == 'Q') {
+          flagcd = charset.s[charset.len - 1];
+          charset.s[charset.len - 2] = '\0';
+        }
+      }
+    } else
+      if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+    if (!stralloc_0(&charset)) die_nomem();
+    set_cpoutlocal(&listname);         /* necessary in case there are <#l#> */
+    set_cpouthost(&hostname);          /* necessary in case there are <#h#> */
+                                       /* we don't want to be send to a list*/
+    qmail_puts(&qq,"Mailing-List: ezmlm-request");
+    if (getconf(&line,"listid",0,FATAL)) {
+      qmail_puts(&qq,"List-ID: ");
+      qmail_put(&qq,line.s,line.len);
+    }
+    qmail_puts(&qq,"\nDate: ");
+    when = now();
+    datetime_tai(&dt,when);
+    qmail_put(&qq,date,date822fmt(date,&dt));
+    qmail_puts(&qq,"Message-ID: <");
+    if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
+       die_nomem();
+    if (!stralloc_append(&line,".")) die_nomem();
+    if (!stralloc_catb(&line,strnum,
+               fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+    if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
+    if (!stralloc_cats(&line,outhost.s)) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+    qmail_puts(&qq,line.s);
+    qmail_puts(&qq,">\nFrom: ");
+    if (!quote2(&line,outlocal.s)) die_nomem();
+    qmail_put(&qq,line.s,line.len);
+    if (cmdidx == EZREQ_HELP)
+      qmail_puts(&qq,"-return-@");
+    else
+      qmail_puts(&qq,"-help@");
+    qmail_puts(&qq,outhost.s);
+    qmail_puts(&qq,"\n");
+    qmail_put(&qq,mydtline.s,mydtline.len);
+    qmail_puts(&qq,"To: ");
+    if (!quote2(&line,to.s)) die_nomem();
+    qmail_put(&qq,line.s,line.len);
+    qmail_puts(&qq,"\n");
+    qmail_puts(&qq,"MIME-Version: 1.0\n");
+    if (flagcd) {
+      qmail_puts(&qq,"Content-Type: multipart/mixed; charset=");
+      qmail_puts(&qq,charset.s);
+      qmail_puts(&qq,";\n\tboundary=");
+      qmail_puts(&qq,boundary);
+    } else {
+      qmail_puts(&qq,"Content-type: text/plain; charset=");
+      qmail_puts(&qq,charset.s);
+    }
+    qmail_puts(&qq,"\nSubject: ");
+    if (!quote2(&line,outlocal.s)) die_nomem();
+    qmail_put(&qq,line.s,line.len);
+    qmail_puts(&qq,TXT_RESULTS);
+    transferenc();
+    copy(&qq,"text/top",flagcd,FATAL);
+   if (cmdidx == EZREQ_LISTS || cmdidx == EZREQ_WHICH) {
+      switch (cmdidx) {
+        case EZREQ_LISTS:
+          code_qput("LISTS:",6);
+          break;
+        case EZREQ_WHICH:
+          code_qput("WHICH (",7);
+          code_qput(to.s,to.len - 1);
+          code_qput("):\n\n",4);
+          break;
+        default: break;
+      }
+      fd = open_read(cfname);
+      if (fd == -1)
+        strerr_die4sys(111,FATAL,ERR_OPEN,cfname,": ");
+      substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+      for (;;) {
+        if (getln(&sstext,&line,&match,'\n') == -1)
+          strerr_die3sys(111,FATAL,ERR_READ,cfname);
+        if (!match)
+          break;
+        if (line.len <= 1 || line.s[0] == '#')
+          continue;
+        if (!stralloc_0(&line)) die_nomem();
+        pos = str_chr(line.s,':');
+        if (!line.s[pos])
+          break;
+        line.s[pos] = '\0';
+        ++pos;
+        pos1 = pos + str_chr(line.s + pos,':');
+        if (line.s[pos1]) {
+          line.s[pos1] = '\0';
+          ++pos1;
+        } else
+          pos1 = 0;
+
+        switch (cmdidx) {
+          case EZREQ_LISTS:
+            code_qput("\n\n\t",3);
+            code_qput(line.s,pos-1);
+            code_qput("\n",1);
+            if (pos1) {
+              code_qput(line.s+pos1,line.len-2-pos1);
+            }
+            break;
+          case EZREQ_WHICH:
+            if (issub(line.s+pos,to.s,(char *) 0,FATAL)) {
+              code_qput(line.s,pos-1);
+              code_qput("\n",1);
+            }
+           closesql();         /* likely different dbs for different lists */
+            break;
+        }
+      }
+      code_qput("\n",1);
+      close(fd);
+    } else
+      copy(&qq,"text/help",flagcd,FATAL);
+
+    copy(&qq,"text/bottom",flagcd,FATAL);
+    if (flagcd) {
+      if (flagcd == 'B') {
+        encodeB("",0,&line,2,FATAL);   /* flush */
+        qmail_put(&qq,line.s,line.len);
+      }
+       qmail_puts(&qq,"\n--");
+       qmail_puts(&qq,boundary);
+       qmail_puts(&qq,"\nContent-Type: message/rfc822");
+       qmail_puts(&qq,
+               "\nContent-Disposition: inline; filename=request.msg\n\n");
+    }
+    qmail_puts(&qq,"Return-Path: <");
+    if (!quote2(&line,sender)) die_nomem();
+    qmail_put(&qq,line.s,line.len);
+    qmail_puts(&qq,">\n");
+    if (seek_begin(0) == -1)
+      strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+    if (substdio_copy(&ssqq,&ssin2) != 0)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (flagcd) {
+      qmail_puts(&qq,"\n--");
+      qmail_puts(&qq,boundary);
+      qmail_puts(&qq,"--\n");
+    }
+  }
+  qmail_from(&qq,from.s);
+  qmail_to(&qq,to.s);
+  if (*(err = qmail_close(&qq)) != '\0')
+      strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
+
+  strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+  strerr_die3x(99,INFO, "qp ",strnum);
+}
index 3f84989..e3a8e89 100644 (file)
@@ -3,12 +3,16 @@
 ezmlm-return \- handle mailing list bounces
 .SH SYNOPSIS
 .B ezmlm-return
+[
+.B \-dD
+]
 .I dir
 .SH DESCRIPTION
 .B ezmlm-return
 handles bounces for the mailing list
 stored in
-.IR dir .
+.I dir
+and, if it exists, the associated digest list.
 
 .B ezmlm-return
 is normally invoked from a
@@ -21,6 +25,30 @@ and a mail envelope from the
 and
 .BR HOST
 environment variables.
+
+.B ezmlm-return
+exits 99, not 0, upon success.
+.SH OPTIONS
+.TP
+.B \-d
+.B ezmlm-return
+will assume the bounce is for a digest list.
+Normally,
+.B ezmlm-return
+will autodetect this from the bounce address. Autodetection makes
+.B ezmlm-return
+less flexible and will be removed in future versions.
+.TP
+.B \-D
+.B ezmlm-return
+will assume that the bounce is for a normal (non-digest) list.
+Normally,
+.B ezmlm-return
+will autodetect this from the bounce address. Autodetection makes
+.B ezmlm-return
+less flexible and will be removed in future versions.
+.B \-D
+will become the default.
 .SH ADDRESSES
 .B ezmlm-return
 handles mail sent to any of the following addresses:
@@ -59,6 +87,21 @@ bounced.
 will remove
 .I box\fB@\fIdomain
 from the mailing list.
+.TP
+.I local\fB\-return\-receipt\-\fIcookie\-fImsg\-
+A receipt from the list. This is logged. For SQL supporting lists,
+.I cookie
+is verified and receipt logged only if the cookie is correct. The arrival
+of the receipt shows that qmail at the sending host is running.
+
+For all the above addresses if,
+.I local
+is followed by
+.IR \-digest ,
+bounces are assumed to be from the digest list, and are stored in
+.I dir\fB/digest/bounce
+rather than in
+.I dir \fB/bounce .
 .SH "SEE ALSO"
 ezmlm-manage(1),
 ezmlm-make(1),
index 3bc2421..7ca3fb2 100644 (file)
@@ -1,3 +1,7 @@
+/*$Id: ezmlm-return.c,v 1.26 1999/08/07 20:50:52 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include <sys/types.h>
+#include "direntry.h"
 #include "stralloc.h"
 #include "str.h"
 #include "env.h"
 #include "now.h"
 #include "cookie.h"
 #include "subscribe.h"
-#include "issub.h"
+#include "errtxt.h"
+#include "idx.h"
 
 #define FATAL "ezmlm-return: fatal: "
-void die_usage() { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return dir"); }
-void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+#define INFO "ezmlm-return: info: "
+void die_usage()
+{ strerr_die1x(100,"ezmlm-return: usage: ezmlm-return [-dD] dir"); }
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
 void die_badaddr()
 {
-  strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
+  strerr_die2x(100,FATAL,ERR_BAD_RETURN_ADDRESS);
 }
 void die_trash()
 {
-  strerr_die1x(0,"ezmlm-return: info: trash address");
+  strerr_die2x(99,INFO,"trash address");
 }
 
 char outbuf[1024];
@@ -38,21 +45,37 @@ substdio ssin;
 char strnum[FMT_ULONG];
 char hash[COOKIE];
 char hashcopy[COOKIE];
+char *hashp = (char *) 0;
 unsigned long cookiedate;
+unsigned long addrno = 0L;
+unsigned long addrno1 = 0L;
+stralloc fndir = {0};
 stralloc fndate = {0};
 stralloc fndatenew = {0};
 stralloc fnhash = {0};
 stralloc fnhashnew = {0};
+void *psql = (void *) 0;
 
 stralloc quoted = {0};
+stralloc ddir = {0};
 char *sender;
+char *dir;
+char *workdir;
 
 void die_hashnew()
-{ strerr_die4sys(111,FATAL,"unable to write ",fnhashnew.s,": "); }
+{ strerr_die4sys(111,FATAL,ERR_WRITE,fnhashnew.s,": "); }
 void die_datenew()
-{ strerr_die4sys(111,FATAL,"unable to write ",fndatenew.s,": "); }
+{ strerr_die4sys(111,FATAL,ERR_WRITE,fndatenew.s,": "); }
 void die_msgin()
-{ strerr_die2sys(111,FATAL,"unable to read input: "); }
+{ strerr_die2sys(111,FATAL,ERR_READ_INPUT); }
+
+void makedir(s)
+char *s;
+{
+  if (mkdir(s,0755) == -1)
+    if (errno != error_exist)
+      strerr_die4x(111,FATAL,ERR_CREATE,s,": ");
+}
 
 void dowit(addr,when,bounce)
 char *addr;
@@ -60,16 +83,31 @@ unsigned long when;
 stralloc *bounce;
 {
   int fd;
+  unsigned int wpos;
+  unsigned long wdir,wfile;
 
-  if (!issub(addr)) return;
+  if (!issub(workdir,addr,(char *) 0,FATAL)) return;
 
-  if (!stralloc_copys(&fndate,"bounce/w")) die_nomem();
-  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
+  if (!stralloc_copys(&fndate,workdir)) die_nomem();
+  if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
+  if (!stralloc_0(&fndate)) die_nomem();
+  fndate.s[fndate.len - 1] = '/';      /* replace '\0' */
+  wpos = fndate.len - 1;
+  wdir = when / 10000;
+  wfile = when - 10000 * wdir;
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,wdir))) die_nomem();
+  if (!stralloc_0(&fndate)) die_nomem();
+  makedir(fndate.s);
+  --fndate.len;                                /* remove terminal '\0' */
+  if (!stralloc_cats(&fndate,"/w")) die_nomem();
+  wpos = fndate.len - 1;
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,wfile))) die_nomem();
   if (!stralloc_cats(&fndate,".")) die_nomem();
-  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
+       die_nomem();
   if (!stralloc_0(&fndate)) die_nomem();
   if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
-  fndatenew.s[7] = 'W';
+  fndatenew.s[wpos] = 'W';
 
   fd = open_trunc(fndatenew.s);
   if (fd == -1) die_datenew();
@@ -86,7 +124,7 @@ stralloc *bounce;
   if (close(fd) == -1) die_datenew(); /* NFS stupidity */
 
   if (rename(fndatenew.s,fndate.s) == -1)
-    strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
+    strerr_die6sys(111,FATAL,ERR_MOVE,fndatenew.s," to ",fndate.s,": ");
 }
 
 void doit(addr,msgnum,when,bounce)
@@ -97,16 +135,37 @@ stralloc *bounce;
 {
   int fd;
   int fdnew;
+  unsigned int pos;
+  unsigned long ddir,dfile;
 
-  if (!issub(addr)) return;
+  if (!issub(workdir,addr,(char *) 0,FATAL)) return;
 
-  if (!stralloc_copys(&fndate,"bounce/d")) die_nomem();
-  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
+  if (!stralloc_copys(&fndate,workdir)) die_nomem();
+  if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
+  if (!stralloc_0(&fndate)) die_nomem();
+  makedir(fndate.s);
+  fndate.s[fndate.len-1] = '/';                /* replace terminal '\0' */
+  ddir = when / 10000;
+  dfile = when - 10000 * ddir;
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,ddir))) die_nomem();
+  if (!stralloc_copy(&fndir,&fndate)) die_nomem();
+  if (!stralloc_0(&fndir)) die_nomem();        /* make later if necessary (new addr)*/
+  if (!stralloc_cats(&fndate,"/d")) die_nomem();
+  pos = fndate.len - 2;
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,dfile))) die_nomem();
   if (!stralloc_cats(&fndate,".")) die_nomem();
-  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
+        die_nomem();
+  if (addrno) {        /* so that pre-VERP bounces make a d... file per address */
+               /* for the first one we use the std-style fname */
+    if (!stralloc_cats(&fndate,".")) die_nomem();
+    if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,addrno))) die_nomem();
+  }
+  addrno++;    /* get ready for next */
   if (!stralloc_0(&fndate)) die_nomem();
   if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
-  fndatenew.s[7] = 'D';
+  fndatenew.s[pos] = '_';      /* fndate = bounce/d/nnnn/dmmmmmm */
+                               /* fndatenew = bounce/d/nnnn_dmmmmmm */
 
   fd = open_trunc(fndatenew.s);
   if (fd == -1) die_datenew();
@@ -123,11 +182,21 @@ stralloc *bounce;
   if (close(fd) == -1) die_datenew(); /* NFS stupidity */
 
   cookie(hash,"",0,"",addr,"");
-  if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
-  if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
+  if (!stralloc_copys(&fnhash,workdir)) die_nomem();
+  if (!stralloc_cats(&fnhash,"/bounce/h")) die_nomem();
+  if (!stralloc_0(&fnhash)) die_nomem();
+  makedir(fnhash.s);
+  fnhash.s[fnhash.len - 1] = '/';              /* replace terminal '\0' */
+  if (!stralloc_catb(&fnhash,hash,1)) die_nomem();
+  if (!stralloc_0(&fnhash)) die_nomem();
+  makedir(fnhash.s);
+  --fnhash.len;                                        /* remove terminal '\0' */
+  if (!stralloc_cats(&fnhash,"/h")) die_nomem();
+  pos = fnhash.len - 1;
+  if (!stralloc_catb(&fnhash,hash+1,COOKIE-1)) die_nomem();
   if (!stralloc_0(&fnhash)) die_nomem();
   if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
-  fnhashnew.s[7] = 'H';
+  fnhashnew.s[pos] = 'H';
 
   fdnew = open_trunc(fnhashnew.s);
   if (fdnew == -1) die_hashnew();
@@ -136,9 +205,10 @@ stralloc *bounce;
   fd = open_read(fnhash.s);
   if (fd == -1) {
     if (errno != error_noent)
-      strerr_die4sys(111,FATAL,"unable to read ",fnhash.s,": ");
+      strerr_die4sys(111,FATAL,ERR_READ,fnhash.s,": ");
+    makedir(fndir.s);
     if (rename(fndatenew.s,fndate.s) == -1)
-      strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
+      strerr_die6sys(111,FATAL,ERR_MOVE,fndatenew.s," to ",fndate.s,": ");
   }
   else {
     substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
@@ -148,7 +218,7 @@ stralloc *bounce;
     }
     close(fd);
     if (unlink(fndatenew.s) == -1)
-      strerr_die4sys(111,FATAL,"unable to unlink ",fndatenew.s,": ");
+      strerr_die4sys(111,FATAL,ERR_DELETE,fndatenew.s,": ");
   }
   if (substdio_puts(&ssout,"   ") == -1) die_hashnew();
   if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
@@ -158,7 +228,7 @@ stralloc *bounce;
   if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
 
   if (rename(fnhashnew.s,fnhash.s) == -1)
-    strerr_die6sys(111,FATAL,"unable to rename ",fnhashnew.s," to ",fnhash.s,": ");
+    strerr_die6sys(111,FATAL,ERR_MOVE,fnhashnew.s," to ",fnhash.s,": ");
 }
 
 stralloc bounce = {0};
@@ -167,14 +237,11 @@ stralloc header = {0};
 stralloc intro = {0};
 stralloc failure = {0};
 stralloc paragraph = {0};
+int flagmasterbounce = 0;
 int flaghaveheader;
 int flaghaveintro;
 
 stralloc key = {0};
-stralloc inhost = {0};
-stralloc outhost = {0};
-stralloc inlocal = {0};
-stralloc outlocal = {0};
 
 char msginbuf[1024];
 substdio ssmsgin;
@@ -183,16 +250,22 @@ void main(argc,argv)
 int argc;
 char **argv;
 {
-  char *dir;
-  char *host;
   char *local;
   char *action;
+  char *def;
+  char *ret;
+  char *cp;
   unsigned long msgnum;
   unsigned long cookiedate;
   unsigned long when;
+  unsigned long listno = 0L;
   int match;
-  int i;
+  unsigned int i;
+  int flagdig = 0;
+  int flagmaster = 0;
+  int flagreceipt = 0;
   int fdlock;
+  register char ch;
 
   umask(022);
   sig_pipeignore();
@@ -200,104 +273,158 @@ char **argv;
 
   dir = argv[1];
   if (!dir) die_usage();
+  if (*dir == '-') {                   /* for normal use */
+    if (dir[1] == 'd') {
+      flagdig = 1;
+    } else if (dir[1] == 'D') {
+      flagdig = 0;
+    } else
+      die_usage();
+    dir = argv[2];
+    if (!dir) die_usage();
+  }
 
   sender = env_get("SENDER");
-  if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
+  if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
   local = env_get("LOCAL");
-  if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
-  host = env_get("HOST");
-  if (!host) strerr_die2x(100,FATAL,"HOST not set");
+  if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+  def = env_get("DEFAULT");            /* qmail-1.02 */
 
   if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
 
   switch(slurp("key",&key,32)) {
     case -1:
-      strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
+      strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
     case 0:
-      strerr_die3x(100,FATAL,dir,"/key does not exist");
+      strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
   }
-  getconf_line(&inhost,"inhost",1,FATAL,dir);
-  getconf_line(&inlocal,"inlocal",1,FATAL,dir);
-  getconf_line(&outhost,"outhost",1,FATAL,dir);
-  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
-
-  if (inhost.len != str_len(host)) die_badaddr();
-  if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
-  if (inlocal.len > str_len(local)) die_badaddr();
-  if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+  workdir = dir;
+  action = def;
 
-  action = local + inlocal.len;
-
-  if (!str_start(action,"-return-")) die_badaddr();
-  action += 8;
+    if (str_start(action,"receipt-")) {
+      flagreceipt = 1;
+      action += 8;
+    }
+    ch = *action;              /* -d -digest, -m -master, -g -getmaster */
+    if (ch && action[1] == '-') {
+      switch (ch) {
+       case 'g': flagmaster = 1; flagdig = 1; action += 2; break;
+       case 'm': flagmaster = 1; action += 2; break;
+       default: break;
+      }
+    }
+  if (flagdig) {
+    if (!stralloc_copys(&ddir,dir)) die_nomem();
+    if (!stralloc_cats(&ddir,"/digest")) die_nomem();
+    if (!stralloc_0(&ddir)) die_nomem();
+    workdir = ddir.s;
+  }
 
   if (!*action) die_trash();
 
-  if (str_start(action,"probe-")) {
-    action += 6;
-    action += scan_ulong(action,&cookiedate);
-    if (now() - cookiedate > 3000000) die_trash();
-    if (*action++ != '.') die_trash();
-    i = str_chr(action,'-');
-    if (i != COOKIE) die_trash();
-    byte_copy(hashcopy,COOKIE,action);
-    action += COOKIE;
-    if (*action++ != '-') die_trash();
-    i = str_rchr(action,'=');
-    if (!stralloc_copyb(&line,action,i)) die_nomem();
-    if (action[i]) {
-      if (!stralloc_cats(&line,"@")) die_nomem();
-      if (!stralloc_cats(&line,action + i + 1)) die_nomem();
-    }
-    if (!stralloc_0(&line)) die_nomem();
-    strnum[fmt_ulong(strnum,cookiedate)] = 0;
-    cookie(hash,key.s,key.len,strnum,line.s,"P");
-    if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
-
-    if (subscribe(line.s,0) == 1) log("-probe",line.s);
-    _exit(0);
+  if (flagreceipt || flagmaster)                       /* check cookie */
+    if (str_chr(action,'-') == COOKIE) {
+      action[COOKIE] = '\0';
+      hashp = action;
+      action += COOKIE + 1;
   }
 
-  fdlock = open_append("lockbounce");
-  if (fdlock == -1)
-    strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
-  if (lock_ex(fdlock) == -1)
-    strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
-
-  if (str_start(action,"warn-")) {
-    action += 5;
-    action += scan_ulong(action,&cookiedate);
-    if (now() - cookiedate > 3000000) die_trash();
-    if (*action++ != '.') die_trash();
-    i = str_chr(action,'-');
-    if (i != COOKIE) die_trash();
-    byte_copy(hashcopy,COOKIE,action);
-    action += COOKIE;
-    if (*action++ != '-') die_trash();
-    i = str_rchr(action,'=');
-    if (!stralloc_copyb(&line,action,i)) die_nomem();
-    if (action[i]) {
-      if (!stralloc_cats(&line,"@")) die_nomem();
-      if (!stralloc_cats(&line,action + i + 1)) die_nomem();
+  if (!flagreceipt) {
+    if (!flagmaster && str_start(action,"probe-")) {
+      action += 6;
+      action += scan_ulong(action,&cookiedate);
+      if (now() - cookiedate > 3000000) die_trash();
+      if (*action++ != '.') die_trash();
+      i = str_chr(action,'-');
+      if (i != COOKIE) die_trash();
+      byte_copy(hashcopy,COOKIE,action);
+      action += COOKIE;
+      if (*action++ != '-') die_trash();
+      i = str_rchr(action,'=');
+      if (!stralloc_copyb(&line,action,i)) die_nomem();
+      if (action[i]) {
+       if (!stralloc_cats(&line,"@")) die_nomem();
+       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
+      }
+      if (!stralloc_0(&line)) die_nomem();
+      strnum[fmt_ulong(strnum,cookiedate)] = 0;
+      cookie(hash,key.s,key.len,strnum,line.s,"P");
+      if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
+
+      (void) subscribe(workdir,line.s,0,"","-probe",1,-1,(char *) 0,FATAL);
+      _exit(99);
     }
+
+    if (!stralloc_copys(&line,workdir)) die_nomem();
+    if (!stralloc_cats(&line,"/lockbounce")) die_nomem();
     if (!stralloc_0(&line)) die_nomem();
-    strnum[fmt_ulong(strnum,cookiedate)] = 0;
-    cookie(hash,key.s,key.len,strnum,line.s,"W");
-    if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
 
-    if (slurpclose(0,&bounce,1024) == -1) die_msgin();
-    dowit(line.s,when,&bounce);
-    _exit(0);
+    fdlock = open_append(line.s);
+    if (fdlock == -1)
+      strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,FATAL,ERR_OBTAIN,line.s,": ");
+
+    if (!flagmaster && str_start(action,"warn-")) {
+      action += 5;
+      action += scan_ulong(action,&cookiedate);
+      if (now() - cookiedate > 3000000) die_trash();
+      if (*action++ != '.') die_trash();
+      i = str_chr(action,'-');
+      if (i != COOKIE) die_trash();
+      byte_copy(hashcopy,COOKIE,action);
+      action += COOKIE;
+      if (*action++ != '-') die_trash();
+      i = str_rchr(action,'=');
+      if (!stralloc_copyb(&line,action,i)) die_nomem();
+      if (action[i]) {
+        if (!stralloc_cats(&line,"@")) die_nomem();
+        if (!stralloc_cats(&line,action + i + 1)) die_nomem();
+      }
+      if (!stralloc_0(&line)) die_nomem();
+      strnum[fmt_ulong(strnum,cookiedate)] = 0;
+      cookie(hash,key.s,key.len,strnum,line.s,"W");
+      if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
+
+      if (slurpclose(0,&bounce,1024) == -1) die_msgin();
+      dowit(line.s,when,&bounce);
+      _exit(99);
+    }
   }
-
   action += scan_ulong(action,&msgnum);
-  if (*action != '-') die_badaddr();
-  ++action;
+  if (*action++ != '-') die_badaddr();
+  cp = action;
+  if (*action >= '0' && *action <= '9') {              /* listno */
+    action += scan_ulong(action,&listno);
+    listno++;                                  /* logging is 1-53, not 0-52 */
+  }
 
-  if (*action) {
-    if (slurpclose(0,&bounce,1024) == -1) die_msgin();
+  if (hashp) {         /* scrap bad cookies */
+      if ((ret = checktag(workdir,msgnum,0L,"x",(char *) 0,hashp))) {
+        if (*ret)
+         strerr_die2x(111,FATAL,*ret);
+       else
+         die_trash();
+      } else if (flagreceipt) {
+       if (!(ret = logmsg(dir,msgnum,listno,0L,5))) {
+         closesql();
+         strerr_die6x(99,INFO,"receipt:",cp," [",hashp,"]");
+       }
+       if (*ret) strerr_die2x(111,FATAL,ret);
+       else strerr_die2x(0,INFO,ERR_DONE);
+      } else if (*action) {    /* post VERP master bounce */
+       if ((ret = logmsg(dir,msgnum,listno,0L,-1))) {
+         closesql();
+         strerr_die4x(0,INFO,"bounce [",hashp,"]");
+       }
+       if (*ret) strerr_die2x(111,FATAL,ret);
+       else strerr_die2x(99,INFO,ERR_DONE);
+      }
+   } else if (flagreceipt || flagmaster)
+       die_badaddr();
 
+  if (*action) {
     i = str_rchr(action,'=');
     if (!stralloc_copyb(&line,action,i)) die_nomem();
     if (action[i]) {
@@ -305,11 +432,12 @@ char **argv;
       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
     }
     if (!stralloc_0(&line)) die_nomem();
+    if (slurpclose(0,&bounce,1024) == -1) die_msgin();
     doit(line.s,msgnum,when,&bounce);
-    _exit(0);
+    _exit(99);
   }
 
-  /* pre-VERP bounce, in QSBMF format */
+  /* pre-VERP bounce, in QSBMF format. Receipts are never pre-VERP */
 
   substdio_fdbuf(&ssmsgin,read,0,msginbuf,sizeof(msginbuf));
 
@@ -332,6 +460,8 @@ char **argv;
     }
 
     if (!flaghaveintro) {
+      if (paragraph.s[0] == '-' && paragraph.s[1] == '-')
+        continue;              /* skip MIME boundary if it exists */
       if (paragraph.len < 15) die_trash();
       if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
       if (!stralloc_copy(&intro,&paragraph)) die_nomem();
@@ -355,10 +485,23 @@ char **argv;
       if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
       if (byte_chr(line.s,line.len,'\0') == line.len) {
         if (!stralloc_0(&line)) die_nomem();
-        doit(line.s,msgnum,when,&bounce);
+        if (flagmaster) {                              /* bounced msg slave! */
+         if ((i = str_rchr(line.s,'@')) >= 5) {        /* 00_52@host */
+           line.s[i] = '\0';                           /* 00_52 */
+           (void) scan_ulong(line.s + i - 5,&listno);
+             if ((ret = logmsg(dir,msgnum,listno + 1,0L,-1)) && *ret)
+               strerr_die2x(111,FATAL,ret);
+           strerr_warn3(INFO,"bounce ",line.s + i - 5,(struct strerr *) 0);
+           flagmasterbounce = 1;
+         }
+       } else
+         doit(line.s,msgnum,when,&bounce);
       }
     }
   }
-
-  _exit(0);
+  closesql();
+  if (flagmasterbounce)
+    strerr_die3x(0,"[",hashp,"]");
+  else
+    _exit(99);
 }
index ff70976..b7ccc59 100644 (file)
@@ -3,6 +3,11 @@
 ezmlm-send \- distribute a message to a mailing list
 .SH SYNOPSIS
 .B ezmlm-send
+[
+.B \-cCrRvV
+] [
+.B \-h\fI header
+]
 .I dir
 .SH DESCRIPTION
 .B ezmlm-send
@@ -14,9 +19,34 @@ If
 exists,
 .B ezmlm-send
 records a copy of the message in the
-.I dir\fB/archive
+.I dir\fB/archive/
 directory.
 
+If
+.I dir\fB/indexed
+exists,
+.B ezmlm-send
+adds the subject, author and time stamp of the message to the index, kept with
+the message in a subdirectory of
+.IR dir\fB/archive/ .
+The subject is processed to make reply-subject entries identical to
+original
+message subject entries.
+The subject index is used for the archive retrieval functions of
+.BR ezmlm-get(1) .  
+Use
+.B ezmlm-idx(1)
+to create a subject index from a preexisting archive.
+
+Subject and author lines are decoded if they are encoded per rfc2047. When
+split lines are unfolded, the number of escape sequences for
+iso-2022-* character sets is minimized. For instance, two
+consequtive toascii sequences are reduced.
+This processing is done for the character set specified in
+.IR dir\fB/charset .
+The result of this process is the same for a given subject, irrespective
+of encoding.
+
 At the beginning of the message,
 .B ezmlm-send
 prints a new
@@ -27,10 +57,24 @@ It rejects the message if there is already a
 .B Mailing-List
 field.
 
+If
+.I dir\fB/listid
+exists,
+.B ezmlm-send
+will assume that the format is correct and
+create a ``List-ID:'' header by placing the contents after the
+text ``List-ID: ''. 
+
+Next,
+.B ezmlm-send
+prints all the new fields listed in
+.IR dir\fB/headeradd .
+Any tags, ``<#h#>'', ``<#l#>'', or ``<#n#>'' found in these headers
+are replaced by the list host name, list local name, and message number,
+respectively.
+
 .B ezmlm-send
-then prints all the new fields listed in
-.IR dir\fB/headeradd ,
-followed by an appropriate
+then prints an appropriate
 .B Delivered-To
 line.
 
@@ -39,6 +83,63 @@ deletes any incoming fields with names listed in
 .IR dir\fB/headerremove .
 
 .B ezmlm-send
+removes MIME parts specified in
+.I dir\fB/mimeremove
+before archiving and distribution of the message.
+
+If
+.I dir\fB/text/trailer
+exists,
+.B ezmlm-send
+adds the trailer to simple text/plain messages in the same encoding as used for
+the the message. However, if the encoding is ``base64'' it is not safe
+to do this and the header is suppressed.
+For composite MIME messages, the trailer is added as a separate
+part, with the character set and encoding specified in
+.IR dir\fB/charset .
+The trailer is not added to multipart/alternative messages.
+Any tags, ``<#h#>'', ``<#l#>'', or ``<#n#>'' found in
+.I dir\fB/text/trailer
+are replaced by the list host name, list local name, and message number,
+respectively.
+
+If
+.I dir\fB/prefix
+exists,
+.B ezmlm-send
+will prefix the subject line with the first line of this
+file. A space will be added to separate
+.B prefix
+from the subject text.
+.B prefix
+is ignored for sublists. If
+.I dir\fB/prefix
+contains a ``#'', the last ``#'' will be replaced by the message number.
+Any prefix starting with text of a
+reply indicator (``Re:'', ``Re[n]:'', etc) will cause problems.
+The prefix may be
+rfc2047 encoded. Rfc2047 Iso-2022-* encoded prefixes
+.I must
+end in ascii.
+
+The prefix feature and especially the message number feature
+modify the message in violation
+with Internet mail standards. The features have been implemented by popular
+demand. Use at your own peril.
+
+.I dir\fB/sequence
+is ignored as of ezmlm-idx-0.32. Use
+.I dir\fB/headeradd
+with substitution to achieve the same goal.
+
+If
+.I dir\fB/qmqpservers
+exists,
+.B ezmlm-send will use
+.B qmail-qmqp(1)
+to send messages.
+
+.B ezmlm-send
 does not distribute bounce messages:
 if the environment variable
 .B SENDER
@@ -46,6 +147,60 @@ is set, and is either empty or
 .BR #@[] ,
 .B ezmlm-send
 rejects the message.
+.SH OPTIONS
+.TP
+.B \-c
+No longer supported. Ignored for backwards compatibility.
+.TP
+.B \-C
+No longer supported. Ignored for backwards compatibility.
+.B ezmlm-send
+has to parse the subscriber database.
+.TP
+.B \-h\fI header
+If the list is a sublist, i.e.
+.I dir\fB/sublist
+exists,
+.I header
+is required in all messages to the list. This option is used
+when ezmlm is used to run a sublist of a lists run by a different
+mailing list
+manager that uses
+.I header
+rather than ``Mailing-List'' to identify messages from the list.
+Anything after the first colon (if present) in
+.I header
+is ignored.
+.TP
+.B \-r
+Copy incoming ``Received:'' headers to the outgoing message.
+.TP
+.B \-R
+(Default.)
+Do not copy incoming ``Received:'' headers, except the one added by
+the (last) listhost, to the outgoing message.
+In some
+cases, especially for sublists,
+the messages can have a large number of ``Received:''
+headers. This may lead to bounces for some users due to
+sendmail ``hopcounts'' set too low somewhere in the mail path. These users can
+subscribe and receive warning and probe messages, but no list messages, unless
+the number of ``Received:'' headers is reduced.
+
+Pre-list ``Received:'' headers are of little interest to normal list
+subscribers. ``Received:'' headers are
+still copied to the archive and available
+to anyone from there for message tracking purposes.
+.TP
+.B \-v
+Display
+.B ezmlm-send
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-send
+version information.
 .SH "SUBLISTS"
 If
 .I dir\fB/sublist
@@ -77,10 +232,71 @@ Third,
 does not add its own
 .B Mailing-List
 field.
+
+Fourth,
+.B ezmlm-send
+uses the incoming message number for the outgoing message, if the list
+is not archived and the incoming SENDER has the correct format.
+This allows you to refer bounce warning recipients to the main list for
+archive retrieval of the missed messages. If the sublist archives
+message, it is assumed that missed messages will be retrieved from the sublist
+archive.
+
+The list
+still increments
+.I dir\fB/num
+for each message. If the sublist is archived, use of incoming message number
+for archive storage would be a security risk. In this case, the local sublist
+message number is used.
+.SH "OPTION USAGE"
+In general, the use of a prefix is discouraged. It wastes subject line space,
+creates trouble when MUAs add non-standard reply indicators. However, many
+users expect it not because it is useful, but because they are used to it.
+
+The
+.B \-C
+switch prevents posts from being set to SENDER. Rather than just copying
+out subscriber address files,
+.B ezmlm-send
+has to parse them to look for SENDER. This makes it less efficient. Also,
+it is useful for the SENDER to see the post to know that it has made it
+to the list, and it's context to other subscribers, i.e. where it came
+within the traffic of messages on the list.
+
+Avoiding SENDER as a recipient is useful in small lists, such as small
+teams with varying members, where ezmlm serves mainly as an efficient tool
+to keep the team connected without administrator intervention. Here the
+overhead of subscriber list parsing is negligible.
+.SH "CHARACTER SETS"
+If the list is indexed,
+.B ezmlm-send
+will keep a message index. rfc2047-encoded subject and from lines will be
+decoded.
+If
+.I dir\fB/charset
+exists,
+.B ezmlm-send
+will eliminate redundant escape sequences from the headers according to
+the character set specified in this file.
+Only character sets using escape sequences need this support. Currently,
+supported are iso-2022-jp*, iso-2022-kr, and iso-2022-cn*. Only iso-2022-jp
+has been tested extensively.
+
+The character set can be suffixed
+by ``:'' followed by a code. Recognized codes are ``Q'' 
+for ``Quoted-Printable'', and ``B'' for ``base64''.
+
+For
+.BR ezmlm-send ,
+this affects the format of the trailer, if a trailer is specified and if the
+message is a multipart mime message
 .SH "SEE ALSO"
+ezmlm-get(1),
+ezmlm-idx(1),
 ezmlm-manage(1),
 ezmlm-make(1),
 ezmlm-sub(1),
 ezmlm-unsub(1),
 ezmlm-reject(1),
-ezmlm(5)
+ezmlm(5),
+qmail-qmqp(1)
index 5748891..fa2913e 100644 (file)
@@ -1,3 +1,5 @@
+/* $Id: ezmlm-send.c,v 1.77 1999/10/29 02:49:14 lindberg Exp $*/
+/* $Name: ezmlm-idx-040 $*/
 #include "stralloc.h"
 #include "subfd.h"
 #include "strerr.h"
 #include "substdio.h"
 #include "getconf.h"
 #include "constmap.h"
-
+#include "byte.h"
+#include "sgetopt.h"
+#include "quote.h"
+#include "subscribe.h"
+#include "mime.h"
+#include "errtxt.h"
+#include "makehash.h"
+#include "cookie.h"
+#include "idx.h"
+#include "copy.h"
+
+int flagnoreceived = 1;                /* suppress received headers by default. They*/
+                               /* are still archived. =0 => archived and */
+                               /* copied. */
+int flaglog = 1;               /* for lists with mysql support, use tags */
+                               /* and log traffic to the database */
 #define FATAL "ezmlm-send: fatal: "
 
 void die_usage()
 {
-  strerr_die1x(100,"ezmlm-send: usage: ezmlm-send dir");
+  strerr_die1x(100,"ezmlm-send: usage: ezmlm-send [-cClLqQrR] [-h header] dir");
 }
 void die_nomem()
 {
-  strerr_die2x(111,FATAL,"out of memory");
+  strerr_die2x(111,FATAL,ERR_NOMEM);
 }
 
+       /* for writing new index file indexn later moved to index. */
+substdio ssindexn;
+char indexnbuf[1024];
+
 char strnum[FMT_ULONG];
+char szmsgnum[FMT_ULONG];
+char hash[HASHLEN];
 
 stralloc fnadir = {0};
 stralloc fnaf = {0};
+stralloc fnif = {0};
+stralloc fnifn = {0};
 stralloc fnsub = {0};
 stralloc line = {0};
+stralloc qline = {0};
+stralloc lines = {0};
+stralloc subject = {0};
+stralloc from = {0};
+stralloc received = {0};
+stralloc prefix = {0};
+stralloc content = {0};
+stralloc boundary = {0};
+stralloc charset = {0};
+stralloc dcprefix = {0};
+stralloc dummy = {0};
+stralloc qmqpservers = {0};
+
+void die_indexn()
+{
+  strerr_die4x(111,FATAL,ERR_WRITE,fnifn.s,": ");
+}
 
+void *psql = (void *) 0;
+
+unsigned long innum;
+unsigned long outnum;
+unsigned long msgnum;
+unsigned long hash_lo = 0L;
+unsigned long hash_hi = 52L;
+unsigned long msgsize = 0L;
+unsigned long cumsize = 0L;    /* cumulative archive size bytes / 256 */
+char flagcd = '\0';            /* no transfer-encoding for trailer */
+char encin = '\0';
+int flagindexed;
+int flagfoundokpart;           /* Found something to pass on. If multipart */
+                               /* we set to 0 and then set to 1 for any */
+                               /* acceptable mime part. If 0 -> reject */
+int flagreceived;
+int flagprefixed;
+unsigned int serial = 0;
 int flagarchived;
 int fdarchive;
+int fdindex;
+int fdindexn;
+char hashout[COOKIE+1];
+
 substdio ssarchive;
 char archivebuf[1024];
 
@@ -48,7 +112,9 @@ stralloc outlocal = {0};
 stralloc outhost = {0};
 stralloc headerremove = {0};
 struct constmap headerremovemap;
-stralloc headeradd = {0};
+stralloc mimeremove = {0};
+struct constmap mimeremovemap;
+char *dir;
 
 struct qmail qq;
 substdio ssin;
@@ -56,7 +122,10 @@ char inbuf[1024];
 substdio ssout;
 char outbuf[1];
 
-int mywrite(fd,buf,len)
+char textbuf[512];
+substdio sstext;
+
+unsigned int mywrite(fd,buf,len)
 int fd;
 char *buf;
 unsigned int len;
@@ -65,23 +134,33 @@ unsigned int len;
   return len;
 }
 
+int subto(s,l)
+char *s;
+unsigned int l;
+{
+  qmail_put(&qq,"T",1);
+  qmail_put(&qq,s,l);
+  qmail_put(&qq,"",1);
+  return (int) l;
+}
+
 void die_archive()
 {
-  strerr_die4sys(111,FATAL,"unable to write to ",fnaf.s,": ");
+  strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": ");
 }
 void die_numnew()
 {
-  strerr_die2sys(111,FATAL,"unable to create numnew: ");
+  strerr_die3sys(111,FATAL,ERR_CREATE,"numnew: ");
 }
 
-void put(buf,len) char *buf; int len;
+void qa_put(buf,len) char *buf; unsigned int len;
 {
   qmail_put(&qq,buf,len);
   if (flagarchived)
     if (substdio_put(&ssarchive,buf,len) == -1) die_archive();
 }
 
-void puts(buf) char *buf;
+void qa_puts(buf) char *buf;
 {
   qmail_puts(&qq,buf);
   if (flagarchived)
@@ -91,8 +170,8 @@ void puts(buf) char *buf;
 int sublistmatch(sender)
 char *sender;
 {
-  int i;
-  int j;
+  unsigned int i;
+  unsigned int j;
 
   j = str_len(sender);
   if (j < sublist.len) return 0;
@@ -109,14 +188,12 @@ char *sender;
 
 substdio ssnumnew;
 char numnewbuf[16];
-unsigned long msgnum;
-stralloc num = {0};
 
 char buf0[256];
 substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
 
 void numwrite()
-{
+{              /* this one deals with msgnum, not outnum! */
   int fd;
 
   fd = open_trunc("numnew");
@@ -124,70 +201,277 @@ void numwrite()
   substdio_fdbuf(&ssnumnew,write,fd,numnewbuf,sizeof(numnewbuf));
   if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,msgnum)) == -1)
     die_numnew();
+  if (substdio_puts(&ssnumnew,":") == -1) die_numnew();
+  if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,cumsize)) == -1)
+    die_numnew();
+
   if (substdio_puts(&ssnumnew,"\n") == -1) die_numnew();
   if (substdio_flush(&ssnumnew) == -1) die_numnew();
   if (fsync(fd) == -1) die_numnew();
   if (close(fd) == -1) die_numnew(); /* NFS stupidity */
   if (rename("numnew","num") == -1)
-    strerr_die2sys(111,FATAL,"unable to move numnew to num: ");
+    strerr_die3sys(111,FATAL,ERR_MOVE,"numnew: ");
 }
 
 stralloc mydtline = {0};
 
+int idx_copy_insertsubject()
+/* copies old index file up to but not including msg, then adds a line with */
+/* 'sub' trimmed of reply indicators, then closes the new index and moves it*/
+/* to the name 'index'. Errors are dealt with directly, and if the routine  */
+/* returns, it was successful. 'fatal' points to a program-specific error   */
+/* string. Sub is not destroyed, but from is!!!                             */
+/* returns 1 if reply-indicators were found, 0 otherwise.                   */
+/* no terminal \n or \0 in any of the strallocs! */
+{
+  char *cp;
+  unsigned long idx;
+  int match;
+  int r;
+  unsigned int pos;
+
+  if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
+  if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,outnum / 100)))
+       die_nomem();
+  if (!stralloc_copy(&fnif,&fnadir)) die_nomem();
+  if (!stralloc_copy(&fnifn,&fnif)) die_nomem();
+  if (!stralloc_cats(&fnif,"/index")) die_nomem();
+  if (!stralloc_cats(&fnifn,"/indexn")) die_nomem();
+  if (!stralloc_0(&fnif)) die_nomem();
+  if (!stralloc_0(&fnifn)) die_nomem();
+  if (!stralloc_0(&fnadir)) die_nomem();
+
+                       /* may not exists since we run before ezmlm-send */
+  if (mkdir(fnadir.s,0755) == -1)
+    if (errno != error_exist)
+      strerr_die4x(111,FATAL,ERR_CREATE,fnadir.s,": ");
+
+                       /* Open indexn */
+  fdindexn = open_trunc(fnifn.s);
+  if (fdindexn == -1)
+    strerr_die4x(111,FATAL,ERR_WRITE,fnifn.s,": ");
+
+                       /* set up buffers for indexn */
+  substdio_fdbuf(&ssindexn,write,fdindexn,indexnbuf,sizeof(indexnbuf));
+
+  concatHDR(subject.s,subject.len,&lines,FATAL);       /* make 1 line */
+  decodeHDR(lines.s,lines.len,&qline,charset.s,FATAL); /* decode mime */
+  r = unfoldHDR(qline.s,qline.len,&lines,charset.s,&dcprefix,1,FATAL);
+                                                /* trim mime */
+
+  fdindex = open_read(fnif.s);
+  if (fdindex == -1) {
+    if (errno != error_noent)
+      strerr_die4x(111,FATAL,ERR_OPEN, fnif.s, ": ");
+  } else {
+    substdio_fdbuf(&ssin,read,fdindex,inbuf,sizeof(inbuf));
+    for(;;) {
+      if (getln(&ssin,&qline,&match,'\n') == -1)
+        strerr_die4sys(111,FATAL,ERR_READ, fnif.s, ": ");
+      if (!match)
+        break;
+      pos = scan_ulong(qline.s,&idx);
+      if (!idx)                                /* "impossible!" */
+        strerr_die2x(111,FATAL,ERR_BAD_INDEX);
+      if (idx >= outnum)
+        break;                         /* messages always come in order */
+      if (substdio_put(&ssindexn,qline.s,qline.len) == -1)
+        die_indexn();
+      if (qline.s[pos] == ':') {       /* has author line */
+        if (getln(&ssin,&qline,&match,'\n') == -1)
+          strerr_die4x(111,FATAL,ERR_READ, fnif.s, ": ");
+        if (!match && qline.s[0] != '\t')      /* "impossible! */
+          strerr_die2x(111,FATAL,ERR_BAD_INDEX);
+        if (substdio_put(&ssindexn,qline.s,qline.len) == -1)
+          die_indexn();
+      }
+    }
+    close(fdindex);
+  }
+  if (!stralloc_copyb(&qline,strnum,fmt_ulong(strnum,outnum))) die_nomem();
+  if (!stralloc_cats(&qline,": ")) die_nomem();        /* ':' for new ver */
+  makehash(lines.s,lines.len,hash);
+  if (!stralloc_catb(&qline,hash,HASHLEN)) die_nomem();
+  if (!stralloc_cats(&qline," ")) die_nomem();
+  if (r & 1)           /* reply */
+    if (!stralloc_cats(&qline,"Re: ")) die_nomem();
+  if (!stralloc_cat(&qline,&lines)) die_nomem();
+  if (!stralloc_cats(&qline,"\n\t")) die_nomem();
+  if (!stralloc_cat(&qline,&received)) die_nomem();
+  if (!stralloc_cats(&qline,";")) die_nomem();
+
+  concatHDR(from.s,from.len,&lines,FATAL);
+  mkauthhash(lines.s,lines.len,hash);
+
+  if (!stralloc_catb(&qline,hash,HASHLEN)) die_nomem();
+  if (!stralloc_cats(&qline," ")) die_nomem();
+
+  decodeHDR(cp,author_name(&cp,lines.s,lines.len),&from,charset.s,FATAL);
+  (void) unfoldHDR(from.s,from.len,&lines,charset.s,&dcprefix,0,FATAL);
+  if (!stralloc_cat(&qline,&lines)) die_nomem();
+
+  if (!stralloc_cats(&qline,"\n")) die_nomem();
+  if (substdio_put(&ssindexn,qline.s,qline.len) == -1) die_indexn();
+  if (substdio_flush(&ssindexn) == -1) die_indexn();
+  if (fsync(fdindexn) == -1) die_indexn();
+  if (fchmod(fdindexn,MODE_ARCHIVE | 0700) == -1) die_indexn();
+  if (close(fdindexn) == -1) die_indexn(); /* NFS stupidity */
+  if (rename(fnifn.s,fnif.s) == -1)
+    strerr_die4x(111,FATAL,ERR_MOVE,fnifn.s,": ");
+  return r;
+}
+
+void transferenc()
+{
+       if (flagcd) {
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"Quoted-printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        } else
+          qmail_puts(&qq,"\n\n");
+}
+
+void getcharset()
+{
+    if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+      if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+        if (charset.s[charset.len - 1] == 'B' ||
+                       charset.s[charset.len - 1] == 'Q') {
+          flagcd = charset.s[charset.len - 1];
+          charset.s[charset.len - 2] = '\0';
+        }
+      }
+    } else
+      if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+
+    if (!stralloc_0(&charset)) die_nomem();
+}
+
 void main(argc,argv)
 int argc;
 char **argv;
 {
-  int fd;
-  char *dir;
+  unsigned long subs;
   int fdlock;
   char *sender;
+  char *mlheader = (char *) 0;
+  char *ret;
+  char *err;
   int flagmlwasthere;
+  int flagqmqp = 0;    /* don't use qmqp by default */
+  int flaglistid = 0;  /* no listid header added */
   int match;
-  int i;
-  char ch;
+  unsigned int i;
+  int r,fd;
   int flaginheader;
   int flagbadfield;
+  int flagbadpart;
+  int flagseenext;
+  int flagsubline;
+  int flagfromline;
+  int flagcontline;
+  int flagarchiveonly;
+  int flagtrailer;
+  unsigned int pos;
+  int opt;
+  char *cp, *cpstart, *cpafter;
 
   umask(022);
   sig_pipeignore();
 
-  dir = argv[1];
+  while ((opt = getopt(argc,argv,"cCh:H:lLrRqQs:S:vV")) != opteof)
+    switch(opt) {
+      case 'c': case 'C': break;       /* ignore for backwards compat */
+      case 'h':
+      case 'H': mlheader = optarg;     /* Alternative sublist check header */
+                mlheader[str_chr(mlheader,':')] = '\0';
+                break;
+      case 'l': flaglog = 1; break;
+      case 'L': flaglog = 0; break;
+      case 'r': flagnoreceived = 0; break;
+      case 'R': flagnoreceived = 1; break;
+      case 's':
+      case 'S':        pos = scan_ulong(optarg,&hash_lo);
+               if (!optarg[pos++]) break;
+               (void) scan_ulong(optarg+pos,&hash_hi);
+               if (hash_hi > 52L) hash_hi = 52L;
+               if (hash_lo > hash_hi) hash_lo = hash_hi;
+
+ break;
+      case 'q': flagqmqp = 0; break;
+      case 'Q': flagqmqp = 1; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-send version: ezmlm-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+
+  dir = argv[optind++];
   if (!dir) die_usage();
 
   sender = env_get("SENDER");
 
   if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
 
   fdlock = open_append("lock");
   if (fdlock == -1)
-    strerr_die4sys(111,FATAL,"unable to open ",dir,"/lock: ");
+    strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: ");
   if (lock_ex(fdlock) == -1)
-    strerr_die4sys(111,FATAL,"unable to obtain ",dir,"/lock: ");
-
-  if (qmail_open(&qq) == -1)
-    strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: ");
 
   flagarchived = getconf_line(&line,"archived",0,FATAL,dir);
+  flagindexed = getconf_line(&line,"indexed",0,FATAL,dir);
+  getcharset();
+  flagprefixed = getconf_line(&prefix,"prefix",0,FATAL,dir);
+  if (prefix.len) {            /* encoding and serial # support */
+                               /* no sanity checks - you put '\n' or '\0' */
+                               /* into the coded string, you pay */
+
+    decodeHDR(prefix.s,prefix.len,&line,charset.s,FATAL);
+    (void) unfoldHDR(line.s,line.len,&dcprefix,charset.s,&dummy,0,FATAL);
+    if (!stralloc_copy(&dcprefix,&line)) die_nomem();
+    serial = byte_rchr(prefix.s,prefix.len,'#');
+  }
+  if ((fd = open_read("text/trailer")) == -1) {        /* see if there is a trailer */
+    if (errno == error_noent) flagtrailer = 0;
+    else strerr_die2sys(111,ERR_OPEN,"text/trailer: ");
+  } else {
+    close(fd);
+    flagtrailer = 1;
+  }
 
-  getconf_line(&num,"num",1,FATAL,dir);
-  if (!stralloc_0(&num)) die_nomem();
-  scan_ulong(num.s,&msgnum);
-  ++msgnum;
+  getconf(&mimeremove,"mimeremove",0,FATAL,dir);
+
+  if (getconf_line(&line,"num",0,FATAL,dir)) { /* Now non-FATAL, def=0 */
+    if (!stralloc_0(&line)) die_nomem();
+    cp = line.s + scan_ulong(line.s,&msgnum);
+    ++msgnum;
+    if (*cp++ == ':')
+      scan_ulong(cp,&cumsize);
+  } else
+    msgnum = 1L;                       /* if num not there */
 
   getconf_line(&outhost,"outhost",1,FATAL,dir);
   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
-  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  set_cpoutlocal(&outlocal);
+  set_cpouthost(&outhost);
   flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir);
 
-  getconf(&headerremove,"headerremove",1,FATAL,dir);
-  constmap_init(&headerremovemap,headerremove.s,headerremove.len,0);
+  if (flagqmqp) {                      /* forward compatibility ;-) */
+    if (!stralloc_copys(&line,QMQPSERVERS)) die_nomem();
+    if (!stralloc_cats(&line,"/0")) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+    (void) getconf_line(&qmqpservers,line.s,0,FATAL,dir);
+  }
 
-  getconf(&headeradd,"headeradd",1,FATAL,dir);
-  for (i = 0;i < headeradd.len;++i)
-    if (!headeradd.s[i])
-      headeradd.s[i] = '\n';
+  getconf(&headerremove,"headerremove",1,FATAL,dir);
+  if (!constmap_init(&headerremovemap,headerremove.s,headerremove.len,0))
+       die_nomem();
 
   if (!stralloc_copys(&mydtline,"Delivered-To: mailing list ")) die_nomem();
   if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
@@ -197,123 +481,326 @@ char **argv;
 
   if (sender) {
     if (!*sender)
-      strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)");
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
     if (str_equal(sender,"#@[]"))
-      strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)");
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
     if (flagsublist)
       if (!sublistmatch(sender))
-        strerr_die2x(100,FATAL,"this message is not from my parent list (#5.7.2)");
+        strerr_die2x(100,FATAL,ERR_NOT_PARENT);
   }
+  innum = msgnum;                              /* innum = incoming */
+  outnum = msgnum;                             /* outnum = outgoing */
+  if (flagsublist && !flagarchived) {          /* msgnum = archive */
+    pos = byte_rchr(sublist.s,sublist.len,'@');        /* checked in sublistmatch */
+    if (str_start(sender+pos,"-return-"))
+      pos += 8;
+      pos += scan_ulong(sender+pos,&innum);
+      if (!flagarchived && innum && sender[pos] == '-')
+        outnum = innum;
+  }
+  szmsgnum[fmt_ulong(szmsgnum,outnum)] = '\0';
+  set_cpnum(szmsgnum);                         /* for copy */
 
   if (flagarchived) {
     if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
-    if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,msgnum / 100))) die_nomem();
+    if (!stralloc_catb(&fnadir,strnum,
+               fmt_ulong(strnum,outnum / 100))) die_nomem();
     if (!stralloc_copy(&fnaf,&fnadir)) die_nomem();
     if (!stralloc_cats(&fnaf,"/")) die_nomem();
-    if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,(unsigned int) (msgnum % 100),2))) die_nomem();
+    if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,
+               (unsigned int) (outnum % 100),2))) die_nomem();
     if (!stralloc_0(&fnadir)) die_nomem();
     if (!stralloc_0(&fnaf)) die_nomem();
 
     if (mkdir(fnadir.s,0755) == -1)
       if (errno != error_exist)
-       strerr_die4sys(111,FATAL,"unable to create ",fnadir.s,": ");
+       strerr_die4sys(111,FATAL,ERR_CREATE,fnadir.s,": ");
     fdarchive = open_trunc(fnaf.s);
     if (fdarchive == -1)
-      strerr_die4sys(111,FATAL,"unable to write ",fnaf.s,": ");
+      strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": ");
 
     substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf));
+                                               /* return-path to archive */
+    if (!stralloc_copys(&line,"Return-Path: <")) die_nomem();
+    if (sender) {                              /* same as qmail-local */
+      if (!quote2(&qline,sender)) die_nomem();
+      for (i = 0;i < qline.len;++i) if (qline.s[i] == '\n') qline.s[i] = '_';
+      if (!stralloc_cat(&line,&qline)) die_nomem();
+    }
+    if (!stralloc_cats(&line,">\n")) die_nomem();
+    if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive();
   }
 
+  if (flagqmqp) {
+    if (qmail_open(&qq,&qmqpservers) == -1)            /* open qmqp */
+      strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+  } else if (qmail_open(&qq,(stralloc *) 0) == -1)     /* open queue */
+      strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+
   if (!flagsublist) {
-    puts("Mailing-List: ");
-    put(mailinglist.s,mailinglist.len);
-    puts("\n");
+    getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+    qa_puts("Mailing-List: ");
+    qa_put(mailinglist.s,mailinglist.len);
+    if (getconf_line(&line,"listid",0,FATAL,dir)) {
+      flaglistid = 1;
+      qmail_puts(&qq,"\nList-ID: ");
+      qmail_put(&qq,line.s,line.len);
+    }
+    qa_puts("\n");
   }
-  put(headeradd.s,headeradd.len);
-  put(mydtline.s,mydtline.len);
+  copy(&qq,"headeradd",'H',FATAL);
+  qa_put(mydtline.s,mydtline.len);
 
   flagmlwasthere = 0;
   flaginheader = 1;
+  flagfoundokpart = 1;
   flagbadfield = 0;
-
+  flagbadpart = 0;
+  flagseenext = 0;
+  flagsubline = 0;
+  flagfromline = 0;
+  flagreceived = 0;
   for (;;) {
     if (getln(&ss0,&line,&match,'\n') == -1)
-      strerr_die2sys(111,FATAL,"unable to read input: ");
-
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
     if (flaginheader && match) {
-      if (line.len == 1)
+      if (line.len == 1) {             /* end of header */
        flaginheader = 0;
-      if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
-       flagbadfield = 0;
+        if (flagindexed)               /* std entry */
+          r = idx_copy_insertsubject();        /* all indexed lists */
+        if (flagprefixed && !flagsublist) {
+          qa_puts("Subject:");
+          if (!flagindexed) {          /* non-indexed prefixed lists */
+            concatHDR(subject.s,subject.len,&lines,FATAL);
+            decodeHDR(lines.s,lines.len,&qline,charset.s,FATAL);
+            r = unfoldHDR(qline.s,qline.len,&lines,
+                       charset.s,&dcprefix,1,FATAL);
+          }
+          if (!(r & 2)) {
+            qmail_puts(&qq," ");
+            if (serial == prefix.len)
+              qmail_put(&qq,prefix.s,prefix.len);
+            else {
+              qmail_put(&qq,prefix.s,serial);
+              qmail_puts(&qq,szmsgnum);
+              qmail_put(&qq,prefix.s+serial+1,prefix.len-serial-1);
+            }
+          }
+          qa_put(subject.s,subject.len);
+        }
+               /* do other stuff to do with post header processing here */
+       if (content.len) {              /* get MIME boundary, if exists */
+          concatHDR(content.s,content.len,&qline,FATAL);
+          if (!stralloc_copy(&content,&qline)) die_nomem();
+
+         if (flagtrailer &&            /* trailer only for some multipart */
+               case_startb(content.s,content.len,"multipart/"))
+           if (!case_startb(content.s+10,content.len-10,"mixed") &&
+               !case_startb(content.s+10,content.len-10,"digest") &&
+               !case_startb(content.s+10,content.len-10,"parallel"))
+             flagtrailer = 0;
+
+           cp = content.s;
+           cpafter = cp + content.len; /* check after each ';' */
+           while ((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
+             ++cp;
+             while (cp < cpafter &&
+                       (*cp == ' ' || *cp == '\t' || *cp == '\n')) ++cp;
+             if (case_startb(cp,cpafter-cp,"boundary=")) {
+               cp += 9;                        /* after boundary= */
+               if (*cp == '"') {       /* quoted boundary */
+                 ++cp;
+                 cpstart = cp;
+                 while (cp < cpafter && *cp != '"') ++cp;
+                if (cp == cpafter)
+                       strerr_die1x(100,ERR_MIME_QUOTE);
+               } else {                        /* non-quoted boundary */
+                 cpstart = cp;         /* find terminator of boundary */
+                 while (cp < cpafter && *cp != ';' &&
+                       *cp != ' ' && *cp != '\t' && *cp != '\n') ++cp;
+               }
+               if (!stralloc_copys(&boundary,"--")) die_nomem();
+               if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
+                       die_nomem();
+                flagfoundokpart = 0;
+               if (!constmap_init(&mimeremovemap,mimeremove.s,mimeremove.len,0))
+                       die_nomem();
+               flagbadpart = 1;                /* skip before first boundary */
+               qa_puts("\n");          /* to make up for the lost '\n' */
+            }
+          }
+        }
+      } else if ((*line.s != ' ') && (*line.s != '\t')) {
+        flagsubline = 0;
+        flagfromline = 0;
+        flagbadfield = 0;
+        flagarchiveonly = 0;
+        flagcontline = 0;
        if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
          flagbadfield = 1;
-       if (case_startb(line.s,line.len,"mailing-list:"))
-         flagmlwasthere = 1;
-       if (line.len == mydtline.len)
+        if ((flagnoreceived || !flagreceived) &&
+               case_startb(line.s,line.len,"Received:")) {
+            if (!flagreceived) {               /* get date from first rec'd */
+              flagreceived = 1;                        /* line (done by qmail) */
+              pos = byte_chr(line.s,line.len,';');
+              if (pos != line.len)             /* has '\n' */
+                if (!stralloc_copyb(&received,line.s+pos+2,line.len - pos - 3))
+                  die_nomem();
+            } else {                           /* suppress, but archive */
+              flagarchiveonly = 1;             /* but do not suppress the */
+              flagbadfield = 1;                        /* top one added by qmail */
+            }
+       } else if (case_startb(line.s,line.len,"Mailing-List:"))
+         flagmlwasthere = 1;           /* sublists always ok ezmlm masters */
+       else if (mlheader && case_startb(line.s,line.len,mlheader))
+         flagmlwasthere = 1;           /* mlheader treated as ML */
+        else if ((mimeremove.len || flagtrailer) &&    /* else no MIME need*/
+               case_startb(line.s,line.len,"Content-Type:")) {
+          if (!stralloc_copyb(&content,line.s+13,line.len-13)) die_nomem();
+          flagcontline = 1;
+       } else if (case_startb(line.s,line.len,"Subject:")) {
+          if (!stralloc_copyb(&subject,line.s+8,line.len-8)) die_nomem();
+         flagsubline = 1;
+          if (flagprefixed && !flagsublist)    /* don't prefix for sublists */
+           flagbadfield = 1;                   /* we'll print our own */
+        } else if (flagtrailer &&
+                case_startb(line.s,line.len,"Content-Transfer-Encoding:")) {
+          cp = line.s + 26;
+          cpafter = cp + line.len;
+          while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp;
+          if (case_startb(cp,cpafter-cp,"base64")) encin = 'B';
+          else if (case_startb(cp,cpafter-cp,"Quoted-Printable")) encin = 'Q';
+        } else if (flaglistid && case_startb(line.s,line.len,"list-id:"))
+         flagbadfield = 1;             /* suppress if we added our own */
+       else if (flagindexed) {
+
+          if (case_startb(line.s,line.len,"From:")) {
+            flagfromline = 1;
+            if (!stralloc_copyb(&from,line.s+5,line.len-5)) die_nomem();
+          }
+        } else if (line.len == mydtline.len)
          if (!byte_diff(line.s,line.len,mydtline.s))
-            strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)");
+            strerr_die2x(100,FATAL,ERR_LOOPING);
+      } else {                 /* continuation lines */
+        if (flagsubline) {
+         if (!stralloc_cat(&subject,&line)) die_nomem();
+        } else if (flagfromline) {
+         if (!stralloc_cat(&from,&line)) die_nomem();
+        } else if (flagcontline) {
+          if (!stralloc_cat(&content,&line)) die_nomem();
+        }
       }
-    }
-
-    if (!(flaginheader && flagbadfield))
-      put(line.s,line.len);
+    } else                             /* body */
+      msgsize += line.len;             /* always for tstdig support */
+
+    if (!(flaginheader && flagbadfield)) {
+      if (boundary.len && line.len > boundary.len &&
+               !str_diffn(line.s,boundary.s,boundary.len)) {
+        if (line.s[boundary.len] == '-' && line.s[boundary.len+1] == '-') {
+          flagbadpart = 0;             /* end boundary should be output */
+          if (flagtrailer) {
+            qmail_puts(&qq,"\n");
+            qmail_put(&qq,boundary.s,boundary.len);
+            qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+            qmail_puts(&qq,charset.s);
+            transferenc();             /* trailer for multipart message */
+           copy(&qq,"text/trailer",flagcd,FATAL);
+            if (flagcd == 'B') {       /* need to do our own flushing */
+              encodeB("",0,&qline,2,FATAL);
+              qmail_put(&qq,qline.s,qline.len);
+            }
+         }
+        } else {                       /* new part */
+            flagbadpart = 1;           /* skip lines */
+            if (!stralloc_copy(&lines,&line)) die_nomem();     /* but save */
+            flagseenext = 1;           /* need to check Cont-type */
+        }
+      } else if (flagseenext) {                /* last was boundary, now stored */
+        if (case_startb(line.s,line.len,"content-type:")) {
+          flagseenext = 0;             /* done thinking about it */
+          cp = line.s + 13;                    /* start of type */
+          while (*cp == ' ' || *cp == '\t') ++cp;
+          cpstart = cp;                        /* end of type */
+          while (*cp != '\n' && *cp != '\t' && *cp != ' ' && *cp != ';') ++cp;
+         if (constmap(&mimeremovemap,cpstart,cp-cpstart)) {
+            flagbadpart = 1;
+          } else {
+           flagfoundokpart = 1;
+            qa_put(lines.s,lines.len); /* saved lines */
+            flagbadpart = 0;           /* do this part */
+          }
+        } else if (line.len == 1) {    /* end of content desc */
+          flagbadpart = 0;             /* default type, so ok */
+          flagseenext = 0;             /* done thinking about it */
+        } else                         /* save line in cont desc */
+          if (!stralloc_cat(&lines,&line)) die_nomem();
+      }
+      if (!flagbadpart)
+        qa_put(line.s,line.len);
 
+    } else if (flagarchiveonly && flagarchived)        /* received headers */
+      if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive();
     if (!match)
       break;
   }
+  if (!boundary.len && flagtrailer) {
+    qmail_puts(&qq,"\n");              /* trailer for non-multipart message */
+    if (!encin || encin == 'Q') {      /* can add for QP, but not for base64 */
+      copy(&qq,"text/trailer",encin,FATAL);
+      qmail_puts(&qq,"\n");            /* no need to flush for plain/QP */
+    }
+  }
 
-  if (flagsublist)
-    if (!flagmlwasthere)
-      strerr_die2x(100,FATAL,"sublist messages must have Mailing-List (#5.7.2)");
-  if (!flagsublist)
+  cumsize += (msgsize + 128L) >> 8;    /* round to 256 byte 'records' */
+                                       /* check message tag */
+  if (flagsublist) {                   /* sublists need tag if selected/suppt*/
+    if (flaglog)
+      if ((ret = checktag(dir,innum,hash_lo+1L,"m",(char *) 0,hashout))) {
+        if (*ret) strerr_die2x(111,FATAL,ret);
+        else strerr_die2x(100,FATAL,ERR_NOT_PARENT);
+      }
+    if (!flagmlwasthere)               /* sublists need ML header */
+      strerr_die2x(100,FATAL,ERR_SUBLIST);
+  } else                               /* others are not allowed to have one */
     if (flagmlwasthere)
-      strerr_die2x(100,FATAL,"message already has Mailing-List (#5.7.2)");
+      strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+  if (!flagfoundokpart)                        /* all parts were on the strip list */
+      strerr_die2x(100,FATAL,ERR_BAD_ALL);
 
   if (flagarchived) {
     if (substdio_flush(&ssarchive) == -1) die_archive();
     if (fsync(fdarchive) == -1) die_archive();
-    if (fchmod(fdarchive,0744) == -1) die_archive();
+    if (fchmod(fdarchive,MODE_ARCHIVE | 0700) == -1) die_archive();
     if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */
   }
 
-  numwrite();
+  if (flaglog) {
+    tagmsg(dir,innum,sender,"m",hashout,qq.msgbytes,53L,FATAL);
+    hashout[COOKIE] = '\0';
+  }
 
+  numwrite();
   if (!stralloc_copy(&line,&outlocal)) die_nomem();
   if (!stralloc_cats(&line,"-return-")) die_nomem();
-  if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem();
+  if (!stralloc_cats(&line,szmsgnum)) die_nomem();
   if (!stralloc_cats(&line,"-@")) die_nomem();
   if (!stralloc_cat(&line,&outhost)) die_nomem();
   if (!stralloc_cats(&line,"-@[]")) die_nomem();
   if (!stralloc_0(&line)) die_nomem();
-
-  qmail_from(&qq,line.s);
-
-  for (i = 0;i < 53;++i) {
-    ch = 64 + i;
-    if (!stralloc_copys(&fnsub,"subscribers/")) die_nomem();
-    if (!stralloc_catb(&fnsub,&ch,1)) strerr_die2x(111,FATAL,"out of memory");
-    if (!stralloc_0(&fnsub)) strerr_die2x(111,FATAL,"out of memory");
-    fd = open_read(fnsub.s);
-    if (fd == -1) {
-      if (errno != error_noent)
-       strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": ");
-    }
-    else {
-      substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
-      substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf));
-      if (substdio_copy(&ssout,&ssin) != 0)
-       strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": ");
-      close(fd);
-    }
-  }
-
-  switch(qmail_close(&qq)) {
-    case 0:
+  qmail_from(&qq,line.s);                      /* envelope sender */
+  subs = putsubs(dir,hash_lo,hash_hi,subto,1,FATAL);   /* subscribers */
+  if (flagsublist) hash_lo++;
+
+  if (*(err = qmail_close(&qq)) == '\0') {
+      if (flaglog)                             /* mysql logging */
+       (void) logmsg(dir,outnum,hash_lo,subs,flagsublist ? 3 : 4);
+      closesql();
       strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
       strerr_die2x(0,"ezmlm-send: info: qp ",strnum);
-    default:
+  } else {
       --msgnum;
+      cumsize -= (msgsize + 128L) >> 8;
       numwrite();
-      strerr_die2x(111,FATAL,"temporary qmail-queue error");
+      strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
   }
 }
diff --git a/ezmlm-split.1 b/ezmlm-split.1
new file mode 100644 (file)
index 0000000..9c4e3e4
--- /dev/null
@@ -0,0 +1,171 @@
+.de Vb
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve
+.ft R
+
+.fi
+..
+.TH ezmlm-split 1
+.SH NAME
+ezmlm-split \- distribute (un)subscribe requests to sublists
+.SH SYNOPSIS
+.B ezmlm-split
+.I dir
+[
+.B \-dD
+][
+.I splitfile
+]
+.SH DESCRIPTION
+If the action is
+.I \-subscribe
+or
+.IR \-unsubscribe ,
+.B ezmlm-split
+analyzes the target of the request,
+ computing a hash in the range 0-52 from
+the address, and determines the ``domain key'', i.e. 
+the two top levels of the host address in reverse order.
+Thus, the domain entry for ``d@a.b.c'' becomes ``c.b''
+and the one for ``d@a'' becomes ``a''.
+
+The hash and domain parts are then tested against successive lines of
+.I splitfile
+(default is
+.IR dir\fB/split ).
+If a match is found, the request is forwarded to the corresponding
+sublist, and
+.B ezmlm-split
+exits 99. If a match is not found or action is not
+.I \-subscribe
+or
+.IR \-unsubscribe ,
+.B ezmlm-split
+exits 0.
+
+In
+.IR splitfile ,
+blank lines and lines with ``#'' in position 1 are ignored. Other lines are
+expected to be of the format ``dom:low:hi:list@host'', where ``dom'' is
+the top level domain, ``low''-``hi'' the range of the hash (defaults 0 and 52),
+and ``list@host'' the name of the corresponding list (default is the
+local list). A line is considered to match if the address hash is
+between ``low'' and ``hi'' inclusive and ``dom'' is empty,
+or if the ``domain key'' matches ``dom'' for the full length of ``dom''. Thus,
+the address ``user@aol.com'' would match ``com'' and ``aol.com'',
+but not ``host.com''.
+
+If the domain
+specified is the top level domain up to 3 characters, the split is identical
+as that used by the SQL subscriber interface. This is recommended.
+There can be several entries for a given sublist.
+
+.B ezmlm-split
+can be used also for list with SQL-based sublisting. In this case,
+addresses matching the
+.I splitfile
+are forwarded to the respective non-SQL sublist, and non-matching addresses
+are handed by the SQL sublist.
+
+.SH OPTIONS
+.TP
+.B \-d
+(Default.)
+Do. Forward requests to the appropriate list.
+.TP
+.B \-D
+Do not process messages. Rather, read addresses, one per line from stdin, and
+print ``sublist@host: address'' where ``sublist@host'' is the address to which
+the request would have been forwarded in normal operation. This is used to
+process a set of existing addresses into a set of address collections, one
+per sublist. The output can be sorted and easily processed into a set of files,
+one per sublist containing the addresses that sublist handles.
+.SH "SPLIT LIST SETUP"
+To use a hierarchy of sublists, set up the master list and add a
+.B ezmlm-split
+line before the
+.B ezmlm-manage(1)
+line in
+.IR dir\fB/manager .
+Create any number of sublists of the main list on other local or
+distant hosts. Ideally, these should be non-archived, to point to the correct
+message numbers of the main list archive (see
+.BR ezmlm-send(1) .
+You can use
+.B ezmlm-make -C\fIezmlmsubrc
+for this. If you don't, use the text files from the main list, except
+.IR bounce-bottom .
+Next, create
+.I split
+in the main list directory to achieve an appropriate split. Load splitting
+between several local hosts is best achieved by hash, with a blank domain.
+Geographical splitting with hosts in different countries is best done
+via ``domain'' and naturally, a large domain can be subdivided by hash.
+
+Subscribers will receive all messages 'From:' the main list. When they
+subscribe or unsubscribe, the request will be forwarded to the appropriate
+sublist, which will handle the confirmation. All information, except
+.I bounce-bottom
+refers the user to the main list. Thus, to the user the list appears as
+a single list with the address of the main list, and distribution among
+sublists is at the discretion of the administrator of the main list.
+
+.SH "ADDING/REMOVING SUBLISTS"
+In general, the main list should be disabled, while changing the sublist
+split. This can be done by changing the mode of
+.I dir\fB/lock
+to 0 or by setting the sticky bit for the home directory of the user
+controlling the list.
+
+To remove a sublist, edit the lines for that sublist in the splitfile to
+point to another list, and add the existing subscribers of the removed
+sublist to the sublist taking the load.
+When splitting a sublist into several sublists, create the new sublists,
+and edit the split file to distribute the load
+between them (usually by hash). Process the subscribers of the old list
+with:
+
+.Vb 1
+ | ezmlm-split -D dir | sort | program
+.Ve
+where to get one file of addresses per new sublist, ``program'' could be:
+
+.Vb 12
+\&#!/usr/bin/perl
+\&while (<>) {
+\&  ($f,$t) = split (':');
+\&  $t =~ s/^\ //;
+\&  if ($f ne $of) {
+\&    $of = $f;
+\&    close(OUT) if ($of);
+\&    open(OUT,">$f") or die("Unable to open $f");
+\&  }
+\&  print OUT $t;
+\&}
+\&close(OUT) if ($of);
+.Ve
+
+Remove all subscribers from the old list,
+and add the respective subscribers to each list.
+
+For any more drastic reorganizations, collect all the subscribes of the
+affected sublists, create the new subscriber lists, and update the
+subscribers of the affected lists.
+
+Reorganizations are easier done when lists use SQL support. By
+temporarily using SQL support, reorganizations can be done on running
+lists even when normally using
+.B ezmlm-split
+and local subscriber databases.
+.SH "SEE ALSO"
+ezmlm-list(1),
+ezmlm-make(1),
+ezmlm-manage(1),
+ezmlm-sub(1)
+ezmlm-unsub(1),
+ezmlm(5),
+ezmlmrc(5),
+ezmlmsubrc(5)
diff --git a/ezmlm-split.c b/ezmlm-split.c
new file mode 100644 (file)
index 0000000..f115358
--- /dev/null
@@ -0,0 +1,316 @@
+/*$Id: ezmlm-split.c,v 1.6 1999/05/12 22:17:54 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "sig.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "getln.h"
+#include "case.h"
+#include "qmail.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "quote.h"
+#include "now.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "errtxt.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-split: fatal: "
+#define INFO "ezmlm-split: info: "
+
+int flagdo = 1;                /* default is manager function */
+
+char *sender;
+char *split;
+stralloc outhost = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+stralloc target = {0};
+stralloc lctarget = {0};
+stralloc line = {0};
+stralloc domain = {0};
+stralloc name = {0};
+stralloc from = {0};
+stralloc to = {0};
+char strnum[FMT_ULONG];
+unsigned long lineno;
+int flagfound;
+
+void die_usage() {
+  strerr_die1x(100,"ezmlm-split: usage: ezmlm-split [-dD] dir [splitfile]"); }
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_badaddr()
+{
+  strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+
+void die_syntax()
+{
+  strnum[fmt_ulong(strnum,lineno)] = '\0';
+  strerr_die6x(111,FATAL,split," syntax error line ",strnum,": ",line.s);
+}
+
+char spbuf[1024];      /* should normally hold entire file */
+substdio sssp;
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return (int) len;
+}
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
+
+char outbuf[1];
+substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,(int) sizeof(outbuf));
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+
+int findname()
+/* returns 1 if a matching line was found, 0 otherwise. name will contain */
+/* the correct list address in either case */
+{
+  char *cpat,*cp,*cpname,*cp1,*cp2,*cplast;
+  unsigned long u;
+  uint32 h;
+  unsigned char hash,hash_hi,hash_lo;
+  unsigned int pos,pos_name,pos_hi;
+  char ch;
+  int fd,match;
+
+  /* make case insensitive hash */
+  flagfound = 0;                       /* default */
+  cpname = "";                         /* default */
+  if (!stralloc_copy(&lctarget,&target)) die_nomem();
+  case_lowerb(lctarget.s,lctarget.len -1);
+  h = 5381;
+  cp = lctarget.s;
+  while ((ch = *cp++)) {
+    h = (h + (h << 5)) ^ (uint32) ch;
+  }
+  hash = (h % 53);
+
+  /* make domain pointer */
+  cpat = lctarget.s + str_chr(lctarget.s,'@');
+  if (!*cpat)
+    strerr_die4x(100,FATAL,ERR_ADDR_AT,": ",target.s);
+  cplast = cpat + str_len(cpat) - 1;
+  if (*cplast == '.') --cplast;                /* annonying special case */
+  cp1 = cpat + byte_rchr(cpat,cplast - cpat, '.');
+  if (cp1 != cplast) {                 /* got one '.' */
+    if (!stralloc_copyb(&domain,cp1 + 1, cplast - cp1)) die_nomem();
+    cp2 = cpat + byte_rchr(cpat, cp1 - cpat,'.');
+    if (cp2 == cp1) cp2 = cpat;
+    ++cp2;
+    if (!stralloc_append(&domain,".")) die_nomem();
+    if (!stralloc_catb(&domain,cp2, cp1 - cp2)) die_nomem();
+  } else                               /* no '.' */
+    if (!stralloc_copyb(&domain,cpat + 1,cplast - cpat)) die_nomem();
+  if (!stralloc_0(&domain)) die_nomem();
+
+  if ((fd = open_read(split)) == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,split,": ");
+  substdio_fdbuf(&sssp,read,fd,spbuf,(int) sizeof(spbuf));
+  lineno = 0;
+  for (;;) {   /* dom:hash_lo:hash_hi:listaddress */
+    if (getln(&sssp,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,split,": ");
+     lineno++;
+    if (!match)
+      break;
+    if (line.s[0] == '#') continue;    /* comment */
+    line.s[line.len - 1] = '\0';       /* no need to allow \0 in lines */
+    if (!line.s[pos = str_chr(line.s,':')])
+      continue;                                /* usually blank line */
+    line.s[pos] = '\0';
+    if (pos == 0 ||                    /* no domain */
+         (case_starts(domain.s,line.s))) {     /* or matching domain */
+       if (!line.s[++pos]) die_syntax();
+       pos_hi = pos + str_chr(line.s + pos,':');
+       if (!line.s[pos_hi]) die_syntax();
+       pos_hi++;
+       (void) scan_ulong(line.s + pos, &u);    /* scan_uint() not in ezmlm */
+       hash_lo = (unsigned char) u;
+       (void) scan_ulong(line.s + pos_hi, &u);
+       hash_hi = (unsigned char) u;
+       pos_name = pos_hi + str_chr(line.s + pos_hi,':');
+       if (pos_hi == pos_name) hash_hi = 52L;  /* default hi = 52 */
+       if (line.s[pos_name]) pos_name++;
+       if (hash > hash_hi || hash < hash_lo) continue; /* not us */
+       cpname = line.s + pos_name;
+       while (*cpname &&                               /* isolate name */
+           (*cpname == ' ' || *cpname == '\t')) cpname++;
+       pos = line.len - 2;
+       while (pos && (line.s[pos] == '\n' || line.s[pos] == ' ' ||
+               line.s[pos] == '\t')) line.s[pos--] = '\0';
+       break;
+    }
+  }
+  close(fd);
+
+  if (*cpname) {
+    if (!stralloc_copys(&name,cpname)) die_nomem();
+    if (byte_chr(name.s,name.len,'@') == name.len) {   /* local sublist */
+      if (!stralloc_append(&name,"@")) die_nomem();
+      if (!stralloc_cat(&name,&outhost)) die_nomem();
+    }
+    if (!stralloc_0(&name)) die_nomem();
+    return 1;
+  } else {                     /* match without name or no match =>this list */
+    if (!stralloc_copy(&name,&outlocal)) die_nomem();
+    if (!stralloc_append(&name,"@")) die_nomem();
+    if (!stralloc_cat(&name,&outhost)) die_nomem();
+    if (!stralloc_0(&name)) die_nomem();
+    return 0;
+  }
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *local;
+  char *action;
+  char *def;
+  char *dtline;
+  char *nhost;
+  char *err;
+  unsigned int i;
+  int match;
+  int optind = 1;
+
+  sig_pipeignore();
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+  if (dir[0] == '-') {
+    if (dir[1] == 'd') flagdo = 1;
+    else if (dir[1] == 'D') flagdo = 0;
+    else die_usage();
+    if (!(dir = argv[optind++])) die_usage();
+  }
+  if (!(split = argv[optind]))
+    split = "split";
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+
+  if (flagdo) {
+    sender = env_get("SENDER");
+    if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
+    if (!*sender)
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
+    if (!sender[str_chr(sender,'@')])
+      strerr_die2x(100,FATAL,ERR_ANONYMOUS);
+    if (str_equal(sender,"#@[]"))
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
+
+    def = env_get("DEFAULT");
+    if (def) {
+      action = def;
+    } else {
+      local = env_get("LOCAL");
+      if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+      getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+      if (inlocal.len > str_len(local)) die_badaddr();
+      if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+      action = local + inlocal.len + 1;
+    }
+    if (!stralloc_copys(&target,sender)) die_nomem();
+    if (action[0]) {
+      i = str_chr(action,'-');
+      if (action[i]) {
+        action[i] = '\0';
+        if (!stralloc_copys(&target,action + i + 1)) die_nomem();
+        i = byte_rchr(target.s,target.len,'=');
+        if (i < target.len)
+         target.s[i] = '@';
+      }
+    }
+    if (!stralloc_0(&target)) die_nomem();
+
+    if (case_diffs(action,ACTION_SUBSCRIBE) &&
+      case_diffs(action,ALT_SUBSCRIBE) &&
+      case_diffs(action,ACTION_UNSUBSCRIBE) &&
+      case_diffs(action,ALT_UNSUBSCRIBE))
+    _exit(0);                  /* not for us */
+
+    if (findname()) {
+                               /* new sender */
+      if (!stralloc_copy(&from,&outlocal)) die_nomem();
+      if (!stralloc_cats(&from,"-return-@")) die_nomem();
+      if (!stralloc_cat(&from,&outhost)) die_nomem();
+      if (!stralloc_0(&from)) die_nomem();
+      if (name.s[i = str_rchr(name.s,'@')]) {          /* name must have '@'*/
+       nhost = name.s + i;
+       *(nhost++) = '\0';
+      }
+      if (!stralloc_copys(&to,name.s)) die_nomem();    /* local */
+      if (!stralloc_append(&to,"-")) die_nomem();      /* - */
+      if (!stralloc_cats(&to,action)) die_nomem();     /* subscribe */
+      if (!stralloc_append(&to,"-")) die_nomem();      /* - */
+      if (target.s[i = str_rchr(target.s,'@')])
+       target.s[i] = '=';
+      if (!stralloc_cats(&to,target.s)) die_nomem();   /* target */
+      if (!stralloc_append(&to,"@")) die_nomem();      /* - */
+      if (!stralloc_cats(&to,nhost)) die_nomem();      /* host */
+      if (!stralloc_0(&to)) die_nomem();
+      dtline = env_get("DTLINE");
+      if (!dtline) strerr_die2x(100,FATAL,ERR_NODTLINE);
+
+      if (qmail_open(&qq,(stralloc *) 0) == -1)
+        strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+      qmail_puts(&qq,dtline);                          /* delivered-to */
+      if (substdio_copy(&ssqq,&ssin) != 0)
+        strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      qmail_from(&qq,from.s);
+      qmail_to(&qq,to.s);
+
+      if (*(err = qmail_close(&qq)) != '\0')
+        strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
+
+      strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+      strerr_die3x(99,INFO,"qp ",strnum);
+    }
+    _exit(0);
+  } else {
+
+    for (;;) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+         strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!match) break;
+      if (line.len == 1) continue;     /* ignore blank lines */
+      if (line.s[0] == '#') continue;  /* ignore comments */
+      if (!stralloc_copy(&target,&line)) die_nomem();
+      target.s[target.len - 1] = '\0';
+      (void) findname();
+      if (!stralloc_cats(&name,": ")) die_nomem();
+      if (!stralloc_cats(&name,target.s)) die_nomem();
+      if (!stralloc_append(&name,"\n")) die_nomem();
+      if (substdio_put(&ssout,name.s,name.len) == -1)
+       strerr_die2sys(111,ERR_WRITE,"output: ");
+    }
+    if (substdio_flush(&ssout) == -1)
+      strerr_die2sys(111,ERR_FLUSH,"output: ");
+    _exit(0);
+  }
+}
+
+
diff --git a/ezmlm-store.1 b/ezmlm-store.1
new file mode 100644 (file)
index 0000000..1350577
--- /dev/null
@@ -0,0 +1,210 @@
+.TH ezmlm-store 1
+.SH NAME
+ezmlm-store \- store messages posted to a moderated lists and send
+moderation requests to the moderator(s).
+.SH SYNOPSIS
+.B ezmlm-store [-bBcCmMpPrRsSvV]
+.I dir
+.SH DESCRIPTION
+.B ezmlm-store
+reads a mail message and
+stores it in
+.I dir\fB/mod/pending/.
+.B ezmlm-store
+also sends a moderation request with a copy of the message to the list of
+moderators stored in
+.I dir\fB/mod/subscribers/.
+If
+.I dir\fB/modpost
+contains a directory name,
+.IR moddir ,
+with a leading forward slash, the moderator list in
+.I /moddir/subscribers/
+is used instead. This allows separate sets of moderators for
+subscription and message moderation, as
+well as the use of the same moderator list for several
+.B ezmlm
+mailing lists, owned by the same user. If the
+.B \-s
+switch is used, the moderators are divided into two sets. Those pointed to by
+.I dir\fB/modpost
+which will receive confirmation requests for posts with their SENDER address,
+and those stored with the base directory
+.I dir\fB/mod/
+which will receive moderation requests for messages posted from SENDERs not
+part of the first group. This can be used to have a set of trusted
+users (``moderating'' themselves to thwart SENDER faking), and a set of more
+traditional moderators, which receive approval requests for other posts.
+
+.B Note:
+The contents of
+.I dir\fB/modpost
+do not affect the location of the message moderation queue.
+
+If SENDER is a moderator, the request is sent
+to that moderator only.
+
+The moderation request includes cookies for message rejection and acceptance.
+The moderator can take either action by replying to the appropriate address.
+For moderator convenience, the
+.I accept
+address is given in
+the ``Reply-To:'' header, and the
+.I reject
+address in the ``From:'' header.
+
+If the list is set up with remote administration and the sender is not a
+moderator, the ``Cc:'' header will contain a confirmation address to add
+the sender to the ``allow'' database. This allows the moderator to both
+approve the post and add the sender to the list of accepted posters by
+replying to ``all''.
+
+The (default) subject of all message moderation requests is
+.B MODERATE for\fI listname@host.
+
+Once the request has been successfully sent to the moderators, 
+.B ezmlm-store
+sets the owner-execute bit of the message in
+.I dir\fB/mod/pending/.
+Messages without this bit set are the result of incomplete executions of
+.B ezmlm-store
+and are ignored.
+
+At the beginning of the message,
+.B ezmlm-store
+prints a new
+.B Mailing-List
+field with the contents of
+.IR dir\fB/mailinglist .
+It rejects the message if there is already a
+.B Mailing-List
+field.
+
+.B ezmlm-store
+does not distribute bounce messages:
+if the environment variable
+.B SENDER
+is set, and is either empty or
+.BR #@[] ,
+.B ezmlm-store
+rejects the message.
+.SH OPTIONS
+.TP
+.B \-b
+(Default.)
+With the moderation request,
+send both headers and body of the message to be moderated.
+.TP
+.B \-B
+With the moderation request,
+send header, but not body of the message to be moderated.
+This may be useful if moderation is used to allow one or a few poorly
+connected addresses to post, and posts are very large (e.g. when the list
+is used as a distribution channel of e.g. catalogs).
+.TP
+.B \-cCrR
+Passed on to
+.B ezmlm-send(1)
+in case this program is executed. This happens only if
+.I dir\fB/modpost
+does not exist.
+.TP
+.B \-m
+(Default.)
+The post is sent as a MIME enclosure.
+.TP
+.B \-M
+The post is appended to the message.
+.TP
+.B \-s
+The contents of
+.I dir\fB/modpost
+with the default
+.I dir\fB/mod/
+are used as the base directory for self-approving users, whereas
+.I dir\fB/mod/
+is used as the base directory
+for the moderators to whom approval requests for other
+posts are sent.
+.TP
+.B \-S
+(Default.)
+The contents of
+.I dir\fB/modpost
+with the default
+.I dir\fB/mod/
+are used as the base directory
+for the moderators for all moderator functions.
+.TP
+.B \-p
+(Default.)
+Anyone can post. Posts that are not from a moderator are sent out to
+all moderators for approval. Posts that are from a moderator are
+sent only to that moderator.
+.TP
+.B \-P
+Only moderators can post. Those posts are sent to the posting moderator
+for approval. Posts from other addresses are bounced. This can be used
+to relatively securely restrict posts to messages originating from
+a given set of addresses.
+.TP
+.B \-v
+Display
+.B ezmlm-manage
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-manage
+version information.
+.SH USAGE
+First of all, remember that
+.B ezmlm-store
+will simply execute
+.B ezmlm-send(1)
+unless
+.I dir\fB/modpost
+exists.
+For the switches, use
+.B \-P
+for lists where only moderators can post securely, i.e. they are
+asked to approve their own posts after posting,
+whereas other SENDERs are rejected. For
+low-security more convenient versions of this type of restriction, see
+.BR ezmlm-gate(1) .
+
+Normally (no switches),
+posts from moderators are sent for approval only to the sending
+moderator, whereas posts from other addresses are sent to all moderators.
+Thus, you can set up an announcement list by adding all potential posters
+as moderators. However, you may also want to allow others to post, as
+long as the posts are approved. This would be default
+.B ezmlm-store
+function.
+
+To make the set of ``approved posters'' who approve their own posts, different
+from the set of addresses that approve posts from other users, use the
+.B \-s
+switch. Add a directory name to
+.IR dir\fB/modpost .
+This directory is the base directory of the ``approved posters''
+database. Add the moderators for other posts to
+.I dir\fB/mod/
+using
+.BR ezmlm-sub(1) .
+.SH "CHARACTER SETS"
+If
+.I dir\fB/charset
+exists,
+.B ezmlm-store
+will use the character set listed for all messages. Otherwise, the
+default ``us-ascii'' will be used. The character set can be suffixed
+by ``:'' followed by a code. If the code is ``Q'', outgoing messages are 
+sent as ``Quoted-Printable'', if it is '`B'' they are sent ``base64'' encoded.
+Otherwise, text is sent as is.
+.SH "SEE ALSO"
+ezmlm-clean(1),
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-moderate(1),
+ezmlm(5)
diff --git a/ezmlm-store.c b/ezmlm-store.c
new file mode 100644 (file)
index 0000000..d293cfa
--- /dev/null
@@ -0,0 +1,506 @@
+/*$Id: ezmlm-store.c,v 1.52 1999/10/09 16:49:56 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "stralloc.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "error.h"
+#include "qmail.h"
+#include "env.h"
+#include "lock.h"
+#include "sig.h"
+#include "open.h"
+#include "getln.h"
+#include "str.h"
+#include "fmt.h"
+#include "readwrite.h"
+#include "auto_bin.h"
+#include "fork.h"
+#include "wait.h"
+#include "exit.h"
+#include "substdio.h"
+#include "getconf.h"
+#include "datetime.h"
+#include "now.h"
+#include "date822fmt.h"
+#include "cookie.h"
+#include "sgetopt.h"
+#include "errtxt.h"
+#include "idx.h"
+#include "copy.h"
+#include "subscribe.h"
+#include "mime.h"
+
+int flagmime = MOD_MIME;       /* default is message as attachment */
+int flagpublic = 1;            /* default anyone can post */
+                               /* =0 for only moderators can */
+int flagself = 0;              /* `modpost` mods approve own posts */
+                               /* but mod/ is used for moderators */
+                               /* of other posts. Def=no=0 */
+char flagcd = '\0';            /* default: don't use quoted-printable */
+int flagbody = 1;              /* body of message enclosed with mod request */
+                               /* 0 => headers only */
+
+#define FATAL "ezmlm-store: fatal: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-store: usage: ezmlm-store [-cCmMpPrRsSvV] dir");
+}
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+stralloc fnmsg = {0};
+
+void die_msg() { strerr_die4sys(111,FATAL,ERR_WRITE,fnmsg.s,": "); }
+
+int fdmsg;
+int fdmod;
+int pid;
+int match;
+
+char strnum[FMT_ULONG];
+char date[DATE822FMT];
+char hash[COOKIE];
+char boundary[COOKIE];
+datetime_sec when;
+struct datetime dt;
+struct stat st;
+
+void *psql = (void *) 0;
+
+stralloc fnbase = {0};
+stralloc line = {0};
+stralloc mailinglist = {0};
+stralloc outlocal = {0};
+stralloc outhost = {0};
+stralloc mydtline = {0};
+stralloc returnpath = {0};
+stralloc accept = {0};
+stralloc action = {0};
+stralloc reject = {0};
+stralloc quoted = {0};
+stralloc key = {0};
+stralloc subject = {0};
+stralloc moderators = {0};
+stralloc charset = {0};
+stralloc sendopt = {0};
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+
+int subto(s,l)
+char *s;
+unsigned int l;
+{
+  qmail_put(&qq,"T",1);
+  qmail_put(&qq,s,l);
+  qmail_put(&qq,"",1);
+  return (int) l;
+}
+
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+
+substdio ssin;
+char inbuf[1024];
+
+substdio ssmsg;
+char msgbuf[1024];
+
+substdio sstext;
+char textbuf[512];
+
+substdio sssub;
+char subbuf[512];
+
+void transferenc()
+{
+       if (flagcd) {
+         qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+          if (flagcd == 'Q')
+            qmail_puts(&qq,"Quoted-Printable\n\n");
+          else
+           qmail_puts(&qq,"base64\n\n");
+        } else
+          qmail_puts(&qq,"\n\n");
+}
+
+void makehash(act)
+stralloc *act;                                 /* has to be 0-terminated  */
+/* act is expected to be -reject-ddddd.ttttt or -accept-ddddd.ttttt        */
+/* The routine will add .hash@outhost to act. act will NOT be 0-terminated */
+{
+  int d;
+
+  d = 2 + str_chr(act->s + 1,'-');
+  cookie(hash,key.s,key.len,act->s + d,"","a");
+  *(act->s + act->len - 1) = '.';      /* we put a '.' Bad, but works */
+  if (!stralloc_catb(act,hash,COOKIE)) die_nomem();
+  if (!stralloc_cats(act,"@")) die_nomem();
+  if (!stralloc_cat(act,&outhost)) die_nomem();
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  int fdlock;
+  char *sender;
+  int match;
+  int flaginheader;
+  int flagmodpost;
+  int flagremote;
+  char *pmod;
+  char *err;
+  int opt;
+  unsigned int i;
+  char szchar[2] = "-";
+  char *sendargs[4];
+  int child,wstat;
+
+  (void) umask(022);
+  sig_pipeignore();
+
+  if (!stralloc_copys(&sendopt," -")) die_nomem();
+  while ((opt = getopt(argc,argv,"bBcCmMpPrRsSvV")) != opteof)
+    switch(opt) {
+      case 'b': flagbody = 1; break;
+      case 'B': flagbody = 0; break;
+      case 'm': flagmime = 1; break;
+      case 'M': flagmime = 0; break;
+      case 'p': flagpublic = 1; break; /* anyone can post (still moderated)*/
+      case 'P': flagpublic = 0; break; /* only moderators can post */
+      case 's': flagself = 1; break;   /* modpost and DIR/mod diff fxns */
+      case 'S': flagself = 0; break;   /* same fxn */
+      case 'c':                                /* ezmlm-send flags */
+      case 'C':
+      case 'r':
+      case 'R':
+        szchar[0] = (char) opt & 0xff;
+        if (!stralloc_append(&sendopt,szchar)) die_nomem();
+        break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-store version: ",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+  sender = env_get("SENDER");
+
+  if (sender) {
+    if (!*sender || str_equal(sender,"#@[]"))
+      strerr_die2x(100,FATAL,ERR_BOUNCE);
+  }
+
+  dir = argv[optind];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  flagmodpost = getconf_line(&moderators,"modpost",0,FATAL,dir);
+  flagremote = getconf_line(&line,"remote",0,FATAL,dir);
+  if (!flagmodpost) {                  /* not msg-mod. Pipe to ezmlm-send */
+    sendargs[0] = "/bin/sh";
+    sendargs[1] = "-c";
+    if (!stralloc_copys(&line,auto_bin)) die_nomem();
+    if (!stralloc_cats(&line,"/ezmlm-send")) die_nomem();
+    if (sendopt.len > 2)
+      if (!stralloc_cat(&line,&sendopt)) die_nomem();
+    if (!stralloc_cats(&line," '")) die_nomem();
+    if (!stralloc_cats(&line,dir)) die_nomem();
+    if (!stralloc_cats(&line,"'")) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+    sendargs[2] = line.s;
+    sendargs[3] = 0;
+    switch(child = fork()) {
+      case -1:
+        strerr_die2sys(111,FATAL,ERR_FORK);
+      case 0:
+        execvp(*sendargs,sendargs);
+        if (errno == error_txtbsy || errno == error_nomem ||
+            errno == error_io)
+          strerr_die5sys(111,FATAL,ERR_EXECUTE,"/bin/sh -c ",sendargs[2],": ");
+        else
+          strerr_die5sys(100,FATAL,ERR_EXECUTE,"/bin/sh -c ",sendargs[2],": ");
+    }
+         /* parent */
+    wait_pid(&wstat,child);
+    if (wait_crashed(wstat))
+      strerr_die2x(111,FATAL,ERR_CHILD_CRASHED);
+    switch(wait_exitcode(wstat)) {
+      case 100:
+        strerr_die2x(100,FATAL,"Fatal error from child");
+      case 111:
+         strerr_die2x(111,FATAL,"Temporary error from child");
+      case 0:
+        _exit(0);
+      default:
+        strerr_die2x(111,FATAL,"Unknown temporary error from child");
+    }
+  }
+
+  if (!moderators.len || !(moderators.s[0] == '/')) {
+    if (!stralloc_copys(&moderators,dir)) die_nomem();
+    if (!stralloc_cats(&moderators,"/mod")) die_nomem();
+  }
+  if (!stralloc_0(&moderators)) die_nomem();
+
+  if (sender) {
+      pmod = issub(moderators.s,sender,(char *) 0,FATAL);
+      closesql();
+                               /* sender = moderator? */
+  } else
+    pmod = 0;
+
+  if (!pmod && !flagpublic)
+    strerr_die2x(100,FATAL,ERR_NO_POST);
+
+  switch(slurp("key",&key,32)) {
+    case -1:
+      strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
+    case 0:
+      strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
+  }
+
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+
+  fdlock = open_append("mod/lock");
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/mod/lock: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/mod/lock: ");
+
+  if (!stralloc_copys(&mydtline,"Delivered-To: moderator for ")) die_nomem();
+  if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
+  if (!stralloc_append(&mydtline,"@")) die_nomem();
+  if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+
+  if (!stralloc_copys(&returnpath,"Return-Path: <")) die_nomem();
+  if (sender) {
+    if (!stralloc_cats(&returnpath,sender)) die_nomem();
+    for (i = 14; i < returnpath.len;++i)
+      if (returnpath.s[i] == '\n' || !returnpath.s[i] )
+        returnpath.s[i] = '_';
+               /* NUL and '\n' are bad, but we don't quote since this is */
+               /* only for ezmlm-moderate, NOT for SMTP */
+  }
+  if (!stralloc_cats(&returnpath,">\n")) die_nomem();
+
+ pid = getpid();               /* unique file name */
+ for (i = 0;;++i)              /* got lock - nobody else can add files */
+  {
+   when = now();               /* when is also used later for date! */
+   if (!stralloc_copys(&fnmsg,"mod/pending/")) die_nomem();
+   if (!stralloc_copyb(&fnbase,strnum,fmt_ulong(strnum,when))) die_nomem();
+   if (!stralloc_append(&fnbase,".")) die_nomem();
+   if (!stralloc_catb(&fnbase,strnum,fmt_ulong(strnum,pid))) die_nomem();
+   if (!stralloc_cat(&fnmsg,&fnbase)) die_nomem();
+   if (!stralloc_0(&fnmsg)) die_nomem();
+   if (stat(fnmsg.s,&st) == -1) if (errno == error_noent) break;
+   /* really should never get to this point */
+   if (i == 2)
+     strerr_die2x(111,FATAL,ERR_UNIQUE);
+   sleep(2);
+  }
+
+  if (!stralloc_copys(&action,"-")) die_nomem();
+  if (!stralloc_cats(&action,ACTION_REJECT)) die_nomem();
+  if (!stralloc_cat(&action,&fnbase)) die_nomem();
+  if (!stralloc_0(&action)) die_nomem();
+  makehash(&action);
+  if (!quote(&quoted,&outlocal)) die_nomem();
+  if (!stralloc_copy(&reject,&quoted)) die_nomem();
+  if (!stralloc_cat(&reject,&action)) die_nomem();
+  if (!stralloc_0(&reject)) die_nomem();
+
+  if (!stralloc_copys(&action,"-")) die_nomem();
+  if (!stralloc_cats(&action,ACTION_ACCEPT)) die_nomem();
+  if (!stralloc_cat(&action,&fnbase)) die_nomem();
+  if (!stralloc_0(&action)) die_nomem();
+  makehash(&action);
+  if (!stralloc_copy(&accept,&quoted)) die_nomem();
+  if (!stralloc_cat(&accept,&action)) die_nomem();
+  if (!stralloc_0(&accept)) die_nomem();
+
+  set_cpoutlocal(&outlocal);
+  set_cpouthost(&outhost);
+  set_cptarget(accept.s);      /* for copy () */
+  set_cpconfirm(reject.s);
+
+  fdmsg = open_trunc(fnmsg.s);
+  if (fdmsg == -1)
+    strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnmsg.s,": ");
+  substdio_fdbuf(&ssmsg,write,fdmsg,msgbuf,sizeof(msgbuf));
+
+  if (qmail_open(&qq, (stralloc *) 0) == -1)           /* Open mailer */
+    strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+
+  qmail_puts(&qq,"Mailing-List: ");
+  qmail_put(&qq,mailinglist.s,mailinglist.len);
+  if (getconf_line(&line,"listid",0,FATAL,dir)) {
+    qmail_puts(&qq,"List-ID: ");
+    qmail_put(&qq,line.s,line.len);
+  }
+  qmail_puts(&qq,"\nDate: ");
+  datetime_tai(&dt,when);
+  qmail_put(&qq,date,date822fmt(date,&dt));
+  qmail_puts(&qq,"Message-ID: <");
+  if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
+     die_nomem();
+  if (!stralloc_append(&line,".")) die_nomem();
+  if (!stralloc_catb(&line,strnum,
+               fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  qmail_puts(&qq,line.s);
+               /* "unique" MIME boundary as hash of messageid */
+  cookie(boundary,"",0,"",line.s,"");
+  qmail_puts(&qq,">\nFrom: ");
+  qmail_puts(&qq,reject.s);
+  qmail_puts(&qq,"\nReply-To: ");
+  qmail_puts(&qq,accept.s);
+  if (!pmod && flagremote) {   /* if remote admin add -allow- address */
+    qmail_puts(&qq,"\nCc: ");  /* for ezmlm-gate users */
+    strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
+    cookie(hash,key.s,key.len-FLD_ALLOW,strnum,sender,"t");
+    if (!stralloc_copy(&line,&outlocal)) die_nomem();
+    if (!stralloc_cats(&line,"-allow-tc.")) die_nomem();
+    if (!stralloc_cats(&line,strnum)) die_nomem();
+    if (!stralloc_append(&line,".")) die_nomem();
+    if (!stralloc_catb(&line,hash,COOKIE)) die_nomem();
+    if (!stralloc_append(&line,"-")) die_nomem();
+    i = str_rchr(sender,'@');
+    if (!stralloc_catb(&line,sender,i)) die_nomem();
+    if (sender[i]) {
+      if (!stralloc_append(&line,"=")) die_nomem();
+      if (!stralloc_cats(&line,sender + i + 1)) die_nomem();
+    }
+    qmail_put(&qq,line.s,line.len);
+    qmail_puts(&qq,"@");
+    qmail_put(&qq,outhost.s,outhost.len);
+  }
+  qmail_puts(&qq,"\nTo: Recipient list not shown: ;");
+  if (!stralloc_copys(&subject,"\nSubject: ")) die_nomem();
+  if (!stralloc_cats(&subject,TXT_MODERATE)) die_nomem();
+  if (!quote(&quoted,&outlocal)) die_nomem();
+  if (!stralloc_cat(&subject,&quoted)) die_nomem();
+  if (!stralloc_append(&subject,"@")) die_nomem();
+  if (!stralloc_cat(&subject,&outhost)) die_nomem();
+  if (flagmime) {
+    if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+      if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+        if (charset.s[charset.len - 1] == 'B' ||
+               charset.s[charset.len - 1] == 'Q') {
+          flagcd = charset.s[charset.len - 1];
+          charset.s[charset.len - 2] = '\0';
+        }
+      }
+    } else
+      if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+    if (!stralloc_0(&charset)) die_nomem();
+    qmail_puts(&qq,"\nMIME-Version: 1.0\n");
+    qmail_puts(&qq,"Content-Type: multipart/mixed;\n\tboundary=");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_put(&qq,subject.s,subject.len);
+    qmail_puts(&qq,"\n\n--");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+    qmail_puts(&qq,charset.s);
+    transferenc();
+  } else {
+    qmail_put(&qq,subject.s,subject.len);
+    qmail_puts(&qq,"\n\n");
+  }
+  copy(&qq,"text/mod-request",flagcd,FATAL);
+  if (flagcd == 'B') {
+    encodeB("",0,&line,2,FATAL);
+    qmail_put(&qq,line.s,line.len);
+  }
+  if (substdio_put(&ssmsg,returnpath.s,returnpath.len) == -1) die_msg();
+  if (substdio_put(&ssmsg,mydtline.s,mydtline.len) == -1) die_msg();
+  substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+  if (flagmime) {
+    qmail_puts(&qq,"\n--");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n");
+  }
+
+  qmail_put(&qq,returnpath.s,returnpath.len);
+  qmail_put(&qq,mydtline.s,mydtline.len);
+  flaginheader = 1;
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+    if (!match) break;
+    if (line.len == 1) flaginheader = 0;
+    if (flaginheader) {
+      if ((line.len == mydtline.len) &&
+               !byte_diff(line.s,line.len,mydtline.s)) {
+       close(fdmsg);                   /* be nice - clean up */
+       unlink(fnmsg.s);
+        strerr_die2x(100,FATAL,ERR_LOOPING);
+      }
+      if (case_startb(line.s,line.len,"mailing-list:")) {
+       close(fdmsg);                   /* be nice - clean up */
+       unlink(fnmsg.s);
+        strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+      }
+    }
+
+    if (flagbody || flaginheader)      /* skip body if !flagbody */
+      qmail_put(&qq,line.s,line.len);
+    if (substdio_put(&ssmsg,line.s,line.len) == -1) die_msg();
+  }
+
+  if (flagmime) {
+    qmail_puts(&qq,"\n--");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_puts(&qq,"--\n");
+  }
+
+/* close archive before qmail. Loss of qmail will result in re-run, and   */
+/* worst case this results in a duplicate msg sitting orphaned until it's */
+/* cleaned out.                                                           */
+
+  if (substdio_flush(&ssmsg) == -1) die_msg();
+  if (fsync(fdmsg) == -1) die_msg();
+  if (fchmod(fdmsg,MODE_MOD_MSG | 0700) == -1) die_msg();
+  if (close(fdmsg) == -1) die_msg(); /* NFS stupidity */
+
+  close(fdlock);
+
+  if (!stralloc_copy(&line,&outlocal)) die_nomem();
+  if (!stralloc_cats(&line,"-return-@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  qmail_from(&qq,line.s);                      /* envelope sender */
+  if (pmod)                                    /* to moderator only */
+    qmail_to(&qq,pmod);
+  else {
+    if (flagself) {                            /* to all moderators */
+      if (!stralloc_copys(&moderators,dir)) die_nomem();
+      if (!stralloc_cats(&moderators,"/mod")) die_nomem();
+      if (!stralloc_0(&moderators)) die_nomem();
+    }
+    putsubs(moderators.s,0,52,subto,1,FATAL);
+  }
+
+  if (*(err = qmail_close(&qq)) == '\0') {
+      strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+      strerr_die2x(0,"ezmlm-store: info: qp ",strnum);
+  } else
+      strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err+1);
+}
index 219b88f..ae4bf68 100644 (file)
@@ -3,9 +3,20 @@
 ezmlm-sub \- manually add addresses to a mailing list
 .SH SYNOPSIS
 .B ezmlm-sub
+[
+.B \-HmMnNsSvV
+][
+.B \-h
+.I hash
+]
+.B 
 .I dir
 [
-.I box\fB@\fIdomain ...
+.I box\fB@\fIdomain 
+[
+.I name
+]
+.I ...
 ]
 .SH DESCRIPTION
 .B ezmlm-sub
@@ -13,12 +24,20 @@ adds each address
 .I box\fB@\fIdomain
 to the mailing list stored in
 .IR dir .
+.I name
+is added as a comment to the subscription log, if the
+.B \-n
+switch is used.
+
+If no argument is given on the command line,
+.B ezmlm-sub
+processes stdin.
 
 If
 .I box\fB@\fIdomain
 is already on the mailing list,
 .B ezmlm-sub
-leaves it there.
+leaves it there and does not modify the subscription log.
 
 .B ezmlm-sub
 converts
@@ -28,11 +47,61 @@ to lowercase before adding
 to the mailing list.
 
 .I box\fB@\fIdomain
-cannot be longer than 400 characters.
+cannot be longer than 400 characters (255 characters with mysql support).
+.SH "GENERAL OPTIONS"
+.TP
+.B \-n
+Assume arguments are pairs of
+.I box\fB@\fIdomain
+and
+.IR name
+(or other subscriber info)
+rather than addresses alone.
+.B ezmlm-sub(1)
+will add the first argument in each pair to the subscriber list. If it is
+a new address,
+.I name
+will be added to the subscription log.
+.TP
+.B \-N
+(Default.)
+Arguments are all addresses of the type
+.IR box\fB@\fIdomain .
+.TP
+.B \-v
+Display
+.B ezmlm-sub(1)
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-sub(1)
+version information.
+.SH "MYSQL OPTIONS"
+These option is silently ignored in the absence of mysql support.
+.TP
+.B \-h \fIhash
+With mysql support the argument is used as the hash. The argument should
+be between 1 and 99. The hash is used in
+in the distribution of addresses between sublists. As the hash is normally
+between 0 and 52, controlling the hash makes it possible to add addresses
+that cannot be manipulated remotely.
+.TP
+.B \-m
+(Default.)
+Use SQL support if available.
+.TP
+.B \-M
+Do not use SQL support even if available. This option can be used to build
+a normal local subscriber database even for lists with SQL support. Use
+in combination with
+.B ezmlm-list(1)
+to convert an SQL address db to a ezmlm standard address database.
 .SH "SEE ALSO"
 ezmlm-list(1),
 ezmlm-manage(1),
 ezmlm-make(1),
+ezmlm-receipt(1),
 ezmlm-send(1),
 ezmlm-unsub(1),
 ezmlm(5)
index 85fce6a..62e3166 100644 (file)
@@ -1,9 +1,32 @@
+/*$Id: ezmlm-sub.c,v 1.18 1999/08/02 02:57:57 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
 #include "strerr.h"
 #include "subscribe.h"
-#include "log.h"
+#include "sgetopt.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "getln.h"
+#include "errtxt.h"
+#include "scan.h"
+#include "idx.h"
 
 #define FATAL "ezmlm-sub: fatal: "
-#define WARNING "ezmlm-sub: warning: "
+
+void *psql = (void *) 0;
+
+char inbuf[512];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+
+stralloc line = {0};
+
+void die_usage() {
+  strerr_die1x(100,
+       "ezmlm-sub: usage: ezmlm-sub [-mMvV] [-h hash] [-n] dir "
+               "[box@domain [name]] ...");
+}
+
+int flagname = 0;
 
 void main(argc,argv)
 int argc;
@@ -11,23 +34,81 @@ char **argv;
 {
   char *dir;
   char *addr;
+  char *comment;
+  char *cp;
+  char ch;
+  int opt;
+  int match;
+  int flagmysql = 1;   /* use mysql if supported */
+  int forcehash = -1;
+  unsigned int u;
+
+  (void) umask(022);
+  while ((opt = getopt(argc,argv,"h:HmMnNvV")) != opteof)
+    switch(opt) {
+      case 'h': (void) scan_ulong(optarg,&u); forcehash = 0; break;
+      case 'H': forcehash = -1; break;
+      case 'm': flagmysql = 1; break;
+      case 'M': flagmysql = 0; break;
+      case 'n': flagname = 1; break;
+      case 'N': flagname = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-sub version: ezmlm-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+
+  if (dir[0] != '/')
+    strerr_die2x(100,FATAL,ERR_SLASH);
 
-  dir = argv[1];
-  if (!dir)
-    strerr_die1x(100,"ezmlm-sub: usage: ezmlm-sub dir box@domain ...");
   if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
-
-  argv += 2;
-  while (addr = *argv++)
-    switch(subscribe(addr,1)) {
-      case -1:
-       strerr_die1(111,FATAL,&subscribe_err);
-      case -2:
-       strerr_warn4(WARNING,"cannot subscribe ",addr,": ",&subscribe_err);
-       break;
-      case 1:
-       log("+manual",addr);
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  if (forcehash == 0) forcehash = (int) u;
+
+  if (argv[optind]) {
+    if (flagname) {
+               /* allow repeats and last addr doesn't need comment */
+      while ((addr = argv[optind++])) {
+        (void) subscribe(dir,addr,1,argv[optind],"+manual",
+               flagmysql,forcehash,(char *) 0,FATAL);
+        if (!argv[optind++]) break;
+      }
+    } else {
+
+      while ((addr = argv[optind++]))
+        (void) subscribe(dir,addr,1,"","+manual",flagmysql,
+               forcehash,(char *) 0,FATAL);
+    }
+  } else {             /* stdin */
+    for (;;) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+       strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!match) break;
+      if (line.len == 1 || *line.s == '#') continue;
+      line.s[line.len - 1] = '\0';
+      comment = (char *) 0;
+      if (flagname) {
+       cp = line.s;
+       while ((ch = *cp)) {
+         if (ch == '\\') {
+            if (!*(++cp)) break;
+         } else if (ch == ' ' || ch == '\t' || ch == ',') break;
+         cp++;
+        }
+        if (ch) {
+         *cp = '\0';
+         comment = cp + 1;
+        }
+      }
+      (void) subscribe(dir,line.s,1,comment,"+manual",flagmysql,
+               forcehash,(char *) 0,FATAL);
     }
+  }
+  closesql();
   _exit(0);
 }
diff --git a/ezmlm-test.1 b/ezmlm-test.1
new file mode 100644 (file)
index 0000000..65dee58
--- /dev/null
@@ -0,0 +1,153 @@
+.TH ezmlm-test 1
+.SH NAME
+ezmlm-test \- test ezmlm programs
+.SH SYNOPSIS
+.B ezmlm-test
+[
+.B \-on
+][
+.B \-h\fI host
+][
+.B \-u\fI user
+][
+.B \-l\fI sqluser
+][
+.B \-t\fI sqltable
+][
+.B \-d\fI sqldatabase
+][
+.B \-p\fI sqlpassword
+][
+.B \-s\fI section
+]
+.I dir
+.SH DESCRIPTION
+.B ezmlm-test
+is run from the ezmlm build directory. It will test most of the functions
+of most of the programs in ezmlm-idx>=0.313. The program prints status
+and error messages to stdout. It requires that qmail runs on the host and that
+mail delivery to a local user functions. By default, it runs as the
+user ``eztest'' who should have read and execute permission to the files
+in the build directory.
+
+For testing with an SQL database (requires ezmlm-idx>=0.32; see
+.BR -p ),
+you need to have first created the tables in the database, e.g. using
+.BR ezmlm-mktab .
+A number of switches allow overriding the default connection information.
+
+.B ezmlm-test
+creates the list ``eztest-__tstlist@host''
+in the directory ``~/__TSTDIR''. This directory and ``~/.qmail-__tstlist*''
+will be overwritten/removed by the program. In addition,
+the file ``~/__TSTDIR_err'' is created. In cases of error, it often contains
+the error message form the failing program.
+
+.B ezmlm-test
+should complete without error.
+As many invocations of the programs test several functions it is not easy
+to determine what went wrong if
+.B ezmlm-test
+fails. Usually, ``~/__TSTDIR__err'' gives some leads, but then debugging
+of the particular program is required. Usually, this involves recreating
+the failing circumstances, including environment variables.
+.SH OPTIONS
+.TP
+.B \-d\fI sqldatabase
+If SQL support is tested, use
+.I sqldatabase
+as the database, overriding the default ``ezmlm''. See
+.BR \-p .
+.TP
+.B \-h\fI host
+Connect to an SQL server on
+.IR host ,
+if an SQL database is used (see
+.BR \-p ).
+Default is empty, which for most database managers defaults to
+localhost/unix socket. To specify a non-default port,
+add ``:port'' to the host name.
+.TP
+.B \-l\fI sqluser
+Connect as
+.I sqluser
+to the SQL server, if an SQL database is used (see
+.BR \-p ).
+By default, it is the same as the executing user as
+specified by the
+.B \-u
+switch or the default ``eztest''.
+.TP
+.B \-n
+Test assuming qmail>=1.02 and ezmlm-idx>=0.32. Normally, this is auto-detected.
+This switch is for testing of
+.B ezmlm-test
+and unlikely to be generally useful.
+.TP
+.B \-o
+Test assuming an old (<1.02) version of qmail which does not support
+the DEFAULT environment variable. Normally, this is auto-detected, and
+DEFAULT support is used if qmail>=1.02 and ezmlm-idx>=0.32. The ``old''
+style works with all versions of qmail, but requires adjustments for
+virtual domains. DEFAULT support makes these adjustments unnecessary.
+.TP
+.B \-p\fI sqlpassword
+Test with SQL support, and use
+.I sqlpassword
+as the connection password. By default, local databases, rather than an
+SQL database are used for testing. To use the SQL database with an
+empty password, specify
+.BR \-p\ '' .
+.TP
+.B \-s\fI section
+Restart
+.B ezmlm-test
+from section
+.IR section .
+This can be used to resume execution if the 30 s given for mail delivery
+was not sufficient for the test message to be delivered. ``9'' can be
+used to clean up any files remaining after incomplete execution of
+.BR ezmlm-test .
+This option should not be needed.
+.TP
+.B \-t\fI sqltable
+Use
+.I sqltable
+as the table root name for the SQL database tables, if an SQL database
+is used (see
+.BR \-p ).
+Default is ``list''.
+.TP
+.B \-u\fI user
+Execute
+.B ezmlm-test
+as
+.IR user .
+Default is ``eztest''. The program refuses to run, unless the effective
+user name matches this user name.
+.SH BUGS
+On some systems, some of the tests give a broken pipe error. This is because
+code needs to be added to the make_message function to capture error messages.
+These errors can be safely ignored for now. 
+.SH "SEE ALSO"
+ezmlm(5),
+ezmlm-clean(1),
+ezmlm-gate(1),
+ezmlm-get(1),
+ezmlm-idx(1),
+ezmlm-issubn(1),
+ezmlm-list(1),
+ezmlm-make(1),
+ezmlm-manage(1),
+ezmlm-mktab(1),
+ezmlm-moderate(1),
+ezmlm-request(1),
+ezmlm-return(1),
+ezmlm-send(1),
+ezmlm-split(1),
+ezmlm-store(1),
+ezmlm-sub(1),
+ezmlm-tstdig(1),
+ezmlm-unsub(1),
+ezmlm-warn(1)
+
diff --git a/ezmlm-test.sh b/ezmlm-test.sh
new file mode 100644 (file)
index 0000000..a1d03b8
--- /dev/null
@@ -0,0 +1,2366 @@
+# Above should have been added a QMPATH definition
+# DEBUG=1
+# This program is meant to test ezmlm-idx.
+# Several criteria have to be met for this to work:
+# 0. you need to have qmail installed allowing mail delivery to
+#    "eztest@host" and subaddresses.
+# 2. you need to have a user "eztest" or edit the script to change to a
+#      user that exists on your system.
+# 3. you need to be that user when you invoke this script and the script
+#      needs to reside in the same dir as the binaries you want to test.
+# 4. this user needs to have execute permission for the build dir
+#
+# The script will use ~eztest/__TSTDIR and ~eztest/__TSTDIR__err and destroy
+# any files therein. Both user name and directory names etc can be configured
+# below, but this should only very rarely be necessary.
+#
+# This program is experimental and not yet properly documented. Please
+# send comments to lindberg@id.wustl.edu. I'm attempting to make this a
+# rigorous test for future ezmlm+idx installations so that full function
+# can be verified when upgrading to a new version.
+
+# must be absolute
+EZBIN=`pwd`
+
+# the user that should run the scripts (test list is created in this
+# users home directory).
+EZTEST='eztest'
+
+# HOST is normally set from /var/qmail/control/me. Set it here to override
+HOST=''
+
+# Version of ezmlm-idx for which this test script is designed
+OLD_VER='ezmlm-idx-031'                # ezmlm-idx-0.31x
+VER='ezmlm-idx-0.40'
+EZVER='40'                     # default version
+
+#does the current version have qmail<1.02 support?
+OLD_QMAIL='n'
+
+# basedir for test list. It and all files therein are destroyed by
+# the script
+DIR="$HOME/__TSTDIR"
+
+# part that follows user name of local name of the list
+LIST='__tstlist'
+
+# file not within listdir where some error output is stored. If something
+# fails, this file may have more info.
+ERR="${DIR}__err"
+
+# file that can hold crated test msg to avoid sigpipe
+TMP="${DIR}/__tmp"
+
+# defaults for SQL version - overridden by command line switches
+TABLE='list'
+DB='ezmlm'
+SQLHOST=''
+SQLUSR="$EZTEST"       # -u overrides, -l overrides that
+
+# sets umask so that qmail paternalism doesn't complain. With most
+# installations, there is a umask in the user's profile. 022 should be ok,
+# but 002 is safer.
+
+UMASK='umask 002'
+
+#programs:
+# to report disk usage summary
+DU='du -s'
+# Need full path in case qmail doesn't have these in the path
+ECHO=`which echo` 2>/dev/null || ECHO='echo'
+GREP=`which grep` 2>/dev/null || GREP='grep'
+# std programs
+HEAD='head'
+MKDIR='mkdir'
+MV='mv'
+# a ps command that would list qmail if running. This works for RedHat Linux
+PS='ps auxw'
+RM='rm'
+SED='sed'
+STRINGS='strings'
+TAIL='tail'
+UNSET='unset'
+WC='wc'
+# if you don't have this, you can put 'echo "user"' where user is the current
+# login user name.
+WHOAMI='whoami'
+
+###################### END CONFIRGRABLE ITEMS #########################
+SQLUSER=''     # must be empty
+ARR='-------------------->'
+ALLOW='allow'
+DENY='deny'
+DASH='-'
+# switch for ezmlm-return
+DLC='-d'
+DUC='-D'
+# file for ezmlm-request testing
+REQ="${DIR}/__req"
+# Set if we've found bug from old version
+BUG=''
+# Use RDBMS, set if -p was specified even if PW empty, e.g. Postgres
+USESQL=''
+# process arguments
+
+SECT="1"
+while [ ! -z "$1" ]; do                # not everyone has getopt :-(
+       case "$1" in
+               -/)
+                       DASH='-/'; shift;;
+               -d)
+                       DB="$2"; shift; shift;;
+               -h)
+                       SQLHOST="$2"; shift; shift;;
+               -l)
+                       SQLUSER="$2"; shift; shift;;
+               -n)
+                       QMVER=n; shift;;
+               -o)
+                       QMVER=o; shift;;
+               -p)
+                       PW="$2"; USESQL=1; shift; shift;;
+               -u)
+                       EZTEST="$2"; SQLUSR="$2"; shift; shift;;
+               -s)
+                       SECT="$2"; shift; shift;;
+               -t)
+                       TABLE="$2"; shift; shift;;
+               --)
+                       shift; break;;
+               *)
+                       ${ECHO} "$i"
+                       ${ECHO} "Usage: ezmlm-test [-/] [-on] [-u user]" \
+                               "[-p sqlpassword [-l sqluser] [-d sqldb]" \
+                               "[-t sqltable] [-h sqlhost]] [-s section]"
+                       ${ECHO}
+                       ${ECHO} "args have to be separated from switches!"
+                       ${ECHO}
+                       ${ECHO} "defaults: -d ezmlm"
+                       ${ECHO} "          -h [empty => RDBMS default]"
+                       ${ECHO} "          -l eztest or -u arg if specified"
+                       ${ECHO} "          -n/o [autodetected]"
+                       ${ECHO} "          -p [empty - don't use SQL support]"
+                       ${ECHO} "          -s 1 [run test from beginning]"
+                       ${ECHO} "          -t list"
+                       ${ECHO} "          -u eztest"
+                       ${ECHO}
+                       exit 99;;
+       esac
+done
+
+if [ -z "$SQLUSER" ]; then
+       SQLUSER="$SQLUSR"
+fi
+
+USER=`${WHOAMI}` >/dev/null 2>&1 || \
+       { ${ECHO} "whoami doesn't work. If you're not \"${EZTEST}\" the";
+         ${ECHO} "will fail."; USER="${EZTEST}"; }
+
+if [ "$USER" != "${EZTEST}" ]; then 
+  ${ECHO} "Must be user ${EZTEST} to execute"; exit 99
+fi
+LOC="$EZTEST-$LIST"
+# calculate position in LOCAL where [normally] default starts
+LOCLEN=`${ECHO} "$LOC-" | ${WC} -c | ${SED} 's/ //g'`
+REJLEN=`${ECHO} "$LOC-reject-" | ${WC} -c | ${SED} 's/ //g'`
+ACCLEN=`${ECHO} "$LOC-accept-" | ${WC} -c | ${SED} 's/ //g'`
+
+${UMASK} >/dev/null || \
+      {
+       ${ECHO} "Umask failed. Usually, this is OK. Fix this if ezmlm-test"
+       ${ECHO} "fails and messages remain queued with the qmail error"
+       ${ECHO} "'Uh-oh: .qmail file is writable'."
+       ${ECHO}
+      }
+
+DOT="$HOME/.qmail$DASH$LIST"
+
+if [ "$SECT" != "1" ]; then
+       ${ECHO} "Starting with section $SECT ..."
+fi
+# test addresses. These are all within the list dir and all addresses
+# are subaddresses of the list address. You can change this, but it is
+# usually pointless.
+SINK='sink'
+SINKDIR="${DIR}/${SINK}"
+SND="${LOC}-${SINK}"
+
+# moddir
+MODACC='modacc'
+MODDIR="${DIR}/${MODACC}"
+MOD="$LOC-${MODACC}"
+
+# digdir
+DIGGG='dig'
+DIGDIR="${DIR}/${DIGGG}"
+DIG="$LOC-${DIGGG}"
+
+# mandir
+MANAG="man"
+MANDIR="${DIR}/${MANAG}"
+MAN="$LOC-${MANAG}"
+
+# bouncedir
+BOUNCE='bnc'
+BNC="$LOC-bnc"
+
+if [ -z "$HOST" ]; then
+  HOST=`${HEAD} -1 ${QMPATH}/control/me` || \
+       { ${ECHO} "unable to get host name. Set HOST in script" ; exit 99; }
+fi
+
+if [ ! -x "${EZBIN}/ezmlm-make" ]; then
+       ${ECHO} "can't execute ${EZBIN}/ezmlm-make. Most likely, user ``$USER''"
+       ${ECHO} "doesn't have execute permission to files in directory"
+       ${ECHO} "``${EZBIN}''. Adjust permissions or edit the script to"
+       ${ECHO} "use a different test user."
+       exit 99
+fi
+
+if [ ! -x "${QMPATH}/bin/qmail-local" ]; then
+       ${ECHO} "can't find qmail-local. Please correct the path in the script"
+       exit 99
+fi
+if [ ! -x "${QMPATH}/bin/qmail-inject" ]; then
+       ${ECHO} "can't find qmail-inject. Please correct the path in the script"
+       exit 99
+fi
+# Check if qmail is running. Don't fail if not, as it's most likely a
+# ps issue and not a lack of qmail.
+${PS} | ${GREP} qmai\l-send >/dev/null 2>&1 || \
+       {
+        ${ECHO} "qmail isn't running or ps doesn't work as expected. If"
+        ${ECHO} "qmail is not running, this script will fail. Will continue..."
+       }
+
+###############################
+# message generating function #
+###############################
+make_body()
+{
+  ${ECHO} "This is a simple message body"
+  ${ECHO} "--bound123ary"
+  ${ECHO} "Content-type: Text/pLAIn"
+  ${ECHO}
+  ${ECHO} "plain text"
+  ${ECHO} "--bound123ary"
+  ${ECHO} "Content-type: texT/Html"
+  ${ECHO}
+  ${ECHO} "html text"
+  ${ECHO} "--bound123ary--"
+  ${ECHO}
+  ${ECHO} "junk after boundary"
+  return 0
+}
+
+make_message()
+{
+  ${ECHO} "ReCEIved: #LAST#"
+  ${ECHO} "ReCeIved: #PENULTIMATE#"
+  ${ECHO} "retuRN-RECeipt-to: nobody"
+  ${ECHO} "To: $TO"
+  ${ECHO} "CC: "
+  ${ECHO} " $CC"
+  ${ECHO} "FROM: $FROM"
+  if [ ! -z "$CONTENT" ]; then
+       ${ECHO} "MIME-Version: 1.0"
+       ${ECHO} "Content-type: $CONTENT;"
+       ${ECHO} " boundary=bound123ary${AFTERBOUND}"
+  fi
+  if [ ! -z "$SUBJECT" ]; then
+       ${ECHO} "Subject: $SUBJECT"
+  fi
+  ${ECHO}
+  make_body
+  return 0
+}
+
+############################
+# function to test testmsg #
+############################
+send_test()
+{
+  {
+       ${ECHO} "X-num: #TSTMSG$1#"
+       ${ECHO} "To: ${SND}@$HOST"
+  } | ${QMPATH}/bin/qmail-inject
+  return 0
+}
+
+############################
+# sleeping 5 secs function #
+############################
+sleep_5()
+{
+  sleep 1; ${ECHO} -n "."; sleep 1; ${ECHO} -n "."
+  sleep 1; ${ECHO} -n "."; sleep 1; ${ECHO} -n "."
+  sleep 1; ${ECHO} -n "${1}"
+  return 0
+}
+
+################################
+# waiting for delivery fuction #
+################################
+wait_test()
+{
+${ECHO} -n "max 35s for delivery: "
+sleep_5 5s
+TSTMSG=`${GREP} -l "#TSTMSG$1" $SINKDIR/new/* 2>/dev/null`
+if [ -z "$TSTMSG" ]; then
+  sleep_5 10s
+  TSTMSG=`${GREP} -l "#TSTMSG$1" $SINKDIR/new/* 2>/dev/null`
+  if [ -z "$TSTMSG" ]; then
+    sleep_5 15s
+    TSTMSG=`${GREP} -l "#TSTMSG$1" $SINKDIR/new/* 2>/dev/null`
+    if [ -z "$TSTMSG" ]; then
+      sleep_5 20s
+      TSTMSG=`${GREP} -l "#TSTMSG$1" $SINKDIR/new/* 2>/dev/null`
+      if [ -z "$TSTMSG" ]; then
+        sleep_5 25s
+        TSTMSG=`${GREP} -l "#TSTMSG$1" $SINKDIR/new/* 2>/dev/null`
+        if [ -z "$TSTMSG" ]; then
+          sleep_5 30s
+          TSTMSG=`${GREP} -l "#TSTMSG$1" $SINKDIR/new/* 2>/dev/null`
+          if [ -z "$TSTMSG" ]; then
+               ${ECHO}
+               ${ECHO} "Delivery of test message failed. Fix qmail or wait"
+               ${ECHO} "longer and continue with argument \"-s $1\""
+               exit 100
+          fi
+        fi
+      fi
+    fi
+  fi
+fi
+sleep_5 OK
+${ECHO}
+${RM} -f "$TSTMSG" > "${ERR}" 2>&1 || \
+       { ${ECHO} "failed to remove test message for section $1"; exit 100; }
+TSTMSG=''
+return 0
+}
+
+########################
+# remove old test list #
+########################
+if [ "$SECT" = "1" ]; then
+  if [ $USESQL ]; then
+       ${EZBIN}/ezmlm-unsub "${DIR}/digest" "${MAN}@$HOST" "${DIG}@$HOST" \
+               >"${ERR}" 2>&1
+       ${EZBIN}/ezmlm-unsub "${DIR}/mod" "${MOD}@$HOST" \
+               >"${ERR}" 2>&1
+       ${EZBIN}/ezmlm-unsub "${DIR}/${ALLOW}" "aaa@bbb" "ccc@ddd" "eee@fff" \
+               >"${ERR}" 2>&1
+  fi
+  ${RM} -rf "${DIR}" ${DOT}* "${ERR}" >/dev/null 2>&1
+fi
+${ECHO}
+
+#################
+# check version #
+#################
+
+# assume ezmlm-idx and ezmlm-test are different versions
+SAME_VER='n'
+
+TMP_VER=`${EZBIN}/ezmlm-make -V 2>&1` || \
+       {
+               ${ECHO} "This program only works with ezmlm-idx"
+               exit 100
+       }
+# ezmlm-idx-0313 does not support DEFAULT, so no sense testing it
+THIS_VER=`${ECHO} "$TMP_VER" | cut -d'+' -f2`
+${ECHO} "$THIS_VER" | ${GREP} "ezmlm-idx" > /dev/null 2>&1 ||
+  THIS_VER=`${ECHO} "$TMP_VER" | cut -d' ' -f4`
+
+${ECHO} "testing ezmlm-idx:    $THIS_VER"
+${ECHO} "Using FQDN host name: $HOST"
+
+${ECHO} "$THIS_VER" | ${GREP} "$OLD_VER" >/dev/null 2>&1
+if [ "$?" = "0"        ]; then
+       DLC=''
+       DUC=''
+       EZVER='31'
+       QMVER='o'
+       ALLOW='extra'           # support old nomenclature
+       DENY='blacklist'
+else
+       ${ECHO} "$THIS_VER" | ${GREP} "$VER" >/dev/null 2>&1
+       if [ "$?" != "0" ]; then
+               ${ECHO} "Warning: ezmlm-make version is not $VER"
+               ${ECHO} "         test info may not be reliable"
+               ${ECHO}
+       fi
+########## should add testing of From line logging for non-0.31x
+       SW_FROM="-f"
+fi
+
+# Now see if we support old qmail
+
+  ${ECHO} "$THIS_VER" | ${GREP} "ezmlm-idx-0.32" >/dev/null 2>&1 && \
+       EZVER='32'; 
+
+if [ "$EZVER" != '31' -a "$EZVER" != '32' -a "$QMVER" = 'o' ]; then
+       ${ECHO} "Sorry, this version of ezmlm-idx requires qmail>=1.02"
+       exit 100;
+fi
+
+if [ "$SECT" = "1" ]; then
+##############
+# ezmlm-make #
+##############
+  ${ECHO} -n "ezmlm-make (1/2):     "
+
+# edit non-existant list
+  ${EZBIN}/ezmlm-make -e -C${EZBIN}/ezmlmrc "${DIR}" "${DOT}" \
+       "$LOC" "$HOST" > /dev/null 2>&1 && \
+       { ${ECHO} "ezmlm-make failed reject edit of non-existing list:"
+         exit 100; }
+
+# make simple test list
+  ${EZBIN}/ezmlm-make -C${EZBIN}/ezmlmrc "${DIR}" "${DOT}" \
+       "$LOC" "$HOST" || \
+       { ${ECHO} "ezmlm-make failed to create test list"; exit 100; }
+
+# remake simple test list which should fail
+  ${EZBIN}/ezmlm-make -C${EZBIN}/ezmlmrc "${DIR}" "${DOT}" \
+       "$LOC" "$HOST" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject creation of existing list"; exit 100; }
+
+# edit the list (add digest)
+  ${EZBIN}/ezmlm-make -+d -C${EZBIN}/ezmlmrc "${DIR}" || \
+       { ${ECHO} "ezmlm-make failed to edit test list"; exit 100; }
+
+# edit the list (add digest)
+  ${MV} "${DIR}/config" "${DIR}/config~"
+  ${EZBIN}/ezmlm-make -ed -C${EZBIN}/ezmlmrc "${DIR}" "$DOT" "$LOC" "$HOST" \
+       >/dev/null 2>&1 || \
+       { ${ECHO} "failed without DIR/config: 0.313 bug, fixed in 0.314."
+         ${ECHO} -n "ezmlm-make ......     "
+         BUG="${BUG} config"
+       }
+  ${MV} "${DIR}/config~" "${DIR}/config"
+
+  ${GREP} "ezmlm-weed" "${DIR}/bouncer" >/dev/null 2>&1 || \
+       { ${ECHO} "no ezmlm-weed in bouncer"; exit 100; }
+  ${GREP} "ezmlm-return" "${DIR}/bouncer" >/dev/null 2>&1 || \
+       { ${ECHO} "no ezmlm-return in bouncer: 0.32 bug, fixed in 0.321."
+         ${ECHO} -n "ezmlm-make ......     "
+         BUG="${BUG} return"
+       }
+# digest/bouncer only for >=0.32
+  if [  "$EZVER" != '31' ]; then
+    if [ ! -f "${DIR}/digest/bouncer" ]; then
+       echo "failed to create digest/bouncer"; exit 100;
+    fi
+    ${GREP} "ezmlm-weed" "${DIR}/digest/bouncer" >/dev/null 2>&1 || \
+       { ${ECHO} "no ezmlm-weed in bouncer"; exit 100; }
+    ${GREP} "ezmlm-return" "${DIR}/digest/bouncer" >/dev/null 2>&1 || \
+       { ${ECHO} "no ezmlm-return in digest/bouncer: 0.32 bug, OK in 0.321."
+         ${ECHO} -n "ezmlm-make ......     "
+         BUG="${BUG} return"
+       }
+  fi
+  ${ECHO} "OK"
+
+# Add sql files for sql testing
+RDBMS='STD'
+${ECHO} -n "Using RDBMS support:  "
+if [ $USESQL ]; then
+  ${EZBIN}/ezmlm-make -+6 "$SQLHOST::$SQLUSER:$PW:$DB:$TABLE" \
+       -C${EZBIN}/ezmlmrc "${DIR}"|| \
+       { ${ECHO} "ezmlm-make failed to add SQL config info"; exit 100; }
+
+  ${STRINGS} ${EZBIN}/ezmlm-sub | ${GREP} -i 'MySQL' >/dev/null 2>&1 && \
+       RDBMS='MySQL'
+  ${STRINGS} ${EZBIN}/ezmlm-sub | ${GREP} -i 'libpq.' >/dev/null 2>&1 && \
+       RDBMS='PostgreSQL'
+  if [ "$RDBMS" = 'STD' ]; then
+       ${ECHO} "No recognized support. If none, will default to std dbs."
+  else
+       ${ECHO} "$RDBMS. Hope empty tables exist."
+  fi
+
+else
+       ${ECHO} "No."
+fi
+
+###############################################################
+# set up subscriber/moderator/sender/digest recipient account #
+###############################################################
+  ${MKDIR} "$SINKDIR" "$SINKDIR/new" "$SINKDIR/cur" "$SINKDIR/tmp" || \
+       { ${ECHO} "mkdir for sinkdir failed"; exit 100; }
+  ${ECHO} "${SINKDIR}/" > "$DOT-$SINK"
+# link for qmail version testing 
+  ${ECHO} '|echo $DEFAULT >' "${DIR}/default" > "$DOT-$SINK-default"
+  ${ECHO} "${SINKDIR}/" >> "$DOT-$SINK-default"
+
+  ${MKDIR} "$MODDIR" "$MODDIR/new" "$MODDIR/cur" "$MODDIR/tmp" || \
+       { ${ECHO} "mkdir for moddir failed"; exit 100; }
+  ${ECHO} "${MODDIR}/" > "$DOT-$MODACC"
+
+  ${MKDIR} "$MANDIR" "$MANDIR/new" "$MANDIR/cur" "$MANDIR/tmp" || \
+       { ${ECHO} "mkdir for mandir failed"; exit 100; }
+  ${ECHO} "${MANDIR}/" > "$DOT-$MANAG"
+
+  ${MKDIR} "$DIGDIR" "$DIGDIR/new" "$DIGDIR/cur" "$DIGDIR/tmp" || \
+       { ${ECHO} "mkdir for digdir failed"; exit 100; }
+  ${ECHO} "${DIGDIR}/" > "$DOT-$DIGGG"
+
+fi
+
+###########################
+# determine qmail version #
+###########################
+
+if [ "$SECT" != "9" ]; then
+
+${ECHO} "Subject: zzz-test" > "${DIR}/__tmp"
+${QMPATH}/bin/qmail-local "$EZTEST" "$HOME" "$SND-zzz" "$DASH" \
+               "$LIST-$SINK-zzz" "$HOST" \
+               "<>" '' < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "-failed to deliver message with qmail-local"; exit 100; }
+
+if [ ! -r "${DIR}/default" ]; then
+       ${ECHO} "qmail-local failed to deliver the message. Can't determine"
+       ${ECHO} "qmail version"
+       exit 99
+fi
+
+if [ `cat "${DIR}/default"` = "zzz" ]; then
+       if [ -z "$QMVER" ]; then
+               QMVER="n"
+       fi
+else
+       if [ -z "$QMVER" ]; then
+               QMVER="o"
+       fi
+fi
+
+${ECHO} -n "testing for qmail:    "
+if [ "$QMVER" = "n" ]; then
+       ${ECHO} ">=1.02"
+else
+       ${ECHO} "[any]"
+fi
+
+# if testing for old version, make sure DEFAULT is not defined. If printenv
+# is not available, we hope for the best and continue. unset should work ...
+# Set BADUNSET if unset doesn't do the job
+
+A='a'
+export A
+${UNSET} A
+[ -z "$A" ] || BADUNSET='y'
+
+${UNSET} DEFAULT
+
+if [ "$QMVER" = "o" ]; then
+  printenv PATH >/dev/null 2>&1 && \
+    printenv DEFAULT > /dev/null 2>&1 && \
+      { ${ECHO} "Can't test for old version of qmail if DEFAULT is defined. ";
+      ${ECHO} "Please undefine it."; exit 99; }
+fi
+
+# correct bouncer for our binaries:
+###################################
+# NOTE: This is duplicated (and should be) after next ezmlm-make block.
+  ${ECHO} "|/${EZBIN}/ezmlm-weed" > "${DIR}/bouncer"
+  ${ECHO} "|/${EZBIN}/ezmlm-weed" > "${DIR}/digest/bouncer"
+  if [ "$EZVER" = "31" ]; then # autodetecting bouncer for 0.31x
+    ${ECHO} "|/${EZBIN}/ezmlm-return '${DIR}'" >> "${DIR}/bouncer"
+    ${ECHO} "|/${EZBIN}/ezmlm-return '${DIR}'" >> "${DIR}/digest/bouncer"
+  else                         # split bouncer with args for later versions
+       # edited for ezmlm-new
+    ${ECHO} "|/${EZBIN}/ezmlm-return '${DIR}'" >> "${DIR}/bouncer"
+    ${ECHO} "|/${EZBIN}/ezmlm-return '${DIR}'" >> "${DIR}/digest/bouncer"
+  fi
+
+# if testing qmail>=1.02, remove inlocal/inhost - shouldn't be used
+  if [ "$QMVER" = "n" ]; then
+       ${RM} -f "${DIR}/inlocal" "${DIR}/inhost" > /dev/null || \
+         { ${ECHO} "failed to remove inlocal/inhost for testlist"; exit 100; }
+  fi
+
+###########################
+# set up bouncing account #
+###########################
+  ${ECHO} "|${GREP} 'MAILER-DAEMON' >/dev/null && exit 99" > "$DOT-$BOUNCE"
+  ${ECHO} "|exit 100" > "$DOT-$BOUNCE"
+
+fi
+
+###################################################
+# account to receive digests and archive excerpts #
+###################################################
+
+if [ "$SECT" = "1" ]; then
+
+#####################
+# test ezmlm-reject #
+#####################
+  ${ECHO} -n "ezmlm-reject:         "
+  FROM="$EZTEST"
+  TO="$EZTEST-__tstlist@$HOST"
+  SUBJECT="test"
+  CONTENT="multipart/mixed"
+  CC="<>"
+  BODY=''
+
+# with directory
+  make_message | ${EZBIN}/ezmlm-reject "${DIR}" || \
+       { ${ECHO} "failed to accept good message with dir"; \
+       exit 100; }
+# without directory
+
+  make_message | ${EZBIN}/ezmlm-reject || \
+       { ${ECHO} "failed to accept good message without dir: $?"; \
+       exit 100; }
+
+#too small
+  ${ECHO} "5000:1000" > "${DIR}/msgsize"
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "ezmlm-reject failed to reject too small message"; \
+               exit 100; }
+
+# just right
+  ${ECHO} "500:5" > "${DIR}/msgsize"
+  make_message | ${EZBIN}/ezmlm-reject "${DIR}" || \
+       { ${ECHO} "failed to accept message of ok size"; \
+       exit 100; }
+
+#too large
+  ${ECHO} "20:10" > "${DIR}/msgsize"
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "ezmlm-reject failed to reject too large message"; \
+               exit 100; }
+
+# restore
+  ${RM} -f "${DIR}/msgsize"
+
+# without subject
+  SUBJECT=''
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "ezmlm-reject failed to reject message without subject"; \
+               exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject 2>&1` && \
+       { ${ECHO} "ezmlm-reject failed to reject message without subject"; \
+               exit 100; }
+
+# with empty subject
+  SUBJECT='(NUll)'
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "ezmlm-reject failed to reject null subject"; \
+               exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject 2>&1` && \
+       { ${ECHO} "ezmlm-reject failed to reject null subject"; \
+               exit 100; }
+
+# testing -S
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -S "${DIR}"` || \
+       { ${ECHO} "-S switch failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -S ` || \
+       { ${ECHO} "-S switch failed without dir"; exit 100; }
+
+# with command subject
+  SUBJECT='REmOVE'
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "failed to reject command subject with dir"; \
+               exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject 2>&1` && \
+       { ${ECHO} "failed to reject command subject without dir"; \
+               exit 100; }
+
+# testing -C
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -C "${DIR}"` || \
+       { ${ECHO} "-C switch failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -C ` || \
+       { ${ECHO} "-C switch failed without dir"; exit 100; }
+
+  SUBJECT='test'
+
+# Test with list name in Cc:
+  CC="$TO"
+  TO="nobody@$HOST"
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}"` || \
+       { ${ECHO} "failed to accept good Cc: with dir"; \
+               exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject` || \
+       { ${ECHO} "failed to accept good Cc: without dir"; \
+               exit 100; }
+
+# Bad To/Cc
+  CC="$TO"
+  OUT=`make_message "$MESSAGE" | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+               { ${ECHO} "failed to reject bad To/Cc with dir"; \
+               exit 100; }
+  if [ "$?" != "100" ]; then
+       ${ECHO} "failed to exit 100 on error"; exit 100
+  fi
+  OUT=`make_message "$MESSAGE" | ${EZBIN}/ezmlm-reject -q "${DIR}" 2>&1` && \
+               { ${ECHO} "failed to reject bad To/Cc with dir"; \
+               exit 100; }
+  if [ "$?" -ne "99" ]; then
+       ${ECHO} "-q failed"; exit 100
+  fi
+
+# for backwards-compatibility and since we don't know inlocal@inhost without
+# dir, ezmlm-reject doesn't check To/Cc when there is no dir
+  OUT=`make_message "$MESSAGE" | ${EZBIN}/ezmlm-reject` || \
+               { ${ECHO} "failed to accept bad To/Cc without dir"; \
+               exit 100; }
+
+# testing -T
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -T "${DIR}"` || \
+       { ${ECHO} "-T switch failed with dir"; exit 100; }
+OUT=`make_message | ${EZBIN}/ezmlm-reject -T ` || \
+       { ${ECHO} "-T switch failed without dir"; exit 100; }
+
+# restore good TO
+  TO="$EZTEST-__tstlist@$HOST"
+
+# if part is mimereject message should be rejected
+  touch "${DIR}"/mimeremove
+  ${ECHO} "text/html" > "${DIR}"/mimereject
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "mimereject failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject` || \
+       { ${ECHO} "mimereject without dir"; exit 100; }
+
+# if part is removed ezmlm-reject should not reject
+  ${ECHO} "tExt/htMl" > "${DIR}"/mimeremove
+  ${ECHO} "" > "${DIR}"/mimereject 
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}"` || \
+       { ${ECHO} "mimeremove failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject` || \
+       { ${ECHO} "mimeremove without dir"; exit 100; }
+
+# test content-type with something after boundary=xxx
+  AFTERBOUND=';micalg=pgp-md5'
+  ${ECHO} "text/html" > "${DIR}"/mimereject
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "err with text after boundary: 0.30 bug fixed in 0.322"
+         ${ECHO} -n "ezmlm-reject.......   "
+         BUG="${BUG} reject_bound"
+       }
+
+# restore
+  ${RM} "${DIR}"/mimereject
+  AFTERBOUND=''
+
+# if entire message is mimeremove type is should be rejected
+  ${ECHO} "multipart/mixed" > "${DIR}"/mimeremove
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}" 2>&1` && \
+       { ${ECHO} "mimereject failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject` || \
+       { ${ECHO} "mimereject without dir"; exit 100; }
+
+# restore
+  ${RM} "${DIR}"/mimeremove
+
+# test headerreject
+  ${ECHO} "Content-TYPE" > "${DIR}"/headerreject
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -H "${DIR}"` || \
+       { ${ECHO} "headerreject -H failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -h "${DIR}" 2>&1` && \
+       { ${ECHO} "headerreject failed with dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject` || \
+       { ${ECHO} "headerreject failed without dir"; exit 100; }
+  OUT=`make_message | ${EZBIN}/ezmlm-reject -h 2>&1` && \
+       { ${ECHO} "-h was accepted without dir"; exit 100; }
+
+# Suppress content-type header
+  CONTENT=''
+  OUT=`make_message | ${EZBIN}/ezmlm-reject "${DIR}"` || \
+       { ${ECHO} "headerreject rejected even though header isn't there"; \
+       exit 100; }
+
+  CONTENT='multIpart/mIXed'
+
+  ${ECHO} "OK"
+###############################
+# ezmlm-sub/unsub/list/issubn #
+###############################
+
+  ${ECHO} -n "ezmlm-[un|is]sub[n]:  "
+
+  SENDER="XYZZY@HOst"; export SENDER
+
+# With mysql testing, there may be junk left from earlier testing that
+# gives false positives in testing. Make sure it's detected
+  ${EZBIN}/ezmlm-list "${DIR}" >/dev/null || \
+       { ${ECHO} "ezmlm-list: failed"; exit 100; }
+
+  ${EZBIN}/ezmlm-list "${DIR}" | ${GREP} '@' >/dev/null 2>&1 && \
+       { ${ECHO} "already addresses in table - please remove and start again";
+               exit 100; }
+
+  ${EZBIN}/ezmlm-list "${DIR}/digest" | ${GREP} '@' >/dev/null 2>&1 && \
+       { ${ECHO} "already addresses in table - please remove and start again";
+               exit 100; }
+
+  ${EZBIN}/ezmlm-list "${DIR}/${ALLOW}" | ${GREP} '@' >/dev/null 2>&1 && \
+       { ${ECHO} "already addresses in table - please remove and start again";
+               exit 100; }
+
+# not subscriber. Test default
+  ${EZBIN}/ezmlm-issubn "${DIR}" "${DIR}/${ALLOW}" && \
+       { ${ECHO} "ezmlm-issubn: failed: exit 0 on non-subscriber"; exit 100; }
+
+# not subscriber. Test -n
+  ${EZBIN}/ezmlm-issubn -n "${DIR}" "${DIR}/${ALLOW}" || \
+       { ${ECHO} "ezmlm-issubn: -n failed for non-subscriber"; exit 100; }
+
+# add subscriber
+  ${EZBIN}/ezmlm-sub "${DIR}" "xyZZy@hoSt" || \
+       { ${ECHO} "ezmlm-sub: failed to add subscriber"; exit 100; }
+
+# is subscriber. Test default
+  ${EZBIN}/ezmlm-issubn "${DIR}" "${DIR}/${ALLOW}" || \
+       { ${ECHO} "ezmlm-issubn: failed: exit false for subscriber"; exit 100; }
+
+# is subscriber. Test -n
+  ${EZBIN}/ezmlm-issubn -n "${DIR}" "${DIR}/${ALLOW}" && \
+       { ${ECHO} "ezmlm-issubn: -n failed for subscriber"; exit 100; }
+
+# add to allow
+  ${EZBIN}/ezmlm-sub "${DIR}/${ALLOW}" "ZZtop@hoSt" || \
+       { ${ECHO} "ezmlm-sub: failed to add address to ${DIR}/${ALLOW}"; exit 100; }
+
+# list subscribers
+  ${EZBIN}/ezmlm-list "${DIR}" | ${GREP} "xyZZy" >"${ERR}" 2>&1 || \
+       { ${ECHO} "ezmlm-list: failed to list subscribers"; exit 100; }
+
+# remove subscriber
+  ${EZBIN}/ezmlm-unsub "${DIR}" "xYzZy@hOst" || \
+       { ${ECHO} "ezmlm-sub: failed to add subscriber"; exit 100; }
+
+# see that it was removed
+  ${EZBIN}/ezmlm-list "${DIR}" | ${GREP} "xyZZy" >"${ERR}" 2>&1 && \
+       { ${ECHO} "ezmlm-unsub: failed to remove subscriber"; exit 100; }
+
+  SENDER="zztop@HOst"; export SENDER
+
+# check for address in allow
+  ${EZBIN}/ezmlm-issubn "${DIR}" "${DIR}/${ALLOW}" || \
+       { ${ECHO} "ezmlm-sub/issubn: failed to add/look in 2nd db"; exit 100; }
+
+# remove (multiple) (non)existing addresses from allow
+  ${EZBIN}/ezmlm-unsub "${DIR}/${ALLOW}" "xYzZy@hOst" "zZToP@HOSt" || \
+       { ${ECHO} "ezmlm-unsub: failed to remove subscriber"; exit 100; }
+
+# verify removal
+  ${EZBIN}/ezmlm-issubn "${DIR}" "${DIR}/${ALLOW}" && \
+       { ${ECHO} "ezmlm-unsub/issubn: failed to remove address"; exit 100; }
+
+# clean up
+  LOCAL=''; export LOCAL
+
+  ${ECHO} "OK"
+##############
+# ezmlm-send #
+##############
+  ${ECHO} -n "ezmlm-send (1/2):     "
+
+  SENDER="${SND}@$HOST"; export SENDER
+  ${EZBIN}/ezmlm-sub "${DIR}" "$SENDER"
+# set up prefix
+  ${ECHO} "[PFX]" > "${DIR}/prefix"
+# set up trailer
+  { ${ECHO} "--- TRAILER ---"; ${ECHO}; } > "${DIR}/text/trailer"
+# test
+  { ${ECHO} "X-num: msg1"; make_message; } | \
+       ${EZBIN}/ezmlm-send "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to accept normal message"; exit 100; }
+  if [ `cat "${DIR}/num"` != "1:1" ]; then
+       ${ECHO} "failed to create num for normal message 1"; exit 100; 
+  fi
+  if [ ! -x "${DIR}/archive/0/01" ]; then
+       { ${ECHO} "failed to archive normal message"; exit 100; }
+  fi
+  ${GREP} "1:" "${DIR}/archive/0/index" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to index archive"; exit 100; }
+
+  ${RM} -f "${DIR}/indexed"
+  ${RM} -f "${DIR}/archived"
+
+# test to see that trailer is added to nom-mime messages
+  CONTENT=''
+  { echo "X-num: msg5"; make_message; } | \
+       ${EZBIN}/ezmlm-send "${DIR}" >"${ERR}" 2>&1  || \
+       { ${ECHO} "failed to accept non-mime message"; exit 100; }
+
+# test to see that trailer is suppressed for multipart/signed
+  CONTENT='multipart/signed'
+  { echo "X-num: msg6"; make_message; } | \
+       ${EZBIN}/ezmlm-send "${DIR}" >"${ERR}" 2>&1  || \
+       { ${ECHO} "failed to accept multipart/signed message"; exit 100; }
+
+# restore
+  CONTENT='multipart/mixed'
+
+# test content-type with something after boundary=xxx
+  AFTERBOUND=';micalg=pgp-md5'
+  ${ECHO} "text/html" > "${DIR}"/mimeremove
+  make_message | ${EZBIN}/ezmlm-send "${DIR}" >"${ERR}" 2>&1  || \
+       { ${ECHO} "err with text after boundary: 0.30 bug fixed in 0.322"
+         ${ECHO} -n "ezmlm-send.........   "
+         BUG="${BUG} send_bound"
+       }
+# restore
+  AFTERBOUND=''
+  ${ECHO} "1:1" > "${DIR}/num"
+  ${RM} "${DIR}"/mimeremove
+
+# -r => don't trim received headers
+  { ${ECHO} "X-num: msg2"; make_message; } | \
+       ${EZBIN}/ezmlm-send -r "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to accept normal message 2"; exit 100; }
+
+  ${GREP} "2:" "${DIR}/archive/0/index" >/dev/null 2>&1 && \
+       { ${ECHO} "indexed message with DIR/indexed missing"; exit 100; }
+  ${GREP} "msg2" ${DIR}/archive/0/* >/dev/null 2>&1 && \
+       { ${ECHO} "archived message with DIR/archived missing"; exit 100; }
+
+# -C eliminate SENDER from addressees
+  { ${ECHO} "X-num: msg3"; make_message; } | \
+       ${EZBIN}/ezmlm-send -C "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to accept normal message 3"; exit 100; }
+  ${EZBIN}/ezmlm-unsub "${DIR}" "$SENDER"
+
+# make sure headerremove was done
+  ${GREP} -i 'return-receipt-to' < "${DIR}/archive/0/01" >/dev/null &&
+       { ${ECHO} "failed to remove headerremove"; exit 100; }
+# test mimeremove
+  touch "${DIR}/archived" "${DIR}/indexed"
+  ${ECHO} "teXT/hTml" > "${DIR}/mimeremove"
+  { ${ECHO} "X-num: msg4"; make_message; } | \
+       ${EZBIN}/ezmlm-send "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to accept mimeremove message"; exit 100; }
+  ${GREP} -i 'text/html' < "${DIR}/archive/0/04" >/dev/null &&
+       { ${ECHO} "failed to remove mimeremove part"; exit 100; }
+
+  ${ECHO} "OK"
+################
+# ezmlm-tstdig #
+################
+  ${ECHO} -n "ezmlm-tstdig:         "
+
+  ${EZBIN}/ezmlm-tstdig -k2 -m5 -t1 "${DIR}" || \
+       { ${ECHO} "-t1 failed"; exit 100; }
+  ${EZBIN}/ezmlm-tstdig -k2 -m5 -t0 "${DIR}" && \
+       { ${ECHO} "-t0 failed"; exit 100; }
+
+  LOCAL="$LOC-xx"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='xx'; export DEFAULT
+  fi
+  ${EZBIN}/ezmlm-tstdig -k2 -m5 -t0 "${DIR}" || \
+       { ${ECHO} "problem with -xx in manager position"; exit 100; }
+  LOCAL="$LOC-dig."; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='dig.'; export DEFAULT
+  fi
+  ${EZBIN}/ezmlm-tstdig -k2 -m5 -t0 "${DIR}" && \
+       { ${ECHO} "problem with -dig in manager position"; exit 100; }
+  LOCAL="$LOC-digest-"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='digest-'; export DEFAULT
+  fi
+  ${EZBIN}/ezmlm-tstdig -k2 -m5 -t0 "${DIR}" || \
+       { ${ECHO} "err with -digest- in mgr pos: 0.31 bug fixed in 0.321"
+         ${ECHO} -n "ezmlm-tstdig.......   "
+         BUG="${BUG} digest"
+       }
+  LOCAL=''; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       unset DEFAULT
+  fi
+  ${EZBIN}/ezmlm-tstdig -k2 -m4 -t1 "${DIR}" || \
+       { ${ECHO} "-m failed"; exit 100; }
+  ${EZBIN}/ezmlm-tstdig -k1 -m5 -t0 "${DIR}" || \
+       { ${ECHO} "-k failed"; exit 100; }
+  LOCAL="$LOC"; export LOCAL
+  ${EZBIN}/ezmlm-tstdig -k1 -m5 -t0 "${DIR}" > "${ERR}" 2>&1 || \
+       {
+        ${ECHO} "problem with DEFAULT unset: 0.32 bug, OK in 0.321."
+        ${ECHO} -n "ezmlm-tstdig.......   "
+         BUG="${BUG} tstdig"
+       }
+  ${ECHO} "OK"
+
+##############
+# ezmlm-weed #
+##############
+
+  ${ECHO} -n "ezmlm-weed:           "
+
+  ${ECHO} "Subject: test" | ${EZBIN}/ezmlm-weed || \
+       { ${ECHO} "failed to accept good message"; exit 100; }
+  ${ECHO} "Subject: success notice" | ${EZBIN}/ezmlm-weed >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject bad message"; exit 100; }
+
+  ${ECHO} "OK"
+
+##############
+# ezmlm-make #
+##############
+  ${ECHO} -n "ezmlm-make (2/2):     "
+
+# make sure a few ezmlm-make switches work
+  ${EZBIN}/ezmlm-make -+qkgu -C${EZBIN}/ezmlmrc "${DIR}" || \
+       { ${ECHO} "failed to edit test list to +qkgu"; exit 100; }
+  ${GREP} "${DENY}" "${DIR}/editor" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to implement -k for list"; exit 100; }
+  ${GREP} "ezmlm-request" "${DIR}/manager" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to implement -q for list"; exit 100; }
+  ${GREP} "ezmlm-get -s" "${DIR}/manager" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to implement -g for list"; exit 100; }
+  ${GREP} "${ALLOW}" "${DIR}/editor" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to implement -u for list"; exit 100; }
+
+  ${EZBIN}/ezmlm-make -+QKGU -C${EZBIN}/ezmlmrc "${DIR}" || \
+       { ${ECHO} "failed to edit test list to +QKGU"; exit 100; }
+  ${GREP} "${DENY}" "${DIR}/editor" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to implement -K for list"; exit 100; }
+  ${GREP} "ezmlm-request" "${DIR}/manager" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to implement -Q for list"; exit 100; }
+  ${GREP} "ezmlm-get -s" "${DIR}/manager" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to implement -G for list"; exit 100; }
+  ${GREP} "${ALLOW}" "${DIR}/editor" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to implement -U for list"; exit 100; }
+
+# edit the list (add moderation and remove admin)
+  ${EZBIN}/ezmlm-make -+rsm -C${EZBIN}/ezmlmrc "${DIR}" || \
+       { ${ECHO} "failed to edit test list to +rsm"; exit 100; }
+# edit the list (add text file editing and list/log)
+${EZBIN}/ezmlm-make -+ln -C${EZBIN}/ezmlmrc "${DIR}" || \
+       { ${ECHO} "failed to edit test list to +ln"; exit 100; }
+
+# Now to create our own manager for later tests:
+
+${ECHO} "|${GREP} 'req1' >/dev/null 2>&1 && { ${ECHO} \"\$LOCAL\" >> '${REQ}'; exit 99; }; exit 0" > "${DIR}/manager"
+${ECHO} "|${EZBIN}/ezmlm-manage -le ${SW_FROM} '${DIR}'" >> "${DIR}/manager"
+${ECHO} "OK"
+
+# correct bouncer for our binaries:
+###################################
+  ${ECHO} "|/${EZBIN}/ezmlm-weed" > "${DIR}/bouncer"
+  ${ECHO} "|/${EZBIN}/ezmlm-weed" > "${DIR}/digest/bouncer"
+  if [ "$EZVER" = "31" ]; then # autodetecting bouncer for 0.31x
+    ${ECHO} "|/${EZBIN}/ezmlm-return '${DIR}'" >> "${DIR}/bouncer"
+    ${ECHO} "|/${EZBIN}/ezmlm-return '${DIR}'" >> "${DIR}/digest/bouncer"
+  else                         # split bouncer with args for later versions
+    ${ECHO} "|/${EZBIN}/ezmlm-return -D '${DIR}'" >> "${DIR}/bouncer"
+    ${ECHO} "|/${EZBIN}/ezmlm-return -d '${DIR}'" >> "${DIR}/digest/bouncer"
+  fi
+
+# if testing qmail>=1.02, remove inlocal/inhost - shouldn't be used
+  if [ "$QMVER" = "n" ]; then
+       ${RM} -f "${DIR}/inlocal" "${DIR}/inhost" > /dev/null || \
+         { ${ECHO} "failed to remove inlocal/inhost for testlist"; exit 100; }
+  fi
+
+
+###############
+# ezmlm-clean #
+###############
+
+  ${ECHO} -n "ezmlm-clean (1/2):    "
+
+# clean1 should be silently removed (no -x).
+# clean2 should result in a message
+# clean3 should not since it's time hasn't come
+# clean4 should be removed, but not result in a message since we use -R
+
+  ${ECHO} "Return-Path: <${DIG}@$HOST>" > "${DIR}/mod/pending/1"
+  ${ECHO} "X-num: clean1" >> "${DIR}/mod/pending/1"
+  ${ECHO} "Return-Path: <${DIG}@${HOST}>" > "${DIR}/mod/pending/2"
+  ${ECHO} "X-num: clean2" >> "${DIR}/mod/pending/2"
+  ${ECHO} "Return-Path: <${DIG}@$HOST>" > "${DIR}/mod/pending/999999999"
+  ${ECHO} "X-num: clean3" >> "${DIR}/mod/pending/999999999"
+
+  chmod +x "${DIR}/mod/pending/2" "${DIR}/mod/pending/999999999"
+
+  ${EZBIN}/ezmlm-clean "${DIR}" >"${ERR}" 2>&1 ||
+       { ${ECHO} "failed first invocation"; exit 100; }
+  if [ -r "${DIR}/mod/pending/1" ]; then
+       ${ECHO} "failed to remove non-x moderation queue entry 1"
+       exit 100
+  fi
+  if [ -r "${DIR}/mod/pending/2" ]; then
+       ${ECHO} "failed to remove moderation queue entry 2"
+       exit 100
+  fi
+  if [ ! -r "${DIR}/mod/pending/999999999" ]; then
+       ${ECHO} "removed mod queue entry 3 that wasn't due"
+       exit 100
+  fi
+
+${ECHO} <<EOF > "${DIR}/mod/pending/4"
+Return-Path: <${DIG}@$HOST>
+X-num: clean4
+EOF
+  chmod +x "${DIR}/mod/pending/4"
+  ${EZBIN}/ezmlm-clean -R "${DIR}" >"${ERR}" 2>&1 ||
+       { ${ECHO} "-R failed"; exit 100; }
+  if [ -r  "${DIR}/mod/pending/4" ]; then
+       ${ECHO} "failed to remove moderation queue entry 4"; exit 100
+  fi
+
+  ${ECHO} "OK"
+
+###############
+# ezmlm-store #
+###############
+
+  ${ECHO} -n "ezmlm-store (1/2):    "
+
+  SENDER="${SND}@$HOST"; export SENDER
+  ${EZBIN}/ezmlm-sub "${DIR}/mod" "$SENDER"
+
+# message from mod, normal use -> should queue
+  { ${ECHO} "X-num: mod1"; make_message; } > ${TMP};
+       ${EZBIN}/ezmlm-store "${DIR}" >"${ERR}" 2>&1 < ${TMP} || \
+       { ${ECHO} "failed to process message 1"; exit 100; }
+
+  cat ${DIR}/mod/pending/* | ${GREP} "mod1" > /dev/null || \
+       { ${ECHO} "failed to queue message 1"; exit 100; }
+
+  ${RM} -f "${DIR}/modpost" 
+
+# no modpost - should go directly to list
+  { ${ECHO} "X-num: mod2"; make_message; } > ${TMP};
+       ${EZBIN}/ezmlm-store "${DIR}" >"${ERR}" 2>&1 < ${TMP} || \
+       {
+         ${GREP} -v "child" "${ERR}" > /dev/null 2>&1
+         if [ "$?" != "0" ]; then
+           ${ECHO} "Failed to process message mod2"; exit 100
+         else
+           EZFORK='no'
+         fi
+       }
+
+  cat ${DIR}/mod/pending/* | ${GREP} "mod2" > /dev/null && \
+       { ${ECHO} "queued message 2 despite non-modpost"; exit 100; }
+
+  if [ -z "$EZFORK" ]; then
+       cat ${DIR}/archive/0/* | ${GREP} "mod2" > /dev/null || \
+               { ${ECHO} "failed to archive message 2 (non-modpost)"; exit 100; }
+  fi
+
+  touch "${DIR}/modpost"
+
+# from moderator. Should be queued, even with -P
+  { ${ECHO} "X-num: mod3"; make_message; } > ${TMP};
+       ${EZBIN}/ezmlm-store -P "${DIR}" >"${ERR}" 2>&1 < ${TMP} || \
+       { ${ECHO} "-P failed to accept mods post 3"; exit 100; }
+
+  cat ${DIR}/mod/pending/* | ${GREP} "mod3" > /dev/null || \
+       { ${ECHO} "failed to queue message 3"; exit 100; }
+
+  ${EZBIN}/ezmlm-unsub "${DIR}/mod" "$SENDER"
+
+# not from moderator, should be rejected directly with -P
+  { ${ECHO} "X-num: mod4"; make_message; } > ${TMP};
+       ${EZBIN}/ezmlm-store -P "${DIR}" >"${ERR}" 2>&1 < ${TMP} && \
+       { ${ECHO} "-P failed to reject non-mod message 4"; exit 100; }
+
+  ${ECHO} "OK"
+
+################
+# ezmlm-return #
+################
+  ${ECHO} -n "ezmlm-return:         "
+
+  SENDER="${BNC}@$HOST"; export SENDER
+  HOST="$HOST"; export HOST
+  LOCAL="$LOC-return-1-$BNC=$HOST"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT="1-$BNC=$HOST"; export DEFAULT
+  fi
+# we use 'du' because bounce handling is different in 0.31x and >=0.32
+  BSIZE1=`${DU} "${DIR}/bounce"` || \
+       { ${ECHO} "du doesn't work"; exit 99; } 
+  make_message | ${EZBIN}/ezmlm-return "${DIR}" || \
+    [ "$?" -eq "99" ] || \
+       { ${ECHO} "failed to process normal bounce from non-sub" ; exit 100; }
+  BSIZE2=`${DU} "${DIR}/bounce"`
+  if [ "$BSIZE1" != "$BSIZE2" ]; then
+       ${ECHO} "failed to ignore non-subscriber bounce" ; exit 100
+  fi
+  ${EZBIN}/ezmlm-sub "${DIR}" "${BNC}@$HOST"
+  make_message | ${EZBIN}/ezmlm-return "${DIR}" || \
+    [ "$?" -eq "99" ] || \
+       { ${ECHO} "failed to process normal bounce from sub" ; exit 100; }
+  BSIZE1=`${DU} "${DIR}/bounce"`
+  if [ "$BSIZE1" = "$BSIZE2" ]; then
+       ${ECHO} "failed to note subscriber bounce" ; exit 100
+  fi
+  LOCAL="$LOC-digest-return-1-$BNC=$HOST"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT="1-$BNC=$HOST"; export DEFAULT
+  fi
+  BSIZE1=`${DU} "${DIR}/digest/bounce"`
+  make_message | ${EZBIN}/ezmlm-return $DLC "${DIR}" || \
+    [ "$?" -eq "99" ] || \
+       { ${ECHO} "failed to process normal digest non-sub bounce" ; exit 100; }
+  BSIZE2=`${DU} "${DIR}/digest/bounce"`
+  if [ "$BSIZE1" != "$BSIZE2" ]; then
+       ${ECHO} "failed to ignore non-digest-subscriber bounce" ; exit 100
+  fi
+  ${EZBIN}/ezmlm-unsub "${DIR}" "${BNC}@$HOST"
+  ${EZBIN}/ezmlm-sub "${DIR}/digest" "${BNC}@$HOST"
+  make_message | ${EZBIN}/ezmlm-return $DLC "${DIR}" || \
+    [ "$?" -eq "99" ] || \
+       { ${ECHO} "failed to proc. nl digest-subscriber bounce" ; exit 100; }
+  BSIZE1=`${DU} "${DIR}/digest/bounce"`
+  if [ "$BSIZE1" = "$BSIZE2" ]; then
+       ${ECHO} "failed to note digest-subscriber bounce" ; exit 100
+  fi
+  ${EZBIN}/ezmlm-sub "${DIR}" "${BNC}@$HOST"
+
+  ${ECHO} "OK"
+
+# as we exit, the bounce address is subscribed to both list and digest-list
+# and is the SENDER
+
+##############
+# ezmlm-warn #
+##############
+  ${ECHO} -n "ezmlm-warn (1/3):     "
+
+# should send a warning
+  ${EZBIN}/ezmlm-warn -t0 "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed with normal bounce for warning"; exit 100; }
+  ${EZBIN}/ezmlm-issubn "${DIR}" || \
+       { ${ECHO} "script error: SENDER is not a subscriber"; exit 100; }
+
+  ${EZBIN}/ezmlm-warn -d -t0 "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed with digest bounce for warning"; exit 100; }
+  ${EZBIN}/ezmlm-issubn "${DIR}/digest" || \
+       { ${ECHO} "script error: SENDER is not a digest subscriber"; exit 100; }
+
+  ${ECHO} "OK"
+
+################
+# ezmlm-manage #
+################
+  ${ECHO} -n "ezmlm-manage (1/4):   "
+
+  LOCAL="$LOC-unsubscribe"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='unsubscribe'; export DEFAULT
+  fi
+  SENDER="${SND}@$HOST"; export SENDER
+
+  ${EZBIN}/ezmlm-sub "${DIR}" "${SND}@$HOST"
+  ${EZBIN}/ezmlm-manage -U "${DIR}" </dev/null >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed with -U"; exit 100; }
+  ${EZBIN}/ezmlm-issubn "${DIR}" && \
+       { ${ECHO} "unsubscribe with -U failed"; exit 100; }
+
+  LOCAL="$LOC-digest-subscribe"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='digest-subscribe'; export DEFAULT
+  fi
+  ${EZBIN}/ezmlm-unsub "${DIR}/digest" "${SND}@$HOST"
+
+# test that access to the deny db is restricted to remote admins
+  LOCAL="$LOC-deny-subscribe"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='deny-subscribe'; export DEFAULT
+  fi
+  ${EZBIN}/ezmlm-manage "${DIR}" </dev/null >/dev/null 2>&1 && \
+       {
+        ${ECHO} "Deny open to regular subscribers: 0.31 bug, OK in 0.321."
+        ${ECHO} -n "ezmlm-manage ...      "
+        BUG="${BUG} deny"
+       }
+  SENDER="${MOD}@$HOST"; export SENDER
+  ${EZBIN}/ezmlm-sub "${DIR}/mod" "$SENDER" || exit 100
+  ${EZBIN}/ezmlm-manage "${DIR}" </dev/null > "${ERR}" 2>&1 || \
+       { ${ECHO} "Deny access denied to remote admin!"; exit 100; }
+
+# make non-moderated
+  ${RM} -f "${DIR}/modsub" || \
+       { ${ECHO} "Failed to remove DIR/modsub"; exit 99; }
+
+# make non-remote
+  ${RM} -f "${DIR}/remote" || \
+       { ${ECHO} "Failed to remove DIR/remote"; exit 99; }
+  ${EZBIN}/ezmlm-manage "${DIR}" </dev/null > "${ERR}" 2>&1 && \
+       {
+        ${ECHO} "Deny even without remote/modsub: 0.31 bug, OK in 0.321."
+        ${ECHO} -n "ezmlm-manage ...      "
+        BUG="${BUG} deny"
+       }
+
+# restore remote/SENDER/mod/LOCAL/DEFAULT
+  ${EZBIN}/ezmlm-unsub "${DIR}/mod" "$SENDER" || exit 100
+  SENDER="${SND}@$HOST"; export SENDER # restore order
+  touch "${DIR}/remote" || \
+       { ${ECHO} "Failed to remove DIR/remote"; exit 99; }
+  LOCAL="$LOC-digest-subscribe"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='digest-subscribe'; export DEFAULT
+  fi
+
+  ${EZBIN}/ezmlm-manage -S "${DIR}" </dev/null >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed with -S"; exit 100; }
+  ${EZBIN}/ezmlm-issubn "${DIR}/digest" || \
+       { ${ECHO} "digest-subscribe with -S failed"; exit 100; }
+  ${EZBIN}/ezmlm-unsub "${DIR}/digest" "${SND}@$HOST"
+  touch "${DIR}/modsub" || \
+       { ${ECHO} "Failed to restore DIR/modsub"; exit 99; }
+
+  SENDER="${MAN}@$HOST"; export SENDER
+
+  ${ECHO} "X-num: sub1" > "${DIR}/__tmp"
+  ${ECHO} "From: Mr. $EZTEST requests <${MAN}@$HOST>" >> "${DIR}/__tmp"
+  ${ECHO} >> "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-manage ${SW_FROM} "${DIR}" < "${DIR}/__tmp" \
+               >"${ERR}" 2>&1 || \
+       { ${ECHO} "digest-subscribe with request failed"; exit 100; }
+
+  ${EZBIN}/ezmlm-sub "${DIR}" "${MAN}@$HOST"
+  LOCAL="$LOC-unsubscribe"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='unsubscribe'; export DEFAULT
+  fi
+  ${ECHO} "X-num: sub2" > "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-manage "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "unsubscribe request failed"; exit 100; }
+
+# -get function for backwards compatibility
+  LOCAL="$LOC-get.1"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='get.1'; export DEFAULT
+  fi
+  ${ECHO} "X-num: manget1" > "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-manage "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "get failed"; exit 100; }
+# -C should disable it
+  ${EZBIN}/ezmlm-manage -C "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 && \
+       { ${ECHO} "-C failed to disable -get"; exit 100; }
+
+  ${ECHO} "OK"
+
+#################
+# ezmlm-request #
+#################
+  ${ECHO} -n "ezmlm-request (1/2):  "
+
+  SENDER="${SND}@$HOST"; export SENDER
+  LOCAL="$LOC-request"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT='request'; export DEFAULT
+  fi
+
+  ${ECHO} "X-num: req1" > "${DIR}/__tmp"
+# use a non-existing command
+  ${ECHO} "Subject: qqqq ${SND}@$HOST" >> "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-request "${DIR}" < "${DIR}/__tmp" > "${ERR}" 2>&1
+  if [ "$?" != "99" ]; then
+       ${ECHO} "qqqq command in subject failed to exit 99"
+       exit 100
+  fi
+
+  ${ECHO} "X-num: req1" > "${DIR}/__tmp"
+# test full ezmlm cmd in subject and command substitution
+  ${ECHO} "Subject: ${LOC}-remove-${SND}=${HOST}@${HOST}" >> "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-request "${DIR}" < "${DIR}/__tmp" > "${ERR}" 2>&1
+  if [ "$?" != "99" ]; then
+       ${ECHO} "full ezmlm command in subject failed to exit 99"
+       exit 100
+  fi
+
+
+
+  ${ECHO} "OK"
+
+###############
+# ezmlm-split #
+###############
+if [ "$QMVER" = "n" ]; then
+  ${ECHO} -n "ezmlm-split (1/2):    "
+# set up split file
+  ${ECHO} "edu:1:26:l1@h1" > "${DIR}/split"
+  ${ECHO} "edu:27:52:l2@h2" >> "${DIR}/split"
+  ${ECHO} "com:::l3@h3" >> "${DIR}/split"
+# most testing with -D
+  ${ECHO} "lindberg@ezmlm.org" | ${EZBIN}/ezmlm-split -D "${DIR}" | \
+       ${GREP} "$LIST@$HOST" >/dev/null || \
+       { ${ECHO} "failed to split correctly on domain"; exit 100; }
+  ${ECHO} "lindberg@id.com" | ${EZBIN}/ezmlm-split -D "${DIR}" | \
+       ${GREP} 'l3' >/dev/null || \
+       { ${ECHO} "failed to split correctly on domain"; exit 100; }
+  ${ECHO} "lindberg@id.wustl.edu" | ${EZBIN}/ezmlm-split -D "${DIR}" | \
+       ${GREP} 'l1' >/dev/null || \
+       { ${ECHO} "failed to split correctly on hash + domain"; exit 100; }
+  ${ECHO} "cfl@id.wustl.edu" | ${EZBIN}/ezmlm-split -D "${DIR}" | \
+       ${GREP} 'l2' >/dev/null || \
+       { ${ECHO} "failed to split correctly on hash + domain"; exit 100; }
+# one test with delivery - redirect to local manager
+# should exit 99 after redirecting
+  ${ECHO} ":::${LOC}@$HOST" > "${DIR}/split"
+  SENDER="${MOD}@$HOST"; export SENDER
+  DTLINE="Delivered-To: ezmlm-split@$HOST"; export DTLINE
+  LOCAL="$LOC-subscribe-${SND}=$HOST"; export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT="subscribe-${SND}=$HOST"; export DEFAULT
+  fi
+  ${ECHO} "X-num: spl1" | ${EZBIN}/ezmlm-split "${DIR}" >"${ERR}" 2>&1
+
+  EC="$?"
+  if [ "$EC" -eq "0" ]; then
+       ${ECHO} "exited 0 after forwarding, rather than 99"; exit 100
+  elif [ "$EC" != "99" ]; then
+       ${ECHO} "failed to process message for forwarding"; exit 100
+  fi
+# if no match, should exit 0
+  ${ECHO} "___:::${LOC}@$HOST" > "${DIR}/split"
+  ${ECHO} "X-num: spl1" | ${EZBIN}/ezmlm-split "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to exit 0 after failing to match"; exit 100; }
+
+  ${ECHO} "OK"
+fi
+
+########################
+# waiting for delivery #
+########################
+  send_test 2
+fi             # end of sect 1
+
+####################################### start of section 2
+
+if [ "$SECT" -le "2" ]; then
+  wait_test 2
+
+#############
+# ezmlm-idx #
+#############
+  ${ECHO} -n "ezmlm-idx:            "
+  ${RM} -f "${DIR}/archive/0/index" "${DIR}/indexed"
+  ${EZBIN}/ezmlm-idx "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to run"; exit 100; }
+  if [ ! -r "${DIR}/indexed" ]; then
+       ${ECHO} "failed to create DIR/indexed"; exit 100
+  fi
+  if [ ! -r "${DIR}/archive/0/index" ]; then
+       ${ECHO} "failed to create index"; exit 100
+  fi 
+  ${ECHO} "OK"
+
+#############
+# ezmlm-get #
+#############
+${ECHO} -n "ezmlm-get (1/2):      "
+
+# blast digest recipient account with all these excerpts.
+${EZBIN}/ezmlm-sub "${DIR}/digest" "${DIG}@$HOST"
+
+# first ezmlm-get in the manager position:
+
+# index1/get1/thread1 should bounce and will not be looked for
+# index2 ... should be in DIG@HOST's inbox
+# get3 - r format to DIG@HST
+# get4 - n
+# get5 - v
+# get6 - x
+
+SENDER="${BNC}@$HOST"; export SENDER
+LOCAL="$LOC-xxxx"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='xxxx'; export DEFAULT
+fi
+${ECHO} "X-num: index1" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} " failed to exit 0 for non-recognized commands"; exit 100; }
+
+# This should not give a digest
+LOCAL="$LOC-"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=''; export DEFAULT
+fi
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} " failed to exit 0 for list-@host"; exit 100; }
+
+LOCAL="$LOC-index"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='index'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get -s "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "-s failed to reject -index from non-sub"; exit 100; }
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" -ne "99" ]; then
+       ${ECHO} "failed to exit 99 after -index"
+       exit 100
+fi
+
+${ECHO} "X-num: index2" > "${DIR}/__tmp"
+SENDER="${DIG}@$HOST"; export SENDER
+${EZBIN}/ezmlm-get -s "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" -ne "99" ]; then
+       ${ECHO} "-s failed to exit 99 after -index"
+       exit 100
+fi
+
+SENDER="${BNC}@$HOST"; export SENDER
+${ECHO} "X-num: get1" > "${DIR}/__tmp"
+LOCAL="$LOC-get.2_4"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='get.2_4'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get -s "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "-s failed to reject -get from non-sub"; exit 100; }
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after -get"
+       exit 100
+fi
+${ECHO} "X-num: get2" > "${DIR}/__tmp"
+SENDER="${DIG}@$HOST"; export SENDER
+${EZBIN}/ezmlm-get -s "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "-s failed to exit 99 after -get"
+       exit 100
+fi
+
+# test formats for -get
+${ECHO} "X-num: get3" > "${DIR}/__tmp"
+LOCAL="$LOC-getr.2_4"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='getr.2_4'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after -getr"
+       exit 100
+fi
+${ECHO} "X-num: get4" > "${DIR}/__tmp"
+LOCAL="$LOC-getn.2_4"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='getn.2_4'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after -getn"
+       exit 100
+fi
+
+${ECHO} "X-num: get5" > "${DIR}/__tmp"
+LOCAL="$LOC-getv.2_4"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='getv.2_4'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after -getv"
+       exit 100
+fi
+
+${ECHO} "X-num: get6" > "${DIR}/__tmp"
+LOCAL="$LOC-getx.2_4"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='getx.2_4'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after -getx"
+       exit 100
+fi
+
+SENDER="${BNC}@$HOST"; export SENDER
+LOCAL="$LOC-index"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='index'; export DEFAULT
+fi
+${ECHO} "X-num: thread1" > "${DIR}/__tmp"
+LOCAL="$LOC-thread.1"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='thread.1'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get -s "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "-s failed to reject -thread from non-sub"; exit 100; }
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after -thread"
+       exit 100
+fi
+${ECHO} "X-num: thread2" > "${DIR}/__tmp"
+SENDER="${DIG}@$HOST"; export SENDER
+${EZBIN}/ezmlm-get -s "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "-s failed to exit 99 after -thread"
+       exit 100
+fi
+
+######### digests
+# we use headeradd to label them since trigger headers aren't propagated
+${ECHO} "X-num: not_propagated" > "${DIR}/__tmp"
+
+# dig1 from manager will go to DIG@HOST
+# dig2 from editor
+# dig3 from command line
+# dig4 -fr format check from command line. We check only that they get there.
+# dig5 -fn
+# dig6 -fx
+# dig7 -fv
+# we check that dignum is created and digissue is updated 
+
+# now -dig in the manager position:
+mv -f "${DIR}/headeradd" "${DIR}/headeradd.bak"
+${ECHO} "X-num: dig1" > "${DIR}/headeradd"
+SENDER="${BNC}@$HOST"; export SENDER
+LOCAL="$LOC-dig.code"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT='dig.code'; export DEFAULT
+fi
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject -dig when no digest code was on cmd-line"
+         exit 100
+       }
+if [ -r "${DIR}/dignum" ]; then
+       ${ECHO} "script error: dignum exists"; exit 100
+fi
+${EZBIN}/ezmlm-get "${DIR}" 'code' < "${DIR}/__tmp" >"${ERR}" 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 after digest in manager position"
+       exit 100
+fi
+if [ ! -r "${DIR}/dignum" ]; then
+       ${ECHO} "failed to generate dignum"; exit 100
+fi
+if [ ! -r "${DIR}/digissue" ]; then
+       ${ECHO} "failed to generate digissue"; exit 100
+fi
+${EZBIN}/ezmlm-get "${DIR}" 'code' < "${DIR}/__tmp" >/dev/null 2>&1
+if [ "$?" != "99" ]; then
+       ${ECHO} "failed to exit 99 when nothing to digest in manager position"
+       exit 100
+fi
+
+${EZBIN}/ezmlm-get "${DIR}" 'coden' < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject -dig with bad digest code 'coden'"; exit 100; }
+${EZBIN}/ezmlm-get "${DIR}" 'cod' < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject -dig with bad digest code 'cod'"; exit 100; }
+
+# now in the editor position:
+${RM} -f "${DIR}/dignum"
+LOCAL="$LOC"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       ${UNSET} DEFAULT
+fi
+${ECHO} "X-num: dig2" > "${DIR}/headeradd"
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to exit 0 after digest in editor"; exit 100; }
+
+# This causes an error on systems where 'unset' doesn't work
+# For these, we skip this test.
+  if [ -z "$BADUNSET" ]; then
+    if [ ! -r "${DIR}/dignum" ]; then
+       ${ECHO} "failed to generate dignum after digest in editor"; exit 100
+    fi
+
+    ${GREP} "2:" "${DIR}/digissue" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to update digissue after digest in editor";
+         exit 100; }
+    ${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to exit 0 when nothing to digest in editor";
+         exit 100; }
+  fi
+
+# now from the command line with formats ...
+${RM} -f "${DIR}/dignum"
+LOCAL=''; export LOCAL
+${ECHO} "X-num: dig3" > "${DIR}/headeradd"
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to exit 0 after cmd line digest"; exit 100; }
+${GREP} "3:" "${DIR}/digissue" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to update digissue after cmd line digest"; exit 100; }
+${EZBIN}/ezmlm-get "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to exit 0 when nothing to digest from cmd line"
+       exit 100; }
+${RM} -f "${DIR}/dignum"
+${ECHO} "X-num: dig4" > "${DIR}/headeradd"
+${EZBIN}/ezmlm-get -fr "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "-fr failed for digest"; exit 100; }
+${RM} -f "${DIR}/dignum"
+${ECHO} "X-num: dig5" > "${DIR}/headeradd"
+${EZBIN}/ezmlm-get -fn "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "-fn failed for digest"; exit 100; }
+${RM} -f "${DIR}/dignum"
+${ECHO} "X-num: dig6" > "${DIR}/headeradd"
+${EZBIN}/ezmlm-get -fv "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "-fv failed for digest"; exit 100; }
+${RM} -f "${DIR}/dignum"
+${ECHO} "X-num: dig7" > "${DIR}/headeradd"
+${EZBIN}/ezmlm-get -fx "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "-fx failed for digest"; exit 100; }
+
+# restore headeradd
+mv -f "${DIR}/headeradd.bak" "${DIR}/headeradd"
+
+${ECHO} "OK"
+
+##############
+# ezmlm-send #
+##############
+${ECHO} -n "ezmlm-send (2/2):     "
+MSG1=`${GREP} -l "msg1" $SINKDIR/new/*` || \
+       { ${ECHO} "failed to deliver message 1 to subscriber"; \
+       exit 100; }
+# make sure headeradd was done
+  ${GREP} -i 'precedence: bulk' < "$MSG1" >/dev/null 2>&1 ||
+       { ${ECHO} "failed to add headeradd"; exit 100; }
+# check on received: header handling
+${GREP} '#PENULTIMATE#' "$MSG1" >/dev/null && \
+       { ${ECHO} "-r failed to remove received header"; \
+       exit 100; }
+${GREP} '#LAST#' "$MSG1" >/dev/null || \
+       { ${ECHO} "-r failed to leave last received header"; \
+       exit 100; }
+${GREP} 'Subject:' "$MSG1" | ${GREP} 'PFX' >/dev/null 2>&1 || \
+       { ${ECHO} "failed to add subject prefix"; exit 100; }
+       # the trailer should be a MIME part, so not at the very end
+${TAIL} -6 "$MSG1" | ${HEAD} -2 | ${GREP} 'TRAILER' >/dev/null 2>&1 || \
+       { ${ECHO} "failed to add trailer"; exit 100; }
+
+MSG2=`${GREP} -l "msg2" $SINKDIR/new/*` || \
+       { ${ECHO} "failed to deliver message 2 to subscriber"; \
+       exit 100; }
+${GREP}  '#PENULTIMATE#' "$MSG2" >/dev/null || \
+       { ${ECHO} "-R failed to leave received header"; \
+       exit 100; }
+
+${GREP} "msg3" $SINKDIR/new/* >/dev/null 2>&1 && \
+       { ${ECHO} "-C failed to exclude sender (no longer supported)"; \
+         BUG="${BUG}_noself"; \
+         echo -n "ezmlm-send:           "; }
+
+MSG5=`${GREP} -l "msg5" $SINKDIR/new/*` || \
+       { ${ECHO} "failed to deliver message 5 to subscriber"; \
+       exit 100; }
+${GREP} 'TRAILER' "$MSG5" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to add trailer to non-mime message"; \
+       exit 100; }
+
+MSG6=`${GREP} -l "msg6" $SINKDIR/new/*` || \
+       { ${ECHO} "failed to deliver message 6 to subscriber"; \
+       exit 100; }
+
+${GREP} 'TRAILER' "$MSG6" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to suppress trailer for multipart/signed message"; \
+         echo "                      0.31 bug fixed in 0.316/0.323";
+         BUG="${BUG}_signed"; \
+         echo -n "ezmlm-send ......:    "; }
+
+${GREP} "msg3" $SINKDIR/new/* >/dev/null 2>&1 && \
+       { 
+         ${ECHO} "${BUG}" | ${GREP} 'noself' >/dev/null 2>&1 || \
+         {
+           ${ECHO} "-C failed to exclude sender (no longer supported)"
+           BUG="${BUG}_noself"
+           echo -n "ezmlm-send ......:   ${BUG} "
+         }
+       }
+
+${ECHO} "OK"
+###############
+# ezmlm-clean #
+###############
+
+${ECHO} -n "ezmlm-clean (2/2):    "
+
+${GREP} "clean1" ${DIGDIR}/new/* >/dev/null 2>&1 && \
+       { ${ECHO} "removal of non-x mod queue entry 1 wasn't silent"; exit 100; }
+${GREP} "clean2" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "failed to notify sender of mod queue entry 2 time out"
+         exit 100
+       }
+${GREP} "clean3" ${DIGDIR}/new/* >/dev/null 2>&1 && \
+       { ${ECHO} "notified sender about entry 3 even though it wasn't rejected"
+         exit 100
+       }
+${GREP} "clean4" ${DIGDIR}/new/* >/dev/null 2>&1 && \
+       { ${ECHO} "-R failed: notified sender about entry 3 rejection"; exit 100; }
+
+
+# clean1 should be silently removed (no -x).
+# clean2 should result in a message
+# clean3 should not since it's time hasn't come
+# clean4 should be removed, but not result in a message since we use -R
+
+${ECHO} "OK"
+
+###############
+# ezmlm-store #
+###############
+${ECHO} -n "ezmlm-store (2/2):    "
+
+MOD1=`${GREP} -l "mod1" $SINKDIR/new/* 2>/dev/null`
+if [ -z "$MOD1" ]; then
+       ${ECHO} "ezmlm-store: failed to deliver mod request to moderator"
+       exit 100
+fi
+${GREP} "mod2" $SINKDIR/new/* >/dev/null && \
+       { ${ECHO} "ezmlm-store: didn't post directly in absence of DIR/modpost"; \
+       exit 100; }
+MOD3=`${GREP} -l "mod3" $SINKDIR/new/* 2>/dev/null`
+if [ -z "$MOD3" ]; then
+       ${ECHO} "ezmlm-store: -P failed to deliver mod request to moderator"
+       exit 100
+fi
+${GREP} "mod4" $SINKDIR/new/* >/dev/null && \
+       { ${ECHO} "ezmlm-store: -P failed to reject message from non-mod"; \
+       exit 100; }
+
+${ECHO} "OK"
+
+################
+# ezmlm-manage #
+################
+${ECHO} -n "ezmlm-manage (2/4):   "
+
+# check digest-subscribe and list-unsubscribe replies
+SUB1=`${GREP} -l 'sub1' $MANDIR/new/*` || \
+       { ${ECHO} "failed getting digest-subscribe confirm request"; exit 100; }
+
+SUB2=`${GREP} -l 'sub2' $MANDIR/new/*` || \
+       { ${ECHO} "failed getting -unsubscribe confirm request"; exit 100; }
+
+# Check -get.1 reply
+MANGET1=`${GREP} -l 'manget1' $MANDIR/new/*` || \
+       { ${ECHO} "failed getting -get.1 reply"; exit 100; }
+
+${GREP} 'msg1' "$MANGET1" >/dev/null || \
+       { ${ECHO} "get.1 failed to return archived message"; exit 100; }
+
+# Add moderator
+${EZBIN}/ezmlm-sub "${DIR}/mod" "${MOD}@$HOST"
+
+LOCAL=`${GREP} "Reply-To:" "$SUB1" | cut -d' ' -f2 | cut -d'@' -f1` || \
+       { ${ECHO} "failed to find confirm address in -subscribe reply"; exit 100; }
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$LOCLEN"-`; export DEFAULT
+fi
+${ECHO} "X-num: sub3" > "${DIR}/__tmp"
+${ECHO} "From: Mr. $EZTEST confirms <$SENDER>" >> "${DIR}/__tmp"
+${ECHO} >> "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage ${SW_FROM} "${DIR}" < "${DIR}/__tmp" \
+               >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed to send user conf for sub1"; exit 100; }
+
+LOCAL=`${GREP} "Reply-To:" "$SUB2" | cut -d' ' -f2 | cut -d'@' -f1` || \
+       { ${ECHO} "failed to find confirm address in -unsubscribe reply"
+         exit 100; }
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$LOCLEN"-`; export DEFAULT
+fi
+${ECHO} "X-num: sub4" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to send conf for sub2"; exit 100; }
+
+# now test remote admin functions
+# add a few addresses to allow
+${EZBIN}/ezmlm-sub "${DIR}/${ALLOW}" "aaa@bbb" "ccc@ddd" "eee@fff"
+
+# test -edit
+${ECHO} "#TEST_TEXT#" > "${DIR}/text/test"
+LOCAL="$LOC-edit.test-$MAN=$HOST"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT="edit.test-$MAN=$HOST"; export DEFAULT
+fi
+${ECHO} "X-num: edit1" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage -e "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject edit request from non-mod"; exit 100; }
+LOCAL="$LOC-edit.test-$MOD=$HOST"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT="edit.test-$MOD=$HOST"; export DEFAULT
+fi
+${ECHO} "X-num: edit2" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 && \
+       { ${ECHO} "-E failed for edit2"; exit 100; }
+${ECHO} "X-num: edit3" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage -e "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "-e failed for remote admin for edit3"; exit 100; }
+
+# test list/log
+LOCAL="$LOC-allow-list-$MAN=$HOST"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT="allow-list-$MAN=$HOST"; export DEFAULT
+fi
+${ECHO} "X-num: list1" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage -l "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject list request from non-mod"; exit 100; }
+
+LOCAL="$LOC-allow-log-$MAN=$HOST"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT="allow-log-$MAN=$HOST"; export DEFAULT
+fi
+${ECHO} "X-num: log1" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage -l "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "failed to reject log request from non-mod"; exit 100; }
+
+LOCAL="$LOC-allow-list-$MOD=$HOST"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT="allow-list-$MOD=$HOST"; export DEFAULT
+fi
+${ECHO} "X-num: list2" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 && \
+       { ${ECHO} "-L failed to reject list request"; exit 100; }
+
+${ECHO} "X-num: list3" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage -l "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "-l failed for remote admin for list3"; exit 100; }
+
+LOCAL="$LOC-allow-log-$MOD=$HOST"; export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT="allow-log-$MOD=$HOST"; export DEFAULT
+fi
+${ECHO} "X-num: log2" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 && \
+       { ${ECHO} "-L failed to reject log request"; exit 100; }
+
+${ECHO} "X-num: log3" > "${DIR}/__tmp"
+${EZBIN}/ezmlm-manage -l "${DIR}" < "${DIR}/__tmp" >"${ERR}" 2>&1 || \
+       { ${ECHO} "-l failed for remote admin for log3"; exit 100; }
+
+
+${ECHO} "OK"
+
+##################
+# ezmlm-moderate #
+##################
+
+${ECHO} -n "ezmlm-moderate (1/2): "
+
+# MOD1 and MOD3 are defined from ezmlm-store testing
+
+REJ=`${GREP} "From: $LOC-reject" "$MOD1"| cut -d' ' -f2`
+if [ -z "$REJ" ]; then
+       ${ECHO} "No From: ...-reject header in mod request for mod1"
+       exit 100
+fi
+
+ACC=`${GREP} "Reply-To: $LOC-accept" "$MOD3"| cut -d' ' -f2`
+if [ -z "$ACC" ]; then
+       ${ECHO} "No From: ...-accept header in mod request for mod3"
+       exit 100
+fi
+
+# remove moderation request from sinkdir
+${RM} -f "$MOD1" 2>/dev/null || \
+       { ${ECHO} "failed to remove mod request for mod1"; exit 100; }
+${RM} -f "$MOD3" 2>/dev/null || \
+       { ${ECHO} "failed to remove mod request for mod3"; exit 100; }
+
+# make sure we get the (mis)accepted message(s)
+${EZBIN}/ezmlm-sub "${DIR}" "${SND}@$HOST"
+
+LOCAL=`${ECHO} "$REJ" | cut -d@ -f1`
+export LOCAL
+
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$REJLEN"-`; export DEFAULT
+fi
+${EZBIN}/ezmlm-moderate "${DIR}" "${EZBIN}/ezmlm-send ${DIR}" \
+       </dev/null >"${ERR}" 2>&1 || \
+               { ${ECHO} "failed on rejection"; exit 100; }
+
+LOCAL=`${ECHO} "$ACC" | cut -d@ -f1`
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$ACCLEN"-`; export DEFAULT
+fi
+${EZBIN}/ezmlm-moderate "${DIR}" "${EZBIN}/ezmlm-send ${DIR}" \
+        </dev/null >"${ERR}" 2>&1 || \
+               { ${ECHO} "failed on acceptance"; exit 100; }
+
+ls -l "${DIR}/mod/rejected/" | ${GREP} '[0-9]' >/dev/null 2>&1 || \
+       { ${ECHO} "failed to write reject stub"; exit 100; }
+ls -l "${DIR}/mod/accepted/" | ${GREP} '[0-9]' >/dev/null 2>&1 || \
+       { ${ECHO} "failed to write accept stub"; exit 100; }
+
+REJ1=`${ECHO} "$REJ" | sed s/reject/accept/`
+LOCAL=`${ECHO} "$REJ1" | cut -d@ -f1`
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$REJLEN"-`; export DEFAULT
+fi
+${EZBIN}/ezmlm-moderate "${DIR}" "${EZBIN}/ezmlm-send ${DIR}" \
+       </dev/null >/dev/null 2>&1 && \
+       { ${ECHO} "failed to bounce accept of rejected message"; exit 100; }
+LOCAL=`${ECHO} "$REJ" | cut -d@ -f1`
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$REJLEN"-`; export DEFAULT
+fi
+${EZBIN}/ezmlm-moderate "${DIR}" "${EZBIN}/ezmlm-send ${DIR}" \
+       </dev/null >/dev/null 2>&1 || \
+       { ${ECHO} "failed to silently ignore re-rejection"; exit 100; }
+
+ACC1=`${ECHO} "$ACC" | sed s/accept/reject/`
+LOCAL=`${ECHO} "$ACC1" | cut -d@ -f1`
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$REJLEN"-`; export DEFAULT
+fi
+${EZBIN}/ezmlm-moderate "${DIR}" "${EZBIN}/ezmlm-send ${DIR}" \
+       </dev/null >/dev/null 2>&1 && \
+       { ${ECHO} "failed to bounce reject of accepted message"; exit 100; }
+LOCAL=`${ECHO} "$ACC" | cut -d@ -f1`
+export LOCAL
+if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$ACCLEN"-`; export DEFAULT
+fi
+${EZBIN}/ezmlm-moderate "${DIR}" "${EZBIN}/ezmlm-send ${DIR}" \
+       </dev/null >/dev/null 2>&1 || \
+       { ${ECHO} "failed to silently ignore re-acceptance"; exit 100; }
+
+${ECHO} "OK"
+
+# cleanup
+${EZBIN}/ezmlm-unsub "${DIR}" "${SND}@$HOST"
+
+##############
+# ezmlm-warn #
+##############
+${ECHO} -n "ezmlm-warn (2/3):     "
+
+${EZBIN}/ezmlm-warn -t0 "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed with normal bounce for warning"; exit 100; }
+
+${EZBIN}/ezmlm-warn -d -t0 "${DIR}" >"${ERR}" 2>&1 || \
+       { ${ECHO} "failed with digest bounce for warning"; exit 100; }
+
+${ECHO} "OK"
+
+#################
+# ezmlm-request #
+#################
+
+  ${ECHO} -n "ezmlm-request (2/2):  "
+
+  ${GREP} "$LOC-qqqq-$SND=$HOST" "${REQ}" >/dev/null || \
+       { ${ECHO} "'qqqq' subject query rewriting failed"; exit 100; }
+
+  ${GREP} "$LOC-unsubscribe-$SND=$HOST" "${REQ}" >/dev/null || \
+       { ${ECHO} "ezmlm 'remove' subject query rewriting failed"; exit 100; }
+
+  ${ECHO} "OK"
+
+########################
+# waiting for delivery #
+########################
+  send_test 3
+fi             # end section 2
+
+######################################### start of section 3
+if [ "$SECT" -le "3" ]; then
+  wait_test 3
+
+###############
+# ezmlm-split #
+###############
+if [ "$QMVER" = "n" ]; then
+  ${ECHO} -n "ezmlm-split (2/2):    "
+
+# we know that ezmlm-manage works. A bounce would go to MODDIR, so a
+# message in SINKDIR means that the request was forwarded to ezmlm-manage,
+# which replied with a confirmation request.
+  ${GREP} 'X-num: spl1' $SINKDIR/new/* > /dev/null 2>&1 || \
+       { ${ECHO} "failed to receive sub conf req.";
+         ${ECHO} "this could be a failure of ezmlm-split, but usually,";
+         ${ECHO} "it happens because ezmlm binaries when run by qmail";
+         ${ECHO} "don't have access to shared libraries required for";
+         ${ECHO} "RDBMS access. This happens on systems where RDBMS";
+         ${ECHO} "shared libs are installed in the /usr/local hierarchy.";
+         ${ECHO} "fix: see ld.so man page on how to modify /etc/ld.so.conf";
+         ${ECHO} "or compile statically by adding -static to conf-sqlld.";
+         ${ECHO}
+         exit 100; }
+
+  ${ECHO} "OK"
+fi
+
+##################
+# ezmlm-moderate #
+##################
+
+  ${ECHO} -n "ezmlm-moderate (2/2): "
+
+  MOD1=`${GREP} -l "mod1" $SINKDIR/new/* | head -1` || \
+       { ${ECHO} "failed to send rejection notice for message mod1"; exit 100; }
+
+# ${SND}@$HOST means it was rejected, not send through the list
+  ${GREP} "To: ${SND}@$HOST" "$MOD1" > /dev/null 2>&1 || \
+       { ${ECHO} "failed to reject message mod1"; exit 100; }
+
+  MOD3=`${GREP} -l "mod3" $SINKDIR/new/* | head -1`
+  if [ -z "$MOD3" ]; then
+    ${ECHO} "failed to post message mod3"
+    exit 100
+  fi
+
+# ${LOC}@$HOST means it was not rejected, but sent through the list
+  ${GREP} "To: ${LOC}@$HOST" "$MOD3" > /dev/null 2>&1 || \
+       { ${ECHO} "failed to reject message mod3"; exit 100; }
+
+  ${ECHO} "OK"
+
+################
+# ezmlm-manage #
+################
+  ${ECHO} -n "ezmlm-manage (3/4):   "
+
+  SENDER="${MOD}@$HOST"; export SENDER
+  ${EZBIN}/ezmlm-issubn "${DIR}" && \
+       { ${ECHO} "unsub without mod for moderated list failed"; exit 100; }
+
+  SUB3=`${GREP} -l 'sub3' $MODDIR/new/*` || \
+       { ${ECHO} "failed getting subscribe moderation confirm request"; \
+        exit 100; }
+
+# confirm subscription request
+  LOCAL=`${GREP} "Reply-To:" "$SUB3" | cut -d' ' -f2 | cut -d'@' -f1` || \
+       { ${ECHO} "no confirm address in sub3 mod confirm request"; exit 100; }
+  export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$LOCLEN"-`; export DEFAULT
+  fi
+  ${ECHO} "X-num: modR1" > "${DIR}/__tmp"
+  ${ECHO} "FROM: moderator agrees <$SENDER>" >> "${DIR}/__tmp"
+  ${ECHO} >> "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-manage ${SW_FROM} "${DIR}" < "${DIR}/__tmp"\
+               >/dev/null 2>&1 || \
+       { ${ECHO} "failed to send digest sub mod accept for sub3"; exit 100; }
+
+# complete edit. SENDER can be any address
+  SENDER="${MAN}@$HOST"; export SENDER
+  EDIT3=`${GREP} -l 'edit3' $MODDIR/new/*` || \
+       { ${ECHO} "failed getting edit reply for edit3"; \
+        exit 100; }
+  ${GREP} "#TEST_TEXT#" "$EDIT3" >/dev/null 2>&1 || \
+       { ${ECHO} "old text missing in edit3 edit reply"; exit 100; }
+  LOCAL=`${GREP} "Reply-To:" "$EDIT3" | cut -d' ' -f2 | cut -d'@' -f1` || \
+       { ${ECHO} "no reply address in edit3 edit reply"; exit 100; }
+  export LOCAL
+  if [ "$QMVER" = "n" ]; then
+       DEFAULT=`${ECHO} "$LOCAL" | cut -c"$LOCLEN"-`; export DEFAULT
+  fi
+  ${ECHO} "X-num: edit4" > "${DIR}/__tmp"
+  ${ECHO} >> "${DIR}/__tmp"
+  ${ECHO} "%%% START OF TEXT FILE" >> "${DIR}/__tmp"
+  ${ECHO} "#NEW_TEXT#" >> "${DIR}/__tmp"
+  ${ECHO} "%%% END OF TEXT FILE" >> "${DIR}/__tmp"
+  ${EZBIN}/ezmlm-manage -e "${DIR}" < "${DIR}/__tmp" >/dev/null 2>&1 || \
+       { ${ECHO} "failed to send edit4 reply for edit3"; exit 100; }
+
+# check results of log/list
+  LOG3=`${GREP} -l 'log3' $MODDIR/new/*` || \
+       { ${ECHO} "failed getting -log reply to log3"; \
+        exit 100; }
+  ${GREP} "aaa@bbb" "$LOG3" | ${GREP} "+m" > /dev/null 2>&1 || \
+       { ${ECHO} "failed to get log reply to log3"; exit 100; }
+
+  LIST3=`${GREP} -l 'list3' $MODDIR/new/*` || \
+       { ${ECHO} "failed getting -list reply to list3"; \
+        exit 100; }
+  ${GREP} "aaa@bbb" "$LIST3" > /dev/null 2>&1 || \
+       { ${ECHO} "failed to get list reply to list3"; exit 100; }
+
+  ${ECHO} "OK"
+
+#############
+# ezmlm-get #
+#############
+  ${ECHO} -n "ezmlm-get (2/2):      "
+
+# index1/get1/thread1 should bounce and will not be looked for
+# index2 ... should be in DIG@HOST's inbox
+# get3 - r format to DIG@HST
+# get4 - n
+# get5 - v
+# get6 - x
+
+# well - just a consistency check
+  ${GREP} "index1" ${DIGDIR}/new/* >/dev/null 2>&1 && \
+       { ${ECHO} "index1 found in wrong mailbox"; exit 100; }
+
+# now check that they've been delivered. We don't check the formats,
+# as this would be quite involved.
+  ${GREP} "index2" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "index2 failed to return"; exit 100; }
+  ${GREP} "get2" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "get2 failed to return"; exit 100; }
+  ${GREP} "get3" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "get3 format 'r' failed to return"; exit 100; }
+  ${GREP} "get4" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "get3 format 'n' failed to return"; exit 100; }
+  ${GREP} "get5" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "get3 format 'v' failed to return"; exit 100; }
+  ${GREP} "get6" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "get3 format 'x' failed to return"; exit 100; }
+
+  ${GREP} "dig1" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig1 from manager wasn't delivered"; exit 100; }
+  ${GREP} "dig2" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig2 from editor wasn't delivered"; exit 100; }
+  ${GREP} "dig3" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig3 from command line wasn't delivered"; exit 100; }
+  ${GREP} "dig4" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig4 format 'r' wasn't delivered"; exit 100; }
+  ${GREP} "dig5" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig5 format 'n' wasn't delivered"; exit 100; }
+  ${GREP} "dig6" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig6 format 'x' wasn't delivered"; exit 100; }
+  ${GREP} "dig6" ${DIGDIR}/new/* >/dev/null 2>&1 || \
+       { ${ECHO} "dig6 format 'v' wasn't delivered"; exit 100; }
+
+  ${ECHO} "OK"
+
+
+########################
+# waiting for delivery #
+########################
+  send_test 4
+fi                     # end section 3
+
+####################################### start of section 4
+if [ "$SECT" -le "4" ]; then
+  wait_test 4
+
+##############
+# ezmlm-warn #
+##############
+  ${ECHO} -n "ezmlm-warn (3/3):     "
+
+  SENDER="${BNC}@${HOST}"
+  export SENDER
+  ${EZBIN}/ezmlm-issubn -n "${DIR}" || \
+       { ${ECHO} "failed to remove bouncing subscriber"; exit 100; }
+  ${EZBIN}/ezmlm-issubn -n "${DIR}/digest" || \
+       { ${ECHO} "failed to remove bouncing digest subscriber"; exit 100; }
+
+  ${ECHO} "OK"
+
+################
+# ezmlm-manage #
+################
+  ${ECHO} -n "ezmlm-manage (4/4):   "
+
+  ${GREP} "#NEW_TEXT#" "${DIR}/text/test" >/dev/null 2>&1 || \
+       { ${ECHO} "edit4 failed to update text file"; exit 100; }
+
+  ${ECHO} "OK"
+
+fi                     # end section 4
+
+########################## start of section 9 (cleanup)
+if [ "$SECT" -eq "9" -o -z "$DEBUG" ]; then
+
+#####################
+# remove test files #
+#####################
+
+
+# cleanup the mysql sub tables so we can repeat if necessary
+# the Log test will pass due to old data once we access the mysql log,
+# rather than the file, but what the ...
+  if [ $USESQL ]; then
+       ${EZBIN}/ezmlm-unsub "${DIR}/digest" "${MAN}@$HOST" "${DIG}@$HOST" \
+               >/dev/null 2>&1
+       ${EZBIN}/ezmlm-unsub "${DIR}/mod" "${MOD}@$HOST" \
+               >/dev/null 2>&1
+       ${EZBIN}/ezmlm-unsub "${DIR}/${ALLOW}" "aaa@bbb" "ccc@ddd" "eee@fff" \
+               >/dev/null 2>&1
+  fi
+  ${RM} -rf "${DIR}" ${DOT}* "${ERR}" >/dev/null 2>&1
+
+fi
+${ECHO}
+if [ ! -z "${BUG}" ]; then
+  ${ECHO} "${BUG}" | ${GREP} "config" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The config bug prevents editing lists created with"
+       ${ECHO} "ezmlm-idx<0.31 or ezmlm-0.53. 'touch DIR/config' is a work-"
+       ${ECHO} "around, and upgrading to >=0.314 corrects it."
+    }
+  ${ECHO} "${BUG}" | ${GREP} "deny" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+    if [ "$EZVER" = '31' ]; then
+       ${ECHO} "The DENY bug allows users to remove themselves"
+       ${ECHO} "from DIR/blacklist which is not intended, but OTOH,"
+       ${ECHO} "DIR/blacklist is not intended for this and as a SENDER check"
+       ${ECHO} "inherently insecure anyway. If you need this feature and the"
+       ${ECHO} "bug is a problem, upgrade to >=0.321."
+    else
+       ${ECHO} "DENY access means that subscribers can remove";
+       ${ECHO} "themselves from DIR/deny. This is a bug, but DENY"
+       ${ECHO} "is easy to circumvent and not intended to keep users from"
+       ${ECHO} "posting, anyway."
+       ${ECHO} "The bug is fixed in >=0.321."
+    fi
+    }
+  ${ECHO} "${BUG}" | ${GREP} "return" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The failure to add the ezmlm-return lines means"
+       ${ECHO} "that old lists will work correctly, but bounce handling"
+       ${ECHO} "won't work in lists created with this version."
+       ${ECHO} "The bug is fixed in >=0.321."
+    }
+  ${ECHO} "${BUG}" | ${GREP} "tstdig" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The ezmlm-tstdig bug means that DIR/inlocal still needs to be"
+       ${ECHO} "adjusted for with digests within virtual domains."
+       ${ECHO} "The bug is fixed in >=0.321."
+    }
+  ${ECHO} "${BUG}" | ${GREP} "digest" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The ezmlm-tstdig -digest- bug means that ezmlm-tstdig when"
+       ${ECHO} "in DIR/manager does not pass on digest subscribe request."
+       ${ECHO} "Upgrade to ezmlm-idx>=0.321 if you use ezmlm-tstdig in"
+       ${ECHO} "DIR/manager (this is NOT used except in custom or very"
+       ${ECHO} "old (ezlm-idx<0.30) digest setups)."
+    }
+  ${ECHO} "${BUG}" | ${GREP} "_bound" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The ezmlm-send/reject mimeremove bug caused erroneous"
+       ${ECHO} "rejection of messages with text after the mime boundary in the"
+       ${ECHO} "Content-type header when DIR/mimeremove was used. This type"
+       ${ECHO} "of message is very rare (mainly Mutt with PGP MIME)."
+    }
+  ${ECHO} "${BUG}" | ${GREP} "_noself" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The ezmlm-send -C switch 'not to sender' is no longer" 
+       ${ECHO} "supported. For backwards compatibility the switch is"
+       ${ECHO} "ignored. Instead, implement this feature in the recipients"
+       ${ECHO} "mailbox by rejecting messages from the list with the"
+       ${ECHO} "recipient's address in the From: header."
+    }
+  ${ECHO} "${BUG}" | ${GREP} "_signed" >/dev/null 2>&1 && \
+    {
+       ${ECHO}
+       ${ECHO} "The trailer is added as a separate MIME part to multipart"
+       ${ECHO} "messages, but should be suppressed not only for multipart"
+       ${ECHO} "alternative, but also for many other multipart types,"
+       ${ECHO} "including multipart/signed."
+    }
+    ${ECHO}
+fi
+
+exit 0
+
+
diff --git a/ezmlm-tstdig.1 b/ezmlm-tstdig.1
new file mode 100644 (file)
index 0000000..3bd3185
--- /dev/null
@@ -0,0 +1,112 @@
+.TH ezmlm-tstdig 1
+.SH NAME
+ezmlm-tstdig \- Tests if a digest should be created
+.SH SYNOPSIS
+.B ezmlm-tstdig
+[
+.B -k
+.I kbytes
+] [
+.B -m
+.I msg
+] [
+.B \-t
+.I time
+]
+.I dir
+
+.B ezmlm-tstdig
+reads files in the list directory
+.I dir
+and determines if any of the criteria specified by the options are met.
+If they are, or if all options specified are 0,
+.B ezmlm-tstdig
+exits with success (0). Otherwise,
+.B ezmlm-tstdig
+exits (99), or in case of errors, (100) for permanent and (111) for temporary
+errors.
+
+The normal use of
+.B ezmlm-tstdig
+is to place it in a script so that
+.B ezmlm-get(1)
+is executed if
+.B ezmlm-tstdig
+exists 0, but skipped otherwise.
+
+.B ezmlm-tstdig
+uses LOCAL to adapt its behavior so that it
+can be used from the command line, a script, or from within
+.IR dir\fB/editor ,
+or
+.IR dir\fB/manager .
+If invoked from
+.IR dir\fB/editor ,
+the
+.B ezmlm-tstdig
+line should end in '|| exit 99'.
+When used here,
+.B ezmlm-tstdig
+if digest generation criteria are met
+tests a
+timestamp in
+.IR dir\fB/tstdig .
+If
+.I dir\fB/tstdig
+is more than 1 hour old or a digest has been successfully created since
+.I dir\fB/tstdig
+was written,
+.B ezmlm-tstdig
+will set the timestamp to the current time and exit 0. Otherwise it will
+exit 99. This is to guard against initiating duplicate digests when messages
+arrive while digesting is in progress.
+.SH OPTIONS
+.TP
+.B \-k
+Success if more than
+.I kbytes
+of message body has accumulated since the latest digest.
+.TP
+.B \-m
+Success if more than
+.I msg
+messages have accumulated since the latest digest.
+.TP
+.B \-t
+Success if more than
+.I time
+hours have passed since the latest digest.
+.SH USAGE
+This is an example script for ezmlm-tstdig usage.
+
+.RS
+.nf
+#!/bin/sh
+#usage: script dir diglist@host
+#digest if > 64 kbytes message bodies,
+#          > 30 messages, or
+#          > 48 hours since last digest.
+
+# ezmlm bin path - modify for your setup
+EZPATH='/usr/local/bin/ezmlm'
+
+EZTST="${EZPATH}/ezmlm-tstdig"
+EZGET="${EZPATH}/ezmlm-get"
+${EZTST} -k64 -m30 -t48 "$1" || exit 0
+${EZGET} -t "'$2'" "'$1'" < /dev/null
+# improve by testing exit status
+.fi
+.RE
+.PP
+.SH BUGS
+Obviously,
+.B ezmlm-tstdig
+should be integrated into the digesting program so that 
+.I dir\fB/tstdig
+will not be not necessary. This may be done in the next version.
+.SH "SEE ALSO"
+ezmlm-get(1),
+ezmlm-make(1),
+ezmlm-manage(1),
+ezmlm-send(1),
+ezmlm(5)
diff --git a/ezmlm-tstdig.c b/ezmlm-tstdig.c
new file mode 100644 (file)
index 0000000..dc073ea
--- /dev/null
@@ -0,0 +1,176 @@
+/*$Id: ezmlm-tstdig.c,v 1.17 1999/03/20 16:43:42 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include <sys/types.h>
+#include "stralloc.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "sig.h"
+#include "getconf.h"
+#include "env.h"
+#include "fmt.h"
+#include "now.h"
+#include "lock.h"
+#include "sgetopt.h"
+#include "errtxt.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-tstdig: fatal: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-tstdig: usage: ezmlm-tstdig [-k kbytes] [-m messages] [-t hours] dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+stralloc line = {0};
+
+substdio ssnum;
+char numbuf[16];
+
+char strnum[FMT_ULONG];
+
+int flaglocal = 0;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *local;
+  char *def;
+  int opt;
+  unsigned int pos;
+  unsigned long num, digsize, dignum;
+  unsigned long cumsize = 0L;
+  unsigned long deltanum = 0L;
+  unsigned long deltawhen = 0L;
+  unsigned long deltasize = 0L;
+  unsigned long when, tsttime, digwhen;
+  int fd,fdlock;
+
+  (void) umask(022);
+  sig_pipeignore();
+  when = (unsigned long) now();
+
+  while ((opt = getopt(argc,argv,"k:t:m:vV")) != opteof)
+    switch(opt) {
+      case 'k':
+                if (optarg)
+                  scan_ulong(optarg,&deltasize);
+                break;
+      case 't':
+                if (optarg)    /* hours */
+                  scan_ulong(optarg,&deltawhen);
+                break;
+      case 'm':
+                if (optarg)
+                  scan_ulong(optarg,&deltanum);
+                break;
+      case 'v':
+      case 'V': strerr_die2x(0,"ezmlm-tstdig version: ",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+
+
+  dir = argv[optind++];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  if (argv[optind])
+    die_usage();       /* avoid common error of putting options after dir */
+  if (!getconf_line(&line,"num",0,FATAL,dir))
+    _exit(99);         /* no msgs no shirt -> no digest */
+  if(!stralloc_0(&line)) die_nomem();
+  pos = scan_ulong(line.s,&num);
+  if (line.s[pos] == ':')
+    scan_ulong(line.s+pos+1,&cumsize);
+
+  if (getconf_line(&line,"dignum",0,FATAL,dir)) {
+    if(!stralloc_0(&line)) die_nomem();
+    pos = scan_ulong(line.s,&dignum);
+    if (line.s[pos] == ':')
+      pos += 1 + scan_ulong(line.s+pos+1,&digsize);
+    if (line.s[pos] == ':')
+      scan_ulong(line.s+pos+1,&digwhen);
+  } else {
+    dignum = 0L;       /* no file, not done any digest */
+    digsize = 0L;      /* nothing digested */
+    digwhen = 0L;      /* will force a digest, but the last one was eons  */
+                       /* ago. ezmlm-get sends it out only if there are   */
+                       /* messages. This is as it should for new lists.   */
+  }
+  local = env_get("LOCAL");
+  if (local && *local) {                       /* in editor or manager */
+    def = env_get("DEFAULT");
+    if (def && *def) {                         /* qmail>=1.02 and manager */
+      if (!case_starts(def,"dig") || case_starts(def,"digest-"))
+        _exit(0);
+    } else {                           /* older qmail versions or editor */
+      if (!getconf_line(&line,"inlocal",0,FATAL,dir)) {
+       flaglocal = 1;
+      } else {
+       pos = str_len(local);
+       if (pos <= line.len) {          /* maybe qmail>=1.02 and editor */
+         flaglocal = 1;                /* editor and qmail>=1.02. No harm */
+                                       /* if we're wrong */
+       } else {                        /* older qmail */
+         if (case_diffb(local,line.len,line.s))        /* local */
+           flaglocal = 1;              /* minimal harm */
+          else if (pos < line.len +4 ||        /* in manager and non-digest */
+               !case_starts(local+line.len,"-dig"))
+           _exit(0);
+          else if (case_starts(local+line.len,"-digest-"))
+           _exit(0);
+       }
+      }
+    }
+  }
+
+  if (!deltawhen && !deltasize && !deltanum) _exit(0);
+  if ((deltawhen && ((digwhen + deltawhen * 3600L) <= when)) ||
+      (deltasize && ((digsize + (deltasize << 2)) <= cumsize)) ||
+      (deltanum && ((dignum + deltanum) <= num))) {    /* digest! */
+    if (flaglocal) {   /* avoid multiple digests. Of course, ezmlm-tstdig*/
+                       /* belongs in ezmlm-digest, but it's too late ....*/
+      fdlock = open_append("lock");
+      if (fdlock == -1)
+        strerr_die2sys(111,FATAL,ERR_OPEN_LOCK);
+      if (lock_ex(fdlock) == -1) {
+        close(fdlock);
+        strerr_die2sys(111,FATAL,ERR_OBTAIN_LOCK);
+      }
+      getconf_line(&line,"tstdig",0,dir,FATAL);
+      if (!stralloc_0(&line)) die_nomem();
+      scan_ulong(line.s,&tsttime);     /* give digest 1 h to complete */
+                                       /* nobody does digests more often */
+      if ((tsttime + 3600L < when) || (tsttime <= digwhen)) {
+        fd = open_trunc("tstdign");
+        if (fd == -1)
+          strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/","tstdign",": ");
+        substdio_fdbuf(&ssnum,write,fd,numbuf,sizeof(numbuf));
+        if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,when)) == -1)
+          strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/","tstdign",": ");
+        if (substdio_puts(&ssnum,"\n") == -1)
+          strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/","tstdign",": ");
+        if (substdio_flush(&ssnum) == -1)
+          strerr_die6sys(111,FATAL,ERR_FLUSH,dir,"/","tstdign",": ");
+        if (fsync(fd) == -1)
+          strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/","tstdign",": ");
+        if (close(fd) == -1)
+          strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/","tstdign",": ");
+        if (rename("tstdign","tstdig") == -1)
+          strerr_die4sys(111,FATAL,ERR_MOVE,"tstdign",": ");
+        _exit(0);
+      }
+    } else
+        _exit(0);
+  }
+  _exit(99);
+}
+
index 7223ae9..8fcfd63 100644 (file)
@@ -3,6 +3,11 @@
 ezmlm-unsub \- manually remove addresses from a mailing list
 .SH SYNOPSIS
 .B ezmlm-unsub
+[
+.B \-mMHvV
+] [
+.B \-h\fBhash
+]
 .I dir
 [
 .I box\fB@\fIdomain ...
@@ -14,6 +19,10 @@ removes each address
 from the mailing list stored in
 .IR dir .
 
+If no command line argument is given,
+.B ezmlm-unsub
+will process stdin instead.
+
 If
 .I box\fB@\fIdomain
 is not on the mailing list,
@@ -26,10 +35,46 @@ converts
 to lowercase before removing
 .I box\fB@\fIdomain
 from the mailing list.
+.SH "GENERAL OPTIONS"
+.TP
+.B \-v
+Display
+.B ezmlm-unsub(1)
+version information.
+.TP
+.B \-V
+Display
+.B ezmlm-unsub(1)
+version information.
+.SH "MYSQL OPTIONS"
+These option is silently ignored in the absence of mysql support.
+.TP
+.B \-h \fIhash
+With mysql support the argument is used as the hash. The argument should
+be between 1 and 99. The hash is used in
+in the distribution of addresses between sublists. As the hash is normally
+between 0 and 52, controlling the hash makes it possible to remove addresses
+that cannot be manipulated remotely. A hash of 99 is reserved for sublists,
+and a hash of 98 is reserved for ``receipt'' addresses serviced by
+.B ezmlm-receipt(1).
+.TP
+.B \-H
+(Default.)
+The address can be removed if it has a hash in the normal range (0..52).
+.TP
+.B \-m
+(Default.)
+Use SQL support if available.
+.TP
+.B \-M
+Do not use SQL support even if available.
+This option can be used to manipulate
+a normal local subscriber database even for lists with SQL support.
 .SH "SEE ALSO"
 ezmlm-list(1),
 ezmlm-manage(1),
 ezmlm-make(1),
+ezmlm-receipt(1),
 ezmlm-send(1),
 ezmlm-sub(1),
 ezmlm(5)
index 3f53890..89eb450 100644 (file)
@@ -1,9 +1,27 @@
 #include "strerr.h"
 #include "subscribe.h"
-#include "log.h"
+#include "sgetopt.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "getln.h"
+#include "scan.h"
+#include "errtxt.h"
+#include "idx.h"
 
 #define FATAL "ezmlm-unsub: fatal: "
-#define WARNING "ezmlm-unsub: warning: "
+
+void *psql = (void *) 0;
+
+char inbuf[512];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+
+stralloc line = {0};
+
+void die_usage()
+{
+  strerr_die1x(100,
+   "ezmlm-unsub: usage: ezmlm-unsub [-h hash] [-HmMnNvV] dir box@domain ...");
+}
 
 void main(argc,argv)
 int argc;
@@ -11,24 +29,69 @@ char **argv;
 {
   char *dir;
   char *addr;
+  char *cp;
+  char ch;
+  int match;
+  int flagmysql = 1;           /* if there is mysql support, use it! */
+  int forcehash = -1;
+  int flagname = 0;
+  unsigned int u;
+  int opt;
 
-  dir = argv[1];
-  if (!dir)
-    strerr_die1x(100,"ezmlm-unsub: usage: ezmlm-unsub dir box@domain ...");
-  if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
-
-  argv += 2;
-  while (addr = *argv++)
-    switch(subscribe(addr,0)) {
-      case -1:
-       strerr_die1(111,FATAL,&subscribe_err);
-      case -2:
-       strerr_warn4(WARNING,"cannot unsubscribe ",addr,": ",&subscribe_err);
-       break;
-      case 1:
-       log("-manual",addr);
+  (void) umask(022);
+
+  while ((opt = getopt(argc,argv,"h:HmMnNvV")) != opteof)
+    switch(opt) {
+      case 'h': (void) scan_ulong(optarg,&u); forcehash = 0; break;
+      case 'H': forcehash = -1; break;
+      case 'm': flagmysql = 1; break;
+      case 'M': flagmysql = 0; break;
+      case 'n': flagname = 1; break;
+      case 'N': flagname = 0; break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-unsub version: ezmlm-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
     }
 
+  dir = argv[optind++];
+  if (!dir) die_usage();
+
+  if (dir[0] != '/')
+    strerr_die2x(100,FATAL,ERR_SLASH);
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+  if (forcehash == 0) forcehash = (int) u;
+
+  if (argv[optind]) {
+    while ((addr = argv[optind++]))
+      (void) subscribe(dir,addr,0,"","-manual",flagmysql,
+               forcehash,(char *) 0,FATAL);
+  } else {
+    for (;;) {
+      if (getln(&ssin,&line,&match,'\n') == -1)
+       strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+      if (!match) break;
+      if (line.len == 1 || *line.s == '#') continue;
+      line.s[line.len - 1] = '\0';
+      if (flagname) {
+       cp = line.s;
+       while ((ch = *cp)) {
+         if (ch == '\\') {
+            if (!*(++cp)) break;
+         } else if (ch == ' ' || ch == '\t' || ch == ',') break;
+         cp++;
+       }
+       if (ch)
+         *cp = '\0';
+      }
+      (void) subscribe(dir,line.s,0,"","+manual",flagmysql,
+               forcehash,(char *) 0,FATAL);
+    }
+  }
+  closesql();
   _exit(0);
 }
index 06fd0b0..48048a5 100644 (file)
@@ -3,6 +3,15 @@
 ezmlm-warn \- send out bounce warnings
 .SH SYNOPSIS
 .B ezmlm-warn
+[
+.B \-dD
+][
+.B \-t
+.I timeout
+][
+.B \-l
+.I lockout
+]
 .I dir
 .SH DESCRIPTION
 .B ezmlm-warn
@@ -12,27 +21,91 @@ for the mailing list stored in
 
 .B ezmlm-warn
 scans
-.I dir\fB/bounce
-for bounce messages received by
-.BR ezmlm-return .
-If it sees a distribution bounce for
+.I dir\fB/bounce/d/
+for directories older than
+.I timeout
+days ago (see
+.BR \-t ).
+The directories are created by
+B ezmlm-return
+and contain bounces.
+If
+.B ezmlm-warn
+sees a distribution bounce for
 .I box\fB@\fIdomain
-received more than ten days ago,
+received more than
+.I timeout
+days ago,
 it sends
 .I box\fB@\fIdomain
 a list of all the message numbers missed recently,
 and deletes the bounce.
 If it sees a warning bounce for
 .I box\fB@\fIdomain
-received more than ten days ago,
+received more than
+.I timeout
+days ago,
 it sends
 .I box\fB@\fIdomain
 a probe,
 and deletes the bounce.
 
 .B ezmlm-warn
+uses
+.I dir\fB/bounce/lastd
+to keep track of when it was last run. If insufficient time has
+passed (see
+.BR \-l )
+.B ezmlm-warn
+exits without further action.
+
+.B ezmlm-warn
+keeps files with the bounced message numbers in
+.IR dir\fB/bounce/h .
+Expired files are removed and
+.I dir\fB/bounce/lasth
+keeps track of the last subdirectory scanned.
+
+.B ezmlm-warn
 will not send a warning or probe to an address that is
 not currently a subscriber.
+.SH OPTIONS
+.TP
+.B \-d
+process bounces for the digest list, rather than for the main list.
+Digest list bounces are stored in
+.I dir\fB/digest/bounce/
+rather than in
+.IR dir\fB/bounce/ .
+.TP
+.B \-D
+(Default.)
+Process bounces for the main list.
+.TP
+.B \-l \fIlockout
+.B ezmlm-warn
+will abort execution if it was run less than
+.I lockout
+seconds ago. The default is
+.I timeout /
+50, which with the default
+.I timeout
+is 20,000 seconds (approx. 5.6 hours). There is no reason to use this
+switch, except for testing and possibly in combination with
+.BR \-t.
+.TP
+.B \-t \fItimeout
+Bounces received more than
+.I timeout
+days ago are processed. This overrides the default of 1,000,000
+seconds (approximately 10 days)
+and may possibly be useful for very large busy lists. Also, a
+.I timeout
+of zero can be used to send a warning to all addresses for which
+a bounce has been received and a probe for all addresses for which a
+warning has bounces.
+This is useful to rapidly clear
+out bouncing addresses from a (low quality) address list.
 .SH "SEE ALSO"
 ezmlm-make(1),
 ezmlm-return(1),
index 3140976..7d601e3 100644 (file)
@@ -1,3 +1,5 @@
+/*$Id: ezmlm-warn.c,v 1.27 1999/08/07 20:47:26 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "direntry.h"
@@ -6,6 +8,7 @@
 #include "substdio.h"
 #include "stralloc.h"
 #include "slurp.h"
+#include "sgetopt.h"
 #include "getconf.h"
 #include "byte.h"
 #include "error.h"
 #include "fmt.h"
 #include "cookie.h"
 #include "qmail.h"
+#include "errtxt.h"
+#include "mime.h"
+#include "idx.h"
+#include "subscribe.h"
 
 #define FATAL "ezmlm-warn: fatal: "
-void die_usage() { strerr_die1x(100,"ezmlm-warn: usage: ezmlm-warn dir"); }
-void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-warn: usage: ezmlm-warn -dD -l secs -t days dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
 
 stralloc key = {0};
 stralloc outhost = {0};
 stralloc outlocal = {0};
 stralloc mailinglist = {0};
+stralloc digdir = {0};
+stralloc charset = {0};
+char boundary[COOKIE];
+
+substdio ssout;
+char outbuf[16];
 
 unsigned long when;
 char *dir;
+char *workdir;
+int flagdig = 0;
+char flagcd = '\0';            /* default: don't use transfer encoding */
 stralloc fn = {0};
+stralloc bdname = {0};
+stralloc fnlasth = {0};
+stralloc fnlastd = {0};
+stralloc lasth = {0};
+stralloc lastd = {0};
 struct stat st;
+void *psql = (void *) 0;
 
-void die_read() { strerr_die6sys(111,FATAL,"unable to read ",dir,"/",fn.s,": "); }
+void die_read() { strerr_die4sys(111,FATAL,ERR_READ,fn.s,": "); }
+
+void makedir(s)
+char *s;
+{
+  if (mkdir(s,0755) == -1)
+    if (errno != error_exist)
+      strerr_die4x(111,FATAL,ERR_CREATE,s,": ");
+}
 
 char inbuf[1024];
 substdio ssin;
@@ -46,6 +80,7 @@ char hash[COOKIE];
 stralloc fnhash = {0};
 stralloc quoted = {0};
 stralloc line = {0};
+stralloc qline = {0};
 
 struct qmail qq;
 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
@@ -58,35 +93,29 @@ substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
 struct datetime dt;
 char date[DATE822FMT];
 
-void copy(fn)
-char *fn;
+void code_qput(s,n)
+char *s;
+unsigned int n;
 {
-  int fd;
-  int match;
-
-  fd = open_read(fn);
-  if (fd == -1)
-    strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
-
-  substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
-  for (;;) {
-    if (getln(&sstext,&line,&match,'\n') == -1)
-      strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
-    if (!match)
-      break;
-    qmail_put(&qq,line.s,line.len);
-  }
-
-  close(fd);
+    if (!flagcd)
+      qmail_put(&qq,s,n);
+    else {
+      if (flagcd == 'B')
+        encodeB(s,n,&qline,0,FATAL);
+      else
+        encodeQ(s,n,&qline,FATAL);
+      qmail_put(&qq,qline.s,qline.len);
+    }
 }
 
 void doit(flagw)
 int flagw;
 {
-  int i;
+  unsigned int i;
   int fd;
   int match;
   int fdhash;
+  char *err;
   datetime_sec msgwhen;
 
   fd = open_read(fn.s);
@@ -95,29 +124,42 @@ int flagw;
 
   if (getln(&ssin,&addr,&match,'\0') == -1) die_read();
   if (!match) { close(fd); return; }
-
-  if (!issub(addr.s)) { close(fd); /*XXX*/unlink(fn.s); return; }
-
+  if (!issub(workdir,addr.s,(char *) 0,FATAL)) { close(fd);
+                        /*XXX*/unlink(fn.s); return; }
   cookie(hash,"",0,"",addr.s,"");
-  if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
-  if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
+  if (!stralloc_copys(&fnhash,workdir)) die_nomem();
+  if (!stralloc_cats(&fnhash,"/bounce/h/")) die_nomem();
+  if (!stralloc_catb(&fnhash,hash,1)) die_nomem();
+  if (!stralloc_cats(&fnhash,"/h")) die_nomem();
+  if (!stralloc_catb(&fnhash,hash+1,COOKIE-1)) die_nomem();
   if (!stralloc_0(&fnhash)) die_nomem();
 
-  if (qmail_open(&qq) == -1)
-    strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
+  if (qmail_open(&qq, (stralloc *) 0) == -1)
+    strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
 
   msgwhen = now();
   qmail_puts(&qq,"Mailing-List: ");
   qmail_put(&qq,mailinglist.s,mailinglist.len);
+  if (getconf_line(&line,"listid",0,FATAL,dir)) {
+    qmail_puts(&qq,"\nList-ID: ");
+    qmail_put(&qq,line.s,line.len);
+  }
   qmail_puts(&qq,"\nDate: ");
   datetime_tai(&dt,msgwhen);
   qmail_put(&qq,date,date822fmt(date,&dt));
-  qmail_puts(&qq,"Message-ID: <");
-  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) msgwhen));
-  qmail_puts(&qq,".");
-  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) getpid()));
-  qmail_puts(&qq,".ezmlm-warn@");
-  qmail_put(&qq,outhost.s,outhost.len);
+  if (!stralloc_copys(&line,"Message-ID: <")) die_nomem();
+  if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,(unsigned long) msgwhen)))
+               die_nomem();
+  if (!stralloc_cats(&line,".")) die_nomem();
+  if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
+               die_nomem();
+  if (!stralloc_cats(&line,".ezmlm-warn@")) die_nomem();
+  if (!stralloc_catb(&line,outhost.s,outhost.len)) die_nomem();
+  qmail_put(&qq,line.s,line.len);
+  if (flagcd) {
+    if (!stralloc_0(&line)) die_nomem();
+    cookie(boundary,"",0,"",line.s,"");        /* universal MIME boundary */
+  }
   qmail_puts(&qq,">\nFrom: ");
   if (!quote(&quoted,&outlocal)) die_nomem();
   qmail_put(&qq,quoted.s,quoted.len);
@@ -126,34 +168,84 @@ int flagw;
   qmail_puts(&qq,"\nTo: ");
   if (!quote2(&quoted,addr.s)) die_nomem();
   qmail_put(&qq,quoted.s,quoted.len);
-  qmail_puts(&qq,flagw ? "\nSubject: ezmlm probe\n\n" : "\nSubject: ezmlm warning\n\n");
-
-  copy("text/top");
-  copy(flagw ? "text/bounce-probe" : "text/bounce-warn");
+  if (flagcd) {                        /* to accomodate transfer-encoding */
+    qmail_puts(&qq,"\nMIME-Version: 1.0\n");
+    qmail_puts(&qq,"Content-Type: multipart/mixed; boundary=");
+    qmail_put(&qq,boundary,COOKIE);
+  } else {
+    qmail_puts(&qq,"\nContent-type: text/plain; charset=");
+    qmail_puts(&qq,charset.s);
+  }
+  qmail_puts(&qq,flagw ? "\nSubject: ezmlm probe\n" : "\nSubject: ezmlm warning\n");
+
+  if (flagcd) {                        /* first part for QP/base64 multipart msg */
+    qmail_puts(&qq,"\n\n--");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+    qmail_puts(&qq,charset.s);
+    qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+    if (flagcd == 'Q')
+      qmail_puts(&qq,"Quoted-printable\n\n");
+    else
+      qmail_puts(&qq,"base64\n\n");
+  } else
+    qmail_puts(&qq,"\n");
+
+  copy(&qq,"text/top",flagcd,FATAL);
+  copy(&qq,flagw ? "text/bounce-probe" : "text/bounce-warn",flagcd,FATAL);
 
   if (!flagw) {
-    fdhash = open_read(fnhash.s);
-    if (fdhash == -1) {
-      if (errno != error_noent)
-        strerr_die6sys(111,FATAL,"unable to open ",dir,"/",fnhash.s,": ");
-    }
-    else {
-      copy("text/bounce-num");
-      substdio_fdbuf(&sstext,read,fdhash,textbuf,sizeof(textbuf));
-      if (substdio_copy(&ssqq,&sstext) < 0)
-        strerr_die6sys(111,FATAL,"unable to read ",dir,"/",fnhash.s,": ");
+    if (flagdig)
+      copy(&qq,"text/dig-bounce-num",flagcd,FATAL);
+    else
+      copy(&qq,"text/bounce-num",flagcd,FATAL);
+    if (!flagcd) {
+      fdhash = open_read(fnhash.s);
+      if (fdhash == -1) {
+        if (errno != error_noent)
+          strerr_die4sys(111,FATAL,ERR_OPEN,fnhash.s,": ");
+      } else {
+        substdio_fdbuf(&sstext,read,fdhash,textbuf,sizeof(textbuf));
+        for(;;) {
+          if (getln(&sstext,&line,&match,'\n') == -1)
+            strerr_die4sys(111,FATAL,ERR_READ,fnhash.s,": ");
+          if (!match) break;
+          code_qput(line.s,line.len);
+        }
+      }
       close(fdhash);
+    } else {
+      if (!stralloc_copys(&line,"")) die_nomem();      /* slurp adds! */
+      if (slurp(fnhash.s,&line,256) < 0)
+        strerr_die4sys(111,FATAL,ERR_OPEN,fnhash.s,": ");
+      code_qput(line.s,line.len);
     }
   }
 
-  copy("text/bounce-bottom");
+  copy(&qq,"text/bounce-bottom",flagcd,FATAL);
+  if (flagcd) {
+    if (flagcd == 'B') {
+      encodeB("",0,&line,2,FATAL);
+      qmail_put(&qq,line.s,line.len);  /* flush */
+    }
+    qmail_puts(&qq,"\n\n--");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n");
+  }
   if (substdio_copy(&ssqq,&ssin) < 0) die_read();
   close(fd);
 
+  if (flagcd) {                                /* end multipart/mixed */
+    qmail_puts(&qq,"\n--");
+    qmail_put(&qq,boundary,COOKIE);
+    qmail_puts(&qq,"--\n");
+  }
+
   strnum[fmt_ulong(strnum,when)] = 0;
   cookie(hash,key.s,key.len,strnum,addr.s,flagw ? "P" : "W");
   if (!stralloc_copy(&line,&outlocal)) die_nomem();
-  if (!stralloc_cats(&line,flagw ? "-return-probe-" : "-return-warn-")) die_nomem();
+  if (!stralloc_cats(&line,flagw ? "-return-probe-" : "-return-warn-"))
+       die_nomem();
   if (!stralloc_cats(&line,strnum)) die_nomem();
   if (!stralloc_cats(&line,".")) die_nomem();
   if (!stralloc_catb(&line,hash,COOKIE)) die_nomem();
@@ -170,8 +262,8 @@ int flagw;
   qmail_from(&qq,line.s);
 
   qmail_to(&qq,addr.s);
-  if (qmail_close(&qq) != 0)
-    strerr_die2x(111,FATAL,"temporary qmail-queue error");
+  if (*(err = qmail_close(&qq)) != '\0')
+    strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE, err + 1);
 
   strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
   strerr_warn2("ezmlm-warn: info: qp ",strnum,0);
@@ -179,76 +271,252 @@ int flagw;
   if (!flagw) {
     if (unlink(fnhash.s) == -1)
       if (errno != error_noent)
-        strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fnhash.s,": ");
+        strerr_die4sys(111,FATAL,ERR_DELETE,fnhash.s,": ");
   }
   if (unlink(fn.s) == -1)
-    strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fn.s,": ");
+    strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": ");
 }
 
 void main(argc,argv)
 int argc;
 char **argv;
 {
-  DIR *bouncedir;
-  direntry *d;
+  DIR *bouncedir, *bsdir, *hdir;
+  direntry *d, *ds;
   unsigned long bouncedate;
-  int fdlock;
-
-  umask(022);
+  unsigned long bouncetimeout = BOUNCE_TIMEOUT;
+  unsigned long lockout = 0L;
+  unsigned long ld;
+  unsigned long ddir,dfile;
+  int fdlock,fd;
+  char *err;
+  int opt;
+  char ch;
+
+  (void) umask(022);
   sig_pipeignore();
   when = (unsigned long) now();
-
-  dir = argv[1];
+  while ((opt = getopt(argc,argv,"dDl:t:vV")) != opteof)
+    switch(opt) {
+      case 'd': flagdig = 1; break;
+      case 'D': flagdig = 0; break;
+      case 'l':
+                if (optarg) {  /* lockout in seconds */
+                  (void) scan_ulong(optarg,&lockout);
+                }
+                break;
+      case 't':
+                if (optarg) {  /* bouncetimeout in days */
+                  (void) scan_ulong(optarg,&bouncetimeout);
+                  bouncetimeout *= 3600L * 24L;
+                }
+                break;
+      case 'v':
+      case 'V': strerr_die2x(0,
+               "ezmlm-warn version: ezmlm-0.53+",EZIDX_VERSION);
+      default:
+       die_usage();
+    }
+  dir = argv[optind];
   if (!dir) die_usage();
-
   if (chdir(dir) == -1)
-    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+    strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+  if (flagdig) {
+    if (!stralloc_copys(&digdir,dir)) die_nomem();
+    if (!stralloc_cats(&digdir,"/digest")) die_nomem();
+    if (!stralloc_0(&digdir)) die_nomem();
+    workdir = digdir.s;
+  } else
+    workdir = dir;
+
+  if (!stralloc_copys(&fnlastd,workdir)) die_nomem();
+  if (!stralloc_cats(&fnlastd,"/bounce/lastd")) die_nomem();
+  if (!stralloc_0(&fnlastd)) die_nomem();
+  if (slurp(fnlastd.s,&lastd,16) == -1)                /* last time d was scanned */
+      strerr_die4sys(111,FATAL,ERR_READ,fnlastd.s,": ");
+  if (!stralloc_0(&lastd)) die_nomem();
+  (void) scan_ulong(lastd.s,&ld);
+  if (!lockout)
+    lockout = bouncetimeout / 50;              /* 5.6 h for default timeout */
+  if (ld + lockout > when && ld < when)
+    _exit(0);          /* exit silently. Second check is to prevent lockup */
+                       /* if lastd gets corrupted */
+
+  if (!stralloc_copy(&fnlasth,&fnlastd)) die_nomem();
+  fnlasth.s[fnlasth.len - 2] = 'h';            /* bad, but feels good ... */
 
   switch(slurp("key",&key,32)) {
     case -1:
-      strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
+      strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: ");
     case 0:
-      strerr_die3x(100,FATAL,dir,"/key does not exist");
+      strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST);
   }
   getconf_line(&outhost,"outhost",1,FATAL,dir);
   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  if (flagdig)
+    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
   getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  if (getconf_line(&charset,"charset",0,FATAL,dir)) {
+    if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
+      if (charset.s[charset.len - 1] == 'B' ||
+               charset.s[charset.len - 1] == 'Q') {
+        flagcd = charset.s[charset.len - 1];
+        charset.s[charset.len - 2] = '\0';
+      }
+    }
+  } else
+    if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
+  if (!stralloc_0(&charset)) die_nomem();
+
+  set_cpoutlocal(&outlocal);   /* for copy */
+  set_cpouthost(&outhost);     /* for copy */
+  ddir = when / 10000;
+  dfile = when - 10000 * ddir;
 
-  fdlock = open_append("lockbounce");
+  if (!stralloc_copys(&line,workdir)) die_nomem();
+  if (!stralloc_cats(&line,"/lockbounce")) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  fdlock = open_append(line.s);
   if (fdlock == -1)
-    strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
+    strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
   if (lock_ex(fdlock) == -1)
-    strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
+    strerr_die4sys(111,FATAL,ERR_OBTAIN,line.s,": ");
 
-  bouncedir = opendir("bounce");
+  if (!stralloc_copys(&line,workdir)) die_nomem();
+  if (!stralloc_cats(&line,"/bounce/d")) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  bouncedir = opendir(line.s);
   if (!bouncedir)
-    strerr_die4sys(111,FATAL,"unable to open ",dir,"/bounce: ");
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
+    else
+      _exit(0);                /* no bouncedir - no bounces! */
 
-  while (d = readdir(bouncedir)) {
+  while ((d = readdir(bouncedir))) {           /* dxxx/ */
     if (str_equal(d->d_name,".")) continue;
     if (str_equal(d->d_name,"..")) continue;
 
-    if (!stralloc_copys(&fn,"bounce/")) die_nomem();
-    if (!stralloc_cats(&fn,d->d_name)) die_nomem();
-    if (!stralloc_0(&fn)) die_nomem();
-
-    if (stat(fn.s,&st) == -1) {
-      if (errno == error_noent) continue;
-      strerr_die6sys(111,FATAL,"unable to stat ",dir,"/",fn.s,": ");
+    scan_ulong(d->d_name,&bouncedate);
+       /* since we do entire dir, we do files that are not old enough. */
+       /* to not do this and accept a delay of 10000s (2.8h) of the oldest */
+       /* bounce we add to bouncedate. We don't if bouncetimeout=0 so that */
+       /* that setting still processes _all_ bounces. */
+    if (bouncetimeout) ++bouncedate;
+    if (when >= bouncedate * 10000 + bouncetimeout) {
+      if (!stralloc_copys(&bdname,workdir)) die_nomem();
+      if (!stralloc_cats(&bdname,"/bounce/d/")) die_nomem();
+      if (!stralloc_cats(&bdname,d->d_name)) die_nomem();
+      if (!stralloc_0(&bdname)) die_nomem();
+      bsdir = opendir(bdname.s);
+      if (!bsdir) {
+       if (errno != error_notdir)
+         strerr_die4sys(111,FATAL,ERR_OPEN,bdname.s,":y ");
+       else {                          /* leftover nnnnn_dmmmmm file */
+         if (unlink(bdname.s) == -1)
+           strerr_die4sys(111,FATAL,ERR_DELETE,bdname.s,": ");
+         continue;
+       }
+      }
+      while ((ds = readdir(bsdir))) {                  /* dxxxx/yyyy */
+       if (str_equal(ds->d_name,".")) continue;
+       if (str_equal(ds->d_name,"..")) continue;
+       if (!stralloc_copy(&fn,&bdname)) die_nomem();   /* '\0' at end */
+         fn.s[fn.len - 1] = '/';
+       if (!stralloc_cats(&fn,ds->d_name)) die_nomem();
+       if (!stralloc_0(&fn)) die_nomem();
+       if ((ds->d_name[0] == 'd') || (ds->d_name[0] == 'w'))
+         doit(ds->d_name[0] == 'w');
+        else                           /* other stuff is junk */
+         if (unlink(fn.s) == -1)
+           strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": ");
+      }
+      closedir(bsdir);
+      if (rmdir(bdname.s) == -1)       /* the directory itself */
+      if (errno != error_noent)
+          strerr_die4sys(111,FATAL,ERR_DELETE,bdname.s,": ");
     }
+  }
+  closedir(bouncedir);
 
-    if (when > st.st_mtime + 3000000)
-      if (unlink(fn.s) == -1)
-        strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fn.s,": ");
-
-    if ((d->d_name[0] == 'd') || (d->d_name[0] == 'w')) {
-      scan_ulong(d->d_name + 1,&bouncedate);
-      if (when > bouncedate + 1000000)
-       doit(d->d_name[0] == 'w');
+  if (!stralloc_copy(&line,&fnlastd)) die_nomem();
+  line.s[line.len - 2] = 'D';
+  fd = open_trunc(line.s);                     /* write lastd. Do safe */
+                                               /* since we read before lock*/
+  if (fd == -1) strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
+  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+  if (substdio_put(&ssout,strnum,fmt_ulong(strnum,when)) == -1)
+    strerr_die4sys(111,FATAL,ERR_WRITE,line.s,": ");
+  if (substdio_put(&ssout,"\n",1) == -1)       /* prettier */
+    strerr_die4sys(111,FATAL,ERR_WRITE,line.s,": ");
+  if (substdio_flush(&ssout) == -1)
+    strerr_die4sys(111,FATAL,ERR_FLUSH,line.s,": ");
+  if (fsync(fd) == -1)
+    strerr_die4sys(111,FATAL,ERR_SYNC,line.s,": ");
+  if (close(fd) == -1)
+    strerr_die4sys(111,FATAL,ERR_CLOSE,line.s,": ");
+
+  if (rename(line.s,fnlastd.s) == -1)
+    strerr_die4sys(111,FATAL,ERR_MOVE,fnlastd.s,": ");
+
+                               /* no need to do h dir cleaning more than */
+                               /* once per 1-2 days (17-30 days for all) */
+  if (stat(fnlasth.s,&st) == -1) {
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_STAT,fnlasth.s,": ");
+  } else if (when < st.st_mtime + 100000 && when > st.st_mtime)
+    _exit(0);                  /* 2nd comp to guard against corruption */
+
+  if (slurp(fnlasth.s,&lasth,16) == -1)                /* last h cleaned */
+      strerr_die4sys(111,FATAL,ERR_READ,fnlasth.s,": ");
+  if (!stralloc_0(&lasth)) die_nomem();
+  ch = lasth.s[0];                              /* clean h */
+  if (ch >= 'a' && ch <= 'o')
+    ++ch;
+  else
+    ch = 'a';
+  lasth.s[0] = ch;
+  if (!stralloc_copys(&line,workdir)) die_nomem();
+  if (!stralloc_cats(&line,"/bounce/h/")) die_nomem();
+  if (!stralloc_catb(&line,lasth.s,1)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  hdir = opendir(line.s);              /* clean ./h/xxxxxx */
+
+  if (!hdir) {
+    if (errno != error_noent)
+    strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
+  } else {
+
+    while ((d = readdir(hdir))) {
+      if (str_equal(d->d_name,".")) continue;
+      if (str_equal(d->d_name,"..")) continue;
+      if (!stralloc_copys(&fn,line.s)) die_nomem();
+      if (!stralloc_append(&fn,"/")) die_nomem();
+      if (!stralloc_cats(&fn,d->d_name)) die_nomem();
+      if (!stralloc_0(&fn)) die_nomem();
+      if (stat(fn.s,&st) == -1) {
+       if (errno == error_noent) continue;
+       strerr_die4sys(111,FATAL,ERR_STAT,fn.s,": ");
+      }
+      if (when > st.st_mtime + 3 * bouncetimeout)
+       if (unlink(fn.s) == -1)
+          strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": ");
     }
+    closedir(hdir);
   }
 
-  closedir(bouncedir);
-
+  fd = open_trunc(fnlasth.s);                  /* write lasth */
+  if (fd == -1) strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
+  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+  if (substdio_put(&ssout,lasth.s,1) == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
+  if (substdio_put(&ssout,"\n",1) == -1)       /* prettier */
+    strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
+  if (substdio_flush(&ssout) == -1)
+    strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": ");
+  (void) close(fd);            /* no big loss. No reason to flush/sync */
+                               /* See check of ld above to guard against */
+                               /* it being corrupted and > when */
+
+  closesql();
   _exit(0);
 }
index 5a5911f..4233717 100644 (file)
@@ -7,6 +7,8 @@ ezmlm-weed \- weed out useless messages
 .B ezmlm-weed
 reads a mail message from its standard input.
 If it recognizes the message as an MTA warning message or success message,
+or as a message with precedence ``bulk'' or ``junk'' as generated by
+vacation autoresponders,
 it exits 99;
 this will cause
 .B qmail-alias
@@ -33,6 +35,13 @@ Generic warning message, recommended for all MTAs:
    Subject: deferral notice
 .EE
 
+Delivery-status notification (DSN, rfc1891). All DSN messages with ``Action''
+other than ``failed'':
+
+.EX
+   Content-type: multipart/report
+.EE
+
 Warning message from sendmail, MIME form:
 
 .EX
@@ -104,6 +113,21 @@ Non-MIME form:
 .EX
    THIS IS A WARNING MESSAGE ONLY
 .EE
+
+Notification messages from Novell Groupwise:
+
+.EX
+  Subject: Message status - delivered
+.EE
+.br
+.EX
+  Subject: Message status - opened
+.EE
+.br
+.EX
+  Subject: Out of Office AutoReply:
+.EE
+
 .SH "SEE ALSO"
 ezmlm-return(1),
 qmail-command(8)
index 777fa03..4ec3906 100644 (file)
@@ -5,18 +5,24 @@
 #include "substdio.h"
 #include "getln.h"
 #include "strerr.h"
+#include "errtxt.h"
 
 char buf0[256];
 substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
 
 #define FATAL "ezmlm-weed: fatal: "
 
+void die_nomem()
+{
+  strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
 void get(sa)
 stralloc *sa;
 {
   int match;
   if (getln(&ss0,sa,&match,'\n') == -1)
-    strerr_die2sys(111,FATAL,"unable to read input: ");
+    strerr_die2sys(111,FATAL,ERR_READ_INPUT);
   if (!match) _exit(0);
 }
 
@@ -30,6 +36,9 @@ stralloc line6 = {0};
 stralloc line7 = {0};
 stralloc line8 = {0};
 
+stralloc boundary = {0};
+stralloc dsnline = {0};
+
 char warn1[] = "    **********************************************";
 char warn2[] = "    **      THIS IS A WARNING MESSAGE ONLY      **";
 char warn3[] = "    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **";
@@ -40,19 +49,46 @@ int flagsw = 0;
 int flagsr = 0;
 int flagas = 0;
 int flagbw = 0;
+int flagdsn = 0;
+
+int isboundary()
+/* returns 1 if line.len contains the mime bondary, 0 otherwise */
+{
+    if (line.s[0] == '-' && line.s[1] == '-' && line.len >= boundary.len + 3)
+      if (!byte_diff(line.s + 2,boundary.len,boundary.s))      /* boundary */
+        return 1;
+    return 0;
+}
 
 void main()
 {
-  int match;
+  unsigned int i,j;
 
   for (;;) {
     get(&line);
     if (line.len == 1) break;
-
+    if (line.s[0] == ' ' || line.s[0] == '\t') {       /* continuation */
+      if (flagdsn) {
+       if (!stralloc_catb(&dsnline,line.s,line.len - 1)) die_nomem();
+       continue;
+      }
+    }
+    flagdsn = 0;
     if (stralloc_starts(&line,"Subject: success notice"))
       _exit(99);
     if (stralloc_starts(&line,"Subject: deferral notice"))
       _exit(99);
+    if (stralloc_starts(&line,"Precedence: bulk"))
+      _exit(99);
+    if (stralloc_starts(&line,"Precedence: junk"))
+      _exit(99);
+/* for Novell Groupwise */
+    if (stralloc_starts(&line,"Subject: Message status - delivered"))
+      _exit(99);
+    if (stralloc_starts(&line,"Subject: Message status - opened"))
+      _exit(99);
+    if (stralloc_starts(&line,"Subject: Out of Office AutoReply:"))
+      _exit(99);
 
     if (stralloc_starts(&line,"From: Mail Delivery Subsystem <MAILER-DAEMON@"))
       flagmds = 1;
@@ -62,6 +98,71 @@ void main()
       flagsr = 1;
     if (stralloc_starts(&line,"Auto-Submitted: auto-generated (warning"))
       flagas = 1;
+    if (case_startb(line.s,line.len,"Content-type: multipart/report"))
+      if (!stralloc_copyb(&dsnline,line.s,line.len - 1)) die_nomem();
+      flagdsn = 1;
+  }                    /* end of header */
+
+  if (flagdsn) {       /* always only one recipient/action */
+    flagdsn = 0;       /* will be set for correct report type */
+    for (i=0; i < dsnline.len; i += 1+byte_chr(dsnline.s+i,dsnline.len-i,';')) {
+      while (dsnline.s[i] == ' ' || dsnline.s[i] == '\t')
+       if (++i >= dsnline.len) break;
+      if (case_startb(dsnline.s + i,dsnline.len - i,"report-type=")) {
+       i += 12;
+       while (dsnline.s[i] ==' ' || dsnline.s[i] =='\t' || dsnline.s[i] =='"')
+         if (++i >= dsnline.len) break;
+       if (case_startb(dsnline.s + i,dsnline.len - i,"delivery-status"))
+         flagdsn = 1;
+      } else if (case_startb(dsnline.s + i,dsnline.len - i,"boundary=")) {
+       i += 9;
+       while (dsnline.s[i] ==' ' || dsnline.s[i] =='\t')
+         if (++i >= dsnline.len) break;
+       if (dsnline.s[i] == '"') {
+         if (++i >= dsnline.len) break;
+         j = i + byte_chr(dsnline.s + i,dsnline.len - i,'"');
+         if (j >= dsnline.len) break;
+       } else {
+         j = i;
+         while (dsnline.s[j] !=' ' && dsnline.s[j] !='\t' &&
+               dsnline.s[j] !=';')
+           if (++j >= dsnline.len) break;
+       }                               /* got boundary */
+       if (!stralloc_copyb(&boundary,dsnline.s+i,j-i)) die_nomem();
+      }
+    }
+  }
+  if (flagdsn && boundary.len) {       /* parse DSN message */
+    get(&line);                        /* if bad format we exit(0) via get() */
+    for (;;) {
+      if (isboundary()) {
+      if (line.len == boundary.len + 5 && line.s[line.len - 1] == '-'
+               && line.s[line.len - 2] == '-')
+        _exit(99);                     /* end: not failure report */
+        get(&line);                    /* Content-type */
+        if (case_startb(line.s,line.len,"content-type:")) {
+         i = 13;
+         while (line.s[i] == ' ' || line.s[i] == '\t')
+               if (++i >= line.len) break;
+         if (case_startb(line.s+i,line.len-i,"message/delivery-status")) {
+           for (;;) {
+             get(&line);
+             if (isboundary()) break;
+             if (case_startb(line.s,line.len,"action:")) {
+               i = 8;
+               while (line.s[i] == ' ' || line.s[i] == '\t')
+                 if (++i >= line.len) break;
+               if (case_startb(line.s + i, line.len - i,"failed"))
+                 _exit(0);     /* failure notice */
+               else
+                 _exit(99);    /* there shouldn't be more than 1 action */
+             }
+            }
+         }
+        }
+      } else
+       get(&line);
+    }
   }
 
   get(&line1);
diff --git a/ezmlm.5 b/ezmlm.5
index 2866b91..6ac1cc6 100644 (file)
--- a/ezmlm.5
+++ b/ezmlm.5
@@ -21,7 +21,60 @@ manipulate the subscriber list stored under
 handles administrative requests automatically;
 .B ezmlm-send
 sends a message to all subscribers listed in
-.IR dir .
+.I dir
+and also maintains a message archive and message subject index if the list
+is configured to do so.
+.B ezmlm-reject
+rejects messages that have an empty subject, or a subject consisting of
+only a command word;
+.B ezmlm-return
+handles bounces;
+.B ezmlm-warn
+warns users for which messages bounce and eventually removes them from
+the subscriber list.
+.B ezmlm-idx
+can create a subject index from an existing list archive.
+.B ezmlm-get
+manages message, index, and thread retrieval from the archive, as well
+as the generation of message digests;
+.B ezmlm-cron
+provides a restricted interface to cron for the generation of
+digest generation trigger messages;
+.B ezmlm-store
+queues messages of moderated lists and sends a moderation request to
+the moderator(s);
+.B ezmlm-moderate
+processes moderation requests to accept the queued message to the list
+via
+.B ezmlm-send,
+or to return the message to the sender;
+.B ezmlm-clean
+cleans up the moderation queue and returns to the sender
+any messages that have timed-out;
+.B ezmlm-gate
+posts messages that come from a SENDER in an address database, and sends
+remaining messages out for moderation;
+.B ezmlm-check
+is used to diagnose problems with ezmlm mailing list configuration;
+.B ezmlm-issub
+and
+.B ezmlm-issubn
+determine if a SENDER is a subscriber or a member of a
+collection of addresses;
+.B ezmlm-tstdig
+determines if it is time to create a new digest based on the number and
+volume of messages and the amount of time that has passed since the last
+digest was issued;
+.B ezmlm-request
+can be used to answer
+.B ezmlm
+commands in the subject line easing migration from other mailing list
+managers. It can also function as a global interface mimicking
+the interface of other mailing list manager.
+.B ezmlm-glmake
+can set up the global interface, and
+.B ezmlm-glconf
+can create a configuration file for the global interface from your lists.
 .SH SUBSCRIBERS
 .I dir\fB/subscribers
 is a directory containing the subscriber list.
@@ -69,12 +122,19 @@ is a directory containing messages previously sent to subscribers.
 .B ezmlm-send
 archives all new messages if
 .I dir\fB/archived
-exists.
+exists. If
+.I dir\fB/indexed
+exists,
+.B ezmlm-send
+also maintains a message subject and author index.
 
 Messages sent to the mailing list are numbered from 1 upwards,
 whether or not they are archived.
 .I dir\fB/num
-is the number of messages sent so far.
+is the number of messages sent so far followed by ':', followed by the
+cumulative amount of message body that has passed
+.B ezmlm-send
+stored as kbytes * 4 (4 corresponds to 1kb).
 
 .I dir\fB/archive
 has subdirectories,
@@ -83,11 +143,43 @@ Message number 100m+n, with n between 0 and 99, is stored in
 .IR dir\fB/archive/\fIm\fB/\fIn .
 For example, message number 15307 is stored in
 .IR dir\fB/archive/153/07 .
+The message index is stored in the file
+.B index
+in the same subdirectory of
+.I dir\fB/archive
+holding the corresponding messages.
+Thus, the subject index contains up to 100 entries.
+
+The subject index contains message subjects that are normalized so that
+the original message and all replies have the same entry. The subject index
+is used for advanced message retrieval functions. For safety, the subject
+index is created under a temporary name
+inside
+.I dir\fB/archive
+and then moved into place.
 
 .B ezmlm-manage
 will ignore message files without the owner-execute bit set.
 .B ezmlm-send
 turns on the owner-execute bit after safely writing the message to disk.
+
+.B ezmlm-make
+by default adds
+.B ezmlm-get
+to
+.I dir\fB/manager
+to handle 
+.I \-get, \-index,
+and
+.I \-thread
+requests. If
+.B ezmlm-make
+is invoked with a 
+.I digcode
+command line argument, digest creation
+is enabled by putting this argument on the
+.B ezmlm-get
+command line.
 .SH BOUNCES
 .I dir\fB/bounce
 is a directory containing bounce messages.
@@ -127,9 +219,10 @@ handles incoming bounce messages.
 sets up
 .I dir\fB/bouncer
 to invoke
-.B ezmlm-return
-and then
-.BR ezmlm-warn .
+.BR ezmlm-return .
+.B ezmlm-warn
+is no longer invoked here due to the load it places on systems with many
+large lists with many bounces.
 
 .I dir\fB/manager
 handles incoming administrative requests.
@@ -137,9 +230,266 @@ handles incoming administrative requests.
 sets up
 .I dir\fB/manager
 to invoke
-.B ezmlm-manage
+.BR ezmlm-get ,
+.BR ezmlm-manage ,
 and then
 .BR ezmlm-warn .
+
+.I dir\fB/moderator
+handles incoming message
+.I accept
+and
+.I reject
+requests for moderated lists.
+.B ezmlm-make
+sets up
+.I dir\fB/moderator
+to invoke
+.BR ezmlm-moderate ,
+and .BR ezmlm-clean .
+.SH DIGESTS
+.B ezmlm-get
+can create digests if it is invoked from the command line, from
+.IR dir\fB/editor ,
+or from
+.IR dir\fB/manager .
+The program functions in slightly different ways in these 3 settings (see
+.BR ezmlm-get(1) ).
+
+To enable automatic digests for a mailing list, use the
+.B ezmlm-make \-d
+switch. To also enable the generation of digests at specific times dictated
+by mailed trigger messages, a
+.I digcode
+should be specified on the
+.B ezmlm-get
+command line.
+This can be done by specifying
+.I digcode
+as a fifth argument to
+.B ezmlm-make
+when setting up the list.
+.I digcode
+must be alphanumeric and is case-insensitive.
+
+To generate trigger messages, use
+.B ezmlm-cron(1)
+as an interface to
+.B crond(8)
+or use
+.B crond
+directly.
+
+.I dir\fB/num
+contains the number of the last message processed, followed by ':' and a
+number that is increased by 1 for each 256 bytes of message body text
+processed. The latter number is used by
+.B ezmlm-tstdig
+to determine if a new digest is due.
+
+.I dir\fB/dignum
+contains the contents of
+.I dir\fB/num
+at the time of the last regular digest creation, followed by a ':',
+followed by a timestamp.
+It is updated after each regular digest is sent.
+
+.I dir\fB/digissue
+contains the issue number of the last regular digest. It is incremented
+for each regular digest sent.
+
+The following user crontab entry (all on one line)
+generates a digest of the list
+.I list@host.domain
+at 1600 every day:
+
+.EX
+  00 16 * * * /var/qmail/bin/qmail-inject list-dig.digcode
+.EE
+
+Alternatively,
+.B ezmlm-cron
+can be used:
+
+.EX
+  % ezmlm-cron -t 16:00 list@host digcode
+.EE
+
+.B ezmlm-get
+can also be run from the shell: To generate a digest to
+.I list-digest@host
+from the list housed in
+.IR ~joe/list :
+
+.EX
+  % ezmlm-get ~joe/list
+.EE
+
+Like other
+.B ezmlm-get
+replies, digest can be sent in several formats. See
+.B ezmlm-get(1)
+for more info.
+.SH MODERATION
+There are three aspects of moderation: moderation of posts, moderation
+of subscriptions, and "remote administration", i.e. giving the
+moderator the right to (un)subscribe any user.
+.B ezmlm
+handles these three aspects separately. The two first aspects enhance
+security, while the third decreases security, but makes list administration
+considerably easier. By default, the moderator database is the same for all
+three functions. While "remote administration" and subscription moderation
+always use the same database, the moderators for message moderation can
+be different.
+
+Even with subscription moderation, the user has to verify the request. This
+is to ensure that the user initiating the request really controls the address.
+.B ezmlm-manage
+options exist to disable the user handshake, which may be useful in some
+circumstances.
+
+For moderation options, the moderators are by stored in a subscriber
+list in
+.IR moddir\fB/subscribers .
+By default
+.I moddir
+is
+.IR dir\fB/mod .
+
+Moderators can be added and removed with:
+
+.EX
+.B ezmlm-sub
+.I moddir
+.I moderator@host
+.EE
+
+.EX
+.B ezmlm-unsub
+.I moddir
+.I moderator@host
+.EE
+
+For subscription moderation, touch
+.IR dir\fB/modsub
+after adding moderator(s).
+For remote administration, touch
+.IR dir\fB/remote .
+If the contents of these files start with a leading forward slash, it is 
+assumed to be the name of
+.B moddir
+subscription
+moderation. If both files exist and start with a forward slash, the
+.I dir\fB/remote
+contents are ignored. Moderators are stored in a subscriber list in the
+.B subscribers
+subdirectory of
+.BR moddir .
+If no directory names are specified,
+the default,
+.IR dir\fB/mod ,
+is used.
+In all cases, the
+.I subscribers
+subdirectory of the base directory must exists/be created.
+
+Moderation of messages is achieved by
+creating
+.I dir\fB/modpost
+and  modifying
+.IR dir\fB/editor
+to invoke
+.B ezmlm-store.
+.B ezmlm-store
+stores the message in
+.IR dir\fB/mod/pending
+and sends a moderation request to all moderators stored in
+.IR moddir .
+
+If
+.I dir\fB/modpost
+does not exist,
+.B ezmlm-store
+posts messages directly, and
+.B ezmlm-clean
+does nothing.
+
+If
+.I dir\fB/modpost
+contains a directory name starting with a forward slash,
+this directory is used as
+.I moddir
+for message moderation.
+Moderators are stored in a subscriber list in the
+.I subscribers
+subdirectory of
+.IR moddir .
+If no directory names are specified,
+the default,
+.IR dir\fB/mod ,
+is used.
+
+.IR dir\fB/moderator
+is linked to
+.IR dot\fB\-accept-default
+and
+.IR dot\fB\-reject-default .
+It handles replies from the moderators.
+
+In addition to a moderator list, the directories
+.IR dir\fB/mod/pending ,
+.IR dir\fB/mod/accepted ,
+and
+.IR dir\fB/mod/rejected
+must exist. These directories contain the message moderation queue.
+
+If
+.IR dir\fB/mod/modtime
+it determines the minimal time in hours that messages wait in the moderation
+queue, before they are returned to sender with the message in
+.IR dir\fB/text/mod-timeout .
+
+If a
+.I \-help
+command is send for a moderator and
+.IR dir\fB/modsub
+or
+.IR dir\fB/remote
+exist, a more detailed help message stored in
+.I dir\fB/text/mod-help
+will be sent together with the regular help. This text should not contain
+secrets.
+If
+.I dir\fB/text/mod-help
+does not exist,
+.I dir\fB/text/help
+will be sent.
+
+If a
+.I \-list
+command is sent for a moderator and
+.IR dir\fB/modsub
+or
+.IR dir\fB/remote
+exist, and the
+.B ezmlm-manage \-l
+command line switch is specified, a subscriber list will be returned.
+
+If an
+.I \-edit.file
+command is sent for a moderator and
+.IR dir\fB/remote
+exist, and the
+.B ezmlm-manage \-d
+command line switch is specified,
+.B text\fB/file
+is returned together with an
+.B ezmlm
+cookie. The remote administrator may return an edited version of the
+file, which will be stored, provided that the cookie is valid.
+See
+.B ezmlm-manage(1)
+for more info.
 .SH TEXT
 .I dir\fB/text
 is a directory
@@ -187,6 +537,27 @@ Rejecting a bad unsubscription confirmation number.
 .B get-bad
 Rejecting a bad archive retrieval request.
 .TP
+.B digest
+Text copied into the
+.I Administrativia
+section of the digest. Usually, this will contain subscription info
+for the digest, as well as information on how to post to the list.
+.TP
+.B trailer
+If this files exists, it is copied to the end of all messages to the list.
+.TP
+.B faq
+Sent in response to the
+.I faq
+command. Usually contains frequently asked questions and answers specific
+for the mailing list.
+.TP
+.B info
+Sent in response to the
+.I info
+command. Usually contains a descripition, policy, etc, for the list. The
+first line should in itself be a very brief description of the list.
+.TP
 .B bounce-warn
 Pointing out that messages have bounced.
 .TP
@@ -198,9 +569,96 @@ Explaining that
 .B ezmlm-return
 has kept a list of bounced message numbers.
 .TP
+.B dig-bounce-num
+Explaining that digest messages have bounced. All other text files are used
+for both the main list and the digest list.
+.TP
 .B bounce-bottom
 Separating the bounce message.
+.TP
+.B mod-help
+is set to list moderators issuing a
+.I \-help
+command. It contains instructions for moderators, but it is relatively
+trivial for a non-moderator to read it. Don't put secrets here.
+.TP
+.B mod-reject
+is the returned to the sender of a rejected post.
+.TP
+.B mod-timeout
+is returned if the message timed-out without moderator action.
+.TP
+.B mod-sub
+is added to the text confirming subscription and unsubscription
+instead of
+.B bottom
+and the requesting message, for actions that were approved
+by a moderator. Not copying the requesting message
+hides the moderator identity
+from the subscriber.
+.TP
+.B mod-request
+is the text sent to the moderators to request moderator action on
+a posted message.
+.TP
+.B mod-unsub-confirm
+Requesting that the moderator confirm a request to subscribe.
+If this file does not exist,
+.B sub-confirm
+will be used.
+.TP
+.B mod-unsub-confirm
+Requesting that the moderator confirm a request to unsubscribe.
+If this file does not exist,
+.B unsub-confirm
+will be used.
+.TP
+.B edit-do
+Instructions sent to the remote administrator together with a copy
+of a
+.I dir\fB/text
+file and editing instructions.
+.TP
+.B edit-list
+A list of editable files in
+.I dir\fB/text
+with a one-line description send to a remote administrator in response to a
+.I -edit
+command.
+.TP
+.B edit-done
+Sent to the remote administrator after an edited
+.I dir\fB/text
+file has been successfully saved.
 .PP
+Several tags in the text files are replaced by ezmlm programs.
+All programs replace the tag
+.B <#l#>
+with the name of the list or the list-digest, as appropriate for the request,
+and
+.B <#h#>
+with the hostname for the list.
+.B ezmlm-send
+and
+.B ezmlm-get
+replace
+.B <#n#>
+with the current message number in added headers from
+.I dir\fB/headeradd
+and text files.
+.B ezmlm-get
+does this for digest messages, where the current message is the number of
+the first message in the digest.
+.B ezmlm-manage
+replaces the tag
+.B <#A#>
+anywhere on a line with the subscription address, and
+.B <#R#>
+anywhere on a line
+with the address the subscriber must reply to. Only the first tag on any
+line is processed.
+.PP
+For backwards compatibility,
 .B ezmlm-manage
 replaces the line
 .B !A
@@ -213,6 +671,23 @@ in
 and
 .B unsub-confirm
 with the address that the subscriber must reply to.
+.PP
+.B ezmlm-store
+replaces the tag
+.B <#A#>
+anywhere on a line with the address for accepting the message, and
+.B <#R#>
+anywhere on a line
+with the address for rejecting the message.
+Only the first tag on any line is processed.
+.PP
+For backwards compatibility,
+.B ezmlm-store
+also replaces the line
+.B !A
+with the address for accepting the message and the line
+.B !R
+with the address for rejecting the message.
 .SH "OUTGOING MESSAGE EDITING"
 .I dir\fB/headerremove
 is a list of bad header field names,
@@ -233,10 +708,61 @@ fields.
 is a list of new header fields.
 .B ezmlm-send
 adds these fields to every outgoing message.
-.B ezmlm-make
+.B ezmlm-send
 sets up
 .I dir\fB/headeradd
-with no new fields.
+to add
+.B X-No-Archive: yes
+and
+.BR Precedence: bulk .
+
+If
+.I dir\fB/mimeremove
+exists,
+.B ezmlm-send
+removed parts with the corresponding content-types from composite MIME
+messages. If the
+.B ezmlm-reject
+.I dir
+argument is specified,
+simple MIME messages of these content-types are rejected.
+
+If
+.I dir\fB/mimereject
+exists, and the
+.B ezmlm-reject
+.I dir
+argument is specified,
+simple MIME messages of these content-types, or
+composite MIME messages with any body part of these content-types are rejected.
+
+If
+.I dir\fB/sequence
+exists, the first line is added as a header to all outgoing messages, followed
+by a space and the message number. The message number is useful for archive
+retrievals, since some mail systems do not reveal the return-path to the user.
+.B NOTE:
+Sublists have their own message counter. Adding a sequence header from a
+sublists will give you the sublist message number which is different from
+the main list message number.
+
+.I dir\fB/prefix
+is a subject prefix. If this file exists, its contents are prefixed to the
+subject of the post in the outgoing message. The archived message is not
+processed. Attempts are made to not duplicate an existing prefix in replies.
+Think twice before using this option.
+A prefix takes unnecessary space on the subject line and most mail clients
+can easily filter on other headers, such as 'Mailing-List:'. If
+.I dir\fB/prefix contains a single '#', this will be replaced by the message
+number. The use of this feature is inadvisable and violates internet mail
+standards. However, it is very popular in e.g. Japan. If you must use this
+feature, make sure you are aware that you may be causing problems to users,
+sublists, etc.
+
+.I dir\fB/text/trailer
+is a message trailer. If this file exists, it's contents are copied to the 
+end of outgoing messages. Only lines terminated with new-line are copied.
+No trailer is copied to the archived version of the message.
 .SH MISCELLANY
 The first line of
 .I dir\fB/mailinglist
@@ -248,6 +774,27 @@ field, showing the contents of
 .IR dir\fB/mailinglist ,
 in every outgoing message.
 
+If
+.I dir\fB/listid
+exists,
+ezmlm programs create a new
+.B List-ID
+field, showing the contents of the first line of
+.IR dir\fB/listid ,
+in every outgoing message. The list-id should be unique and within name
+space controlled by the owner. It should remain constant even if lists
+move and be of the format
+
+.EX
+List-ID: optional_text <unique_id.domain>
+.EE
+
+This header would result from a
+.I dir\fB/listid
+file containing ``optional_text <unique_id.domain>''. See
+.I http://www.within.com/~chandhok/ietf/listid.shtml
+for more info.
+
 The first lines of
 .I dir\fB/outlocal
 and
@@ -292,6 +839,44 @@ is the name of the parent list.
 This affects the behavior of
 .BR ezmlm-send .
 
+If
+.I dir\fB/qmqpservers
+exists,
+.B ezmlm-send
+and
+.B ezmlm-get
+will use
+.B qmail-qmqpc(1)
+to send posts and digests. Other mail will use the normal qmail mechanism.
+If
+.B qmail-qmqpc
+is modified correctly, server IP addresses listed one per line in
+.I dir\fB/qmqpsevers
+will be tried in order, rather than the default servers specified in
+.IR /var/qmail/control .
+
+If
+.I dir\fB/msgsize
+exists, it is assumed to contain ``max:min'', where ``max'' is the maximum
+size in bytes of an acceptable message body, and ``min'' the corresponding
+minimal size. Either will be ignored if zero or omitted. If the
+.B ezmlm-reject
+command line specifies the list directory, messages not meeting the size
+criteria are rejected.
+
+If
+.I dir\fB/charset
+exists, the first line is assumed to represent a valid MIME character set,
+which is used for all outgoing MIME messages sent by
+.B ezmlm-get 
+and the message moderation programs. The character set string may be suffixed
+with ':' and 'Q' or 'B' to send all outgoing
+text (ezmlm messages, digest table-of-contents, moderation requests, etc)
+encoded in ``Quoted-Printable'' or ``base64'' encoding. By default, no encoding
+is done, which may result in the transmission of characters with the high
+bit set. When encoding is specified, trigger messages and other parts of the
+reply that should not be encoded are sent as separate MIME parts.
+
 .I dir\fB/lock
 is an empty file.
 Any program that reads or writes the subscriber list,
@@ -304,14 +889,54 @@ is an advisory log of subscription and unsubscription actions.
 .B WARNING:
 .B Log
 is not protected against system crashes.
-Log entries may be missing or corrupted if the system goes down.
+Log entries may be missing or corrupted if the system goes down. There is
+Log for each of the accessory address databases as well. Thus, the log
+for digest subscribers is
+.IR dir\fB/digest/Log .
+If enabled, these logs can be retrieved by remote administrators (see
+.BR ezmlm-manage(1) ).
+
+.I dir\fB/digest
+contains items specific for the digest list.
+
+.I dir\fB/digest/subscribers
+contains hash files of digest subscriber addresses.
+
+.IR dir\fB/digest/Log ,
+.IR dir\fB/digest/bounce ,
+.IR dir\fB/digest/lockbounce ,
+and
+.I dir\fB/digest/lock
+have functions for the digest list that mirror that of the corresponding
+files in
+.IR dir .
+
+.I dir\fB/sql
+contains SQL server access information for list that are configured to
+use an SQL database for storage.
+
+.I dir\fB/tstdig
+is a timestamp used temporarily by
+.B ezmlm-tstdig(1)
+to coordinate digesting.
 .SH "SEE ALSO"
+ezmlm-check(1),
+ezmlm-clean(1),
+ezmlm-gate(1),
+ezmlm-get(1),
+ezmlm-idx(1),
+ezmlm-issub(1),
+ezmlm-issubn(1),
 ezmlm-list(1),
 ezmlm-make(1),
 ezmlm-manage(1),
+ezmlm-moderate(1),
+ezmlm-request(1),
 ezmlm-return(1),
 ezmlm-send(1),
+ezmlm-store(1),
 ezmlm-sub(1),
+ezmlm-tstdig(1),
 ezmlm-unsub(1),
 ezmlm-warn(1),
 dot-qmail(5)
diff --git a/ezmlmglrc b/ezmlmglrc
new file mode 100644 (file)
index 0000000..54e3809
--- /dev/null
+++ b/ezmlmglrc
@@ -0,0 +1,97 @@
+# ezmlmglrc file to set up a global "majordomo"-like interface.
+# You also need to create DIR/config.cf, for instance with ezmlm-glconf
+#
+# Usage: ezmlm-make -Cezmlmglrc [-5 owner] dir dot local host
+#
+# dir   - base directory
+# dot   - qmail file. Usually, ~alias/.qmail-majordomo or such.
+# local - local address, e.g. "majordomo"
+# host  - host
+#
+# 
+# These work just like for standard ezmlm-make
+#
+# directories
+</+text/>
+# links - the second for complete command addresses like
+# majordomo-which-me=myhost, as well as for our own bounces
+</:/ezdomo/>
+</:-default/ezdomo/>
+</ezdomo/>
+|<#B#>/ezmlm-request -f ezdomo.cf '<#D#>'
+# standard stuff
+</inlocal/>
+<#L#>
+</outlocal/>
+<#L#>
+</outhost/>
+<#H#>
+# List owner mail
+</:-owner/owner/>
+</owner#5/>
+&<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</headerremove/>
+return-path
+return-receipt-to
+content-length
+# texts
+</text/bottom/>
+
+--- Administrative commands ---
+
+Administrative functions of ezmlm mailing lists are done by
+sending a message to the lists COMMAND address.
+
+Please do not send the requests to the list address. If you do,
+ezmlm will not see them and other subscribers will be annoyed.
+
+To subscribe to any list "listname@hostname" send a message to:
+   <listname-subscribe@hostname>
+
+To get a complete list of commands for the same list, mail to:
+   <listname-help@hostname>
+
+--- Enclosed is a copy of the request I received.
+
+</text/help/>
+ezmlm mailing lists have an address for each command (see below).
+
+I work at this address to make it easier for users used to other
+mailing list managers to use ezmlm lists. I do this by reading the
+first line of your message and doing my best to translate it into
+a command message to the correct list. Usually, it is more efficient
+for you to talk to the list directly.
+
+In general, I accept and translate commands of the form:
+
+command [listname] [username]
+
+for instance:
+
+subscribe listname
+
+I also directly reply to:
+
+lists [username]
+which [username]
+
+If username is omitted, your address will be used and the
+reply will be sent to your address.
+
+If listname does not contain a host name, I will look into my
+list and try to add the correct host name. If I can't find it,
+I will add the name of this computer. If listname contains a host
+name, it must match a listname on my list. If not, I will
+replace the host name with the name of this computer.
+
+</text/top/>
+Hi! This is the ezmlm program. I'm managing the
+<#L#>@<#H#> address to guide
+your request to the appropriate ezmlm mailing list.
+
+</ezdomo/>
+|<#B#>/ezmlm-request -f '<#D#>/config.cf' '<#D#>'
+# that's all folks!
+
diff --git a/ezmlmglrc.5 b/ezmlmglrc.5
new file mode 100644 (file)
index 0000000..a57dafc
--- /dev/null
@@ -0,0 +1,71 @@
+.TH ezmlmglrc 5
+.SH NAME
+ezmlmglrc \- set up the global ezmlm interface
+.SH SYNOPSIS
+.B ezmlm-make
+.B \-C ezmlmglrc
+[
+.B \-5\fI owner
+]
+.I dir dot local host
+.SH DESCRIPTION
+.B ezmlmglrc
+instructs
+.B ezmlm-make(1)
+to create
+.I dir
+and files within it to support the
+.I local\fB@\fIhost
+address as a global ezmlm interface.
+.I dot
+and
+.I dot\fB-default
+are linked to
+.I dir\fB/ezdomo
+which uses
+.B ezmlm-request(1)
+to handle requests as well as 
+bounces and ezmlm-style command addresses for the global interface.
+The configuration file
+.I dir\fB/config.cf
+should be created separately with
+.BR ezmlm-glconf(1) .
+.SH OPTIONS
+.TP
+.B -5\fI owner
+Forward mail addressed to
+.I local\fB-owner@\fIhost
+to
+.I owner
+rather than storing it in a local mailbox.
+.SH USAGE
+Typical use by ``joe'' to set up
+.B joe-\fImajordomo\fB@\fIthis.com:
+
+.EX
+ezmlm-make -Cezmlmglrc ~/domo ~/.qmail-majordomo joe-majordomo this.com
+.EE
+
+Typical use by ``alias'' to set up
+.I majordomo\fB@\fIthis.com:
+
+.EX
+ezmlm-make -Cezmlmglrc ~/domo ~/.qmail-majordomo majordomo this.com
+.EE
+
+Typical use by ``virt'' controlling the virtual domain
+.I lists.com
+to set up
+.I majordomo\fB@\fIlist.com:
+
+.EX
+ezmlm-make -Cezmlmglrc ~/domo ~/.qmail-majordomo majordomo list.com
+
+echo "virt-majordomo" > ~/domo/inlocal
+.EE
+The ``echo'' line is required only if used with qmail<1.02.
+.SH "SEE ALSO"
+ezmlm-glconf(1),
+ezmlm-make(1),
+ezmlm-request(1),
+ezmlm(5)
diff --git a/ezmlmrc.5 b/ezmlmrc.5
new file mode 100644 (file)
index 0000000..6d30610
--- /dev/null
+++ b/ezmlmrc.5
@@ -0,0 +1,746 @@
+.TH ezmlmrc 5
+.UC 4
+.SH NAME
+ezmlmrc \- ezmlm-make configuration file
+.SH SYNOPSIS
+ezmlmrc
+.SH DESCRIPTION
+.LP
+.B .ezmlmrc
+is a file that configures
+.B ezmlm-make(1)
+for setting up new lists.
+.B ezmlmrc
+if a plain text with four types
+of tags. All start in
+the first position of the line.
+No other text is allowed on the same line. For
+security reasons, no periods are allowed anywhere in a tag.
+Any line with a ``#'' in position 1 is ignored,
+as is any text preceding the first tag.
+
+The first line
+in
+.B .ezmlmrc
+is unique. It should start in position 1 with ``x.yz'', where
+``x'' is the major version number, ``y'' a minor version number, and ``z''
+a bugfix version number.
+.B ezmlm-make(1)
+will print a warning message if it is used with an
+.B .ezmlmrc
+file that lacks the version identifier, or
+with an
+.B ezmlmrc
+file that has a version identifier that differs in
+either major or minor version numbers from the
+.B ezmlm-make
+version.
+
+The
+.B .ezmlmrc
+file is processed top down. Text preceding the first tag is ignored.
+Subsequently, one and only one file is open for writing. Any text encountered
+in
+.B \.ezmlmrc
+is copied to that file as is with a few substitutions (see below). Text
+following conditional tags is copied only if the condition is met. A file is
+automatically closed when a new file is opened. Opening a file overwrites
+any preexisting file with that name.
+Tags are:
+
+.TP
+.B </filename#aI/>
+The following text will be copied to
+.IR dir\fB/filename
+if the options specified after the ``#'' are active, in this case
+.I archived
+and not
+.IR indexed .
+Any number of flags can be specified. This
+is used to adapt the files and
+messages to the type of list created. If no flags are
+used, the ``#'' can be omitted. If the file name is the same as the previous
+tag, or if it is omitted, the text will be added to the previous file.
+When a new file is opened the previous file is closed. Attempts to add
+more text to a already closed file overwrites its contents.
+For all letter switches (except
+.BR \-cCvV ),
+upper and lower
+case tags are opposite and mutually exclusive. Thus, if
+.B \-g
+is specified,
+.B \-G
+is not set and
+if
+.B \-G
+is set,
+.B -g
+is not.
+
+The tag
+.B #E
+has special meaning. It is false if the list is being edited, e.g.
+.B ezmlm-make
+.B \-e
+or
+.BR \+ ,
+but true
+if switches
+.B \-ee
+or
+.BR \-++
+are used, or if
+.B ezmlm-make
+.I local
+is specified. Thus, for normal edits with unchanged list name, the files
+tagged with
+.B #E
+are not overwritten (preserving manual customizations), but if the list name
+changes or if explicitly specified by
+.B \-ee
+or
+.BR \-++
+the
+.B #E
+switch is ignored.
+
+.TP
+.B </filename#^5^i/>
+This is an alternative way of specifying ``if switch \-5 is specified and
+the \-i switch is not specified''. ``^'' is used as ``not''.
+.TP
+.B </-filename#eA/>
+.IR dir\fB/filename
+will be erased, if the options after the ``#'' are active, in this case
+.I not archived
+and
+.IR edit .
+An alternative to specify that a flag, e.g. ``4'' should not be active is
+to use ``^4''.
+.TP
+.B </+directory#aI/>
+The directory ``directory'' is created if the flags specified are active, in
+this case
+.I archived
+and not
+.IR indexed .
+If no flags are specified, the ``#'' can be
+omitted.
+.TP
+.B </:link/directory#aI/>
+.B dot\fI\-link
+is symlinked to
+.I dir/directory
+if the flags specified are active, in
+this case
+.I archived
+and not
+.IR indexed .
+If no flags are specified, the ``#'' can be
+omitted.
+.PP
+In addition,
+.I local
+is substituted for
+.BR <#L#> ,
+the part of
+.I dot
+between the first 2 hyphens (if any) for
+.BR <#1#> ,
+the part of
+.I dot
+between the second and third hyphen (if any) for
+.BR <#2#> ,
+.I host
+for
+.BR <#H#> ,
+.I dir
+for
+.BR <#D#> ,
+.I dot
+for
+.BR <#T#> ,
+.I digestcode
+for
+.BR <#C#> ,
+and the path to the
+.B ezmlm
+binaries for
+.BR <#B#>
+anywhere in the text. Other tags of this format are copied to the files as is.
+
+.BR <#l#> ,
+.BR <#h#> ,
+.BR <#A#> ,
+.BR <#R#> ,
+will be substituted on-the-fly where appropriate for the
+.IR local
+or
+.IR local\fB\-digest
+local part of the list address, the
+.IR host ,
+the subscriber address or the moderation accept address,
+and the subscription reply address or moderation reject address, respectively.
+The use of
+.BR <#l#>
+is to allow the same text file to be used for requests pertaining to both
+the main list and the digest list.
+
+Before the template file is processed,
+.B ezmlm-make
+will create the list directory.
+.B ezmlm-make
+will also create
+.IR dir\fB/key .
+.SH "DESCRIPTION OF EZMLMRC"
+The ezmlmrc file is preconfigured to act upon
+.B ezmlm-make(1)
+switches to produce the results as described in the
+.B ezmlm-make(1)
+man page.
+A number of files are created via
+.B ezmlmrc
+independently of any switched. These are
+.I dir\fB/text/info
+with a short description of the list,
+.I dir\fB/text/faq
+with frequently asked questions about the list,
+.I dir\fB/headeradd
+adding ``Precedence: bulk'' and ``X-No-Archive: yes'' to outgoing messages,
+.I dir\fB/headerremove
+removing ``Return-Path'', ``Return-Receipt-To'', ``Content-length'',
+ and ``Precedence'' from list messages, and
+.I dir\fB/text/sub-ok
+with text sent after successful subscription. These files are not overwritten
+when lists are edited.
+
+Also created are the following files within
+.IR dir\fB/text/ :
+.BR bottom ,
+.BR bounce-bottom ,
+.BR bounce-num ,
+.BR dig-bounce-num ,
+.BR bounce-probe
+.BR bounce-warn ,
+.BR get-bad ,
+.BR help ,
+.BR mod-help ,
+.BR mod-request ,
+.BR mod-reject ,
+.BR mod-timeout ,
+.BR mod-sub-confirm ,
+.BR mod-unsub-confirm ,
+.BR sub-bad ,
+.BR sub-confirm ,
+.BR sub-nop
+.BR unsub-bad ,
+.BR unsub-confirm ,
+.BR unsub-nop ,
+.BR unsub-ok ,
+.BR top .
+
+.I dir\fB/bouncer
+is set up to invoke
+.B ezmlm-weed(1)
+and
+.B ezmlm-return(1)
+to handle bounces.
+In addition to switch-dependent lines, an invocation of
+.B ezmlm-warn(1)
+is placed at the end of
+.IR dir\fB/editor ,
+.IR dir\fB/manager ,
+and
+.I dir\fB/owner
+to process the contents of the bounce directory.
+.BR ezmlm-reject(1)
+is placed first in
+.I dir\fB/editor
+ (unless the
+.B \-0\ mainlist@mainhost
+switch is used) to reject undesirable messages.
+ Below is a description of the switches and the consequences
+the have for list creation with the standard
+.B ezlmrc
+file.
+.B emzlm-make(1)
+by default sets the
+.BR \-a ,
+and
+.B \-p
+switches.
+.TP
+.B \-a
+.I dir\fB/archived
+and
+.I dir\fB/indexed
+are created.
+.I dir\fB/text/bottom
+is adjusted to mention archive access.
+.B \-A
+.I dir\fB/archived
+and
+.I dir\fB/indexed
+are removed.
+.TP
+.B \-b
+Block archive. The list set up with
+.B ezmlm-get -P
+to allow only moderators archive access.
+.TP
+.B \-B
+The
+.B ezmlm-get -P
+switch is not used.
+.TP
+.B \-d
+.I dir\fB/digest/
+is created.
+.I dir\fB/digest/lock
+is created.
+.I dir\fB/digest/lockbounce
+is created.
+.I dir\fB/digest/bounce/
+is created.
+.I dir\fB/digest/subscribers/
+is created.
+.I dot\fB-digest-owner
+is linked to
+.IR dir\fB/owner .
+.I dot\fB-digest-return-default
+is linked to
+.IR dir\fB/bouncer .
+An invocation of
+.B ezmlm-warn(1) -d
+is added to
+.I dir\fB/editor
+and
+.IR dir\fB/manager .
+Invocations of
+.B ezmlm-tstdig(1)
+and
+.B ezmlm-get(1)
+are added with default arguments to
+.IR dir\fB/editor .
+A note on digest (un)subscription is added to
+.I dir\fB/text/bottom
+and to
+.IR dir\fB/text/mod-help .
+.I dir\fB/text/digest
+is created for the ``Administrivia'' section of the digest.
+.TP
+.B \-D
+The items mentioned under switch
+.B \-d
+are not done. The result is that the references to the digest
+in the text files is removed.
+.TP
+.B \-f
+The text ``[\fIlocal\fR]'' is placed in
+.I dir\fB/prefix
+resulting in the text being used as the list's subject index.
+.TP
+.B \-F
+.I dir\fB/prefix
+is removed.
+.TP
+.B \-g
+The -s switch is added to the
+.B ezmlm-get(1)
+line in
+.I dir\fB/manager
+so that only subscribers can access the archive.
+.TP
+.B \-G
+The -s switch for
+.B ezmlm-get(1)
+is not used. Anyone can access the archive if archive (access) in general
+is enabled (see
+.B \-p
+for ``public'',
+.B \-a
+for ``archived'', and
+.B \-i
+for ``indexed''.
+.TP
+.B \-i
+.I dir\fB/editor
+(for normal lists)
+or
+.I dir\fB/moderator
+(for moderated lists)
+are set up to run
+.B ezmlm-archive(1)
+after messages are posted. This sets up the cross-reference for
+.B ezmlm-cgi(1)
+WWW access.
+.TP
+.B \-I
+.B ezmlm-archive(1)
+is not configured.
+.TP
+.B \-j
+.I dir\fB/manager
+uses
+.B ezmlm-manage -U
+to allow unconfirmed unsubscribe.
+.B \-J
+The
+.B ezmlm-manage -U
+switch is not used.
+.TP
+.B \-k
+Blacklist. A
+.B ezmlm-issubn(1)
+line that tests the envelope sender against the address database in
+.I dir\fB/deny
+is inserted into
+.IR dir\fB/editor .
+As a consequence, posts from such senders are rejected. This switch is ignored
+for sublists (i.e. if the
+.B \-0\ mainlist@mainhost
+switch is used).
+.TP
+.B \-K
+The
+.B ezmlm-issubn(1)
+line testing the envelope sender against the ``blacklist'' in
+.I dir\fB/deny
+is not used.
+.TP
+.B \-l
+The -l switch is added to the
+.B ezmlm-manage(1)
+command line in
+.I dir\fB/manager
+to allow retrieval of subscriber list and list log by remote administrators.
+.BR NOTE :
+This is pointless, unless the list is also set up for remote administration
+with the
+.B \-r
+switch.
+.I dir\fB/text/mod-help
+is adjusted.
+.TP
+.B \-L
+Do not allow access to the subscriber list under any circumstances. The
+.B ezmlm-manage(1)
+\-l switch is not specified.
+.TP
+.B \-m
+Message moderation.
+.I dir\fB/modpost
+is created.
+.I dir\fB/editor
+is set up with
+.B ezmlm-store(1)
+instead of
+.BR ezmlm-send(1) .a
+.I dir\fB/moderator
+is set up with
+.BR ezmlm-moderate(1) ,
+and
+.BR ezmlm-clean(1) .
+.I dot\fB/\-accept-default
+and
+.I dot\fB/-reject-default
+are linked to
+.IR dir\fB/moderator .
+.I dir\fB/text/mod-help
+is adjusted.
+Special action is taken when the
+.B \-m
+switch is combined with
+.BR \-u .
+In this case, the setup is as for the
+.B \-m
+switch alone, but
+.I dir\fB/editor
+is set up with
+.B ezmlm-gate(1)
+which will fork
+.B ezmlm-send(1)
+for posts with an envelope sender that is a subscriber or a moderator, and
+for
+.B ezmlm-store(1)
+for posts with other envelope senders. The consequence is that posts from
+subscribers (with the usual caveats for SENDER checks) are posted directly,
+whereas other posts are sent for moderation.
+.TP
+.B \-M
+No message moderation.
+.I dir\fB/editor
+is set up with
+.B ezmlm-send(1)
+as usual.
+.I dir\fB/moderator
+is removed.
+.TP
+.B \-n
+Allow text file editing.
+.ezmlm-manage(1)
+in
+.I dir\fB/manager
+is set up with the \-e switch to allow remote admins to via E-mail edit
+the files in
+.IR dir\fB/text/ .
+.BR NOTE :
+This is pointless, unless the list is also set up for remote administration
+with the
+.B \-r
+switch.
+.I dir\fB/text/mod-help
+is adjusted.
+.TP
+.B \-N
+Remote editing of files in
+.I dir\fB/text
+is not allowed.
+The -e switch for
+.B ezmlm-manage
+will not be used. 
+.TP
+.B \-o
+For moderated lists, the
+.B ezmlm-store -P
+switch is used so that posts from non-moderators are rejected rather than
+sent for moderation. This is for some announcement lists.
+.TP
+.B \-O
+The
+.B ezmlm-store -P
+switch is not used.
+.TP
+.B \-p
+Public.
+.I dir\fB/public
+is created.
+.TP
+.B \-P
+Not public.
+.I dir\fB/public
+is removed.
+.TP
+.B \-q
+A line with
+.B ezmlm-request(1)
+is added to
+.I dir\manager
+to service commands in the ``Subject'' line of messages sent to the
+``list-request'' address.
+.TP
+.B \-Q
+.B ezmlm-request(1)
+is not used.
+.TP
+.B \-r
+Remote admin.
+.I dir\fB/remote
+is created.
+.I dir\fB/text/mod-help
+is adjusted.
+.TP
+.B \-R
+.I dir\fB/remote
+is removed.
+.TP
+.B \-s
+Subscription moderation.
+.I dir\fB/modsub
+is created.
+.I dir\fB/text/mod-help
+is adjusted.
+.I dir\fB/text/sub-confirm
+is adjusted.
+.TP
+.B \-S
+.I dir\fB/modsub
+is removed.
+.TP
+.B \-t
+.I dir\fB/text/trailer
+is created with instructions on how to unsubscribe.
+.TP
+.B \-T
+.I dir\fB/text/trailer
+is removed.
+.TP
+.B \-u
+User-only posts.
+.I dir\fB/editor
+is set up with an
+.B ezmlm-issubn(1)
+line to check the envelope sender against the subscriber address databases.
+If the sender is not found, the post is rejected. This results in
+subscriber-only posts, with the usual caveats for SENDER checks.
+Special action is taken when the
+.B \-u
+switch is combined with
+.BR \-m .
+In this case, the setup is as for the
+.B \-m
+switch alone, but
+.I dir\fB/editor
+is set up with
+.B ezmlm-gate(1)
+which will fork
+.B ezmlm-send(1)
+for posts with an envelope sender that is a subscriber or a moderator, and
+for
+.B ezmlm-store(1)
+for posts with other envelope senders.
+.B ezmlm-clean(1)
+is set up with the \-R switch.
+The consequence is that posts from
+subscribers (with the usual caveats for SENDER checks) are posted directly,
+whereas other posts are sent for moderation.
+.B ezmlm-clean(1)
+is set up with the \-R switch.
+Thus, ignored posts are silently
+removed rather than returned to sender.
+.TP
+.B \-U
+The
+.B ezmlm-issubn(1)
+line
+restricting posts by envelope sender is not used.
+.TP
+.B \-w
+The
+Remove the
+.B ezmlm-warn(1)
+invocations from the list setup. It is assumed that
+.B ezmlm-warn(1)
+for both
+.I local@host
+and
+.I local-digest@host
+will be run by other means, such as crond.
+If the
+.B \-6
+switch is used with this switch, the local list name will be added to the
+SQL config info for
+.I dir\fB/sql
+and
+.I dir\fB/digest/sql .
+This is to support building the main list for a distributed list using
+a SQL address database. In addition,
+.B ezmlm-receipt(1)
+will be set up for bounce handling in
+.I dir\fB/bouncer
+instead of
+.BR ezmlm-return(1).
+.TP
+.B \-W
+No action taken.
+.TP
+.B \-xyzXYZ
+User configurable. By default, if the
+.B \-x
+switch is specified,
+.I dir\fB/mimeremove 
+is created. This file contains many MIME types not routinely supported.
+MIME types in
+.I dir\fB/mimeremove are stripped from multipart posts before archiving
+and distribution.
+To view the list of
+MIME types, see
+.B ezmlmrc
+or create a list and list
+.IR dir\fB/mimeremove .
+In addition
+.I dir\fB/msgsize
+is created containing ``40000:2'' causing
+.B ezmlm-reject(1)
+to reject all posts that have a body of less than 2 bytes (empty) or
+more than 40000 bytes (too large).
+.TP
+.B \-0\fI\ mainlist@mainhost
+.I dir\fB/sublist
+is created with ``mainlist@mainhost''.
+.B dir\fB/ezmlm-reject
+is not used in
+.I dir\fB/editor
+to avoid rejecting messages that the main list has accepted.
+.TP
+.B \-3\fI\ fromarg
+The list is set up to add ``from'' to
+.I dir/fB/headerremove
+and
+.B From:\fI fromarg
+to
+.IR dir\fB/headeradd .
+This replaces the incoming ``From:'' header as desirable for some announcement
+lists.
+.TP
+.B \-4\fI\ tstdigopts
+.I tstdigopts
+will be used as the arguments for
+.ezmlm-tstdig(1)
+in
+.IR dir\fB/editor .
+This must be both switches and their arguments for
+.BR ezmlm-tstdig(1) .
+.BR NOTE :
+This is pointless, unless the list is also set up for digests
+with the
+.B \-d
+switch.
+.TP
+.B \-5\fI\ owner@ownerhost
+.I owner@ownerhost is placed in
+.I dir\fB/owner
+so that mail to ``list-owner'' is forwarded to that address, rather than
+being stored in
+.IR dir\fB/Mailbox .
+If the address does not start with an underscore or alphanumeric character,
+the argument must start with an ampersand.
+.TP
+.B \-6\fI\ host:port:user:password:datab:table
+.TP
+The string, followed by the list name is placed in
+.IR dir\fB/sql .
+The same string with ``table'' suffixed with ``_digest'' and ``_allow''
+is placed in
+.I dir\fB/digest/sql
+and
+.IR dir\fB/allow/sql ,
+respectively.
+.B \-7\fI\ /msgmodPath
+.I msgmodPath
+is placed in
+.IR dir\fB/modpost
+is the list is set up for message moderation with the
+.B \-m
+switch.
+.TP
+.B \-8\fI\ /submodPath
+.I submodPath
+is placed in
+.IR dir\fB/modsub
+is the list is set up for subscription moderation with the
+.B \-s
+switch.
+.TP
+.B \-9\fI\ /remoteAdminPath
+.I remoteAdminPath
+is placed in
+.IR dir\fB/remote
+is the list is set up for remote administration with the
+.B \-r
+switch.
+.SH "SEE ALSO"
+ezmlm(5),
+ezmlm-clean(1),
+ezmlm-gate(1),
+ezmlm-get(1),
+ezmlm-issubn(1),
+ezmlm-make(1),
+ezmlm-manage(1),
+ezmlm-moderate(1),
+ezmlm-request(1),
+ezmlm-return(1),
+ezmlm-send(1),
+ezmlm-store(1),
+ezmlm-tstdig(1),
+ezmlm-warn(1),
diff --git a/ezmlmrc.ch_GB b/ezmlmrc.ch_GB
new file mode 100644 (file)
index 0000000..61afeb3
--- /dev/null
@@ -0,0 +1,966 @@
+0.324 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.ch_GB,v 1.2 1999/12/23 23:08:19 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+To
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+list-help: <mailto:<#l#>-help@<#h#>>
+list-unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+list-post: <mailto:<#L#>@<#H#>>
+To:##L@##H
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+---------------------------------------------------------------------
+È¡Ïû×¢²á£¬½«µç×ÓÓʼþ·¢Ë͵½£º<#L#>-unsubscribe@<#H#>£¬Èç¹ûÐèÒª¸ü¶àµÄ°ïÖú£¬½«
+µç×ÓÓʼþ·¢Ë͵½£º<#L#>-help@<#H#>¡£
+</text/bottom/>
+
+---<#l#> ÓʼþÁбíµÄ¹ÜÀíÃüÁî---
+
+ϵͳÄܹ»×Ô¶¯´¦Àí¶ÔÓʼþÁбíµÄ¹ÜÀíÇëÇó¡£½«ÐèÒª´¦ÀíµÄÃüÁî·¢Ë͵½ÒÔϵĵç×ÓÓʼþ
+µØÖ·£º
+
+×¢²áÓʼþÁÐ±í£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-subscribe@<#H#>>
+
+È¡Ïû×¢²áÓʼþÁÐ±í£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-unsubscribe@<#H#>>
+
+½«µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·Äܹ»»ñµÃÓʼþÁбíµÄ½éÉÜÐÅÏ¢£º
+   <<#L#>-info@<#H#>>
+
+½«µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·Äܹ»»ñµÃÓʼþÁбíµÄ³£¼ûÎÊÌâ½â´ð£¨FAQ£©£º
+   <<#L#>-faq@<#H#>>
+
+</#d/>
+×¢²áÓʼþÁбíµÄÕªÒª£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-digest-subscribe@<#H#>>
+
+È¡Ïû×¢²áÓʼþÁбíµÄÕªÒª£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+»ñµÃÓʼþÁбí´Ó123µ½145µÄÀúÊ·Óʼþ£¨Ã¿´Î×î¶à»ñµÃ100¸öÀúÊ·Óʼþ£©£¬½«µç×ÓÓʼþ·¢
+Ë͵½£º
+   <<#L#>-get.123_145@<#H#>>
+
+</text/bottom#aI/>
+»ñµÃÓʼþÁбíµÚ12ÌõÀúÊ·Óʼþ£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-get.12@<#H#>>
+
+</text/bottom#i/>
+»ñµÃÓʼþÁбí´Ó123µ½456µÄÀúÊ·ÓʼþµÄÖ÷ÌâÓë×÷ÕßµÄË÷Òý£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-index.123_456@<#H#>>
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+»ñµÃÓëÀúÊ·Óʼþ12345ÏàͬÖ÷ÌâµÄÀúÊ·Óʼþ£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-thread.12345@<#H#>>
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+
+Äã¿ÉÒÔͨ¹ýµç×ÓÓʼþµÄ·½Ê½×¢²áÒ»¸öÆäËüµÄµØÖ·£¬¾ÙÀýÀ´½²£¬ÄãÏ£Íûͨ¹ýµç×ÓÓʼþµÄ·½Ê½×¢
+²á¡°john@host.domain¡±£¬Ö»ÒªÔÚÃüÁîºó£¨subscribe£©Ìí¼Ó¡°-¡±²¢ÇÒÓá°=¡±´úÌæ¡°@¡±£º
+<<#L#>-subscribe-john=host.domain@<#H#>>
+
+È¡Ïû×¢²áÓʼþÁÐ±í£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+<<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+ʹÓÃÕâÖÖ·½Ê½×¢²á¡¢È¡Ïû×¢²áÓʼþÁбíʱ£¬ÏµÍ³½«Ïò¸ÃµØÖ··¢ËÍÈ·Èϵĵç×ÓÓʼþ£¬µ±¸ÃµØÖ·
+ÊÕµ½µç×ÓÓʼþʱ£¬Ö»Òª»Ø¸´µç×ÓÓʼþ¾ÍÄܹ»Íê³É×¢²áºÍÈ¡Ïû×¢²á¡£
+
+</text/bottom/>
+Èç¹ûÄãÔÚʹÓõĹý³ÌÖÐÓÐÈκÎÎÊÌ⣬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÓʼþÁбíµÄ¹ÜÀíÕß<#L#>-owner@<#H#>¡£
+Èç¹û²»Äܽâ¾öÎÊÌ⣬ÇëÓëϵͳµÄ¹ÜÀíÔ±ÁªÏµ¡£
+</text/bottom/>
+
+--- ËæП½ÉϵÄÊÇϵͳÊÕµ½µÄÄã·¢Ë͵ÄÓʼþ¡£
+
+</text/bounce-bottom/>
+
+--- ËæП½ÉϵÄÊÇϵͳÊÕµ½µÄϵͳµ¯»Ø£¨bounce£©µÄÓʼþ¡£
+
+</text/bounce-num/>
+
+ϵͳÒѾ­±£´æÁËÒ»·Ý´Ó<#L#>ÓʼþÁÐ±í·¢Ë͵½ÄãµÄµç×ÓÓʼþµØÖ·±»µ¯»Ø£¨bounce£©µç×ÓÓʼþ¡£
+
+</#a/>
+ÕâЩµç×ÓÓʼþµÄ¿½±´¿ÉÄÜÒѾ­±£´æÔÚÀúÊ·ÎĵµÖС£
+</#aI/>
+»ñµÃÓʼþÁбíµÚ12345ÌõÀúÊ·Óʼþ£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+»ñµÃÓʼþÁбí´Ó123µ½145µÄÀúÊ·Óʼþ£¨Ã¿´Î×î¶à»ñµÃ100¸öÀúÊ·Óʼþ£©£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-get.123_145@<#H#>>
+
+»ñµÃÓʼþÁбí´Ó123µ½456µÄÀúÊ·ÓʼþµÄÖ÷ÌâÓë×÷ÕßµÄË÷Òý£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-index.123-456@<#H#>>
+
+<//>
+ÏÂÃæÊǵç×ÓÓʼþµÄ±àºÅ£º
+
+</text/dig-bounce-num/>
+
+ϵͳÒѾ­±£´æÁËÒ»·Ý´Ó<#L#>ÓʼþÁÐ±í·¢Ë͵½ÄãµÄµç×ÓÓʼþµØÖ·±»µ¯»Ø£¨bounce£©µÄÕªÒªÓʼþ¡£
+¶ÔÓÚÄã´í¹ýµÄÿ·ÝÕªÒª£¬ÏµÍ³ÒѾ­°üº¬ÔÚ·¢Ë͸øÄãµÄÕªÒªÓʼþÖеĵÚÒ»¸öÓʼþ¡£ÏµÍ³Ã»Óн«Äã
+´í¹ýµÄÕªÒªÓʼþ·¢Ë͸øÄ㣬µ«ÊÇÄã¿ÉÒÔͨ¹ýÓʼþÁбíµÄÀúÊ·ÎĵµÖлñµÃ¡£
+
+</#aI/>
+»ñµÃÓʼþÁбíµÚ12345ÌõÀúÊ·Óʼþ£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+»ñµÃÓʼþÁбí´Ó123µ½145µÄÀúÊ·Óʼþ£¨Ã¿´Î×î¶à»ñµÃ100¸öÀúÊ·Óʼþ£©£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-get.123_145@<#H#>>
+
+»ñµÃÓʼþÁбí´Ó123µ½456µÄÀúÊ·ÓʼþµÄÖ÷ÌâÓë×÷ÕßµÄË÷Òý£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-index@<#H#>>
+
+<//>
+ÏÂÃæÊÇÕªÒªÓʼþµÄ±àºÅ£º
+
+</text/bounce-probe/>
+
+´Ó<#l#>ÓʼþÁÐ±í·¢Ë͸øÄãµÄµç×ÓÓʼþÒѾ­±»µ¯»Ø£¨bounce£©¡£ÏµÍ³ÒѾ­ÏòÄã·¢ËÍÁËÒ»¸ö¾¯¸æµÄ
+Óʼþ£¬µ«ÊÇÕâ¸öÓʼþͬÑù±»µ¯»Ø£¨bounce£©¡£ÏµÍ³½«¸½Éϵ¯»Ø£¨bounce£©µÄÓʼþ¡£
+
+Õâ¸öÓʼþÊÇϵͳΪ¼ì²âÄãµÄµç×ÓÓʼþµØÖ·ÊÇ·ñÄܹ»µ½´ï¶ø·¢Ë͵ġ£Èç¹ûÕâ·âÓÃÓÚ¼ì²âµÄµç×ÓÓʼþ
+Ò²±»µ¯»Ø£¨bounce£©£¬ÏµÍ³½«°ÑÄãµÄµç×ÓÓʼþµØÖ·´Ó<#l#>@<#H#>ÓʼþÁбíÖÐÈ¡Ïû×¢²á²¢²»ÔÚͨ
+ÖªÄ㡣ͬÑùÄãÒ²¿ÉÒÔͨ¹ýÏòÏÂÃæµÄµØÖ··¢Ë͵ç×ÓÓʼþµÄ·½Ê½ÖØÐÂ×¢²áÓʼþÁÐ±í£º
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn/>
+
+´Ó<#l#>ÓʼþÁÐ±í·¢Ë͸øÄãµÄµç×ÓÓʼþÒѾ­±»µ¯»Ø£¨bounce£©¡£ÏµÍ³ÒѾ­½«±»µ¯»Ø£¨bounce£©µÄ
+µÚÒ»¸öµç×ÓÓʼþ¸½ÉÏ¡£
+
+Èç¹ûÕâ¸öµç×ÓÓʼþͬÑù±»µ¯»Ø£¬ÏµÍ³½«ÏòÄãµÄµç×ÓÓʼþµØÖ··¢ËÍÒ»¸ö¼ì²âÄãµÄµç×ÓÓʼþµØÖ·ÊÇ·ñ
+ÕýÈ·µÄµç×ÓÓʼþ£¬Èç¹ûÕâ¸öµç×ÓÓʼþͬÑù±»µ¯»Ø£¬ÏµÍ³½«°ÑÄãµÄµç×ÓÓʼþµØÖ·´Ó<#l#>ÓʼþÁбí
+È¡Ïû×¢²á²¢²»ÔÚ֪ͨÄã¡£
+
+</text/digest#d/>
+×¢²áÓʼþÁбíµÄÕªÒª£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+       <#L#>-digest-subscribe@<#H#>
+
+È¡Ïû×¢²áÓʼþÁбíµÄÕªÒª£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+       <#L#>-digest-unsubscribe@<#H#>
+
+ÏòÓʼþÁÐ±í·¢Ë͵ç×ÓÓʼþ£¬½«µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+       <#L#>@<#H#>
+
+</text/get-bad/>
+ÔÚÓʼþÁбíµÄÀúÊ·ÎĵµÖв»ÄÜ·¢ÏÖÕâÌõÓʼþ¡£
+
+</text/help/>
+ϵͳÊÕµ½µÄµç×ÓÓʼþÖÐûÓаüÀ¨ÏµÍ³Äܹ»´¦ÀíµÄÃüÁî¡£
+
+</text/mod-help/>
+ÄãÏÖÔÚÒѾ­³ÉΪ<#L#>@<#H#>ÓʼþÁбíµÄ¹ÜÀíÕߣ¨moderator£©£¬ÏÂÃ潫½éÉÜ×÷Ϊһ¸öÓʼþÁбí
+µÄËùÓÐÕß»ò¹ÜÀíÕߣ¨moderator£©Äã¿ÉÒÔͨ¹ýÏÂÃæµÄ·½Ê½¶ÔÓʼþÁбíµÄ¸÷¸ö·½Ãæ½øÐйÜÀí¡£
+
+
+Óû§×¢²áºÍÈ¡Ïû×¢²áµÄ¹ÜÀí
+----------------------
+×÷ΪÓʼþÁбíµÄ¹ÜÀíÕߣ¬ÄãÄܹ»×¢²áºÍÈ¡Ïû×¢²áÓʼþÁбíµÄÈκÎÓû§¡£ÄãÏ£Íûͨ¹ýµç×ÓÓʼþµÄ·½
+ʽע²á¡°john@host.domain¡±£¬Ö»ÒªÔÚÃüÁîºó£¨subscribe£©Ìí¼Ó¡°-¡±²¢ÇÒÓá°=¡±´úÌæ¡°@¡±£¬¾Ù
+ÀýÀ´½²£¬×¢²áÉÏÃæËù½²µÄµØÖ·£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+ͬÑù£¬È¡Ïû×¢²áÉÏÃæµÄµç×ÓÓʼþµØÖ·£¬½«µç×ÓÓʼþ·¢Ë͵½£º
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#d/>
+ͬÑù¶ÔÓÚ×¢²áºÍÈ¡Ïû×¢²áÓʼþÁбíµÄÕªÒª£º
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+<//>
+ËùÓеÄ×¢²áºÍÈ¡Ïû×¢²á¶¼²»ÐèÒªÓÐÓʼþÖ÷ÌâºÍÓʼþÄÚÈÝ!
+
+</#r/>
+ϵͳ½«ÏòÄã·¢ËÍÈ·Èϵĵç×ÓÓʼþ£¬È·ÈÏÊÇÄã·¢Ë͵ÄÇëÇó¡£ÄãÖ»Òª»Ø¸´µç×ÓÓʼþ¾ÍÄÜÍê³ÉËùÓеÄÇëÇó¡£
+</#R/>
+ϵͳ½«ÏòÓû§·¢ËÍÈ·Èϵĵç×ÓÓʼþ£¬ÈçÉÏÃæËù˵µÄµç×ÓÓʼþµØÖ·<john@host.domain>¡£Óû§±ØÐë
+»Ø¸´µç×ÓÓʼþ²ÅÄÜÍê³ÉËùÓеÄÇëÇó¡£
+<//>
+
+È·Èϵķ½Ê½·ÀÖ¹ÁËÆäËûÈËËæÒâµÄÏòÓʼþÁбíÌí¼ÓºÍɾ³ýÓû§¡£
+
+ϵͳ½«Í¨ÖªÓû§Ëû/ËýµÄ×¢²á״̬ÒѾ­¸Ä±ä¡£
+
+×¢²áºÍÈ¡Ïû×¢²á
+-------------
+
+ÈκÎÓû§¿ÉÒÔͨ¹ýÏòÏÂÃæµÄµØÖ··¢Ë͵ç×ÓÓʼþ½øÐÐ×¢²á»òÈ¡Ïû×¢²á£º
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#d/>
+ͬÑù¶ÔÓÚ×¢²áºÍÈ¡Ïû×¢²áÓʼþÁбíµÄÕªÒª£º
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+<//>
+Óû§½«´ÓϵͳÊÕµ½È·ÈÏÐÅËû/Ëýµç×ÓÓʼþµØÖ·µÄÈ·ÈÏÐÅ¡£Ò»µ©µç×ÓÓʼþµØÖ·±»È·ÈÏ£¬×¢²á»òÈ¡Ïû×¢²á½«Íê³É¡£
+
+</#s/>
+ÒòΪÓʼþÁбí¶ÔÓÚÓû§×¢²áÊÇÐèÒª¾­¹ý¹ÜÀíÕߣ¨moderator£©µÄÅú×¼ºó²ÅÄÜÍê³É£¬ËùÒÔϵͳÔÚÍê³É¶ÔÓû§
+µç×ÓÓʼþµØÖ·µÄÑéÖ¤ºó½«ÏòÓʼþÁбíµÄ¹ÜÀíÕߣ¨moderator£©·¢ËÍÇëÇó¹ÜÀíÕßÈ·ÈϵØÖ·×¢²áÓʼþÁбí
+µÄÇëÇó¡£Èç¹ûÄãÏ£ÍûÓû§×¢²áÓʼþÁÐ±í£¬Ö»Òª»Ø¸´µç×ÓÓʼþ½øÐÐÍê³ÉÈ·ÈÏ¡£Èç¹ûÄ㲻ϣÍûÓû§×¢²á
+ÓʼþÁÐ±í£¬Äã¿ÉÒÔɾ³ýÕâ·âµç×ÓÓʼþ¡£
+</#S/>
+×¢²áÒ²ÊÇͬÑùµÄ·½Ê½
+<//>
+
+ͬÑùÓû§Ò²¿ÉÒÔʹÓÃÏÂÃæµÄ·½·¨È¡Ïû×¢²á£º
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+"mary@host.domain"½«ÊÕµ½ÏµÍ³·¢Ë͵ÄÈ·ÈÏÈ¡Ïû×¢²áµÄµç×ÓÓʼþ.ÔÚÑéÖ¤Íê³Éºó¾ÍÄÜÈ¡Ïû×¢²á¡£
+
+</#rl/>
+ͨ¹ýµç×ÓÓʼþµÄ·½Ê½»ñµÃ<#L#>@<#H#>ÓʼþÁбíµÄ×¢²áÓû§£¬½«µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·:
+   <<#L#>-list@<#H#>>
+
+ͨ¹ýµç×ÓÓʼþµÄ·½Ê½»ñµÃ<#L#>@<#H#>ÓʼþÁбíµÄÈÕÖ¾£¨Log£©Îļþ, ½«µç×ÓÓʼþ·¢Ë͵½ÏÂ
+ÃæµÄµØÖ·:
+   <<#L#>-log@<#H#>>
+
+</#rld/>
+ͬÑù£¬Í¨¹ýµç×ÓÓʼþ»ñµÃÓʼþÁбíÕªÒªµÄ×¢²áÓû§£º
+   <<#L#>-digest-list@<#H#>>
+ͬÑù£¬Í¨¹ýµç×ÓÓʼþ»ñµÃÓʼþÁбíÕªÒªµÄÈÕÖ¾Îļþ£º
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+Äã¿ÉÒÔͨ¹ý¶ÔһЩÎļþµÄ±à¼­À´¶¨ÖÆÓʼþÁбíµÄ»ØÓ¦Óʼþ¡£µÃµ½ÐèÒª±à¼­µÄÎļþ¼°ÈçºÎ±à¼­£¬½«µç×ÓÓʼþ·¢ËÍ
+µ½ÏÂÃæµÄµØÖ·£º
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+ÓйÜÀíµÄͶµÝ
+-----------
+µ±Ò»¸öÓʼþÁбíÉèÖóÉËùÓз¢Ë͵½ÓʼþÁбíµÄÓʼþ¶¼Òª¾­¹ý¹ÜÀíÕßµÄÉóºËºó²ÅÄÜ·¢ËÍʱ£¬ÏµÍ³½«±£´æͶµÝµ½
+ÓʼþÁбíµÄÓʼþ²¢½«ÓʼþµÄ¿½±´·¢Ë͵½ÓʼþÁбíµÄ¹ÜÀíÕß¡£Õâ·âÓʼþµÄÌØÕ÷ÊÇÔÚÓʼþµÄÖ÷ÌâÖÐÓÐ
+¡°MODERATE for ...¡±¡£
+
+Èç¹ûÄãÏ£ÍûÔÚÓʼþÁбíÖÐת·¢Óʼþ£¬ÄãÖ»Òª»Ø¸´µç×ÓÓʼþ¡£
+
+Èç¹ûÄ㲻ϣÍûת·¢Óʼþ²¢ÒªÉ¾³ý´ËÓʼþ£¬½«µç×ÓÓʼþ·¢Ë͵½¡¯·¢¼þÈË£º¡®µÄµç×ÓÓʼþµØÖ·¡£Í¨³£ÄãÖ»Òªµã»÷
+¡°È«²¿»Ø¸´¡±£¬È»ºóɾ³ý³ý¡®reject¡¯ÒÔÍâµÄËùÓеç×ÓÓʼþµØÖ·¡£Í¬Ê±ÄãÒ²¿ÉÒÔÔÚ'%'¼äÌí¼ÓΪʲôûÓÐת
+·¢µÄµÄÆÀÂÛ£¬ÏµÍ³½«ÕâЩÆÀÂÛ×Ô¶¯µÄ·¢Ë͵½ÓʼþµÄ·¢¼þÈË¡£
+
+ϵͳ½«¶Ô¸ù¾ÝÓʼþÁбí¹ÜÀíÕ߶ÔÓʼþ»Ø¸´µÄÏȺó˳Ðò¶ÔÓʼþ½øÐд¦Àí£¨ÏµÍ³×ÜÊÇ°´ÕÕ×îÏȻظ´µÄ½øÐд¦Àí£©¡£
+Èç¹ûÄãÇëÇó´¦ÀíµÄµç×ÓÓʼþϵͳÒѾ­½øÐÐÁË´¦Àí£¨·¢ËÍ»òɾ³ý£©£¬ÏµÍ³»áÓõç×ÓÓʼþµÄ·½Ê½Í¨ÖªÄã¡£
+
+Èç¹ûϵͳÔÚÒ»¶Îʱ¼äÄÚ£¨Í¨³£ÊÇ5Ì죩ûÓÐÊÕµ½Èκδ¦ÀíµÄ·½Ê½£¬ÏµÍ³½«Óʼþ×Ô¶¯µÄÍ˻ص½·¢¼þÈ˲¢½âÊÍΪʲ
+ôûÓнøÐд¦Àí¡£Í¬ÑùÄãÒ²¿ÉÒÔÉèÖá°¡±µÄÐÅϢ֪ͨ·¢¼þÈËΪʲôûÓд¦Àí¡£
+<//>
+
+Íâ³ö
+----
+Èç¹ûÄãÖ»ÊÇÁÙʱµÄ¸ü»»µç×ÓÓʼþµØÖ·£¬Ö»Òª½«ÓÐÕýÈ·µÄÓʼþÁбíÍ·(»òÕßÓʼþµÄÖ÷ÌâÊÇÒÔ
+'MODERATE for <#L#>@<#H#>'»ò'CONFIRM subscribe to <#L#>@<#H#>'¿ªÍ·µÄµç×ÓÓʼþ) ×ª·¢µ½ÐµÄ
+µç×ÓÓʼþµØÖ·¾Í¿ÉÒÔÁË¡£Äã¿ÉÒÔͨ¹ýеĵØÖ·¶ÔÓʼþÁбí½øÐйÜÀí¡£Í¬Ñù£¬ÄãÒ²¿ÉÒÔ½«µç×ÓÓʼþת·¢µ½Äã
+µÄÅóÓѵĵç×ÓÓʼþÐÅÏ䣬ίÍÐËû/ËýÌæÄãÁÙʱ¹ÜÀíÓʼþÁÐ±í¡£
+
+µ±ÄãÍâ³öʱ£¬Èç¹ûÄãÏ£Íûϵͳ×Ô¶¯ÑéÖ¤ËùÓеÄÇëÇó£¬ÔÚÄãµÄµç×ÓÓʼþ¿Í»§¶ËÉèÖóÉ×Ô¶¯»Ø¸´·ûºÏÉÏÊöÌõ¼þµÄ
+µç×ÓÓʼþ¡£
+
+</#r/>
+Èç¹ûÄãÊÔͼͨ¹ýÒ»¸ö²¢²»ÊÇÄãµÄµç×ÓÓʼþµØÖ·¹ÜÀíÄãµÄÓʼþÁÐ±í£¬ÏµÍ³½«ÏòÓû§·¢ËÍÈ·ÈϵÄÓʼþ£¬ÔÚÓʼþÈ·
+ÈϺó£¬Ò»·âÒªÇóÓʼþÁбí¹ÜÀíÕß½øÐÐÈ·Èϵĵç×ÓÓʼþ½«·¢Ë͵½ÓʼþÁбíµÄËùÓйÜÀíÕß¡£ÏµÍ³ÕâÑù´¦ÀíÊÇÒòΪ
+ϵͳ²»ÄÜÈ·ÈÏÊÇË­·¢Ë͵ÄÇëÇó¡£
+
+<//>
+
+×¢Ò⣺Èç¹ûÄãÔÚʹÓõĹý³ÌÖÐÓÐÈκεÄÎÊÌ⣬ÇëÄãÓëÓʼþÁбíµÄËùÓÐÕß(<#L#>-owner@<#H#>)ÁªÏµ¡£
+
+</text/mod-reject/>
+¶Ô²»Æð£¬Äã·¢Ë͵½ÓʼþÁбíµÄÓʼþûÓб»ÓʼþÁбíµÄ¹ÜÀíÕßÅú×¼¡£Èç¹ûÓʼþÁбíµÄ¹ÜÀíÕ߶ÔÄãµÄÓʼþ
+×öÁËÆÀÂÛ£¬Ò²¸½ÔÚÕâ·âÓʼþÖС£
+</text/mod-request/>
+Õâ·âÓʼþÊÇijÈË·¢Ë͵½<#L#>@<#H#>ÓʼþÁбíÖеġ£Èç¹ûÄãÏ£ÍûÓʼþÁбíµÄ×¢²áÓû§¶¼ÊÕµ½Õâ·âÓʼþ£¬Çë
+½«µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!A
+
+ͨ³££¬ÄãÖ»ÒªÖ±½Ó»Ø¸´µç×ÓÓʼþ¾ÍÄܽ«µç×ÓÓʼþ·¢Ë͵½ÓʼþÁÐ±í¡£µØÖ·µÄ¸ñʽÊÇ"<#L#>-accept"¡£»òÕß
+½«µØÖ·Ö±½Ó¿½±´µ½ÓʼþÖеġ±ÊÕ¼þÈË¡°¡£
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#A#>
+<//>
+
+ɾ³ýÕâ·âÓʼþ²¢½«Õâ·âÓʼþÖ±½ÓÍ˻ط¢ÐÅÈË£¬Ç뽫Õâ·âÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+
+ͨ³££¬ÄãÖ»Òªµã»÷¡±È«²¿»Ø¸´¡°£¬²¢½«³ýÁË"<#L#>-reject"µÄµØÖ·È«²¿É¾³ý¡£
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+Èç¹ûÄãÏ£Íû¶ÔÓÚÍ˻صÄÓʼþ¼ÓÉÏÄãµÄÆÀÂÛ£¬ÇëÄ㽫ÄãµÄÆÀÂÛÌí¼ÓÔÚ¡±%%% Start comment¡°
+ºÍ¡±%%% End comment¡°Ö®¼ä£º
+
+%%% Start comment
+%%% End comment
+
+ллÄãµÄ°ïÖú£¡
+
+--- ËæП½ÉϵÄÊÇͶµÝµ½ÓʼþÁбíµÄµç×ÓÓʼþ¡£
+
+</text/mod-sub#E/>
+--- ÓÉÓÚ<#l#>@<#H#>¹ÜÀíÕßµÄÇëÇó£¬ÏµÍ³ÒѾ­½«Äã×¢²á»òÈ¡Ïû×¢²áÓʼþÁÐ±í¡£
+
+Èç¹û×¢²á»òÈ¡Ïû×¢²á²¢²»·ûºÏÄãµÄÏë·¨£¬ÇëÄãÖ±½Ó½«µç×ÓÓʼþ·¢Ë͵½ÓʼþÁбíµÄËùÓÐÕß(<#l#>-owner@<#H#>)¡£
+
+Èç¹ûÄãÏ£ÍûµÃµ½¸ü¶àµÄ°ïÖú£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½<#L#>-help@<#H#>¡£
+
+</text/mod-timeout/>
+¶Ô²»Æð£¬<#L#>ÓʼþÁбíµÄ¹ÜÀíÕß²»Äܹ»´¦ÀíÄãͶµÝµÄÓʼþ¡£ÏµÍ³½«ÓʼþÍ˻ظøÄ㣬ÇëÄãÖØÐÂͶµÝÓʼþ»òÓëÓÊ
+¼þÁбíµÄ¹ÜÀíÕßÁªÏµ¡£
+
+--- ËæП½ÉϵÄÊÇͶµÝµ½ÓʼþÁбíµÄµç×ÓÓʼþ¡£
+
+</text/mod-sub-confirm/>
+Óû§!AÇëÇó×¢²á<#l#>ÓʼþÁÐ±í£¬
+ËûµÄÇëÇó±ØÐëµÃµ½ÄãµÄÈ·ÈϺó²ÅÄÜÍê³É£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+
+ͨ³££¬ÄãÖ»ÒªÖ±½Ó»Ø¸´µç×ÓÓʼþ¾ÍÄÜÍê³ÉÈ·ÈÏ¡£»òÕß½«µØÖ·Ö±½Ó¿½±´µ½ÓʼþÖеġ±ÊÕ¼þÈË¡°¡£
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+Èç¹ûÄã²»ÏëÈÃÓû§×¢²áÓʼþÁÐ±í£¬ÄãÖ»ÒªºöÂÔÕâ·âÓʼþ¾Í¿ÉÒÔÁË¡£
+
+ллÄãµÄ°ïÖú£¡
+
+</text/mod-unsub-confirm/>
+Óû§!AÇëÇóÈ¡Ïû×¢²á<#l#>ÓʼþÁÐ±í£¬
+ËûµÄÇëÇó±ØÐëµÃµ½ÄãµÄÈ·ÈϺó²ÅÄÜÍê³É£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+
+ͨ³££¬ÄãÖ»ÒªÖ±½Ó»Ø¸´µç×ÓÓʼþ¾ÍÄÜÍê³ÉÈ·ÈÏ¡£»òÕß½«µØÖ·Ö±½Ó¿½±´µ½ÓʼþÖеġ±ÊÕ¼þÈË¡°¡£
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+Èç¹ûÄã²»ÏëÈÃÓû§´ÓÓʼþÁбíÖÐÈ¡Ïû×¢²á£¬ÄãÖ»ÒªºöÂÔÕâ·âÓʼþ¾Í¿ÉÒÔÁË¡£
+
+ллÄãµÄ°ïÖú£¡
+
+</text/sub-bad/>
+ÓÃÓÚÈ·Èϵıê¼ÇÊÇ·Ç·¨µÄ¡£
+
+×î´óµÄ¿ÉÄÜÐÔÊÇÒòΪ±ê¼ÇÒѾ­¹ýÆÚ¡£ÓÃÓÚÈ·Èϵıê¼ÇÔÚ10ÌìÄÚÊÇÓÐЧµÄ¡£»¹ÓÐÒ»ÖÖ¿ÉÄÜÊÇÒòΪÓÃÓÚ
+È·ÈϵĻظ´Óʼþ²¢Ã»ÓаüÀ¨ËùÓеÄÈ·ÈÏÐÅÏ¢¡£
+
+ϵͳÒѾ­Éú³ÉÁËÒ»¸öеÄÈ·Èϱê¼Ç¡£Íê³ÉÈ·ÈÏ
+
+!A
+
+¼ÓÈë<#l#>ÓʼþÁÐ±í£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+×¢Ò⣬ÔÚÄãÈ·ÈÏÄãµÄ×¢²áÒÔÇ°ÇëºË¶Ô»Ø¸´µØÖ·¡£
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm/>
+ΪÁËÍê³É×¢²á<#l#>ÓʼþÁÐ±í£¬ÄãÐèҪȷÈÏÏÂÃæµÄµØÖ·ÊÇ·ñÕýÈ·
+
+!A
+
+ΪÁËÍê³ÉÈ·ÈÏ£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+
+ͨ³££¬ÄãÖ»ÒªÖ±½Ó»Ø¸´µç×ÓÓʼþ¾ÍÄÜÍê³ÉÈ·ÈÏ¡£»òÕß½«µØÖ·Ö±½Ó¿½±´µ½ÓʼþÖеġ±ÊÕ¼þÈË¡°¡£
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+ͨ¹ýÈ·ÈÏ£¬ÏµÍ³¿ÉÒÔ£º
+1.È·ÈÏÊÇ·ñÄܹ»´ÓÄãµÄµç×ÓÓʼþµØÖ·ÊÕµ½Óʼþ¡£
+2.ËüÄܹ»·ÀֹijЩÈËðÓÃÄãµÄÃûÒå½øÐÐ×¢²á¡£
+
+</#q/>
+ÓÐЩµç×ÓÓʼþµÄ¿Í»§¶Ë³ÌÐò²»Äܹ»½âÎöºÜ³¤µÄµØÖ·¡£Èç¹ûÄã²»Äܹ»Ö±½Ó»Ø¸´Õâ·âµç×ÓÓʼþ£¬Äã¿É
+ÒÔÏò<<#L#>-request@<#H#>>·¢ËÍÓʼþ²¢½«ÉÏÃæµÄµØÖ··ÅÔÚÓʼþµÄÖ÷ÌâÖС£
+
+</text/sub-confirm#s/>
+Õâ¸öÓʼþÁбíµÄ×¢²áÊÇÐèÒª¾­¹ýÓʼþÁбíµÄ¹ÜÀíÕßÅú×¼µÄ¡£Ò»µ©Äã»Ø¸´ÁËÈ·ÈÏÐÅ£¬×¢²áÇëÇó½«Ö±
+½Ó·¢Ë͵½ÓʼþÁбíµÄ¹ÜÀíÕß¡£ÏµÍ³½«Í¨ÖªÄãµ±ÓʼþÁбíµÄ¹ÜÀíÕßͬÒâÄã¼ÓÈëÓʼþÁÐ±í¡£
+
+</text/sub-nop/>
+ÏÂÃæµÄµç×ÓÓʼþµØÖ·
+
+!A
+
+ÒѾ­ÊÇ<#l#>ÓʼþÁбíµÄ×¢²áÓû§¡£Äã²»ÄÜÖظ´×¢²áÒ»¸öÓʼþÁÐ±í¡£
+
+</text/sub-ok#E/>
+ÏÂÃæµÄµç×ÓÓʼþµØÖ·
+
+!A
+
+ÒѾ­³É¹¦µÄ×¢²á<#l#>ÓʼþÁÐ±í¡£
+
+»¶Ó­¼ÓÈë<#l#>@<#H#>ÓʼþÁбí!
+
+ÇëÄã±£´æÕâ·âµç×ÓÓʼþ£¬ÒÔ±ãÒÔºóÈ¡Ïû×¢²á»ò¸Ä±ä×¢²áµÄµç×ÓÓʼþµØÖ·¡£
+
+</text/top/>
+ÓʼþÁбíµÄÃû³ÆÊÇ£º<#l#>@<#H#>
+
+</#x/>
+ÓʼþÁбíËùÓÐÕߵĵç×ÓÓʼþµØÖ·£º<#l#>-owner@<#H#>.
+
+</text/unsub-bad/>
+ÓÃÓÚÈ·Èϵıê¼ÇÊÇ·Ç·¨µÄ¡£
+
+×î´óµÄ¿ÉÄÜÐÔÊÇÒòΪ±ê¼ÇÒѾ­¹ýÆÚ¡£ÓÃÓÚÈ·Èϵıê¼ÇÔÚ10ÌìÄÚÊÇÓÐЧµÄ¡£»¹ÓÐÒ»ÖÖ¿ÉÄÜÊÇÒòΪÓÃÓÚ
+È·ÈϵĻظ´Óʼþ²¢Ã»ÓаüÀ¨ËùÓеÄÈ·Èϱê¼Ç¡£
+
+ϵͳÒѾ­Éú³ÉÁËÒ»¸öеÄÈ·Èϱê¼Ç¡£Íê³ÉÈ·ÈÏ
+
+!A
+
+´Ó<#l#>ÓʼþÁбíÖÐɾ³ý£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+×¢Ò⣬ÔÚÄãÈ·ÈÏÈ¡ÏûÄãµÄ×¢²áÒÔÇ°ÇëºË¶Ô»Ø¸´µØÖ·¡£
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm/>
+ΪÁËÍê³É´Ó<#l#>ÓʼþÁбíÖÐÈ¡Ïû×¢²á£¬ÄãÐèҪȷÈÏÏÂÃæµÄµØÖ·ÊÇ·ñÕýÈ·
+
+!A
+
+ΪÁËÍê³ÉÈ·ÈÏ£¬Ç뽫µç×ÓÓʼþ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+
+ͨ³££¬ÄãÖ»ÒªÖ±½Ó»Ø¸´µç×ÓÓʼþ¾ÍÄÜÍê³ÉÈ·ÈÏ¡£»òÕß½«µØÖ·Ö±½Ó¿½±´µ½ÓʼþÖеġ±ÊÕ¼þÈË¡°¡£
+</#x/>
+
+ͬÑù£¬Ò²¿ÉÒÔµã»÷ÏÂÃæµÄµØÖ·£º
+       mailto:<#R#>
+<//>
+
+ϵͳ²»ÄÜÈ·ÈÏÄãµÄµç×ÓÓʼþµØÖ·ÊÇ·ñÊÇÓʼþÁбíµÄ×¢²áÓû§¡£Äã¿ÉÒÔʹÓÃÏÂÃæµÄ·½·¨»ñµÃÄãʹÓÃʲô
+µØÖ·×¢²áÓʼþÁÐ±í£ºÔÚÄãÊÕµ½µÄ´ÓÓʼþÁÐ±í·¢Ë͵ĵç×ÓÓʼþÖУ¬Ã¿¸öµç×ÓÓʼþµÄÍ˻ط¾¶
+£¨Return path£©ÖаüÀ¨ÁËÄã×¢²áÓõĵç×ÓÓʼþµØÖ·£¬ÀýÈ磬mary@xdd.ff.comÊÕµ½µÄµç×ÓÓʼþÖаü
+À¨µÄÍ˻ط¾¶£¨Return path£©ÊÇ<<#l#>-return-<number>-mary=xdd.ff.com@<#H#>¡£
+
+</#q/>
+ÓÐЩµç×ÓÓʼþµÄ¿Í»§¶Ë³ÌÐò²»Äܹ»½âÎöºÜ³¤µÄµØÖ·¡£Èç¹ûÄã²»Äܹ»Ö±½Ó»Ø¸´Õâ·âµç×ÓÓʼþ£¬Äã¿ÉÒÔÏò
+<<#L#>-request@<#H#>>·¢ËÍÓʼþ²¢½«ÉÏÃæµÄµØÖ··ÅÔÚÓʼþµÄÖ÷ÌâÖС£
+
+</text/unsub-nop/>
+ÏÂÃæµÄµç×ÓÓʼþµØÖ·
+
+!A
+
+²¢Ã»ÓÐ×¢²á<#l#>ÓʼþÁÐ±í¡£
+
+Èç¹ûÄãÒѾ­È¡Ïû×¢²á£¬µ«ÊÇÄ㻹ÊÇÊÕµ½´ÓÓʼþÁÐ±í·¢ËÍÀ´µÃµÄµç×ÓÓʼþ£¬ÇëÄãºË¶ÔÕâ·âÓʼþµÄ£º
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+ÄãÖ»Òª½«µç×ÓÓʼþ·¢Ë͵½£º
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+¾ÍÄܹ»È¡Ïû×¢²á£¬¡®user=host.dom¡¯ÊÇÄãµÄµç×ÓÓʼþµØÖ·£¬ÔÚÕâ·âÓʼþ·¢³öºó£¬Ä㽫ÊÕµ½ÏµÍ³·¢
+Ë͵ÄÓйØÈ¡Ïû×¢²áµÄÈ·ÈÏÐÅ£¬ÄãÖ»Òª»Ø¸´µç×ÓÓʼþ¾ÍÄÜÍê³ÉÈ¡Ïû×¢²á£¬×îºóϵͳ½«ÏòÄã·¢ËÍÈ¡Ïû
+×¢²á³É¹¦µÄµç×ÓÓʼþ¡£
+
+Èç¹ûÄãÔÚʹÓõĹý³ÌÖÐÓÐÈκÎÎÊÌ⣬Ç뽫µç×ÓÓʼþ·¢Ë͵½£º
+
+    <#l#>-owner@<#H#>
+
+Ëû¸ºÔð¹ÜÀíÕâ¸öÓʼþÁÐ±í¡£
+
+</text/unsub-ok/>
+ÏÂÃæµÄµØÖ·
+
+!A
+
+ÒѾ­³É¹¦µÄ´Ó<#l#>ÓʼþÁбíÖÐÈ¡Ïû×¢²á¡£
+
+</text/edit-do#n/>
+ÇëÄã±à¼­ÏÂÃæµÄÎļþ²¢½«ËûÃÇ·¢Ë͵½ÏÂÃæµÄµØÖ·£º
+
+!R
+
+ÒÔ'%%%'¿ªÊ¼µÄÎÄ×ÖÊDz»Äܹ»±à¼­µÄ¡£
+
+
+</text/edit-list#n/>
+<#L#>-edit.file ÕßÌõÃüÁî¿ÉÒÔÓÃÓÚÔ¶³ÌµÄ±à¼­<#L#>@<#H#>ÓʼþÁбíµÄ¸÷ÖÖ¿ØÖÆ
+Îļþ¡£
+
+ÏÂÃæÊÇÄã¿ÉÒÔͨ¹ýµç×ÓÓʼþµÄ·½Ê½±à¼­µÄÎļþµÄÃû³Æ¡£ÄãÖ»Òª½«ÐèÒª±à¼­µÄÎļþ·¢Ë͵½<#L#>-edit.file
+¾ÍÄÜÍê³É±à¼­¡£
+
+ÎļþÃû              ÓÃ;
+
+bottom              Ìí¼ÓÔÚϵͳ×Ô¶¯·´À¡Óʼþβ²¿µÄÐÅÏ¢¡£
+digest              ÕªÒª´¦ÀíµÄÐÅÏ¢¡£
+faq                 ÓʼþÁбíµÄ³£¼ûÎÊÌâ½â´ð¡£
+get_bad             ÔÚÓʼþÁбíµÄÀúÊ·ÎĵµÖÐûÓз¢ÏÖÎĵµµÄÐÅÏ¢¡£
+help                ÓʼþÁбíµÄ°ïÖúÎļþ¡£
+info                ÓʼþÁбíµÄÐÅÏ¢Îļþ£¬ÓÃÓÚ½éÉÜÕâ¸öÓʼþÁбíµÄÖ÷ÒªÓÃ;¡£
+mod_help            ÓʼþÁбí¹ÜÀíÕߵİïÖúÎļþ¡£
+mod_reject          ·¢Ë͸ø·¢ÐÅÈ˾ܾøͶµÝµÄÐÅÏ¢¡£
+mod_request         ·¢Ë͸øÓʼþÁбíÕßµÄÇëÇóÐÅÏ¢¡£
+mod_sub             ÔÚÓʼþÁбí¹ÜÀíÕßͬÒâÄãµÄ×¢²áºó·¢Ë͸øÓû§µÄÐÅÏ¢¡£
+mod_sub_confirm     ·¢Ë͸øÓʼþÁбí¹ÜÀíÕßÒªÇóÈ·ÈÏijÓû§×¢²áµÄÐÅÏ¢¡£
+mod_timeout         ·¢Ë͸ø·¢ÐÅÈ˳¬Ê±Í¶µÝµÄÐÅÏ¢¡£
+mod_unsub_confirm   ·¢Ë͸øÓʼþÁбí¹ÜÀíÕßÒªÇóÈ·ÈÏijÓû§È¡Ïû×¢²áµÄÐÅÏ¢¡£
+sub_bad             Èç¹û×¢²áµÄÈ·ÈÏÐÅÏ¢Ë𻵣¬·¢Ë͸øÓû§µÄÐÅÏ¢¡£
+sub_confirm         ÐèÒªÓû§È·ÈϵÄ×¢²áÐÅÏ¢¡£
+sub_nop             ·¢Ë͸øÖظ´×¢²áÓû§×¢²áÁбíµÄÐÅÏ¢¡£
+sub_ok              ×¢²á³É¹¦ºóµÄ·¢Ë͸øÓû§µÄÐÅÏ¢¡£
+top                 Ìí¼ÓÔÚϵͳ×Ô¶¯·´À¡Óʼþ¶¥²¿µÄÐÅÏ¢¡£
+</#tn/>
+trailer             Ìí¼ÓÔÚÓʼþÁбíת·¢Óʼþβ²¿µÄÐÅÏ¢¡£
+</#n/>
+unsub_bad           Èç¹ûÈ¡Ïû×¢²áµÄÈ·ÈÏÐÅÏ¢Ë𻵣¬·¢Ë͸øÓû§µÄÐÅÏ¢¡£
+unsub_confirm       ÐèÒªÓû§È·ÈϵÄÈ¡Ïû×¢²áÐÅÏ¢¡£
+unsub_nop           ·¢Ë͸ø²»ÊÇÓʼþÁбí×¢²áÓû§ÒªÇóÈ¡Ïû×¢²áÓʼþÁбíµÄÐÅÏ¢¡£
+unsub_ok            È¡Ïû×¢²á³É¹¦ºó·¢Ë͸øÓû§µÄÐÅÏ¢¡£
+
+</text/edit-done#n/>
+ÒѾ­³É¹¦µÄ¸üÐÂÅäÖÃÎļþ¡£
+</text/info#E/>
+ÓʼþÁбíµÄ½éÉÜÎļþ²»´æÔÚ¡£
+</text/faq#E/>
+FAQ - ÓйØ<#l#>ÓʼþÁбíµÄ³£¼ûÎÊÌâ½â´ð¡£
+
diff --git a/ezmlmrc.cs b/ezmlmrc.cs
new file mode 100644 (file)
index 0000000..7620bec
--- /dev/null
@@ -0,0 +1,1097 @@
+#$Id: ezmlmrc.cs,v 1.8 1999/05/11 03:28:11 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Explicitly specify character-set, when this ezmlmrc was used.
+# Use Quoted-Printable to make averyone happy.
+iso-8859-2:Q
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Omlovám se, ale mám pøíkaz odmítnout pøíspìvky od vás. Kontaktujte <#L#>-owner@<#H#>, máte-li dotazy k tomuto faktu (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Omlouvám se, ale do tohoto listu smìjí pøispívat pouze pøihlá¹ení u¾ivatelé. Jste-li pøihlá¹eným u¾ivatelem, pøepo¹lete tuto zprávu na adresu <#L#>-owner@<#H#>, aby akceptoval va¹i novou adresu (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+List-Post: <mailto:<#L#>@<#H#>>
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+---------------------------------------------------------------------
+Pro odhlá¹ení, po¹lete mail na <#L#>-unsubscribe@<#H#>
+Dal¹í pøíkazy vypí¹e e-mail: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- Administrativní pøíkazy pro list <#l#> ---
+
+Umím automaticky obsluhovat administrativní pøíkazy. Neposílejte
+je prosím do listu! Po¹lete zprávu na adresu pøíslu¹ného pøíkazu:
+
+Pro pøihlá¹ení do listu, po¹lete e-mail na adresu:
+   <<#L#>-subscribe@<#H#>>
+
+Pro odhlá¹ení se z listu pou¾ijte adresu
+   <<#L#>-unsubscribe@<#H#>>
+
+Informace o listu a FAQ (èasto kladené dotazy) získáte zasláním dopisu
+na následující adresy:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#d/>
+Podobné adresy existují i pro list digestù:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+Zprávy èíslo 123 a¾ 145 z archívu (maximálnì 100 zpráv na jeden mail)
+získáte zasláním zprávy na následující adresu:
+   <<#L#>-get.123_145@<#H#>>
+
+</text/bottom#aI/>
+Zprávu èíslo 12 získáte pomocí následující adresy:
+   <<#L#>-get.12@<#H#>>
+
+</text/bottom#i/>
+Seznam zpráv se subjectem a autorem zpráv 123-456 - e-mail:
+   <<#L#>-index.123_456@<#H#>>
+
+Tyto adresy v¾dy vrací seznam 100 zpráv, maximálnì 2000 na jeden po¾adavek.
+Tak¾e ve vý¹e uvedeném pøíkladì ve skuteènosti dostanete seznam zpráv 100-499.
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+Pro získání zpráv se stejným subjectem jako zpráva 12345 po¹lete
+prázdný mail na adresu:
+   <<#L#>-thread.12345@<#H#>>
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+Zprávy ve skuteènosti nemusí být prázdné - budu jejich obsah ignorovat.
+Jediná dùle¾itá vìc je ADRESA na kterou mail posíláte.
+
+Mù¾ete se také do listu pøihlásit pod jinou adresou - napøíklad
+"pepa@domena.cz". Staèí pøidat pomlèku a novou adresu s rovnítkem (=)
+namísto zavináèe:
+<<#L#>-subscribe-pepa=domena.cz@<#H#>>
+
+Odhlásit tuto adresu lze pomocí mailu na adresu
+<<#L#>-unsubscribe-pepa=domena.cz@<#H#>>
+
+V obou pøípadech po¹lu ¾ádost o souhlas na tuto adresu. Kdy¾ ji dostanete,
+jednodu¹e na ni odpovìzte a va¹e pøihlá¹ení/odhlá¹ení se dokonèí.
+
+</text/bottom/>
+Pokud nedosáhnete po¾adovaných výsledkù, kontaktujte mého správce
+na adrese <#L#>-owner@<#H#>. Prosím o trpìlivost, mùj správce je
+podstatnì pomalej¹í ne¾ já ;-)
+</text/bottom/>
+
+--- Pøipojuji kopii po¾adavku, který jsem dostal.
+
+</text/bounce-bottom/>
+
+--- Pøipojuji kopii zprávy, která se mi vrátila.
+
+</text/bounce-num/>
+
+Uschoval jsem si seznam zpráv z listu <#L#>, které se z va¹í adresy
+vrátily.
+
+</#a/>
+Kopie tìchto zpráv mù¾ete získat v archívu.
+</#aI/>
+Pro získání zprávy 12345 z archívu, po¹lete prázdný mail na adresu
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+Zprávy èíslo 123 a¾ 145 získáte z archívu (maximálnì 100 zpráv na jeden mail)
+získáte zasláním zprávy na následující adresu:
+   <<#L#>-get.123_145@<#H#>>
+
+Seznam zpráv se subjectem a autorem zpráv 123-456 - e-mail:
+   <<#L#>-index@<#H#>>
+
+<//>
+Následují èísla zpráv:
+
+</text/dig-bounce-num/>
+
+Uschoval jsem èísla digestù z listu <#L#>-digest, které se vrátily
+z va¹í adresy. Pro ka¾dý takový digest jsem si zapamatoval èíslo
+první zprávy v digestu. Nearchivuji si digesty samotné, ale
+mù¾ete si vy¾ádat jednotlivé zprávy z archívu hlavního listu.
+
+</#aI/>
+
+Pro získání zprávy 12345 z archívu, po¹lete prázdný mail na adresu
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+Zprávy èíslo 123 a¾ 145 získáte z archívu (maximálnì 100 zpráv na jeden mail)
+získáte zasláním zprávy na následující adresu:
+   <<#L#>-get.123_145@<#H#>>
+
+Seznam zpráv se subjectem a autorem zpráv 123-456 - e-mail:
+   <<#L#>-index@<#H#>>
+
+<//>
+Následují èísla prvních zpráv v digestech:
+
+</text/bounce-probe/>
+
+Vracejí se mi zprávy pro vás z listu <#l#>.
+Poslal jsem vám varovnou zprávu, ale ta se také vrátila.
+Pøipojuji kopii varovné zprávy.
+
+Toto je testovací zpráva pro ovìøení, jestli je va¹e adresa dosa¾itelná.
+Pokud se tato zpráva vrátí, zru¹ím va¹i adresu z listu <#l#>@<#H#>
+bez dal¹ího upozornìní. Mù¾ete se pøihlásit znovu posláním prázdné zprávy
+na adresu
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn/>
+
+Vracejí se mi zprávy pro vás z listu <#l#>.
+Pøipojuji kopii první vrácené zprávy, kterou jsem dostal.
+
+Pokud se tato zpráva také vrátí, po¹lu testovací zprávu.
+Pokud se vrátí i testovací zpráva, zru¹ím va¹i adresu z listu <#l#>
+bez dal¹ího upozornìní.
+
+</text/digest#d/>
+Pro pøihlá¹ení do digestu po¹lete mail na adresu
+       <#L#>-digest-subscribe@<#H#>
+
+Pro odhlá¹ení z digestu pou¾ijte adresu
+       <#L#>-digest-unsubscribe@<#H#>
+
+Chcete-li poslat zprávu do listu, pi¹te na adresu
+       <#L#>@<#H#>
+
+</text/get-bad/>
+Promiòte, ale tato zpráva v archívu není.
+
+</text/help/>
+Toto je zpráva se v¹eobecnou nápovìdou. Zpráva, kterou jsem dostal,
+nebyla poslána na ¾ádnou z adres platných pro zasílání pøíkazù.
+
+</text/mod-help/>
+Dìkuji, ¾e jste se uvoli moderovat nebo spravovat list <#L#>@<#H#>.
+
+Moje pøíkazy jsou ponìkud odli¹nìj¹í od jiných listù, ale myslím,
+¾e je seznáte intuitivními a pøíjemnými k pou¾ití.
+
+Tady jsou instrukce pro èinnosti, které pøípadnì mù¾ete vykonávat
+jako správce listu nebo moderátor.
+
+Vzdálené pøihlá¹ení
+-------------------
+
+Jako moderátor mù¾ete pøihla¹ovat a odhla¹ovat libovolnou adresu
+do svého listu. Pro pøihlá¹ení u¾ivatele "pepa@domena.cz" jednodu¹e
+doplòte pomlèku za název pøíkazu a pak tuto adres s rovnítkem
+místo zavináèe. Napøíklad pro pøihlá¹ení vý¹e uvedené adresy
+po¹lete mail na adresu
+   <<#L#>-subscribe-pepa=domena.cz@<#H#>>
+
+Podobnì mù¾ete odhla¹ovat u¾ivatele pomocí mailu na adresu
+   <<#L#>-unsubscribe-pepa=domena.cz@<#H#>>
+
+</#d/>
+Pro digestový list:
+   <<#L#>-digest-subscribe-pepa=domena.cz@<#H#>>
+   <<#L#>-digest-unsubscribe-pepa=domena.cz@<#H#>>
+
+<//>
+To je v¹echno. ®ádný speciální subject ani obsah zprávy není potøeba!
+
+</#r/>
+Po¹lu vám ¾ádost o potvrzení, abych se ujistil, ¾e po¾adavek
+opravdu pochází od vás. Jednodu¹e odpovìzte na mail,
+který obdr¾íte a vá¹ pøíkaz se vykoná.
+</#R/>
+Po¹lu ¾ádost o potvrzení, v tomto pøípadì na adresu <pepa@domena.cz>.
+V¹echno co bude muset udìlat u¾ivatel je odpovìdìt na tuto
+¾ádost.
+<//>
+
+Potvrzení jsou nezbytná, aby se tøetím stranám co nejvíce
+ztí¾ila mo¾nost pøidávat a ru¹it cizí adresu do/z listu.
+
+Uvìdomuji u¾ivatele, kdy¾ se stav jeho pøihlá¹ení zmìní.
+
+Pøihlá¹ení
+----------
+
+Libovolný u¾ivatel se smí pøihla¹ovat a odhla¹ovat do/z listu
+zasláním zprávy na adresu
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#d/>
+Pro digestový list:
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+U¾ivatel obdr¾í ¾ádost o potvrzení, abych se ujistil,
+¾e mu skuteènì daná adresa patøí. Jakmile se toto ovìøí,
+u¾ivatel je odhlá¹en.
+
+</#s/>
+Proto¾e toto je list s moderovaným pøihlá¹ením, po¹lu dal¹í
+¾ádost o potvrzení moderátorovi. Proto¾e u¾ivatel ji¾ potvrdil
+pøání být na listu, mù¾ete si jako moderátor být dostateènì jist,
+¾e adresa pøihla¹ovaného je skuteèná. Pokud chcete pøijmout
+u¾ivatelovu ¾ádost, jednodu¹e po¹lete odpovìï na tuto zprávu.
+Pokud ne, sma¾te tuto zprávu a pøípadnì kontaktujte u¾ivatele
+pro dal¹í informace.
+</#S/>
+Pøihlíá¹ení funguje stejnì.
+<//>
+
+U¾ivatel také mù¾e pou¾ít adresu
+
+   <<#L#>-subscribe-jana=domena.cz@<#H#>>
+   <<#L#>-unsubscribe-jana=domena.cz@<#H#>>
+
+pro zaslání mailu pro "jana@domena.cz". Pokud tato skuteènì má
+vý¹e uvedenou adresu, obdr¾í ¾ádost o potvrzení a mù¾e ji
+potvrdit.
+
+Va¹e adresa a identita není otevøena pøihlá¹enému, pokud mu sám
+nepo¹lete mail.
+
+</#rl/>
+Pro získání seznamu pøihlá¹ených pro list <#L#>@<#H#>, po¹lete
+zprávu na adresu
+   <<#L#>-list@<#H#>>
+
+Seznam provedených transakcí listu <#L#>@<#H#> získáte z adresy
+   <<#L#>-list@<#H#>>
+
+</#rld/>
+Pro pøihlá¹ené do digestu:
+   <<#L#>-digest-list@<#H#>>
+a
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+Mù¾ete vzdálenì editovat textové soubory, ze kterých se sestavují
+odpovìdi, které posílám. Chcete-li získat seznam editovatelných souborù
+a pokyny pro editaci, napi¹te na adresu
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+Moderované pøíspìvky
+--------------------
+
+Pokud je list moderovaný, ulo¾ím si zprávy, které obdr¾ím a po¹lu vám
+kopii a instrukce. Zpráva pro vás bude mít subject "MODERATE for ...".
+
+Chcete-li zprávu pøijmout, staèí poslat odpovìï (na adresu v "Reply-To:"),
+kterou nastavím na pøíkaz pro pøijetí zprávy do listu. Nemusíte 
+posílat obsah pùvodní zprávy. Ve skuteènosti ignoruji cokoli, co mi
+po¹lete, pokud bude adresa, na kterou to po¹lete, korektní.
+
+Pokud chcete zprávu odmítnout, po¹lete odpovìï na adresu ve "From:",
+kterou nastavím na pøíkaz pro odmítnutí zprávy. Toto se obvykle udìlá
+pøíkazem "Odpovìz v¹em" ve va¹em po¹tovním klientovi, pøièem¾
+sma¾ete v¹echny ostatní adresy kromì adresy pro odmítnutí (reject).
+Mù¾ete pøidat komentáø odesílateli - napi¹te jej mezi dva øádky,
+zaèínající tøema znaky "%". Po¹lu autorovi pouze tento komentáø,
+co¾ neprozradí va¹i identitu.
+
+Se zprávou nalo¾ím podle první odpovìdi, kterou dostanu.
+Uvìdomím vás, pokud mi po¹lete po¾adavek na potvrzení ji¾
+odmítnuté zprávy a naopak,
+
+Pokud nedostanu odpovìï od moderátora do urèité doby (implicitnì 5 dní),
+vrátím zprávu odesílateli s vysvìtlením, ¾e byla odmítnuta.
+Jako administrátor mù¾ete také list nastavit tak, ¾e ignorované
+zprávy jsou jednodu¹e smazány bez upozornìní odesílateli.
+<//>
+
+Dovolená
+--------
+Pokud jste doèasnì na jiné adrese, staèí si pøeposlat v¹echny zprávy,
+které mají správnou hlavièku "Mailing-List:" (nebo v¹echny se subjectem
+"MODERATE for <#L#>@<#H#>" (nebo "CONFIRM subscribe to <#L#>@<#H#>")
+na novou adresu. Mù¾ete také pøeposílat tyto zprávy pøíteli, který
+bude moderovat za vás. Prosím uvìdomte o tomto také správce listserveru.
+
+Pokud chcete automaticky potvrdit v¹echny po¾adavky bìhem své nepøítomnosti,
+nastavte si po¹tovního klienta na posílání automatických odpovìdí na zprávy,
+které splòují vý¹e uvedená kritéria.
+
+</#r/>
+Pokud zkusíte dìlat vzdálenou administraci z adresy, která není
+va¹e vlastní, bude o potvrzení po¾ádán u¾ivatel, nikoli vy.
+Po schválení u¾ivatelem po¹lu ¾ádost o potvrzení v¹em moderátorùm.
+Toto dìlám proto, ¾e nemám zpùsob, jak zjistit, ¾e jste to skuteènì vy,
+kdo poslal pùvodní po¾adavek.
+
+Berte také na vìdomí, ¾e v tomto pøípadì je vá¹ pùvodní po¾adavek
+(vèetnì va¹í adresy!) zaslán u¾ivateli s ¾ádostí o potvrzení.
+<//>
+
+Mnoho ¹tìstí!
+
+PS: Prosím kontaktujte správce listu (<#L#>-owner@<#H#>),
+budete-li mít dotazy nebo problémy.
+
+</text/mod-reject/>
+Je mi líto, ale va¹e ní¾e citované zpráva nebyla potvrzena moderátorem.
+Pokud moderátor pøipojil nìjaký komentáø, uvádím jej ní¾e.
+</text/mod-request/>
+Ní¾e citovaná zpráva byla zaslána na adresu listu <#L#>@<#H#>.
+Pokud souhlasíte s distribucí této zprávy v¹em pøihlá¹eným, po¹lete
+mail na adresu
+
+!A
+
+Toho obvykle dosáhnete pomocí tlaèítka "Odpovìï/Reply". Mù¾ete zkontrolovat
+adresu, zaèíná-li øetìzcem "<#L#>-accept". Pokud toto nefunguje, staèí
+zkopírovat adresu a ulo¾it ji do políèka "To:" nové zprávy.
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#A#>
+<//>
+
+Chcete-li zprávu odmítnout a zpùsobit její vrácení odesílateli,
+po¹lete zprávu na adresu
+
+!R
+
+Toto se bvykle nejsnáze udìlá pomocí tlaèítka "Odpovìz v¹em/Reply to all"
+a následného vymazání v¹ech adres kromì té, která zaèíná "<#L#>-reject".
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#R#>
+<//>
+
+Není tøeba kopírovat zprávu ve svém potvrzení nebo odmítnutí této zprávy.
+Chcete-li poslat komentáø odesílateli odmítnuté zprávy, doplòte jej
+mezi následující øádky zaèínající tøemi znaky "%":
+
+%%% Zaèátek komentáøe
+%%% Konec komentáøe
+
+Dìkuji za spolupráci.
+
+--- Dále uvádím zaslanou zprávu.
+
+</text/mod-sub#E/>
+--- Pøihlásil nebo odhlásil jsem vás na ¾ádost moderátora
+listu <#l#>@<#H#>.
+
+Pokud to není akce, se kterou souhlasíte, po¹lete co nejdøíve
+stí¾nost nebo dal¹í komentáøe správci listu (<#l#>-owner@<#H#>).
+
+Chcete-li získat podrobnìj¹í návod pro práci s listem <#L#>, po¹lete
+prázdnou zprávu na adresu
+<#L#>-help@<#H#>.
+
+</text/mod-timeout/>
+Je mi líto, ale moderátor listu <#L#> nezareagoval na vá¹ pøíspìvek.
+Tento tedy pova¾uji za odmítnutý a vracím vám jej. Pokud máte pocit,
+¾e do¹lo k chybì, obra»te se pøímo na moderátora listu.
+
+--- Dále uvádím vámi zaslanou zprávu.
+
+</text/mod-sub-confirm/>
+®ádám zdvoøile oprávnìní pøidat adresu
+
+!A
+
+do seznamu ètenáøù listu <#l#>. Po¾adavek vze¹el buïto od vás,
+nebo ji¾ byl ovìøen u potenciálního ètenáøe.
+
+Souhlasíte-li, po¹lete prázdný mail na adresu
+
+!R
+
+Toho obvykle dosáhnete pomocí tlaèítka "Odpovìï/Reply". Mù¾ete zkontrolovat
+adresu, zaèíná-li øetìzcem "<#L#>-sc". Pokud toto nefunguje, staèí
+zkopírovat adresu a ulo¾it ji do políèka "To:" nové zprávy.
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#R#>
+<//>
+
+Nesouhlasíte-li, ignorujte tuto zprávu.
+
+Díky za spolupráci!
+
+</text/mod-unsub-confirm/>
+Obdr¾el jsem po¾adavek na zru¹ení adresy
+
+!A
+
+z listu <#l#>. Souhlasíte-li, po¹lete prázdnou odpovìï
+na tuto adresu:
+
+!R
+
+Toho obvykle dosáhnete pomocí tlaèítka "Odpovìï/Reply". Mù¾ete zkontrolovat
+adresu, zaèíná-li øetìzcem "<#L#>-sc". Pokud toto nefunguje, staèí
+zkopírovat adresu a ulo¾it ji do políèka "To:" nové zprávy.
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#R#>
+<//>
+
+Nesouhlasíte-li, ignorujte tuto zprávu.
+
+Dìkuji za spolupráci!
+
+</text/sub-bad/>
+Hmmm, tohle èíslo potvrzení nevypadá platnì.
+
+Nejèastìj¹ím dùvodem výskytu neplatných èísel je vypr¹ení èasu.
+Musím dostat odpovìï na ka¾dý po¾adavek nejpozdìji do deseti dnù.
+Ujistìte se také, ¾e v odpovìdi, kterou jsem obdr¾el, bylo _celé_
+èíslo potvrzení. Nìkteré e-mailové programy mohou oøíznout èást
+adresy pro odpovìï, která mù¾e být i dosti dlouhá.
+
+Posílám novou ¾ádost o potvrzení. Pro potvrzení, ¾e chcete pøidat adresu
+
+!A
+
+do listu <#l#>, po¹lete prázdnou odpovìï na adresu
+
+!R
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#R#>
+<//>
+
+Je¹tì jednou: ujistìte se, ¾e adresa pro odpovìï je skuteènì v poøádku
+pøedtím, ne¾ potvrdíte tuto ¾ádost.
+
+Omlouvám se za potí¾e.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm/>
+Chcete-li opravdu pøidat adresu
+
+!A
+
+do listu <#l#>, po¹lete prázdnou zprávu na adresu
+
+!R
+
+Toho obvykle dosáhnete pomocí tlaèítka "Odpovìï/Reply". Mù¾ete zkontrolovat
+adresu, zaèíná-li øetìzcem "<#L#>-sc". Pokud toto nefunguje, staèí
+zkopírovat adresu a ulo¾it ji do políèka "To:" nové zprávy.
+
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#R#>
+<//>
+
+Vy¾adování tohoto potvrzení má dva dùvody. Za prvé ovìuje, ¾e jsem
+schopen zasílat mail na va¹i adresu. A za druhé, chrání vás v pøípadì,
+¾e nìkdo zfal¹uje ¾ádost o pøihlá¹ení s va¹ím jménem.
+
+</#q/>
+Nìkteré po¹tovní programy jsou chybné a nemohou zpracovávat dlouhé
+adresy. Pokud nemù¾ete odpovìdìt na tuto adresu, po¹lete
+zprávu na adresu <<#L#>-request@<#H#>>
+a vlo¾te celou vý¹e uvedenou adresu do subjectu.
+
+</text/sub-confirm#s/>
+Tento list je moderovaný. Jakmile po¹lete potvrzení, po¾adavek
+bude poslán moderátorovi tohoto listu. Uvìdomím vás, jakmile
+bude pøihlá¹ení hotovo.
+
+</text/sub-nop/>
+Potvrzení: Adresa
+
+!A
+
+byla v ji¾ v listu <#l#>, kdy¾ jsem obdr¾el va¹i ¾ádost,
+a zùstává pøihlá¹ena.
+
+</text/sub-ok#E/>
+Potvrzení: Pøidal jsem adresu
+
+!A
+
+do listu <#l#>.
+
+Vítejte v listu <#l#>@<#H#>!
+
+Prosím uschovejte si tuto zprávu pro informaci, z jaké adresy
+bylo pøihlá¹ení do listu provedeno. Budete ji potøebovat v pøípadì,
+¾e se budete chtít odhlásit nebo zmìnit svoji adresu.
+
+</text/top/>
+Zdravím, tady je program ezmlm. Spravuji diskusní list
+<#l#>@<#H#>.
+
+</#x/>
+Pracuji pro svého správce, který mù¾e být zasti¾en na adrese
+at <#l#>-owner@<#H#>.
+
+</text/unsub-bad/>
+Hmmm, tohle èíslo potvrzení nevypadá platnì.
+
+Nejèastìj¹ím dùvodem výskytu neplatných èísel je vypr¹ení èasu.
+Musím dostat odpovìï na ka¾dý po¾adavek nejpozdìji do deseti dnù.
+Ujistìte se také, ¾e v odpovìdi, kterou jsem obdr¾el, bylo _celé_
+èíslo potvrzení. Nìkteré e-mailové programy mohou oøíznout èást
+adresy pro odpovìï, která mù¾e být i dosti dlouhá.
+
+Posílám novou ¾ádost o potvrzení. Pro potvrzení, ¾e chcete zru¹it adresu
+
+!A
+
+do listu <#l#>, po¹lete prázdnou odpovìï na adresu
+
+!R
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+        mailto:<#R#>
+<//>
+
+Je¹tì jednou: ujistìte se, ¾e adresa pro odpovìï je skuteènì v poøádku
+pøedtím, ne¾ potvrdíte tuto ¾ádost.
+
+Omlouvám se za potí¾e.
+
+        <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm/>
+Potvrzujete-li, ¾e chcete adresu
+
+!A
+
+zru¹it z listu <#l#>, po¹lete prázdnou odpovìï na adresu
+
+!R
+
+Toho obvykle dosáhnete pomocí tlaèítka "Odpovìï/Reply". Mù¾ete zkontrolovat
+adresu, zaèíná-li øetìzcem "<#L#>-sc". Pokud toto nefunguje, staèí
+zkopírovat adresu a ulo¾it ji do políèka "To:" nové zprávy.
+</#x/>
+
+Mù¾ete také zkusit kliknout zde:
+       mailto:<#R#>
+<//>
+
+Nekontroloval jsem, je-li va¹e adresa v souèasné dobì na listu.
+Chcete-li zjistit, ze které adresy bylo provedeno pøihlá¹ení,
+podívejte se do zpráv, které dostáváte z listu. Ka¾dá zpráva
+má adresu v návratové cestì:
+<<#l#>-return-<number>-jana=domena.cz@<#H#>.
+
+</#q/>
+Nìkteré po¹tovní programy jsou chybné a nemohou zpracovávat dlouhé
+adresy. Pokud nemù¾ete odpovìdìt na tuto adresu, po¹lete
+zprávu na adresu <<#L#>-request@<#H#>>
+a vlo¾te celou vý¹e uvedenou adresu do subjectu.
+
+</text/unsub-nop/>
+Potvrzení: Adresa
+
+!A
+
+nebyla na listu <#l#> v dobì, kdy jsem obdr¾el
+vá¹ po¾adavek a není na nìm ani teï.
+
+Pokud se odhlásíte, ale stále vám budou chodit dopisy, je pøihlá¹ení
+provedeno pod jinou adresou, ne¾ kterou v souèasné dobì pou¾íváte.
+Podívejte se prosím do hlavièek zpráv na text
+
+"Return-Path: <<#l#>-return-1234-pepa=domena.cz@<#H#>>'
+
+Odhla¹ovací adresa pro tohoto u¾ivatele pak bude
+"<#l#>-unsubscribe-pepa=domena.cz@<#H#>".
+Staèí poslat mail na tuto adresu s tím, ¾e pepa=domena.cz nahradíte
+skuteènými hodnotami. Pak odpovíte na ¾ádost o potvrzení a mìla
+by vám dojít zpráva o odhlá¹ení z listu.
+
+V nìkterých po¹tovních programech si musíte zprávu zobrazit vèetnì
+hlavièek, jinak není hlavièka "Return-Path" viditelná.
+
+Pokud toto stále nefunguje, pak je mi líto, ale nemohu vám pomoci.
+Prosím PREPO©LETE (forward) zprávu z listu spolu s poznámkou o tom,
+èeho se sna¾íte dosáhnout a seznamem adres, ze kterých potenciálnì
+mù¾ete být pøihlá¹en[a] mému správci:
+
+    <#l#>-owner@<#H#>
+
+Tento se bude sna¾it vá¹ problém øe¹it. Mùj správce je tro¹ku pomalej¹í
+ne¾ já, tak¾e prosím o trochu trpìlivosti.
+
+</text/unsub-ok/>
+Potvrzení: Zru¹il jsem adresu
+
+!A
+
+z listu <#l#>. Tato adresa ji¾ dále není v seznamu
+pøihlá¹ených.
+
+</text/edit-do#n/>
+Prosím editujte následující soubor a po¹lete jej na adresu
+
+!R
+
+Vá¹ po¹tovní program by mìl mít tlaèítko "Odpovìï/Reply",
+který tuto adresu pou¾ije automaticky.
+
+Umím dokonce sám smazat citovací znaèky, které vá¹ po¹tovní program
+pøidá pøed text, pokud ov¹em ponecháte znaèkovací øádky samotné.
+
+Tyto øádky zaèínají tøemi znaky procento. Nesmí být modifikovány
+(s výjimkou pøípadných znakù pøed nimi, pøidaných pøipadnì va¹ím
+po¹tovním klientem).
+
+</text/edit-list#n/>
+Pøíkaz <#L#>-edit.soubor mù¾e být pou¾it vzdáleným správcem k editaci
+textových souborù, ze kterých se skládají odpovìdi pro list <#L#>@<#H#>.
+
+Následuje seznam pøíslu¹ných souborù a krátký popis toho, kdy je
+jejich obsah vyu¾íván. Chcete-li editovat soubor, staèí poslat
+mail na adresu <#L#>-edit.soubor, pøièem¾ je nutno "soubor"
+nahradit skuteèným jménem souboru. Obdr¾íte soubor spolu s instrukcemi,
+jak tento soubor editovat.
+
+Soubor              Pou¾ití
+
+bottom              pøidává se za ka¾dou odpovìï. V¹eobecné informace.
+digest              "administrativní" èást digestù.
+faq                 èasto kladené dotazy, specifické pro tento list.
+get_bad             neni-li zpráva nalezena v archívu.
+help                v¹eobecná nápovìda (mezi "top" a "bottom").
+info                informace o listu. První øádek by mìl dávat smysl sám o sobì.
+mod_help            nápovìda pro moderátory.
+mod_reject          odesílateli odmítnuté zprávy.
+mod_request         moderátorovi spolu s pøíspìvkem.
+mod_sub             pøihla¹ovanému, jakmile jeho pøihlá¹ení potvrdí moderátor.
+mod_sub_confirm     moderátorovi s ¾ádostí o potvrzení pøihlá¹ení.
+mod_timeout         odesílateli, nestihne-li moderátor potvrdit zprávu.
+mod_unsub_confirm   administrátorovi s ¾ádostí o potvrzení odhlá¹ení.
+sub_bad             odesílateli, bylo-li pøihlá¹ení neplatné.
+sub_confirm         odesílateli - ¾ádost potvrzení pøihlá¹ení.
+sub_nop             odesílateli - pøi pokusu o opìtovné pøihlá¹ení
+sub_ok              odesílateli - oznamuje pøihlá¹ení.
+top                 zaèátek v¹ech odpovìdí.
+</#tn/>
+trailer             pøidá se za ka¾dý pøíspìvek do listu.
+</#n/>
+unsub_bad           odesílateli, byla li ¾ádost o odhlá¹ení neplatná.
+unsub_confirm       odesílateli - ¾ádost o potvrzení odhlá¹ení.
+unsub_nop           odesílateli - nebyl-li pøihlá¹en a sna¾il-li se odhlásit.
+unsub_ok            odesílateli po úspì¹ném odhlá¹ení.
+
+</text/edit-done#n/>
+Textový soubor byl úspì¹nì upraven.
+</text/info#E/>
+®ádná informace nebyla k tomuto listu poskytnuta.
+</text/faq#E/>
+FAQ - Èasto kladené dotazy v listu <#l#>@<#H#>.
+
+[ ®ádné zatím nejsou dostupné ]
+
+
diff --git a/ezmlmrc.da b/ezmlmrc.da
new file mode 100644 (file)
index 0000000..8733272
--- /dev/null
@@ -0,0 +1,1097 @@
+0.324 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.da,v 1.17 1999/12/23 23:08:19 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Explicitly specify character-set, when this ezmlmrc was used.
+# Use Quoted-Printable to make averyone happy.
+iso-8859-1:Q
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Desværre. Jeg har fået instruktion om at afvise post fra dig. Kontakt <#L#>-owner@<#H#> hvis du mener der må være sket en fejl (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Beklager, kun tilmeldte må poste. Hvis du er tilmeldt under et andet navn, videresend venligst dette brev til <#L#>-owner@<#H#> så din nye adresse kan blive tilmeldt (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+list-help: <mailto:<#l#>-help@<#h#>>
+list-unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+list-post: <mailto:<#L#>@<#H#>>
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+---------------------------------------------------------------------
+Vil du fjernes fra listen, så skriv til: <#L#>-unsubscribe@<#H#>
+Vil du have en kommandooversigt, skriv til: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- Kommandooversigt:
+
+Jeg kan håndtere administrative forespørgsler automatisk.
+De må IKKE sendes til <#L#>, da de så kommer ud til
+alle tilmeldte på listen (og det virker ikke).
+Send blot et brev uden indhold til en af adresserne som eksemplerne
+viser:
+
+Tilmelding: send et tomt brev til:
+   <<#L#>-subscribe@<#H#>>
+
+Framelding: send et tomt brev til:
+   <<#L#>-unsubscribe@<#H#>>
+
+Send email til følgende for at få info eller FAQ for denne liste:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+  
+
+</#d/>
+Lignende adresser findes for udtræks listen:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+Hent alle brevene i intervallet 123 til 145 (max. 100 ad gangen),
+send et tomt brev til:
+   <<#L#>-get.123_145@<#H#>>
+
+</text/bottom#aI/>
+Hent brev nummer 12, send et tomt brev til:
+   <<#L#>-get.12@<#H#>>
+
+</text/bottom#i/>
+Hent en oversigt over breve i intervallet 123 til 456, send et
+tomt brev til:
+   <<#L#>-index.123_456@<#H#>>
+
+Resultatet kommer i breve med hundrede titler pr. brev,
+max. 2000 i alt, så eksemplet vil vise dig brev 100 til 499.
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+Hent alle breve med samme emne som brev 123:
+   <<#L#>-thread.123@<#H#>>
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+Dine administrative breve behøver ikke at være tomme, men deres
+indhold vil blive ignoreret. Kommandoen ligger alene i
+modtageradressen 
+
+Hvis du vil tilmelde dig med en anden adresse end den du skriver
+fra, f.eks. jens@ferieby.dk, skal du skrive til:
+<<#L#>-subscribe-jens=ferieby.dk@<#H#>>
+Bemærk -jens og at @ i adressen er lavet om til et =.
+
+Du kan framelde din alternative adresse ved at skrive til:
+<<#L#>-unsubscribe-jens=ferieby.dk@<#H#>>
+
+jens@ferieby.dk vil i begge tilfælde få et brev som skal
+bekræftes for at få udført til/fra meldingen.
+
+</text/bottom/>
+Hvis disse instruktioner ikke er tilstrækkelige, og du ikke
+har held med dine kommandoer, kan du skrive til min ejer
+<#L#>-owner@<#H#>, som er et menneske
+(og som derfor ikke svarer så hurtigt som jeg gør).
+
+Uanset hvor håbløst det ser ud, så forsøg IKKE at skrive
+til selve mailing listen, f.eks. for at blive frameldt.
+</text/bottom/>
+
+--- Vedlagt: En kopi af det kommando brev du sendte til mig.
+
+</text/bounce-bottom/>
+
+--- Vedlagt: En kopi af det brev som ikke kunne afleveres.
+
+</text/bounce-num/>
+
+Jeg har hold øje med hvilke breve fra <#L> listen der er
+kommet retur fordi de ikke kunne afleveres til dig.
+
+</#a/>
+Kopier af disse brev er muligvis i arkivet.
+</#aI/>
+Du kan f.eks. hente brev 123 fra arkivet ved at send et tomt
+brev til:
+   <<#L#>-get.123@<#H#>>
+
+</#ia/>
+Du kan f.eks. hente brev 123-145 (max. 100 ad gangen), ved at
+sende et tomt brev til:
+   <<#L#>-get.123_145@<#H#>>
+
+Du kan få en oversigt over emne og afsender for de sidste 100
+breve ved at sende et tomt brev til:
+   <<#L#>-index@<#H#>>
+
+<//>
+Her er brevnumrene:
+
+</text/dig-bounce-num/>
+
+Jeg har gemt en liste over hvilke udtræk fra <#L#> listen som er
+kommet retur fordi de ikke kunne afleveres. Kopier af disse breve
+kan hentes i arkivet. For hvert udtræk du mangler, kender jeg
+nummeret på det første brev i udtrækket.
+Jeg gemmer ikke selve udtrækkene, men hvis brevene er der endnu,
+kan du f.eks. få brev 12345 ved at sende et tomt brev til:
+<#L#>-get.12345@<#H#>.
+
+</#ia/>
+Du kan hente en oversigt over brevene 123-145 (max. 100 ad gangen)
+ved at sende et tomt brev til:
+   <<#L#>-get.123_145@<#H#>>
+
+Du kan hente en oversigt over emne og afsender i de nyeste 100
+breve ved at sende et tomt brev til:
+   <<#L#>-index@<#H#>>
+
+<//>
+Her er en liste over brevnumre i udtrækket:
+
+</text/bounce-probe/>
+
+<#L#> listen har haft svigtende held med at aflevere breve til dig.
+Jeg har sendt dig en advarsel, men den kom retur. Fejlmeddelse vedlagt.
+
+Dette er en test for at se om post kan afleveres til dig. Hvis
+det ikke lykkes, bliver du automatisk fjernet fra <#L#>listen.
+Du kan melde dig til igen ved at sende et tomt brev til:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn/>
+
+Mine breve til dig fra <#l#> er kommet retur uden at kunne afleveres.
+Kopi af fejlmeddelsen er vedlagt.
+
+Hvis dette brev også kommer retur uden at kunne afleveres, sender
+jeg et test brev. Hvis det heller ikke kan afleveres, fjerner jeg
+din adresse fra modtagerlisten uden at give yderligere besked.
+
+</text/digest#d/>
+Du kan abonnere på listeudtræk ved at sende et tomt brev til:
+       <#L#>-digest-subscribe@<#H#>
+
+Du kan ophæve listeudtræk abonnementet ved at sende et tomt
+brev til:
+       <#L#>-digest-unsubscribe@<#H#>
+
+Du kan sende et brev ud til alle liste modtagerne ved at skrive til:
+       <#L#>@<#H#>
+
+</text/get-bad/>
+Ikke godt. Det brev findes ikke i arkivet.
+
+</text/help/>
+Dette er en generel hjælpetekst. Det brev jeg modtog fra dig var
+ikke sendt til nogen af mine kommandoadresser.
+
+</text/mod-help/>
+Tak fordi du vil moderere <#L#>@<#H#> listen.
+
+Mine kommandoer er lidt anderledes end i andre postlister,
+men jeg tror du bliver glad for mig når du lærer mig at kende.
+
+Her er en oversigt over hvordan du gør:
+
+Fjernadministration
+--------------------
+Som moderator kan du til- og framelde hvem som helst,
+f.eks. jens@ferieby.dk, ved at sende et tomt brev til:
+   <<#L#>-subscribe-jens=ferieby.dk@<#H#>> eller
+   <<#L#>-unsubscribe-jens=ferieby.dk@<#H#>>
+Bemærk at det ene @ er lavet om til =.
+
+</#d/>
+For udtrækslisterne (digest):
+   <<#L#>-digest-subscribe-jens=ferieby.dk@<#H#>>
+   <<#L#>-digest-unsubscribe-jens=ferieby.dk@<#H#>>
+
+<//>
+Så simpelt er det. Der behøver hverken være emne eller
+indhold i dine kommandobreve.
+
+</#r/>
+Jeg sender dig så et brev som du skal kvittere for ved at
+sende et tomt svar på tilbage.
+</#R/>
+Jeg sender et brev til brugeren som vedkommende skal bekræfte,
+for at abonnementet kan træde i kraft.
+<//>
+
+Disse dialoger er nødvendige for at sikre at den person jeg har
+kontakt med er den han/hun giver sig ud for at være. Ellers
+kunne en fremmed tilmelde et uvidende offer til en masse
+postlister.
+
+Jeg sender en bekræftelse til brugeren når abonnementsstatus
+er ændret.
+
+Tilmelding
+----------
+
+Enhver kan til- eller framelde sig ved at sende et tomt
+brev til:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#d/>
+For udtrækslisterne:
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+Brugeren vil modtage et brev som skal bekræftes med et tomt
+svar for at sikre at vedkommende råder over den pågældende
+postadresse.
+
+</#s/>
+Eftersom denne liste er tilmeldingsmodereret, skal en moderator
+godkende tilmeldingen. Du kan godkende tilmeldingen ved at
+leve et tomt svar på CONFIRM brevet. (Eller du kan lade være).
+</#S/>
+Tilmelding sker på samme måde.
+<//>
+
+Brugeren kan også skrive til:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+for at tilmelde "mary@host.domain". Kun hvis hun modtager post
+på den adresse, vil hun modtage bekræftelses forespørgslen og
+være i stand til at svare på den.
+
+Din adresse og identitet er skjult for abonnenten medmindre
+du skriver direkte til vedkommende.
+
+</#rl/>
+En liste over tilmeldte fås ved at sende et tomt brev til:
+  <<#L#>-list@<#H#>>
+
+En transaksjonslog fås ved at sende et tomt brev til:
+  <<#L#>-list@<#H#>>
+
+</#rld/>
+Over digest tilmeldte:
+   <<#L#>-digest-list@<#H#>>
+og:
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+Du kan fjernrette de tekstsvar som listen udsender. En liste
+over filer samt instruktioner fås ved at sende et tomt brev til:
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+Modererede lister
+-----------------
+Når listen er modereret, gemmer jeg det indsendte brev og sender
+dig en kopi sammen med en instruktion. Brevet du modtager vil
+have teksten "MODERATE for ..." i emne linjen.
+
+Du godkender at brevet bliver sendt til listen ved blot at
+svare på "MODERATE for ..." brevet. Du behøver ikke inkludere
+selve brevets indhold i dit svar.
+
+Hvis du vil afvise det indsendte brev, svarer du til brevets
+'From:' adresse istedetfor som normalt, dets 'Reply-To:" adresse.
+Du kan skrive en kommentar til indsenderen, om hvorfor du afviser
+brevet, imellem de to linjer som begynder med %%%. Din identitet
+forbliver anonym.
+
+Det første svar jeg får fra en moderator afgør det videre
+forløb. Hvis en anden moderator har svaret før dig, får du
+besked.
+
+Hvis jeg ikke får svar fra en moderator inden for en bestemt
+tid (normalt 5 dage), returnerer jeg brevet til afsender med
+en forklaring. Din administrator kan evt. konfigurere listen
+så ignorerede "MODERATE for ..." breve bliver slettet uden
+at brevskriveren informeres.
+<//>
+
+Ferie
+-----
+Hvis du midlertidigt har en anden adresse, kan du videresende alle
+breve som har en korrekt 'Mailing-List:' linje i brevhovedet, eller
+med andre ord, har titlen 'MODERATE for <#L#>@<#H#>'
+eller 'CONFIRM subscribe to <#L#>@<#H#>' til din nye adresse eller en
+anden som passer moderator hvervet mens du er væk. Sørg for at listens
+ejer er indforstået med dette.
+
+Hvis du hellere vil lave automatisk godkendelse af alle breve
+mens du er væk, kan du bede dit post program om at lave auto-svar
+på alle breve som har et emne der svarer til de ovennævnte kriterier.
+
+</#r/>
+Hvis du forsøger at fjernadministrere fra en adresse som ikke er
+din, bliver abonnenten spurgt om bekræftelse, derefter moderator(erne).
+Jeg er nødt til at gøre det på den måde når jeg ikke kan verificere
+din identitet.
+
+Bemærk at din originale forespørgsel (og din adresse) bliver sendt
+til abonnenten i dette filfælde!
+<//>
+
+Held og lykke!
+
+PS: Kontakt venligst liste ejeren (<#L#>-owner@<#H#>)
+hvis du har spørgsmål eller problemer.
+
+</text/mod-reject/>
+Jeg må desværre meddele at dit brev (vedlagt) ikke er blevet godkendt
+af moderator. Hvis moderatoren har skrevet en kommentar, ses den
+nedenfor.
+</text/mod-request/>
+Det vedlagte brev er blevet postet til <#L#>@<#H#> listen.
+Hvis du vil godkende at det kommer ud til alle abonnenterne,
+skriver du til:
+
+!A
+
+Bekræftelse sker normalt blot ved at svare på dette brev. Svar adressen
+skulle gerne starte med "<#L#>-accept". Hvis det ikke er tilfældet, må
+den korrekte svar adresse overføres manuelt til modtagerfeltet, evt.
+med klippe/klistre.
+Mulighederne afhænger af at du har et fornuftigt postprogram.
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#A#>
+<//>
+
+Hvis du istedet vil afvise at brevet postes til listen, svar til:
+
+!R
+
+Normalt er det nemmest at lave et "svar-til-alle", og fjerne alle
+adresserne undtagen den der starter med
+"<#L#>-reject".
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Du behøver ikke inkludere brevet i dit svar for at acceptere eller
+afvise det. Hvis du vil sende en besked til afsenderen af et
+afvist brev, kan du skrive beskeden mellem de to linjer der
+begynder med %%%.
+
+%%% Kommentar start
+%%% Kommentar slut
+
+Tak for hjælpen!
+
+--- Her er det indsendte brev.
+
+</text/mod-sub#E/>
+--- Jeg har til- eller afmeldt dig fra <#l#>@<#H#> på begæring
+af moderator.
+
+Hvis du ikke er indforstået med dette, skriv snarest til liste
+ejeren på (<#l#>-owner@<#H#>).
+
+Hvis du ønsker oplysninger om hvordan man bruger <#L#> arkivet,
+send et tomt brev til <#L#>-help@<#H#>.
+
+</text/mod-timeout/>
+Moderator fra <#L#> listen har desværre ikke reageret på dit brev.
+Derfor sender jeg det hermed retur til dig. Hvis du mener det er
+en fejl, send brevet igen eller kontakt en moderator direkte.
+
+--- Enclosed, please find the message you sent.
+
+</text/mod-sub-confirm/>
+Jeg beder hermed om din tilladelse til at tilføje
+
+!A
+
+som abonnent på <#l#> listen. Denne forespørgsel kom enten fra
+dig, eller den er allerede blevet godkendt af den potentielle
+abonnent.
+
+Bekræft ved at sende et tomt svar til denne adresse:
+
+!R
+
+Dit post program burde have en svarfunktion som automatisk bruger
+den adresse. (Ellers må man klippe/klistre).
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Hvis du ikke vil godkende, kan du bare glemme dette brev.
+
+Tak for hjælpen!
+
+</text/mod-unsub-confirm/>
+Nogen har bedt om at få fjernet
+
+!A
+
+fra <#l#> listen. Du kan godkende ved at lave et tomt svar
+til denne adresse:
+
+!R
+
+Normalt sker dette ved at du svarer på dette brev. Der behøver ikke
+være noget indhold. Hvis det ikke virker, må svaradressen kopieres
+over i modtagerfeltet på dit svarbrev, f.eks. med en mus.
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Hvis du ikke vil godkende, kan du bare ignorere dette brev.
+
+Tak for hjælpen!
+
+</text/sub-bad/>
+Ups, bekræftelsesnummeret er ikke gyldigt.
+
+Den mest almindelige grund til ugyldige numre er at deres levetid
+er udløbet. De gælder kun i 10 dage.
+Det kan også være at noget af nummeret mangler i det brev du har
+sendt til mig. Nogle post programmer kan ikke finde ud af lange
+adresser.
+
+Hermed et nyt bekræftelsesnummer. For at bekræfte at du vil have
+
+!A
+
+tilføjet til <#l#> listen, send et tomt svar til denne adresse:
+
+!R
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Kontroller hellere adressen igen, for at være sikker på det hele
+er der, inden du svarer.
+
+Undskyld besværet.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm/>
+Hvis du vil bekræfte at du vil have
+
+!A
+
+tilføjet til <#l#> listen, send et tomt svar til denne adresse:
+
+!R
+
+Normalt kan det simpelthen gøres ved at besvare dette brev.
+Der behøver ikke være noget indhold i brevet.
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Denne bekræftelse tjener to formål. For det firste konstateres om
+jeg er i stand til at sende post til dig. For det andet sikrer det
+dig imod at andre melder sig til med din adresse.
+
+</#q/>
+Nogle post programmer er for dårlige og kan ikke håndtere lange
+adresser. Hvis du er ramt af et sådant, kan du istedet skrive
+til <<#L#>-request@<#H#>> og indsætte
+hele adressen ovenfor i emne linjen.
+
+</text/sub-confirm#s/>
+Denne liste er modereret. Når du har sendt denne bekræftelse,
+vil moderator få besked om af godkende. Du får besked når
+din tilmelding er blevet aktiv.
+
+</text/sub-nop/>
+Anerkendelse: Adressen
+
+!A
+
+var allerede tilmeldt <#l#> listen da jeg fik din tilmelding,
+og er fortsæt tilmeldt.
+
+</text/sub-ok#E/>
+Anerkendelse: Jeg har tilføjet adressen
+
+!A
+
+til <#l#> listen.
+
+Velkommen til <#l#>@<#H#>!
+
+Gem dette brev så du ved hvilken adresse du er tilmeldt med, da
+du skal bruge denne adresse hvis du senere vil frameldes eller
+ændre din tilmeldingsadresse.
+
+</text/top/>
+Hej! Dette er ezmlm programmet. Jeg håndterer <#l#>@<#H#> listen.
+
+</#x/>
+Jeg arbejder for min ejer som kan nås på
+<#l#>-owner@<#H#>.
+
+</text/unsub-bad/>
+Ups! Bekræftelsesnummeret er ikke gyldigt.
+
+Den mest almindelige grund til ugyldige numre er at deres levetid
+er udløbet. De gælder kun i 10 dage.
+Det kan også være at noget af nummeret mangler i det brev du har
+sendt til mig. Nogle post programmer kan ikke finde ud af lange
+adresser.
+
+Hermed et nyt bekræftelsesnummer. For at bekræfte at du vil have
+
+!A
+
+fjernet fra <#l#> listen, send et tomt svar til denne adresse:
+
+!R
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Check hellere adressen igen, for at være sikker på det hele er der,
+inden du svarer.
+
+Undskyld besværet.
+
+        <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm/>
+Hvis du vil bekræfte at du vil have
+
+!A
+
+fjernet fra <#l#> listen, send et tomt svar til denne adresse:
+
+!R
+
+Normalt kan det simpelthen gøres ved at besvare dette brev.
+Der behøver ikke være noget indhold i brevet.
+</#x/>
+
+Måske virker det at klikke med musen på næste linje:
+        mailto:<#R#>
+<//>
+
+Jeg har ikke undersøgt om din adresse er tilmeldt i øjeblikket.
+Du kan se hvilken adresse du har brugt i din tilmelding ved
+at kigge på de breve du modtager fra listen. Hvert brev har
+din abonnementsadresse gemt i sin retur sti, f.eks. modtager
+mary@xdd.ff.com breve med retur stien
+<<#l#>-return-<number>-mary=xdd.ff.com@<#H#>.
+
+</#q/>
+Nogle post programmer er meget syge og kan ikke håndtere lange
+adresser. Hvis du er ramt af et sådant, kan du istedet skrive
+til <<#L#>-request@<#H#>> og putte hele adressen ovenfor i emne
+linjen.
+
+</text/unsub-nop/>
+Anerkendelse: Adressen
+
+!A
+
+var ikke tilmeldt <#l#> listen da jeg fik din tilmelding, og
+er det stadig ikke.
+
+Hvis du afmelder, men stadig får post, er du tilmeldt med en
+andren adresse end den du bruger i øjeblikket. Kig i brevhoved
+efter noget der ser sådan ud:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+Afmeldingsadressen for denne bruger ville blive:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+
+Send nu post til denne adresse, erstat user=host.dom med rigtige
+værdier, og du burde modtage et brev om at du er frameldt listen.
+
+I nogle post programmer, skal man gøre brevhovedet synligt for
+at se retur stien:
+
+I Eudora 4.0, klik på (øh, hvem kender Eudora?) knappen.
+I PMMail, klik på "Window->Show entire message/header"
+   hvis du har en engelsk udgave ;-)
+
+Hvis dette stadig ikke virker, kan jeg nok ikke hjælpe dig mere.
+Så videresend din liste kommando til liste ejeren sammen med en
+besked om hvad det er du prøver at opnå, samt en liste over de
+andresser du evt. kan være tilmeldt under. Ejeren,
+
+    <#l#>-owner@<#H#>
+
+Vil tage sig af det. Min ejer er ikke så hurtig som mig, så hav
+venligst lidt tålmodighed.
+
+</text/unsub-ok/>
+Anderkendelse: Jeg har fjernet adressen
+
+!A
+
+fra <#l#> listen. Adressen er ikke længere tilmeldt.
+
+</text/edit-do#n/>
+Ret den følgende tekst og send den til denne adresse:
+
+!R
+
+Dit post program har en svar facilitet som bruger denne adresse
+automatisk.
+
+Jeg kan fjerne de de gåseøjne dit post program tilføjer i
+teksten, bare du ikke retter i selve markeringslinjerne.
+
+Markeringslinjerne er de linjer der starter med %%%. De må ikke
+ændres.
+
+</text/edit-list#n/>
+<#L#>-edit.file kommandoen kan bruges af en fjernadministrator,
+til at rette teksterne i de fleste af svarteksterne i
+svar-tekst filerne .
+
+Her følger en list en liste over svar-tekst filer samt en
+kort beskrivelse af hvornår deres indhold bliver brugt.
+For at rette en fil, skriv til <#L#>-edit.file, erstat
+"file" delen med filnavnet. Editeringsinstruktioner følger
+med tekst filen.
+
+filnavn             Anvendelse
+
+bottom              Afslutning på alle breve. Generel kommando information.
+digest              'administrationsdelen' af et udtræk.
+faq                 De mest spurgte spørgsmål om denne postliste.
+get_bad             Istedetfor breve som ikke kan findes i arkivet.
+help                generel hjælp (mellem 'top' og 'bottom').
+info                liste information. Første linje skal give mening i sig selv
+mod_help            specifik hjælp til liste moderatorer.
+mod_reject          til afsender af afvist post.
+mod_request         til post moderatorer sammen med brevet.
+mod_sub             til abonnent når moderator har bekræftet tilmelding.
+mod_sub_confirm     til tilmeldingsmoderator for at bekræfte tilmelding.
+mod_timeout         til afsender af brev som moderator ikke har reageret på.
+mod_unsub_confirm   til fjernadministrator om at framelde.
+sub_bad             til afsender hvis manglende bekræftelse.
+sub_confirm         til afsender for at bekræfte tilmelding.
+sub_nop             til afsender efter gentilmelding.
+sub_ok              til afsender efter vellykket tilmelding.
+top                 Begynnelse på alle breve.
+</#tn/>
+trailer             tilføjes til alle breve som udsendes fra listen.
+</#n/>
+unsub_bad           til afsender hvis frameldingsbekræftelse ikke var korrekt.
+unsub_confirm       til afsender for at bekræfte frameldingsbekræftelse.
+unsub_nop           til ikke-tilmeldte efter framelding.
+unsub_ok            til ex-tilmeldte efter vellykket framelding.
+
+</text/edit-done#n/>
+Denne text er med held blevet opdateret
+</text/info#E/>
+Der findes pt. ingen information om denne liste.
+</text/faq#E/>
+FAQ - Ofte stillede spørgsmål om <#l#>@<#H#> listen.
+
+Der ikke lavet nogen endnu.
+
diff --git a/ezmlmrc.de b/ezmlmrc.de
new file mode 100644 (file)
index 0000000..cd8f44f
--- /dev/null
@@ -0,0 +1,1412 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.de,v 1.22 1999/12/23 02:43:12 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# Deutsche Übersetzung: Frank Tegtmeyer <fte@pobox.com>
+#                       Anmerkungen zur Übersetzung bitte direkt
+#                       an mich
+# translation to German: Frank Tegtmeyer <fte@pobox.com>
+#                       comments regarding the translation
+#                       directly to me please
+#
+# Changes:
+# 1999-11-23     Update for version 0.40, most help texts are moved to
+#                the answer of the -help command.
+#                The sample address ich@irgendwo.de was replaced by
+#                ich@lightwerk.de because I this address is under
+#                my control.
+# 1999-02-17     some refinements, sql support by Fred,
+#                mod-help with -log command
+# 1998-11-26     added -list command to mod-help
+#                more verbose explanation for ezmlm-issubn
+# 1998-08-14     one spelling error, added faq/info commands to 'bottom'
+#                instead of mod-help, Fred corrected one a-switch
+#                added a pointer to my web page "Mailinglisten und ihre
+#                Benutzung"
+# 1998-06-29     update for ezmlm-idx-0.31
+#                added faq/info, correction of some switches (archive, digest)
+#                again use of mailto:, additional hint to prevent the use
+#                of the sample address ich@irgendwo.de, some spelling errors
+#                corrected, only ASCII in qmail bounces
+#
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Explicitly specify character-set, when this ezmlmrc was used.
+# Use Quoted-Printable to make averyone happy.
+iso-8859-1:Q
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean state.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Tut mir leid, ich wurde angewiesen, Ihre Nachrichten nicht anzunehmen. Diesbezuegliche Fragen richten Sie bitte an <#L#>-owner@<#H#> (#5.7.2)."; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Hier werden nur Nachrichten von Abonnenten der Liste akzeptiert. Falls Sie die Liste beziehen aber trotzdem nicht schreiben duerfen, schicken Sie bitte diese Nachricht an <#L#>-owner@<#H#> weiter, damit Ihre neue Adresse aufgenommen werden kann. Der Listeneigentuemer kann Sie ebenso gezielt fuer das Schreiben von Nachrichten freischalten, auch wenn Sie die Liste nicht beziehen (#5.7.2)."; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+Um die Liste abzubestellen, schicken Sie eine Mail an:
+    <#L#>-unsubscribe@<#H#>
+Um eine Liste aller verfügbaren Kommandos zu bekommen, schicken
+Sie eine Mail an:
+    <#L#>-help@<#H#>
+</text/bottom#E/>
+
+Wenn Sie weiterführende Informationen zu Mailinglisten haben möchten,
+schauen Sie sich doch im World Wide Web einmal das Dokument
+"Mailinglisten und ihre Benutzung"  unter folgender Adresse an:
+http://www.pobox.com/~fte/ml.html
+
+--- Hier eine Aufstellung der wichtigsten Kommandos zur Steuerung des
+    Mailinglistenmanagers ezmlm für diese Liste:
+
+Ich kann alle Ihre Anforderungen bezüglich der Liste
+automatisch bearbeiten. Schicken Sie einfach eine leere Mail
+an die jeweilige Adresse.
+SENDEN SIE KEINE KOMMANDOS AN DIE MAILINGLISTE SELBST.
+In dem Fall kann ich nicht darauf reagieren und viele Empfänger
+der Mailingliste werden sich bei Ihnen oder über Sie beschweren.
+
+   <<#L#>-help@<#H#>>
+   Beschreibung der Steuerungsmöglichkeiten für diese
+   Mailingliste
+
+   <<#L#>-subscribe@<#H#>>
+   wenn Sie die Mailingliste in Zukunft beziehen möchten
+
+   Wenn Sie keine Nachrichten der Mailingliste <#L#>
+   mehr empfangen möchten, benutzen Sie bitte die Adresse, die
+   im Header "List-Unsubscribe:" angegeben ist. Wenn Sie Ihre
+   Email-Adresse nach der Listenbestellung nicht geändert haben,
+   geht auch:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+   Für die Digest-Liste existieren die entsprechenden Adressen:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+</#HJE/>
+Beim Löschen oder Eintragen von Abonnenten verschicke ich eine
+zusätzliche Kontrollnachricht. Diese muß zur Bestätigung einfach
+nur beantwortet werden.
+
+</#E/>
+Falls Sie menschliche Hilfe brauchen, wenden Sie sich bitte an
+den Listeneigentümer:
+
+   <<#L#>-owner@<#H#>>
+
+Bitte schicken Sie eine Listenmail inklusive ALLER Header mit -
+dadurch wird es sehr viel leichter, Ihnen schnell zu helfen.
+
+--- Anbei eine Kopie der Anforderung die ich erhalten habe:
+
+</text/bounce-bottom#E/>
+
+--- Anbei eine Kopie der Fehlermeldung (bounce), die ich
+    erhalten habe:
+
+</text/bounce-num#E/>
+
+Ich habe mir gemerkt, welche Nachrichten der Mailingliste
+   <<#L#>@<#H#>>
+nicht an Sie zugestellt werden konnten.
+</#aE/>
+Eventuell befinden sich noch Kopien dieser Nachrichten
+im Archiv.
+
+</#aE/>
+Um beispielsweise eine Kopie der Nachricht 123
+anzufordern, schicken Sie eine Nachricht an
+   <#L#>-get.123@<#H#>
+
+Um die Nachrichten 123 bis 145 (maximal einhundert Nachrichten pro
+Anforderung) zu bekommen, benutzen Sie die folgende Adresse:
+   <#L#>-get.123_145@<#H#>
+
+Um eine Liste (Verfasser und Betreff) der letzen einhundert
+Nachrichten zu bekommen, schicken Sie eine leere Mail an die
+folgende Adresse:
+   <#L#>-index@<#H#>
+
+</#E/>
+Hier die Nummern der Nachrichten:
+
+</text/dig-bounce-num#E/>
+
+Ich habe mir gemerkt, welche Nachrichten der Mailingliste
+<#L#>-digest nicht an Sie zugestellt werden konnten.
+Pro nicht zugestelltem Digest wird jeweils die Nummer der
+ersten Nachricht vermerkt.
+
+</#aE/>
+Unter Umständen befinden sich noch Kopien der Nachrichten
+im Archiv. Ich habe mir jeweils die Nummer der ersten
+Nachricht pro Digest gemerkt. Sie können sich die Nachrichten
+vom Archiv schicken lassen. Eine Anforderung der Digests
+selber ist nicht möglich, da diese nicht archiviert werden.
+
+Um beispielsweise eine Kopie der Nachricht 123
+anzufordern, schicken Sie eine Nachricht an
+   <#L#>-get.123@<#H#>
+
+Um die Nachrichten 123 bis 145 (maximal einhundert Nachrichten pro
+Anforderung) zu bekommen, benutzen Sie die folgende Adresse:
+   <#L#>-get.123_145@<#H#>
+
+Um eine Liste (Verfasser und Betreff) der letzen einhundert
+Nachrichten zu bekommen, schicken Sie eine leere Mail an die
+folgende Adresse:
+   <#L#>-index@<#H#>
+
+</#E/>
+Hier die Nummern der ersten Nachricht pro Digest:
+
+</text/bounce-probe#E/>
+
+Es scheint, daß einige Nachrichten der Mailingliste
+<#l#> an Sie nicht zustellbar waren.
+Ich habe Ihnen eine Warnung geschickt, aber auch diese konnte
+nicht zugestellt werden. Eine Kopie der Fehlernachricht
+ist angehängt.
+
+Diese Nachricht ist ein weiterer Versuch zu prüfen, ob Ihre Adresse
+erreichbar ist. Wenn auch dieser letzte Versuch scheitert Ihnen
+Mail zuzustellen, dann wird Ihre Adresse ohne weitere Hinweise
+von der Mailingliste
+   <#l#>@<#H#>
+entfernt.
+
+Durch eine leere Mail an die Adresse
+   <<#l#>-subscribe@<#H#>>
+können Sie sich wieder in die Mailingliste eintragen.
+
+</text/bounce-warn#E/>
+
+Es scheint, daß einige Nachrichten der Mailingliste
+<#l#> an Sie nicht zustellbar waren.
+Ich habe eine Kopie der ersten Fehlernachricht mitgeschickt.
+
+Falls diese Nachricht auch nicht zustellbar ist, werde ich eine
+weitere Versuchsnachricht schicken. Ist auch diese ohne Erfolg, wird
+Ihre Adresse von der Mailingliste <#l#> gestrichen.
+
+</text/digest#dE/>
+Um sich in die Digest-Liste einzutragen, schicken Sie eine Mail an:
+   <#L#>-digest-subscribe@<#H#>
+
+Um sich abzumelden, schicken Sie eine Mail an:
+   <#L#>-digest-unsubscribe@<#H#>
+
+Nachrichten, die Sie in der Liste veröffentlichen wollen,
+schicken Sie bitte an:
+   <#L#>@<#H#>
+
+</text/get-bad#E/>
+Diese Nachricht ist im Archiv nicht vorhanden.
+
+</text/help#E/>
+Dies ist eine allgemeine Hilfsnachricht. Entweder haben Sie die Hilfe
+direkt angefordert oder die Mail, die ich empfangen habe, ist nicht an
+eine meiner korrekten Steuerungsadressen geschickt worden.
+
+Hier eine Aufstellung der Kommandos zur Steuerung des
+Mailinglistenmanagers ezmlm für diese Liste:
+
+<<#L#>-info@<#H#>>
+<<#L#>-faq@<#H#>>
+Die beiden Adressen sind zur Anforderung eines
+Informationstextes zur Mailingliste bzw. zur Anforderung der
+FAQ (Frequently Asked Questions - oft gestellte Fragen und
+die Antworten darauf) vorgesehen.
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+<<#L#>-get.12@<#H#>>
+Auf diese Weise fordern Sie eine Kopie der Nachricht 12
+vom Archiv an.
+
+<<#L#>-get.123_145@<#H#>>
+Fordern Sie auf diese Weise eine Kopie der Nachrichten
+123 bis 145 vom Archiv an. Sie können mit einer Anforderung
+maximal einhundert Nachrichten bekommen.
+
+<<#L#>-index.123_456@<#H#>>
+Fordern Sie auf diese Weise die Betreff-Zeilen (Subjects)
+der Nachrichten von 123 bis 456 (inklusive) vom Archiv an.
+Pro Anforderung werden maximal 2000 Betreff-Zeilen zugestellt. 
+Die Zustellung erfolgt in Gruppen zu je einhundert Betreff-Zeilen,
+so daß Sie mit dieser Anforderung real die Betreff-Zeilen
+der Nachrichten 100 bis 499 erhalten.
+Zusätzlich zu den Betreffzeilen wird der jeweilige Autor
+übermittelt.
+
+<<#L#>-thread.123@<#H#>>
+Anforderung aller Nachrichten, die den gleichen Betreff wie
+die Nachricht 123 haben.
+
+</#E/>
+Der Inhalt der Nachrichten muß nicht unbedingt leer sein, Sie
+können auch etwas in die Betreff-Zeilen schreiben. Ich werde
+jedoch beide Angaben ignorieren - nur die angeschriebene Adresse
+ist für meine Steuerung maßgebend.
+
+------------------------------------------------------------
+ACHTUNG: bitte probieren Sie nicht die im folgenden genannte
+Adresse ich@lightwerk.de aus, sondern ersetzen Sie
+diese durch Ihre eigene Adresse, unter der Sie
+Abonnent der Liste werden möchten!
+------------------------------------------------------------
+
+Sie können direkt die Adresse vorgeben, unter der Sie als
+Empfänger der Liste eingetragen werden möchten. Dazu bauen Sie
+die gewünschte Adresse in meine Steuerungsadresse ein, indem Sie
+das at-Zeichen (@) durch ein Gleichheitszeichen (=) ersetzen.
+Um zum Beispiel als Adresse ich@lightwerk.de anzugeben, senden
+Sie eine Mail an die Adresse
+   <<#L#>-subscribe-ich=lightwerk.de@<#H#>>.
+Die entsprechende Adresse zum abmelden des Abonnements ist
+   <<#L#>-unsubscribe-ich=lightwerk.de@<#H#>>.
+In beiden Fällen werde ich eine Bestätigung an ich@lightwerk.de
+schicken, die Sie nach Empfang einfach nur beantworten müssen,
+um den An- oder Abmeldevorgang abzuschließen.
+
+Auch wenn diese Erklärungen zu kompliziert für Sie sein sollten
+oder Sie Probleme mit der Steuerung der Liste haben, schreiben
+Sie bitte keine Hilfemails an die Liste selbst. Wenden Sie sich
+stattdessen an den Listeneigentümer:
+   <#L#>-owner@<#H#>
+Bitte haben Sie Geduld, da es sich dabei um ein eventuell
+überlastetes menschliches Wesen handelt, das nie so schnell
+reagieren kann wie ich :)
+
+</text/mod-help#E/>
+Vielen Dank, daß Sie bereit sind, die Moderation der Liste
+   <#L#>@<#H#>
+zu übernehmen.
+
+Die Kommandos, die ich benutze, sind sehr verschieden von denen
+anderer Mailinglistenmanager. Sie werden Ihnen anfangs sehr
+ungewohnt vorkommen, wenn Sie sich aber erst einmal daran gewöhnt
+haben werden Sie die Einfachheit und Geschwindigkeit des Systems
+schätzen lernen.
+
+Hier einige Hinweise, wie die Moderation funktioniert.
+Generelle Hinweise zur Listenbedienung folgen am Ende dieser
+Mail.
+
+Eintragen von Nutzern per Mail
+------------------------------
+Als Moderator sind Sie in der Lage, per Mail beliebige Adressen 
+in die Liste aufzunehmen oder sie daraus zu streichen. Sie
+bauen einfach die Adresse, um die es geht in meine Kommando-Adresse
+ein und ersetzen dabei das at-Zeichen (@) durch ein
+Gleichheitszeichen (=).
+Für die Adresse ich@lightwerk.de geht das zum Beispiel
+über die folgenden Steueradressen:
+
+Eintragen:
+   <#L#>-subscribe-ich=lightwerk.de@<#H#>
+Löschen:
+   <#L#>-unsubscribe-ich=lightwerk.de@<#H#>
+
+</#dE/>
+Entsprechend für die Digest-Liste:
+   <#L#>-digest-subscribe-ich=lightwerk.de@<#H#>
+   <#L#>-digest-unsubscribe-ich=lightwerk.de@<#H#>
+
+</#E/>
+So einfach ist das :) Sie brauchen nicht einmal einen
+Betreff oder irgendeinen Nachrichtentext schreiben.
+
+</#rE/>
+Ich werde Ihnen eine Rückfrage schicken, um sicherzugehen daß
+wirklich Sie das Kommando geschickt haben.
+Sie brauchen dann einfach nur noch antworten und der Vorgang
+ist damit abgeschlossen.
+</#RE/>
+Ich werde zur Sicherheit an die angegebene Adresse (in diesem
+Fall ich@lightwerk.de) eine Rückfrage schicken.
+Alles was der Nutzer tun muß, ist auf diese Anfrage zu antworten.
+</#E/>
+
+Die Bestätigungsnachrichten sind erforderlich um es Dritten
+schwerer zu machen, beliebige Mailadressen in die Liste
+aufzunehmen.
+
+Der Nutzer wird darüber informiert, wenn er in die Liste
+aufgenommen oder aus ihr gelöscht wird.
+
+
+Abonnement durch die User
+-------------------------
+
+Jeder kann die Liste abonnieren oder abbestellen indem eine
+Mail an die entsprechende Adresse geschickt wird:
+
+Bestellen der Liste:
+   <#L#>-subscribe@<#H#>
+
+Abbestellen der Liste:
+   <#L#>-unsubscribe@<#H#>
+
+</#dE/>
+Entsprechend für die Digest-Liste:
+
+   <#L#>-digest-subscribe@<#H#>
+   <#L#>-digest-unsubscribe@<#H#>
+
+</#E/>
+In beiden Fällen bekommt der Nutzer eine Rückfrage, um
+Fälschungen vorzubeugen. Nach Bestätigung wird der Nutzer
+Abonnent der Liste oder aus ihr gelöscht.
+
+</#sE/>
+Da diese Liste mit moderierten Abonnements arbeitet, wird
+bei einem Abonnement eine zweite Rückfrage an den oder die
+Moderatoren geschickt.
+Da die Bestätigung durch den Nutzer schon erfolgte, können Sie
+relativ sicher sein, daß die angegebene Adresse nicht gefälscht
+ist. Wenn Sie dem Abonnement durch den User zustimmen,
+beantworten Sie die Bestätigungsanforderung einfach.
+Falls nicht, können Sie die Anforderung einfach löschen.
+Eventuell können Sie auch mehr Informationen vom Nutzer einholen
+oder ihm Ihre Gründe für die Nicht-Bestätigung mitteilen.
+Beachten Sie, daß dadurch Ihre Email-Adresse bekannt wird.
+
+</#E/>
+Die Nutzer können auch die folgende Adressen benutzen:
+
+   <#L#>-subscribe-ich=lightwerk.de@<#H#>
+   <#L#>-unsubscribe-ich=lightwerk.de@<#H#>
+
+</#dE/>
+Für Digests:
+   <#L#>-digest-subscribe-ich=lightwerk.de@<#H#>
+   <#L#>-digest-unsubscribe-ich=lightwerk.de@<#H#>
+
+</#E/>
+Damit können sie eine andere als die aktuell benutzte
+Email-Adresse in die Mailingliste einzutragen bzw. sie daraus
+löschen (in diesem Beispiel die Adresse ich@lightwerk.de).
+Nur wenn die Rückfrage an diese Adresse dann auch beantwortet
+wird, kann die Aktion ausgeführt werden.
+
+All diese Rückfragen werden durchgeführt um sicherzustellen
+daß keine gefälschten Kommandos vorliegen und daß es sich
+wirklich um den Wunsch der Person handelt, die Mails über
+diese Adresse empfängt.
+
+Ihre Adresse und Identität wird keiner weiteren Person
+offengelegt, es sei denn Sie nehmen direkt Kontakt auf.
+
+</#rlE/>
+Information über den Listenzustand
+----------------------------------
+
+Um eine Liste der Abonnenten von <#L#>@<#H#>
+zu bekommen, senden Sie eine Mail an:
+   <<#L#>-list@<#H#>>
+
+Um ein Transaktionslog (Liste der Abonnenten-Zu- und
+Abgaenge) für <#L#>@<#H#>
+zu bekommen, senden Sie eine Mail an:
+   <<#L#>-log@<#H#>>
+
+</#rldE/>
+Die Liste der Digest-Abonnenten erhalten Sie über:
+   <<#L#>-digest-list@<#H#>>
+
+Das Logfile für die <#L#>-digest@<#H#> Liste über:
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+
+Textänderungen
+--------------
+
+Sie können die erläuternden Texte, die ich verschicke auch per Mail
+verändern. Um eine Datei- und Kommandoliste dafür zu erhalten schreiben
+Sie an:
+   <<#L#>-edit@<#H#>>
+
+</#mE/>
+
+Moderierte Nachrichten
+----------------------
+
+Moderierte Nachrichten werden von mir zunächst in eine
+Warteschlange eingereiht, daraufhin wird eine Bestätigungs-
+anforderung an Sie geschickt. In der Betreffzeile steht
+in diesem Fall "MODERATE for ...".
+
+Falls Sie die Nachricht genehmigen, müssen Sie einfach nur
+auf diese Anforderung antworten. Dazu benutzen Sie bitte
+die Adresse im Reply-To:-Header (sollte automatisch von
+Ihrem Mailprogramm angeboten werden). In einigen Programmen
+wird die Adresse auch als Hyperlink im Mailtext angezeigt, den
+Sie dann nur noch per Mausklick öffnen müssen (zum Beispiel in
+Pegasus Mail).
+Die Nachricht des Nutzers müssen Sie nicht mitschicken, da ich
+noch eine Kopie davon besitze und diese verwende.
+
+Wenn Sie die Nachricht ablehnen möchten, senden Sie an die
+Adresse, die im From:-Header angegeben ist. Auch hier können
+Sie die Möglichkeit des Hyperlinks verwenden, wenn Ihr Mailprogramm
+das unterstützt.
+Sie können eine Begründung oder einen Kommentar mitgeben, indem Sie
+Ihren Text in zwei Zeilen einschließen, die jeweils mit drei
+Prozentzeichen beginnen. Der Nutzer erhält nur diesen
+Kommentar, ohne daß Ihre Identität dabei preisgegeben wird.
+
+Ich werde die Nachricht anhand der ersten Moderatoren-Antwort
+behandeln. Sollten Sie eine Bestätigung oder Ablehnung schicken
+und die Nachricht wurde aufgrund der Entscheidung eines
+weiteren Moderators schon anderweitig verarbeitet, werde
+ich Sie darüber informieren.
+
+Sollte kein Moderator innerhalb einer festgelegten Zeit
+(normalerweise 5 Tage) reagieren, schicke ich die Nachricht
+zusammen mit einer Erklärung an den Absender zurück.
+Die Liste kann auch so konfiguriert werden, daß solche
+Nachrichten nicht an den Nutzer zurückgeschickt, sondern
+kommentarlos gelöscht werden.
+
+</#E/>
+
+Urlaub und andere Abwesenheit
+-----------------------------
+
+Wenn Sie zeitweilig über eine andere Adresse erreichbar sind, schicken
+Sie alle Mails, die den richtigen 'Mailing-List:' Header haben an diese
+Adresse weiter. Behandeln Sie alle Mails, die einen Betreff der Form:
+'MODERATE for <#L#>@<#H#>' oder
+'CONFIRM subscribe to <#L#>@<#H#>'
+haben auf die gleiche Weise.
+Ihre Moderationstätigkeit können Sie dann auch von der neuen Adresse
+aus fortsetzen.
+Es ist ebenfalls möglich, einen Vertreter damit zu beauftragen. Bitte
+klären Sie aber Vertretungen vorher mit dem Listeneigentümer ab.
+
+Wenn Sie für eine Weile alle Anforderungsmails automatisch
+bestätigen wollen, lassen Sie einfach Ihr Mailsystem die Nachrichten
+automatisch beantworten, die die obengenannten Bedingungen erfüllen.
+
+</#rE/>
+Wenn Sie die Liste von einer Fremdadresse aus administrieren wollen,
+werden nicht Sie, sondern der Nutzer nach einer Bestätigung gefragt.
+Danach wird die Bestätigungsanforderung an alle Moderatoren verschickt.
+Diese Verfahrensweise ist notwendig, da ich in diesem Fall nicht wissen kann,
+ob Sie zur Administration berechtigt sind.
+
+Bitte beachten Sie, daß in diesem Fall auch Ihre Administrationsmail
+(und Ihre Mailadresse) an den Nutzer übermittelt werden.
+
+</#E/>
+Viel Erfolg!
+
+PS: Bitte wenden Sie sich an den Listeneigentümer
+   <#L#>-owner@<#H#>
+wenn Sie Probleme irgendwelcher Art mit der Listenadministration
+haben.
+
+</text/mod-reject#E/>
+Tut mir leid, Ihre (angehängte) Nachricht wurde von einem Moderator
+abgewiesen. Falls der Moderator einen Kommentar dazu abgegeben hat,
+können Sie diesen weiter unten finden.
+</text/mod-request#E/>
+Bitte entscheiden Sie als Moderator, ob diese Nachricht an die
+Mailingliste
+   <#L#>@<#H#>
+weitergeleitet werden soll.
+
+Um die Nachricht zuzulassen und sofort an alle Listenabonnenten
+weiterzuleiten, senden Sie bitte eine Mail an:
+
+!A
+
+Normalerweise sollte das über die "Beantworten"-Funktion Ihres
+Mailprogramms funktionieren. Bitte prüfen Sie aber noch einmal,
+ob die Adresse mit "<#L#>-accept" beginnt und am Ende nicht
+abgeschnitten ist. Manche Programme haben leider Probleme mit langen
+Adressen. Falls "Beantworten" nicht funktioniert, kopieren Sie
+die oben genannte Adresse einfach in die Zieladresse einer neuen
+Nachricht. Beachten Sie dabei aber einen eventuell von Ihrem
+Mailprogramm vorgenommenen Zeilenumbruch, falls die Adresse nicht
+in eine Zeile paßt.
+</#xE/>
+Falls Ihr Programm Hyperlinks unterstützt, klicken Sie hier:
+   mailto:<#A#>
+Kontrollieren Sie aber bitte auch in diesem Fall, daß die Adresse
+nicht durch Zeilenumbruch verstümmelt wurde!
+</#E/>
+
+
+Um die Nachricht abzuweisen und an den Absender zurückzuschicken,
+senden Sie bitte eine Mail an:
+
+!R
+
+Falls Ihr Programm das unterstützt, können Sie auch an die im
+From: Header angegebene Adresse antworten (das sollte die oben
+angegebene sein).
+
+</#xE/>
+Wenn Ihr Programm Hyperlinks unterstützt, können Sie auch
+hier klicken:
+   mailto:<#R#>
+
+</#E/>
+
+Sie müssen keine Kopie der Originalnachricht mitschicken.
+Wenn Sie dem Absender bei einer Ablehnung einen Kommentar zukommen
+lassen wollen, schließen Sie diesen zwischen zwei Zeilen ein, die
+jeweils mit drei Prozentzeichen beginnen.
+
+%%% Beginn Kommentar
+%%% Ende Kommentar
+
+Auch bei Ablehnung einer Nachricht kontrollieren Sie bitte, daß
+die Adresse nicht von Ihrem Mailprogramm verstümmelt wird.
+
+Vielen Dank für Ihre Mithilfe!
+
+--- Anbei finden Sie die Nachricht, um die es geht.
+
+</text/mod-sub#E/>
+Ich habe Sie auf Anforderung eines Moderators in die folgende
+Mailingliste aufgenommen bzw. Sie aus dieser gelöscht:
+   <#l#>@<#H#>
+
+Falls das nicht Ihren Wünschen entspricht oder ein Irrtum vorliegt,
+wenden Sie sich mit Ihrer Reklamation so schnell wie möglich an
+den Listeneigentümer:
+   <#l#>-owner@<#H#>
+
+Falls Sie Informationen über die Steuerung der Liste benötigen,
+senden Sie einfach eine leere Mail an 
+   <#l#>-help@<#H#>
+
+</text/mod-timeout#E/>
+Leider hat keiner der Moderatoren Ihre Nachricht bearbeitet. Deswegen
+schicke ich sie Ihnen zurück. Falls Sie dennoch versuchen wollen, die
+Nachricht zu veröffentlichen, schicken Sie sie bitte erneut oder
+nehmen Sie direkt Kontakt mit einem Moderator auf.
+
+--- Anbei finden Sie die Nachricht, die ich von Ihnen empfangen habe.
+
+</text/mod-sub-confirm#E/>
+Bitte entscheiden Sie, ob
+
+!A
+
+die Mailingliste
+   <#l#>@<#H#>
+abonnieren darf. Falls die Anforderung nicht von Ihnen selbst kam
+(die Originalanforderung habe ich angehängt), habe ich schon geprüft,
+daß die Anforderung wirklich von der angegebenen Adresse kam.
+
+Um zuzustimmen, senden Sie einfach eine leere Mail an die
+folgende Adresse:
+
+!R
+
+Die Funktion "Beantworten" in Ihrem Mailprogramm sollte diese Adresse
+automatisch benutzen.
+Beachten Sie dabei aber einen eventuell von Ihrem Mailprogramm
+vorgenommenen Zeilenumbruch, falls die Adresse nicht in eine Zeile paßt.
+Sie können die Adresse auch kopieren und in einer neuen Mail
+als Empfänger einsetzen.
+</#xE/>
+Falls Ihr Programm Hyperlinks unterstützt, klicken Sie hier:
+   mailto:<#R#>
+</#E/>
+
+
+Falls Sie nicht zustimmen, ignorieren Sie diese Nachricht einfach.
+Sie können Dem Nutzer natürlich auch eine Begründung Ihrer Ablehnung
+schicken, damit wird diesem allerdings Ihre Email-Adresse bekannt.
+
+Vielen Dank für Ihre Mithilfe!
+
+</text/mod-unsub-confirm#E/>
+Bitte entscheiden Sie, ob
+
+!A
+
+von der Mailingliste
+   <#l#>@<#H#>
+gestrichen werden soll.
+Um zuzustimmen, senden Sie einfach eine leere Mail an die
+folgende Adresse:
+
+!R
+
+Die Funktion "Beantworten" in Ihrem Mailprogramm sollte diese Adresse
+automatisch benutzen.
+Beachten Sie dabei aber einen eventuell von Ihrem Mailprogramm
+vorgenommenen Zeilenumbruch, falls die Adresse nicht in eine Zeile paßt.
+Sie können die Adresse auch kopieren und in einer neuen Mail
+als Empfänger einsetzen.
+</#xE/>
+Falls Ihr Programm Hyperlinks unterstützt, klicken Sie hier:
+   mailto:<#R#>
+</#E/>
+
+Falls Sie nicht zustimmen, ignorieren Sie diese Nachricht einfach.
+Sie können Dem Nutzer natürlich auch eine Begründung Ihrer Ablehnung
+schicken, damit wird diesem allerdings Ihre Email-Adresse bekannt.
+
+Vielen Dank für Ihre Mithilfe!
+
+</text/sub-bad#E/>
+Es scheint, daß diese Bestätigungsnummer nicht gültig ist.
+
+Der Grund dafür ist in den meisten Fällen eine Zeitüberschreitung.
+Bestätigungen muß ich innerhalb von zehn Tagen bekommen, sonst gelten
+Anforderungen als abgelehnt.
+Stellen Sie auch sicher, daß Sie die Antwortadresse komplett
+übernommen haben - da diese Kontrolladressen recht lang sind, haben
+einige Mailprogramme die Eigenschaft, diese kommentarlos zu kürzen.
+
+Ich habe eine neue Bestätigungsnummer generiert. Um zuzustimmen, daß
+die Adresse
+
+!A
+
+in die Mailingliste
+   <#l#>@<#H#>
+aufgenommen wird, senden Sie eine leere Mail an die Adresse
+
+!R
+</#xE/>
+
+Falls Ihr Programm Hyperlinks unterstützt, klicken Sie hier:
+   mailto:<#R#>
+</#E/>
+
+Prüfen Sie noch einmal genau, daß Sie an die richtige Adresse
+schreiben und diese nicht von Ihrem Mailprogramm verstümmelt wurde.
+
+Bitte entschuldigen Sie den Aufwand.
+
+   <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+Um zu bestätigen, daß Sie unter der Adresse
+
+!A
+
+die Mailingliste <#l#>@<#H#>
+abonnieren möchten, senden Sie eine leere Mail an 
+
+!R
+
+Die Funktion "Beantworten" in Ihrem Mailprogramm sollte diese Adresse
+automatisch benutzen.
+Beachten Sie dabei aber einen eventuell von Ihrem Mailprogramm
+vorgenommenen Zeilenumbruch, falls die Adresse nicht in eine Zeile paßt.
+Sie können die Adresse auch kopieren und in einer neuen Mail
+als Empfänger einsetzen.
+</#xE/>
+Falls Ihr Programm Hyperlinks unterstützt, klicken Sie hier:
+   mailto:<#R#>
+</#qE/>
+
+Manche Mailprogramme sind nicht in der Lage, lange Adressen zu
+verarbeiten. Falls Sie die angegebene Adresse nicht anschreiben
+können, benutzen Sie bitte die Adresse
+<<#L#>-request@<#H#>> und tragen die
+obengenannte Adresse in die Betreff-Zeile (Subject:) ein.
+</#E/>
+
+Diese Bestätigung dient zwei Zwecken. Zum einen wird dadurch sicher-
+gestellt, daß ich Mails an Ihre Adresse zustellen kann.
+Zum anderen dient die Bestätigung als Schutz gegen gefälschte
+Mailinglisten-Abonnements.
+
+</#sE/>
+Diese Mailingliste ist moderiert. Sobald Sie die Bestätigung
+abgeschickt haben, wird eine weitere Bestätigung von den Moderatoren
+eingeholt. Sie erhalten einen Hinweis, wenn der Vorgang abgeschlossen
+wurde. Falls nach etwa zehn Tagen keine Reaktion kam, hat keiner der
+Moderatoren zugestimmt, Sie in die Liste aufzunehmen.
+
+</#E/>
+Wenn Sie diese Nachricht nicht beantworten, erhalten Sie die
+Mailingliste nicht. Wenden Sie sich an Ihren Mail-Administrator, wenn
+Sie der Meinung sind, daß jemand unter Ihrem Namen ein gefälschtes
+Abonnement vorgenommen hat.
+
+</text/sub-nop#E/>
+Hm. Die Adresse
+
+!A
+
+ist schon Abonnent der Mailingliste
+<#l#>@<#H#>.
+Diese Adresse war schon als Abonnent eingetragen, bevor Ihre
+Anforderung empfangen wurde. Daher habe ich keinerlei
+Änderungen ausgeführt.
+
+</text/sub-ok#E/>
+Bestätigung: Ich habe Ihre Adresse
+
+!A
+
+als Abonnenten in die Mailingliste <#l#>
+aufgenommen.
+
+Willkommen bei <#l#>@<#H#>!
+
+Bitte heben Sie diese Nachricht gut auf, damit Sie auch später
+noch wissen, unter welcher Adresse Sie die Mailingliste empfangen.
+Damit vermeiden Sie Probleme, falls sie später Ihre Adresse ändern
+oder die Mailingliste wieder abbestellen wollen.
+
+Um die Liste wieder abzubestellen, senden Sie eine leere Mail an
+die Adresse
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+Hallo, hier ist das ezmlm Programm. Ich verwalte die Mailingliste
+   <#l#>@<#H#>
+
+Ich nehme dem Eigentümer der Liste die meiste Arbeit ab. Er selbst
+ist erreichbar unter der Adresse
+   <#l#>-owner@<#H#>
+
+</text/unsub-bad#E/>
+Es scheint, daß diese Bestätigungsnummer nicht gültig ist.
+
+Der Grund dafür ist in den meisten Fällen eine Zeitüberschreitung.
+Bestätigungen muß ich innerhalb von zehn Tagen bekommen, sonst gelten
+Anforderungen als abgelehnt.
+Stellen Sie auch sicher, daß Sie die Antwortadresse komplett
+übernommen haben - da diese Kontrolladressen recht lang sind, haben
+einige Mailprogramme die Eigenschaft, sie kommentarlos zu kürzen.
+
+Ich habe eine neue Bestätigungsnummer generiert. Um zuzustimmen, daß
+Ihre Adresse
+
+!A
+
+von der Mailingliste <#l#>@<#H#>
+gestrichen werden soll, senden Sie bitte eine leere Mail an
+folgende Adresse:
+
+!R
+</#xE/>
+
+oder klicken Sie hier, wenn Ihr Programm Hyperlinks unterstützt:
+   mailto:<#R#>
+</#E/>
+
+Bitte prüfen Sie noch einmal genau, daß Sie an die richtige Adresse
+schreiben und diese nicht von Ihrem Mailprogramm verstümmelt wurde.
+
+Bitte entschuldigen Sie den Aufwand.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+Um zuzustimmen, daß Ihre Adresse
+
+!A
+
+von der Mailingliste <#l#>@<#H#>
+gestrichen werden soll, senden Sie bitte eine leere Mail an
+folgende Adresse:
+
+!R
+
+Die Funktion "Beantworten" in Ihrem Mailprogramm sollte diese Adresse
+automatisch benutzen.
+Beachten Sie dabei aber einen eventuell von Ihrem Mailprogramm
+vorgenommenen Zeilenumbruch, falls die Adresse nicht in eine Zeile paßt.
+Sie können die Adresse auch kopieren und in einer neuen Mail
+als Empfänger einsetzen.
+</#xE/>
+Falls Ihr Programm Hyperlinks unterstützt, klicken Sie hier:
+   mailto:<#R#>
+</#qE/>
+
+Manche Mailprogramme sind nicht in der Lage, lange Adressen zu
+verarbeiten. Falls Sie die angegebene Adresse nicht anschreiben
+können, benutzen Sie bitte die Adresse
+<<#L#>-request@<#H#>> und tragen die
+obengenannte Adresse in die Betreff-Zeile (Subject:) ein.
+</#E/>
+
+Ich habe nicht überprüft, ob Ihre Adresse wirklich die Mailingliste
+bezieht. Sollte die Adresse nicht zu den Abonnenten der Mailingliste
+gehören erhalten Sie erst nach Ihrer Bestätigung eine Fehlermeldung.
+
+</text/unsub-nop#E/>
+Hm. Die Adresse
+
+!A
+
+ist kein Abonnent der Mailingliste
+   <#l#>@<#H#>
+und konnte deshalb auch nicht von ihr abgemeldet werden.
+
+Wenn Sie die Liste abbestellen aber trotzdem weiterhin Nachrichten
+aus der Mailingliste empfangen, sind sie wahrscheinlich mit einer
+anderen Adresse eingetragen, als diejenige die sie momentan
+benutzen.
+
+Um festzustellen, unter welcher Adresse Sie die Mailingliste
+empfangen, sehen Sie sich die Header der empfangenen Nachrichten an.
+Jede Nachricht hat Ihre Empfängeradresse in ihrem Return-Path: kodiert.
+Zum Beispiel empfängt die Adresse ich@lightwerk.de alle Listennachrichten
+mit folgendem Return-Path:
+Return-Path: <<#l#>-return-<nummer>-ich=lightwerk.de@<#H#>
+Die Nummer wechselt natürlich, also sieht das zum Beispiel so aus:
+'Return-Path: <<#l#>-return-1234-ich=lightwerk.de@<#H#>>'
+
+Das zeigt, daß die Adresse, über die die Nachrichten bezogen werden,
+ich@lightwerk.de ist. Die Adresse zum Abbestellen der Liste wäre in
+diesem Fall 
+   <#l#>-unsubscribe-ich=lightwerk.de@<#H#>
+Benutzen Sie einfach die so ermittelte Adresse.
+
+Wenn Ihre Listen-Nachrichten einen Header "List-Unsubscribe"
+enthalten, dann können sie die dort angegebene Adresse benutzen. Diese
+hat Ihre Bezugsadresse schon mit in die Abmeldeadresse einbezogen.
+
+Wie Sie die Anzeige aller Header in Ihrem Mailprogramm einschalten,
+ist leider bei jedem Programm anders. Einige Beispiele:
+- Pegasus-Mail: Strg-P (deutsche Version) oder Strg-H (engl. Version)
+- Elm und Mutt: Anzeige der Nachricht nicht mit <Enter> sondern mit 'h'
+- Microsoft Outlook 98: sehen Sie unter Ansicht/Optionen nach. Dort
+                        finden Sie die Internet-Header Ihrer Mail
+- Eudora Pro (Mac): klicken Sie auf die mit "Blah" markierte Stelle
+- ...
+
+Falls all das nicht funktioniert, ist es wahrscheinlich, daß jemand
+die Nachrichten der Mailingliste an sie kopiert. Bitte fragen Sie
+ihren lokalen Mailadministrator um Hilfe. Kann auch dieser Ihnen nicht
+helfen, schicken Sie eine Listen-Nachricht inklusive ALLER HEADER
+zusammen mit einer Erklärung, was sie versucht haben an den Eigentümer
+dieser Liste:
+
+  <#l#>-owner@<#H#>
+
+Dieser wird sich dann mit Ihnen in Verbindung setzen und sich um das
+Problem kümmern. Aber haben Sie bitte Geduld - es handelt sich um
+ein menschliches Wesen, das eventuell vielbeschäftigt ist :)
+
+</text/unsub-ok#E/>
+Bestätigung: die Adresse
+
+!A
+
+wurde von der Mailingliste
+   <#l#>@<#H#>
+abgemeldet.
+
+</text/edit-do#nE/>
+Bitte bearbeiten Sie den folgenden Text und senden Sie ihn an:
+
+!R
+
+Die Funktion "Beantworten" in Ihrem Mailprogramm sollte diese Adresse
+automatisch benutzen.
+
+Solange Sie die Markierungszeilen nicht verändern, kann ich
+Quote-Zeichen (Zitat-Markierungen, in der Regel '> '), die Ihr
+Mail-Programm vor dem Text einfügt, selbständig wieder entfernen.
+
+Markierungszeilen sind Zeilen, die mit '%%%' beginnen. Sie dürfen
+nicht geändert werden (vom Mailprogramm eingefügte Zitat-Markierungen
+sind akzeptabel).
+
+
+</text/edit-list#nE/>
+Durch Remote-Administratoren kann das <#L#>-edit.file
+Kommando benutzt werden, um die Textdateien zu verändern, die die
+Hauptmenge der Antworten von der Liste
+    <#L#>@<#H#>
+ausmachen.
+
+Hier finden Sie eine Aufstellung der Dateinamen und eine kurze
+Beschreibung ihres Inhalts bzw. ihrer Funktion.
+Um den Inhalt einer Datei zu ändern, senden Sie einfach eine
+Mail an
+   <#L#>-edit.file@<#H#>
+wobei Sie 'file' durch den konkreten Dateinamen ersetzen.
+Die Instruktionen zur Bearbeitung erhalten Sie zusammen mit dem
+Text.
+
+Datei               Inhalt / Funktion
+
+bottom              an alle Antworten angehängt, allgemeine
+                    Information zu Kommandos
+digest              Steuerungsinformationen für Digests
+faq                 oft gestellte Fragen und die Antworten zur Liste
+get_bad             anstelle nicht im Archiv vorhandener Nachrichten
+help                allg. Hilfe (zwischen 'top' und 'bottom').
+info                Listenbeschreibung. Die erste Zeile ist Kurzbeschreibung.
+mod_help            Hilfe für Listenmoderatoren
+mod_reject          an den Absender abgewiesener Nachrichten
+mod_request         an Moderatoren, zusammen mit der Nachricht
+mod_sub             an Neu-Abonnenten, nach Moderator-Bestätigung
+mod_sub_confirm     Abonnement-Anforderung, an Moderatoren
+mod_timeout         Zeitüberschreitung, an Absender
+mod_unsub_confirm   Abbestellung, an Remote-Administrator
+sub_bad             Abonnement-Ablehnung, an Abonnent
+sub_confirm         an Neu-Abonnenten, zur Bestätigung des Abonnements
+sub_nop             an Abonnenten, falls er schon Abonnent ist
+sub_ok              an Abonnenten, wenn Abonnement erfolgreich
+top                 Anfang von allen Antworten
+</#tnE/>
+trailer             an alle Listennachrichten angehängt
+</#nE/>
+unsub_bad           an Abonnenten, wenn Abmeldung abgelehnt
+unsub_confirm       an Abonnenten, Anforderung Abmeldebestätigung
+unsub_nop           an Nicht-Abonnenten, wenn Abmeldung angefordert
+unsub_ok            an abgemeldeten Abonnenten, Abmeldung erfolgreich
+
+</text/edit-done#nE/>
+Der Text wurde erfolgreich abgeändert.
+
+</text/info#E/>
+Für diese Liste wurde noch keine Beschreibung bereitgestellt.
+
+</text/faq#E/>
+Oft gestellte Fragen+Antworten für die Mailingliste
+   <#l#>@<#H#>
+
+Leider hat sich noch niemand die Mühe gemacht, ein entsprechendes
+Dokument zu erstellen.
diff --git a/ezmlmrc.en_US b/ezmlmrc.en_US
new file mode 100644 (file)
index 0000000..bf51c12
--- /dev/null
@@ -0,0 +1,1152 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.en_US,v 1.17 1999/12/11 03:30:16 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean state.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+To unsubscribe, e-mail: <#L#>-unsubscribe@<#H#>
+For additional commands, e-mail: <#L#>-help@<#H#>
+</text/bottom#E/>
+
+--- Administrative commands for the <#l#> list ---
+
+I can handle administrative requests automatically. Please
+do not send them to the list address! Instead, send
+your message to the correct command address:
+
+For help and a description of available commands, send a message to:
+   <<#L#>-help@<#H#>>
+
+To subscribe to the list, send a message to:
+   <<#L#>-subscribe@<#H#>>
+
+To remove your address from the list, just send a message to
+the address in the ``List-Unsubscribe'' header of any list
+message. If you haven't changed addresses since subscribing,
+you can also send a message to:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+or for the digest to:
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+</#HEJ/>
+For addition or removal of addresses, I'll send a confirmation
+message to that address. When you receive it, simply reply to it
+to complete the transaction.
+
+</#E/>
+If you need to get in touch with the human owner of this list,
+please send a message to:
+
+    <<#L#>-owner@<#H#>>
+
+Please include a FORWARDED list message with ALL HEADERS intact
+to make it easier to help you.
+
+--- Enclosed is a copy of the request I received.
+
+</text/bounce-bottom#E/>
+
+--- Enclosed is a copy of the bounce message I received.
+
+</text/bounce-num#E/>
+
+I've kept a list of which messages from the <#L#> mailing list have 
+bounced from your address.
+
+</#aE/>
+Copies of these messages may be in the archive.
+
+</#aE/>
+To retrieve a set of messages 123-145 (a maximum of 100 per request),
+send an empty message to:
+   <<#L#>-get.123_145@<#H#>>
+
+To receive a subject and author list for the last 100 or so messages,
+send an empty message to:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Here are the message numbers:
+
+</text/dig-bounce-num#E/>
+
+I've kept a list of which digests from the <#L#>-digest mailing list
+have bounced from your address. For each digest you missed, I have
+noted the number of the first message in the digest.
+
+</#aE/>
+I do not archive the digests themselves, but you may be able to
+get the messages from the main list archive.
+
+To retrieve a set of messages 123-145 (a maximum of 100 per request),
+send an empty message to:
+   <<#L#>-get.123_145@<#H#>>
+
+To receive a subject and author list for the last 100 or so messages,
+send an empty message to:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Here are the digest message numbers:
+
+</text/bounce-probe#E/>
+
+Messages to you from the <#l#> mailing list seem to
+have been bouncing. I sent you a warning message, but it bounced.
+I've attached a copy of the bounce message.
+
+This is a probe to check whether your address is reachable. If this
+probe bounces, I will remove your address from the
+<#l#>@<#H#> mailing list, without further notice.
+
+You can re-subscribe by sending a message to this address:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn#E/>
+
+Messages to you from the <#l#> mailing list seem to
+have been bouncing. I've attached a copy of the first bounce
+message I received.
+
+If this message bounces too, I will send you a probe. If the probe bounces,
+I will remove your address from the <#l#> mailing list,
+without further notice.
+
+</text/digest#dE/>
+To subscribe to the digest, e-mail:
+       <<#L#>-digest-subscribe@<#H#>>
+
+To unsubscribe from the digest, e-mail:
+       <<#L#>-digest-unsubscribe@<#H#>>
+
+To post to the list, e-mail:
+       <<#L#>@<#H#>>
+
+</text/get-bad#E/>
+Sorry, that message is not in the archive.
+
+</text/help#E/>
+This is a generic help message. The message I received wasn't sent to
+any of my command addresses.
+
+Here is a list of the command addresses supported:
+
+Send mail to the following for info and FAQ for this list:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#dE/>
+Similar addresses exist for the digest list:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+To get messages 123 through 145 (a maximum of 100 per request), mail:
+   <<#L#>-get.123_145@<#H#>>
+
+To get an index with subject and author for messages 123-456, mail:
+   <<#L#>-index.123_456@<#H#>>
+
+To receive all messages with the same subject as message 12345,
+send an empty message to:
+   <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+The messages do not really need to be empty, but I will ignore
+their content. Only the ADDRESS you send to is important.
+
+You can start a subscription for an alternate address,
+for example "john@host.domain", just add a hyphen and your
+address (with '=' instead of '@') after the command word:
+    <<#L#>-subscribe-john=host.domain@<#H#>>
+
+To stop subscription for this address, mail:
+    <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</text/mod-help#E/>
+Thank you for agreeing to moderate the <#L#>@<#H#>
+mailing list.
+
+My commands are a little different from other mailing lists,
+but I think you will find them intuitive and easy to use.
+
+Here are some instructions for the tasks you may have to perform
+as a list-owner and/or moderator.
+
+General list instructions follow at the end of this message.
+
+Remote subscription
+-------------------
+As a moderator, you can subscribe and unsubscribe any address to
+the mailing list. To subscribe "john@host.domain", simply put
+a hyphen after the command word, then his address with '=' instead
+of the '@'. For instance, to subscribe that address, send mail to:
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+You can similarly remove the address by sending a message to:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#dE/>
+For the digest list:
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+</#E/>
+That's all. No subject and no message body needed!
+
+</#rE/>
+I will send you a confirmation request, to make sure
+that you really sent the request. Simply reply to the
+message, and your wish has been granted.
+</#RE/>
+I will send a confirmation request to the user address, in this
+case <john@host.domain>. All the user has to do is to reply to 
+this confirmation request message.
+</#E/>
+
+The confirmations are necessary to make it extremely hard
+for a third party to add or remove an address to the list.
+
+I will notify the user when his/her subscriber status
+has changed.
+
+Subscription
+------------
+
+Any user can subscribe or unsubscribe by sending mail to:
+
+    <<#L#>-subscribe@<#H#>>
+    <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+For the digest list:
+
+    <<#L#>-digest-subscribe@<#H#>>
+    <<#L#>-digest-unsubscribe@<#H#>>
+
+</#E/>
+The user will receive a confirmation request to make
+sure s/he controls the subscription address. Once this
+is verified the user is unsubscribed.
+
+</#sE/>
+Since this list is moderated for subscriptions, I will send a
+second confirmation request to the moderator(s). Since the user
+has already confirmed the desire to be on the list, you as the
+moderator can be highly confident that the subscriber address is
+real. If you want to approve the users request, simply reply to
+my CONFIRM message. If not, you can simply delete my message or
+possibly contact the potential subscriber for more information.
+</#SE/>
+Subscriptions work the same way.
+</#E/>
+
+The user can also use:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+to have mail sent to "mary@host.domain". Only if she receives mail
+at this address will she receive the confirmation request and be
+able to reply to it.
+
+Your address and identity will not be revealed to the subscriber,
+unless you send mail directly to the user.
+
+</#rlE/>
+To get a subscriber list for <#L#>@<#H#>, send a message to:
+   <<#L#>-list@<#H#>>
+
+To get a list transaction log <#L#>@<#H#>, send a message to:
+   <<#L#>-log@<#H#>>
+
+</#rldE/>
+For digest subscribers:
+   <<#L#>-digest-list@<#H#>>
+and:
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+You can remotely edit the text files that make up the responses the
+list sends out. To get a list of files and editing instructions, mail:
+   <<#L#>-edit@<#H#>>
+
+</#mE/>
+Moderated posts
+---------------
+When posts are moderated, I will save the posted message and send
+you a copy together with instructions. The message to you will
+have "MODERATE for ..." as the subject.
+
+To accept the post, just reply to the 'Reply-To:', which I have
+configured with the correct "accept" address address. You do not need
+to include the post itself. In fact, I will ignore anything you send
+me as long as the address you send to is correct.
+
+If you want to reject it, send mail to the 'From:' address, which
+I have configured with the correct "reject" address. This can
+usually be done by 'reply-to-all', then deleting any address other
+than the "reject" address. You may add a comment to the sender between
+two lines starting with three '%'. I will send only this comment to the
+sender with the rejected post. Again, I will not reveal your identity.
+
+I will process the message according to the first reply I receive.
+If you send me a request to accept a message that has already been
+rejected or vice versa, I will let you know.
+
+If I receive no moderator replies within a certain period of
+time (normally 5 days), I will return the message to the sender
+with an explanation of what happened. Your administrator can also
+set up the list so that such "ignored" messages are simply deleted
+without notification, rather than returned to sender.
+</#E/>
+
+Vacations
+---------
+If you are temporarily at a different address, just forward all messages
+that have the correct 'Mailing-List:' header (or all that have subjects
+starting with 'MODERATE for <#L#>@<#H#>'
+or 'CONFIRM subscribe to <#L#>@<#H#>') to the
+new address. You can then moderate from the new address. Alternatively,
+you can forward the messages to a friend so that s/he can moderate
+for you. Please OK this with the list owner first.
+
+If you would like to automatically approve all requests while you
+are gone, set up you mail client to auto-reply to messages that
+have subjects meeting the above criteria.
+
+</#rE/>
+If you try to do remote administration from an address that is not
+your own, the subscriber, not you, will be asked to confirm. After
+that, a moderator confirm request is sent to all moderators.
+I'm doing this because I have no way of knowing that it is you that
+sent the original request.
+
+Please note that your original request (and your address) are sent to
+the subscriber in this case!
+</#E/>
+
+Good luck!
+
+PS: Please contact the list owner (<#L#>-owner@<#H#>) if you
+have any questions or problems.
+
+</text/mod-reject#E/>
+I'm sorry, your message (enclosed) was not accepted by the moderator.
+If the moderator has made any comments, they are shown below.
+</text/mod-request#E/>
+The enclosed message was submitted to the <#L#>@<#H#>
+mailing list. If you'd like to approve it for distribution to all
+the subscribers, please e-mail:
+
+!A
+
+Usually, this happens when you just hit the "reply" button. You can
+check the address to make sure that it starts with
+"<#L#>-accept". If this does not work, simply copy the
+address and paste it into the "To:" field of a new message.
+</#xE/>
+
+Alternatively, click here:
+       mailto:<#A#>
+</#E/>
+
+To reject the post and cause it to be returned to the
+sender, please send a message to:
+
+!R
+
+Usually, it is easiest to hit the "reply-to-all" button, and then
+remove all the addresses except the one starting with
+"<#L#>-reject".
+</#xE/>
+
+Alternatively, click here:
+       mailto:<#R#>
+</#E/>
+
+You do not need to copy the post in your response to accept or 
+reject it. If you wish to send a comment to the sender of a rejected
+post, please include it between two marker lines starting with three
+percent signs ('%'):
+
+%%% Start comment
+%%% End comment
+
+Thank you for your help!
+
+--- Enclosed, please find the posted message.
+
+</text/mod-sub#E/>
+--- I have subscribed or unsubscribed you at the request of
+a moderator of the <#l#>@<#H#> mailing list.
+
+If this is not an action you desire, please send a complaint 
+or other comments to the list owner (<#l#>-owner@<#H#>) as soon 
+as possible.
+
+</text/mod-timeout#E/>
+I'm sorry, the list moderators for the <#L#> list
+have failed to act on your post. Thus, I'm returning it to you.
+If you feel that this is in error, please repost the message
+or contact a list moderator directly.
+
+--- Enclosed, please find the message you sent.
+
+</text/mod-sub-confirm#E/>
+I respectfully request your permission to add
+
+!A
+
+to the subscribers of the <#l#> mailing list. This request
+either came from you, or it has already been verified by
+the potential subscriber.
+
+To confirm, please send an empty reply to this address:
+
+!R
+
+Usually, this happens when you just hit the "reply" button.
+If this does not work, simply copy the address and paste it into
+the "To:" field of a new message.
+</#xE/>
+
+or click here:
+       mailto:<#R#>
+</#E/>
+
+If you don't approve, simply ignore this message.
+
+Thank you for your help!
+
+</text/mod-unsub-confirm#E/>
+A request has been made to remove
+
+!A
+
+from the <#l#> mailing list. If you agree, please send
+an empty reply to this address:
+
+!R
+
+Usually, this happens when you just hit the "reply" button.
+If this does not work, simply copy the address and paste it into
+the "To:" field of a new message.
+</#xE/>
+
+or click here:
+       mailto:<#R#>
+</#E/>
+
+If you don't approve, simply ignore this message.
+
+Thank you for your help!
+
+</text/sub-bad#E/>
+Oops, that confirmation number appears to be invalid.
+
+The most common reason for invalid numbers is expiration. I have to
+receive confirmation of each request within ten days. Also, make sure
+the entire confirmation number was in the reply you sent me. Some 
+e-mail programs have a habit of cutting off some of the reply address,
+which can be quite long.
+
+I've set up a new confirmation number. To confirm that you would like
+
+!A
+
+added to the <#l#> mailing list, please send
+an empty reply to this address:
+
+!R
+</#xE/>
+
+or click here:
+       mailto:<#R#>
+</#E/>
+
+Again, check the reply address carefully to make sure it is all included 
+before you confirm your subscription.
+
+Sorry for the trouble.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+To confirm that you would like
+
+!A
+
+added to the <#l#> mailing list, please send
+an empty reply to this address:
+
+!R
+
+Usually, this happens when you just hit the "reply" button.
+If this does not work, simply copy the address and paste it into
+the "To:" field of a new message.
+</#xE/>
+
+or click here:
+       mailto:<#R#>
+</#E/>
+
+This confirmation serves two purposes. First, it verifies that I am able
+to get mail through to you. Second, it protects you in case someone
+forges a subscription request in your name.
+
+</#qE/>
+Some mail programs are broken and cannot handle long addresses. If you
+cannot reply to this request, instead send a message to
+<<#L#>-request@<#H#>> and put the
+entire address listed above into the "Subject:" line.
+
+</#sE/>
+This list is moderated. Once you have sent this confirmation, the
+request will be sent to the moderator(s) of this list. I will notify
+you when your subscription has been activated.
+
+</text/sub-nop#E/>
+I've been unable to carrry out your request: The address
+
+!A
+
+was already on the <#l#> mailing list when I received
+your request, and remains a subscriber.
+
+</text/sub-ok#E/>
+Acknowledgment: I have added the address
+
+!A
+
+to the <#l#> mailing list.
+
+Welcome to <#l#>@<#H#>!
+
+Please save this message so that you know the address you are
+subscribed under, in case you later want to unsubscribe or change your
+subscription address.
+
+To unsubscribe, send a message to:
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+Hi! This is the ezmlm program. I'm managing the
+<#l#>@<#H#> mailing list.
+
+I'm working for my owner, who can be reached
+at <#l#>-owner@<#H#>.
+
+</text/unsub-bad#E/>
+Oops, that confirmation number appears to be invalid.
+
+The most common reason for invalid numbers is expiration. I have to
+receive confirmation of each request within ten days. Also, make sure
+the entire confirmation number was in the reply you sent me. Some 
+e-mail programs have a habit of cutting off some of the reply address,
+which can be quite long.
+
+I've set up a new confirmation number. To confirm that you would like
+
+!A
+
+removed from the <#l#> mailing list, please send an empty reply 
+to this address:
+
+!R
+</#xE/>
+
+or click here:
+       mailto:<#R#>
+</#E/>
+
+Again, check the reply address carefully to make sure it is all included 
+before you confirm this action.
+
+Sorry for the trouble.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+To confirm that you would like
+
+!A
+
+removed from the <#l#> mailing list, please send an empty reply 
+to this address:
+
+!R
+
+Usually, this happens when you just hit the "reply" button.
+If this does not work, simply copy the address and paste it into
+the "To:" field of a new message.
+</#xE/>
+
+or click here:
+       mailto:<#R#>
+</#E/>
+
+I haven't checked whether your address is currently on the mailing list.
+To see what address you used to subscribe, look at the messages you are
+receiving from the mailing list. Each message has your address hidden
+inside its return path; for example, mary@xdd.ff.com receives messages
+with return path: <<#l#>-return-<number>-mary=xdd.ff.com@<#H#>.
+
+</#qE/>
+Some mail programs are broken and cannot handle long addresses. If you
+cannot reply to this request, instead send a message to
+<<#L#>-request@<#H#>> and put the entire address listed above
+into the "Subject:" line.
+
+</text/unsub-nop#E/>
+I'm sorry, I've been unable to carry out your request,
+since the address
+
+!A
+
+was not on the <#l#> mailing list when I received
+your request and is not a subscriber of this list.
+
+If you unsubscribe, but continue to receive mail, you're subscribed
+under a different address than the one you currently use.
+Please look at the header for:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+This shows that the subscription address is ``user@host.dom''.
+The unsubscribe address for this user would be:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+
+Just mail to that address, adjusted for the real subscription address.
+
+If the message has a ``List-Unsubscribe:'' header, you can send
+a message to the address in that header. It contains the subscription
+already coded into it.
+
+For some mail programs, you need to make the headers visible to
+see the return path:
+
+For Eudora 4.0, click on the "Blah blah ..." button.
+For PMMail, click on "Window->Show entire message/header". 
+
+If this still doesn't work, I'm sorry to say that I can't help you.
+Please FORWARD a list message together with a note about what you're
+trying to achieve and a list of addresses that you might be subscribed
+under to my owner:
+
+    <<#l#>-owner@<#H#>>
+
+who will take care of it. My owner is a little bit slower than I am, 
+so please be patient.
+
+</text/unsub-ok#E/>
+Acknowledgment: I have removed the address
+
+!A
+
+from the <#l#> mailing list. This address
+is no longer a subscriber.
+
+</text/edit-do#nE/>
+Please edit the following text file and send it to this address:
+
+!R
+
+Your mailer should have a Reply feature that uses this address automatically.
+
+I can remove the quote marks that your mailer adds to the text,
+as long as you do not edit the marker lines themselves.
+
+The marker lines are the lines starting with '%%%'. They must not
+be modified (extra characters added by your mailer at the beginning
+of the line are acceptable).
+
+
+</text/edit-list#nE/>
+The <#L#>-edit.file command can be used by a remote
+administrator to edit the text files than make up the bulk
+of the responses from the <#L#>@<#H#> list.
+
+What follows is a list of the response file name and a short
+description of when their contents are used.  To edit a file,
+simply send mail to <#L#>-edit.file, substituting the file name
+for 'file'. Editing instructions are mailed with the text file.
+
+File                Use
+
+bottom              bottom of all responses. General command info.
+digest              'administrivia' section of digests.
+faq                 frequently asked questions specific to this list.
+get_bad             in place of messages not found in the archive.
+help                general help (between 'top' and 'bottom').
+info                list info. First line should be meaningful on its own.
+mod_help            specific help for list moderators.
+mod_reject          to sender of rejected post.
+mod_request         to message moderators together with post.
+mod_sub             to subscriber after moderator confirmed subscribe.
+mod_sub_confirm     to subscription mod to request subscribe confirm.
+mod_timeout         to sender of timed-out post.
+mod_unsub_confirm   to remote admin to request unsubscribe confirm.
+sub_bad             to subscriber if confirm was bad.
+sub_confirm         to subscriber to request subscribe confirm.
+sub_nop             to subscriber after re-subscription.
+sub_ok              to subscriber after successful subscription.
+top                 top of all responses.
+</#tnE/>
+trailer             added to all posts sent out from the list.
+</#nE/>
+unsub_bad           to subscriber if unsubscribe confirm was bad.
+unsub_confirm       to subscriber to request unsubscribe confirm.
+unsub_nop           to non-subscriber after unsubscribe.
+unsub_ok            to ex-subscriber after successful unsubscribe.
+
+</text/edit-done#nE/>
+The text file was successfully updated.
+</text/info#E/>
+No information has been provided for this list.
+</text/faq#E/>
+FAQ - Frequently asked questions of the <#l#>@<#H#> list.
+
+None available yet.
+
diff --git a/ezmlmrc.es b/ezmlmrc.es
new file mode 100644 (file)
index 0000000..5b5cf00
--- /dev/null
@@ -0,0 +1,1264 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.es,v 1.4 1999/12/22 04:06:38 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc- Traducción de: Vicent Mas, Francesc Alted, Sonia Lorente, Cyndy DePoy
+# #######   Servicom2000
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+Para dar de baja la suscripción, mande un mensaje a:
+
+   <#L#>-unsubscribe@<#H#>
+
+Para obtener el resto de direcciones-comando, mande un mensaje a:
+
+   <#L#>-help@<#H#>
+
+</text/bottom#E/>
+
+--- Comandos administrativos para la lista <#l#> ---
+
+Puedo gestionar automáticamente peticiones administrativas. Por
+favor, no envíe este tipo de peticiones a la lista. Envíelas a la
+dirección-comando adecuada:
+
+Para obtener ayuda y una descripción de los comandos disponibles,
+mande un mensaje a:
+
+   <<#L#>-help@<#H#>>
+
+Para suscribirse a la lista, mande un mensaje a:
+
+   <<#L#>-subscribe@<#H#>>
+
+Para eliminar su dirección de la lista, simplemente mande un
+mensaje a la dirección que hay en la cabecera
+``List-Unsubscribe'' de cualquier mensaje de la lista. Si usted
+no ha cambiado su dirección desde que se suscribió, también puede
+enviar un mensaje a:
+
+   <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+o, para los resúmenes, a:
+
+   <<#L#>-unsubscribe@<#H#>>
+
+</#E/>
+
+Para añadir o eliminar direcciones, le enviaré un mensaje de
+confirmación a esa dirección. Cuando lo reciba, pulse el botón
+'Responder' para completar la transacción.
+
+Si necesita contactar con el propietario de la lista, por favor,
+mande un mensaje a:
+
+    <<#L#>-owner@<#H#>>
+
+Por favor, incluya una lista de mensajes REENVIADOS con TODAS LAS
+CABECERAS intactas para que sea más fácil ayudarle.
+
+--- Le adjunto una copia de la petición que he recibido.
+
+</text/bounce-bottom#E/>
+
+--- Le adjunto una copia del mensaje devuelto que he recibido.
+
+</text/bounce-num#E/>
+
+He guardado una lista de todos los mensajes de la lista de correo
+<#L#> que han sido devueltos procedentes de su dirección.
+
+</#aE/>
+
+Una copia de estos mensajes puede estar en el archivo.
+
+</#aE/>
+
+Para recibir los mensajes desde el número 123 al 145 (con un
+máximo de 100 por petición), escriba a:
+
+   <<#L#>-get.123_145@<#H#>>
+
+Para recibir una lista por "Asunto" y "Autor" de los últimos 100
+mensajes, envíe un mensaje en blanco a:
+
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Aquí están los números de los mensajes:
+
+</text/dig-bounce-num#E/>
+
+He guardado una lista de los resúmenes de la lista de correo
+<#L#> que han sido devueltos desde su dirección. Por cada uno de
+los resúmenes perdidos, he anotado el número del primer mensaje
+en el resumén.
+
+</#aE/>
+
+No archivo los resúmenes propiamente dichos, pero puede conseguir
+los mensajes del archivo principal de la lista.
+
+Para recuperar el grupo de mensajes del 123 al 145 (máximo 100
+por petición), envíe un mensaje en blanco a:
+
+   <<#L#>-get.123_145@<#H#>>
+
+Para recibir una lista por "Asunto" y "Autor" de los últimos 100
+mensajes, envíe un mensaje en blanco a:
+
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Estos son los números de mensaje de los resúmenes:
+
+</text/bounce-probe#E/>
+
+Parece que han sido devueltos algunos mensajes dirigidos a usted
+de la lista de correo <#l#>. Le he enviado un mensaje de aviso,
+pero también ha sido devuelto. Le adjunto una copia del mensaje
+devuelto.
+
+Esta es una prueba para comprobar si su dirección es accesible.
+Si esta prueba me es devuelta, eliminaré su dirección de la lista
+de correo <#l#>@<#H#>, sin más avisos. En ese caso, puede usted
+volver a suscribirse mandando un mensaje a esta dirección:
+
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn#E/>
+
+Han sido devueltos algunos mensajes para usted de la lista de
+correo <#l#>. Le adjunto una copia del primer mensaje devuelto
+que recibí. Si también se devuelve este mensaje, le mandaré una
+prueba. Si se devuelve la prueba, eliminaré su dirección de la
+lista de correo <#l#> sin más avisos.
+
+</text/digest#dE/>
+Para suscribirse al resumen escriba a:
+
+       <#L#>-digest-subscribe@<#H#>
+
+Para cancelar su suscripción al resumen, escriba a:
+
+       <#L#>-digest-unsubscribe@<#H#>
+
+Para mandar un mensaje a la lista, escriba a:
+
+       <#L#>@<#H#>
+
+</text/get-bad#E/>
+Lo siento, ese mensaje no está en el archivo.
+
+</text/help#E/>
+
+Este es un mensaje genérico de ayuda. El mensaje que recibí no
+fue mandado a ninguna de mis direcciones-comando.
+
+Aquí hay una lista de las direcciones comando disponibles:
+
+Mande un correo a las siguientes direcciones-comando para obtener
+información y FAQ de esta lista:
+
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#dE/>
+Para la lista de resúmenes existen direcciones-comando análogas:
+
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+
+Para recibir los mensajes desde el número 123 al 145 (con un
+máximo de 100 por petición), escriba a:
+
+   <<#L#>-get.123_145@<#H#>>
+
+Para obtener un índice con los campos "Asunto" y "Autor" para los
+mensajes del 123 al 456, debe escribir a:
+
+   <<#L#>-index.123_456@<#H#>>
+
+Para recibir todos los mensajes con el mismo "Asunto" que el
+mensaje 12345, mande un mensaje en blanco a:
+
+   <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+
+En realidad no es necesario que los mensajes estén en blanco,
+pero si no lo están ignoraré su contenido. Sólo es importante la
+DIRECCIÓN a la que se envía.
+
+Usted puede suscribir una dirección alternativa, por ejemplo,
+para "david@ordenador.dominio", simplemente añada un guión y su
+dirección (con '=' en lugar de '@') después del comando:
+
+   <<#L#>-subscribe-david=ordenador.dominio@<#H#>>
+
+Para cancelar la suscripción de esta dirección, escriba a:
+
+<<#L#>-unsubscribe-david=ordenador.dominio@<#H#>>
+
+</text/mod-help#E/>
+Gracias por acceder a moderar la lista <#L#>@<#H#>.
+
+Mis comandos son algo distintos a los de otras listas de correo,
+pero creo que los encontrará intuitivos y fáciles de utilizar.
+
+Estas son algunas instrucciones para las tareas que debe realizar
+como propietario y/o moderador de una lista de correo.
+
+Al final del mensaje se incluyen algunas instrucciones generales
+para la lista.
+
+Suscripción remota
+-------------------
+
+Como moderador, puede añadir o quitar cualquier dirección de la
+lista. Para suscribir "david@ordenador.dominio", basta con poner
+un guión después del comando, y después su dirección con '=' en
+lugar de '@'. Por ejemplo, para suscribir esa dirección, mande
+correo a:
+
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+De la misma manera puede eliminar la dirección de la lista
+mandando un mensaje a:
+
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#dE/>
+Para suscribirse o darse de baja de la lista de resúmenes:
+
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+</#E/>
+
+Eso es todo. No es necesario poner "Asunto" o cuerpo principal en
+el mensaje.
+
+</#rE/>
+
+Le mandaré una petición de confirmación, para asegurarme que
+usted me envió la petición. Simplemente responda al mensaje, y se
+cursará su petición. </#RE/> Mandaré una petición de confirmación
+a la dirección del usuario, en este caso a
+<david@ordenador.dominio>. El usuario simplemente debe responder
+a este mensaje de confirmación. </#E/>
+
+Las confirmaciones son necesarias para impedir, en la medida de
+lo posible, que un tercero añada o quite una dirección de la
+lista.
+
+Notificaré al usuario cualquier cambio en el estado de su
+suscripción.
+
+Suscripción
+------------
+
+Cualquier usuario puede suscribirse o darse de baja mandando un
+correo a:
+
+   <#L#>-subscribe@<#H#>
+   <#L#>-unsubscribe@<#H#>
+
+</#dE/>
+Para la lista de resúmenes:
+
+   <#L#>-digest-subscribe@<#H#>
+   <#L#>-digest-unsubscribe@<#H#>
+
+</#E/>
+
+El usuario recibirá una petición de confirmación para asegurarse
+que él/ella posee la dirección de suscripción. Una vez
+verificada, se procederá a dar de baja al usuario.
+
+</#sE/>
+
+Como esta lista está moderada para suscripciones, mandaré una
+segunda petición de confirmación al moderador. Como el usuario ya
+ha confirmado su deseo de estar en la lista, usted, como
+moderador, puede estar seguro de que la dirección del suscriptor
+es real. Si quiere aprobar la petición del usuario, simplemente
+responda a mi mensaje de confirmación. En caso contrario, puede
+simplemente borrar mi mensaje o contactar con el suscriptor para
+pedir más información.
+
+</#SE/>
+Las suscripciones funcionan del mismo modo.
+</#E/>
+
+El usuario también puede utilizar:
+
+   <<#L#>-subscribe-maria=ordenador.dominio@<#H#>>
+   <<#L#>-unsubscribe-maria=ordenador.dominio@<#H#>>
+
+para que le manden correo a: "maria@ordenador.dominio". Solo si
+ella recibe correo en esta dirección, podrá recibir la petición
+de confirmación y mandar una contestación.
+
+Su dirección e identidad no serán reveladas al suscriptor, a no
+ser que le mande correo directamente al usuario.
+
+</#rlE/>
+
+Para conseguir una lista de suscriptores para <#L#>@<#H#> envíe
+un mensaje a:
+
+   <<#L#>-list@<#H#>>
+
+Para conseguir un log de la lista de transacciones para
+<#L#>@<#H#> mande un mensaje a:
+
+   <<#L#>-log@<#H#>>
+
+</#rldE/>
+Para suscriptores al resumen:
+
+   <<#L#>-digest-list@<#H#>>
+
+y:
+
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+
+Usted puede modificar remotamente los ficheros de texto que
+componen las respuestas mandadas por la lista. Para conseguir una
+lista de ficheros e instrucciones de edición, escriba a:
+
+   <<#L#>-edit@<#H#>>
+
+</#mE/>
+Mensajes moderados.
+-------------------
+
+Cuando los mensajes están moderados, guardaré el mensaje enviado
+y le mandaré una copia junto con instrucciones. El mensaje para
+usted llevará "MODERATE for ..." como "Asunto".
+
+Para aceptar el mensaje, simplemente responda a la dirección que
+figura en el campo 'Responder a:' , que ya he configurado con la
+dirección correcta de aceptación. No necesita incluir el mensaje
+en sí. De hecho, ignoraré el contenido siempre y cuando la
+dirección a la que escriba sea correcta.
+
+Si quiere rechazar el mensaje, mande un correo a la dirección
+'De:', que ya he configurado con la dirección correcta de
+"rechazo". Eso normalmente se hace con 'Responder a todos', y
+borrando después todas las direcciones salvo la dirección
+"rechazada". Puede añadir un comentario al remitente insertando
+dicho comentario entre dos líneas que empiecen con tres '%'. Solo
+mandaré este comentario al remitente con el mensaje rechazado.
+Una vez más, no revelaré su identidad.
+
+Procesaré el mensaje en función de la primera respuesta que
+recibo. Le avisaré si me manda una petición para aceptar un
+mensaje que, Previamente, había sido rechazado o viceversa.
+
+Si no recibo respuestas del moderador dentro de un cierto periodo
+de tiempo (normalmente 5 días), devolveré el mensaje al remitente
+con una explicación de lo que ha pasado. Su administrador también
+puede configurar la lista para que estos mensajes "ignorados"
+simplemente sean borrados sin notificación, en lugar de ser
+devueltos al remitente.
+
+</#E/>
+
+Vacaciones
+----------
+
+Si está temporalmente en otra dirección, simplemente reenvíe
+todos los mensajes que tienen el encabezamiento 'Mailing-List:'
+(o todos los que tienen "Asunto" empezando con 'MODERATE for
+<#L#>@<#H#>' o con 'CONFIRM subscribe to <#L#>@<#H#>') a la nueva
+dirección. Así podrá leer la lista desde la nueva dirección. Como
+alternativa, puede reenviar los mensajes a un amigo para que él o
+ella los lea en su lugar. Por favor, antes de hacerlo consiga el
+permiso del propietario de la lista.
+
+Si desea aprobar automáticamente todas las peticiones mientras
+esté fuera, configure su programa de correo para responder
+automáticamente a mensajes que tienen un "Asunto" que reúna los
+criterios anteriormente expuestos.
+
+</#rE/>
+
+Si intenta administrar la lista remotamente, desde una dirección
+que no es la suya, el suscriptor cuya dirección está utilizando,
+y no usted, recibirá la petición de confirmación. Entonces, se
+mandará una petición de confirmación a todos los moderadores.
+Hago esto porque no tengo manera de saber que usted realmente ha
+mandado la petición original.
+
+En este caso, ¡Recuerde que se manda su petición original (y su
+dirección) al suscriptor!
+
+</#E/>
+
+¡Buena suerte!
+
+PD: Por favor, póngase en contacto con el propietario de la lista
+(<#L#>-owner@<#H#>) si tiene preguntas o problemas.
+
+</text/mod-reject#E/>
+
+Lo siento, el mensaje que le adjunto no fue aceptado por el
+moderador. Si el moderador ha hecho algún comentario, aparecerá
+en la parte inferior.
+
+</text/mod-request#E/>
+
+El mensaje adjunto fue mandado a la lista de correo <#L#>@<#H#>.
+
+Si desea aprobar su distribucisn a todos los suscriptores, por
+favor, escriba a:
+
+!A
+
+Normalmente esto ocurre al pulsar el botón "responder". Usted
+puede comprobar la dirección para asegurarse de que empieza por
+"<#L#>-accept" . Si esto no funciona, simplemente copie la
+dirección y péguela en el campo "Para:" de un nuevo mensaje.
+</#xE/>
+
+Como alternativa haga clic aquí:
+
+       mailto:<#A#>
+</#E/>
+
+Para rechazar el mensaje y hacer que sea devuelto al remitente,
+por favor, mande un mensaje a:
+
+!R
+
+Normalmente, lo más fácil es hacer clic en el botón "Responder a
+todos" y luego quitar todas las direcciones menos la que empieza
+con "<#L#>-reject".
+</#xE/>
+
+Como alternativa, haga clic aquí:
+       mailto:<#R#>
+</#E/>
+
+No es necesario copiar el mensaje en su respuesta para aceptarlo
+o rechazarlo. Si desea mandar un comentario al remitente de un
+mensaje rechazado, por favor, inclúyalo entre dos líneas que
+empiezan con tres signos de porcentaje ('%'):
+
+%%% Inicio del comentario
+%%% Fin del comentario
+
+¡Gracias por su ayuda!
+
+--- Se adjunta el mensaje enviado.
+
+</text/mod-sub#E/>
+
+--- Le he suscrito o dado de baja por petición del moderador de
+la lista de correo <#l#>@<#H#>.
+
+Si no está de acuerdo con esta acción, por favor, mande una queja
+u otros comentarios al propietario de la lista
+(<#l#>-owner@<#H#>) tan pronto como le sea posible.
+
+</text/mod-timeout#E/>
+
+Lo siento, los moderadores de la lista <#L#> no han procesado su
+mensaje. Por esa razón, se lo devuelvo. Si cree que esto es un
+error, por favor, vuelva a mandar el mensaje o póngase en
+contacto con el moderador de la lista directamente.
+
+--- Le adjunto el mensaje que mandó.
+
+</text/mod-sub-confirm#E/>
+Respetuosamente pido permiso para añadir
+
+!A
+
+a los suscriptores de la lista de correo <#l#>. Esta petición o
+procede de usted o ha sido ya verificada por el suscriptor.
+
+Para confirmar, por favor, envíe un mensaje en blanco a esta
+dirección:
+
+!R
+
+Normalmente esto ocurre al pulsar el botón "Responder". Si esto
+no funciona, simplemente copie la dirección y péguela en el campo
+"Para:" de un nuevo mensaje. </#xE/>
+
+o haga clic aquí:
+
+       mailto:<#R#>
+</#E/>
+
+Si no está de acuerdo, simplemente ignore este mensaje. 
+
+¡Gracias por su ayuda!
+
+</text/mod-unsub-confirm#E/>
+Se ha hecho una petición para eliminar
+
+!A
+
+de la lista de correo <#l#>. Si está de acuerdo, por favor, envíe
+un mensaje en blanco a esta dirección:
+
+!R
+
+Normalmente, esto ocurre al pulsar el botón "Responder". Si esto
+no funciona, simplemente copie la dirección y péguela en el campo
+"Para:" de un nuevo mensaje. 
+</#xE/>
+
+o haga clic aquí:
+
+       mailto:<#R#>
+</#E/>
+
+Si no está de acuerdo, simplemente ignore este mensaje. 
+
+¡Gracias por su ayuda!
+
+</text/sub-bad#E/>
+¡Vaya!, parece que el número de confirmación no es válido. 
+
+La principal causa por la que los números se invalidan es su
+expiración. Yo tengo que recibir confirmación de cada petición en
+un plazo de diez días. Además, asegúrese de que el número de
+confirmación completo estaba incluido en la respuesta que me
+mandó. Algunos programas de correo tienen la (mala) costumbre de
+cortar parte de la dirección de respuesta, que puede ser muy
+larga.
+
+He configurado un nuevo número de confirmación. Para confirmar
+que le gustaría que
+
+!A
+
+fuese añadida a la lista de correo <#l#>, por favor, mande un
+mensaje en blanco a esta dirección:
+
+!R
+</#xE/>
+
+o haga clic aquí:
+
+       mailto:<#R#>
+</#E/>
+
+De nuevo, compruebe cuidadosamente la dirección de la respuesta
+para asegurarse que esté todo incluido antes de confirmar su
+suscripción.
+
+Perdone las molestias.
+
+       <#L#>-Propietario <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+Para confirmar que le gustaría que
+
+!A
+
+fuese añadido a la lista de correo <#l#>, por favor, envíe un
+mensaje en blanco a esta dirección:
+
+!R
+
+Normalmente esto ocurre al pulsar el botón "Responder". Si eso no
+funciona, es suficiente copiar la dirección y pegarla en el campo
+"Para:" de un nuevo mensaje. 
+</#xE/>
+
+o haga clic aquí:
+
+       mailto:<#R#>
+</#E/>
+
+Esta confirmación cumple dos propósitos. Primero, verifica que
+puedo mandarle correo. Segundo, le protege en el caso de que
+alguien intente falsificar una petición de suscripción en su
+nombre.
+
+</#qE/>
+
+Algunos programas de correo no pueden manejar direcciones largas.
+Si no puede responder a esta petición, envíe un mensaje a
+<<#L#>-request@<#H#>> y ponga la dirección entera escrita arriba
+en la línea de "Asunto:".
+
+</#sE/>
+
+Esta lista está moderada. Una vez que haya enviado esta
+confirmación, se mandará la petición al moderador de la lista.
+Cuando su suscripción haya sido activada, se lo notificaré.
+
+</text/sub-nop#E/>
+No he conseguido cursar su petición: La dirección
+
+!A
+
+ya estaba en la lista de correo <#l#> cuando recibí su petición,
+y usted sigue siendo suscriptor.
+
+</text/sub-ok#E/>
+Acuse de recibo: He añadido la dirección
+
+!A
+
+A la lista de correo <#l#>.
+
+¡Bienvenido a <#l#>@<#H#>!
+
+Por favor guarde este mensaje para que sepa bajo que dirección
+está suscrito, por si luego quiere cancelar su suscripción o
+cambiar la dirección de la misma.
+
+Para cancelar su suscripción mande un mensaje a:
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+
+¡Hola! Soy el programa ezmlm. Me ocupo de la lista de correo
+<#l#>@<#H#>.
+
+</#x/>
+
+Estoy trabajando para mi propietario, a quien se puede localizar
+en <#l#>-owner@<#H#>.
+
+</text/unsub-bad#E/>
+¡Vaya!, parece que ese número de confirmación es inválido. 
+
+La principal razón por la que los números de confirmación se
+invalidan es la expiración. Debo recibir confirmación de cada
+petición en un plazo de diez días. Además, asegúrese que el
+número completo de confirmación estaba incluido en la respuesta
+que me mandó. Tenga en cuenta que algunos programas de correo
+tienen la (mala) costumbre de cortar parte de la dirección de
+respuesta, que puede ser muy larga.
+
+He configurado un nuevo número de confirmación. Para confirmar
+que le gustaría que
+
+!A
+
+fuese dado de baja en la lista de correo <#l#>, por favor, mande
+un mensaje en blanco a esta dirección:
+
+!R
+</#xE/>
+
+o haga clic aquí:
+
+       mailto:<#R#>
+</#E/>
+
+De nuevo, compruebe la dirección de respuesta cuidadosamente para
+asegurarse que esté todo incluido antes de confirmar esta acción.
+
+Perdone las molestias.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+Para confirmar que le gustaría que
+
+!A
+
+sea dado de baja de la lista de correo <#l#>, por favor, mande un
+mensaje en blanco a esta dirección:
+
+!R
+
+Normalmente esto ocurre al pulsar el botón "Responder". Si no
+funciona, simplemente copie la dirección y péguela en el campo
+"Para:" de un nuevo mensaje. 
+</#xE/>
+
+o haga clic aquí:
+
+       mailto:<#R#>
+</#E/>
+
+No he comprobado si su dirección está actualmente en la lista de
+correo. Para ver que dirección utilizó para suscribirse, mire los
+mensajes que está recibiendo de la lista de correo. Cada mensaje
+tiene su dirección oculta dentro de la ruta de retorno; por
+ejemplo, maria@xdd.ff.com recibe mensajes con la ruta de retorno:
+<<#l#>-return-<número>-maria=xdd.ff.com@<#H#>>.
+
+</#qE/>
+
+Algunos programas de correo no pueden manejar direcciones largas.
+Si no puede responder a esta petición, envíe un mensaje a
+<<#L#>-request@<#H#>> y escriba la dirección completa en la línea
+de "Asunto:".
+
+</text/unsub-nop#E/>
+Lo siento, no he podido cursar su petición porque la dirección
+
+!A
+
+no estaba en la lista de correo <#l#> cuando recibí su petición y
+no es suscriptor de esta lista.
+
+Si se da de baja, pero sigue recibiendo correo, es que está
+suscrito con una dirección distinta a la que usa actualmente. Por
+favor, busque en las cabeceras el texto:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+La dirección para dar de baja a este usuario sería:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+
+Simplemente escriba a esa dirección, tras modificarla con la
+verdadera dirección de suscripción.
+
+Si el mensaje tiene una cabecera ``List-Unsubscribe:'' puede
+usted mandar un mensaje a la dirección de esa cabecera. La
+cabecera ya contiene la petición de suscripción.
+
+En algunos programas de correo, necesitará hacer visibles los
+encabezamientos para ver el campo de retorno:
+
+Con Eudora 4.0, haga clic en el botón "Blah blah ...". Con
+PMMail, haga clic en "Window->Show entire message/header".
+
+Si esto tampoco da resultado, siento decirle que no le puedo
+ayudar. Por favor, reenvíe el mensaje junto con una nota sobre lo
+que está intentando hacer y una lista de direcciones bajo las
+cuales puede estar suscrito, a mi propietario:
+
+    <#l#>-owner@<#H#>
+
+que se ocupará de todo. Mi propietario es un poco más lento que
+yo; por favor, tenga paciencia.
+
+</text/unsub-ok#E/>
+Acuse de recibo: He dado de baja la dirección
+
+!A
+
+de la lista de correo <#l#>. Esta dirección ya no está suscrita.
+
+</text/edit-do#nE/>
+
+Por favor edite el siguiente fichero de texto y envíelo a esta
+dirección:
+
+!R
+
+Su programa de correo debería tener la opción "Responder" que
+utiliza esta dirección automáticamente.
+
+Puedo quitar las comillas que su programa añade al texto, siempre
+y cuando usted no modifique las líneas marcadoras (las que
+empiezan con '%%%'). Estas líneas no deben ser modificadas (solo
+son tolerados caracteres añadidos por su programa de correo al
+principio de la línea).
+
+</text/edit-list#nE/>
+
+El comando <#L#>-edit.file puede ser utilizado por un
+administrador remoto para modificar los ficheros de texto que
+componen la mayoría de las respuestas de la lista de correo
+<#L#>@<#H#>.
+
+Lo que sigue es un listado de los ficheros de respuesta y una
+corta indicación de cuando se utilizan sus contenidos. Para
+modificar un fichero, simplemente envíe un correo a
+<#L#>-edit.fichero, sustituyendo 'fichero' por el nombre del
+fichero. Las instrucciones para las modificaciones se envían con
+el fichero.
+
+File                Use
+
+bottom        final de todas las respuestas. Información general
+              sobre comandos.        
+digest        sección administrativa de resúmenes. 
+faq           preguntas frecuentes propias de esta lista.
+get_bad       en lugar de mensajes no encontrados en el archivo.
+help          ayuda general (entre 'top' y 'bottom').
+info          información sobre la lista. La primera línea debe
+              tener significado por sí misma. 
+mod_help      ayuda específica para moderadores.
+mod_reject    al remitente del mensaje rechazado.
+mod_request   a los moderadores de mensajes junto a los mensajes.
+mod_sub       al suscriptor después de que el moderador confirme
+              la suscripción.
+mod_sub_confirm  al moderador para pedir confirmación de 
+                 suscripción.
+mod_timeout   al remitente de correo caducado.  
+mod_unsub_confirm  al administrador remoto para pedir confirmación
+                   de baja.
+sub_bad       al suscriptor si la confirmación no fue correcta.
+sub_confirm   al suscriptor para pedir confirmación de 
+              suscripción.
+sub_nop       al suscriptor después de re-suscribirse.
+sub_ok        al suscriptor después de la suscripción. 
+top           el principio de todas las respuestas.
+</#tnE/>
+trailer       añadido a todos los mensajes enviados de la lista.
+</#nE/>
+unsub_bad     al suscriptor si la confirmación de baja fue 
+              incorrecta.
+unsub_confirm al suscriptor para pedir confirmación de 
+              cancelación.
+unsub_nop     al no suscriptor después de darse de baja.
+unsub_ok      al ex suscriptor después de darse de baja.
+
+</text/edit-done#nE/>
+El fichero de texto fue actualizado correctamente.
+</text/info#E/>
+No se ha proporcionado información para esta lista.
+</text/faq#E/>
+FAQ - Preguntas más comunes para la lista <#l#>@<#H#>.
+
+Ninguno disponible todavía. 
+
+
diff --git a/ezmlmrc.fr b/ezmlmrc.fr
new file mode 100644 (file)
index 0000000..dd04a17
--- /dev/null
@@ -0,0 +1,1226 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.fr,v 1.22 1999/12/22 04:02:15 lindberg Exp $
+#Name:$
+#
+# ezmlmrc.fr - Traduction: Frank DENIS "Jedi/Sector One" <j@4u.net>
+# ##########
+# Controle les actions de ezmlm-make apres le patch ezmlm-idx-0.31 ou suivant.
+#
+# Le repertoire de base 'DIR' est toujours cree par ezmlm-make, comme DIR/key.
+# Tout le reste est genere a partir d'ici.
+#
+# ezmlm-make cherche ce fichier,d'abord en tant que .ezmlmrc dans le repertoire
+# ou les fichiers .qmail seront places (si vous avez utilise l'option -c en
+# ligne de commande), puis dans /etc/ezmlmrc, et enfin ezmlmrc dans le
+# repertoire contenant l'executable de ezmlm-make.
+# Ainsi, vous pouvez personnaliser ezmlm-make au niveau global en placant une
+# copie personnalisee d'ezmlmrc dans /etc et au niveau utilisateur en le
+# copiant dans .ezmlmrc dans le repertoire utilisateur ET en utilisant le
+# commutateur -c d'ezmlm-make.
+#
+# Les reperes sont:
+#      </nomdefichier/>  : ce qui suit sera place dans DIR/nomdefichier.
+#      </-nomdefichier/> : efface DIR/nomdefichier.
+#      </+repertoire/>   : cree le repertoire DIR/repertoire.
+#      </:lien/repertoire>: lien symbolique DIR/.qmail-list-lien -> DIR/repertoire.
+#
+# Le nom a l'interieur d'un repere peut etre suffixe de '#' et de n'importe
+# quel commutateur correspondant a ceux de la ligne de commande. L'objet sera
+# cree/etendu uniquement si tous les commutateurs listes sont positionnes.
+# Les fichiers peuvent etre agrandis tant qu'ils sont les derniers crees, mais
+# pas si un autre fichier a ete ebauche depuis. Les commutateurs inconnus
+# sont ignores sans preavis.
+# 
+# Ainsi, </nomfichier#aP> cree le fichier si et seulement si la liste est
+# archivee (-a) et non publique (-P). Si le repere suitant est
+# </nomfichier#m/> , le fichier est agrandi avec le texte qui suit jusqu'au
+# prochain repere si les messages de la liste sont moderes (-m).
+# Si le repere suivant est </autrechose/>, 'nomfichier' est ferme. Tout autre
+# repere impliquant la reouverture de 'nomfichier' tronquera le fichier au
+# lieu de l'agrandir.
+#
+# Un ensemble de commutateurs pour l'utilisateur (xX, yY, zZ) sont disponibles
+# pour la personalisation.
+# 
+# Les substitutions sont:
+# <#B#> chemin exe d'ezmlm    <#C#> code bulletin       <#D#> repertoire
+# <#H#> machine               <#L#> local               <#F#> commutateurs
+# <#T#> point-qmail           <#0#> arg pour -0. <#3#>..<#9#> arg pour -3..9
+# <#1#> ext1                  <#2#> ext2 [si le point-qmail est /chemin/.qmail-ext1-ext2-nom]
+# Le dernier est utile quand un seul utilisateur controle plusieurs domaines
+# virtuels.
+# Les reperes inconnus sont copies tels quels. ezmlm-manage remplace <#A#> par
+# l'adresse de l'abonne et <#R#> par celle de confirmation. ezmlm-store
+# remplace <#A#> par l'adresse d'acceptation et <#R#> par celle de rejet.
+#-----------------------------ici
+#
+# -0 est utilise pour l'adresse de la liste principale dans le cas de sous-listes.
+# -3 est pour le nouvel en-tete "from" si nous voulons que celui-ci soit remplace
+# -4 pour specifier les commutateurs de ezmlm-tstdig utilises dans dir/editor.
+#    Par defaut, il s'agit de -k64 -m30 -t24
+# -5 pour l'adresse du proprietaire de la liste: liste-owner. Les messages
+# envoyes a liste-owner seront renvoyes vers cette adresse.
+# -6 pour les informations de connexion SQL
+# -7 pour le contenu de DIR/modpost
+# -8 pour le contenu de DIR/modsub
+# -9 pour le contenu de DIR/remote
+#
+# Pour faciliter la tache, le commutateur '-x' produit les actions
+# suivantes non standards :
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Des tentatives de creation de lien ou de repertoires deja existants
+# provoqueront une erreur FATALE. Des tentatives d'ouverture de fichiers deja
+# fermes ou abandonnes causeront leur ecrasement.
+#
+# Un des problemes majeurs avec les listes ezmlm est DIR/inlocal. Pour des
+# utilisateurs normaux, il contient le nom de la liste (genre utilisateur-liste)
+# ce qui est correct. En revanche, pour l'utilisateur 'ezmlm' controlant le
+# domaine virtuel 'machine.domaine.com' le nom de la liste est
+# 'liste@machine.domaine.com' , mais inlocal doit etre 'ezmlm-liste', et non
+# 'liste'. De facon similaire, si ezmlm-domaine1 controle 'machine.domaine.com'
+# liste@machine.domaine.com doit garantir un inlocal contenant
+# 'ezmlm-domaine1-liste'. Pour avoir toujours une liste correcte, placez ce
+# fichier dans le repertoire utilisateur (~ezmlm/.ezmlmrc) et changez le
+# texte du fichier inlocal en 'ezmlm-<#L#>' ou 'ezmlm-<#1#>-<#L#>',
+# respectivement.
+#
+# Configuration pour prevoir l'edition future sans ligne de commande ezmlm-make
+# autre que le repertoire. Utile pour les outils d'edition IHM/WWW.
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# Repertoires opur les bulletins
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# Pour la base de donnees d'adresses supplementaires
+</+allow/>
+</+allow/subscribers/>
+# Pour la liste noire
+</+deny#k/>
+</+deny/subscribers#k/>
+# Base de donnees des moderateurs et repertoires des files d'attente.
+# Requis opur -m, -r -s, c'set pourquoi nous les mettons en place par defaut.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# liens: point -> DIR/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# Uniquement pour les listes moderees
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# On se debarasse des commutateurs de configuration pour le mode d'edition
+# pour pouvoir debuter sur de bonnes bases.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Inutile, si ce n'est pour la moderation des messages.
+</-moderator#eM/>
+# Nous ne nettoyons pas les fichiers de texte pour aider
+# les utilisateurs effectuant leur configuration manuellement,
+# par exemple en modifiant le repertoire DIR/remote.
+</modsub#s/>
+<#8#>
+# Administration distante
+</remote#r/>
+<#9#>
+# Moderation des messages
+</modpost#m/>
+<#7#>
+# Adresse du proprietaire de la liste
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Gestion des inscriptions. Ajoutez des commutateurs si vous desirez des
+# bulletins dans un format personnalise. Les commandes de services sont
+# donnees en tant que sujet de la requete a l'adresse # si le commutateur
+# -q est selectionne. De meme, -l et -d activent l'edition ou le fichier
+# texte des abonnes, pour l'administration distante.
+# -u donne a l'abonne uniquement acces a l'archive.
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Il est possible d'ajouter -l et -d meme pour les listes non
+# moderees, etant donne que ezmlm-manage ne l'acceptera pas a moins
+# qu'il n'y ait des administrateurs distants.
+# Tout d'abord pour les listes avec une confirmation normale :
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... et maintenant sans confirmation d'inscription ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... maintenant aucune confirmation pour se desinscrire ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... et finalement aucune confirmation nulle part ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# Le rejet ne devrait pas etre configure pour les sous-listes.
+</#^0/>
+# Par defaut, on rejete maintenant tout, pour satisfaire les exigences des
+# adresses de listes dans les champs To et Cc.
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => rejette les contributions des adresses en liste noire. Valable aussi
+# pour les listes moderees - permet de filtrer les perturbateurs.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# commutateur -u=> Restriction aux sous-lists et aux bulletins.
+# Sans l'option 'm', le faire avec ezmlm-issubn, si 'm' est active, en
+# revanche, le faire avec ezmlm-gate.
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# Pour la moderation de messages, l'editeur dispose de store et de clean.
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# Pour les listes non moderees, il y a send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive est ici pour les listes normales. A mettre dans moderator pour
+# les listes qui utilisent mess-mod.
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# Toutes les listes peuvent provoquer des avertissements, a moins que
+# l'option -w ne soit activee.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Pour les retours a l'expediteur des bulletins.
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# Retours a l'expediteur pour les listes et bulletins.
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# Le fichier moderator est ajoute uniquement pour les listes dont les messages sont
+# moderes. Toutefois, '-e' ne le retire pas vu qu'il est impossible d'en
+# retirer les liens symboliques (ils sont en dehors du repertoire de la liste).
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# Pour les sous-listes, ce qui suit doit etre preserve
+list-post
+# Retire l'en-tete "from" si -3 est actif.
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+Pour toute requete administrative, contactez <#L#>-help@<#H#>; Liste geree par ezmlm
+# Headeradd doit toujours exister mais les sous-listses sont un cas a part.
+</headeradd#E^0/>
+# Impeccable pour les listes de diffusion (et le programme "vacation").
+Precedence: bulk
+# Pour eviter d'etre indexe par le site findmail.com
+X-No-Archive: yes
+# rfc2369, d'abord ce qui ne concerne que la liste principale, ensuite
+# les en-tetes pour les sous-listes.
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# Ajoute une nouvelle ligne "From: xxx" si l'on a utilise -3 'xxx' .
+</#3E/>
+From: <#3#>
+# Taille maximale et taille minimale de chaque message.
+</msgsize#x/>
+30000:2
+# Retire les parties en mime si l'option -x est utilisee.
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# Ceux qui suivent peuvent aussi etre exclus. Mais pour beaucoup de listes,
+# il est preferable de les accepter. Pour ce faire, retirez leur commentaire.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Traite les informations de connexion SQL
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- Fin de tout ce qui concerne le SQL.
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+Desinscription: envoyez un message a: <#L#>-unsubscribe@<#H#>
+Pour obtenir de l'aide, ecrivez a: <#L#>-help@<#H#>
+</text/bottom#E/>
+
+--- Voici les adresses relatives aux commandes de cette liste:
+
+Je peux prendre en charge les requetes administratives automatiquement.
+Envoyez simplement un petit message a l'une de ces adresses:
+
+Pour recevoir de l'aide et une description des commandes possibles, envoyez
+un message a :
+   <<#L#>-help@<#H#>>
+
+Pour vous inscrire a la liste de diffusion <#L#>, envoyez un message a :
+   <<#L#>-subscribe@<#H#>>
+
+Pour retirer votre adresse de la liste, envoyez simplement un message a
+l'adresse qui se trouve dans l'en-tete ``List-Unsubscribe'' situe dans
+n'importe quel message de la liste. Si votre adresse actuelle est strictement
+identique a celle que vous avez utilisee pour vous inscrire, il vous est aussi
+possible d'envoyer un message quelconque a :
+   <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+ou pour une liste qui vous serait envoyee sous forme de bulletin, a :
+   <<#L#>-unsubscribe@<#H#>>
+
+</#E/>
+Pour l'ajout ou le retrait d'adresses, vous receverez systematiquement un
+message de confirmation. Il vous suffira d'y repondre pour achever la
+transaction.
+
+Si vous desirez contacter le proprietaire de cette liste, veuillez envoyer un
+message a :
+
+    <<#L#>-owner@<#H#>>
+
+S'il vous plait, envoyez un message qui vous a ete adresse avec TOUS SES
+EN-TETES (faite suivre le message : "forward") pour qu'il puisse plus
+aisement vous aider.
+
+--- Ci-dessous se trouve une copie de la requete que j'ai recue.
+
+</text/bounce-bottom#E/>
+
+--- Ci-dessous se trouve une copie du message problematique qui m'est revenu :
+
+</text/bounce-num#E/>
+
+
+J'ai conserve une liste des messages de la liste de diffusion <#L#> qui
+n'ont temporairement pas pu etre delivres a votre adresse.
+
+</#aE/>
+Une copie de ces messages doit se trouver dans l'archive.
+
+</#aE/>
+Pour obtenir l'ensemble des messages depuis le numero 123 jusqu'au numero 145
+(avec un maximum de 100 par requete), envoyez un message vide avec un sujet
+quelconque a :
+   <<#L#>-get.123_145@<#H#>>
+
+Pour recevoir un resume des 100 derniers messages, avec uniquement le nom des
+auteurs et les sujets traites, envoyez un message a :
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Voici les numeros des messages en question :
+
+</text/dig-bounce-num#E/>
+
+J'ai conserve une liste des bulletins de la liste de diffusion <#L#>
+qui n'ont temporairement pas pu etre delivres a votre adresse. Pour chaque
+bulletin que vous n'avez pas pu consulter, j'ai note les numeros du premier
+message qui s'y trouvait.
+
+</#aE/>
+Aucune archive des bulletins eux-memes n'est conservee, mais vous pouvez
+toujours recuperer les messages a partir de la liste principale.
+
+Ainsi, pour obtenir les messages situes entre le numero 123 et le numero 145
+(avec un maximum de 100 par requete), il suffit d'envoyer un message 
+a l'adresse :
+   <<#L#>-get.123_145@<#H#>>
+
+Et pour recevoir un resume des 100 derniers messages (auteur/sujet), envoyez
+un message a :
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Voici le numero du premier message de chaque bulletin :
+
+</text/bounce-probe#E/>
+
+Des messages vous etant adresses en tant qu'abonne a la liste de diffusion
+<#l#> n'ont pas pu atteindre votre compte E-Mail. J'ai tente de vous en
+informer, mais ce message d'information n'a lui aussi pas pu atteindre sa
+cible.
+Voici une copie du message de retour a l'expediteur.
+
+Ceci est en realite un test pour determiner s'il est possible de vous
+contacter a l'adresse de votre inscription. Si ce dernier essai ne s'avere
+pas concluant, je serais contraint de retirer votre adresse de la liste
+<#l#>@<#H#> sans autre avertissement.
+
+Il vous sera toujours possible de vous re-inscrire a l'adresse :
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn#E/>
+
+Un certain nombre de messages provenant de la liste de diffusion
+<#l> n'ont pas pu vous etre remis correctement. En attachement, vous trouverez
+une copie du premier message de retour a l'envoyeur que j'ai recu.
+
+Si le message que vous lisez actuellement ne parvient pas non plus a
+destination, un dernier message vous sera envoye. Si celui-ci echoue aussi,
+votre adresse sera malheureusement retiree de la liste <#l#>.
+
+</text/digest#dE/>
+Pour vous abonner a la liste sous forme de bulletins, ecrivez a :
+       <#L#>-digest-subscribe@<#H#>
+
+Pour vous desabonner des bulletins, ecrivez a :
+       <#L#>-digest-unsubscribe@<#H#>
+
+Les participations a la liste se font a l'adresse suivante :
+       <#L#>@<#H#>
+
+</text/get-bad#E/>
+Desole, mais ce message est introuvable dans l'archive.
+
+</text/help#E/>
+Ceci est un message d'aide generique. Le message que j'ai recu n'etait pas
+adresse a l'une de mes adresses correspondant aux commandes valides.
+
+Voici donc une liste des commandes supportees, representees par des adresses
+auxquelles ecrire.
+
+Envoyez un message a l'adresse suivant pour obtenir des informations et des
+reponses aux questions courantes sur cette liste de diffusion :
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#dE/>
+Des adresses similaires existent pour la liste sous forme de bulletins :
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i necessite l'ajout de la ligne ezmlm-get. Faute de quoi il
+# serait impossible de faire de la saisie multiple !
+</#aE/>
+Pour obtenir tous les messages situes entre le numero 123 et le numero 145
+(avec un maximum de 100 par requete), envoyez un message a :
+   <<#L#>-get.123_145@<#H#>>
+
+Pour obtenir un resume des messages 123 a 456 avec uniquement les sujets et
+le nom des auteurs, envoyez un message a :
+   <<#L#>-index.123_456@<#H#>>
+
+Pour recevoir tous les messages comportant le meme sujet que le message 12345,
+envoyez un message a :
+   <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+En realite, les messages n'ont pas besoin d'etre vides, leur contenu sera
+totalement ignore. Seul l'ADRESSE a laquelle vous ecrivez est determinante.
+
+Il est possible de demander un abonnement a une autre adresse que la votre.
+Par exemple pour abonner "john@machine.domaine", il suffit d'ajouter un tiret
+et l'adresse (avec le signe "=" a la place de "@") apres le nom de la
+commande :
+<<#L#>-subscribe-john=machine.domaine@<#H#>>
+
+Pour desabonner cette derniere adresse :
+<<#L#>-unsubscribe-john=machine.domaine@<#H#>>
+
+</text/mod-help#E/>
+Merci beaucoup d'accepter le role de moderateur pour la liste de diffusion
+<#L#>@<#H#> .
+
+Mes commandes sont legerement differentes des autres listes.
+Elles peuvent d'abord sembler inhabituelles, mais une fois que
+vous les aurez utilisees, vous apprecierez la simplicite du
+systeme, et la vitesse a laquelle je repond a vos souhaits.
+
+Voici quelques informations au sujet du fonctionnement de la
+moderation:
+
+Inscription a distance
+----------------------
+En tant que moderateur, vous pouvez inscrire et desinscrire n'importe quelle
+adresse a la liste de diffusion.
+Ainsi, pour abonner "john@machine.domaine", mettez simplement un tiret apres
+le nom de la commande, puis l'adresse du futur abonne en remplacant le signe
+"@" par "=".
+Par exemple, pour inscrire l'utilisateur ci-dessus, envoyez un message a :
+   <<#L#>-subscribe-john=machine.domaine@<#H#>>
+
+De facon similaire, il est possible de retirer son adresse de la liste en
+ecrivant a :
+   <<#L#>-unsubscribe-john=machine.domaine@<#H#>>
+
+</#dE/>
+Idem pour les listes presentees sous la forme de bulletins :
+   <<#L#>-digest-subscribe-john=machine.domaine@<#H#>>
+   <<#L#>-digest-unsubscribe-john=machine.domaine@<#H#>>
+
+</#E/>
+C'est tout ! Aucun sujet special ni corps de message n'est requis.
+
+</#rE/>
+Je vous enverrai une demande de confirmation, pour etre
+sur que vous etes bien a l'origine de la requete. Repondez
+simplement au message et la transaction sera achevee.
+</#RE/>
+J'enverrai une demande de confirmation a l'adresse de
+l'utilisateur (ici john@machine.domaine).
+Tout ce qu'il aura a faire sera de repondre au message.
+</#E/>
+
+Ces etapes de confirmation sont necessaires pour rendre beaucoup plus ardu
+le fait qu'un tiers puisse ajouter ou retirer une adresse de la liste.
+
+Je previendrai bien entendu l'utilisateur quand son etat d'abonne changera.
+
+Inscription
+-----------
+
+N'importe quel utilisateur peut s'abonner ou se desabonner en envoyant un
+message a :
+
+<#L#>-subscribe@<#H#> pour l'inscription
+<#L#>-unsubscribe@<#H#> pour la desinscription
+
+</#dE/>
+Et pour les listes presentees sous forme de bulletins :
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+</#E/>
+L'utilisateur recevra une demande de confirmation pour etre
+sur qu'il/elle controle l'adresse d'inscription. Une fois
+ceci verifie, l'utilisateur sera desinscrit.
+
+</#sE/>
+Etant donne que cette liste est moderee pour les abonnements,
+j'enverrai une seconde demande de confirmation aux moderateurs.
+Etant donne que l'utilisateur a deja confirme son desir
+d'appartenir a la liste, vous, en tant que moderateur, pouvez
+etre surs que l'adresse de l'abonne est reelle. Si vous desirez
+approuver la requete d'un utilisateur, repondez simplement a mon
+message de confirmation. Dans le cas contraire, vous pouvez
+simplement effacer mon message ou eventuellement contacter l'abonne
+potentiel pour davantage de renseignements.
+
+</#SE/>
+Les inscriptions fonctionnent de facon analogue.
+</#E/>
+
+Il est aussi possible d'utiliser :
+
+   <<#L#>-subscribe-mary=machine.domaine@<#H#>>
+   <<#L#>-unsubscribe-mary=machine.domaine@<#H#>>
+
+pour que les messages soient envoyes a "mary@machine.domaine". Elle
+recevra la demande de confirmation uniquement si elle recoit son
+courrier a cette adresse et si elle est alors en mesure d'y repondre.
+
+Votre adresse et votre identite ne seront pas revelees a l'abonne,
+a moins que vous ne lui ecriviez directement.
+
+</#rlE/>
+Pour obtenir la liste des abonnees a <#L#>@<#H#>, envoyez un
+message a : 
+   <<#L#>-list@<#H#>>
+
+Pour obtenir un historique des transactions de la liste
+<#L#>@<#H#>, envoyez un message a :
+   <<#L#>-log@<#H#>>
+
+</#rldE/>
+Pour les abonnes aux bulletins :
+   <<#L#>-digest-list@<#H#>>
+et :
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+Vous pouvez a distance editer les fichiers de texte contenant les
+messages du robot. Pour obtenir la liste des fichiers et les
+instructions pour les editer, ecrivez a :
+   <<#L#>-edit@<#H#>>
+
+</#mE/>
+Envoi de messages moderes
+-------------------------
+Lorsque l'envoi de messages est modere, je conserverai chaque
+message en attente et vous en enverrai une copie avec les
+instructions. Ces messages auront pour sujet "MODERATE for ..." .
+
+Pour accepter la contribution, repondez simplement a l'adresse
+specifiee dans le champs "Reply-To:" (ceci doit etre de toutes
+facons pris en charge automatiquement par votre logiciel de
+messagerie) si vous souhaitez accepter le message. Inutile de
+renvoyer le message en question. En fait, le corps du message
+sera ignore du moment que l'adresse est correcte.
+
+Si vous desirez le rejeter, envoyez un message a l'adresse
+precisee de le champ "From" (peut etre habituellement fait a
+l'aide d'un "reply-to-all" suivi d'un effacement de l'adresse
+d'acceptation). Vous pouvez ajouter un commentaire optionnel a
+l'envoyeur entre les deux lignes debutant par '%%%' sans les
+guillemets.
+La encore, votre anonymat sera conserve.
+
+Je traiterai chaque message d'apres la premiere reponse recue.
+Si vous m'envoyez un message pour accepter un message qui a deja
+ete rejete ou l'inverse, je vous tiendrai au courant.
+
+Si je ne recois aucune reponse d'un moderateur apres un certain
+delai (normallement 5 jours), je renverrai le message a
+l'expediteur avec une explication de ce qu'il est arrive.
+L'adminsitrateur peut aussi parametrer la liste de facon a ce que
+ces messages "ignores" soient purement et simplement effaces sans
+avertissement prealable, plutot que retournes a l'envoyeur.
+</#E/>
+
+Absences
+--------
+Si vous etes temporairement a une adresse differente, faites simplement
+suivre tous les messages qui ont un entete "Mailing-List:" correct (ou
+tous les messages qui ont un sujet debutant par
+'MODERATE for <#L#>@<#H#>' ou 'CONFIRM subscribe to <#L#>@<#H#>') a la
+nouvelle addresse. Vous pouvez alors moderer a partir de la nouvelle
+adresse. Alternativement, vous pouvez faire suivre les messages a un
+ami qui moderera pour vous. Pensez a vous mettre d'accord avec le
+proprietaire de la liste avant tout.
+
+Si vous souhaitez que toutes les requetes soient approuvees lorsque
+vous etes absents, configurez votre client de messagerie pour
+repondre automatiquement a tous les messages qui correspondent aux
+criteres ci-dessus.
+
+</#rE/>
+Si vous essayez de faire de l'administration a distance a partir
+d'une adresse qui n'est pas la votre, l'abonne, et non vous, devra
+confirmer. Apres cela, une demande de validation sera envoyee a
+tous les moderateurs.
+Je suis oblige de faire cela car je n'ai aucun moyen de verifier
+que c'est bien vous qui avez emis la requete originale.
+
+A noter que votre requete (et votre adresse) sont transmises a
+l'abonne dans ce cas.
+</#E/>
+
+Bonne chance !
+
+PS: Contactez le proprietaire (<#L#>-owner@<#H#>) si vous avez
+une question ou des difficultes.
+
+</text/mod-reject#E/>
+Je suis desole, votre message n'a pas ete accepte par le
+moderateur. Si le moderateur y a joint des commentaires, ils
+se trouvent ci-dessous.
+</text/mod-request#E/>
+A vous de decider si, en tant que moderateur, vous approuvez
+le message ci-joint pour la liste <#L#>@<#H#>.
+
+Pour accepter le message et l'envoyer immediatement a tous les
+abonnes de la liste, envoyez un message a: 
+
+!A
+
+En regle generale, il suffit de repondre a ce message (Reply) .
+Verifiez simplement que l'adresse debute bien par
+"<#L#>-accept".
+Si cela ne fonctionne pas, copiez simplement l'adresse et
+collez la dans le champs representant le destinataire.
+</#xE/>
+
+Si votre brouteur le permet, vous pouvez aussi cliquer ici :
+       mailto:<#A#>
+</#E/>
+
+Pour rejeter la contribution et le retourner a l'expediteur,
+veuillez envoyer un message a :
+
+!R
+
+En pratique, il est souvent plus simple d'effectuer une reponse
+a tous et de retirer tous les adresses, en dehors de celle qui
+debute par "<#L#>-reject".
+</#xE/>
+
+Si votre brouteur prend en compte les liens hypertexte, vous
+pouvez aussi cliquer ici :
+       mailto:<#R#>
+</#E/>
+
+Vous n'avez pas besoin de recopier le message dans vos reponses pour
+l'accepter ou le rejeter. Si vous souhaitez y ajouter un commentaire
+en cas de rejet, ajoutez-le entre les deux lignes debutant pas '%%%'
+(sans les guillemets).
+
+%%% Debut du commentaire
+%%% Fin du commentaire
+
+Merci de votre aide !
+
+--- Ci-joint, le message envoye:
+
+</text/mod-sub#E/>
+--- Je vous ai inscrit ou desinscrit a la demande d'un moderateur
+de la liste de diffusion <#l#>@<#H#>.
+
+Si ce n'est pas ce que vous souhaitez, envoyez s'il vous plait
+un message au proprietaire <#l>-owner@<#H#> des que possible.
+
+Si vous souhaitez davantage d'informations sur la facon d'acceder
+a l'archive de la liste <#L#>, envoyez simplement un message vide
+a: <#L#>-help@<#H#>.
+
+</text/mod-timeout#E/>
+
+Je suis desole, les moderateurs de la liste n'ont pas reagit a
+votre participation. Donc, je vous la renvoie. Si vous pensez
+qu'il s'agit d'une erreur, faites part de ce message directement
+a un moderateur de la liste <#L#>.
+
+--- Ci-joint, le message que vous avez envoye:
+
+</text/mod-sub-confirm#E/>
+Je vous demande humblement la permission d'ajouter
+
+!A
+
+aux abonnes de la liste de diffusion <#l>. Cette requete
+peut venir de vous-meme ou avoir deja ete supervisee par
+l'abonne potentiel.
+
+Pour confirmer, veuillez envoyer une reponse a cette
+adresse :
+
+!R
+
+Votre logiciel de messagerie devrait avoir une fonction "Reply"
+pour la traiter automatiquement.
+Si cela ne fonctionne pas, effectuez un copier/coller pour y
+ecrire.
+</#xE/>
+
+ou, si votre logiciel de messagerie le permet, cliquer ici :
+       mailto:<#R#>
+</#E/>
+
+Si vous n'approuvez pas cet abonnement, ne tenez pas compte de
+ce message.
+
+Merci de votre aide !
+
+</text/mod-unsub-confirm#E/>
+Je vous demande humblement la permission de retirer
+
+!A
+
+de la liste <#l#>. Si vous acceptez, envoyez un message
+quelconque a cette adresse:
+
+!R
+
+Votre logiciel de messagerie devrait avoir une fonction "Reply"
+pour utiliser automatiquement cette adresse. Sinon, il vous
+reste le copier/coller.
+
+</#xE/>
+
+Le lien suivant pourrait aussi fonctionner :
+       mailto:<#R#>
+</#E/>
+
+En cas de desaccord, ne tenez pas compte de ce message.
+
+Merci de votre aide !
+
+</text/sub-bad#E/>
+
+Oups, ce numero de confirmation semble invalide.
+
+La raison la plus courante des nombres invalides est leur expiration.
+Je dois recevoir la confirmation de chaque requete dans les dix jours.
+De plus, soyez certains que l'integrite du nombre de confirmation
+figurait dans la reponse que vous m'avez envoyee. Certains logiciels de
+messagerie ont la facheuse habitude de tronquer les adresses un peu
+longues.
+
+J'ai mis en place un nouveau nombre de confirmation. Pour confirmer que
+vous souhaitez voir
+
+!A
+
+parmi les abonnes de la liste <#l#>, envoyez s'il vous plait une reponse
+quelconque a cette adresse:
+
+!R
+</#xE/>
+
+ou suivez ce lien :
+       mailto:<#R#>
+</#E/>
+
+Une fois de plus, verifiez soigneusement l'adresse de la reponse pour
+etre sur qu'elle est complete avant de renouveller votre inscription.
+
+Toutes mes excuses pour le derangement.
+
+       Le proprietaire de <#L#> <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+
+Pour confirmer que vous souhaitez voir
+
+!A
+
+parmi les abonnes de la liste <#l#>, veuillez renvoyer un message
+quelconque a l'adresse suivante:
+
+!R
+
+En fait, inutile de la recopier: la fonction "Reply" (repondre a
+l'expediteur) de votre logiciel de messagerie doit s'en charger
+automatiquement. Sinon, il reste le copier/coller.
+</#xE/>
+
+ou suivez ce lien :
+       mailto:<#R#>
+</#E/>
+
+Cette confirmation a deux buts. Tout d'abord, elle verifie que je suis
+capable d'obtenir des messages de vous. Ensuite, elle vous protege au
+cas ou quelqu'un ferait une inscription frauduleuse sous votre nom.
+
+</#qE/>
+Certains logiciels de messageries sont boggues et ne peuvent prendre
+en compte de longues adresses. Si vous ne pouvez pas repondre a cette
+requete, envoyez a la place un message a <<#L#>-request@<#H#>> et
+mettez l'adresse complete presentee ci-dessus dans le champs "Sujet".
+
+</#sE/>
+Cette liste est moderee. Apres avoir recu cette confirmation, la
+requete sera envoyee au(x) moderateur(s) de la liste. Je vous
+previendrai lorsque votre inscription sera effective.
+
+</text/sub-nop#E/>
+Il a ete impossible de satisfaire votre requete : l'adresse
+
+!A
+
+etait deja sur la liste de diffusion <#l#> lorsque
+j'ai recu votre requete, et est toujours abonnee.
+
+</text/sub-ok#E/>
+
+Accuse de reception: j'ai ajoute l'adresse
+
+!A
+
+a la liste de diffusion <#l#>.
+
+Bienvenue dans la liste <#l#>@<#H#> !
+
+Pensez a sauvegarder ce message pour que vous puissiez connaitre
+l'adresse sous laquelle vous vous etes abonnes, au cas ou vous
+souhaiteriez ulterieurement vous desinscrire ou changer votre
+adresse d'abonnement.
+
+Pour vous desinscrire, envoyez un message a :
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+Bonjour ! Je suis le programme ezmlm. Je m'occupe de la liste
+de diffusion <#l#>@<#H#>.
+
+</#x/>
+Je travaille pour mon proprietaire, qui peut etre joint a:
+<#l#>-owner@<#H#>.
+
+</text/unsub-bad#E/>
+Oups, ce numero de confirmation semble invalide.
+
+La raison la plus courante des nombres invalides est leur expiration.
+Je dois recevoir la confirmation de chaque requete dans les dix jours.
+De plus, soyez certains que l'integrite du nombre de confirmation
+figurait dans la reponse que vous m'avez envoyee. Certains logiciels de
+messagerie ont la facheuse habitude de tronquer les adresses un peu
+longues.
+
+J'ai mis en place un nouveau nombre de confirmation. Pour confirmer que
+vous souhaitez voir
+
+!A
+
+desabonne de la liste <#l#> , envoyez une reponse quelconque a :
+
+!R
+</#xE/>
+
+ou suivez ce lien :
+       mailto:<#R#>
+</#E/>
+
+A nouveau, verifiez que l'adresse a laquelle vous ecrivez est complete.
+
+Toutes mes excuses pour le derangement.
+
+       Le proprietaire de <#l#> <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+Pour confirmer que vous souhaitez voir
+
+!A
+
+supprime de la liste de diffusion <#l#> , veuillez envoyer un message
+quelconque a l'adresse suivante:
+
+!R
+
+En fait, inutile de la recopier: la fonction "Reply" (repondre a
+l'expediteur) de votre logiciel de messagerie doit s'en charger
+automatiquement. Un copier/coller vers le champs "To:" est aussi
+une possible.
+</#xE/>
+
+Vous pouvez aussi tout aussi bien suivre ce lien :
+       mailto:<#R#>
+</#E/>
+
+Je n'ai pas verifie que votre adresse fait actuellement partie de la
+liste. Pour verifier a quelle adresse vous vous etes abonnes, regardez
+les messages que vous recevez de cette liste. Chaque message a votre
+adresse cachee dans son chemin de retour; par exemple
+dieu@ciel.paradis.com recoit ses messages avec dans le champ
+"return-path": <<#l#>-return-<nombre>-dieu=ciel.paradis.com@<#H#>.
+
+</#qE/>
+Certains logiciels de messagerie mal concus ne peuvent traiter les
+adresses au dela d'une certaine taille. S'il vous est impossible de
+repondre a ce message, envoyez a la place un message a l'adresse
+<<#L#>-request@<#H#>> et mettez l'adresse complete dans le champs
+"Sujet".
+
+</text/unsub-nop#E/>
+Desole, mais il est impossible de satisfaire votre requete, etant
+donne que l'adresse
+
+!A
+
+ne figurait pas sur la liste de diffusion <#l#> lorsque
+j'ai recu votre requete, et n'est toujours pas abonnee.
+
+Si vous annulez votre abonnement, mais continuez pourtant a recevoir
+des messages de la liste, vous avez probablement effectue votre
+inscription sous une adresse differente de celle que vous utilisez
+actuellement.
+Verifiez dans l'en-tete une ligne de la forme :
+
+'Return-Path: <<#l#>-return-1234-utilisateur=machine.domaine@<#H#>>'
+
+Cela reflete une inscription de l'adresse
+"utilisateur@machine.domaine". Dans ce cas, il est possible de
+se desabonner en ecrivant a :
+'<#l#>-unsubscribe-utilisateur=machine.domaine@<#H#>'.
+
+Reprenez cet exemple apres y avoir mis votre adresse d'inscription.
+
+Si vous avez recu des messages disposant dans leur en-tete d'une
+ligne "List-Unsubscribe:", vous pouvez directement ecrire a cette
+adresse. Inutile de preciser quoi que ce soit d'autre : il est
+deja personnalise.
+
+Dans certains programmes, il est necessaires d'activer des options
+pour faire figurer toutes les lignes de l'en-tete des messages.
+
+Si malgre tout cela ne fonctionne toujours pas, je suis au regret
+de vous informer que je ne peut guere vous aider davantage.
+Il vous reste donc a faire suivre (Forward) un message au
+proprietaire de la liste accompagne d'un petit mot doux.
+Voici son adresse :
+
+    <#l#>-owner@<#H#>
+
+N'etant pas un robot, sa reponse ne sera pas immediate.
+
+</text/unsub-ok#E/>
+Accuse de reception : l'adresse
+
+!A
+
+a ete retiree de la liste de diffusion <#l#>.
+Cette adresse ne figure donc plus parmis les abonnes.
+
+</text/edit-do#nE/>
+Veuillez editer le fichier de texte suivant et l'envoyer a
+cette adresse :
+
+!R
+
+
+Votre logiciel de messagerie doit posseder une fonction de reponse (Reply)
+pour y ecrire automatiquement.
+
+A noter que je peux retirer les quotes ajoutes par votre logiciel dans
+la mesure ou vous n'editez pas les lignes concernees.
+
+Les lignes de reperage sont celles qui debutent par '%%%'. Elles ne doivent
+pas etre modifiees (des caracteres supplementaires ajoutes par votre logiciel
+au debut d'une ligne sont tolerables).
+
+
+</text/edit-list#nE/>
+
+La commande <#L#>-edit.file peut etre utilisee par un administrateur
+distant pour editer les fichiers de textes a la base des reponses de la
+liste <#L#>@<#H#>.
+
+Ci-dessous, une liste des fichiers contenant les textes de reponses
+et une breve description de l'utilisation de leur contenu. Pour editer un
+fichier, envoyez simplement un message a <#L#>-edit.fichier, en substituant
+le nom du fichier a 'fichier'. Les instructions d'edition seront envoyees
+avec le fichier de texte.
+
+Fichier             Utilisation
+
+bottom              pied de page de toutes les reponses: infos generales.
+digest              section 'administrative' des bulletins periodiques.
+faq                 reponses aux questions frequentes au sujet de cette liste.
+get_bad             dans le cas de messages absents des archives.
+help                aide generale (entre 'top' et 'bottom').
+info                informations sur la liste. La premiere ligne en est un resume.
+mod_help            aide specifique aux moderateurs de liste.
+mod_reject          a l'expediteur d'envois refuses.
+mod_request         aux moderateurs avec un envoi.
+mod_sub             a l'abonne apres confirmation d'inscription du moderateur.
+mod_sub_confirm     aux moderateurs pour valider une inscription.
+mod_timeout         a l'expediteur d'un message non valide depuis longtemps.
+mod_unsub_confirm   a un administrateur pour demander une desinscription.
+sub_bad             a l'abonne si la confirmation etait mauvaise.
+sub_confirm         a l'abonne pour confirmer sa requete.
+sub_nop             a l'abonne apres une nouvelle inscription.
+sub_ok              a l'abonne apres un abonnement reussi.
+top                 en-tete de chaque reponse.
+</#tnE/>
+trailer             ajoute a la fin de chaque contribution a la liste.
+</#nE/>
+unsub_bad           a l'abonne si la confirmation de desinscription est fausse.
+unsub_confirm       a l'abonne pour demander confirmation de desinscription.
+unsub_nop           a un non-abonne apres une demande de desabonnement.
+unsub_ok            a un ex-abonne apres une desinscription reussie.
+
+</text/edit-done#nE/>
+Le fichier de texte a ete mis a jour avec succes.
+</text/info#E/>
+Aucune information complementaire n'existe pour cette liste.
+</text/faq#E/>
+FAQ - Reponses aux questions courantes sur la liste <#l#>@<#H#> 
+
+Aucune pour le moment.
+
+
diff --git a/ezmlmrc.id b/ezmlmrc.id
new file mode 100644 (file)
index 0000000..506918f
--- /dev/null
@@ -0,0 +1,1155 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.id,v 1.5 1999/12/22 04:02:15 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc.id translated By Aria Prima Novianto.
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Maaf, program ini menolak posting anda. Hubungi <#L#>-owner@<#H#> jika anda mempunyai pertanyaan tentang hal ini (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Maaf, hanya pelanggan boleh mengirim posting. Jika anda seorang pelanggan, forward mail ini ke <#L#>-owner@<#H#> supaya email baru anda juga dimasukkan (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+Untuk berhenti, e-mail: <#L#>-unsubscribe@<#H#>
+Untuk perintah yang lain, e-mail: <#L#>-help@<#H#>
+</text/bottom#E/>
+
+--- Perintah-perintah untuk list <#l#> ---
+
+Program ini bisa menerima permohonan secara otomatis. Tolong jangan 
+kirim permohonan ke alamat list!  Tetapi, kirim permohonan anda ke 
+alamat berikut:
+
+Untuk daftar dan deskripsi perintah yang ada, kirim e-mail ke:
+   <<#L#>-help@<#H#>>
+
+Untuk menjadi pelanggan list, kirim e-mail ke:
+   <<#L#>-subscribe@<#H#>>
+
+Untuk berhenti sebagai pelanggan, kirim e-mail ke alamat yang ada di
+``List-Unsubscribe'' header dari e-mail yang anda terima dari milis.
+Jika anda belum ganti alamat e-mail sejak menjadi pelanggan, bisa juga
+berhenti dengan mengirim e-mail ke:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+atau untuk digest ke:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#E/>
+Untuk penambahan atau penghapusan satu alamat, konfirmasi akan dikirim
+ke alamat tersebut. Jika anda menerimanya, cukup reply e-mail
+konfirmasi tersebut.
+
+Jika anda ingin berhubungan dengan pemilik list ini, silakan kirim
+e-mail ke:
+
+    <<#L#>-owner@<#H#>>
+
+Tolong FORWARD mail dari milis yang anda ikuti lengkap dengan SEMUA HEADER
+untuk mempermudah kami menolong anda.
+
+--- Berikut adalah kopi dari permohonan yang kami terima.
+
+</text/bounce-bottom#E/>
+
+--- Berikut adalah kopi dari e-mail mental yang kami terima.
+
+</text/bounce-num#E/>
+
+Kami menyimpan daftar posting dari milis <#L#> yang mental dari
+alamat anda.
+
+</#aE/>
+Kopi dari posting-posting ini mungkin masih ada di dalam arsip.
+
+</#aE/>
+Untuk mendapatkan posting 123-145 (maksimum 100 posting untuk setiap 
+permohonan), kirim e-mail kosong ke:
+   <<#L#>-get.123_145@<#H#>>
+
+Untuk mendapatkan daftar subjek dan penulis dari 100 posting terakhir,
+kirim e-mail kosong ke:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Berikut adalah nomor-nomor posting:
+
+</text/dig-bounce-num#E/>
+
+Kami telah menyimpan daftar digest dari milis <#L#>-digest yang
+mental dari alamat anda.  Untuk setiap digest yang tidak anda terima,
+kami telah mencatat nomor dari posting pertama dari digest tersebut.
+
+</#aE/>
+Kami sendiri tidak menyimpan digest-digest tersebut, tapi mungkin anda
+dapat mengambilnya dari arsip milis utamanya.
+
+Untuk mendapatkan posting 123-145 (maksimum 100 posting untuk setiap
+permohonan), kirim e-mail kosong ke:
+   <<#L#>-get.123_145@<#H#>>
+
+Untuk mendapatkan daftar subjek dan penulis dari 100 posting terakhir,
+kirim e-mail kosong ke:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Berikut adalah nomor-nomor posting dari digest:
+
+</text/bounce-probe#E/>
+
+Posting-posting untuk anda dari milis <#l#> nampaknya mental.
+Kami telah mengirimkan sebuah mail peringatan, namun mental juga.
+Kami lampirkan kopi dari mail yang mental tersebut.
+
+Ini adalah mail percobaan untuk mencek apakah alamat e-mail anda dapat
+terjangkau.  Jika mail ini mental, kami akan menghapus alamat anda dari
+milis <#l#>@<#H#> tanpa pemberitahuan lebih lanjut.
+
+Anda dapat mendaftar kembali dengan mengirimkan e-mail kosong ke:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn#E/>
+
+Posting-posting ke anda dari milis <#l#> nampaknya mental.
+Kami lampirkan kopi dari posting mental pertama yang kami
+terima.
+
+Jika posting ini juga mental, kami akan mengirimkan sebuah mail
+percobaan. Jika mail tersebut juga mental, kami akan menghapus alamat
+anda dari milis <#l#> tanpa pemberitahuan lebih lanjut.
+
+</text/digest#dE/>
+Untuk berlangganan digest, e-mail:
+       <#L#>-digest-subscribe@<#H#>
+
+Untuk berhenti berlangganan digest, e-mail:
+       <#L#>-digest-unsubscribe@<#H#>
+
+Untuk mengirim posting ke list, e-mail:
+       <#L#>@<#H#>
+
+</text/get-bad#E/>
+Maaf, posting tersebut tidak ada di arsip kami.
+
+</text/help#E/>
+Ini adalah sebuah panduan umum.  Posting yang kami terima tidak
+terkirim ke salah satu dari alamat-alamat perintah kami.
+
+Berikut ini adalah daftar perintah yang tersedia:
+
+Kirim e-mail ke alamat berikut untuk info dan FAQ list:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#dE/>
+Alamat berikut bisa digunakan untuk list digest:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+Untuk mendapatkan posting 123-145 (maksimum 100), kirim e-mail ke:
+   <<#L#>-get.123_145@<#H#>>
+
+Untuk mendapatkan indeks subjek dan penulis posting 123-456, kirim e-mail ke:
+   <<#L#>-index.123_456@<#H#>>
+
+Untuk menerima semua posting dengan subjek yang sama dengan posting 12345,
+kirim e-mail ke:
+   <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+E-mail tidak perlu kosong, tapi program ini tidak akan membaca isi mail. 
+Hanya alamat tujuan e-mail yang penting.
+
+Anda dapat berlangganan menggunakan alamat e-mail anda yang lain,
+misalnya "john@host.domain", dengan menambahkan '-' dan alamat e-mail
+anda (gunakan '=' sebagai pengganti '@') setelah kata perintah:
+<<#L#>-subscribe-john=host.domain@<#H#>>
+
+Untuk berhenti menerima email di alamat ini, kirim e-mail ke:
+<<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</text/mod-help#E/>
+Terima kasih atas kesediaan anda untuk memoderatori
+milis <#L#>@<#H#>.
+
+Perintah-perintah yang kami punya agak sedikit berbeda dari milis-milis 
+yang lain, tapi kami harap anda dapat menggunakannya dengan mudah.
+
+Berikut adalah beberapa perintah untuk tugas-tugas yang mungkin harus
+anda lakukan sebagai list-owner dan/atau moderator.
+
+Perintah-perintah umum akan ada di bagian akhir mail ini.
+
+Pendaftaran jarak jauh
+----------------------
+Sebagai moderator, anda dapat mendaftarkan dan menghapuskan siapa saja ke
+dalam milis. Untuk mendaftarkan "john@host.domain", taruh '-' setelah kata
+perintahnya, kemudian alamat emailnya dengan menggunakan tanda '=' sebagai
+ganti dari '@'. Contoh, untuk mendaftarkan alamat tersebut, kirim mail ke:
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+Dengan cara yang sama, untuk menghapus alamat tersebut, kirim mail ke:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#dE/>
+Untuk list digest:
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+</#E/>
+Itu saja. Tidak perlu subjek dan isi dari postingnya!
+
+</#rE/>
+Kami akan mengirimkan sebuah permohonan konfirmasi, untuk memastikan
+bahwa anda benar-benar mengirimkan permohonan tersebut. Yang perlu anda
+lakukan adalah mereply ke mail konfirmasi tersebut.
+</#RE/>
+Kami akan mengirimkan sebuah permohonan konfirmasi ke alamat tersebut,
+dalam hal ini <john@host.domain>. Yang bersangkutan tinggal menjawab
+mail konfirmasi tersebut.
+</#E/>
+
+Konfirmasi-konfirmasi tersebut sangat penting untuk menghindari pihak
+ketiga untuk menambah atau menghapus sebuah alamat e-mail ke dalam list.
+
+Kami akan memberitahu pelanggan tersebut ketika status berlangganannya
+telah berubah.
+
+Berlangganan
+------------
+
+Setiap pemakai dapat berlangganan atau berhenti dengan mengirim e-mail ke:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#dE/>
+Untuk list digest:
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+</#E/>
+Si fulan akan menerima sebuah permohonan konfirmasi untuk memastikan
+bahwa dialah pemilik alamat e-mail yang sebenarnya.  Setelah ini telah dapat
+dikonfirmasikan, yang bersangkutan akan dihapus dari list.
+
+</#sE/>
+Karena milis ini telah dimoderatori untuk dapat berlangganan, kami akan
+mengirimkan permohonan konfirmasi kedua kepada moderator. Karena si
+fulan telah berkeinginan untuk masuk ke dalam list, anda sebagai
+moderator dapat percaya bahwa alamat pemakai tersebut adalah benar. Jika
+anda ingin menyetujui permohonan tersebut, reply ke mail CONFIRM kami.
+Jika tidak, anda dapat menghapus mail dari kami ini atau mungkin
+mengkontak calon pelanggan list untuk keterangan lebih lanjut.
+</#SE/>
+Untuk berlangganan adalah sama caranya.
+</#E/>
+
+Si fulan dapat juga mengirim mail ke:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+untuk mendapatkan mailnya terkirim ke "mary@host.domain".  Hanya jika 
+yang bersangkutan menerima mail pada alamat tersebut, dia akan menerima
+permohonan konfirmasi tersebut dan dapat mereplynya.
+
+Alamat dan identitas anda tidak akan tampak pada mail ke si pelanggan,
+kecuali kalau anda mengirim mail secara langsung ke yang bersangkutan.
+
+</#rlE/>
+Untuk mendapatkan daftar pelanggan <#L#>@<#H#>, kirim mail ke:
+   <<#L#>-list@<#H#>>
+
+Untuk mendapatkan daftar log transaksi <#L#>@<#H#>, kirim mail ke:
+   <<#L#>-log@<#H#>>
+
+</#rldE/>
+Untuk pelanggan-pelanggan digest:
+   <<#L#>-digest-list@<#H#>>
+dan:
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+Anda dapat dari jarak jauh mengedit file-file yang dikirim oleh list.
+Untuk mendapatkan daftar file dan petunjuk untuk mengeditnya, e-mail ke:
+   <<#L#>-edit@<#H#>>
+
+</#mE/>
+Posting-posting termoderatori
+-----------------------------
+Ketika posting-posting dimoderatori, kami akan menyimpan posting yang
+terkirim dan mengirimkan kopinya ke anda berikut petunjuk-petunjuk.
+Posting ke anda akan mempunyai subjek "MODERATE for ..."
+
+Untuk menyetujui posting tersebut, reply ke 'Reply-To:' yang telah kami
+persiapkan dengan alamat "accept" yang benar.  Anda tidak perlu untuk
+memasukkan postingnya sendiri.  Bahkan, kami tidak akan menghiraukan
+apapun yang anda kirim ke kami asalkan alamat yang anda kirim sudah benar.
+
+Jika anda ingin menolak, mail ke alamat 'From:' yang telah kami persiapkan
+dengan alamat "reject" yang benar. Ini dapat dilakukan dengan cara
+'reply-to-all', kemudian menghapus alamat selain alamat 'reject' tersebut.
+Anda dapat memberi komentar ke si pengirim diantara dua baris yang dimulai
+dengan tiga buah '%'. Komentar ini hanya akan dikirim ke si fulan yang
+yang postingnya ditolak. Sekali lagi, identitas anda akan dirahasiakan.
+
+Kami akan memproses posting sesuai dengan reply pertama yang kami terima.
+Jika anda mengirim mail untuk menyetujui posting yang sebelumnya sudah
+ditolak, atau sebaliknya, kami akan memberitahu anda.
+
+Jika kami tidak menerima reply dari moderator dalam waktu tertentu
+(biasanya 5 hari), kami akan mengembalikan posting tersebut ke si pengirim
+dengan penjelasan tentang apa yang telah terjadi. Anda sebagai administrator
+dapat juga mengatur listnya untuk menghapus posting-posting tersebut tanpa
+pemberitahuan, juga tanpa perlu dikembalikan ke si pengirim.
+</#E/>
+
+Vakansi
+-------
+Jika sedang berada pada alamat yang berbeda, forward semua posting yang
+mempunyai header 'Mailing-List:' yang benar (atau semuanya yang mempunyai
+subjek dengan awalan 'MODERATE for <#L#>@<#H#>' atau
+'CONFIRM subscribe to <#L#>@<#H#>') ke alamat yang baru. Anda kemudian dapat
+memoderatorinya dari alamat yang baru. Atau anda dapat memforwardkan
+posting-posting tersebut ke teman anda sehingga dia dapat memoderatorinya
+untuk anda. Mohon untuk meminta persetujuan dari pemilik list terlebih dahulu.
+
+Jika anda ingin secara otomatis menyetujui semua permohonan-permohonan
+ketika anda sedang pergi, atur program mail anda untuk mereply otomatis ke
+semua posting yang mempunyai subjek sesuai keterangan di atas.
+
+</#rE/>
+Jika anda mencoba untuk melakukan administrasi jarak jauh dari sebuah
+alamat yang bukan milik anda, si pemohon, bukan anda, akan dimintakan
+konfirmasinya.  Setelah itu, sebuah permohonan konfirmasi akan
+dikirimkan ke semua moderator.  Kami lakukan itu karena kami tidak dapat
+mengetahui apakah permohonan tersebut benar berasal dari anda.
+
+Tolong perhatikan bahwa permohonan asli anda (dan alamat anda) akan
+dikirimkan ke si pemohon dalam hal ini!
+</#E/>
+
+Semoga sukses!
+
+PS: Silakan kontak si pemilik list (<#L#>-owner@<#H#>) jika anda
+mempunyai pertanyaan-pertanyaan atau masalah-masalah.
+
+</text/mod-reject#E/>
+Maaf, posting anda (terlampir) tidak diterima oleh moderator.  Jika ada
+komentar-komentar dari moderator, mereka akan terlihat di bawah.
+</text/mod-request#E/>
+Posting terlampir telah dikirim ke milis <#L#>@<#H#>.  Jika anda
+ingin menyetujuinya untuk disebarluaskan ke semua pelanggan, kirim
+e-mail ke:
+
+!A
+
+Biasanya, yang perlu anda lakukan adalah dengan menekan tombol "reply".
+Anda dapat mencek untuk memastikan bahwa alamatnya dimulai dengan
+"<#L#>-accept".  Jika ini tidak berhasil, kopi alamatnya dan letakkan
+kembali ke dalam kolom "To:" di posting yang baru.
+</#xE/>
+
+Alternatif lainnya, klik di sini:
+       mailto:<#A#>
+</#E/>
+
+Untuk menolak posting dan mengembalikannya ke si pengirim, kirim sebuah
+posting ke:
+
+!R
+
+Biasanya, cara termudah adalah dengan menekan tombol "reply-to-all", dan
+kemudian menghapus semua alamat kecuali yang dimulai dengan
+"<#L#>-reject".
+</#xE/>
+
+Alternatif lainnya, klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Anda tidak perlu untuk mengkopi isi dari posting ke dalam respon
+persetujuan atau penolakan anda.  Jika anda ingin membuat komentar
+kepada si pengirim yang tertolak postingnya, masukkan komentarnya di 
+antara dua garis penanda yang dimulai dengan tiga tanda persen ('%'):
+
+%%% Start comment
+%%% End comment
+
+Terima kasih atas bantuan anda!
+
+--- Berikut adalah posting yang terkirim.
+
+</text/mod-sub#E/>
+--- Kami telah mendaftarkan atau menghapus e-mail anda sesuai permohonan
+dari moderator milis <#l#>@<#H#>.
+
+Jika hal ini tidak sesuai dengan yang anda harapkan, silakan kirim
+sebuah mail komplain atau komentar-komentar ke pemilik list
+(<#l#>-owner@<#H#>) secepatnya.
+
+</text/mod-timeout#E/>
+Maaf, moderator-moderator dari milis <#L#> sedang sibuk saat ini.  
+Oleh karenanya, kami mengirimkan posting ini kembali ke anda.  Jika anda 
+menyadari bahwa ini adalah sebuah kesalahan, tolong kirimkan ulang posting 
+anda atau kontak seorang moderator list secara langsung.
+
+--- Berikut adalah posting yang anda kirim.
+
+</text/mod-sub-confirm#E/>
+Dengan hormat kami mengharapkan ijin anda untuk menambahkan 
+
+!A
+
+ke dalam daftar pelanggan-pelanggan milis <#l#>.  Permohonan ini datangnya 
+adalah dari anda sendiri, atau dari si calon pelanggan yang sudah 
+memverifikasikannya.
+
+Untuk mengkonfirmasikannya, kirim reply kosong ke alamat berikut:
+
+!R
+
+Biasanya, yang perlu anda lakukan adalah dengan menekan tombol "reply".
+Jika ini tidak berhasil, kopi alamatnya dan letakkan kembali ke dalam 
+kolom "To:" di posting yang baru.
+</#xE/>
+
+atau klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Jika anda tidak menyetujuinya, jangan hiraukan posting tersebut.
+
+Terima kasih atas bantuan anda!
+
+</text/mod-unsub-confirm#E/>
+Sebuah permohonan telah dibuat untuk menghapus
+
+!A
+
+dari milis <#l#>. Jika anda setuju, kirim mail kosong
+ke alamat ini:
+
+!R
+
+Biasanya, yang perlu anda lakukan adalah dengan menekan tombol "reply".
+Jika ini tidak berhasil, kopi alamatnya dan letakkan kembali ke dalam
+kolom "To:" di posting yang baru.
+</#xE/>
+
+atau klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Jika anda tidak menyetujuinya, jangan hiraukan posting tersebut.
+
+Terima kasih atas bantuan anda!
+
+</text/sub-bad#E/>
+Oops, nomor konfirmasi tersebut kelihatannya tidak valid.
+
+Sebab yang paling umum untuk nomor yang invalid adalah kadaluarsa. Kami
+harus menerima konfirmasi dari setiap permohonan dalam jangka waktu 10 hari.
+Juga, pastikan bahwa seluruh nomor konfirmasi masuk dalam reply yang anda
+kirimkan ke kami. Beberapa program e-mail mempunyai kebiasaan untuk
+memotong sebagian dari alamat reply, yang kadang sangat panjang.
+
+Kami telah membuat sebuah nomor konfirmasi baru. Kalau anda ingin
+
+!A
+
+masuk ke dalam milis <#l#>,
+kirim mail kosong ke alamat:
+
+!R
+</#xE/>
+
+atau klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Sekali lagi, cek alamat replynya secara cermat untuk memastikan bahwa
+semuanya telah masuk sebelum anda mengirim konfirmasi anda.
+
+Maaf atas ketidaknyamanan ini.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+Untuk memastikan bahwa anda ingin
+
+!A
+
+masuk ke dalam milis <#l#>, kirim mail kosong
+ke alamat berikut:
+
+!R
+
+Biasanya, yang perlu anda lakukan adalah dengan menekan tombol "reply".
+Jika ini tidak berhasil, kopi alamatnya dan letakkan kembali ke dalam
+kolom "To:" di posting yang baru.
+</#xE/>
+
+atau klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Mail konfirmasi ini mempunyai dua tujuan.  Pertama, untuk memastikan
+bahwa kami dapat menghubungi anda.  Kedua, untuk mencegah orang lain
+memalsukan permohonan pendaftaran atas nama anda.
+
+</#qE/>
+Beberapa program mail tidak berfungsi sempurna dan tidak dapat menangani
+alamat-alamat panjang.  Jika anda tidak dapat mereply ke permohonan ini,
+kirimkan sebuah mail ke <<#L#>-request@<#H#>> dan taruh
+alamat-alamat yang nampak di atas ke dalam baris "Subject:". 
+
+</#sE/>
+List ini dimoderatori.  Sekali anda telah mengirim konfirmasi ini,
+permohonannya akan dikirimkan ke moderator dari list ini.  Kami akan
+mengabari anda jika e-mail anda telah diaktifkan.
+
+</text/sub-nop#E/>
+Untuk diketahui: Alamat berikut
+
+!A
+
+sudah terdaftar pada milis <#l#> ketika kami menerima permohonan anda,
+dan masih berstatus sebagai pelanggan.
+
+</text/sub-ok#E/>
+Untuk diketahui: Kami telah menambahkan alamat
+
+!A
+
+ke milis <#l#>.
+
+Selamat datang di <#l#>@<#H#>!
+
+Tolong simpan mail ini sehingga anda dapat tahu dengan alamat apa anda
+terdaftar pada milis, jika sewaktu-waktu anda ingin berhenti atau
+mengganti alamat berlangganan anda di kemudian hari.
+
+Untuk berhenti, kirim mail ke:
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+Hai! Ini adalah program ezmlm.
+Kami yang mengurus milis <#l#>@<#H#>.
+
+</#x/>
+Kami bekerja untuk pemilik kami, yang dapat dijangkau pada alamat e-mail 
+<#l#>-owner@<#H#>.
+
+</text/unsub-bad#E/>
+Oops, nomor konfirmasi tersebut kelihatannya tidak valid.
+
+Sebab yang paling umum untuk nomor yang invalid adalah kadaluarsa. Kami
+harus menerima konfirmasi dari setiap permohonan dalam jangka waktu 10 hari.
+Juga, pastikan bahwa seluruh nomor konfirmasi masuk dalam reply yang anda
+kirimkan ke kami. Beberapa program e-mail mempunyai kebiasaan untuk
+memotong sebagian dari alamat reply, yang kadang sangat panjang.
+
+Kami telah membuat sebuah nomor konfirmasi baru. Kalau anda ingin
+
+!A
+
+dihapus dari milis <#l#>,
+kirim reply kosong ke alamat:
+
+!R
+</#xE/>
+
+atau klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Sekali lagi, cek alamat replynya secara cermat untuk memastikan bahwa
+semuanya telah masuk sebelum anda mengirim konfirmasi anda.
+
+Maaf atas ketidaknyamanan ini.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+Untuk memastikan bahwa anda ingin
+
+!A
+
+dihapus dari milis <#l#>,
+kirim mail kosong ke alamat:
+
+!R
+
+Biasanya, yang perlu anda lakukan adalah dengan menekan tombol "reply".
+Jika ini tidak berhasil, kopi alamatnya dan letakkan kembali ke dalam
+kolom "To:" di posting yang baru.
+</#xE/>
+
+atau klik di sini:
+       mailto:<#R#>
+</#E/>
+
+Kami belum mencek apakah alamat anda sudah ada di dalam milis. Untuk
+melihat alamat apa yang anda pakai untuk mendaftar, lihat mail yang anda
+terima dari milis. Setiap mail mempunyai alamat anda yang tersembunyi di
+dalam return-pathnya; sebagai contoh, mary@xdd.ff.com menerima mail
+dengan return-path: <<#l#>-return-<number>-mary=xdd.ff.com@<#H#>.
+
+</#qE/>
+Beberapa program mail tidak berfungsi sempurna dan tidak dapat menangani
+alamat-alamat panjang. Jika anda tidak dapat mereply ke permohonan ini,
+kirimkan sebuah mail ke <<#L#>-request@<#H#>> dan taruh alamat-alamat
+yang nampak di atas ke dalam baris "Subject:".
+
+</text/unsub-nop#E/>
+Permohonan anda tidak dapat kami penuhi, disebabkan
+alamat berikut
+
+!A
+
+tidak terdaftar dalam milis <#l#> ketika kami menerima permohonan anda
+dan bukan pelanggan dari list ini.
+
+Jika anda berhenti, tapi masih menerima mail, anda terdaftar dengan
+alamat lain dari yang anda pakai sekarang.  Tolong lihat header surat
+untuk:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+Ini menunjukkan bahwa alamat pelanggan adalah ``user@host.dom''.
+Alamat berhenti untuk pelanggan ini adalah:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+
+Kirim mail ke alamat tersebut, disesuaikan dengan alamat sebenarnya.
+
+Kalau mail dari milis punya header ``List-Unsubscribe:'', anda dapat
+mengirim mail ke alamat yang ada di dalam header tersebut. Header
+tersebut berisi alamat khusus untuk anda gunakan untuk berhenti.
+
+Untuk beberapa program mail, anda perlu untuk membuat headersnya
+terlihat untuk dapat melihat return path:
+
+Untuk Eudora 4.0, klik pada tombol "Blah blah ...".
+Untuk PMMail, klik pada "Window->Show entire message/header".
+
+Jika itu masih belum berhasil, kami mohon maaf kami tidak dapat membantu
+anda. Tolong FORWARD mail dari list tersebut bersama-sama dengan sedikit
+catatan tentang apa yang ingin anda lakukan dan daftar kemungkinan
+alamat-alamat e-mail yang anda daftarkan pada milis, ke pemilik list:
+
+    <#l#>-owner@<#H#>
+
+yang kemudian akan menanganinya.  Pemilik list ini agak sedikit lambat
+dari kami, harap sabar.
+
+</text/unsub-ok#E/>
+Untuk diketahui: Kami telah menghapus alamat
+
+!A
+
+dari milis <#l#>. Alamat tersebut sudah tidak ada lagi di dalam
+daftar pelanggan.
+
+</text/edit-do#nE/>
+Silakan edit file teks berikut dan kirimkan ke alamat ini:
+
+!R
+
+Program mail anda punya Reply yang menggunakan alamat ini secara otomatis.
+
+Kami dapat menghapus tanda-tanda kutip yang ditambahkan pada teks oleh
+program mail anda, jika anda tidak mengedit baris-baris penanda tersebut.
+
+Baris-baris penanda tersebut dimulai dengan '%%%'.  Jangan merubahnya
+(karakter-karakter ekstra yang ditambahkan oleh program mail anda pada
+baris pertama dapat diterima).
+
+
+</text/edit-list#nE/>
+Perintah <#L#>-edit.file dapat digunakan oleh administrator dari jarak
+jauh untuk mengedit file-file teks untuk kemudian membuat kumpulan dari
+tanggapan-tanggapan dari milis <#L#>@<#H#>.
+
+Berikut adalah daftar dari nama-nama file tanggapan beserta keterangan
+singkat tentang kapan mereka digunakan. Untuk mengedit suatu file,
+kirim mail ke <#L#>-edit.file, dengan menggantikan nama file untuk 'file'.
+Instruksi-instruksi pengeditan juga dikirimkan beserta file teksnya.
+
+File                Kegunaan
+
+bottom              bagian bawah dari semua tanggapan.  Informasi secara umum.
+digest              'administrivia' bagian dari digests.
+faq                 frequently asked questions khusus untuk list tersebut.
+get_bad             pengganti posting-posting yang tidak ditemukan pada arsip.
+help                panduan umum (antara 'top' dan 'bottom').
+info                info list. Baris pertama sangat berarti.
+mod_help            panduan khusus untuk moderator list. 
+mod_reject          untuk pengirim yang postingnya tertolak.
+mod_request         untuk moderator dengan postingnya.
+mod_sub             untuk pelanggan setelah moderator menyetujui.
+mod_sub_confirm     untuk moderator untuk permohonan berlangganan.
+mod_timeout         untuk pengirim yang postingnya kadaluarsa.
+mod_unsub_confirm   untuk admin untuk permohonan berhenti.
+sub_bad             untuk pelanggan jika konfirmasi berlangganan tidak sukses.
+sub_confirm         untuk pelanggan untuk permohonan berlangganan.
+sub_nop             untuk pelanggan setelah mendaftar ulang.
+sub_ok              untuk pelanggan setelah permohonannya sukses.
+top                 bagian atas dari semua tanggapan.
+</#tnE/>
+trailer             ditambahkan ke semua posting yang terkirim.
+</#nE/>
+unsub_bad           untuk pelanggan jika konfirmasi berhenti tidak sukses.
+unsub_confirm       untuk pelanggan untuk permohonan berhenti.
+unsub_nop           untuk bukan-pelanggan setelah berhenti.
+unsub_ok            untuk eks-pelanggan setelah suksesnya permohonan berhenti.
+
+</text/edit-done#nE/>
+Teks filenya sudah diupdate dengan sukses.
+</text/info#E/>
+Tidak ada informasi yang telah disediakan untuk list ini.
+</text/faq#E/>
+FAQ - Frequently asked questions untuk milis <#l#>@<#H#>.
+
+Belum tersedia sampai saat ini.
+
+
diff --git a/ezmlmrc.it b/ezmlmrc.it
new file mode 100644 (file)
index 0000000..3db0eec
--- /dev/null
@@ -0,0 +1,1155 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.it,v 1.3 1999/12/22 04:02:15 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# translation by Roberto De Carlo rodeca@tiscalinet.it
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+Per cancellarsi, scrivi a: <#L#>-unsubscribe@<#H#>
+Se vuoi conoscere altri comandi, scrivi a: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- Comandi di gestione per la mailing list <#l#>  ---
+
+Posso elaborare comandi di gestione autonomamente. Per 
+favore non inviarli direttamente all'indirizzo della lista!
+Invece spedisci il tuo messaggio al giusto indirizzo per i comandi:
+
+Per ricevere aiuto e una descrizione dei comandi, scrivi a:
+   <<#L#>-help@<#H#>>
+
+Per iscriversi alla lista, spedisci un messaggio a:
+   <<#L#>-subscribe@<#H#>>
+
+Per cancellare il tuo indirizzo dalla lista, ti basta scrivere 
+all'indirizzo presente nell'intestazione ``List-Unsubscribe'' di
+qualsiasi messaggio della lista. Se non hai cambiato indirizzo da
+quando ti sei iscritto, puoi anche mandare un messaggio a:
+    <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+o per le raccolte a:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#E/>
+
+Per l'aggiunta o la rimozione degli indirizzi di email, ti spedirò
+una conferma. Quando la riceverai, ti basta semplicemente rispondere
+per completare l'operazione.
+
+Se desideri contattare il responsabile in carne ed ossa di questa
+lista perfavore spedisci un messaggio a:
+
+    <<#L#>-owner@<#H#>>
+
+Perfavore inccludi un messaggio della lista con tutte le intestazioni
+complete per farti aiutare più facilmente.
+
+--- Allego una copia della richiesta che ho ricevuto.
+
+</text/bounce-bottom#E/>
+
+--- Allego una copia del messaggio di ritorno che ho ricevuto.
+
+</text/bounce-num#E/>
+
+Ho tenuto una copia dei messaggi della lista <#L#> che sono tornati
+indietro dal tuo indirizzo.
+
+</#aE/>
+Le copie di questi messaggi potrebbero essere nell'archivio.
+
+</#aE/>
+
+Per ricevere l'insieme dei messaggi 123-145 (al massimo
+100 per richiesta) dall'archivio, scrivi un messaggio vuoto a:   
+   <<#L#>-get.123_145@<#H#>>
+
+
+Per ricevere l'elenco dei soggetti e degli autori degli ultimi 100
+messaggi scrivi a:
+
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Ecco i numeri dei messaggi:
+
+</text/dig-bounce-num#E/>
+
+Ho un elenco delle raccolte dalla mailing list <#L#>-digest che sono
+tornate indietro dal tuo indirizzo. Per ogni raccolta che ti sei
+perso ho annotato il numero del primo messaggio della raccolta. 
+
+</#aE/>
+Io non archivio le raccolte stesse, ma sei in grado di prendere i
+messaggi dall'archivio  principale della mailing list.
+
+Per ricevere i messaggi dal numero 123 al numero 145 (massimo 100 per ogni
+richiesta),  scrivi un messaggio vuoto a:
+
+   <<#L#>-get.123_145@<#H#>>
+
+Per ricevere l'elenco dei soggetti e degli autori degli ultimi 100 messaggi
+scrivi a:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Ecco i numeri delle raccolte:
+
+</text/bounce-probe#E/>
+
+I messaggi a te inviati dalla mailing list <#l#> sembrano tornare indietro.
+Ti ho spedito un messaggio di avvertimento, ma anche questo è tornato
+indietro.
+ Allego una copia del primo messaggio che mi è tornato indietro.
+
+Questa è una prova per vedere se il tuo indirizzo è raggiungibile.
+Se anche questo messaggio torna indietro toglierò il tuo indirizzo dalla
+mailing list <#l#>@<#H#> senza ulteriori avvertimenti.
+
+Ti potrai reiscrivere spedendo un messaggio a questo indirizzo:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn#E/>
+
+I messaggi a te inviati dalla mailing list <#l#> sembrano tornare indietro.
+Ti ho spedito un messaggio di avvertimento, ma anche questo è tornato
+indietro.
+ Allego una copia del primo messaggio che mi è tornato indietro.
+
+Se anche questo messaggio torna indietro ti spedirò un messaggio di
+prova. Se ritorna anche questo messaggio toglierò il tuo indirizzo
+dalla mailing list <#l#> senza ulteriori avvertimenti. 
+
+</text/digest#dE/>
+Per iscriversi alla lista delle raccolte scrivi a:
+       <#L#>-digest-subscribe@<#H#>
+
+Per rimuoverti dalla lista delle raccolte scrivi a:
+       <#L#>-digest-unsubscribe@<#H#>
+
+Per spedire un messaggio alla lista scrivi a:
+       <#L#>@<#H#>
+
+</text/get-bad#E/>
+Spiacente, questo messaggio non è nell'archivio.
+
+</text/help#E/>
+Questo è un messaggio di aiuto generico. Il messaggio che ho ricevuto
+non è stato spedito ad alcuno dei miei indirizzi per i comandi.
+
+Ecco un elenco degli indirizzi per i comandi consentiti:
+
+Scrivi una email ai seguenti indirizzi per le informazioni o le FAQ
+della lista:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#dE/>
+Indirizzi analoghi esistono per la lista delle raccolte:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+Per ricevere i messaggi dal numero 123 al numero 145 (massimo 100 per ogni
+richiesta), scrivi a:
+   <<#L#>-get.123_145@<#H#>>
+
+Per ricevere l'indice dei messaggi 123-456 con l'oggetto e l'autore, scrivi a:
+   <<#L#>-index.123_456@<#H#>>
+
+Per ricevere tutti i messaggi con lo stesso oggetto del messaggio
+12345, spedisci un messaggio vuoto a:
+   <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+I messaggi in realtà non é necessario che siano vuoti, ma ignorerò
+il loro contenuto. E' importante solo l'INDIRIZZO dal quale li spedisci.
+
+Puoi iscriverti anche da un altro indirizzo,per esempio se vuoi iscrivere
+"john@host.domain", basta solo aggiungere un trattino e il tuo indirizzo (con
+'=' al posto di '@') dopo i comandi: <<#L#>-subscribe-john=host.domain@<#H#>>
+
+Per cancellare l'iscrizione da questo indirizzo, scrivi a:
+<<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</text/mod-help#E/>
+Grazie per aver accettato di moderare la mailing list <#L#>@<#H#>.
+
+I miei comandi sono leggermente differenti da quelli delle altre
+mailing list, ma credo che li troverai intuitivi e facili da usare.
+
+Ecco alcune istruzioni per i compiti che dovrai assolvere come 
+amministratore della lista e/o moderatore.
+
+Sottoscrizione remota
+-------------------
+Come moderatore, puoi iscrivere e cancellare qualsiasi indirizzo alla
+mailing list. Per iscrivere "john@host.domain" , basta solo
+aggiungere un trattino  dopo il comando, quindi il suo indirizzo con
+'=' invece di '@'. Per esempio, per iscrivere questo indirizzo scrivi
+a:    <<#L#>-subscribe-john=host.domain@<#H#>>
+
+Analogamente puoi rimuovere l'indirizzo scrivendo a:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#dE/>
+Per la lista delle raccolte:
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+</#E/>
+Questo è tutto. Non sono necessari comandi nell' oggetto o nel corpo del 
+messaggio!
+
+</#rE/>
+Ti spedirò una richiesta di conferma, per essere sicuro che tu abbia
+realmente spedito la richiesta. Rispondi semplicemente al messaggio e i 
+tuoi desideri saranno eseguiti.
+</#RE/>
+Spedirò una richiesta di conferma all'indirizzo dell'utente, in questo caso
+a <john@host.domain>. Tutto ciò che l'utente dovrà fare sarà quello di 
+rispondere alla richiesta di conferma.
+</#E/>
+
+I messaggi di conferma sono necessari per rendere estremamente difficile a
+qualcun altro di aggiungere o togliere un indirizzo dalla lista.
+
+Avvertirò l'utente quando cambia la sua iscrizione.
+
+Iscrizione
+-----------
+
+Qualsiasi utente può iscriversi o rimuoversi scrivendo a:
+
+    <<#L#>-subscribe@<#H#>>
+    <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+Per la lista delle raccolte:
+
+    <<#L#>-digest-subscribe@<#H#>>
+    <<#L#>-digest-unsubscribe@<#H#>>
+
+</#E/>
+L'utente riceverà una richiesta di conferma per essere sicuri che
+controlli veramente l'indirizzo di iscrizione. Una volta fatta la
+verifica l'utente viene rimosso.
+
+</#sE/>
+Visto che questa lista è moderata per le iscrizioni, spedirò una seconda
+richiesta di conferma al moderatore. Dato che l'utente ha già confermato 
+il desiderio di essere nella lista, come moderatore puoi essere certo che 
+l'indirizzo sia esistente. Se vuoi approvare la richiesta dell'utente,
+rispondi semplicemente al mio messaggio di CONFERMA. In caso contrario puoi
+cancellare il mio messaggio o il potenziale iscrivente per maggiori 
+informazioni.
+</#SE/>
+Le iscrizioni funzionano allo stesso modo.
+</#E/>
+
+L'utente può anche utilizzare:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+per ricevere le e-mail a: "mary@host.domain". Solo se riceve effettivamente
+le e-mail a questo indirizzo potrà ricevere il messaggio di conferma ed essere 
+in grado di rispondergli.
+
+Il tuo indirizzo e la tua identità non saranno resi noti agli iscritti, a meno
+che tu non decida di scrivergli direttamente.
+
+</#rlE/>
+Per ricevere l'elenco degli iscritti a: <#L#>@<#H#> invia un messaggio a:
+   <<#L#>-list@<#H#>>
+
+Per ricevere il log delle transazioni per <#L#>@<#H#> scrivi a:
+   <<#L#>-log@<#H#>>
+</#rldE/>
+Per gli iscritti alle raccolte:
+   <<#L#>-digest-list@<#H#>>
+e:
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+Puoi modificare a distanza i file di testo che compongono le risposte che 
+la lista spedisce. Per ricevere un elenco dei file e delle istruzioni per
+modificarli, scrivi a:
+   <<#L#>-edit@<#H#>>
+
+
+</#mE/>
+Moderazione dei messaggi
+------------------------
+Quando i messaggi sono moderati, archivierò il messaggio spedito e te ne
+invierò una copia con le istruzioni. Il messaggio che riceverai avrà
+"MODERATE for ..." come oggetto.
+
+Per accettare il messaggio, basta solo rispondere all'indirizzo 'Rispondi a:' 
+che ho già configurato con il corretto indirizzo di accettazione. Non è 
+necessario allegare il messaggio stesso. Difatti ignorerò tutto ciò che mi
+invierai se l'indirizzo dal quale scrivi è corretto.
+.
+Se vuoi rifutarlo, scrivi una e-mail allindirizzo 'Da:', che avrò già 
+configurato con il corretto indirizzo di "rifuto". Questo di solito può essere
+fatto con il 'Rispondi a tutti:' e cancellando tutti i destinatari escluso 
+quello di "rifiuto". Puoi aggiungere un commento al mittente tra due linee che 
+iniziano con tre '%'. Io spedirò solo questo commento con il messaggio 
+rifiutato. E ancora, non rivelerò la tua identità.
+
+Processerò il messaggio secondo la prima risposta che mi arriverà.
+Se mi spedisci una richiesta di accettare un messaggio che è stato già
+rifiutato o vice versa, te lo farò sapere.
+
+Se non ricevo risposte dal moderatore entro un certo numero di giorni
+(di solito 5), spedirò un messaggio al mittente con la spiegazione di ciò
+che è successo. Il tuo amministratore può anche configurare la lista in modo
+tale che questi messaggi "ignorati" vengano cancellati senza avviso, invece
+che rispediti al mittente. 
+</#E/>
+
+Assenze
+---------
+Se hai momentaneamente un indirizzo diverso, ti basta forwardare tutti i
+messaggi che hanno l'intestazione 'Mailing-List:' (o tutti i messaggi che
+iniziano con 'MODERATE for <#L#>@<#H#>' o con 'CONFIRM subscribe to
+<#L#>@<#H#>') al nuovo indirizzo.
+Puoi perciò moderare la mailing-list dal nuovo indirizzo. In alternativa, puoi
+forwardare i messaggi a un amico in modo tale che li possa moderare lui per te.
+Però assicurati di essere d'accordo con il proprietario della mailing list.
+
+Se vuoi approvare in automatico tutte le richieste mentre sei via, allora
+configura il tuo programma di posta in modo tale da risondere in automatico ai
+messaggi che hanno gli oggetti conformi ai criteri sopracitati.
+
+</#rE/>
+Se vuoi amministrare la lista da un indirizzo che non è il tuo ti verrà
+chiesta una conferma. Fatto ciò, verrà inviata una richiesta di conferma a
+tutti i moderatori. Faccio così perchè non c'è modo di conoscere che sia stato
+davvero tu a mandarmi la richiesta originale. 
+
+Ricorda che la tua richiesta originale ( e il tuo indirizzo) sono spediti a
+tutti gli iscritti in questo caso! 
+</#E/>
+
+Buona fortuna!
+
+PS: Per favore contatta l'amministratore della lista (<#L#>-owner@<#H#>) se
+hai domande o problemi.
+</text/mod-reject#E/>
+Spiacente, ma il tuo messaggio (allegato) non è stato accettato dal moderatore.
+Se il moderatore ha scritto qualche commento, essi sono riportati qui sotto.
+</text/mod-request#E/>
+Il messaggio allegato è stato inviato alla mailing list <#L#>@<#H#>
+Se vuoi approvarlo affinchè venga spedito a tutti gli iscritti, scrivi a:
+
+
+!A
+
+In genere ciò accade non appena schiacci il bottone "Rispondi". Puoi
+controllare l'indirizzo per assicurarti che inizi con "<#L#>-accept".
+Se non funziona, allora ti basta copiare l'indirizzo e incollarlo nel campo
+"A:" di un nuovo messaggio.
+</#xE/>
+
+Oppure clicca qui:
+       mailto:<#A#>
+</#E/>
+
+Per rifiutare il messaggio e fare in modo che esso venga rispedito al
+mittente, per favore spedisci un messaggio a:
+
+!R
+
+Di solito è più facile cliccare sul bottone "Rispondi a tutti:", e poi
+togliere tutti gli indirizzi tranne quello che inizia con "<#L#>-reject".
+</#xE/>
+
+Oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Non è necessario copiare il messaggio nella risposta per accettarlo o
+rifiutarlo. Se desideri spedire un commento al mittente del messaggio
+rifiutato, per favore includilo tra due segnalatori di linea che iniziano
+con tre segni di percentuale ('%'):
+
+%%% Start comment
+%%% End comment
+
+Grazie per il tuo aiuto!
+
+--- In allegato, per favore verifica il messaggio inviato.
+
+</text/mod-sub#E/>
+--- Ti ho iscritto o cancellato dietro richiesta del moderatore della mailing
+list  <#l#>@<#H#>.
+
+Se non approvi quello che è stato fatto, per favore invia una lamentela o
+altri commenti al responsabile della lista (<#l#>-owner@<#H#>) non appena
+possibile.
+
+</text/mod-timeout#E/>
+Sono spiacente, ma il moderatore della lista <#L#> non ha ancora vagliato il
+tuo messaggio, perciò te lo rimando indietro. Se credi che ci sia un errore,
+per favore rispediscilo o contatta il moderatore della lista direttamente.
+
+--- In allegato, per favore, verifica il messaggio che hai spedito.
+
+</text/mod-sub-confirm#E/>
+Chiedo rispettosamente il tuo permesso di aggiungere
+
+!A
+
+agli iscritti della mailing list <#l#>. Questa richiesta o viene da
+te, o è già stata verificata dal potenziale iscritto. 
+
+Per confermare spedisci una risposta vuota a questo indirizzo:
+
+!R
+
+
+In genere ciò accade non appena schiacci il bottone "Rispondi". 
+Se non funziona, allora ti basta copiare l'indirizzo e incollarlo nel campo
+"A:" di un nuovo messaggio.
+</#xE/>
+
+oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Se non sei d'accordo ti basta ignorare questo messaggio.
+
+Grazie per il tuo aiuto!
+
+</text/mod-unsub-confirm#E/>
+E' stato richiesto di cancellare
+
+!A
+
+dalla miling list <#l#>. Se sei d'accordo, per favore spedisci una 
+risposta vuota a questo indirizzo: 
+
+!R
+
+In genere ciò accade non appena schiacci il bottone "Rispondi". 
+Se non funziona, allora ti basta copiare l'indirizzo e incollarlo nel campo
+"A:" di un nuovo messaggio.
+</#xE/>
+
+oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Se non sei d'accordo ti basta ignorare questo messaggio.
+
+Grazie per il tuo aiuto!
+
+</text/sub-bad#E/>
+Spiacente, il tuo numero di conferma sembra essere non valido.
+
+La ragione più comune per i numeri non validi è la scadenza. Devo 
+ricevere conferma di ogni richiesta entro 10 giorni. Oppure
+assicurati che era incluso tutto il numero di conferma nella
+risposta che mi hai inviato.  Alcuni programmi di posta elettronica
+hanno l'abitudine di eliminare  qualcosa nell'indirizzo di risposta
+che può essere abbastanza lungo.
+
+Ho predisposto un altro numero di conferma. Per confermare che vuoi
+che
+
+!A
+
+venga aggiunto alla mailing list <#l#>, per favore spedisci una
+risposta vuota a questo indirizzo:
+
+!R
+</#xE/>
+
+oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Di nuovo, controlla l'indirizzo di risposta per assicurarti che sia tutto
+incluso prima di confermare la tua iscrizione.
+
+Spiacente per l'inconveniente.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+Per confermare che vuoi che
+
+!A
+
+venga iscritto alla mailing list <#l#>, per favore invia una risposta vuota a
+questo indirizzo:
+
+!R
+
+In genere ciò accade non appena schiacci il bottone "Rispondi". 
+Se non funziona, allora ti basta copiare l'indirizzo e incollarlo nel campo
+"A:" di un nuovo messaggio.
+</#xE/>
+
+oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Questa conferma è necessaria per due motivi. Il primo è che verifica che io
+sia in grado  di ricevere la posta da te. Il secondo motivo è che ti protegge 
+nel caso qualcuno stia tentando di fare un'iscrizione a nome tuo.
+
+</#qE/>
+Alcuni programmi di posta sono difettosi e non sono in grado di gestire
+indirizzi molto lunghi. Se non riesci a rispondere a questa richiesta
+spedisci un messaggio a <<#L#>-request@<#H#>> e metti l'indirizzo
+intero indicato sopra nel campo "Oggetto:".
+
+</#sE/>
+Questa mailing list è moderata. Una volta spedita questa conferma la richiesta
+verrà spedita al/i moderatore/i della lista. Ti farò sapere quando la tua
+iscrizione sarà accettata.
+
+</text/sub-nop#E/>
+Ho delle difficoltà a soddisfare la tua richiesta: l'indirizzo
+
+!A
+
+è già iscritto nella mailing list <#l#> quando ho ricevuto la tua
+richiesta e rimane iscritto.
+
+</text/sub-ok#E/>
+Conferma: ho aggiunto l'indirizzo
+
+!A
+
+alla mailing list <#l#> .
+
+Benvenuto su <#l#>@<#H#>!
+
+Per favore salva questo messaggio in modo tale da sapere con quale
+indirizzo ti sei iscritto nel caso tu voglia successivamente cancellarti o
+modificare il tuo indirizzo di iscrizione.
+
+Per cancellare l'scrizione scrivi a:
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+Ciao! Sono il programma ezmlm. Mi occupo della 
+mailing list <#l#>@<#H#> .
+
+Sto lavorando per il mio responsabile, che può essere contattato presso
+at <#l#>-owner@<#H#>.
+
+</text/unsub-bad#E/>
+Spiacente, ma questo numero di conferma sembra essere non
+valido.
+
+La ragione più comune per i numeri non validi è la scadenza. Devo ricevere
+conferma di ogni richiesta entro 10 giorni. Oppure assicurati che era incluso
+tutto il numero di conferma nella risposta che mi hai inviato. 
+Alcuni programmi di posta elettronica hanno l'abitudine di eliminare qualcosa
+nell'indirizzo di risposta che può essere abbastanza lungo.
+
+Ho predisposto un altro numero di conferma. Per confermare che vuoi che
+
+!A
+
+venga rimosso dalla mailing list <#l#>, per favore manda una risposta vuota
+all'indirizzo:
+
+!R
+</#xE/>
+
+oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Di nuovo, controlla l'indirizzo di risposta per assicurarti che sia tutto
+incluso prima di confermare la tua iscrizione.
+
+Spiacente per l'inconveniente.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+Per confermare che vuoi che
+
+!A
+
+venga rimosso dalla mailing list <#l#>, per favore invia una risposta vuota a
+questo indirizzo:
+
+!R
+
+In genere ciò accade non appena schiacci il bottone "Rispondi". 
+Se non funziona, allora ti basta copiare l'indirizzo e incollarlo nel campo
+"A:" di un nuovo messaggio.
+</#xE/>
+
+oppure clicca qui:
+       mailto:<#R#>
+</#E/>
+
+Non ho controllato che il tuo indirizzo sia al momento nella mailing list.
+Per vedere quale indirizzo hai usato per iscriverti, guarda ai messaggi che
+stai ricevendo dalla mailing list. Ciascun messaggio ha il tuo indirizzo
+nascosto nel campo di ritorno; per esempio, mary@xdd.ff.com riceve i messaggi
+con il campo di ritorno:<<#l#>-return-<numero>-mary=xdd.ff.com@<#H#>.
+
+</#qE/>
+Alcuni programmi di posta sono difettosi e non sono in grado di gestire
+indirizzi molto lunghi. Se non riesci a rispondere a questa richiesta
+spedisci un messaggio a <<#L#>-request@<#H#>> e metti l'indirizzo
+intero indicato sopra nel campo "Oggetto:".
+
+</text/unsub-nop#E/>
+Mi dispiace, ma non sono in grado di soddisfare la tua richiesta,
+visto che l'indirizzo
+
+!A
+
+non è nella mailing list <#l#> quando ho ricevuto la tua
+richiesta e non è iscritto a questa mailing list.
+
+Se ti sei cancellato, ma continui a ricevere i messaggi, allora ti sei
+iscritto con un altro indirizzo diverso da quello che usi al momento. per
+favore cerca nell'intestazione:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+Ciò indica che l'indirizzo usato per l'iscrizione è``user@host.dom''.
+L'indirizzo per cancellarsi per questo utente sarà:
+'<#l#>-unsubscribe-utente=host.dominnio@<#H#>'.
+Basta scrivere a questo indirizzo, sostituendo ai valori utente=host.dominio
+quelli corretti.
+
+Se il messaggio ha un'intestazione con ``List-Unsubscribe:'' puoi
+spedire un messaggio all'indirizzo in quell'intestazione. Esso
+contiene l'indirizzo di iscrizione già codificato al suo interno.
+
+Per alcuni programmi di posta devi rendere le intestazioni visibili per
+leggere il percorso di ritorno:
+
+Con Eudora 4.0, clicca sul bottone "Blah blah ..." .
+Con PMMail, clicca su "Finestra->Mostra l'intero messaggio/intestazione". 
+
+Se tutto ciò ancora non funziona, mi dispiace dirti che non posso aiutarti.
+Per favore INOLTRA un messaggio della lista con una nota nella quale spieghi
+cosa stai cercando di ottenere insieme a un elenco di indirizzi dai quali ti
+potresti essere iscritto al mio responsabile: 
+    <#l#>-owner@<#H#>
+
+che se ne occuperà lui. Il mio responsabile è un po' più lento di quello che
+sono io, così per favore sii paziente.
+</text/unsub-ok#E/>
+Conferma: ho cancellato l'indirizzo
+
+!A
+
+dalla mailing list <#l#> . Questo indirizzo non è più iscritto.
+
+</text/edit-do#nE/>
+Per favore modifica i seguenti file di testo e inviali all'indirizzo:
+
+!R
+
+Il tuo programma di posta dovrebbe avere un'opzione di Rispondi che utilizzi
+questo indirizzo automaticamente.
+
+Posso rimouvere i segni di citazione che il tuo programma aggiunge al testo, a
+meno che tu non modifichi le linee citate stesse.
+
+Le linee segnate sono quelle che iniziano con '%%%'. Esse non devono essere
+modificate (caratteri aggiuntivi aggiunti dal tuo programma di posta
+all'inizio della linea sono tollerati).
+
+
+</text/edit-list#nE/>
+Il comando <#L#>-edit.file può essere usato da un amministratore remoto per
+modificare il file di testo che costituiscono il corpo delle risposte dalla
+mailing list <#L#>@<#H#> .
+
+Quello che segue è una lista dei nomi dei file delle risposte e una breve
+descrizione di quando è utilizzato il loro contenuto. Per modificare un file,
+spedisci semplicemente una e-mail a <#L#>-edit.file, sostituendo 'file' con il
+nome del file. Le istruzioni di modifica sono inviate con il file.
+
+File                Utilizzo
+
+bottom        la fine di tutte le risposte. informazioni generali sui comandi
+digest        sezione "amministrativa" per le raccolte. 
+faq           domande più frequenti relative alla lista.
+get_bad       al posto dei messaggi non trovati nell'archivio.
+help          aiuto generico (tra l'inizio e la fine).
+info          informazioni sulla lista.
+mod_help      aiuto specifico per i moderatori.
+mod_reject    al mittente dei messaggi rifiutati.
+mod_request   ai moderatori insieme con il messaggio.
+mod_sub       agli iscritti dopo che il moderatore ha confermato l'iscrizione.
+mod_sub_confirm  ai moderatori per avere conferma delle iscrizioni.
+mod_timeout   al mittente del messaggio scaduto. 
+mod_unsub_confirm  all'amministratore remoto per chiedere conferma di cancellazione
+sub_bad        all'iscritto se la richiesta di iscrizione è sbagliata.
+sub_confirm   all'iscritto per chiedere conferma dell'iscrizione.
+sub_nop       all'iscritto dopo una re-iscrizione.
+sub_ok        all'iscritto dopo che è stata accettata l'iscrizione.
+top           l'inizio di tutte le risposte.
+</#tnE/>
+trailer       aggiunto ai messaggi spediti fuori dalla lista
+</#nE/>
+unsub_bad     all'iscritto se la conferma di cancellazione è sbagliata.
+unsub_confirm all'iscritto per chiedere conferma di cancellazione.
+unsub_nop     al non-iscritto dopo la cancellazione.
+unsub_ok      all'ex iscritto dopo l'avvenuta cancellazione.
+
+</text/edit-done#nE/>
+Il file di testo è stato aggiornato correttamente.
+</text/info#E/>
+Non sono state fornite informazioni per questa lista.
+</text/faq#E/>
+FAQ - Frequently asked questions per la lista <#l#>@<#H#> .
+
+Non ancora disponibili.
diff --git a/ezmlmrc.jp b/ezmlmrc.jp
new file mode 100644 (file)
index 0000000..a275392
--- /dev/null
@@ -0,0 +1,1334 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.jp,v 1.28 1999/12/22 04:02:15 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc.jp - Translated: Masashi Fujita <objectx@bandit.co.jp>
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#       </filename/>       : put succeeding text lines in DIR/filename
+#       </-filename/>      : erase DIR/filename.
+#       </+dirname/>       : create directory DIR/dirname
+#       </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+To unsubscribe, e-mail: <#L#>-unsubscribe@<#H#>
+For additional commands, e-mail: <#L#>-help@<#H#>
+</text/bottom#E/>
+
+--- mailing list <#l#> \e$B$GMxMQ2DG=$J\e(B command \e$B0lMw$G$9\e(B ---
+
+ezmlm \e$B$O0J2<$N\e(B command \e$B$r<+F0$G<B9T$7$^$9!#\e(B
+
+[[[ \e$B@dBP$K\e(B mailing list \e$BK\BN$K\e(B command \e$B$rAw$i$J$$$G2<$5$$\e(B ]]]
+
+command \e$B$O0J2<$N\e(B address \e$B08$K6u$N\e(B message \e$B$rAw$k$3$H$G<B9T$5$l$^$9!#\e(B
+
+   * \e$BMxMQ2DG=$J\e(B command \e$B0lMw$r<h$j=P$9\e(B
+     <<#L#>-help@<#H#>>
+     (Get help and commands)
+
+   * \e$B9XFI$r3+;O$9$k\e(B
+     <<#L#>-subscribe@<#H#>>
+     (Start subscription) 
+
+\e$B9XFI$rCf;_$9$k$K$O\e(B mailing list <#L#> \e$B$+$i$N\e(B message \e$B$N\e(B header \e$B$K\e(B
+\e$B4^$^$l$F$$$k\e(B ``List-Unsubscribe'' \e$B9T$,<($9\e(B address \e$B$K6u\e(B message
+\e$B$rAw$C$F$/$@$5$$!#$^$?$O0J2<$N\e(B address \e$B08$F$K6u\e(B message \e$B$rAw$C$F\e(B
+\e$B2<$5$$!#\e(B
+     <<#L#>-unsubscribe@<#H#>>
+     (Stop subscription)
+
+</#dE/>
+digest \e$B$N9XFI$rCf;_$9$k$K$O0J2<$N\e(B address \e$B08$F$K6u$N\e(B message \e$B$r\e(B
+\e$BAw$C$F2<$5$$!#\e(B
+     <<#L#>-digest-unsubscribe@<#H#>>
+     (Stop digest subscription)
+
+</#E/>
+\e$B9XFI$N3+;O$*$h$SCf;_$N:]$K$O\e(B ezmlm \e$B$O3NG'$N0Y$N\e(B message \e$B$r\e(B
+\e$B;XDj$5$l$?\e(B address \e$B08$F$KAw?.$7$^$9!#$=$N3NG'$N0Y$N\e(B message
+\e$B$KBP$7$F!"\e(Breply \e$B$rJV$9$3$H$G3NG'$N<jB3$-$O=*N;$7$^$9!#\e(B
+
+\e$B0J>e$N;X<($K=>$C$F$b!"K>$s$@7k2L$K$J$i$J$$>l9gEy$O!"\e(B
+mailing list <#L#> \e$B$N<g:K<T$N\e(B
+<#L#>-owner@<#H#> \e$BKxO"Mm$7$F2<$5$$!#\e(B
+\e$B!JJV;v$,CY$l$k$+$b$7$l$^$;$s!#$4N;>52<$5$$!K\e(B
+
+\e$BLdBj$N2r@O$r?J$a$d$9$/$9$k0Y$K\e(B ezmlm \e$B$+$iAw$i$l$F$-$?\e(B message \e$B$r\e(B
+\e$B!XA4$F$N\e(B header \e$B$r4^$a$F!YE:IU$7$F$/$@$5$$!#\e(B
+
+--- \e$B0J2<$KAw$i$l$F$-$?\e(B message \e$B$rE:IU$7$F$*$-$^$9\e(B ---
+
+</text/bounce-bottom#E/>
+
+--- \e$B0J2<$KJVAw$5$l$F$-$?\e(B message \e$B$rE:IU$7$F$*$-$^$9\e(B ---
+
+</text/bounce-num#E/>
+
+mailing list <#L#> \e$B$,5.J}08$KAw$C$?\e(B message \e$B$NFb\e(B
+\e$BG[Aw$K<:GT$7$?$b$N$NHV9f$rE:IU$7$F$*$-$^$9!#\e(B
+
+</#aE/>
+message \e$B$O\e(B archive \e$BCf$KJ]B8$5$l$F$$$^$9!#\e(B
+
+</#aE/>
+\e$B6u$N\e(B message \e$B$r0J2<$N\e(B command address \e$B$KAw$k$3$H$G!"5.J}$,<u$1<h$l\e(B
+\e$B$J$+$C$?\e(B message \e$B$r\e(B archive \e$BFb$+$i<h$j=P$9$3$H$,=PMh$^$9!#\e(B
+
+\e$BNc!K\e(B
+   * 123 \e$BHV$+$i\e(B 145 \e$BHVKx$N\e(B message \e$B$r<h$j=P$9\e(B
+     <<#L#>-get.123_145@<#H#>>
+     \e$B!J0lEY$K<h$j=P$;$k$N$O:GBg$G\e(B 100\e$BDL$G$9!K\e(B
+
+   * \e$B:G?7\e(B 100\e$BDL$NEj9F<T$H\e(B Subject: \e$B9T$r<h$j=P$9\e(B
+     <<#L#>-index@<#H#>>
+
+</#E/>
+\e$BG[Aw$K<:GT$7$?\e(B message \e$B$NHV9f$O0J2<$NDL$j$G$9\e(B:
+
+</text/dig-bounce-num#E/>
+
+\e$BG[Aw$K<:GT$7$?\e(B mailing list <#L#> \e$B$N\e(B digest \e$BHG$K\e(B
+\e$B4^$^$l$F$$$?:G=i$N\e(B message \e$BHV9f$rE:IU$7$F$*$-$^$9!#\e(B
+
+</#aE/>
+ezmlm \e$B$O\e(B digest \e$B<+BN$OJ]B8$7$F$$$^$;$s!#\e(B
+\e$B$G$9$,!"0J2<$N\e(B command address \e$B$K6u$N\e(B message \e$B$rAw$k;v$G\e(B
+mailing list <#L#> \e$BK\BN$N\e(B
+archive \e$B$+$i!"5.J}$,<u$1<h$l$J$+$C$?\e(B message \e$B72$r<h$j=P$9\e(B
+\e$B$3$H$,$G$-$^$9!#\e(B
+
+\e$BNc!K\e(B
+   * 123 \e$BHV$+$i\e(B 145 \e$BHVKx$N\e(B message \e$B$r<h$j=P$9\e(B
+     <<#L#>-get.123_145@<#H#>>
+     \e$B!J0lEY$K<h$j=P$;$k$N$O:GBg$G\e(B 100\e$BDL$G$9!K\e(B
+
+   * \e$B:G?7\e(B 100\e$BDL$NEj9F<T$H\e(B Subject: \e$B9T$r<h$j=P$9\e(B
+     <<#L#>-index@<#H#>>
+
+</#E/>
+--- \e$B0J2<$O\e(B digest \e$BHG$N3+;O\e(B message \e$BHV9f0lMw$G$9\e(B ---
+
+</text/bounce-probe#E/>
+
+\e$B5.J}08$K\e(B mailing list <#L#> \e$B$,Aw$C$?\e(B
+message \e$B$N4v$D$+$,G[Aw=PMh$:!"3NG'$N0Y$K\e(B ezmlm \e$B$,Aw?.$7$?\e(B
+message \e$B$bG[Aw$K<:GT$7$F$7$^$$$^$7$?!#\e(B
+
+\e$BG[Aw$K<:GT$7$FJVAw$5$l$F$-$?\e(B message \e$B$rE:IU$7$F$*$-$^$9!#\e(B
+
+\e$B$3$N\e(B message \e$B$O!"5.J}08$KG[Aw$,2DG=$+H]$+$rD4$Y$k$?$a$N$b$N$G$9!#\e(B
+\e$B$3$N\e(B message \e$B$bG[Aw$K<:GT$7$?>l9g$O!"\e(Bezmlm \e$B$O5.J}$N\e(B address \e$B$r\e(B
+mailing list <#L#>@<#H#> \e$B$+$i\e(B
+\e$B<+F0E*$K:o=|$7$^$9!#\e(B
+
+\e$B$b$&0lEY9XFI$r$7$?$$>l9g$O!"0J2<$N\e(B address
+
+        <<#L#>-subscribe@<#H#>>
+
+\e$B08$K6u$N\e(B message \e$B$rAw$C$F!"?75,$K9XFI$r$7$J$*$7$F2<$5$$!#\e(B
+
+</text/bounce-warn#E/>
+
+\e$B5.J}08$K\e(B mailing list <#L#> \e$B$,Aw$C$?\e(B message \e$B$N4v$D$+$G\e(B
+\e$BG[Aw$K<:GT$7$^$7$?!#\e(B
+
+\e$BG[Aw$K<:GT$7$?\e(B message \e$B$N:G=i$N0lDL$rE:IU$7$F$*$-$^$9!#\e(B
+
+\e$B$b$7$b$3$N\e(B message \e$B<+BN$bG[Aw$K<:GT$7$?>l9g$K$O\e(B
+ezmlm \e$B$O3NG'$N\e(B message \e$B$rAw$j$^$9!#$=$N\e(B message \e$B$bG[Aw$K<:GT$7$?>l9g\e(B
+\e$B5.J}$N\e(B address \e$B$O\e(B mailing list <#L#> \e$B$+$i\e(B
+\e$BL5>r7o$K:o=|$5$l$^$9!#\e(B
+
+</text/digest#dE/>
+digest \e$B$N9XFI$r4uK>$9$k>l9g$O!"6u$N\e(B message \e$B$r\e(B
+
+        <<#L#>-digest-subscribe@<#H#>>
+        (To subscribe to the digest)
+
+\e$B08$KAw$C$F2<$5$$!#\e(B
+
+digest \e$B$N9XFI$r$d$a$k>l9g$O!"6u$N\e(B message \e$B$r\e(B
+
+        <<#L#>-digest-unsubscribe@<#H#>>
+        (To unsubscribe from the digest)
+
+\e$B08$KAw$C$F2<$5$$!#\e(B
+
+mailing list <#L#> \e$B$X$N\e(B post \e$B$O\e(B
+
+        <<#L#>@<#H#>>
+        (To post to the list)
+
+\e$B08$K$*4j$$$7$^$9!#\e(B
+
+</text/get-bad#E/>
+\e$B?=$7LuM-$j$^$;$s$,!";XDj$5$l$?\e(B message \e$B$O\e(B archive \e$BCf$K\e(B
+\e$B$"$j$^$;$s$G$7$?!#\e(B
+
+</text/help#E/>
+\e$B$3$l$OHFMQ$N\e(B help message \e$B$G$9!#\e(B
+\e$B5.J}$N\e(B message \e$B$O\e(B ezmlm \e$B$N\e(B command \e$B$H$7$F$OG'<1$5$l$^$;$s$G$7$?!#\e(B
+
+\e$B0J2<$KMxMQ2DG=$J\e(B command address \e$B0J2<$NDL$j$G$9!'\e(B
+\e$B!J6u\e(B message \e$B$r;XDj$5$l$?\e(B command address \e$B$KAw?.$9$k$3$H$G<B9T$5$l$^$9!K\e(B
+
+   * mailing list <#l#> \e$B$N@bL@$r<h$j=P$7$^$9!#\e(B
+     <<#L#>-info@<#H#>>
+     (Get information) 
+   * mailing list <#l#> \e$B$N!X$h$/?R$M$i$l$k<ALd$H$=$NEz$(!Y$r<h$j=P$7$^$9!#\e(B
+     <<#L#>-faq@<#H#>>
+     (Get FAQ) 
+
+</#dE/>
+   * digest list <#L#>-digest \e$B$N9XFI$r3+;O$9$k\e(B
+     <<#L#>-digest-subscribe@<#H#>>
+     (Start digest subscription) 
+
+   * digest list <#L#>-digest \e$B$N9XFI$rCf;_$9$k\e(B
+     <<#L#>-digest-unsubscribe@<#H#>>
+     (Stop digest subscription) 
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+   * 123 \e$BHV$+$i\e(B 145 \e$BHVKx$N\e(B message \e$B$r<h$j=P$9\e(B
+     <<#L#>-get.123_145@<#H#>>
+     \e$B!J0lEY$K<h$j=P$;$k$N$O:GBg$G\e(B 100\e$BDL$G$9!K\e(B
+
+   * 123 \e$BHV$+$i\e(B 145 \e$BHVKx$N\e(B message \e$B$NAw?.<T$H\e(B Subject: \e$B$r<h$j=P$9\e(B
+     <<#L#>-index.123_456@<#H#>>
+
+   * message 12345 \e$BHV$N\e(B Subject: \e$B$HF1$8\e(B Subject: \e$B$r;}$D\e(B message \e$B$r\e(B
+     \e$B<h$j=P$9\e(B
+     <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+command address \e$B08$F$N\e(B message \e$B$O\e(B address \e$B$N$_$,I,MW$G$9!#Cf?H$O\e(B
+\e$B2?$,$"$C$F$bL5;k$5$l$^$9!#\e(B
+
+\e$BI,MW$G$"$l$PDL>o$H$O0[$J$k\e(B address \e$B$G\e(B mailing list <#L#> \e$B$+$i$N\e(B
+message \e$B$r<u$1<h$k$h$&$K=PMh$^$9!#\e(B
+
+\e$BNc!K\e(Bjohn@host.domain \e$B$G<u$1<h$kMM$K$9$k\e(B
+
+    \e$B<u$1<h$j\e(B address \e$B$r0J2<$NMM$KJQ49$7$F\e(B
+
+        john@host.domain
+                \e$B"-\e(B      \e$B!J\e(B'@' --> '='\e$B!K\e(B
+        john=host.domain
+                \e$B"-\e(B
+        <<#L#>-subscribe-john=host.domain@<#H#>>
+
+<#L#>-subscribe-john=host.domain@<#H#> \e$B08$F$K6u\e(B message \e$B$rAw$k$3$H$G\e(B
+john@host.domain \e$B$G$N9XFI$r3+;O$G$-$^$9!#\e(B
+
+\e$B$3$N>l9g!"9XFI$rCf;_$9$k$K$O\e(B <#L#>-unsubscribe-john=host.domain@<#H#>
+\e$B08$F$K6u\e(B message \e$B$rAw$C$F$/$@$5$$!#\e(B
+
+</text/mod-help#E/>
+--- \e$B4IM}<T$N3'MM$X\e(B ---
+
+mailing list <#L#>@<#H#> \e$B$N4IM}$K\e(B
+\e$B6(NO$7$F$$$?$@$-46<U$7$^$9!#\e(B
+
+ezmlm \e$B$N\e(B command \e$B$O!"B>$N\e(B mailing list program \e$B$H$O<c430[$J$C$F\e(B
+\e$B8+$($k$+$b$7$l$^$s$,!"\e(Bcommand \e$B$,D>46E*$G$"$j!";HMQ$,4JC1$@$H\e(B
+\e$B;W$($k$G$7$g$&!#\e(B
+
+\e$B0J2<$O\e(B mailing list \e$B$N4IM}<T$H$7$F$N:n6H$N$?$a$N<j0z$-$G$9!#\e(B
+
+Remote subscription
+-------------------
+\e$B4IM}<T$OG$0U$N\e(B address \e$B$r9XFI<T$K2C$($?$j30$7$?$j$9$k;v$,=PMh$^$9!#\e(B
+\e$BNc$($P\e(B "john@host.domain" \e$B$r9XFI<T$K2C$($k>l9g$O!"\e(B
+
+                john@host.domain
+                        \e$B"-\e(B
+                john=host.domain
+                        \e$B"-\e(B
+        <<#L#>-subscribe-john=host.domain@<#H#>>
+
+\e$B$NMM$K$7$F\e(B command address \e$B$r@8@.$7!"$=$N\e(B address \e$B$X\e(B
+\e$B6u$N\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+
+\e$B$^$?!"\e(Bjohn@host.domain \e$B$r9XFI<T$+$i30$9>l9g$O\e(B
+
+        <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+\e$B$X6u$N\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+
+</#dE/>
+digest list \e$B$K2C$($?$j!&30$7$?$j$9$k>l9g$bF1MM$K$7$F\e(B
+
+        <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+        <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+\e$B$NMM$K$J$j$^$9!#\e(B
+
+</#E/>
+message \e$B$NCf?H$OI,MW$"$j$^$;$s!#\e(Baddress \e$B$@$1$,=EMW$G$9!#\e(B
+
+</#rE/>
+ezmlm \e$B$O5.J}$K3NG'$N\e(B message \e$B$rAw$j$^$9!#$3$l$O\e(B request \e$B$,\e(B
+\e$BK\Ev$K5.J}$+$i$N$b$N$+$r3NG'$9$k$?$a$G$9!#Aw$i$l$?\e(B message \e$B$K\e(B
+reply \e$B$9$k;v$G<jB3$-$O40N;$7$^$9!#\e(B
+
+</#RE/>
+ezmlm \e$B$O\e(B user \e$B$KBP$7$F3NG'$N\e(B message \e$B$rAw$j$^$9!#\e(B
+\e$B>e5-$N>l9g$O\e(B <john@host.domain> \e$B08$K3NG'$N\e(B message \e$B$,Aw$i$l$k\e(B
+\e$B$3$H$K$J$j$^$9!#3NG'$N\e(B message \e$B$r<u$1<h$C$?\e(B user \e$B$O\e(B reply \e$B$r\e(B
+\e$B$9$k$3$H$G3NG'$,=*N;$7$^$9!#\e(B
+</#E/>
+
+\e$B$3$N3NG'$K$h$C$F!"Bh;0<T$K$h$k967b$r:$Fq$J$b$N$K$7$F$$$^$9!#\e(B
+
+\e$B$^$?!"\e(Bezmlm \e$B$O9XFI$N>uBV$,JQ2=$7$?$3$H$r\e(B user \e$B$KDLCN$7$^$9!#\e(B
+
+
+Subscription
+------------
+\e$B4IM}<T$K8B$i$:!"C/$G$b0J2<$N\e(B address \e$B08$K6u$N\e(B message \e$B$r\e(B
+\e$BAw$k;v$G9XFI$r3+;O$7$?$j!&=*N;$5$;$?$j=PMh$^$9!#\e(B
+
+        <<#L#>-subscribe@<#H#>>
+       (Start subscription)
+
+       <<#L#>-unsubscribe@<#H#>>
+       (Stop subscription)
+
+</#dE/>
+digest list \e$B$N9XFI3+;O!&=*N;$O0J2<$N\e(B command address \e$B$G$9!#\e(B
+
+        <<#L#>-digest-subscribe@<#H#>>
+       (Start digest subscription)
+
+       <<#L#>-digest-unsubscribe@<#H#>>
+       (Stop digest subscription)
+
+</#E/>
+\e$BH/?.<T$K$O3NG'$N\e(B message \e$B$,\e(B ezmlm \e$B$+$iAw$i$l$^$9!#\e(B
+
+</#sE/>
+\e$B$3$N\e(B mailing list \e$B$N9XFI3+;O!&C&B`$O4IM}<T$N4FFD2<$G9T$o$l$kMM\e(B
+\e$B@_Dj$5$l$F$$$^$9!#4IM}<T08$K3NG'$r=P$9A0$K\e(B ezmlm \e$B$O\e(B request \e$B$r\e(B
+\e$B=P$7$?H/?.<T$X3NG'$N\e(B message \e$B$rAw$j!"\e(Brequest \e$B$,K\J*$+H]$+$r3NG'\e(B
+\e$B$7$F$$$k$N$G!"$=$N\e(B request \e$B$,K\J*$G$"$k$H9M$($F$bBg>fIW$G$9!#\e(B
+
+request \e$B$,@5Ev$J$b$N$H;W$&$N$G$"$l$P!"\e(Bezmlm \e$B$+$iFO$$$?3NG'$N\e(B
+message \e$B$KBP$7$F\e(B reply \e$B$7$F2<$5$$!#@5Ev$G$J$$$H;W$&$N$G$"$l$P\e(B
+ezmlm \e$B$+$i$N\e(B message \e$B$rC1=c$KL5;k$7$F2<$5$$!#$=$7$F!"I,MW$J$i\e(B
+request \e$B$NH/?.<T$KO"Mm$7$F$_$F2<$5$$!#\e(B
+</#SE/>
+\e$B9XFI3+;O!&C&B`$O<+F0$G=hM}$5$l$^$9!#\e(B
+</#E/>
+
+\e$B$^$?!"\e(B
+
+        <<#L#>-subscribe-mary=host.domain@<#H#>>
+       (Start subscription as mary@host.domain)
+
+       <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+       (Stop subscription as mary@host.domain)
+
+\e$B$NMM$K$9$k;v$G!"H/?.85$H$O0[$J$k\e(B address\e$B!J$3$N>l9g$O\e(B mary@host.domain\e$B!K\e(B
+\e$B$G\e(B mailing list \e$B$+$i$N\e(B message \e$B$r<u$1<h$kMM$K$9$k;v$b=PMh$^$9!#\e(B
+\e$B3NG'$O\e(B mary@host.domain \e$B08$KH/9T$5$l!"$=$N\e(B message \e$B$KBP$7$F\e(B
+\e$B@5$7$$\e(B reply \e$B$,5"$C$F$-$?>l9g$N$_9XFI!&C&B`$OM-8z$K$J$j$^$9!#\e(B
+
+\e$B4IM}<T$N\e(B address \e$B$=$NB>$N>pJs$O9XFI<T$K$O0l@ZO"Mm$5$l$^$;$s!#\e(B
+\e$B!JL^O@!"5.J}$,D>@\\e(B mail \e$B$rAw$C$?>l9g$OJL$G$9!K\e(B
+
+</#rlE/>
+mailing list <#L#>@<#H#> \e$B$N9VFI<T0lMw$O\e(B
+\e$B0J2<$N\e(B address \e$B$+$iF@$i$l$^$9!#\e(B
+        <<#L#>-list@<#H#>>
+ezmlm \e$B$N\e(B log \e$B$O0J2<$N\e(B address \e$B$+$iF@$i$l$^$9!#\e(B
+        <<#L#>-log@<#H#>>
+
+</#rldE/>
+mailing list <#L#>@<#H#> \e$B$N\e(B digest \e$BHG$N9VFI<T0lMw$O\e(B
+\e$B0J2<$N\e(B address \e$B$+$iF@$i$l$^$9!#\e(B
+        <<#L#>-digest-list@<#H#>>
+digest \e$BHG$K4X$9$k\e(B log \e$B$O0J2<$N\e(B address \e$B$G$9!#\e(B
+        <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+ezmlm \e$B$,Aw$j=P$9\e(B message text \e$B$rJT=8$9$k$K$O!"0J2<$N\e(B command address:
+
+        <<#L#>-edit@<#H#>>
+
+\e$B08$K6u$N\e(B e-mail \e$B$rAw$C$F2<$5$$!#\e(B
+\e$B!JJT=82DG=$J\e(B message \e$B0lMw5Z$S!"JT=8J}K!$OJV?.$K4^$^$l$F$$$^$9!K\e(B
+
+</#mE/>
+Moderated posts
+---------------
+message \e$B$r?3::$9$kMM$K@_Dj$5$l$F$$$k>l9g$O!"\e(Bezmlm \e$B$O\e(B post \e$B$5$l$?\e(B
+message \e$B$r0lC6J]B8$7!"?3::<jB3$-$N@bL@$rIU$1$?$b$N$r4IM}<T08$K\e(B
+\e$BAw$j$^$9!#$=$N\e(B message \e$B$O\e(B "Subject: MODERATE for ..." \e$B$H$J$C$F\e(B
+\e$B$$$^$9!#\e(B
+
+post \e$B$rG'$a$k>l9g$O!"\e(Bezmlm \e$B$,\e(B "Reply-To:" \e$B$K@_Dj$7$?\e(B address
+\e$B08$K\e(B reply \e$B$r$7$F2<$5$$!#K\J8$N\e(B copy \e$B$OI,MW$"$j$^$;$s!#\e(B
+\e$B!J\e(Bezmlm \e$B$O\e(B reply \e$B$NCf?H$O0l@Z8+$F$$$^$;$s!K\e(B
+
+post \e$B$r5qH]$9$k>l9g$O!"\e(Bezmlm \e$B$,\e(B "From:" \e$B$K@_Dj$7$?\e(B address \e$B08$K\e(B
+reply \e$B$r$7$F2<$5$$!#DL>o\e(B Mail User Agent (MUA) \e$B$N\e(B 'reply-to-all'
+\e$B$N5!G=$r;H$C$F\e(B message \e$B$N=`Hw$r$7$?8e$K!"ITMW$J\e(B address \e$B$r:o$k\e(B
+\e$B$@$1$@$H;W$$$^$9!#5qH]$9$kM}M3Ey$r#38D$N\e(B'%'\e$B$G;O$^$k9T$N8e$KIU$1\e(B
+\e$B#38D$N\e(B'%' \e$B$GJD$8$k$3$H$GF1:-$9$k;v$,=PMh$^$9!#\e(B
+
+\e$B7+$jJV$7$^$9$,!"4IM}<T$N\e(B address \e$B$=$NB>$N>pJs$O\e(B ezmlm \e$B7PM3$G$O\e(B
+\e$BL@$i$+$K$5$l$^$;$s!#\e(B
+
+\e$BJ#?t$N4IM}<T$,$$$k>l9g!"\e(Bezmlm \e$B$O:G=i$K4IM}<T$+$iFO$$$?\e(B message
+\e$B$N$_$KH?1~$7$^$9!#5.J}$,\e(B post \e$B$rG'$a$k\e(B reply \e$B$r$7$?;~$K!"4{$K\e(B
+\e$BB>$N4IM}<T$,5qH]$r$7$F$$$?>l9g!"5Z$S$=$N5U$N>l9g$O\e(B ezmlm \e$B$O\e(B
+\e$B5.J}08$K\e(B message \e$B$rAw$j$^$9!#\e(B
+
+ezmlm \e$B$ODj$a$i$l$?4|8B!JDL>o$O\e(B 5\e$BF|!K0JFb$K?3::$N7k2L$r<u$1\e(B
+\e$B<h$l$J$$>l9g$K$O!"H/?.<T08$KM}M3$rF1:-$7$F\e(B message \e$B$rJVAw$7$^$9!#\e(B
+\e$B!JI,MW$H$"$l$P!"4|8B@Z$l$N\e(B message \e$B$rC1=c$K:o=|$9$kMM$K$b\e(B
+  \e$B@_Dj=PMh$^$9!K\e(B
+</#E/>
+
+Vacations
+---------
+\e$B0l;~E*$K0[$J$k\e(B address \e$B$G4IM}$N:n6H$r$7$?$$>l9g$O!"\e(B
+"Mailing-List: contact <#L#>-help@<#H#>; run by ezmlm" \e$B$d\e(B
+"Subject: MODERATE for <#L#>@<#H#>" \e$B$d\e(B
+"Subject: CONFIRM subscribe to <#L#>@<#H#>" \e$BEy$N\e(B
+\e$B4IM}$N0Y$N\e(B message \e$B$,K>$s$@\e(B address \e$B08$KFO$/MM$K@_Dj$9$l$P\e(B
+\e$B2DG=$G$9!#\e(B
+
+\e$B$^$?$OCN?M08$KAw$jD>$9MM$K$9$k$H$$$&<j$b$"$j$^$9!#\e(B
+\e$B!J$3$N>l9g$O;vA0$K<g:K<T$K3NG'$r$H$C$F2<$5$$!K\e(B
+
+\e$B5.J}$,4IM}$N:n6H$r<B9T=PMh$J$$4V$O!"<+F0$G>5G'$r9T$&$H$$$&$N$G\e(B
+\e$B$"$l$P!">e5-$N4IM}MQ\e(B message \e$B$KBP$7$FE,@Z$J1~Ez$r<+F0@8@.$9$k\e(B
+\e$BMM$K@_Dj$7$F2<$5$$!#\e(B
+
+</#rE/>
+\e$B9XFI3+;O!&=*N;$N:n6H$r4IM}<T$G$"$k5.J}$N$b$N$G$J$$\e(B address \e$B$+$i\e(B
+\e$B9T$C$?>l9g!"9XFI3+;O!&=*N;$NBP>]$K$J$C$?\e(B address \e$B08$K3NG'$N\e(B message
+\e$B$,FO$-$^$9!#$=$N8e$KA44IM}<T08$K3NG'$N\e(B message \e$B$,FO$-$^$9!#$3$l$O\e(B
+request \e$B$rH/9T$7$?$N$,5.J}<+?H$+H]$+$r\e(B ezmlm \e$B$,H=CG$9$k<jCJ$r;}$C$F\e(B
+\e$B$$$J$$0Y$G$9!#\e(B
+
+\e$B$3$N>l9g$O!"5.J}$NAw?.$7$?\e(B message \e$B$d\e(B address \e$B$,BP>]$H$J$C$?\e(B user
+\e$B08$KAw$i$l$k;v$r3P$($F$*$$$F2<$5$$!#\e(B
+
+</#E/>
+\e$B$*<j?t$r$*$+$1$7$^$9!#\e(B
+
+PS: \e$B2?$+ITL@$JE@Ey$,$"$j$^$7$?$i!"<g:K<T$N\e(B
+
+        <#L#>-owner@<#H#>
+
+    \e$B$KO"Mm$7$F2<$5$$!#\e(B
+
+</text/mod-reject#E/>
+\e$B?=$7LuM-$j$^$;$s$,!"5.J}$N\e(B post \e$B$7$?\e(B message \e$B$O4IM}<T$K$h$C$F\e(B
+\e$B5qH]$5$l$^$7$?!#4IM}<T$,2?$+\e(B comment \e$B$rIU$1$F$$$k>l9g$O\e(B
+\e$B0J2<$KE:IU$5$l$F$$$^$9!#\e(B
+</text/mod-request#E/>
+mailing list <#L#> \e$B$K\e(B
+\e$B0J2<$KE:IU$5$l$?\e(B message \e$B$,\e(B post \e$B$5$l$^$7$?!#\e(B
+post \e$B$rG'$a$k>l9g$O!"0J2<$N\e(B address
+
+!A
+
+\e$B$X\e(B reply \e$B$7$F2<$5$$!#\e(B
+
+\e$BDL>o$O\e(B Mail User Agent (MUA) \e$B$N\e(B reply \e$B$N5!G=$@$1$G==J,$JH&$G$9!#\e(B
+
+\e$B$&$^$/$$$+$J$$>l9g$O!"<jF0$G\e(B
+
+        To: <#A#>
+
+\e$B$rIU$1$F\e(B reply message \e$B$r:n@.$7$F2<$5$$!#\e(B
+
+</#xE/>
+
+MUA \e$B$K$h$C$F$O\e(B
+
+        mailto:<#A#>
+
+\e$B$r\e(B click \e$B$9$k$@$1$G=`Hw$,=PMh$k>l9g$b$"$k$G$7$g$&!#\e(B
+</#E/>
+
+post \e$B$rG'$a$J$$>l9g$O!"0J2<$N\e(B address
+
+!R
+
+\e$B08$K\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+
+MUA \e$B$K\e(B "reply-to-all" \e$B$N5!G=$,$"$k$N$J$i\e(B <#L#>-reject \e$B0J30$G\e(B
+\e$B;O$^$kB>$N\e(B address \e$B$r:o=|$9$k$N$,4JC1$G$7$g$&!#\e(B
+</#xE/>
+
+MUA \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$9$k$@$1$G=`Hw$,=PMh$k>l9g$b$"$k$G$7$g$&!#\e(B
+</#E/>
+
+*** post \e$B$5$l$?\e(B message \e$BK\BN$O\e(B copy \e$B$9$kI,MW$O$"$j$^$;$s\e(B ***
+
+post \e$B$rG'$a$J$$>l9g$K!"2?$+\e(B comment \e$B$rIU$1$?$$$N$G$"$l$P\e(B
+\e$B0J2<$NMM$J\e(B marker line
+
+%%% Start comment
+%%% End comment
+
+\e$B$N4V$K\e(B comment \e$B$r=q$$$F2<$5$$!#\e(B
+
+--- \e$B0J2<$KAw$i$F$-$?\e(B message \e$B$rE:IU$7$F$*$-$^$9\e(B
+
+</text/mod-sub#E/>
+--- \e$B4IM}<T$,5.J}$N\e(B
+    mailing list <#L#>@<#H#> \e$B$G$N\e(B
+    \e$B9XFI$N>uBV$rJQ99$7$^$7$?!#\e(B
+
+\e$B$3$NJQ99$,K>$s$@$b$N$G$J$$>l9gEy$O!"$9$0$K\e(B
+
+        <#L#>-owner@<#H#>
+
+\e$BKxO"Mm$r2<$5$$!#\e(B
+
+mailing list <#L#> \e$B$N\e(B archive \e$B$K\e(B access \e$B$7$?$$>l9g$O\e(B
+
+        <#L#>-help@<#H#>
+
+\e$BKx!"6u$N\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+
+</text/mod-timeout#E/>
+\e$B?=$7LuM-$j$^$;$s!#\e(B
+mailing list <#L#> \e$B$N4IM}<T$,\e(B
+\e$B5.J}$N\e(B message \e$B$r?3::$9$kA0$K;~4V@Z$l$K$J$j$^$7$?!#\e(B
+
+\e$B$3$l$,\e(B error \e$B$@$H;W$&$N$G$"$l$P!"$b$&0lEY\e(B post \e$B$9$k$+\e(B
+\e$B4IM}<T$KD>@\AjCL$7$F$_$F2<$5$$!#\e(B
+
+--- \e$B5.J}$+$iAw$i$l$?\e(B message \e$B$rE:IU$7$F$*$-$^$9\e(B ---
+
+</text/mod-sub-confirm#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$N\e(B
+\e$B9XFI<T$H$7$F2C$($FM_$7$$;]$N\e(B request \e$B$,FO$-$^$7$?!#\e(B
+\e$B!J$3$N\e(B request \e$B$,9XFI4uK><TK\?M$+$i$N\e(B request \e$B$N>l9g$O!"\e(B
+  \e$BI,MW$J3NG'$O:Q$s$G$$$^$9!K\e(B
+
+\e$B9XFI$rG'$a$k>l9g$O!"6u$N\e(B message \e$B$r0J2<$N\e(B address
+
+!R
+
+\e$B$K\e(B reply \e$B$7$F2<$5$$!#\e(B
+
+\e$BDL>o$O\e(B Mail User Agent (MUA) \e$B$N\e(B reply \e$B$N5!G=$@$1$G==J,$JH&$G$9!#\e(B
+\e$B$=$l$,$&$^$/9T$+$J$$>l9g$O!"\e(B
+
+        To: <#R#>
+
+\e$B$r<jF0$G@_Dj$7$F\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+</#xE/>
+
+MUA \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$9$k$@$1$G=`Hw$,=PMh$k$G$7$g$&!#\e(B
+</#E/>
+
+\e$B9XFI$rG'$a$J$$>l9g$O!"$3$N\e(B message \e$B$rL5;k$7$F$/$@$5$$!#\e(B
+
+</text/mod-unsub-confirm#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$+$i\e(B
+\e$B30$9$N$G$"$l$P!"0J2<$N\e(B address
+
+!R
+
+\e$B08$K\e(B reply \e$B$7$F2<$5$$!#\e(B
+
+\e$BDL>o$O\e(B Mail User Agent (MUA) \e$B$N\e(B reply \e$B$N5!G=$@$1$G==J,$JH&$G$9!#\e(B
+\e$B$=$l$,$&$^$/9T$+$J$$>l9g$O!"\e(B
+
+        To: <#R#>
+
+\e$B$r<jF0$G@_Dj$7$F\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+</#xE/>
+
+MUA \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$9$k$@$1$G=`Hw$,=PMh$k$G$7$g$&!#\e(B
+</#E/>
+
+\e$BC&B`$rG'$a$J$$>l9g$O!"$3$N\e(B message \e$B$rL5;k$7$F2<$5$$!#\e(B
+
+</text/sub-bad#E/>
+\e$B3NG'$N0Y$N\e(B key \e$B$,IT@5$G$9!#\e(B
+
+\e$B4v$D$+$NM}M3$,9M$($i$l$^$9$,!"$b$C$H$bB?$$$N$,4|8B@Z$l$K\e(B
+\e$B$h$k$b$N$G$9!#3NG'$OLs\e(B 10\e$BF|0JFb$K9T$o$l$J$1$l$P$J$j$^$;$s!#\e(B
+\e$B$^$?!"\e(Be-mail \e$B$r07$&4D6-$K$h$C$F$O\e(B ezmlm \e$B$,;H$C$F$$$kD9$$\e(B
+address \e$B$r@Z$j5M$a$F$7$^$&;v$,$"$k$N$G!"A4$F$N\e(B address \e$B$,\e(B
+\e$B@5$7$/EA$o$C$F$$$k$+$I$&$+3NG'$7$F$_$F2<$5$$!#\e(B
+
+\e$B?7$7$$\e(B key \e$B$rMQ0U$7$^$7$?!"0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$K\e(B
+\e$B2C$($?$$>l9g$O!"6u$N\e(B message \e$B$r0J2<$N\e(B address
+
+!R
+
+\e$B$KAw$C$F2<$5$$!#\e(B
+</#xE/>
+
+Mail User Agent (MUA) \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$9$k$@$1$G3NG'$N\e(B message \e$B$rAw$k=`Hw$b=PMh$k$G$7$g$&!#\e(B
+</#E/>
+
+\e$B7+$jJV$9MM$G$9$,!"\e(Breply \e$B$N\e(B address \e$BA4$F$,@5$7$/<h$j9~$^$l$F\e(B
+\e$B$$$k$+!"Aw?.A0$K3NG'$7$F2<$5$$!#\e(B
+
+\e$B$b$7$&$^$/9T$+$J$$MM$J$i\e(B
+
+        <#L#>-Owner <<#L#>-owner@<#H#>>
+
+\e$B$KO"Mm$r2<$5$$!#\e(B
+
+</text/sub-confirm#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#>
+\e$B$K2C$($k;v$rG'$a$k>l9g$O!"6u$N\e(B message \e$B$r\e(B
+
+!R
+
+\e$B$KJV?.$7$F2<$5$$!#\e(B
+
+\e$BDL>o$O\e(B Mail User Agent (MUA) \e$B$N\e(B reply \e$B$N5!G=$r;H$&$@$1$G$7$g$&!#\e(B
+
+\e$B$=$l$G$&$^$/9T$+$J$$>l9g$O\e(B
+
+        To:<#R#>
+
+\e$B$H$7$F\e(B message \e$B$r:n@.$7$F2<$5$$!#\e(B
+</#xE/>
+
+MUA \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$9$k$@$1=`Hw$,=PMh$k$b$N$b$"$k$G$7$g$&!#\e(B
+</#E/>
+
+\e$B$3$N3NG'$K$O0J2<$NFs$D$N0UL#$,$"$j$^$9!#\e(B
+
+        * \e$B;XDj$5$l$?\e(B address \e$B$G\e(B message \e$B$,Aw?.=PMh$k$+H]$+\e(B
+        * \e$B5.J}$N\e(B address \e$B$r56$C$?\e(B request \e$B$rGS=|$9$k\e(B
+
+</#qE/>
+e-mail \e$B$r07$&4D6-$K$h$C$F$O!"\e(Bezmlm \e$B$,;H$&D9$$\e(B address \e$B$r\e(B
+\e$B$&$^$/07$($J$$;v$,$"$j$^$9!#$=$NMM$J>l9g$O0J2<$N\e(B address
+
+        <<#L#>-request@<#H#>>
+
+\e$B$K\e(B "Subject: <#R#>" \e$B$H$7$F\e(B
+message \e$B$rAw$C$F2<$5$$!#\e(B
+
+</#sE/>
+mailing list <#L#> \e$B$O\e(B moderated \e$B$J\e(B mailing list \e$B$G$9!#\e(B
+\e$B5.J}$,$3$N3NG'$rAw?.$9$k$H!"A4$F$N4IM}<T08$F$K5.J}$N9XFI$rG'$a$k$+H]$+\e(B
+\e$B$N3NG'$,FO$-$^$9!#4IM}<T$,9XFI$r>5G'$7$?8e!"5.J}08$F$K$=$N;]$NDLCN$,\e(B
+\e$BFO$/$O$:$G$9!#\e(B
+
+</text/sub-nop#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$O4{$K\e(B mailing list <#L#> \e$B$N\e(B
+\e$B9XFI<T$K$J$C$F$$$^$9!#\e(B
+
+</text/sub-ok#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$K2C$($^$7$?!#\e(B
+
+        -       -       -       -       -       -       -       -       -
+
+[[[ Welcome to <#L#>@<#H#>! ]]]
+
+\e$B8e$G3NG'$N$?$a$KI,MW$K$J$k;v$,$"$j$^$9$N$G!"$3$N\e(B message \e$B$O\e(B
+\e$BK:$l$:J]B8$7$F$*$$$F2<$5$$!#\e(B
+
+\e$B9XFI$rCf;_$9$k>l9g$O!"0J2<$N\e(B address \e$B08$F$K6u$N\e(B message \e$B$rAw$C$F2<$5$$\e(B
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+\e$B$3$s$K$A$O!#;d$O\e(B ezmlm
+
+mailing list <#L#>@<#H#> \e$B$r4IM}$7$F$$$^$9!#\e(B
+
+</#x/>
+\e$B$3$N\e(B mailing list \e$B$N<g:K<T$K$O\e(B
+
+        <#L#>-owner@<#H#>
+
+\e$B$GO"Mm$9$k$3$H$,=PMh$^$9!#\e(B
+
+</text/unsub-bad#E/>
+\e$BAw$i$l$F$-$?3NG'$N0Y$N\e(B key \e$B$,@5$7$/$"$j$^$;$s!#\e(B
+
+\e$B4v$D$+$NM}M3$,9M$($i$l$^$9$,!"$b$C$H$bB?$$$N$,4|8B@Z$l$K\e(B
+\e$B$h$k$b$N$G$9!#3NG'$OLs\e(B 10\e$BF|0JFb$K9T$o$l$J$1$l$P$J$j$^$;$s!#\e(B
+\e$B$^$?!"\e(Be-mail \e$B$r07$&4D6-$K$h$C$F$O\e(B ezmlm \e$B$,;H$C$F$$$kD9$$\e(B
+address \e$B$r@Z$j5M$a$F$7$^$&;v$,$"$k$N$G!"A4$F$N\e(B address \e$B$,\e(B
+\e$B@5$7$/EA$o$C$F$$$k$+$I$&$+3NG'$7$F$_$F2<$5$$!#\e(B
+
+\e$B?7$7$$\e(B key \e$B$rMQ0U$7$^$7$?!"0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$+$i\e(B
+\e$B30$7$?$$>l9g$O!"6u$N\e(B message \e$B$r0J2<$N\e(B address
+
+!R
+
+\e$B$KAw$C$F2<$5$$!#\e(B
+</#xE/>
+
+Mail User Agent (MUA) \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$9$k$@$1$G3NG'$N\e(B message \e$B$rAw$k=`Hw$b=PMh$k$G$7$g$&!#\e(B
+</#E/>
+
+\e$B7+$jJV$9MM$G$9$,!"\e(Breply \e$B$N\e(B address \e$BA4$F$,@5$7$/<h$j9~$^$l$F\e(B
+\e$B$$$k$+!"Aw?.A0$K3NG'$7$F2<$5$$!#\e(B
+
+\e$B$b$7$&$^$/9T$+$J$$MM$J$i\e(B
+
+        <#L#>-Owner <<#L#>-owner@<#H#>>
+
+\e$B$KO"Mm$r2<$5$$!#\e(B
+
+</text/unsub-confirm#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$N\e(B
+\e$B9XFI<T$+$i30$7$?$$>l9g$O!"6u$N\e(B message \e$B$r0J2<$N\e(B address
+
+!R
+
+\e$BKxAw$C$F2<$5$$!#\e(B
+
+\e$BDL>o$O\e(B Mail User Agent (MUA) \e$B$N\e(B reply \e$B$N5!G=$r;H$($PBg>fIW$G$9!#\e(B
+
+\e$B$b$7!"$=$l$G$&$^$/9T$+$J$$>l9g$O!"\e(B
+
+        To: <#R#>
+
+\e$B$H$7$F\e(B message \e$B$r:n@.$7$F2<$5$$!#\e(B
+</#xE/>
+
+\e$B$^$?!"\e(BMail User Agent (MUA) \e$B$K$h$C$F$O\e(B
+
+        mailto:<#R#>
+
+\e$B$r\e(B click \e$B$7$F\e(B message \e$B$r:n@.$9$k;v$b=PMh$k$G$7$g$&!#\e(B
+</#E/>
+
+\e$B5.J}$N\e(B address \e$B$,9XFI<T$+H]$+$O\e(B check \e$B$7$F$$$^$;$s!#\e(B
+\e$B$I$N\e(B address \e$B$G9XFI$r$7$F$$$k$N$+$O!"\e(Bmailing list \e$B$+$i\e(B
+\e$BAw$i$l$F$/$k\e(B message \e$B$N\e(B "Return-Path:" \e$B$N=j$KKd$a9~$^$l$F\e(B
+\e$B$$$^$9!#Nc$($P!"\e(Bmary@xdd.ff.com \e$B$,9XFI$N\e(B address \e$B$J$i\e(B
+
+        Return-Path: <<#L#>-return-<number>-mary=xdd.ff.com@<#H#>>
+
+\e$B$H$$$&6q9g$$$G$9!#\e(B
+
+</#qE/>
+\e$B4D6-$K$h$C$F$O!"D9$$\e(B address \e$B$r$&$^$/07$($J$$>l9g$,M-$j$^$9!#\e(B
+\e$B$3$N\e(B message \e$B$K\e(B reply \e$B$r$&$^$/JV$;$J$$MM$J$i!"0J2<$N\e(B address
+
+        <<#L#>-request@<#H#>>
+
+\e$B$K\e(B "Subject: <#R#>" \e$B$rIU$1$FAw$C$F2<$5$$!#\e(B
+
+</text/unsub-nop#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$O\e(B mailing list <#L#> \e$B$N\e(B
+\e$B9XFI<T$G$O$"$j$^$;$s!#\e(B
+
+        -       -       -       -       -       -       -       -
+
+\e$B9XFI$r;_$a$?$K$b4X$o$i$:!"\e(Bmailing list <#L#>
+\e$B$+$i$N\e(B mail \e$B$,FO$/>l9g$O!"5.J}$,8=:_;H$C$F$$$k\e(B e-mail address \e$B$H$O\e(B
+\e$B0c$&\e(B address \e$B$G\e(B mailing list <#L#> \e$B$r\e(B
+\e$B9XFI$7$F$$$k$N$+$b$7$l$^$;$s!#\e(B
+
+hearder \e$B$N0J2<$NItJ,$K\e(B
+
+'Return-Path: <<#L#>-return-1234-user=host.dom@<#H#>>'
+
+\e$B5.J}$,$I$N\e(B address \e$B$G9XFI$r$7$F$$$k$N$+$,8=$l$F$$$^$9!#\e(B
+\e$B!J$3$N>l9g!"\e(Buser@host.dom \e$B$G9XFI$r$7$F$$$k$3$H$K$J$j$^$9!K\e(B
+
+address \e$B$rL@<($7$F9XFICf;_$N\e(B request \e$B$r=P$9>l9g$O!"0J2<$NMM$K\e(B
+
+        <<#L#>-unsubscribe-user=host.dom@<#H#>>
+
+'<#L#>-unsubscribe-' \e$B$N8e$m$K!"\e(Buser@host.dom \e$B$r\e(B user=host.dom \e$B$K\e(B
+\e$B=q$-49$($?J*$r7R$$$G2<$5$$!#\e(B
+
+\e$B4v$D$+$N\e(B Mail User Agent (MUA) \e$B$O!"\e(BReturn-Path \e$B$r8+$k$?$a$K$O\e(B
+\e$BFCJL$JA`:n$rI,MW$K$J$j$^$9!#\e(B
+
+        * Eudora 4.0 \e$B$@$H!X\e(BBlah blah ...\e$B!Y%\%?%s$r\e(B click \e$B$9$k\e(B
+        * PMMail \e$B$@$H\e(B Window menu \e$B$N!X\e(BShow entire message/header\e$B!Y\e(B
+          \e$B%\%?%s$r\e(B click \e$B$9$k\e(B
+
+        -       -       -       -       -       -       -       -
+
+address \e$B$rL@<($7$?9XFICf;_$b$&$^$/$$$+$J$$>l9g$O!"?=$7Lu$"$j$^$;$s$,\e(B
+
+        * \e$B5.J}$,2?$r$7$h$&$H$7$F<:GT$7$?$N$+\e(B
+        * ezmlm \e$B$+$i$N\e(B message
+        * \e$B9XFICf;_$r4uK>$9$k\e(B address
+
+\e$B$rE:IU$7$F!"\e(Bmailing list <#L#> \e$B$N\e(B owner \e$B$N\e(B
+
+    <#L#>-owner@<#H#>
+
+\e$BKxAw$C$F2<$5$$!#!JJV;v$K;~4V$,$+$+$k$+$b$7$l$^$;$s$,$4N;>52<$5$$!K\e(B
+
+</text/unsub-ok#E/>
+\e$B0J2<$N\e(B address
+
+!A
+
+\e$B$r\e(B mailing list <#L#> \e$B$N9XFI<T$+$i30$7$^$7$?!#\e(B
+
+</text/edit-do#nE/>
+\e$BE:IU$5$l$?\e(B message text \e$B$rJT=8$7$F!"0J2<$N\e(B address
+
+!R
+
+\e$B08$KJVAw$7$F2<$5$$!#\e(B
+
+\e$B!X\e(B%%%\e$B!Y$G;O$^$k9T$,\e(B message text \e$B3+;O$N\e(B marker line \e$B$K\e(B
+\e$B$J$C$F$$$^$9!#\e(B
+
+\e$B!J$3$N9T$K5.J}$N;H$C$F$$$k\e(B Mail User Agent (MUA) \e$B$G$N\e(B
+  \e$B0zMQ$N0u\e(B (ex. foo>%%%) \e$B$,IU2C$5$l$F$$$k>l9g$O!"\e(Bmessage text
+  \e$BK\BN$NItJ,$KIU$$$?0zMQ$N0u$O<+F0=|5n$5$l$^$9!K\e(B
+
+</text/edit-list#nE/>
+\e$B4IM}<T$O\e(B <#L#>-edit.FILE command \e$B$r;H$&$3$H$G\e(B ezmlm \e$B$,=PNO$9$k\e(B
+mailing list <#L#> \e$B$N0Y$N\e(B message text \e$B$rJT=8$9$k$3$H$,=PMh$^$9!#\e(B
+
+\e$B0J2<$OJT=82DG=$J\e(B FILE \e$B$H!"$=$NL\E*$G$9!#\e(Bfile FOO \e$B$rJT=8$7$?$$\e(B
+\e$B>l9g$O\e(B <#L#>-edit.FOO \e$B08$K6u$N\e(B message \e$B$rAw$C$F2<$5$$!#\e(B
+\e$BJT=8$N;EJ}$OAw?.$5$l$k\e(B message \e$B$K4^$^$l$F$$$^$9!#\e(B
+
+File                Use
+
+bottom              ezmlm \e$B$N\e(B command \e$B0lMw$G$9!#\e(B
+                    ezmlm \e$B$,JVAw$9$kA4$F$N\e(B message \e$B$KIU2C$5$l$^$9!#\e(B
+digest              digest \e$BHG$N0Y$N\e(B command \e$B$N@bL@$G$9!#\e(B
+faq                 mailing list <#L#> \e$B$G$N\e(B FAQ \e$B$G$9!#\e(B
+get_bad             archive \e$BCf$K;XDj$5$l$?\e(B message \e$B$r8+IU$1$i$l$J$+$C$?>l9g$K\e(B
+                    \e$BAw$i$l$^$9!#\e(B
+help                help message \e$B$G$9!J\e(B'top' \e$B$H\e(B 'bottom' \e$B$N4V$KF~$j$^$9!K\e(B
+info                mailing list <#L#> \e$B$N>pJs$G$9!#\e(B
+mod_help            \e$B4IM}<T8~$1$N\e(B help message \e$B$G$9!#\e(B
+mod_reject          \e$B4IM}<T$,<uM}$7$J$+$C$?\e(B post \e$B$NAw$j<g$KBP$7$FAw$i$l$^$9!#\e(B
+mod_request         moderated \e$B$J\e(B mailing list \e$B$KBP$7$F\e(B post \e$B$,$"$C$?>l9g\e(B
+                    \e$B4IM}<T$KAw$i$l$^$9!#\e(B
+mod_sub             \e$B9XFI4uK><T$KBP$7$F!"4IM}<T$,9XFI\e(B request \e$B$r<uM}$7$?;~$KAw$i$l$^$9!#\e(B
+mod_sub_confirm     \e$B4IM}<T$KBP$7$F9XFI\e(B request \e$B$N3NG'$N$?$aAw$i$l$^$9!#\e(B
+mod_timeout         \e$B4IM}<T$,<uM}$;$:;~4V@Z$l$K$J$C$?\e(B post \e$B$NAw?.<T$KAw$i$l$^$9!#\e(B
+mod_unsub_confirm   \e$B4IM}<T$KBP$7$F9XFICf;_$N3NG'$r<h$k>l9g$KAw$i$l$^$9!#\e(B
+sub_bad             \e$B9XFI4uK><T$+$i$N9XFI3NG'$N0Y$N1~Ez$,IT@5$J>l9g$KAw$i$l$^$9!#\e(B
+sub_confirm         \e$B9XFI4uK><T$KBP$7$F!"3NG'$N$?$a$KAw$i$l$^$9!#\e(B
+sub_nop             \e$B4{$K9XFI<T$K$J$C$F$$$k?M$,:F$S9XFI\e(B request \e$B$r=P$7$?>l9g$K\e(B
+                    \e$BAw$i$l$^$9!#\e(B
+sub_ok              \e$B9XFI$N<jB3$-$,=*N;$7$?8e$KAw$i$l$^$9!#\e(B
+top                 ezmlm \e$B$,Aw?.$9$kA4$F$N1~Ez$NF,$KIU$-$^$9!#\e(B
+</#tnE/>
+trailer             \e$B$3$N\e(B mailing list \e$B$KBP$9$kA4$F$N\e(B post \e$B$NKvHx$K\e(B
+                    \e$BIU$1B-$5$l$FAw$i$l$^$9!#\e(B
+</#nE/>
+unsub_bad           \e$B9XFI<T$+$i$N9XFICf;_3NG'$,IT@5$J>l9g$KAw$i$l$^$9!#\e(B
+unsub_confirm       \e$B9XFI<T$+$i$N9XFICf;_$N\e(B request \e$B$N3NG'$N$?$a$KAw$i$l$^$9!#\e(B
+unsub_nop           \e$B9XFI<T0J30$,9XFI$rCf;_$7$h$&$H$7$?;~$KAw$i$l$^$9!#\e(B
+unsub_ok            \e$B9XFI<T$+$i$N9XFICf;_$N\e(B request \e$B$,<uM}$5$l$?;~$KAw$i$l$^$9!#\e(B
+
+</text/edit-done#nE/>
+message text \e$B$N99?7$K@.8y$7$^$7$?!#\e(B
+
+</text/info#E/>
+--- mailing list <#L#>@<#H#> \e$B$K$D$$$F\e(B ---
+\e$B!J8=:_9);vCf$G$9!K\e(B
+</text/faq#E/>
+--- mailing list <#L#>@<#H#> \e$B$K4X$7$F!"$h$/?R$M$i$l$k<ALd$H$=$NEz$(\e(B ---
+\e$B!J8=:_9);vCf$G$9!K\e(B
+
diff --git a/ezmlmrc.pl b/ezmlmrc.pl
new file mode 100644 (file)
index 0000000..58df48a
--- /dev/null
@@ -0,0 +1,1106 @@
+#$Id: ezmlmrc.pl,v 1.19 1999/05/11 03:28:11 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# English ---> polish translation by Sergiusz Paw³owicz, 1998.
+# Translation (C) to Free Software Foundation, 1998.
+# Wszelkie uwagi dotycz±ce t³umaczenia proszê przesy³aæ na adres
+#                                        cheeze@hyperreal.art.pl
+# Troche opisow po polsku znajdziecie pod adresem
+#                        http://www.arch.pwr.wroc.pl/~ser/lists/
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Explicitly specify character-set, when this ezmlmrc was used.
+ISO-8859-2
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Przykro mi, ale nakazano mi odrzucaæ Twoje listy... Skontaktuj siê, proszê, z opiekunem listy, osi±galnym pod adresem: <#L#>-owner@<#H#>, je¿eli masz jakiekolwiek w±tpliwo¶ci co do tego stanu rzeczy (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Przykro mi, ale tylko prenumeratorzy mog± wysy³aæ pocztê na listê... Jesli jeste¶ pewna/pewien prenumerowania tej w³a¶nie listy, prze¶lij, proszê, kopiê tego listu opiekunowi: <#L#>-owner@<#H#>, aby dopisa³ on Twój nowy adres pocztowy (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+List-Post: <mailto:<#L#>@<#H#>>
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+---------------------------------------------------------------------
+Wypisanie?     wy¶lij list: <#L#>-unsubscribe@<#H#>
+Wiêcej pomocy? wy¶lij list: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- Poni¿ej znajduj± siê komendy menad¿era list dyskusyjnych "ezmlm".
+
+Spe³niam komendy administracyjne automatycznie.
+Wy¶lij jedynie pusty list pod którykolwiek z tych adresów:
+
+   <<#L#>-subscribe@<#H#>>:
+   Zapisanie siê na listê o nazwie <#L#>.
+
+   <<#L#>-unsubscribe@<#H#>>:
+   Wypisanie siê z listy o nazwie <#L#>.
+
+Wy¶lij list pod nastêpuj±ce adresy, je¶li chcesz uzykaæ krótk± notkê
+informacyjn± o li¶cie dyskusyjnej lub jej FAQ (czyli 'najczê¶ciej
+zadawana pytania na temat listy wraz z odpowiedziami'):
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#d/>
+I odpowiadaj±ce powy¿szemu adresy, je¶li chodzi ci o przegl±d listy:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+   <<#L#>-get.12_45@<#H#>>:
+   Chêæ otrzymania kopii listów od nr 12 do 45 z archiwum.
+   Jednym listem mo¿esz otrzymaæ maksymalnie 100 listów.
+
+</text/bottom#aI/>
+   <<#L#>-get.12@<#H#>>:
+   Chêæ otrzymania kopii listu nr 12 z archiwum.
+</text/bottom#i/>
+   <<#L#>-index.123_456@<#H#>>:
+   Chêæ otrzymania tytu³ów listów od nr 123 do 456 z archiwum.
+   Tytu³y s± wysy³ane do Ciebie w paczkach po sto na jeden list.
+   Mo¿esz maksymalnie zamówiæ 2000 tytu³ów na jeden raz.
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+   <<#L#>-thread.12345@<#H#>>:
+   Chêæ otrzymania wszystkich kopii listów z tym samym tytu³em
+   jaki posiada list o numerze 12345.
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+
+NIE WYSY£AJ POLECEÑ ADMINISTRACYJNYCH NA LISTÊ DYSKUSYJN¡!
+Jesli to zrobisz, administrator listy ich nie zobaczy, natomiast
+wszyscy prenumeratorzy nie¼le siê na Ciebie wkurz± i zostaniesz
+uznany(a) za internetowego ¿ó³todzioba, z którym nie warto gadaæ.
+
+Aby okresliæ adres Bóg@niebo.prezydent.pl jako swój adres subskrybcyjny,
+wy¶lij list na adres:
+<<#L#>-subscribe-Bóg=niebo.prezydent.pl@<#H#>>.
+Wy¶lê Ci wtedy potwierdzenie na ten w³asnie adres; kiedy je otrzymasz,
+po prostu odpowiedz na nie, aby staæ siê prenumeratorem.
+
+</text/bottom#x/>
+Je¿eli instrukcje, których Ci udzieli³em, s± zbyt trudne, albo
+nie jeste¶ zadowolony z ich efektów, skontaktuj siê z opiekunem listy
+pod adresem <#L#>-owner@<#H#>. 
+Proszê byæ cierpliwym, opiekun listy jest o wiele wolniejszy
+od komputera, którym ja jestem ;-)
+</text/bottom/>
+
+--- Za³±cznik jest kopi± polecenia, które otrzyma³em.
+
+</text/bounce-bottom/>
+
+--- Za³±cznik jest kopi± odrzuconego listu, który dosta³em.
+
+</text/bounce-num/>
+
+Na wszelki wypadek przechowujê odrzucone listy z twojego adresu,
+wys³ane na listê o nazwie <#L#>. Aby dostaæ kopiê tych listów
+z archiuwm, np. listu o numerze 12345, wyslij pust± wiadomosæ
+na adres: <#L#>-get.12345@<#H#>.
+
+</#ia/>
+Aby zamówiæ listê tytu³ów i autorów stu ostatnich wiadomo¶ci,
+wy¶lij pusty list na adres: <#L#>-index@<#H#>.
+
+Aby dostaæ pêczek listów od 123 do 131 (maksymalnie 100 na jeden raz),
+wy¶lij pust± wiadomosæ na adres:
+<#L#>-get.123_145@<#H#>.
+
+<//>
+Poni¿ej znajduj± sie numery listów:
+
+</text/dig-bounce-num/>
+
+Trzymam indeks przesy³ek, które nie zosta³y prawid³owo dorêczone
+pod Twój adres z listy <#L#>-digest. Mam zachowany numer pierwszego
+listu ka¿dego przegl±du, z którym mia³e¶ k³opoty. Mo¿esz wiêc
+spróbowaæ dostaæ listy z archiwum g³ównego.
+
+Aby dostaæ list nr 12345 z archiwum, wy¶lij pusty list na
+adres: <#L#>-get.12345@<#H#>.
+
+</#ia/>
+Aby dostaæ listê tytu³ów i autorów ostatnich stu listów, wy¶lij
+pust± wiadomosc na adres: <#L#>-index@<#H#>.
+
+Aby dostaæ pêczek listów od numeru 123 do 145 (maksymalnie sto naraz)
+wy¶lij pust± wiadomosæ pod adres: <#L#>-get.123_145@<#H#>.
+
+<//>
+Poni¿ej znajdziesz numery przegl±dów listy:
+
+</text/bounce-probe/>
+
+Jeste¶ zapisany(a) na listê dyskusyjn± o nazwie <#l#>,
+niestety listy z niej nie mog± do Ciebie dotrzeæ, gdy¿ s± odrzucane
+przez twój serwer. Wys³a³em Ci ostrze¿enie o tym fakcie,
+ale i ono przepad³o... Nie miej wiêc do mnie ¿alu, je¶li
+zmuszony bêdê wykresliæ Ciê z listy prenumeratorów, a nast±pi
+to w przypadku, je¶li i ten list nie zostanie przyjêty przez Twój
+serwer pocztowy.
+
+</text/bounce-warn/>
+
+Poczta kierowana do Ciebie z listy <#l#> jest odrzucana przez Twój serwer.
+Za³±czam kopiê pierwszej przesy³ki, z dostarczeniem której mia³em k³opoty.
+
+Je¿eli niniejszy list ostrzegawczy równie¿ przepadnie, wy¶lê Ci
+przesy³kê testow±, której odbicie siê od Twojego adresu spowoduje
+trwa³e wypisanie z pocztowej listy dyskusyjnej.
+
+</text/digest#ia/>
+Aby zaprenumerowaæ przegl±d listy, napisz pod adres:
+       <#L#>-digest-subscribe@<#H#>
+
+Aby wykre¶liæ swoój adres pocztowy z listy subskrybentów, napisz pod adres:
+       <#L#>-digest-unsubscribe@<#H#>
+
+Aby wys³aæ cokolwiek na listê, pisz pod adres:
+       <#L#>@<#H#>
+
+</#iax/>
+       Opiekun listy pocztowej - 
+        - <<#L#>-owner@<#H#>>
+
+</text/get-bad/>
+Przykro mi, tego listu nie ma w archiwum.
+
+</text/help/>
+Niestety nie poda³e¶ prawid³owego adresu prenumeraty.
+List, który otrzyma³e¶, jest niczym innym, jak standardow± instrukcj±
+obs³ugi menad¿era list dyskusyjnych zainstalowanego na naszym serwerze.
+Mam nadziejê, ¿e teraz ju¿ sobie poradzisz.
+
+</text/mod-help/>
+Dziêkujê za zgodê na zostanie moderatorem listy o adresie:
+<#L#>@<#H#>.
+
+Moje komendy s± nieco inne, ni¿ bywaj± w programach obs³uguj±cych
+pocztowe listy dyskusyjne. Mo¿esz nawet na pocz±tku stwierdziæ, ¿e s±
+niezwyk³e, ale jak tylko zaczniesz ich u¿ywaæ, docenisz prostotê systemu
+oraz szybko¶æ, z jak± obs³ugujê twoje ¿yczenia.
+
+Poni¿ej kilka s³ów o tym, jak dzia³a moderowanie: 
+
+Zdalne wpisanie na listê prenumeratorów
+---------------------------------------
+Bêd±c moderatorem, mo¿esz zapisaæ lub wypisaæ internautê
+o adresie janek@komputer-janka.domena poprzez wys³anie listu na adres:
+
+<#L#>-subscribe-janek=komputer-janka.domena@<#H#>
+<#L#>-unsubscribe-janek=komputer-janka.domena@<#H#>
+
+</#g/>
+Odpowiednio dla przegl±du listy:
+
+<#L#>-digest-subscribe-janek=komputer-janka.domena@<#H#>
+<#L#>-digest-unsubscribe-janek=komputer-janka.domena@<#H#>
+
+<//>
+To wszystko. Nie musisz nadawaæ listowi tytu³u, nie musisz
+równie¿ pisaæ nic w jego tre¶ci!
+
+</#r/>
+Wy¶lê Ci pro¶bê o potwierdzenie 
+Co zrobiæ z moim listem?
+Po prostu odpowiedz na niego i gotowe.
+</#R/>
+Wy¶lê pro¶bê o potwierdzenie chêci uczestnictwa na adres
+autora listu.
+Wystarczy, ze na te pro¶be odpowie, a bedzie zapisany.
+<//>
+
+Zawiadomiê subskrybenta, je¿eli jego/jej dane dotyczace
+jego subskrybcji zostan± zmienione.
+
+Prenumerata
+-----------
+
+Ka¿dy mo¿e zapisaæ siê na listê, albo z niej wypisaæ, poprzez
+wys³anie listu na adres:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#g/>
+Obs³uga przegl±du listy:
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+Chêtny otrzyma pro¶be o potwierdzenie ¿yczenia wypisania siê z listy,
+aby siê upewniæ czy ¿yczenie to zosta³o rzeczywiscie przez niego
+wys³ane. Je¿eli zostanie to potwierdzone, zostanie usuniêty 
+z listy prenumeratorów.
+Podobna procedura obowi±zuje podczas zapisywania siê na listê.
+
+</#s/>
+Podczas procedury zapisywania siê na listê, pro¶ba
+o potwierdzenie zapisu jest wysy³ana równie¿ do moderatora listy.
+Pozytywna odpowied¼ moderatora dope³ni zapisu na listê.
+</#S/>
+Zapisy funkcjonuj± w ten sam sposób.
+<//>
+
+U¿ytkownik mo¿e równie¿ u¿ywaæ adresów:
+
+<#L#>-subscribe-janek=komputer-janka.domena@<#H#>
+<#L#>-unsubscribe-janek=komputer-janka.domena@<#H#>
+
+aby poczta z listy wêdrowa³a na inny adres bed±cy jego w³asno¶ci±.
+Tylko wówczas, kiedy u¿ytkownik dostaje listy na adres
+janek@komputer-janka.domena, bêdzie w stanie odpowiedzieæ
+na ¿yczenie potwierdzenia skierowanego od menad¿era listy.
+
+Wszystkie powy¿sze kroki s± przedsiêbrane po to, by unikaæ
+z³o¶liwo¶ci typu zapisanie kogo¶ wbrew jego woli na listê
+oraz aby moderator móg³ byæ pewnym, ¿e adres prenumeratora
+rzeczywi¶cie istnieje.
+
+Twój rzeczywisty adres pocztowy nie bêdzie ujawniony prenumaratorowi.
+Pozwoli to zachowaæ Ci anonimowo¶æ.
+
+</#rl/>
+Aby uzyskaæ listê prenumeratorów <#L#>@<#H#>, 
+wy¶lij list na adres:
+   <<#L#>-list@<#H#>>
+
+Chc±c uzyskaæ dziennik pok³adowy listy <#L#>@<#H#>,
+wy¶lij list na adres:
+   <<#L#>-list@<#H#>>
+
+</#rld/>
+Adresy w³a¶ciwe dla przegl±du listy:
+   <<#L#>-digest-list@<#H#>>
+i:
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+Mo¿esz zdalnie zmieniaæ pliki tekstowe, którymi pos³uguje siê automat
+podczas obs³ugi listy dyskusyjnej. Aby uzyskaæ zbiór plików oraz
+instrukcje, jak je zmieniaæ, napisz pod adres:
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+Przesy³ki moderowane
+--------------------
+Je¿eli przesy³ki podlegaj± moderowaniu, umieszczam wys³ane listy
+w kolejce pocztowej i wysy³am ¿yczenie przyjrzenia sie listowi do
+moderatora.
+
+Wystarczy, aby¶ odpowiedzia³ na adres zwrotny tego listu, u¿ywaj±c
+funkcji "odpowiedz (reply)", aby zaakceptowaæ tre¶æ listu. 
+
+Je¿eli natomiast chcesz odmówiæ zgody na wys³anie listu, wy¶lij
+pocztê na adres wziêty z nag³ówka "From:" (mo¿na zwykle tego
+dokonaæ poprzez u¿ycie opcji "reply-to-all/odpowiedz-na-wszystkie-
+-adresy" i skasowanie z tej listy w³asnego adresu oraz adresu 
+akceptuj±cego). Mo¿esz równie¿ dodaæ jak±¶ notkê do wysy³aj±cego
+odrzucony przez Ciebie list, aby wyt³umaczyæ mu, dlaczego to
+uczyni³e¶. Notkê dodajesz umieszczaj±c j± w tre¶ci powy¿szego listu
+odrzucaj±cego, wstawiaj±c j± pomiêdzy dwie linie, zawieraj±ce
+trzy znaki '%'.
+
+Wezmê pod uwagê pierwszy list, który od Ciebie dostanê.
+Je¿eli wy¶lesz najpierw potwierdzenie akceptacji listu, który
+wcze¶niej odrzuci³e¶, lub odwrotnie, oczywi¶cie Ci o tym powiem.
+
+Je¿eli nie dostanê odpowiedzi od moderatora przez okre¶lony czas,
+zwykle jest to piêæ dni, zwrócê list nadawcy z opisem zaistnia³ej
+sytuacji.
+<//>
+
+Wakacje
+-------
+Je¿eli korzystasz tymczasowo z innego adresu, po prostu przekieruj
+wszystkie listy z nag³ówkiem 'Mailing-List:',
+albo te, których tematy zaczynaj± siê s³owami
+"MODERATE for <#L#>@<#H#>",
+lub
+"CONFIRM subscribe to <#L#>@<#H#>",
+na nowy adres.
+Mo¿esz wtedy moderowaæ z nowego adresu. Innym sposobem jest
+przekierowanie tych listów do przyjaciela, który mo¿e Ciê zast±piæ.
+Musisz oczywi¶cie uzgodniæ przedtem wszystko z opiekunem listy.
+
+Je¿eli chcesz automatycznie aprobowaæ wszelkie przesy³ki kierowane
+na listê, skonfiguruj swój program pocztowy tak, aby automatycznie
+odpowiada³ na listy maj±ce tematy zgodne z powy¿szymi kryteriami.
+
+</#r/>
+Uwa¿aj, je¿eli bêdziesz próbowa³ zdalnie zarz±dzaæ list± z adresu,
+który nie jest wpisany jako adres administratora, prenumerator,
+a nie Ty, zostanie poproszony o potwierdzenie, które zostanie
+potem przekazane do moderatorów.
+Robiê tak, gdy¿ nie ma sposobu upewniæ siê czy to rzeczywi¶cie Ty
+kryjesz siê za poczt± przychodz±c± z nieznanego adresu.
+
+Pamiêtaj, ¿e w powy¿szym wypadku Twoje ¿ycznie skierowane do serwera
+i równie¿ Twój adres s± przesy³ane do prenumeratora! Przestajesz byæ
+wówczas anonimowy.
+<//>
+
+Powodzenia!
+
+PS: Skontaktuj siê, proszê, z opiekunem listy pod adresem
+<#L#>-owner@<#H#>
+je¿eli masz jakie¶ pytania, lub napotkasz na problemy.
+
+</text/mod-reject/>
+Przykro mi, ale Twój list nie zosta³ zaakceptowany przez moderatora.
+Je¿eli moderator uczyni³ jakie¶ uwagi, zamieszczam je poni¿ej.
+</text/mod-request/>
+Proszê o decyzjê czy b±d±c moderatorem zgadzasz siê na wys³anie
+za³±czonej przesy³ki na listê o nazwie <#L#>.
+
+Aby zgodziæ siê na natychmiastowe wys³anie listu do wszystkich
+prenumeratorów, wy¶lij, proszê, list na adres:
+
+# This does the default !A for normal setups, but puts in a 'mailto:address'
+# for lists created with the -x switch.
+!A
+</#x/>
+
+mailto:<#A#>
+# Below is a minimalist tag. It just means that succeeding lines should be
+# added to the currently open file in all cases.
+<//>
+
+Aby odmówiæ wys³ania listu, i zwróciæ go nadawcy, proszê
+przes³aæ wiadomosæ na adres: 
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Nie musisz za³±czaæ kopii listu, który akceptujesz (lub nie) do
+wys³ania na listê. Je¿eli chcesz przes³aæ jak±¶ notkê do nadawcy
+odrzuconego listu, zawrzyj j± pomiêdzy dwie linie zaczynaj±ce siê
+trzema znakami procentu ("%").
+
+%%% Pocz±tek notki
+%%% Koniec notki
+
+Dziêkujê za pomoc!
+
+--- W za³±czniu mo¿esz znale¼æ wys³any list.
+
+</text/mod-sub#E/>
+--- zapisa³em Ciê (lub wypisa³em) z listy
+<#l#>@<#H#>
+na ¿yczenie jej moderatora.
+
+Je¶li nie jest to dzia³anie, którego pragn±³e¶, mo¿esz
+przes³aæ swoje w±tpliwo¶ci do opiekuna listy:
+<#l#>-owner@<#H#>
+
+Je¿eli potrzebujesz informacji o tym, jak dostaæ siê do archiwum
+listy <#L#>, prze¶lij pusty list na adres:
+<#L#>-help@<#H#>
+
+</text/mod-timeout/>
+Przykro mi, ale moderatorzy nie zareagowali na Twój list.
+Dlatego zwracam go Tobie, je¿eli uwa¿asz, ¿e kryje siê za tym
+jaki¶ b³±d maszyny, wy¶lij list ponownie, albo skontaktuj siê
+bezpo¶rednio z moderatorem listy.
+
+--- W za³±czeniu Twoja oryginalna przesy³ka na listê.
+
+</text/mod-sub-confirm/>
+Bardzo proszê o zgodê na dodanie internauty o adresie:
+
+<#A#>
+
+jako prenumeratora listy dyskusyjnej <#l#>.
+Poniewa¿ powy¿sze ¿yczenie mog³o nie pochodziæ od Ciebie (patrz
+koniec listu), ju¿ zd±¿y³em potwierdziæ, ¿e internauta o adresie:
+<#A#>
+rzeczywiscie podobne ¿yczenie wys³a³.
+
+Aby udzieliæ zgody, proszê wys³aæ pust± odpowiedz na adres:
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Twój program pocztowy powienien automatycznie prawid³owo zaadresowaæ
+list, je¿eli u¿yjesz opcji "Odpowiedz/Reply".
+
+Je¿eli natomiast siê nie zgadzasz, po prostu zignoruj ten list.
+
+Dziekujê za pomoc!
+
+</text/mod-unsub-confirm/>
+Bardzo proszê o Twoj± zgodê na usuniêcie internauty o adresie:
+
+!A
+
+z listy prenumeratorów listy <#l#>. 
+Je¿eli siê zgadzasz, wyslij, proszê, pust± odpowiedz na adres:
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Twój program pocztowy powienien automatycznie prawid³owo zaadresowaæ
+list, je¿eli u¿yjesz opcji "Odpowiedz/Reply".
+
+Dziekujê za pomoc!
+
+</text/sub-bad/>
+Hmmm, numer potwierdzenia wydaje mi siê nieprawid³owy.
+
+Najczêstsz± przyczyn± nieprawid³owo¶ci jest jego przeterminowanie.
+Muszê dostaæ potwierdzenie ka¿dego ¿ycznia w ci±gu dziesiêciu dni.
+Mo¿esz równie¿ upewniæ siê czy za³±czy³e¶(a¶) ca³y numer potwierdzenia
+w poprzednim liscie, niektóre programy pocztowe maj± z³y nawyk
+ucinania adresów, które wydaj± im siê zbyt d³ugie.
+
+Przygotowa³em nowy numer potwierdzenia. Aby ponownie potwierdziæ
+chêæ zapisania siê z adresu:
+
+!A
+
+na listê dyskusyjn± <#L#>, wy¶lij pust± odpowied¼
+pod adres:
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Pamiêtaj, sprawd¼ dok³adnie czy zalaczy³e¶(a¶) pe³ny numer, oczywi¶cie
+uczyñ to przed wys³aniem listu ;-)
+
+Przepraszam za k³opoty.
+Opiekun listy -
+</#x/>
+<#L#>-owner@<#H#>
+<//>
+
+</text/sub-confirm/>
+Aby potwierdziæ chêæ zapisania siê z adresu:
+
+!A
+
+na listê dyskusyjn± o nazwie <#L#>,
+wy¶lij, proszê, pust± odpowied¼ pod adres:
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Twój program pocztowy powienien automatycznie zaadresowaæ przesy³kê,
+je¿eli u¿yjesz opcji "odpowiedz/reply".
+
+Potwierdzenie ma na celu
+a) sprawdzenie siê czy potrafiê wysy³aæ listy na adres przez Ciebie podany,
+b) upewnienie siê czy ktos nie zrobi³ Ci g³upiego dowcipu, zapisuj±c Ciê
+   bez twojej wiedzy na nasz± listê.
+
+</text/sub-confirm#s/>
+Lista jest moderowana. Kiedy wys³a³e¶ ju¿ potwierdzenie, chêæ prenumeraty
+zosta³a przes³ana moderatorowi. Zawiadomiê Ciê odrêbnym listem, je¶li
+zostaniesz wpisany(a) na listê prenumeratorów.
+
+</text/sub-nop/>
+
+Potwierdzenie: Adres poczty elektronicznej:
+
+!A
+
+jest ju¿ na li¶cie prenumeratorów <#L#>. By³ on ju¿ na li¶cie
+przed wys³aniem ponownej pro¶by, a wiêc j± zignorujê.
+
+</text/sub-ok#E/>
+Potwierdzenie: Doda³em adres
+
+!A
+
+do listy prenumeratorów <#L#>.
+
+**** WITAJ NA LI¦CIE <#L#>@<#H#>!
+
+Proszê o zachowanie tej przesy³ki, bêdziesz dziêki niej pamiêta³,
+z którego dok³adnie adresu jestes zapisany(a), informacja ta bêdzie
+niezbêdna, je¶li chcia³(a)by¶ kiedy¶ z listy siê wypisaæ lub zmieniæ
+adres korespondencyjny.
+
+</text/top/>
+Czesæ! Nazywam siê "ezmlm" i zarz±dzam serwerem pocztowych
+list dyskusyjnych, miêdzy innymi list± o nazwie:
+<#L#>@<#H#>
+
+</#x/>
+Jestem komputerem, bezdusznym s³ug± opiekuna listy, cz³owieka :-),
+którego mo¿na znale¼æ pod adresem:
+<#L#>-owner@<#H#>.
+
+</text/unsub-bad/>
+Hmmm, numer potwierdzenia wydaje mi siê nieprawid³owy.
+
+Najczêstsz± przyczyn± nieprawid³owo¶ci jest przeterminowanie,
+muszê dostaæ potwierdzenie ka¿dego ¿ycznia w ci±gu dziesiêciu dni.
+Mo¿esz równie¿ upewniæ siê czy za³±czy³e¶(a¶) ca³y numer potwierdzenia
+w poprzednim li¶cie, niektóre programy pocztowe maj± z³y nawyk
+ucinania adresów, które wydaj± im siê zbyt d³ugie.
+
+Przygotowa³em nowy numer potwierdzenia. Aby ponownie potwierdziæ
+¿e mam usun±æ adres
+
+!A
+
+z listy prenumeratorów <#l#>, wy¶lij pust± odpowiedz na adres:
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Pamiêtaj, sprawd¼ dokladnie czy za³aczy³e¶(a¶) pe³ny numer potwierdzenia,
+uczyñ to oczywi¶cie przed wys³aniem listu ;-)
+
+Przepraszam za k³opoty.
+Opiekun -
+<#L#>-owner@<#H#>
+
+</text/unsub-confirm/>
+Aby potwierdziæ, ¿e chcesz usunac adres
+
+!A
+
+z listy prenumaratorów <#L#>, prze¶lij, proszê, pust±
+odpowied¼ na ten list pod adres:
+
+!R
+</#x/>
+
+mailto:<#R#>
+<//>
+
+Twój program pocztowy powienien automatycznie zaadresowaæ przesy³kê,
+je¿eli u¿yjesz opcji "odpowiedz/reply".
+
+Nie sprawdzi³em jednak czy twój adres znajduje siê na li¶cie
+prenumeratorów. Aby sprawdziæ, którego adresu u¿ywasz jako koresponde-
+cyjnego, spojrzyj na jak±kolwiek wiadomosæ z listy dyskusyjnej.
+Ka¿dy list ma schowany Twój adres w scie¿ce zwrotnej; np.
+Bóg@niebo.prezydent.pl dostaje listy ze scie¿k± zwrotn±:
+<<#l#>-return-<number>-Bóg=niebo.prezydent.pl@<#H#>.
+
+</text/unsub-nop/>
+Powiadomienie: Adresu pocztowego
+
+!A
+
+nie ma na li¶cie prenumeratorów <#l#>. Nie by³o go równie¿
+przed dosteniem Twojego ¿yczenia wykre¶lenia z listy.. 
+
+Je¿eli wypisa³e¶(a¶) siê, lecz nadal dostajesz listy, jeste¶ po prostu
+zapisany(a) z innego adresu, ni¿ ten, którego w³a¶nie u¿ywasz.
+Spojrzyj, proszê, ma nag³ówki któregokolwiek listu z prenumeraty,
+znajdziesz tam frazê:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+W³a¶ciwy adres, na który powinno wiêc trafiæ twoje ¿yczenie wypisania to:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+Po prostu napisz na powy¿szy adres, zamieniaj±c user=host.dom na twoje
+prawdziwe dane, odpowiedz po¼niej na pro¶bê potwierdzenia, 
+powieniene¶(a¶) wówczas dostaæ list, ¿e jeste¶ wykre¶lony(a)
+z listy prenumeratorów.
+
+Je¿eli moje rady nie skutkuj±, niestety nie mogê Ci ju¿ wiêcej pomóc.
+Prze¶lij, proszê, jak±kolwiek przesy³kê z listy wraz z krótk± informacj±,
+co chcesz osi±gn±æ, do opiekuna listy, pod adres:
+
+</#x/>
+mailto:<#L#>-owner@<#H#>
+</#X/>
+    <#l#>-owner@<#H#>
+<//>
+
+który to opiekun siê tym zajmie. We¼ pod uwagê, ¿e cz³owiek jest nieco
+wolniejszy od komputera :-), wiêc b±d¼ cierpliwy(a).
+
+</text/unsub-ok/>
+Zawiadomienie: usun±³em adres pocztowy
+
+!A
+
+z listy prenumeratorów <#l#>.
+Na adres ten nie bêd± ju¿ przychodzi³y przesy³ki z listy.
+
+</text/edit-do#n/>
+Przerób, proszê, za³±czony tekst i wy¶lij go na adres:
+
+!R
+
+Twój program powienien uczyniæ to automatycznie, je¿eli u¿yjesz
+funkcji "reply/odpowiedz".
+
+Mogê bez k³opotu usun±æ dodatki jakie mo¿e dodawaæ do tekstów
+Twój program pocztowy, je¿eli zachowasz linie znaczników.
+
+Linie znaczników s± to linie rozpoczynaj±ce siê "%%%".
+Nie mog± one byæ zmienione, dodatkowe znaki dodane przez program
+pocztowy na pocz±tku linii s± dozwolone.
+
+
+</text/edit-list#n/>
+Pliki <#L#>-edit.nazwa_pliku mog± byæ stosowane przez zdalnego
+administratora do edycji tekstów zarz±dczych listy dyskusyjnej.
+
+Tabela pod spodem jest list± plików wraz z krótkim opisem
+ich dzia³ania. Aby zamieniæ okre¶lony plik, po prostu wy¶lij
+pusty list na adres <#L#>-edit.nazwa_pliku ,
+zamieniaj±c wyra¿enie 'nazwa_pliku' nazw± zamienianego pliku.
+Instrukcja sposobu zamiany pliku zostanie dostarczona wraz ze
+starym plikiem poczt± zwrotn±.
+
+File                Use
+
+bottom              spód ka¿dej odpowiedzi administracyjnej. G³ówne komendy.
+digest              administracyjna czê¶æ przegl±du listy.
+faq                 najczê¶ciej zadawane pytania na naszej li¶cie.
+get_bad             tekst w miejsce listu nie znalezionego w archiwum.
+help                g³ówna tre¶æ pomocy.
+info                informacje o naszej li¶cie.
+mod_help            pomoc dla moderatorów list.
+mod_reject          list przesy³any nadawcy przesy³ki nie zaakceptowanej.
+mod_request         tekst przesy³any razem z pro¶b± moderowania.
+mod_sub             tekst przesy³any prenumeratorowi po zaakceptowaniu.
+mod_sub_confirm     tekst do moderatora z pro¶b± o wpisanie na listê.
+mod_timeout         do nadawcy przterminowanego listu.
+mod_unsub_confirm   do zdalnego opiekuna z pro¶b± o potwierdzenie wypisu.
+sub_bad             do prenumaratora, je¿eli potwierdzenie by³o z³e.
+sub_confirm         do prenumeratora z pro¶b± dokonania potwierdzenia.
+sub_nop             tre¶æ listu powitalnego dla ponownie subskrybuj±cego.
+sub_ok              tre¶æ listu powitalnego dla nowozapisanego.
+top                 pocz±tek ka¿dego listu, ostro¿nie!
+</#tn/>
+trailer             tekst dodawany do ka¿dego listu, ostro¿nie!
+</#n/>
+unsub_bad           do prenumeratora, je¶li jego potwierdzenie jest z³e.
+unsub_confirm       tre¶æ potwierdzenia po ¿yczeniu wypisania siê z listy.
+unsub_nop           do osoby nie bêd±cej prenumeratorem, wypisuj±cej siê.
+unsub_ok            list po¿egnalny do rezygnuj±cego z prenumeraty.
+
+</text/edit-done#n/>
+Plik tekstowy zosta³ szczê¶liwie zamieniony.
+
+</text/info#E/>
+Nie mam ¿adnych informacji na temat listy :-(
+</text/faq#E/>
+FAQ - najczê¶ciej zadawane pytania na li¶cie
+<#l#>@<#H#>.
+
+Jeszcze nic tu nie ma :-(, przepraszamy!
+
+
diff --git a/ezmlmrc.pt b/ezmlmrc.pt
new file mode 100644 (file)
index 0000000..a8532a5
--- /dev/null
@@ -0,0 +1,1133 @@
+0.324 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.pt_BR,v 1.15 1999/05/06 09:26:00 lindberg Exp $
+#$Name:$
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Explicitly specify character-set, when this ezmlmrc was used.
+ISO-8859-1:Q
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sinto muito, eu estou rejeitando as suas mensagens. Contacte <#L#>-owner@<#H#> se você tem questões sobre isto (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sinto muito, somente assinantes podem enviar mensagens. Se você é um assinante, por favor repasse esta mensagem para <#L#>-owner@<#H#> para ter o seu novo endereço incluído (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contacte <#L#>-help@<#H#>; gerenciado pelo ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+list-help: <mailto:<#l#>-help@<#h#>>
+list-unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+list-post: <mailto:<#L#>@<#H#>>
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+---------------------------------------------------------------------
+Para cancelar a subscrição, envie mensagem para: <#L#>-unsubscribe@<#H#>
+Para comandos adicionais, envie mensagem para: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- Aqui estão os endereços de comando do ezmlm.
+
+Eu posso tratar as requisições administrativas automaticamente.
+Basta enviar uma mensagem vazia para qualquer destes endereços:
+
+   <<#L#>-subscribe@<#H#>>:
+   Receber as futuras mensagens enviadas para a lista <#L#>.
+
+   <<#L#>-unsubscribe@<#H#>>:
+   Parar de receber as mensagens da lista <#L#>.
+
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+   Para receber mais informações ou o FAQ desta lista,
+   se disponíveis.
+
+</#d/>
+Existem também estes endereços similares para a lista "digest":
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+   <<#L#>-get.12_45@<#H#>>:
+   Recupera uma cópia das mensagens 12 a 45 do arquivo.
+   No máximo 100 mensagens serão retornadas por requisição.
+
+</text/bottom#aI/>
+   <<#L#>-get.12@<#H#>>:
+   Recupera uma cópia da mensagem 12 do arquivo.
+
+</text/bottom#i/>
+   <<#L#>-index.123_456@<#H#>>:
+   Recupera os assuntos das mensagens número 123 a 456.
+   Assuntos são retornados em grupos de 100, ou seja você receberá
+   os assuntos das mensagens 100 a 499.
+   Um máximo de 2000 assuntos são retornados por requisição.
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+   <<#L#>-thread.12345@<#H#>>:
+   Recupera uma cópia de todas as mensagens com o mesmo assunto
+   da mensagem 12345.
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+Na verdade as mensagens não precisam ser vazias, mas eu irei
+ignorar o seu conteúdo. Somente o ENDEREÇO é importante.
+
+Você pode iniciar a assinatura para um endereço alternativo,
+por exemplo "john@host.domain", bastando adicionar um traço
+e o seu endereço (com '=' no lugar de '@'), ou seja:
+<<#L#>-subscribe-john=host.domain@<#H#>>
+
+Para cancelar a subscrição para este endereço, envie mensagem
+para: <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+Em ambos os casos, eu enviarei uma mensagem de confirmação para
+esse endereço; quando você receber esta mensagem, simplesmente
+responda para completar sua subscrição (ou cancelamento).
+
+</text/bottom/>
+Se estas instruções são muito complicadas ou você não teve os
+resultados que esperava, por favor contacte o meu dono em 
+<#L#>-owner@<#H#>. Mas tenha paciência, o meu dono é
+muito mais lento do que eu ;-)
+</text/bottom/>
+
+--- Abaixo está uma cópia da requisição que eu recebi.
+
+</text/bounce-bottom/>
+
+--- Abaixo está uma cópia da mensagem devolvida que eu recebi.
+
+</text/bounce-num/>
+
+Eu mantenho um controle das mensagens da lista <#L> que não
+foram entregues para o seu endereço.
+</#a/>
+Cópias destas mensagens podem estar no arquivo.
+</#aI/>
+Para pegar a mensagem 12345 do arquivo, envie uma mensagem vazia para
+  <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+Para recuperar uma lista de assuntos e autores das últimas 100
+mensagens, envie uma mensagem vazia para:
+   <<#L#>-index@<#H#>>
+
+Para recuperar as mensagens 123 a 145 (no máximo 100 por requisição),
+envie uma mensagem vazia para:
+   <<#L#>-get.123_145@<#H#>>
+
+<//>
+Aqui estão os números das mensagens:
+
+</text/dig-bounce-num/>
+
+Eu mantenho um controle de quais mensagens da lista <#L#>-digest
+não foram entregues para o seu endereço. Para cada "digest" que você
+não recebeu, eu tenho anotado o número da primeira mensagem do "digest".
+Então você pode pegar as mensagens do arquivo da lista principal.
+
+</#aI/>
+Para recuperar a mensagem 12345 do arquivo, envie uma mensagem vazia para:
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+Para recuperar as mensagens 123 a 145 (no máximo 100 por requisição),
+envie uma mensagem vazia para:
+   <<#L#>-get.123_145@<#H#>>
+
+Para recuperar uma lista de assuntos e autores das últimas 100
+mensagens, envie uma mensagem vazia para:
+   <<#L#>-index@<#H#>>
+
+<//>
+Aqui estão os números dos "digests":
+
+</text/bounce-probe/>
+
+Mensagens da lista <#l#> para você estão sendo devolvidas.
+Eu enviei um aviso para você, que também foi devolvido.
+Eu anexei uma cópia da mensagem devolvida.
+
+Esta é uma verificação para o seu endereço. Se esta verificação
+for devolvida, eu removerei o seu endereço da lista
+<#l#>@<#H#>, sem mais avisos. Você pode assinar novamente a
+lista enviando uma mensagem vazia para o endereço:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn/>
+
+Mensagens da lista <#l#> para você não foram entregues.
+Eu anexei uma cópia da primeira mensagem que eu recebi de volta.
+
+Se esta mensagem também não for entregue, eu enviarei uma última
+verificação. Se a verificação também for devolvida, eu removerei o seu
+endereço da lista <#l#>, sem mais avisos.
+
+</text/digest#d/>
+Para subscrever-se à lista "digest", envie mensagem para:
+       <#L#>-digest-subscribe@<#H#>
+
+Para cancelar a subscrição da lista "digest", envie mensagem para:
+       <#L#>-digest-unsubscribe@<#H#>
+
+Para enviar mensagens para a lista, use o endereço:
+       <#L#>@<#H#>
+
+</text/get-bad/>
+Sinto muito, esta mensagem não está arquivada.
+
+</text/help/>
+Esta é uma mensagem de ajuda genérica. A mensagem que eu recebi 
+não era para os meus endereços de comando.
+
+</text/mod-help/>
+Obrigado por concordar em moderar a lista
+<#L#>@<#H#>.
+
+Meus comandos são um pouco diferentes de outros gerenciadores de
+listas. À primeira vista eles parecem não usuais, mas à medida que
+você os for usando, vai apreciar a simplicidade do sistema e
+a rapidez com que eu trato suas requisições.
+
+Aqui estão algumas observações sobre como atuar como dono da
+lista e/ou moderador:
+
+Subscrição Remota
+-----------------
+Como um moderador, você pode subscrever e cancelar a subscrição
+de qualquer endereço da lista. Para cadastrar john@johnhost.johndomain,
+simplesmente coloque um traço depois do comando, e depois o endereço
+com '=' no lugar de '@'. Por exemplo, para cadastrar este endereço,
+envie uma mensagem para:
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+Você pode analogamente remover esse endereço, enviando mensagem para:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#d/>
+Para a lista "digest":
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+<//>
+Isto é tudo. Nem assunto nem corpo de mensagem são necessários!
+
+</#r/>
+Eu enviarei para você um pedido de confirmação, para ter certeza
+que é realmente você quem enviou a requisição. Simplesmente
+retorne a mensagem, e a requisição será completada.
+</#R/>
+Eu enviarei um pedido de confirmação para o endereço do usuário,
+neste caso <john@host.domain>. Tudo que o usuário tem que fazer é
+retornar este pedido de confirmação.
+<//>
+
+As confirmações são necessárias para evitar que pessoas
+não autorizadas possam cadastrar ou remover endereços
+da lista.
+
+Eu notificarei o usuário quando a situação de sua subscrição
+for alterada.
+
+Subscrição
+----------
+
+Qualquer usuário pode subscrever-se ou cancelar a subscrição
+enviando mensagem para:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#d/>
+Para a lista "digest":
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+<//>
+O usuário receberá um pedido de confirmação para eu ter 
+certeza que ele controla o endereço de subscrição. Quando este
+endereço for verificado, ele será descadastrado.
+
+</#s/>
+Sendo esta lista moderada para subscrição, eu enviarei um segundo
+pedido de confirmação para o(s) moderador(es). Se o usuário
+já tiver confirmado o desejo de ficar na lista, você como
+moderador pode confiar que o endereço do assinante é verdadeiro.
+Se você deseja aprovar os pedidos do usuário, simplesmente
+retorne a mensagem de CONFIRMAÇÃO. Caso contrário, simplesmente
+ignore a minha mensagem ou possivelmente contacte o potencial
+assinante para obter mais informações.
+</#S/>
+Subscrições funcionam da mesma forma.
+<//>
+
+O usuário também pode usar estas formas:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+para que as mensagens sejam enviadas para "mary@host.domain".
+Somente se ela receber mensagens neste endereço, ela será
+capaz de responder a confirmação.
+
+Seu endereço e identidade não serão revelados para o assinante,
+a não ser que você envie mensagens diretamente para o usuário.
+
+</#rl/>
+Para obter o cadastro de assinantes da lista <#L#>@<#H#>,
+envie uma mensagem para:
+   <<#L#>-list@<#H#>>
+
+Para um registro de transações da lista,
+envie uma mensagem para:
+   <<#L#>-log@<#H#>>
+
+</#rld/>
+Para o cadastro de assinantes da lista digest:
+   <<#L#>-digest-list@<#H#>>
+e:
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+Você pode editar remotamente os arquivos-texto de resposta que a lista
+envia. Para obter uma lista dos arquivos e instruções de edição, envie
+mensagem para:
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+Postagens Moderadas
+-------------------
+Quando as postagens são moderadas, eu colocarei as mensagens em 
+uma fila, e enviarei a você uma cópia junto com instruções. A
+mensagem terá "MODERATE for ..." como assunto.
+
+Se você aceita a mensagem, basta responder para o endereço 'Reply-To:',
+o qual já deixei configurado como o endereço de "aceitação".
+Não é necessário incluir a mensagem; na verdade, eu ignorarei qualquer
+coisa que você envie, desde que o endereço para o qual você enviou
+esteja correto.
+
+Se você quiser rejeitá-la, envie a mensagem para o endereço 'From:',
+que por sua vez, está configurado com o endereço de "rejeição".
+Você pode usualmente fazer um 'reply-to-all' - resposta para todos,
+e então remover o seu endereço e o endereço de "aceitação").
+Você pode adicionar um comentário opcional para o remetente entre
+duas linhas começando com três '%'. Eu enviarei este comentário somente
+para o remetente, sem revelar sua identidade.
+
+Eu irei processar a mensagem de acordo com o primeiro retorno que eu
+receber. Se você me enviar uma requisição para aceitar uma mensagem
+que já foi rejeitada ou vice-versa, eu o avisarei.
+
+Se eu não receber nenhuma resposta do moderador dentro de um certo
+período de tempo (normalmente 5 dias), eu retornarei uma mensagem
+para o remetente com uma explicação do ocorrido. O administrador
+também pode configurar a lista para que as mensagens "ignoradas"
+sejam simplesmente removidas, ao invés de retornar para o remetente.
+<//>
+
+Férias
+------
+Se você está temporariamente em um endereço diferente, basta repassar 
+todas as mensagens que têm o cabeçalho correto 'Mailing-List:' (ou todas
+as mensagens que têm assuntos começando com 'MODERATE for <#L#>@<#H#>'
+ou 'CONFIRM subscribe to <#L#>@<#H#>') para o novo endereço.
+Você pode então moderar a partir do novo endereço. Alternativamente,
+você pode repassar as mensagens para um amigo, assim ele pode moderar 
+para você. Por favor, avise e confirme este procedimento com o dono da lista.
+
+Se você quiser aprovar automaticamente todas as mensagens enquanto
+está fora, configure o seu programa de correio eletrônico para responder 
+automaticamente as mensagens que têm os assuntos com os critérios acima.
+
+</#r/>
+Se você tentar administrar remotamente a partir de um endereço que
+não é seu, o assinante, não você, será questionado para confirmar.
+Depois disto, um pedido de confirmação é enviado para todos os
+moderadores. Estou fazendo isto, porque eu não tenho como saber que
+foi você quem enviou o pedido original. 
+
+Por favor note que o seu pedido original (e seu endereço) é enviado
+para o assinante neste caso!
+<//>
+
+Boa sorte!
+
+PS: Por favor, contacte o dono da lista (<#L#>-owner@<#H#>) se você
+tiver qualquer dúvida ou problema.
+
+</text/mod-reject/>
+Sinto muito, sua mensagem (incluída) não foi aceita pelo moderador.
+Se o moderador fez algum comentário, este será mostrado abaixo.
+</text/mod-request/>
+Esta mensagem foi submetida à lista <#L#>@<#H#>.
+Se você aprova a mensagem para distribuição a todos
+os assinantes, por favor envie mensagem para:
+
+!A
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Você pode confirmar se o endereço começa com
+"<#L#>-accept". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+Alternativamente, clique aqui:
+       mailto:<#A#>
+
+<//>
+
+Para rejeitar esta mensagem, retornando-a para o remetente,
+por favor envie uma mensagem para:
+
+!R
+
+Na prática, é fácil usar o botão "reply-to-all" ou "responder
+para todos", e então remover todos os endereços, exceto o que
+começar com
+"<#L#>-reject".
+</#x/>
+
+Alternativamente, clique aqui:
+       mailto:<#R#>
+<//>
+
+Você não precisa copiar esta mensagem na sua resposta para
+aceitá-la ou rejeitá-la. Se você quiser enviar um comentário para
+o remetente de uma mensagem rejeitada, por favor inclua o comentário
+entre duas linhas começando com três sinais de porcentagem ('%').
+
+%%% Início do comentário
+%%% Fim do comentário
+
+Obrigado pela sua ajuda!
+
+--- Inclusão, por favor encontre a mensagem postada.
+
+</text/mod-sub#E/>
+--- Eu subscrevi você ou cancelei sua subscrição através
+da requisição de um moderador da lista <#l#>@<#H#>.
+
+Se esta é uma ação que você não deseja, por favor envie uma
+reclamação ou outros comentários para o dono da lista
+(<#l#>-owner@<#H#>) assim que possível.
+
+Se você quer alguma informação em como acessar o arquivo
+da lista <#L#>, basta enviar uma mensagem vazia para 
+<#L#>-help@<#H#>.
+
+</text/mod-timeout/>
+Sinto muito, os moderadores não tomaram nenhuma ação com a sua
+mensagem. Eu a estou retornado para você. Se você acha que isto
+é um erro, por favor reenvie sua mensagem ou contacte um moderador
+da lista diretamente.
+
+--- Inclusão, por favor encontre a mensagem que você enviou.
+
+</text/mod-sub-confirm/>
+Respeitosamente eu solicito sua permissão para incluir
+
+!A
+
+como assinante da lista <#l#>. Este pedido ou vem de você
+ou já foi verificado pelo potencial assinante.
+
+Para confirmar, por favor envie uma resposta vazia para este endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Se você não aprovar, simplesmente ignore esta mensagem.
+
+Obrigado pela sua ajuda!
+
+</text/mod-unsub-confirm/>
+Foi feito um pedido para remover
+
+!A
+
+da lista <#l#>. Se você concordar, por favor envie
+uma resposta vazia para este endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Se você não aprova, simplesmente ignore esta mensagem.
+
+Obrigado pela sua ajuda!
+
+</text/sub-bad/>
+Este número de confirmação perece ser inválido.
+
+A maioria dos casos de números inválidos é devida a expiração. Eu 
+tenho que receber uma confirmação de cada requisição dentro de 10 dias. 
+Certifique-se também que o número de confirmação está inteiro na
+resposta que você me enviou. Alguns programas de correio eletrônico têm
+o hábito de cortar parte do endereço de retorno, o qual pode ser
+bastante longo.
+
+Eu configurei um novo número. Para confirmar que você deseja que
+
+!A
+
+seja incluído na lista <#l#>, por favor envie uma resposta vazia
+para o endereço:
+
+!R
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Novamente verifique o endereço de resposta cuidadosamente, para ter
+certeza que está completo antes que você confirme a sua subscrição.
+
+Sinto muito pelo problema.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm/>
+Para confirmar que você deseja que
+
+!A
+
+seja incluído na lista <#l#>, por favor envie uma resposta vazia
+para o endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Esta confirmação tem dois propósitos. Primeiro, ela verifica que eu
+sou consigo receber mensagens de você. Segundo, ela protege você no caso
+de alguém estar forjando um pedido de subscrição em seu nome.
+
+</#q/>
+Alguns programas de correio eletrônico não conseguem manipular endereços
+muito longos. Se você não está conseguindo responder esta requisição por
+causa disto, envie uma mensagem para <<#L#>-request@<#H#>> 
+e coloque o endereço inteiro listado abaixo na linha "Subject:" ou
+"Assunto:".
+
+</text/sub-confirm#s/>
+Esta lista é moderada. Depois que você enviar esta confirmação,
+o pedido será enviado para o(s) moderador(es) desta lista. Eu notificarei
+você quando sua subscrição for ativada.
+
+</text/sub-nop/>
+Confirmação: o endereço
+
+!A
+
+está na lista <#l#>. Este endereço já estava
+cadastrado antes do seu pedido ser recebido.
+
+</text/sub-ok#E/>
+Confirmação: eu incluí o endereço
+
+!A
+
+para a lista <#l#>.
+
+Bem-vindo à <#l#>@<#H#>!
+
+Por favor salve esta mensagem para que você saiba o endereço sob o
+qual está inscrito; se mais tarde você quiser cancelar ou alterar
+o seu endereço de subscrição, esta mensagem poderá ser útil.
+
+</text/top/>
+Oi! Este é o programa ezmlm. Eu estou gerenciando
+a lista <#l#>@<#H#>.
+
+</#x/>
+Eu estou trabalhando para o meu dono, que pode ser
+contactado pelo endereço <#l#>-owner@<#H#>.
+
+</text/unsub-bad/>
+O número de confirmação perece ser inválido.
+
+A maioria dos casos de números inválidos é devida a expiração. Eu 
+tenho que receber uma confirmação de cada requisição dentro de 10 dias. 
+Certifique-se também que o número de confirmação está inteiro na
+resposta que você me enviou. Alguns programas de correio eletrônico têm
+o hábito de cortar parte do endereço de retorno, o qual pode ser
+bastante longo.
+
+Eu configurei um novo número. Para confirmar que você deseja que
+
+!A
+
+seja removido da lista <#l#>, por favor envie uma resposta vazia
+para o endereço:
+
+!R
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Novamente verifique o endereço de resposta cuidadosamente, para ter
+certeza que está completo antes que você confirme esta ação.
+
+Sinto muito pelo problema.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm/>
+Para confirmar que você deseja que
+
+!A
+
+seja removido da lista <#l#>, por favor envie uma resposta vazia
+para este endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Eu não verifiquei se o seu endereço está atualmente na lista.
+Para ver qual endereço você usou ao se subscrever, olhe as mensagens
+que você está recebendo da lista. Cada mensagem tem seu endereço 
+oculto dentro do caminho de retorno; por exemplo, mary@xdd.ff.com 
+recebe mensagens com caminho de retorno:
+<<#l#>-return-<number>-mary=xdd.ff.com@<#H#>.
+
+</#q/>
+Alguns programas de correio eletrônico não conseguem manipular endereços
+muito longos. Se você não está conseguindo responder esta requisição por
+causa disto, envie uma mensagem para <<#L#>-request@<#H#>> 
+e coloque o endereço inteiro listado abaixo na linha "Subject:" ou
+"Assunto:".
+
+</text/unsub-nop/>
+Confirmação: o endereço
+
+!A
+
+não está na lista <#l#>. Ele não foi encontrado quando
+o seu pedido foi recebido.
+
+Se você cancelou a subscrição, mas continua recebendo mensagens,
+você está incluído com um endereço diferente do que está usando 
+agora. Por favor, olhe o cabeçalho da mensagem, procurando por:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+O endereço de cancelamento para este endereço deve ser:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+Basta enviar uma mensagem para este endereço, substituindo
+user=host.dom pelos valores corretos, responda ao pedido de confirmação
+e você deverá receber um aviso que está fora da lista.
+
+Para alguns programas de correio, você precisará tornar os cabeçalhos
+visíveis para ver o caminho de retorno (return-path):
+
+Para o Eudora 4.0, clique no botão "Blah blah ...".
+Para o PMMail, clique em "Window->Show entire message/header". 
+
+Se isto ainda não funcionar, eu sinto muito em dizer que eu não posso
+ajudar você. Por favor, REPASSE uma mensagem da lista junto com uma
+observação sobre o que você está tentando fazer para o meu dono:
+
+    <#l#>-owner@<#H#>
+
+que cuidará disto. Meu dono é um pouco mais lento do que eu,
+por favor tenha paciência.
+
+</text/unsub-ok/>
+Confirmação: Eu removi o endereço
+
+!A
+
+da lista <#l#>. Este endereço não está
+mais na lista.
+
+</text/edit-do#n/>
+Por favor edite o arquivo texto seguinte e envie-o para este endereço: 
+
+!R
+
+O seu programa de correio eletrônico deve ser capaz de enviar uma resposta
+para este endereço automaticamente.
+
+Eu posso remover as aspas que o seu programa de correio adiciona
+ao texto, desde que você não edite as próprias linhas marcadoras.
+
+As linhas marcadoras são as linhas começando com '%%%'. Elas não 
+devem ser modificadas (caracteres extras adicionados pelo seu programa
+de correio eletrônico no início da linha são aceitos).
+
+
+</text/edit-list#n/>
+O comando <#L#>-edit.file pode ser usado por um administrador
+remoto para editar os arquivos texto que são usados para as
+respostas da lista <#L#>@<#H#>.
+
+O que segue é uma lista dos nomes dos arquivos de resposta
+e uma breve descrição de quando os seus conteúdos são usados.
+Para editar um arquivo, simplesmente envie uma mensagem para
+<#L#>-edit.file, substituindo 'file' pelo nome do arquivo.
+Instruções para edição serão enviadas com o arquivo texto.
+
+Arquivo             Uso
+
+bottom              rodapé de todas as respostas. Informação geral dos comandos.
+digest              seção 'administrativa' das listas "digest".
+faq                 perguntas frequentes específicas desta lista.
+get_bad             no lugar de mensagem não encontradas no arquivo.
+help                ajuda geral (entre 'top' e 'bottom').
+info                lista informações.
+mod_help            ajuda específica para moderadores.
+mod_reject          para o remetente de uma mensagem rejeitada.
+mod_request         para os moderadores junto com a mensagem.
+mod_sub             para o assinante depois que o moderador confirma a 
+                    sua subscrição.
+mod_sub_confirm     para o moderador de subscrição requerendo a confirmação.
+mod_timeout         para o remetente de uma mensagem expirada.
+mod_unsub_confirm   para o administrador remoto confirmar o cancelamento
+                    de subscrição.
+sub_bad             para o assinante, se confirmado que a msg era inválida.
+sub_confirm         para o assinante solicitando confirmação de subscrição.
+sub_nop             para o assinante depois de re-subscrição.
+sub_ok              para o assinante depois de subscrição com sucesso.
+top                 topo de todas as respostas.
+</#tn/>
+trailer             incluído em todas as mensagens enviadas pela lista.
+</#n/>
+unsub_bad           para o assinante, se a confirmação de cancelamento
+                    é inválida.
+unsub_confirm       para assinante pedindo confirmação para o cancelamento.
+unsub_nop           para não-assinante depois de pedido de cancelamento.
+unsub_ok            para ex-assinante depois do cancelamento com sucesso.
+
+</text/edit-done#n/>
+O arquivo texto foi atualizado com sucesso.
+
+</text/info#E/>
+Ainda não há informações para esta lista.
+</text/faq#E/>
+FAQ - Questões mais freqüentes na lista <#l#>@<#H#>.
+
+Ainda não disponível.
+  
+
diff --git a/ezmlmrc.pt_BR b/ezmlmrc.pt_BR
new file mode 100644 (file)
index 0000000..81aadbf
--- /dev/null
@@ -0,0 +1,1134 @@
+0.324 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.pt_BR,v 1.16 1999/12/23 23:08:19 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Explicitly specify character-set, when this ezmlmrc was used.
+ISO-8859-1:Q
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sinto muito, eu estou rejeitando as suas mensagens. Contacte <#L#>-owner@<#H#> se você tem questões sobre isto (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sinto muito, somente assinantes podem enviar mensagens. Se você é um assinante, por favor repasse esta mensagem para <#L#>-owner@<#H#> para ter o seu novo endereço incluído (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contacte <#L#>-help@<#H#>; gerenciado pelo ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+List-Post: <mailto:<#L#>@<#H#>>
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+---------------------------------------------------------------------
+Para cancelar a subscrição, envie mensagem para: <#L#>-unsubscribe@<#H#>
+Para comandos adicionais, envie mensagem para: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- Aqui estão os endereços de comando do ezmlm.
+
+Eu posso tratar as requisições administrativas automaticamente.
+Basta enviar uma mensagem vazia para qualquer destes endereços:
+
+   <<#L#>-subscribe@<#H#>>:
+   Receber as futuras mensagens enviadas para a lista <#L#>.
+
+   <<#L#>-unsubscribe@<#H#>>:
+   Parar de receber as mensagens da lista <#L#>.
+
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+   Para receber mais informações ou o FAQ desta lista,
+   se disponíveis.
+
+</#d/>
+Existem também estes endereços similares para a lista "digest":
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+   <<#L#>-get.12_45@<#H#>>:
+   Recupera uma cópia das mensagens 12 a 45 do arquivo.
+   No máximo 100 mensagens serão retornadas por requisição.
+
+</text/bottom#aI/>
+   <<#L#>-get.12@<#H#>>:
+   Recupera uma cópia da mensagem 12 do arquivo.
+
+</text/bottom#i/>
+   <<#L#>-index.123_456@<#H#>>:
+   Recupera os assuntos das mensagens número 123 a 456.
+   Assuntos são retornados em grupos de 100, ou seja você receberá
+   os assuntos das mensagens 100 a 499.
+   Um máximo de 2000 assuntos são retornados por requisição.
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+   <<#L#>-thread.12345@<#H#>>:
+   Recupera uma cópia de todas as mensagens com o mesmo assunto
+   da mensagem 12345.
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+Na verdade as mensagens não precisam ser vazias, mas eu irei
+ignorar o seu conteúdo. Somente o ENDEREÇO é importante.
+
+Você pode iniciar a assinatura para um endereço alternativo,
+por exemplo "john@host.domain", bastando adicionar um traço
+e o seu endereço (com '=' no lugar de '@'), ou seja:
+<<#L#>-subscribe-john=host.domain@<#H#>>
+
+Para cancelar a subscrição para este endereço, envie mensagem
+para: <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+Em ambos os casos, eu enviarei uma mensagem de confirmação para
+esse endereço; quando você receber esta mensagem, simplesmente
+responda para completar sua subscrição (ou cancelamento).
+
+</text/bottom/>
+Se estas instruções são muito complicadas ou você não teve os
+resultados que esperava, por favor contacte o meu dono em 
+<#L#>-owner@<#H#>. Mas tenha paciência, o meu dono é
+muito mais lento do que eu ;-)
+</text/bottom/>
+
+--- Abaixo está uma cópia da requisição que eu recebi.
+
+</text/bounce-bottom/>
+
+--- Abaixo está uma cópia da mensagem devolvida que eu recebi.
+
+</text/bounce-num/>
+
+Eu mantenho um controle das mensagens da lista <#L> que não
+foram entregues para o seu endereço.
+</#a/>
+Cópias destas mensagens podem estar no arquivo.
+</#aI/>
+Para pegar a mensagem 12345 do arquivo, envie uma mensagem vazia para
+  <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+Para recuperar uma lista de assuntos e autores das últimas 100
+mensagens, envie uma mensagem vazia para:
+   <<#L#>-index@<#H#>>
+
+Para recuperar as mensagens 123 a 145 (no máximo 100 por requisição),
+envie uma mensagem vazia para:
+   <<#L#>-get.123_145@<#H#>>
+
+<//>
+Aqui estão os números das mensagens:
+
+</text/dig-bounce-num/>
+
+Eu mantenho um controle de quais mensagens da lista <#L#>-digest
+não foram entregues para o seu endereço. Para cada "digest" que você
+não recebeu, eu tenho anotado o número da primeira mensagem do "digest".
+Então você pode pegar as mensagens do arquivo da lista principal.
+
+</#aI/>
+Para recuperar a mensagem 12345 do arquivo, envie uma mensagem vazia para:
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+Para recuperar as mensagens 123 a 145 (no máximo 100 por requisição),
+envie uma mensagem vazia para:
+   <<#L#>-get.123_145@<#H#>>
+
+Para recuperar uma lista de assuntos e autores das últimas 100
+mensagens, envie uma mensagem vazia para:
+   <<#L#>-index@<#H#>>
+
+<//>
+Aqui estão os números dos "digests":
+
+</text/bounce-probe/>
+
+Mensagens da lista <#l#> para você estão sendo devolvidas.
+Eu enviei um aviso para você, que também foi devolvido.
+Eu anexei uma cópia da mensagem devolvida.
+
+Esta é uma verificação para o seu endereço. Se esta verificação
+for devolvida, eu removerei o seu endereço da lista
+<#l#>@<#H#>, sem mais avisos. Você pode assinar novamente a
+lista enviando uma mensagem vazia para o endereço:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn/>
+
+Mensagens da lista <#l#> para você não foram entregues.
+Eu anexei uma cópia da primeira mensagem que eu recebi de volta.
+
+Se esta mensagem também não for entregue, eu enviarei uma última
+verificação. Se a verificação também for devolvida, eu removerei o seu
+endereço da lista <#l#>, sem mais avisos.
+
+</text/digest#d/>
+Para subscrever-se à lista "digest", envie mensagem para:
+       <#L#>-digest-subscribe@<#H#>
+
+Para cancelar a subscrição da lista "digest", envie mensagem para:
+       <#L#>-digest-unsubscribe@<#H#>
+
+Para enviar mensagens para a lista, use o endereço:
+       <#L#>@<#H#>
+
+</text/get-bad/>
+Sinto muito, esta mensagem não está arquivada.
+
+</text/help/>
+Esta é uma mensagem de ajuda genérica. A mensagem que eu recebi 
+não era para os meus endereços de comando.
+
+</text/mod-help/>
+Obrigado por concordar em moderar a lista
+<#L#>@<#H#>.
+
+Meus comandos são um pouco diferentes de outros gerenciadores de
+listas. À primeira vista eles parecem não usuais, mas à medida que
+você os for usando, vai apreciar a simplicidade do sistema e
+a rapidez com que eu trato suas requisições.
+
+Aqui estão algumas observações sobre como atuar como dono da
+lista e/ou moderador:
+
+Subscrição Remota
+-----------------
+Como um moderador, você pode subscrever e cancelar a subscrição
+de qualquer endereço da lista. Para cadastrar john@johnhost.johndomain,
+simplesmente coloque um traço depois do comando, e depois o endereço
+com '=' no lugar de '@'. Por exemplo, para cadastrar este endereço,
+envie uma mensagem para:
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+Você pode analogamente remover esse endereço, enviando mensagem para:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#d/>
+Para a lista "digest":
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+<//>
+Isto é tudo. Nem assunto nem corpo de mensagem são necessários!
+
+</#r/>
+Eu enviarei para você um pedido de confirmação, para ter certeza
+que é realmente você quem enviou a requisição. Simplesmente
+retorne a mensagem, e a requisição será completada.
+</#R/>
+Eu enviarei um pedido de confirmação para o endereço do usuário,
+neste caso <john@host.domain>. Tudo que o usuário tem que fazer é
+retornar este pedido de confirmação.
+<//>
+
+As confirmações são necessárias para evitar que pessoas
+não autorizadas possam cadastrar ou remover endereços
+da lista.
+
+Eu notificarei o usuário quando a situação de sua subscrição
+for alterada.
+
+Subscrição
+----------
+
+Qualquer usuário pode subscrever-se ou cancelar a subscrição
+enviando mensagem para:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#d/>
+Para a lista "digest":
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+<//>
+O usuário receberá um pedido de confirmação para eu ter 
+certeza que ele controla o endereço de subscrição. Quando este
+endereço for verificado, ele será descadastrado.
+
+</#s/>
+Sendo esta lista moderada para subscrição, eu enviarei um segundo
+pedido de confirmação para o(s) moderador(es). Se o usuário
+já tiver confirmado o desejo de ficar na lista, você como
+moderador pode confiar que o endereço do assinante é verdadeiro.
+Se você deseja aprovar os pedidos do usuário, simplesmente
+retorne a mensagem de CONFIRMAÇÃO. Caso contrário, simplesmente
+ignore a minha mensagem ou possivelmente contacte o potencial
+assinante para obter mais informações.
+</#S/>
+Subscrições funcionam da mesma forma.
+<//>
+
+O usuário também pode usar estas formas:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+para que as mensagens sejam enviadas para "mary@host.domain".
+Somente se ela receber mensagens neste endereço, ela será
+capaz de responder a confirmação.
+
+Seu endereço e identidade não serão revelados para o assinante,
+a não ser que você envie mensagens diretamente para o usuário.
+
+</#rl/>
+Para obter o cadastro de assinantes da lista <#L#>@<#H#>,
+envie uma mensagem para:
+   <<#L#>-list@<#H#>>
+
+Para um registro de transações da lista,
+envie uma mensagem para:
+   <<#L#>-log@<#H#>>
+
+</#rld/>
+Para o cadastro de assinantes da lista digest:
+   <<#L#>-digest-list@<#H#>>
+e:
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+Você pode editar remotamente os arquivos-texto de resposta que a lista
+envia. Para obter uma lista dos arquivos e instruções de edição, envie
+mensagem para:
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+Postagens Moderadas
+-------------------
+Quando as postagens são moderadas, eu colocarei as mensagens em 
+uma fila, e enviarei a você uma cópia junto com instruções. A
+mensagem terá "MODERATE for ..." como assunto.
+
+Se você aceita a mensagem, basta responder para o endereço 'Reply-To:',
+o qual já deixei configurado como o endereço de "aceitação".
+Não é necessário incluir a mensagem; na verdade, eu ignorarei qualquer
+coisa que você envie, desde que o endereço para o qual você enviou
+esteja correto.
+
+Se você quiser rejeitá-la, envie a mensagem para o endereço 'From:',
+que por sua vez, está configurado com o endereço de "rejeição".
+Você pode usualmente fazer um 'reply-to-all' - resposta para todos,
+e então remover o seu endereço e o endereço de "aceitação").
+Você pode adicionar um comentário opcional para o remetente entre
+duas linhas começando com três '%'. Eu enviarei este comentário somente
+para o remetente, sem revelar sua identidade.
+
+Eu irei processar a mensagem de acordo com o primeiro retorno que eu
+receber. Se você me enviar uma requisição para aceitar uma mensagem
+que já foi rejeitada ou vice-versa, eu o avisarei.
+
+Se eu não receber nenhuma resposta do moderador dentro de um certo
+período de tempo (normalmente 5 dias), eu retornarei uma mensagem
+para o remetente com uma explicação do ocorrido. O administrador
+também pode configurar a lista para que as mensagens "ignoradas"
+sejam simplesmente removidas, ao invés de retornar para o remetente.
+<//>
+
+Férias
+------
+Se você está temporariamente em um endereço diferente, basta repassar 
+todas as mensagens que têm o cabeçalho correto 'Mailing-List:' (ou todas
+as mensagens que têm assuntos começando com 'MODERATE for <#L#>@<#H#>'
+ou 'CONFIRM subscribe to <#L#>@<#H#>') para o novo endereço.
+Você pode então moderar a partir do novo endereço. Alternativamente,
+você pode repassar as mensagens para um amigo, assim ele pode moderar 
+para você. Por favor, avise e confirme este procedimento com o dono da lista.
+
+Se você quiser aprovar automaticamente todas as mensagens enquanto
+está fora, configure o seu programa de correio eletrônico para responder 
+automaticamente as mensagens que têm os assuntos com os critérios acima.
+
+</#r/>
+Se você tentar administrar remotamente a partir de um endereço que
+não é seu, o assinante, não você, será questionado para confirmar.
+Depois disto, um pedido de confirmação é enviado para todos os
+moderadores. Estou fazendo isto, porque eu não tenho como saber que
+foi você quem enviou o pedido original. 
+
+Por favor note que o seu pedido original (e seu endereço) é enviado
+para o assinante neste caso!
+<//>
+
+Boa sorte!
+
+PS: Por favor, contacte o dono da lista (<#L#>-owner@<#H#>) se você
+tiver qualquer dúvida ou problema.
+
+</text/mod-reject/>
+Sinto muito, sua mensagem (incluída) não foi aceita pelo moderador.
+Se o moderador fez algum comentário, este será mostrado abaixo.
+</text/mod-request/>
+Esta mensagem foi submetida à lista <#L#>@<#H#>.
+Se você aprova a mensagem para distribuição a todos
+os assinantes, por favor envie mensagem para:
+
+!A
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Você pode confirmar se o endereço começa com
+"<#L#>-accept". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+Alternativamente, clique aqui:
+       mailto:<#A#>
+
+<//>
+
+Para rejeitar esta mensagem, retornando-a para o remetente,
+por favor envie uma mensagem para:
+
+!R
+
+Na prática, é fácil usar o botão "reply-to-all" ou "responder
+para todos", e então remover todos os endereços, exceto o que
+começar com
+"<#L#>-reject".
+</#x/>
+
+Alternativamente, clique aqui:
+       mailto:<#R#>
+<//>
+
+Você não precisa copiar esta mensagem na sua resposta para
+aceitá-la ou rejeitá-la. Se você quiser enviar um comentário para
+o remetente de uma mensagem rejeitada, por favor inclua o comentário
+entre duas linhas começando com três sinais de porcentagem ('%').
+
+%%% Início do comentário
+%%% Fim do comentário
+
+Obrigado pela sua ajuda!
+
+--- Inclusão, por favor encontre a mensagem postada.
+
+</text/mod-sub#E/>
+--- Eu subscrevi você ou cancelei sua subscrição através
+da requisição de um moderador da lista <#l#>@<#H#>.
+
+Se esta é uma ação que você não deseja, por favor envie uma
+reclamação ou outros comentários para o dono da lista
+(<#l#>-owner@<#H#>) assim que possível.
+
+Se você quer alguma informação em como acessar o arquivo
+da lista <#L#>, basta enviar uma mensagem vazia para 
+<#L#>-help@<#H#>.
+
+</text/mod-timeout/>
+Sinto muito, os moderadores não tomaram nenhuma ação com a sua
+mensagem. Eu a estou retornado para você. Se você acha que isto
+é um erro, por favor reenvie sua mensagem ou contacte um moderador
+da lista diretamente.
+
+--- Inclusão, por favor encontre a mensagem que você enviou.
+
+</text/mod-sub-confirm/>
+Respeitosamente eu solicito sua permissão para incluir
+
+!A
+
+como assinante da lista <#l#>. Este pedido ou vem de você
+ou já foi verificado pelo potencial assinante.
+
+Para confirmar, por favor envie uma resposta vazia para este endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Se você não aprovar, simplesmente ignore esta mensagem.
+
+Obrigado pela sua ajuda!
+
+</text/mod-unsub-confirm/>
+Foi feito um pedido para remover
+
+!A
+
+da lista <#l#>. Se você concordar, por favor envie
+uma resposta vazia para este endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Se você não aprova, simplesmente ignore esta mensagem.
+
+Obrigado pela sua ajuda!
+
+</text/sub-bad/>
+Este número de confirmação perece ser inválido.
+
+A maioria dos casos de números inválidos é devida a expiração. Eu 
+tenho que receber uma confirmação de cada requisição dentro de 10 dias. 
+Certifique-se também que o número de confirmação está inteiro na
+resposta que você me enviou. Alguns programas de correio eletrônico têm
+o hábito de cortar parte do endereço de retorno, o qual pode ser
+bastante longo.
+
+Eu configurei um novo número. Para confirmar que você deseja que
+
+!A
+
+seja incluído na lista <#l#>, por favor envie uma resposta vazia
+para o endereço:
+
+!R
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Novamente verifique o endereço de resposta cuidadosamente, para ter
+certeza que está completo antes que você confirme a sua subscrição.
+
+Sinto muito pelo problema.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm/>
+Para confirmar que você deseja que
+
+!A
+
+seja incluído na lista <#l#>, por favor envie uma resposta vazia
+para o endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Esta confirmação tem dois propósitos. Primeiro, ela verifica que eu
+sou consigo receber mensagens de você. Segundo, ela protege você no caso
+de alguém estar forjando um pedido de subscrição em seu nome.
+
+</#q/>
+Alguns programas de correio eletrônico não conseguem manipular endereços
+muito longos. Se você não está conseguindo responder esta requisição por
+causa disto, envie uma mensagem para <<#L#>-request@<#H#>> 
+e coloque o endereço inteiro listado abaixo na linha "Subject:" ou
+"Assunto:".
+
+</text/sub-confirm#s/>
+Esta lista é moderada. Depois que você enviar esta confirmação,
+o pedido será enviado para o(s) moderador(es) desta lista. Eu notificarei
+você quando sua subscrição for ativada.
+
+</text/sub-nop/>
+Confirmação: o endereço
+
+!A
+
+está na lista <#l#>. Este endereço já estava
+cadastrado antes do seu pedido ser recebido.
+
+</text/sub-ok#E/>
+Confirmação: eu incluí o endereço
+
+!A
+
+para a lista <#l#>.
+
+Bem-vindo à <#l#>@<#H#>!
+
+Por favor salve esta mensagem para que você saiba o endereço sob o
+qual está inscrito; se mais tarde você quiser cancelar ou alterar
+o seu endereço de subscrição, esta mensagem poderá ser útil.
+
+</text/top/>
+Oi! Este é o programa ezmlm. Eu estou gerenciando
+a lista <#l#>@<#H#>.
+
+</#x/>
+Eu estou trabalhando para o meu dono, que pode ser
+contactado pelo endereço <#l#>-owner@<#H#>.
+
+</text/unsub-bad/>
+O número de confirmação perece ser inválido.
+
+A maioria dos casos de números inválidos é devida a expiração. Eu 
+tenho que receber uma confirmação de cada requisição dentro de 10 dias. 
+Certifique-se também que o número de confirmação está inteiro na
+resposta que você me enviou. Alguns programas de correio eletrônico têm
+o hábito de cortar parte do endereço de retorno, o qual pode ser
+bastante longo.
+
+Eu configurei um novo número. Para confirmar que você deseja que
+
+!A
+
+seja removido da lista <#l#>, por favor envie uma resposta vazia
+para o endereço:
+
+!R
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Novamente verifique o endereço de resposta cuidadosamente, para ter
+certeza que está completo antes que você confirme esta ação.
+
+Sinto muito pelo problema.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm/>
+Para confirmar que você deseja que
+
+!A
+
+seja removido da lista <#l#>, por favor envie uma resposta vazia
+para este endereço:
+
+!R
+
+Na prática, você estará enviado para este endereço quando pressionar o
+botão "reply" ou "responder". Se isto não funcionar, simplesmente copie
+o endereço e cole-o para o campo "To:" de uma nova mensagem.
+</#x/>
+
+ou clique aqui:
+       mailto:<#R#>
+<//>
+
+Eu não verifiquei se o seu endereço está atualmente na lista.
+Para ver qual endereço você usou ao se subscrever, olhe as mensagens
+que você está recebendo da lista. Cada mensagem tem seu endereço 
+oculto dentro do caminho de retorno; por exemplo, mary@xdd.ff.com 
+recebe mensagens com caminho de retorno:
+<<#l#>-return-<number>-mary=xdd.ff.com@<#H#>.
+
+</#q/>
+Alguns programas de correio eletrônico não conseguem manipular endereços
+muito longos. Se você não está conseguindo responder esta requisição por
+causa disto, envie uma mensagem para <<#L#>-request@<#H#>> 
+e coloque o endereço inteiro listado abaixo na linha "Subject:" ou
+"Assunto:".
+
+</text/unsub-nop/>
+Confirmação: o endereço
+
+!A
+
+não está na lista <#l#>. Ele não foi encontrado quando
+o seu pedido foi recebido.
+
+Se você cancelou a subscrição, mas continua recebendo mensagens,
+você está incluído com um endereço diferente do que está usando 
+agora. Por favor, olhe o cabeçalho da mensagem, procurando por:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+O endereço de cancelamento para este endereço deve ser:
+'<#l#>-unsubscribe-user=host.dom@<#H#>'.
+Basta enviar uma mensagem para este endereço, substituindo
+user=host.dom pelos valores corretos, responda ao pedido de confirmação
+e você deverá receber um aviso que está fora da lista.
+
+Para alguns programas de correio, você precisará tornar os cabeçalhos
+visíveis para ver o caminho de retorno (return-path):
+
+Para o Eudora 4.0, clique no botão "Blah blah ...".
+Para o PMMail, clique em "Window->Show entire message/header". 
+
+Se isto ainda não funcionar, eu sinto muito em dizer que eu não posso
+ajudar você. Por favor, REPASSE uma mensagem da lista junto com uma
+observação sobre o que você está tentando fazer para o meu dono:
+
+    <#l#>-owner@<#H#>
+
+que cuidará disto. Meu dono é um pouco mais lento do que eu,
+por favor tenha paciência.
+
+</text/unsub-ok/>
+Confirmação: Eu removi o endereço
+
+!A
+
+da lista <#l#>. Este endereço não está
+mais na lista.
+
+</text/edit-do#n/>
+Por favor edite o arquivo texto seguinte e envie-o para este endereço: 
+
+!R
+
+O seu programa de correio eletrônico deve ser capaz de enviar uma resposta
+para este endereço automaticamente.
+
+Eu posso remover as aspas que o seu programa de correio adiciona
+ao texto, desde que você não edite as próprias linhas marcadoras.
+
+As linhas marcadoras são as linhas começando com '%%%'. Elas não 
+devem ser modificadas (caracteres extras adicionados pelo seu programa
+de correio eletrônico no início da linha são aceitos).
+
+
+</text/edit-list#n/>
+O comando <#L#>-edit.file pode ser usado por um administrador
+remoto para editar os arquivos texto que são usados para as
+respostas da lista <#L#>@<#H#>.
+
+O que segue é uma lista dos nomes dos arquivos de resposta
+e uma breve descrição de quando os seus conteúdos são usados.
+Para editar um arquivo, simplesmente envie uma mensagem para
+<#L#>-edit.file, substituindo 'file' pelo nome do arquivo.
+Instruções para edição serão enviadas com o arquivo texto.
+
+Arquivo             Uso
+
+bottom              rodapé de todas as respostas. Informação geral dos comandos.
+digest              seção 'administrativa' das listas "digest".
+faq                 perguntas frequentes específicas desta lista.
+get_bad             no lugar de mensagem não encontradas no arquivo.
+help                ajuda geral (entre 'top' e 'bottom').
+info                lista informações.
+mod_help            ajuda específica para moderadores.
+mod_reject          para o remetente de uma mensagem rejeitada.
+mod_request         para os moderadores junto com a mensagem.
+mod_sub             para o assinante depois que o moderador confirma a 
+                    sua subscrição.
+mod_sub_confirm     para o moderador de subscrição requerendo a confirmação.
+mod_timeout         para o remetente de uma mensagem expirada.
+mod_unsub_confirm   para o administrador remoto confirmar o cancelamento
+                    de subscrição.
+sub_bad             para o assinante, se confirmado que a msg era inválida.
+sub_confirm         para o assinante solicitando confirmação de subscrição.
+sub_nop             para o assinante depois de re-subscrição.
+sub_ok              para o assinante depois de subscrição com sucesso.
+top                 topo de todas as respostas.
+</#tn/>
+trailer             incluído em todas as mensagens enviadas pela lista.
+</#n/>
+unsub_bad           para o assinante, se a confirmação de cancelamento
+                    é inválida.
+unsub_confirm       para assinante pedindo confirmação para o cancelamento.
+unsub_nop           para não-assinante depois de pedido de cancelamento.
+unsub_ok            para ex-assinante depois do cancelamento com sucesso.
+
+</text/edit-done#n/>
+O arquivo texto foi atualizado com sucesso.
+
+</text/info#E/>
+Ainda não há informações para esta lista.
+</text/faq#E/>
+FAQ - Questões mais freqüentes na lista <#l#>@<#H#>.
+
+Ainda não disponível.
+  
+
diff --git a/ezmlmrc.ru b/ezmlmrc.ru
new file mode 100644 (file)
index 0000000..49e6d34
--- /dev/null
@@ -0,0 +1,1015 @@
+0.324 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.ru,v 1.4 1999/12/23 23:08:19 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</charset/>
+# Charset file is a must for russian mailing lists
+koi8-r
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eI/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#iG/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#ig/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins.
+</manager#LN/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lN/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#Ln/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#ln/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Sorry, I've been told to reject your posts. Contact <#L#>-owner@<#H#> if you have questions about this (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Sorry, only subscribers may post. If you are a subscriber, please forward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mU/>
+|<#B#>/ezmlm-store '<#D#>'
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer is complicated. We use ezmlm-receipt if -6 AND -w, but ezmlm-return
+# if (-6 and -W) OR (not -6 and -w). Since there is no or, we need 2 lines.
+</bouncer/>
+|<#B#>/ezmlm-weed
+</#^6/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6W/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</#6w/>
+|<#B#>/ezmlm-receipt -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+</#^6d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6Wd/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</#6wd/>
+|<#B#>/ezmlm-receipt -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+# Only one allowed
+list-help
+list-unsubscribe
+list-post
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#i/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#L#>-help@<#H#>; run by ezmlm
+# Headeradd needs to always exist
+</headeradd#E/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+List-Post: <mailto:<#L#>@<#H#>>
+X-Comment: <#l#> mailing list (Russian, KOI8-R)
+# max & min message size
+</msgsize#x/>
+40000:2
+# remove mime parts if -x
+</mimeremove#x/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#f/>
+[<#L#>]
+</text/trailer#t/>
+-- 
+To unsubscribe, e-mail: <#L#>-unsubscribe@<#H#>
+For additional commands, e-mail: <#L#>-help@<#H#>
+</text/bottom/>
+
+--- ëÏÍÁÎÄÙ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#> ---
+
+÷ÓÅ ËÏÍÁÎÄÙ ÏÂÒÁÂÁÔÙ×ÁÀÔÓÑ Á×ÔÏÍÁÔÉÞÅÓËÉ. ðÏÖÁÌÕÊÓÔÁ,
+ÎÅ ÐÏÓÙÌÁÊÔÅ ÉÈ ÎÁ ÁÄÒÅÓ ÓÁÍÏÇÏ ÓÐÉÓËÁ, ÔÁÍ ÏÎÉ ÏÂÒÁÂÏÔÁÎÙ
+ÎÅ ÂÕÄÕÔ, Á ×ÁÓ ÏÂÒÕÇÁÀÔ ÐÏÄÐÉÓÞÉËÉ. ëÏÍÁÎÄÏÊ Ñ×ÌÑÅÔÓÑ
+ÐÉÓØÍÏ (ÎÅÚÁ×ÉÓÉÍÏ ÏÔ ÅÇÏ ÓÏÄÅÒÖÁÎÉÑ), ÐÏÓÌÁÎÎÏÅ ÎÁ
+ÏÄÉΠÉÚ ÁÄÍÉÎÉÓÔÒÁÔÉ×ÎÙÈ ÁÄÒÅÓÏ×:
+
+áÄÒÅÓ ÄÌÑ ÐÏÄÐÉÓËÉ:
+   <<#L#>-subscribe@<#H#>>
+
+áÄÒÅÓ ÄÌÑ ÏÔËÁÚÁ ÏÔ ÒÁÓÓÙÌËÉ:
+   <<#L#>-unsubscribe@<#H#>>
+
+äÌÑ ÐÏÌÕÞÅÎÉÑ ÐÒÁ×ÉÌ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ É FAQ:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#d/>
+ôÁË ÖÅ ÐÏÄÄÅÒÖÉ×ÁÀÔÓÑ É ÄÁÊÄÖÅÓÔÙ:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</text/bottom#ai/>
+þÔÏÂÙ ÐÏÌÕÞÉÔØ ÓÏÏÂÝÅÎÉÑ Ó 123 ÐÏ 145 (max 100 ÎÁ ÐÉÓØÍÏ), ÐÉÛÉÔÅ ÓÀÄÁ:
+   <<#L#>-get.123_145@<#H#>>
+
+</text/bottom#aI/>
+äÌÑ ÐÏÌÕÞÅÎÉÑ ÐÉÓØÍÁ #12 ÉÚ ÁÒÈÉ×Á ÐÏÛÌÉÔÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+   <<#L#>-get.12@<#H#>>
+
+</text/bottom#i/>
+áÄÒÅÓ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÉÎÄÅËÓÁ Ó subject É Á×ÔÏÒÁÍÉ ÐÉÓÅÍ 123-456:
+   <<#L#>-index.123_456@<#H#>>
+
+# Lists need to be both archived and indexed for -thread to work
+</text/bottom#ai/>
+áÄÒÅÓ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ×ÓÅ ÐÉÓÅÍ, Ó×ÑÚÁÎÎÙÈ Ó ÐÉÓØÍÏÍ #12345:
+   <<#L#>-thread.12345@<#H#>>
+
+# The '#' in the tag below is optional, since no flags follow.
+# The name is optional as well, since the file will always be open
+# at this point.
+</text/bottom#/>
+óÏÄÅÒÖÉÍÏÅ ÐÉÓÅÍ ÒÏÌÉ ÎÅ ÉÇÒÁÅÔ, ×ÁÖÅΠÌÉÛØ ÁÄÒÅÓ, ÎÁ ËÏÔÏÒÙÊ
+×Ù ÐÏÓÙÌÁÅÔÅ ÐÉÓØÍÏ.
+
+</text/bottom/>
+åÓÌÉ ÎÉÞÅÇÏ ÎÅ ÐÏÍÏÇÁÅÔ, ×Ù ÍÏÖÅÔÅ Ó×ÑÚÁÔØÓÑ Ó ×ÌÁÄÅÌØÃÅÍ
+ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ ÐÏ ÁÄÒÅÓÕ <#L#>-owner@<#H#>. 
+</text/bottom/>
+
+--- îÉÖÅ ÐÒÉ×ÅÄÅÎÁ ËÏÐÉÑ ÚÁÐÒÏÓÁ
+
+</text/bounce-bottom/>
+
+--- îÉÖÅ ÐÒÉ×ÅÄÅÎÁ ËÏÐÉÑ ÓÏÏÂÝÅÎÉÑ Ï ÏÛÉÂËÅ
+
+</text/bounce-num/>
+
+óÉÓÔÅÍÏÊ ÓÏÈÒÁÎÅΠÓÐÉÓÏË ÐÉÓÅÍ ÉÚ <#L#>, ËÏÔÏÒÙÅ ÎÅ ÄÏÛÌÉ ÄÏ
+×ÁÛÅÇÏ ÁÄÒÅÓÁ (Ô.Å. ÎÁ ÎÉÈ ÂÙÌÏ ÐÏÌÕÞÅÎÏ ÓÏÏÂÝÅÎÉÅ Ï ÏÛÉÂËÅ)
+
+</#a/>
+ëÏÐÉÉ ÜÔÉÈ ÐÉÓÅÍ ÎÁÈÏÄÑÔÓÑ × ÁÒÈÉ×Å.
+</#aI/>
+þÔÏÂÙ ÐÏÌÕÞÉÔØ ÐÉÓØÍÏ 12345 ÉÚ ÁÒÈÉ×Á, ÐÏÛÌÉÔÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+áÄÒÅÓ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÓÏÏÂÝÅÎÉÊ 123-145 (max 100 ÎÁ ÚÁÐÒÏÓ):
+   <<#L#>-get.123_145@<#H#>>
+
+áÄÒÅÓ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÓÐÉÓËÁ ÐÏÓÌÅÄÎÉÈ 100 ÓÏÏÂÝÅÎÉÊ Ó subject É Á×ÔÏÒÏÍ:
+   <<#L#>-index@<#H#>>
+
+<//>
+HÏÍÅÒÁ ÓÏÏÂÝÅÎÉÊ:
+
+</text/dig-bounce-num/>
+
+óÉÓÔÅÍÏÊ ÓÏÈÒÁÎÅÎÙ ÓÐÉÓÏË ÄÁÊÄÖÅÓÔÏ× <#L#>, ËÏÔÏÒÙÅ ÎÅ ÄÏÛÌÉ ÄÏ ×ÁÛÅÇÏ
+ÁÄÒÅÓÁ (Ô.Å. ÎÁ ÎÉÈ ÂÙÌÉ ÐÏÌÕÞÅÎÙ ÓÏÏÂÝÅÎÉÑ Ï ÏÛÉÂËÅ). óÁÍÉ ÄÁÊÄÖÅÓÔÙ
+ÎÅ ÈÒÁÎÑÔÓÑ, ÎÏ ÉÚ×ÅÓÔÎÙ ÎÏÍÅÒÁ ÐÉÓÅÍ × ÄÁÊÄÖÅÓÔÁÈ. ôÁËÉÍ ÏÂÒÁÚÏÍ
+ÍÏÖÎÏ ÚÁÐÒÏÓÉÔØ ÐÒÏÐÕÝÅÎÎÙÅ ÐÉÓØÍÁ ÉÚ ÁÒÈÉ×Á.
+
+</#aI/>
+þÔÏÂÙ ÐÏÌÕÞÉÔØ ÐÉÓØÍÏ 12345 ÉÚ ÁÒÈÉ×Á, ÐÏÛÌÉÔÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+   <<#L#>-get.12345@<#H#>>
+
+</#ia/>
+áÄÒÅÓ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÓÏÏÂÝÅÎÉÊ 123-145 (max 100 ÎÁ ÚÁÐÒÏÓ):
+   <<#L#>-get.123_145@<#H#>>
+
+áÄÒÅÓ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÓÐÉÓËÁ ÐÏÓÌÅÄÎÉÈ 100 ÓÏÏÂÝÅÎÉÊ Ó subject É Á×ÔÏÒÏÍ:
+   <<#L#>-index@<#H#>>
+
+<//>
+ðÏÓÌÅÄÎÉÅ ÎÏÍÅÒÁ ÐÉÓÅÍ × ÄÁÊÄÖÅÓÔÁÈ:
+
+</text/bounce-probe/>
+
+óÏÏÂÝÅÎÉÑ ÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#> ÎÁ ×ÁÛ ÁÄÒÅÓ
+ÐÒÉ×ÏÄÑÔ Ë ÏÛÉÂËÁÍ ÄÏÓÔÁ×ËÉ (bounce). ÷ÁÍ ÂÙÌÏ ÐÏÓÌÁÎÏ ÐÉÓØÍÏ Ó
+ÐÒÅÄÕÐÒÅÖÄÅÎÉÅÍ, ÎÏ ÏÎÏ ÔÏÖÅ ÎÅ ÂÙÌÏ ÄÏÓÔÁ×ÌÅÎÏ. ÷ ËÏÎÃÅ ÐÉÓØÍÁ
+ÐÒÉ×ÅÄÅÎÁ ËÏÐÉÑ ÓÏÏÂÝÅÎÉÑ Ï ÏÛÉÂËÅ.
+
+üÔÏ ÐÉÓØÍÏ Ñ×ÌÑÅÔÓÑ ÔÅÓÔÏÍ ÎÁ ÓÕÝÅÓÔ×Ï×ÁÎÉÅ É ÁËÔÉ×ÎÏÓÔØ ×ÁÛÅÇÏ 
+ÁÄÒÅÓÁ. åÓÌÉ ÐÏÐÙÔËÁ ÄÏÓÔÁ×ÉÔØ ×ÁÍ ÜÔÏ ÐÉÓØÍÏ ÔÁË ÖÅ ÂÕÄÅÔ ÎÅÕÄÁÞÎÁ,
+×ÁÛ ÁÄÒÅÓ ÂÕÄÅÔ ÕÄÁÌÅΠÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#> ÂÅÚ ÄÁÌØÛÅÊÛÉÈ
+ÐÒÅÄÕÐÒÅÖÄÅÎÉÊ. ðÏÄÐÉÓÁÔØÓÑ ÚÁÎÏ×Ï ÍÏÖÎÏ ÐÏ ÁÄÒÅÓÕ:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn/>
+
+óÏÏÂÝÅÎÉÑ ÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#> ÎÁ ×ÁÛ ÁÄÒÅÓ
+ÐÒÉ×ÏÄÑÔ Ë ÏÛÉÂËÁÍ ÄÏÓÔÁ×ËÉ (bounce). ÷ ËÏÎÃÅ ÐÉÓØÍÁ ÐÒÉ×ÅÄÅÎÁ ËÏÐÉÑ
+ÓÏÏÂÝÅÎÉÑ Ï ÏÛÉÂËÅ.
+
+åÓÌÉ ÜÔÏ ÐÉÓØÍÏ ÔÁË ÖÅ ÐÒÉ×ÅÄÅÔ Ë ÏÛÉÂËÅ, ×ÁÍ ÂÕÄÅÔ ×ÙÓÌÁΠÔÅÓÔ. åÓÌÉ
+ÔÅÓÔ ÔÏÖÅ ÐÒÉ×ÅÄÅÔ Ë ÏÛÉÂËÅ, ×ÁÛ ÁÄÒÅÓ ÂÕÄÅÔ ÕÄÁÌÅΠÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ
+<#l#>@<#H#> ÂÅÚ ÄÁÌØÎÅÊÛÉÈ ÐÒÅÄÕÐÒÅÖÄÅÎÉÊ.
+
+</text/digest#d/>
+áÄÒÅÓ ÄÌÑ ÐÏÄÐÉÓËÉ ÎÁ ÄÁÊÄÖÅÓÔ:
+       <#L#>-digest-subscribe@<#H#>
+
+áÄÒÅÓ ÄÌÑ ÏÔÐÉÓËÉ ÏÔ ÄÁÊÄÖÅÓÔÁ:
+       <#L#>-digest-unsubscribe@<#H#>
+
+áÄÒÅÓ ÄÌÑ ÏÔÐÒÁ×ËÉ ÐÉÓÅÍ × ÓÐÉÓÏË ÒÁÓÓÙÌËÉ:
+       <#L#>@<#H#>
+
+</text/get-bad/>
+éÚ×ÉÎÉÔÅ, ÎÏ ÔÁËÏÇÏ ÐÉÓØÍÁ × ÁÒÈÉ×Å ÎÅÔ.
+
+</text/help/>
+üÔÏ ÐÉÓØÍÏ Ñ×ÌÑÅÔÓÑ ÏÂÝÉÍ ÏÐÉÓÁÎÉÅÍ ÒÁÂÏÔÙ ezmlm.
+
+</text/mod-help/>
+óÐÁÓÉÂÏ, ÞÔÏ ×Ù ÓÏÇÌÁÓÉÌÉÓØ ÍÏÄÅÒÉÒÏ×ÁÔØ <#L#>@<#H#>.
+
+ëÏÍÁÎÄÙ ezmlm ÎÅÍÎÏÇÏ ÏÔÌÉÞÁÀÔÓÑ ÏÔ ÄÒÕÇÉÈ ÓÉÓÔÅÍ ÓÐÉÓËÏ×
+ÒÁÓÓÙÌËÉ ËÁË majordomo, listserver, etc, ÎÏ ÏÎÉ ÌÅÇËÏ
+ÚÁÐÏÍÉÎÁÀÔÓÑ É ÉÈ ÌÅÇËÏ ÉÓÐÏÌØÚÏ×ÁÔØ.
+
+HÉÖÅ ÐÒÉ×ÅÄÅÎÙ ÉÎÓÔÕËÃÉÉ ÐÏ ×ÙÐÏÌÎÅÎÉÀ ÚÁÄÁÞ, ÎÅÏÂÈÏÄÉÍÙÈ 
+×ÌÁÄÅÌØÃÕ ÓÐÉÓËÁ É/ÉÌÉ ÍÏÄÅÒÁÔÏÒÕ.
+
+õÄÁÌÅÎÎÁÑ ÐÏÄÐÉÓËÁ
+------------------
+ëÁË ÍÏÄÅÒÁÔÏÒ, ×Ù ÍÏÖÅÔÅ ÐÏÄÐÉÓÁÔØ ÉÌÉ ÏÔÐÉÓÁÔØ ÌÀÂÏÊ ÁÄÒÅÓ
+× ÓÐÉÓËÅ. äÌÑ ÐÏÄÐÉÓËÉ ÁÄÒÅÓÁ john@host.domain ÄÏÂÁ×ØÔÅ
+ÐÅÒÅÎÏÓ ÐÏÓÌÅ ËÏÍÁÎÄÙ, ÐÏÔÏÍ ÁÄÒÅÓ Ó = ×ÍÅÓÔÏ @. HÁÐÒÉÍÅÒ,
+ÄÌÑ ÐÏÄÐÉÓËÉ ×ÙÛÅÕËÁÚÁÎÎÏÇÏ ÁÄÒÅÓÁ, ÓÌÅÄÕÅÔ ÐÏÓÌÁÔØ ÐÉÓØÍÏ
+ÐÏ ÁÄÒÅÓÕ
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+ôÏÞÎÏ ÔÁË ÖÅ ÍÏÖÎÏ ÕÄÁÌÉÔØ ÁÄÒÅÓ ÉÚ ÓÐÉÓËÁ:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#d/>
+ôÏ ÖÅ ÓÁÍÏÅ ÄÌÑ ÄÁÊÄÖÅÓÔÏ×:
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+<//>
+÷ÏÔ É ×ÓÅ. HÅ ÔÒÅÂÕÅÔÓÑ ÎÉÞÅÇÏ ÚÁÐÏÌÎÑÔØ ÎÉ × subject, ÎÉ × ÔÅÌÅ ÐÉÓØÍÁ.
+
+</#r/>
+÷ÁÍ ÂÕÄÅÔ ×ÙÓÌÁΠÚÁÐÒÏÓ ÎÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ, ÄÅÊÓÔ×ÉÔÅÌØÎÏ ÌÉ ×Ù ÈÏÔÅÌÉ
+×ÙÐÏÌÎÉÔØ ÐÏÄÐÉÓËÕ/ÏÔÐÉÓËÕ. HÁÄÏ ÐÒÏÓÔÏ ÏÔ×ÅÔÉÔØ ÎÁ ÎÅÇÏ.
+</#R/>
+âÕÄÅÔ ×ÙÓÌÁΠÚÁÐÒÏÓ ÎÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ ÐÏÄÐÉÓËÉ ÐÏ ÁÄÒÅÓÕ <john@host.domain>.
+ðÏÌØÚÏ×ÁÔÅÌÀ ÄÏÓÔÁÔÏÞÎÏ ÂÕÄÅÔ ÏÔ×ÅÔÉÔØ ÎÁ ÚÁÐÒÏÓ.
+<//>
+
+óÉÓÔÅÍÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ ÁÂÓÏÌÀÔÎÁ ÎÅÏÂÈÏÄÉÍÁ, ÞÔÏÂÙ ÎÅ ÄÁÔØ ÎÅÄÏÂÒÏÖÅÌÁÔÅÌÀ
+×ÏÚÍÏÖÎÏÓÔÉ ÄÏÂÁ×ÉÔØ ÉÌÉ ÕÄÁÌÉÔØ ÁÄÒÅÓ × ÓÐÉÓËÅ ÂÅÚ ÖÅÌÁÎÉÑ ×ÌÁÄÅÌØÃÁ
+ÁÄÒÅÓÁ.
+
+ðÏÄÐÉÓËÁ
+--------
+
+ìÀÂÏÊ ÐÏÌØÚÏ×ÁÔÅÌØ ÍÏÖÅÔ ÐÏÄÐÉÓÁÔØÓÑ ÉÌÉ ÏÔÐÉÓÁÔØÓÑ,
+ÐÏÓÌÁ× ÐÉÓØÍÏ ÎÁ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÊ ÁÄÒÅÓ:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#d/>
+äÌÑ ÄÁÊÄÖÅÓÔÏ×:
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+<//>
+ðÏÌØÚÏ×ÁÔÅÌØ ÐÏÌÕÞÉÔ ÚÁÐÒÏÓ ÎÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ, ÞÔÏÂÙ ÕÂÅÄÉÔØÓÑ
+× ÔÏÍ, ÞÔÏ ÚÁÐÒÏÓ ÂÙÌ ÓÄÅÌÁΠÉÍÅÎÎÏ ÉÍ. 
+
+</#s/>
+ðÏÓËÏÌØËÕ × ÄÁÎÎÏÍ ÓÐÉÓËÅ ËÏÎÔÒÏÌÉÒÕÅÔÓÑ ÐÏÄÐÉÓËÁ/ÏÔÐÉÓËÁ,
+ÂÕÄÅÔ ×ÙÓÌÁΠÄÏÐÏÌÎÉÔÅÌØÎÙÊ ÚÁÐÒÏÓ ÍÏÄÅÒÁÔÏÒÕ. ðÏÓËÏÌØËÕ
+ÐÏÌØÚÏ×ÁÔÅÌØ ÕÖÅ ÐÏÄÔ×ÅÒÄÉÌ Ó×ÏÅ ÖÅÌÁÎÉÅ, ×Ù, ËÁË ÍÏÄÅÒÁÔÏÒ,
+ÍÏÖÅÔÅ ÂÙÔØ Õ×ÅÒÅÎÙ, ÞÔÏ ÜÔÏ ÉÍÅÎÎÏ ÅÇÏ ÖÅÌÁÎÉÅ, Á ÁÄÒÅÓ
+ÒÁÂÏÔÁÀÝÉÊ. åÓÌÉ ×Ù ÓÏÇÌÁÓÎÙ Ó ÐÏÄÐÉÓËÏÊ ÄÁÎÎÏÇÏ ÐÏÌØÚÏ×ÁÔÅÌÑ,
+ÐÒÏÓÔÏ ÐÏÛÌÉÔÅ ÏÔ×ÅÔ ÎÁ ÄÁÎÎÏÅ ÐÉÓØÍÏ. åÓÌÉ ÎÅÔ, ÔÏ ÓÏÔÒÉÔÅ
+ÅÇÏ É ×ÓÅ.
+
+</#S/>
+ïÔÐÉÓËÁ ÒÁÂÏÔÁÅÔ ÔÁËÉÍ ÖÅ ÏÂÒÁÚÏÍ.
+<//>
+
+ðÏÌØÚÏ×ÁÔÅÌØ ÔÁË ÖÅ ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÁÄÒÅÓÁ:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+ÄÌÑ ÐÏÄÐÉÓËÉ mary@host.domain. óÐÉÓÏË ÂÕÄÅÔ ÉÚÍÅÎÅΠÔÏÌØËÏ ÅÓÌÉ
+ËÔÏ-ÔÏ ÎÁ ÜÔÏÍ ÁÄÒÅÓÅ ÏÔ×ÅÔÉÔ ÎÁ ÚÁÐÒÏÓ.
+
+÷ÁÛ ÁÄÒÅÓ É ÐÒÏÞÁÑ ÉÎÆÏÒÍÁÃÉÑ ÎÅ ÂÕÄÕÔ ÄÏÓÔÕÐÎÙ ÐÏÄÐÉÓÞÉËÕ, ÒÁÚ×Å
+ÞÔÏ ×Ù ÐÏÛÌÅÔÅ ÏÔÄÅÌØÎÏÅ ÐÉÓØÍÏ ÅÍÕ ÎÁÐÒÑÍÕÀ.
+
+</#rl/>
+þÔÏÂÙ ÐÏÌÕÞÉÔØ ÓÐÉÓÏË ÐÏÄÐÉÓÞÉËÏ× <#L#>@<#H#>, ÐÏÛÌÉÔÅ ÐÉÓØÍÏ ÓÀÄÁ:
+   <<#L#>-list@<#H#>>
+
+þÔÏÂÙ ÐÏÌÕÞÉÔØ ÌÏÇ ÔÒÁÎÚÁËÃÉÊ <#L#>@<#H#>, ÐÉÛÉÔÅ ÓÀÄÁ:
+   <<#L#>-log@<#H#>>
+
+</#rld/>
+äÌÑ ÄÁÊÄÖÅÓÔÏ×:
+   <<#L#>-digest-list@<#H#>>
+É:
+   <<#L#>-digest-log@<#H#>>
+
+</#rn/>
+÷Ù ÍÏÖÅÔÅ ÒÅÄÁËÔÉÒÏ×ÁÔØ ÐÏ ÐÏÞÔÅ ÔÅËÓÔÏ×ÙÅ ÆÁÊÌÙ ËÏÎÆÉÇÕÒÁÃÉÉ
+ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ. äÌÑ ÐÏÌÕÞÅÎÉÑ ÓÐÉÓËÁ ÆÁÊÌÏ× É ÉÎÓÔÒÕËÃÉÊ ÐÏ
+ÒÅÄÁËÔÉÒÏ×ÁÎÉÀ, ÐÉÛÉÔÅ ÓÀÄÁ:
+   <<#L#>-edit@<#H#>>
+
+</#m/>
+íÏÄÅÒÉÒÏ×ÁÎÉÅ
+-------------
+ëÏÇÄÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ ÍÏÄÅÒÉÒÕÅÔÓÑ, ÐÉÓØÍÁ ÓÏÈÒÁÎÑÀÔÓÑ É ×ÓÅÍ
+ÍÏÄÅÒÁÔÏÒÁÍ ÐÏÓÙÌÁÅÔÓÑ ËÏÐÉÑ ÐÉÓØÍÁ Ó ÉÎÓÔÒÕËÃÉÅÊ. Subject ÓÏÄÅÒÖÉÔ
+ÓÔÒÏËÕ "MODERATE for ...".
+
+ðÉÓØÍÏ ÓÏÄÅÒÖÉÔ Ä×Á ÚÁÇÏÌÏ×ËÁ: "From:" É "Reply-To:". ôÁËÉÍ ÏÂÒÁÚÏÍ,
+ËÏÇÄÁ ×Ù ÎÁ ÎÅÇÏ ÏÔ×ÅÞÁÅÔÅ, ×ÁÛÁ ÐÏÞÔÏ×ÁÑ ÐÒÏÇÒÁÍÍÁ ÄÏÌÖÎÁ ÓÐÒÏÓÉÔØ,
+ÎÁ ËÁËÏÊ ÉÚ ÁÄÒÅÓÏ× ÏÔ×ÅÞÁÔØ. ïÔ×ÅÔ ÎÁ ÁÄÒÅÓ × Reply-To: ÐÒÉ×ÅÄÅÔ
+Ë ÔÏÍÕ, ÞÔÏ ÉÓÈÏÄÎÏÅ ÐÉÓØÍÏ ÂÕÄÅÔ ÐÒÏÐÕÝÅÎÏ × ÓÐÉÓÏË ÒÁÓÓÙÌËÉ. 
+ïÔ×ÅÔ ÎÁ "From:" ÐÒÉ×ÅÄÅÔ Ë ÏÔËÁÚÕ. ïÂÙÞÎÏ ÐÒÏÇÒÁÍÍÙ ÓÐÒÁÛÉ×ÁÀÔ
+"äÁ/ÎÅÔ", Ô.Å. ×Ù ÐÒÏÓÔÏ ÒÅÛÁÅÔÅ, ÐÒÏÐÕÓËÁÔØ ÉÌÉ ÎÅÔ, ÎÁÖÉÍÁÅÔÅ
+ÏÔ×ÅÔ É ×ÙÂÉÒÁÅÔÅ "ÄÁ" ÉÌÉ "ÎÅÔ". óÏÄÅÒÖÉÍÏÅ ×ÁÛÅÇÏ ÐÉÓØÍÁ
+ÐÒÁËÔÉÞÅÓËÉ ÉÇÎÏÒÉÒÕÅÔÓÑ -- ÚÎÁÞÅÎÉÅ ÉÍÅÀÔ ÔÏÌØËÏ ÁÄÒÅÓÁ, ÏÄÎÁËÏ
+ÐÒÉ ÏÔËÁÚÅ ÍÏÖÎÏ ×ÓÔÁ×ÉÔØ × ÔÅÌÏ ÐÉÓØÍÁ ÔÅËÓÔ ÍÅÖÄÕ Ä×ÕÍÑ ÓÔÒÏËÁÍÉ,
+ÎÁÞÉÎÁÀÝÉÍÉÓÑ Ó ÓÉÍ×ÏÌÏ× %. üÔÏÔ ÔÅËÓÔ ÂÕÄÅÔ ÐÏÓÌÁΠÏÔÐÒÁ×ÉÔÅÌÀ 
+ÐÉÓØÍÁ, ÎÅ ÏÔËÒÙ×ÁÑ ËÔÏ ÉÚ ÍÏÄÅÒÁÔÏÒÏ× ÅÇÏ ÐÏÓÌÁÌ. HÁÐÒÉÍÅÒ:
+
+%%% Start comment
+×ÁÛÅ ÐÉÓØÍÏ ÓÏÄÅÒÖÉÔ ÍÁÔ
+%%% End comment
+
+åÓÌÉ ×ÁÛÁ ÐÏÞÔÏ×ÁÑ ÐÒÏÇÒÁÍÍÁ ÕÍÅÅÔ ÒÁÂÏÔÁÔØ Ó ÔÅÍÐÌÅÊÔÁÍÉ (ÎÁÐÒÉÍÅÒ,
+The Bat!), ÔÏ ÜÔÉ ÓÔÒÏËÉ ÓÔÏÉÔ ÄÏÂÁ×ÉÔØ × ÔÅÍÐÌÅÊÔ ÏÔ×ÅÔÁ.
+
+úÁÐÒÏÓÙ ÎÁ ÍÏÄÅÒÉÒÏ×ÁÎÉÅ ÏÂÒÁÂÁÔÙ×ÁÀÔÓÑ ÐÏ ÐÅÒ×ÏÍÕ ÐÉÓØÍÕ ÏÔ ÍÏÄÅÒÁÔÏÒÁ,
+ÓÒÅÁÇÉÒÏ×Á×ÛÅÇÏ ÒÁÎØÛÅ. åÓÌÉ ËÔÏ-ÔÏ ÉÚ ÍÏÄÅÒÁÔÏÒÏ× ÐÏÚÖÅ ÐÏÛÌÅÔ ÏÔ×ÅÔ
+Ó ÐÒÏÔÉ×ÏÐÏÌÏÖÎÙÍ ÒÅÛÅÎÉÅÍ, ÅÍÕ ÂÕÄÅÔ ÓÏÏÂÝÅÎÏ Ï ÔÏÍ, ÞÔÏ ÕÖÅ ÐÒÏÉÚÏÛÌÏ
+Ó ÄÁÎÎÙÍ ÐÉÓØÍÏÍ.
+
+åÓÌÉ × ÔÅÞÅÎÉÉ ÎÅÓËÏÌØËÉÈ ÄÎÅÊ ÎÅ ÂÕÄÅÔ ÐÏÌÕÞÅÎÏ ÏÔ×ÅÔÁ ÎÉ ÏÔ ÏÄÎÏÇÏ
+ÍÏÄÅÒÁÔÏÒÁ, ÏÔÐÒÁ×ÉÔÅÌÀ ÂÕÄÅÔ ÐÏÓÌÁÎÏ Õ×ÅÄÏÍÌÅÎÉÅ Ï ÚÁÄÅÒÖËÅ. ôÁË ÖÅ,
+ÁÄÍÉÎÉÓÔÒÁÔÏÒ ÓÐÉÓËÁ ÍÏÖÅÔ ÚÁÐÒÅÔÉÔØ ÏÔÓÙÌËÕ ÐÏÄÏÂÎÙÈ Õ×ÅÄÏÍÌÅÎÉÊ.
+<//>
+
+ëÁÎÉËÕÌÙ
+--------
+åÓÌÉ ×Ù ÄÏÌÖÎÙ ÓÒÏÞÎÏ ÐÏËÉÎÕÔØ Ó×ÏÊ ÌÀÂÉÍÙÊ ÇÏÒÏÄ, Á ÔÁÍ, ËÕÄÁ
+×Ù ÓÏÂÒÁÌÉÓØ, ÉÎÔÅÒÎÅÔÁ ÎÅÔ É ÎÅ ÐÒÅÄ×ÉÄÉÔÓÑ, ×Ù ÍÏÖÅÔÅ ÎÁ ×ÒÅÍÑ
+×ËÌÀÞÉÔØ Á×ÔÏÍÁÔÉÞÅÓËÉÊ ÐÒÏÐÕÓË ÐÉÓÅÍ × ÓÐÉÓÏË. ïÄÎÁËÏ ×Ï ÍÎÏÇÉÈ
+ÓÐÉÓËÁÈ ÐÏÄÏÂÎÙÅ ÄÅÊÓÔ×ÉÑ ÍÏÇÕÔ ÐÒÉ×ÅÓÔÉ Ë ÂÁÒÄÁËÕ.
+
+äÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÐÏÓÔÁ×ÉÔØ Á×ÔÏÏÔ×ÅÔÞÉË ÎÁ ×ÁÛÅÍ ÁÄÒÅÓÅ,
+ÏÔÐÒÁ×ÌÑÀÝÉÊ ×ÓÅ ÐÉÓØÍÁ Ó subject "MODERATE for .." ÎÁ ÁÄÒÅÓ × 
+ÚÁÇÏÌÏ×ËÅ "Reply-To:". Hå òåëïíåHäõåôóñ. 
+
+</#r/>
+åÓÌÉ ×Ù ÐÏÐÒÏÂÕÅÔÅ ÐÏÓÌÁÔØ ÁÄÍÉÎÓÔÒÁÔÉ×ÎÙÊ ÚÁÐÒÏÓ ÎÅ Ó Ó×ÏÅÇÏ ÁÄÒÅÓÁ,
+ÔÏ ÐÏÄÐÉÓÞÉË, Á ÎÅ ×Ù ÂÕÄÅÔ ÓÐÒÏÛÅÎ, ÐÏÄÐÉÓÙ×ÁÔØ ÉÌÉ ÎÅÔ. üÔÏ ÓÄÅÌÁÎÏ
+ÄÌÑ ÔÏÇÏ, ÞÔÏÂÙ ÎÉËÔÏ ÎÅ ÐÏÓÌÁÌ ÐÏÄÄÅÌØÎÙÊ ÚÁÐÒÏÓ ÎÁ ÐÏÄÐÉÓËÕ 
+ÏÔ ×ÁÛÅÇÏ ÁÄÒÅÓÁ, ÐÏÄÐÉÓÁ× Ó×ÏÅÇÏ ×ÒÁÇÁ ÎÁ ×ÙÓÏËÏÔÒÁææÉËÏ×ÙÊ ÓÐÉÓÏË
+ÒÁÓÓÙÌËÉ.
+
+<//>
+
+õÄÁÞÉ!
+
+PS: ÷ ÓÌÕÞÁÅ ÐÒÏÂÌÅÍ Ó×ÑÚÙ×ÁÊÔÅÓØ Ó ×ÌÁÄÅÌØÃÅÍ ÓÐÉÓËÁ 
+ÒÁÓÓÙÌËÉ (<#L#>-owner@<#H#>).
+
+</text/mod-reject/>
+éÚ×ÉÎÉÔÅ, ÎÏ ×ÁÛÅ ÐÉÓØÍÏ (ÎÉÖÅ ÐÒÉ×ÅÄÅÎÎÏÅ) ÎÅ ÂÙÌÏ ÐÒÏÐÕÝÅÎÏ ×
+ÓÐÉÓÏË ÍÏÄÅÒÁÔÏÒÏÍ. åÓÌÉ ÍÏÄÅÒÁÔÏÒ ÈÏÔÅÌ(Á) ÓÏÏÂÝÉÔØ ÞÔÏ-ÌÉÂÏ ×ÁÍ
+ÐÏ ÐÏ×ÏÄÕ ×ÁÛÅÇÏ ÐÉÓØÍÁ, ËÏÍÍÅÎÔÁÒÉÉ ÂÕÄÕÔ ÐÒÉ×ÅÄÅÎÙ ÎÉÖÅ.
+</text/mod-request/>
+HÉÖÅÐÒÉ×ÅÄÅÎÎÏÅ ÐÉÓØÍÏ ÂÙÌÏ ÏÔÐÒÁ×ÌÅÎÏ × ÓÐÉÓÏË <#L#>@<#H#>
+åÓÌÉ ×Ù ÓÏÇÌÁÓÎÙ ÅÇÏ ÐÒÏÐÕÓÔÉÔØ, ÐÏÛÌÉÔÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!A
+
+ïÂÙÞÎÏ ÄÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÎÁÖÁÔØ ËÎÏÐËÕ "reply" ÉÌÉ "ÏÔ×ÅÔ" ÄÌÑ
+ÄÁÎÎÏÇÏ ÐÉÓØÍÁ. ïÂÑÚÁÔÅÌØÎÏ ÐÒÏ×ÅÒØÔÅ, ÞÔÏÂÙ × ÐÏÌÅ "To:" ÂÙÌ ÔÏÌØËÏ
+ÏÄÉΠÁÄÒÅÓ. åÓÌÉ ÜÔÏ ÎÅ ÓÒÁÂÏÔÁÅÔ, ÓËÏÐÉÒÕÊÔÅ ÁÄÒÅÓ × clipboard É
+×ÓÔÁ×ØÔÅ ÅÇÏ × ÐÏÌÅ "To:". 
+</#x/>
+<//>
+
+äÌÑ ÏÔËÁÚÁ ÏÔ ÐÒÏÐÕÓËÁ ÐÉÓØÍÁ É ÓÏÏÂÝÅÎÉÑ Ï ÜÔÏÍ ÐÉÛÕÝÅÍÕ ÐÏÛÌÉÔÅ
+ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ
+
+!R
+</#x/>
+<//>
+
+÷ÁÍ ÎÅ ÎÕÖÎÏ ËÏÐÉÒÏ×ÁÔØ ÔÅÌÏ ÉÓÈÏÄÎÏÇÏ ÐÉÓØÍÁ. äÌÑ ÔÏÇÏ, ÞÔÏÂÙ ÐÏÓÌÁÔØ
+×ÁÛ ËÏÍÍÅÎÔÁÒÉÊ ÐÏ ÐÏ×ÏÄÕ ÔÏÇÏ, ÐÏÞÅÍÕ ÐÉÓØÍÏ ÎÅ ÂÙÌÏ ÐÒÏÐÕÝÅÎÏ,
+×ÓÔÁ×ØÔÅ ×ÁÛ ÔÅËÓÔ ÍÅÖÄÕ Ä×ÕÍÑ ÓÔÒÏËÁÍÉ Ó %%%.
+
+%%% Start comment
+%%% End comment
+
+ëÏÍÍÅÎÔÁÒÉÉ ÄÏÌÖÎÙ ÎÁÞÉÎÁÔØÓÑ Ó ÎÁÞÁÌÁ ÓÔÒÏËÉ.
+
+--- éÓÈÏÄÎÏÅ ÐÉÓØÍÏ × ÓÐÉÓÏË.
+
+</text/mod-sub#E/>
+--- ÷ÁÓ ÐÏÄÐÉÓÁÌÉ ÉÌÉ ÏÔÐÉÓÁÌÉ ÐÏ ÚÁÐÒÏÓÕ ÍÏÄÅÒÁÔÏÒÁ 
+ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#>.
+
+åÓÌÉ ×Ù ÜÔÏÇÏ ÎÅ ÈÏÔÅÌÉ, ×Ù ÍÏÖÅÔÅ ÎÁÐÉÓÁÔØ ÖÁÌÏÂÕ
+×ÌÁÄÅÌØÃÕ ÓÐÉÓËÁ ÐÏ ÁÄÒÅÓÕ <#l#>-owner@<#H#>.
+
+åÓÌÉ ×ÁÓ ÉÎÔÅÒÅÓÕÅÔ ÉÎÆÏÒÍÁÃÉÑ Ï ÓÐÉÓËÅ ÒÁÓÓÙÌËÉ <#L#>,
+ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ <#l#>-help@<#H#>.
+
+</text/mod-timeout/>
+éÚ×ÉÎÉÔÅ, ÎÏ ÍÏÄÅÒÁÔÏÒ(Ù) ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#L#>@<#H#>
+ÎÅ ÐÒÅÄÐÒÉÎÉÍÁÀÔ ÄÅÊÓÔ×ÉÊ ÄÌÑ ÐÒÏÐÕÓËÁ ÉÌÉ ÏÔËÁÚÁ ÐÏ ÐÏ×ÏÄÕ 
+×ÁÛÅÇÏ ÐÉÓØÍÁ × ÓÐÉÓÏË.
+
+--- éÓÈÏÄÎÏÅ ÐÉÓØÍÏ.
+
+</text/mod-sub-confirm/>
+üÔÏ ÚÁÐÒÏÓ ×ÁÛÅÇÏ ÒÁÚÒÅÛÅÎÉÑ ÎÁ ÄÏÂÁ×ÌÅÎÉÅ ÁÄÒÅÓÁ
+
+!A
+
+× ÓÐÉÓÏË ÒÁÓÓÙÌËÉ <#l#>@<#H#>. 
+úÁÐÒÏÓ ÐÒÏÉÚÏÛÅÌ ÐÏÔÏÍÕ, ÞÔÏ ×Ù (ÉÌÉ ÎÅ ×Ù) ÐÏÐÙÔÁÌÉÓØ 
+ÐÏÄÐÉÓÁÔØ ×ÁÛ ÁÄÒÅÓ ÎÁ ×ÙÛÅÕÐÏÍÑÎÕÔÙÊ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ.
+
+äÌÑ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ ÐÏÄÐÉÓËÉ, ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!R
+
+äÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÎÁÖÁÔØ ËÎÏÐËÕ "reply" ÉÌÉ "ÏÔ×ÅÔ".
+åÓÌÉ ÜÔÏ ÎÅ ÓÒÁÂÏÔÁÅÔ, ÓËÏÐÉÒÕÊÔÅ ÁÄÒÅÓ × clipboard É 
+×ÓÔÁ×ØÔÅ ÅÇÏ × ÐÏÌÅ "To:".
+</#x/>
+<//>
+
+åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ÐÏÄÐÉÓÙ×ÁÔØÓÑ, ÐÒÏÓÔÏ ÎÅ ÏÔ×ÅÞÁÊÔÅ ÎÁ ÜÔÏ ÐÉÓØÍÏ.
+
+</text/mod-unsub-confirm/>
+úÁÐÒÏÓ ÎÁ ÒÁÚÒÅÛÅÎÉÅ ÕÄÁÌÅÎÉÑ
+
+!A
+
+ÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#>. åÓÌÉ ×Ù ÓÏÇÌÁÓÎÙ, 
+ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!R
+
+äÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÎÁÖÁÔØ ËÎÏÐËÕ "reply" ÉÌÉ "ÏÔ×ÅÔ".
+åÓÌÉ ÜÔÏ ÎÅ ÐÏÍÏÇÁÅÔ, ÓËÏÐÉÒÕÊÔÅ ÔÅËÓÔ É ×ÓÔÁ×ØÔÅ ÅÇÏ × 
+ÐÏÌÅ "To:" ÎÏ×ÏÇÏ ÐÉÓØÍÁ.
+</#x/>
+<//>
+
+åÓÌÉ ×Ù ÎÅ ÓÏÇÌÁÓÎÙ, ÉÇÎÏÒÉÒÕÊÔÅ ÜÔÏ ÐÉÓØÍÏ.
+
+</text/sub-bad/>
+ïÊ. ëÏÄ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ ÎÅËÏÒÒÅËÔÅÎ.
+
+óËÏÒÅÅ ×ÓÅÇÏ ÐÒÏÛÌÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ×ÒÅÍÅÎÉ ÐÏÓÌÅ ÐÏÌÕÞÅÎÉÑ ×ÁÍÉ
+ÚÁÐÒÏÓÁ. íÁËÓÉÍÕÍ 10 ÄÎÅÊ. ôÁË ÖÅ ×ÏÚÍÏÖÎÏ, ÞÔÏ ×ÁÛÁ ÐÏÞÔÏ×ÁÑ
+ÐÒÏÇÒÁÍÍÁ ÓßÅÌÁ ÞÁÓÔØ ÁÄÒÅÓÁ, ËÏÔÏÒÙÊ É ÓÏÄÅÒÖÉÔ ËÏÄ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ.
+
+äÌÑ ×ÁÓ ÓÏÚÄÁΠÎÏ×ÙÊ ËÏÄ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ. þÔÏÂÙ ÏÄÏÂÒÉÔØ ÐÏÄÐÉÓËÕ
+
+!A
+
+ÎÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ <#l#>@<#H#>, ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ
+ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!R
+</#x/>
+<//>
+
+ïÂÑÚÁÔÅÌØÎÏ ÐÒÏ×ÅÒØÔÅ ÁÄÒÅÓ, ÎÁ ËÏÔÏÒÙÊ ×Ù ÐÏÓÙÌÁÅÔÅ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ.
+
+</text/sub-confirm/>
+äÌÑ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ ÐÏÄÐÉÓËÉ ÁÄÒÅÓÁ
+
+!A
+
+ÎÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ <#l#>@<#H#>, ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!R
+
+äÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÎÁÖÁÔØ ËÎÏÐËÕ "reply" ÉÌÉ "ÏÔ×ÅÔ".
+</#x/>
+<//>
+
+üÔÏ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ ÎÅÏÂÈÏÄÉÍÏ ÐÏ Ä×ÕÍ ÐÒÉÞÉÎÁÍ. ÷Ï-ÐÅÒ×ÙÈ, ÐÒÏ×ÅÒÑÅÔÓÑ,
+ÄÏÈÏÄÉÔ ÌÉ ÐÏÞÔÁ ÄÏ ×ÁÛÅÇÏ ÁÄÒÅÓÁ. ÷Ï-×ÔÏÒÙÈ, ÜÔÏ ÚÁÝÉÝÁÅÔ ×ÁÓ ÏÔ
+ÐÏÄÐÉÓËÉ, ÅÓÌÉ ËÔÏ-ÔÏ ÐÏÛÌÅÔ ÐÉÓØÍÏ Ó ÐÏÄÄÅÌÁÎÎÙÍ ×ÁÛÉÍ ÉÓÈÏÄÎÙÍ ÁÄÒÅÓÏÍ.
+
+</#q/>
+HÅËÏÔÏÒÙÅ ÓÔÁÒÙÅ ÐÏÞÔÏ×ÙÅ ÐÒÏÇÒÁÍÍÙ ÎÅ ÍÏÇÕÔ ÓÐÒÁ×ÉÔÓÑ Ó ÄÌÉÎÎÙÍÉ
+ÁÄÒÅÓÁÍÉ. åÓÌÉ ×Ù ÎÅ ÍÏÖÅÔÅ ÐÏÓÌÁÔØ ÏÔ×ÅÔ ÎÁ ÄÁÎÎÏÅ ÐÉÓØÍÏ, ÐÏÛÌÉÔÅ
+ÏÔ×ÅÔ ÎÁ <<#L#>-request@<#H#>> É ×ÓÔÁ×ØÔÅ 
+×ÅÓØ ÁÄÒÅÓ × ÐÏÌÅ subject.
+
+</text/sub-confirm#s/>
+üÔÏÔ ÓÐÉÓÏË ÍÏÄÅÒÉÒÕÅÍÙÊ. ëÏÇÄÁ ×Ù ÐÏÛÌÅÔÅ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ, ÍÏÄÅÒÁÔÏÒ(Ù)
+ÂÕÄÕÔ ÉÚ×ÅÝÅÎÙ Ï ÜÔÏÍ. ÷ ÓÌÕÞÁÅ ÏÄÏÂÒÅÎÉÑ ×ÁÛÅÊ ÐÏÄÐÉÓËÉ ÍÏÄÅÒÁÔÏÒÁÍ
+×ÁÍ ÂÕÄÅÔ ÓÏÏÂÝÅÎÏ.
+
+</text/sub-nop/>
+ðÏÄÔ×ÅÒÖÄÅÎÉÅ: ÁÄÒÅÓ
+
+!A
+
+ÕÖÅ ÐÏÄÐÉÓÁΠÎÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ <#l#>@<#H#>.
+
+</text/sub-ok#E/>
+ðÏÄÔ×ÅÒÖÄÅÎÉÅ: ÁÄÒÅÓ
+
+!A
+
+ÐÏÄÐÉÓÁΠÎÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ <#l#>@<#H#>.
+
+äÏÂÒÏ ÐÏÖÁÌÏ×ÁÔØ!
+
+ðÏÖÁÌÕÊÓÔÁ, ÓÏÈÒÁÎÉÔÅ ÜÔÏ ÐÉÓØÍÏ, ÞÔÏÂÙ ÎÅ ÚÁÂÙÔØ ÁÄÒÅÓ, ËÏÔÏÒÙÊ
+×Ù ÐÏÄÐÉÓÁÌÉ ÎÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ. þÅÒÅÚ ÐÏÌÇÏÄÁ ×Ù ÅÇÏ ÏÂÑÚÁÔÅÌØÎÏ
+ÚÁÂÕÄÅÔÅ, Á ÏÔËÁÚÁÔØÓÑ ÏÔ ÐÏÄÐÉÓËÉ, ÎÅ ÚÎÁÑ ÉÓÈÏÄÎÙÊ ÁÄÒÅÓ, ÂÕÄÅÔ
+ÏÞÅÎØ ÔÒÕÄÎÏ.
+
+</text/top/>
+äÏÂÒÙÊ ÄÅÎØ/ÕÔÒÏ/×ÅÞÅÒ! üÔÏ ÓÏÏÂÝÅÎÉÅ ÏÔ ÐÒÏÇÒÁÍÍÙ ezmlm,
+ÚÁ×ÅÄÕÀÝÅÊ ÓÐÉÓËÏÍ ÒÁÓÓÙÌËÉ <#l#>@<#H#>.
+
+</#x/>
+ó×ÑÚÁÔØÓÑ Ó ×ÌÁÄÅÌØÃÅÍ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ ÍÏÖÎÏ ÐÏ ÁÄÒÅÓÕ
+<#l#>-owner@<#H#>.
+
+</text/unsub-bad/>
+ïÊ. ëÏÄ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ ÎÅËÏÒÒÅËÔÅÎ.
+
+óËÏÒÅÅ ×ÓÅÇÏ ÐÒÏÛÌÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ×ÒÅÍÅÎÉ ÐÏÓÌÅ ÐÏÌÕÞÅÎÉÑ ×ÁÍÉ
+ÚÁÐÒÏÓÁ. íÁËÓÉÍÕÍ 10 ÄÎÅÊ. ôÁË ÖÅ ×ÏÚÍÏÖÎÏ, ÞÔÏ ×ÁÛÁ ÐÏÞÔÏ×ÁÑ
+ÐÒÏÇÒÁÍÍÁ ÓßÅÌÁ ÞÁÓÔØ ÁÄÒÅÓÁ, ËÏÔÏÒÙÊ É ÓÏÄÅÒÖÉÔ ËÏÄ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ.
+
+äÌÑ ×ÁÓ ÓÏÚÄÁΠÎÏ×ÙÊ ËÏÄ ÐÏÄÔ×ÅÒÖÄÅÎÉÑ. þÔÏÂÙ ÏÄÏÂÒÉÔØ ÕÄÁÌÅÎÉÅ
+ÁÄÒÅÓÁ
+
+!A
+
+ÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#>, ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!R
+</#x/>
+<//>
+
+ïÂÑÚÁÔÅÌØÎÏ ÐÒÏ×ÅÒØÔÅ ÁÄÒÅÓ, ÎÁ ËÏÔÏÒÙÊ ×Ù ÐÏÓÙÌÁÅÔÅ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ.
+
+</text/unsub-confirm/>
+äÌÑ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ ÕÄÁÌÅÎÉÑ ÁÄÒÅÓÁ
+
+!A
+
+ÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#>, ÐÏÛÌÉÔÅ ÐÕÓÔÏÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ:
+
+!R
+
+äÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÎÁÖÁÔØ ËÎÏÐËÕ "reply" ÉÌÉ "ÏÔ×ÅÔ".
+</#x/>
+<//>
+
+÷ÎÉÍÁÎÉÅ! þÔÏÂÙ ÕÚÎÁÔØ, ÐÏÄ ËÁËÉÍ ÁÄÒÅÓÏÍ ×Ù ÐÏÄÐÉÓÁÎÙ ÎÁ ÓÐÉÓÏË
+ÒÁÓÓÙÌËÉ, ÚÁÇÌÑÎÉÔÅ × ÚÁÇÏÌÏ×ËÉ ÏÄÎÏÇÏ ÉÚ ÐÉÓÅÍ ÉÚ ÓÐÉÓËÁ. ÷
+ËÁÖÄÏÍ ÐÉÓØÍÅ ÅÓÔØ ÚÁÇÏÌÏ×ÏË "Return-Path:", ×ÎÕÔÒÉ ËÏÔÏÒÇÏ
+ÎÁÈÏÄÉÔÓÑ ÁÄÒÅÓ ÐÏÌÕÞÁÔÅÌÑ. HÁÐÒÉÍÅÒ, ÐÒÉ ÁÄÒÅÓÅ vassily.pupkin@usa.net
+ÚÁÇÏÌÏ×ÏË ÂÕÄÅÔ ×ÙÇÌÑÄÅÔØ ÔÁË:
+Return-Path: <<#l#>-return-<ÞÉÓÌÏ>-vassily.pupkin=usa.net@<#H#>
+
+</#q/>
+HÅËÏÔÏÒÙÅ ÓÔÁÒÙÅ ÐÏÞÔÏ×ÙÅ ÐÒÏÇÒÁÍÍÙ ÎÅ ÍÏÇÕÔ ÓÐÒÁ×ÌÑÔØÓÑ Ó ÄÌÉÎÎÙÍÉ
+ÁÄÒÅÓÁÍÉ. åÓÌÉ ×Ù ÎÅ ÍÏÖÅÔÅ ÐÏÓÌÁÔØ ÏÔ×ÅÔ ÎÁ ÄÁÎÎÏÅ ÐÉÓØÍÏ, ÐÏÛÌÉÔÅ
+ÏÔ×ÅÔ ÎÁ <<#L#>-request@<#H#>> É ×ÓÔÁ×ØÔÅ 
+×ÅÓØ ÁÄÒÅÓ × ÐÏÌÅ subject.
+
+</text/unsub-nop/>
+÷ÎÉÍÁÎÉÅ: ÁÄÒÅÓ
+
+!A
+
+ÎÅ ÐÏÄÐÉÓÁΠÎÁ ÓÐÉÓÏË ÒÁÓÓÙÌËÉ <#l#>@<#H#>!
+
+åÓÌÉ ×Ù ÏÔÐÉÓÙ×ÁÅÔÅÓØ ÏÔ ÓÐÉÓËÁ, ÎÏ ÐÒÏÄÏÌÖÁÅÔÅ ÐÏÌÕÞÁÔØ ÓÏÏÂÝÅÎÉÑ,
+ÚÎÁÞÉÔ ×Ù ÐÏÄÐÉÓÁÎÙ ÐÏÄ ÄÒÕÇÉÍ ÁÄÒÅÓÏÍ. HÁÊÄÉÔÅ ÚÁÇÏÌÏ×ÏË ×ÉÄÁ
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+× ÏÄÎÏÍ ÉÚ ÔÁËÉÈ ÐÉÓÅÍ. äÌÑ ÏÔÐÉÓËÉ ÎÁÄÏ ÂÕÄÅÔ ÐÏÓÌÁÔØ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ
+'<#l#>-unsubscribe-user=host.dom@<#H#>'. ðÒÏÓÔÏ ÓÆÏÒÍÉÒÕÊÔÅ ÔÁËÏÊ
+ÁÄÒÅÓ, ÚÁÍÅÎÉ× user=host.dom ÎÁ ×ÁÛÉ ÒÅÁÌØÎÙÅ ÄÁÎÎÙÅ (ÐÏÄÓÔÁ×É× ÓÉÍ×ÏÌ
+= ×ÍÅÓÔÏ @) É ÏÔ×ÅÔØÔÅ ÎÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ.
+
+</text/unsub-ok/>
+ðÏÄÔ×ÅÒÖÄÅÎÉÅ: ÁÄÒÅÓ
+
+!A
+
+ÕÄÁÌÅΠÉÚ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ <#l#>@<#H#>.
+
+</text/edit-do#n/>
+ðÏÖÁÌÕÊÓÔÁ, ÏÔÒÅÄÁËÔÉÒÕÊÔÅ ÎÉÖÅÐÒÉ×ÅÄÅÎÎÙÊ ÆÁÊÌ É ÏÔÐÒÁ×ØÔÅ ÅÇÏ
+ÐÏ ÁÄÒÅÓÕ:
+
+!R
+
+äÌÑ ÜÔÏÇÏ ÄÏÓÔÁÔÏÞÎÏ ÎÁÖÁÔØ ËÎÏÐËÕ "reply" ÉÌÉ "ÏÔ×ÅÔ".
+
+óÉÍ×ÏÌÙ Ë×ÏÔÉÎÇÁ ÂÕÄÕÔ ÕÄÁÌÅÎÙ ÉÚ ÐÉÓØÍÁ Á×ÔÏÍÁÔÉÞÅÓËÉ, ÅÓÌÉ
+×Ù ÎÅ ÂÕÄÅÔÅ ÔÒÏÇÁÔØ ÓÔÒÏËÉ Ó ÍÁÒËÅÒÁÍÉ. íÁÒËÅÒÙ -- ÓÔÒÏËÉ,
+ÎÁÞÉÎÁÀÝÉÅÓÑ Ó %%%. 
+
+</text/edit-list#n/>
+áÄÒÅÓ <#L#>-edit.file ÐÒÅÄÎÁÚÎÁÞÅΠÄÌÑ ÕÄÁÌÅÎÎÏÇÏ ÒÅÄÁËÔÉÒÏ×ÁÎÉÑ
+ÔÅËÓÔÏ×ÙÈ ÆÁÊÌÏ×, ËÏÔÏÒÙÅ Ñ×ÌÑÀÔÓÑ ÛÁÂÌÏÎÁÍÉ ÏÔ×ÅÔÏ× ÓÐÉÓËÁ
+ÒÁÓÓÙÌËÉ <#L#>@<#H#>.
+
+îÉÖÅ ÐÒÉ×ÅÄÅΠÓÐÉÓÏË ÉÍÅΠÆÁÊÌÏ× É ËÏÒÏÔËÏÅ ÏÐÉÓÁÎÉÅ ÆÕÎËÃÉÊ
+ËÁÖÄÏÇÏ ÉÚ ÎÉÈ. äÌÑ ÒÅÄÁËÔÉÒÏ×ÁÎÉÑ ËÁËÏÇÏ-ÌÉÂÏ ÉÚ ÜÔÉÈ ÆÁÊÌÏ×
+ÐÒÏÓÔÏ ÐÏÛÌÉÔÅ ÐÉÓØÍÏ ÐÏ ÁÄÒÅÓÕ #L#>-edit.ÉÍÑÆÁÊÌÁ. ÷ÁÍ ÂÕÄÅÔ
+×ÙÓÌÁÎÏ ÔÅËÕÝÅÅ ÓÏÄÅÒÖÉÍÏÅ ÆÁÊÌÁ É ÄÁÌØÎÅÊÛÉÅ ÉÎÓÔÒÕËÃÉÉ ÐÏ 
+ÒÅÄÁËÔÉÒÏ×ÁÎÉÀ.
+
+æÁÊÌ                îÁÚÎÁÞÅÎÉÅ
+
+bottom              ÎÉÖÎÑÑ ÞÁÓÔØ ÏÔ×ÅÔÁ, ÏÂÝÁÑ ÉÎÆÏÒÍÁÃÉÑ.
+digest              'ÁÄÍÉÎÉÓÔÒÁÔÉ×ÎÁÑ' ÞÁÓÔØ ÄÌÑ ÄÁÊÄÖÅÓÔÏ×.
+faq                 ÞÁÓÔÏ ÚÁÄÁ×ÁÅÍÙÅ ×ÏÐÒÏÓÙ É ÏÔ×ÅÔÙ ÄÌÑ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ.
+get_bad             ÓÏÏÂÝÅÎÉÅ Ï ÏÔÓÕÔÓÔ×ÉÉ ÐÉÓØÍÁ × ÁÒÈÉ×Å.
+help                ÏÂÝÉÊ ÔÅËÓÔ (ÍÅÖÄÕ 'top' É 'bottom').
+info                ÉÎÆÏÒÍÁÃÉÑ Ï ÓÐÉÓËÅ. ðÅÒ×ÁÑ ÓÔÒÏËÁ Ñ×ÌÑÅÔÓÑ ÚÁÇÏÌÏ×ËÏÍ.
+mod_help            ×ÓÅ ÐÒÏ ÍÏÄÅÒÉÒÏ×ÁÎÉÅ ÄÁÎÎÏÇÏ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ.
+mod_reject          ÔÅËÓÔ ÏÔËÁÚÁ × ÐÏÓÔÉÎÇÅ ÓÏÏÂÝÅÎÉÑ.
+mod_request         ÔÅËÓÔ ÚÁÐÒÏÓÁ ÎÁ ÍÏÄÅÒÁÃÉÀ ÐÉÓØÍÁ.
+mod_sub             ÐÏÄÔ×ÅÒÖÄÅÎÉÅ ÍÏÄÅÒÉÒÕÅÍÏÊ ÐÏÄÐÉÓËÉ.
+mod_sub_confirm     ÚÁÐÒÏÓ ÍÏÄÅÒÁÔÏÒÕ Ï ÐÏÄÐÉÓËÅ ÐÏÌØÚÏ×ÁÔÅÌÑ.
+mod_timeout         ÓÏÏÂÝÅÎÉÅ Ï "ÐÒÏÔÕÈÛÅÍ" ÐÉÓØÍÅ × ÏÞÅÒÅÄÉ ÎÁ ÏÄÏÂÒÅÎÉÅ.
+mod_unsub_confirm   ÚÁÐÒÏÓ ÎÁ ÏÄÏÂÒÅÎÉÅ ÏÔÐÉÓËÉ ÐÏÌØÚÏ×ÁÔÅÌÑ ÍÏÄÅÒÁÔÏÒÕ.
+sub_bad             ÐÏÄÐÉÓÞÉËÕ, ÐÒÉ ÎÅËÏÒÒÅËÔÎÏÍ ÐÉÓØÍÅ-ÐÏÄÔ×ÅÒÖÄÅÎÉÉ.
+sub_confirm         ÚÁÐÒÏÓ ÎÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ ÐÏÄÐÉÓËÉ ÐÏÌØÚÏ×ÁÔÅÌÀ.
+sub_nop             ÅÓÌÉ ÐÏÌØÚÏ×ÁÔÅÌØ ÕÖÅ ÐÏÄÐÉÓÁÎ.
+sub_ok              ÐÒÉ ÕÓÐÅÛÎÏÊ ÐÏÄÐÉÓËÅ.
+top                 ×ÅÒÈÎÑÑ ÞÁÓÔØ ×ÓÅÈ ÏÔ×ÅÔÏ×.
+</#tn/>
+trailer             ÔÅËÓÔ, ÄÏÂÁ×ÌÑÅÍÙÊ × ËÏÎÅàËÁÖÄÏÇÏ ÐÉÓØÍÁ × ÓÐÉÓÏË.
+</#n/>
+unsub_bad           ÐÏÄÐÉÓÞÉËÕ ÐÒÉ ÎÅÕÄÁÞÎÏÊ ÏÔÐÉÓËÅ.
+unsub_confirm       ÚÁÐÒÏÓ ÎÁ ÐÏÄÔ×ÅÒÖÄÅÎÉÅ ÏÔÐÉÓËÉ ÐÏÌØÚÏ×ÁÔÅÌÀ.
+unsub_nop           ÎÅ-ÐÏÄÐÉÓÞÉËÕ Ï ÔÏÍ, ÞÔÏ ÅÇÏ ÁÄÒÅÓÁ × ÓÐÉÓËÅ ÎÅÔ.
+unsub_ok            ÐÒÉ ÕÓÐÅÛÎÏÊ ÏÔÐÉÓËÅ.
+
+</text/edit-done#n/>
+ôÅËÓÔÏ×ÙÊ ÆÁÊÌ ÂÙÌ ÕÓÐÅÛÎÏ ÏÂÎÏ×ÌÅΠÎÁ ÓÅÒ×ÅÒÅ.
+</text/info#E/>
+ðÎÉÔÅ ÁÄÍÉÎÉÓÔÒÁÔÏÒÁ ÓÐÉÓËÁ ÒÁÓÓÙÌËÉ, ÐÕÓÔÏ ÔÕÔ.
+</text/faq#E/>
+FAQ, ÉÌÉ þÁ÷ï -- þÁÓÔÏ ÚÁÄÁ×ÁÅÍÙÅ ÷ÏÐÒÏÓÙ É ïÔ×ÅÔÙ.
+
+úÄÅÓØ ÐÏËÁ ÐÕÓÔÏ. íÏÖÅÔ ËÔÏ-ÎÉÂÕÄØ ÎÁÐÉÛÅÔ?
+
diff --git a/ezmlmrc.sv b/ezmlmrc.sv
new file mode 100644 (file)
index 0000000..4f6c6b5
--- /dev/null
@@ -0,0 +1,1174 @@
+0.40 - This version identifier must be on line 1 and start in pos 1.
+#
+#$Id: ezmlmrc.sv,v 1.23 1999/12/23 23:08:19 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+#
+# ezmlmrc 
+# #######
+# Controls the actions of ezmlm-make as patched with ezmlm-idx-0.31 or later.
+#
+# The base directory 'DIR' is always created by ezmlm-make, as is DIR/key.
+# Everything else is done from here.
+#
+# ezmlm-make looks for this file, first as .ezmlmrc in the directory that the
+# lists .qmail files will be placed in (if you've used the -c command line
+# switch), then /etc/ezmlmrc, then ezmlmrc in the ezmlm-make binary directory.
+# Thus, you can customize ezmlm-make on a global level by placing a customized
+# copy of ezmlmrc in /etc and on a user level by copying it to .ezmlmrc in
+# the user's home directory AND use the ezmlm-make -c switch.
+#
+# Tags are:
+#      </filename/>       : put succeeding text lines in DIR/filename
+#      </-filename/>      : erase DIR/filename.
+#      </+dirname/>       : create directory DIR/dirname
+#      </:lname/dirname>  : symlink DIR/.qmail-list-lname -> DIR/dirname
+#
+# The name in the tag can be suffixed with '#' and any number of flags,
+# corresponding to command line switches. The item will be created/extended
+# only if all the flags listed are set. Files can be extended as long as they
+# were the last one created, but not if another file has been started since
+# then. Flags that are not recognized are silently ignored.
+# 
+# Thus, </filename#aP/> creates the file if and only if the list is archived
+# (-a) and not public (-P). If the next tag is </filename#m/>, the file is
+# extended with the lines up to the next tag if the list is message moderated
+# (-m). If the next tag is </another/>, 'filename' is closed. Any further
+# tags leading to the reopenining of 'filename' will overwrite the file, not
+# extend it.
+#
+# A set of user-defined command line switches (xX, yY, zZ) are available for
+# customization.
+#
+# Within the text, certain tags are substituted. Other tags are copied as
+# is. <#A#> and <#R#> are substituted by ezmlm-manage and -store (see man pages)
+# and <#l#> (lower case L) is replaced dynamically by the list name for
+# programs handling both 'list' and 'list-digest'.
+#
+# Substitutions are:
+# <#B#> ezmlm binaries path   <#C#> digest code         <#D#> dir
+# <#H#> host                  <#L#> local               <#F#> flags
+# <#T#> dot                   <#0#> arg for -0. <#3#>...<#9#> arg for -3..9
+# <#1#> ext1                  <#2#> ext2 [if dot is /path/.qmail-ext1-ext2-name]
+# The latter useful when a single user is controlling several virtual domains.
+#
+# -0 is used for the main list address when setting up sublists
+# -3 is for the new from header if we want that header replaced
+# -4 for specifying the ezmlm-tstdig switches used in dir/editor. Default
+#    -k64 -m30 -t24. Only used if -g is used.
+# -5 for list-owner address. Mail to list-owner will be forwarded to this addr.
+# -6 for sql connection info
+# -7 for contents of DIR/modpost
+# -8 for contents of DIR/modsub
+# -9 for contents of DIR/remote
+#
+# For demonstration purposes, the '-x' switch results in the following
+# non-standard actions:
+# - Removal of many non-text MIME parts from messages.
+# - Limit posts to 2 bytes <= msg body size <= 40000
+#
+# Attempts to create links or directories that already exist, will result
+# in a FATAL error. Attempts to open files that have already been closed
+# or already exits, will cause the old file to be overwritten.
+#
+# One of the major problems with ezmlm-lists is DIR/inlocal. For normal
+# users, it is set up to the list name (user-list or so), which is correct.
+# However, for user 'ezmlm' in control of virtual domain 'host.dom.com'
+# the list name is 'list@host.dom.com', but inlocal should be 'ezmlm-list',
+# not 'list'. Similarly, if ezmlm-domain1 is in control of 'host.dom.com,
+# list@host.dom.com, should yield an inlocal of 'ezmlm-domain1-list'. To
+# always get the lists correct, place this file as '.ezmlmrc' in the 
+# users home directory (~ezmlm/.ezmlmrc) and change the inlocal text below
+# to 'ezmlm-<#L#>' or 'ezmlm-<#1#>-<#L#>, respectively.
+# config to support future editing without giving ezmlm-make command line
+# arguments other than dir. Useful for GUI/WWW editing tools
+</config/>
+F:<#F#>
+X:<#X#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+</inlocal/>
+<#L#>
+</sublist#0/>
+<#0#>
+</+archive/>
+</+subscribers/>
+</+bounce/>
+</+text/>
+# dirs for digests
+</+digest#d/>
+</+digest/subscribers#d/>
+</+digest/bounce#d/>
+# for extra address db
+</+allow/>
+</+allow/subscribers/>
+# for blacklist
+</+deny#k/>
+</+deny/subscribers#k/>
+# moderator db & mod queue dirs. Needed for -m, -r -s, so we just
+# make them by default.
+</+mod/>
+</+mod/subscribers/>
+</+mod/pending/>
+</+mod/accepted/>
+</+mod/rejected/>
+# links: dot -> dir/editor
+</:/editor/>
+</:-owner/owner/>
+</:-digest-owner/owner#d/>
+</:-return-default/bouncer/>
+</:-digest-return-default/digest/bouncer#d/>
+</:-default/manager/>
+# for message moderation only
+</:-accept-default/moderator#m/>
+</:-reject-default/moderator#m/>
+# Get rid of configuration flags for editing mode so we can start with a
+# clean slate.
+</-modpost#eM/>
+</-modsub#eS/>
+</-remote#eR/>
+</-public#eP/>
+</-indexed#eA/>
+</-archived#eA/>
+</-prefix#eF/>
+</-text/trailer#eT/>
+</-sublist#e^0/>
+</-mimeremove#eX/>
+# Not needed, except for message moderation.
+</-moderator#eM/>
+# We don't clean out text files to make it easier for users
+# doing manual config by e.g. touching dir/remote.
+# subscription moderation
+</modsub#s/>
+<#8#>
+# remote admin
+</remote#r/>
+<#9#>
+# message moderation
+</modpost#m/>
+<#7#>
+# List owner mail
+</owner#5/>
+<#5#>
+</owner#^5/>
+<#D#>/Mailbox
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# Handles subscription. Add flags if you want a non-default digest format.
+# Service subject commands to the # request address if the -q switch is given.
+# Also -l and -d enable subscriber listing/text file editing, for remote adms.
+# -u gives subscriber only archive access
+</manager#ab/>
+|<#B#>/ezmlm-get -P '<#D#>' <#C#>
+</manager#aGB/>
+|<#B#>/ezmlm-get '<#D#>' <#C#>
+</manager#agB/>
+|<#B#>/ezmlm-get -s '<#D#>' <#C#>
+</manager#q/>
+|<#B#>/ezmlm-request '<#D#>'
+# Ok to add -l/-d even for non-mod lists, since ezmlm-manage
+# won't allow it unless there are remote admins. The lack of logic other than
+# AND makes this very tedious ...
+# first lists with normal confirmation:
+</manager#LNHJ/>
+|<#B#>/ezmlm-manage '<#D#>'
+</manager#lNHJ/>
+|<#B#>/ezmlm-manage -l '<#D#>'
+</manager#LnHJ/>
+|<#B#>/ezmlm-manage -e '<#D#>'
+</manager#lnHJ/>
+|<#B#>/ezmlm-manage -le '<#D#>'
+# ... now no confirmation for subscribe ...
+</manager#LNhJ/>
+|<#B#>/ezmlm-manage -S '<#D#>'
+</manager#lNhJ/>
+|<#B#>/ezmlm-manage -lS '<#D#>'
+</manager#LnhJ/>
+|<#B#>/ezmlm-manage -eS '<#D#>'
+</manager#lnhJ/>
+|<#B#>/ezmlm-manage -leS '<#D#>'
+# ... now no confirmation for unsubscribe ...
+</manager#LNHj/>
+|<#B#>/ezmlm-manage -U '<#D#>'
+</manager#lNHj/>
+|<#B#>/ezmlm-manage -lU '<#D#>'
+</manager#LnHj/>
+|<#B#>/ezmlm-manage -eU '<#D#>'
+</manager#lnHj/>
+|<#B#>/ezmlm-manage -leU '<#D#>'
+# ... and finally no confirmation at all ...
+</manager#LNhj/>
+|<#B#>/ezmlm-manage -US '<#D#>'
+</manager#lNhj/>
+|<#B#>/ezmlm-manage -lUS '<#D#>'
+</manager#Lnhj/>
+|<#B#>/ezmlm-manage -eUS '<#D#>'
+</manager#lnhj/>
+|<#B#>/ezmlm-manage -leUS '<#D#>'
+</manager#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</editor/>
+# reject shouldn't be configured for sublist.
+</#^0/>
+# full reject is now default, to get To/Cc: listaddress requirement
+|<#B#>/ezmlm-reject '<#D#>'
+# -k => reject posts from blacklisted addresses. Done for moderated
+# lists as well - allows removal of unwanted noise.
+</#k^0/>
+|<#B#>/ezmlm-issubn -n '<#D#>/deny' || { echo "Jag tillåter inte dina meddelanden. Kontakta <#L#>-owner@<#H#> ifall du har några frågor angående det (#5.7.2)"; exit 100 ; }
+# switch -u=> restrict to subs of list & digest. If not m
+# do it with ezmlm-issubn, if 'm' do it with ezmlm-gate
+</#uM/>
+|<#B#>/ezmlm-issubn '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod' || { echo "Tyvärr, endast prenumeranter får posta. Ifall du är en prenumerant, orward this message to <#L#>-owner@<#H#> to get your new address included (#5.7.2)"; exit 100 ; }
+</#um/>
+|<#B#>/ezmlm-gate '<#D#>' '<#D#>' '<#D#>/digest' '<#D#>/allow' '<#D#>/mod'
+# For message moderation, editor has store/clean
+</#mUO/>
+|<#B#>/ezmlm-store '<#D#>'
+</#mUo/>
+|<#B#>/ezmlm-store -P '<#D#>'
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+# for non-message moderated lists, it has send
+</#M/>
+|<#B#>/ezmlm-send '<#D#>'
+# ezmlm-archive here for normal lists. Put into moderator for mess-mod lists
+</#Mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+# all lists have warn unless -w.
+</#W/>
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+# for digest bounces
+</#dW/>
+|<#B#>/ezmlm-warn -d '<#D#>' || exit 0
+</#d^4/>
+|<#B#>/ezmlm-tstdig -m30 -k64 -t48 '<#D#>' || exit 99
+</#d4/>
+|<#B#>/ezmlm-tstdig <#4#> '<#D#>' || exit 99
+</#d/>
+|<#B#>/ezmlm-get '<#D#>' || exit 0
+# bouncer for list and digest
+</bouncer/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -D '<#D#>'
+</digest/bouncer#d/>
+|<#B#>/ezmlm-weed
+|<#B#>/ezmlm-return -d '<#D#>'
+# moderator is set up only for message moderated lists. However, '-e' does
+# not remove it since we can't remove the symlinks to it (they're outside
+# of the list dir.
+</moderator#m/>
+|<#B#>/ezmlm-moderate '<#D#>'
+</#mi/>
+|<#B#>/ezmlm-archive '<#D#>' || exit 0
+</#mU/>
+|<#B#>/ezmlm-clean '<#D#>' || exit 0
+</#mu/>
+|<#B#>/ezmlm-clean -R '<#D#>' || exit 0
+</headerremove#E/>
+return-path
+return-receipt-to
+content-length
+precedence
+x-confirm-reading-to
+x-pmrqc
+list-subscribe
+list-unsubscribe
+list-help
+</headerremove#E^0/>
+# For sublists, these should be left in
+list-post
+# remove from header if -3 'new_from_line'
+</#3E/>
+from
+</lock/>
+</lockbounce/>
+</digest/lockbounce#d/>
+</digest/lock#d/>
+</public#p/>
+</archived#a/>
+</indexed#a/>
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+kontakta <#L#>-help@<#H#>; körs med ezmlm
+# Headeradd needs to always exist but leave out stuff for sublists
+</headeradd#E^0/>
+# Good for mailing list stuff (and vacation program)
+Precedence: bulk
+# To prevent indexing by findmail.com
+X-No-Archive: yes
+# rfc2369, first from main list only, others from sublist only
+List-Post: <mailto:<#L#>@<#H#>>
+</headeradd#E/>
+List-Help: <mailto:<#l#>-help@<#h#>>
+List-Unsubscribe: <mailto:<#l#>-unsubscribe@<#h#>>
+List-Subscribe: <mailto:<#l#>-subscribe@<#h#>>
+# add new from line "From: arg" if -3 'arg'
+</#3E/>
+From: <#3#>
+# max & min message size
+</msgsize#x/>
+30000:2
+# remove mime parts if -x
+</mimeremove#xE/>
+application/excel
+application/rtf
+application/msword
+application/ms-tnef
+text/html
+text/rtf
+text/enriched
+text/x-vcard
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40
+application/mac-compactpro
+application/macwriteii
+application/news-message-id
+application/news-transmission
+application/octet-stream
+application/oda
+application/pdf
+application/postscript
+application/powerpoint
+application/remote-printing
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio
+application/x-cdlink
+application/x-compress
+application/x-cpio
+application/x-csh
+application/x-director
+application/x-dvi
+application/x-hdf
+application/x-httpd-cgi
+application/x-koan
+application/x-latex
+application/x-mif
+application/x-netcdf
+application/x-stuffit
+application/x-sv4cpio
+application/x-sv4crc
+application/x-tar
+application/x-tcl
+application/x-tex
+application/x-texinfo
+application/x-troff
+application/x-troff-man
+application/x-troff-me
+application/x-troff-ms
+application/x-ustar
+application/x-wais-source
+audio/basic
+audio/mpeg
+audio/x-aiff
+audio/x-pn-realaudio
+audio/x-pn-realaudio
+audio/x-pn-realaudio-plugin
+audio/x-realaudio
+audio/x-wav
+image/gif
+image/ief
+image/jpeg
+image/png
+image/tiff
+image/x-cmu-raster
+image/x-portable-anymap
+image/x-portable-bitmap
+image/x-portable-graymap
+image/x-portable-pixmap
+image/x-rgb
+image/x-xbitmap
+image/x-xpixmap
+image/x-xwindowdump
+text/x-sgml
+video/mpeg
+video/quicktime
+video/x-msvideo
+video/x-sgi-movie
+x-conference/x-cooltalk
+x-world/x-vrml
+# These can also be excluded, but for many lists it is desirable
+# to allow them. Uncomment to add to mimeremove.
+# application/zip
+# application/x-gtar
+# application/x-gzip
+# application/x-sh
+# application/x-shar
+# chemical/x-pdb
+# --------------------- Handle SQL connect info
+</-sql#^6e/>
+</-digest/sql#^6e/>
+</-allow/sql#^6e/>
+</sql#6W/>
+<#6#>
+</sql#6w/>
+<#6#>:<#L#>@<#H#>
+</digest/sql#6dW/>
+<#6#>_digest
+</digest/sql#6dw/>
+<#6#>_digest:<#L#>_digest@<#H#>
+</allow/sql#6/>
+<#6#>_allow
+# -------------------- End sql stuff
+</prefix#fE/>
+[<#L#>]
+</text/trailer#tE/>
+---------------------------------------------------------------------
+För att avsluta prenumerationen skicka e-mail till:
+<#L#>-unsubscribe@<#H#>
+För ytterligare kommandon, skicka e-mail till:
+<#L#>-help@<#H#>
+</text/bottom#E/>
+
+--- Administrativa kommandon för <#l#> listan ---
+
+Administrativa förfrågningar kan hanteras automatiskt. Skicka
+dem inte till listans adress! Skicka istället ditt meddelande
+till rätt "kommando adress":
+
+För hjälp och en beskrivning över tillgängliga kommandon,
+skicka ett brev till:
+   <<#L#>-help@<#H#>>
+
+För att prenumerera på listan, skicka ett brev till:
+   <<#L#>-subscribe@<#H#>>
+
+För att avsluta din prenumeration, skicka ett meddelande till
+adressen som står i "List-Unsubscribe" raden i brevhuvudet
+från ett brev som kom från listan. Ifall du inte bytt adress
+sen du påbörjade din prenumeration, skicka ett brev till:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#dE/>
+eller för "digest" versionen:
+   <<#L#>-unsubscribe@<#H#>>
+
+</#E/>
+För nya/avslutade prenumerationer, skickar jag ett bekräftelse
+brev till adressen. När du får brevet, svara bara på det för
+att genomföra prenumerationsförändringen.
+
+Ifall du behöver komma i kontakt med en människa angående
+listan, skicka ett brev till:
+
+    <<#L#>-owner@<#H#>>
+
+Var vänlig och VIDARESKICKA (forward) ett meddelande från listan
+inklusive HELA brevhuvudet så vi lättare kan hjälpa dig.
+
+--- Nedan finner du en kopia på förfrågan jag fick.
+
+</text/bounce-bottom#E/>
+
+--- Nedan finner du en kopia på det "studsade" meddelandet jag fick.
+
+</text/bounce-num#E/>
+
+Jag har skapat en lista på de meddelanden från <#L#> listan som
+har "studsat" på väg till dig.
+
+</#aE/>
+Kopior av dessa meddelanden kan du finna i arkivet.
+
+</#aE/>
+För att hämta meddelande 123-145 (max 100 per förfrågan), skicka
+ett brev till:
+   <<#L#>-get.123_145@<#H#>>
+
+För att få en lista på titlar och författare för de senaste 100
+meddelandena, skicka ett brev till:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Detta är meddelande nummren:
+
+</text/dig-bounce-num#E/>
+
+Jag har skapat en lista på "digest" meddelanden från <#L#>-digest
+listan, som har "studsat" till din adress. För varje "digest" brev
+som du missat, har jag skrivit upp första meddelandenummret i det
+brevet.
+
+</#aE/>
+"Digest" meddelanden arkiveras inte, men du kanske kan finna dem
+i akrivet för huvudlistan.
+
+För att ta emot brev 123-145 (max 100 per förfrågan),
+skicka ett brev till:
+   <<#L#>-get.123_145@<#H#>>
+
+För en lista över författare och titlar på de senaste 100
+meddelandena, skicka ett brev till:
+   <<#L#>-index@<#H#>>
+
+</#E/>
+Här är "digest" meddelande nummren:
+
+</text/bounce-probe#E/>
+
+Meddelanden till dig från <#l#> listan, verkar ha "studsat".
+Jag skickade ett varningsbrev till dig om det, men det "studsade".
+Nedan följer en kopia på det meddelandet.
+
+Detta testbrev kontrollerar om din adress är nåbar. Ifall detta
+brev också studsar, plockas din adress bort från
+<#l#>@<#H#> listan, utan ytterligare varningar.
+
+Du kan prenumerera på nytt genom att skicka ett brev
+till denna adressen:
+   <<#l#>-subscribe@<#H#>>
+
+</text/bounce-warn#E/>
+
+Meddelanden till dig från <#l#> listan har "studsat".
+Jag bifogar en kopia på det första brevet till dig där
+det inträffade.
+
+Ifall detta meddelande också "studsar", kommer ett testbrev skickas
+till dig. Ifall det brevet också studsar, plockas din adress bort
+från <#l#> listan utan ytterligare varning.
+
+</text/digest#dE/>
+För prenumeration på "digest" versionen, skicka ett brev till:
+       <#L#>-digest-subscribe@<#H#>
+
+För att avsluta prenumerationen på "digest" versionen,
+skicka ett brev till:
+       <#L#>-digest-unsubscribe@<#H#>
+
+För att skicka ett brev till listan, skicka brevet till:
+       <#L#>@<#H#>
+
+</text/get-bad#E/>
+Tyvärr, det meddelandet finns inte i arkivet.
+
+</text/help#E/>
+Detta är ett almänt hjälp meddelande. Brevet som kom var inte
+skickat till någon av kommando adresserna.
+
+Detta är en lista på de kommando adresser som stöds:
+
+Skicka brev till något av följande adresser för information
+och "FAQn" för listan:
+   <<#L#>-info@<#H#>>
+   <<#L#>-faq@<#H#>>
+
+</#dE/>
+Liknande adresser finns för "digest" versionen av listan:
+   <<#L#>-digest-subscribe@<#H#>>
+   <<#L#>-digest-unsubscribe@<#H#>>
+
+# ezmlm-make -i needed to add ezmlm-get line. If not, we can't do
+# multi-get!
+</#aE/>
+För att få meddelande 123 till 145 (max 100 per förfrågan),
+skicka ett brev till:
+   <<#L#>-get.123_145@<#H#>>
+
+För att få ett index med författare och titel för meddelande
+123-456, skicka ett brev till:
+   <<#L#>-index.123_456@<#H#>>
+
+För att få alla meddelanden med samma titel som meddelande 12345,
+skicka ett brev till:
+   <<#L#>-thread.12345@<#H#>>
+
+</#E/>
+Meddelandena behöver inte innehålla något särskilt, det är
+bara adressen som är viktig.
+
+Du kan starta en prenumeration till en alternativ adress,
+t ex "john@host.domain", addera bara ett bindesträck
+och din adress (med '=', istället för '@') efter kommando
+ordet. Dvs:
+<<#L#>-subscribe-john=host.domain@<#H#>>
+
+För att avsluta prenumerationen till denna adressen,
+skicka ett brev till:
+<<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</text/mod-help#E/>
+Tack för att du vill moderera <#L#>@<#H#> listan.
+
+Kommandona är lite anorlunda mot andra listor,
+men de är lätta att lära och använda.
+
+Här är lite instruktioner angående de saker du kan behöva
+göra som listägare/moderator.
+
+Allmäna kommandon följer efter detta meddelande.
+
+Fjärr prenumeration.
+--------------------
+Som moderator kan du prenumerera och avprenumerera vilken adress
+som helst på listan. För att prenumerera "john@host.domain",
+skriv bara ett bindesträck efter "kommando ordet", därefter
+adressen med ett '=' tecken istället för '@'. I detta fallet skulle
+du skickat ett brev till:
+   <<#L#>-subscribe-john=host.domain@<#H#>>
+
+Du kan på samma sätt ta bort en adress med ett meddelande till:
+   <<#L#>-unsubscribe-john=host.domain@<#H#>>
+
+</#dE/>
+För "digest" versionen av listan:
+   <<#L#>-digest-subscribe-john=host.domain@<#H#>>
+   <<#L#>-digest-unsubscribe-john=host.domain@<#H#>>
+
+</#E/>
+Det är allt. Titel och innehåll spelar ingen roll!
+
+</#rE/>
+Ett bekräftelse brev kommer skickas för att vara säker
+på att det verkligen var du som skickade brevet.
+Svara bara på det brevet och det hela är klart.
+</#RE/>
+Jag kommer skicka ett bekräftelsebrev till prenumerantens adress,
+i detta fallet <john@host.domain>. Allt prenumeranten behöver
+göra är att svara på brevet.
+</#E/>
+
+Bekräftelserna är nödvändiga för att göra det svårt för
+en tredje part till att lägga till/ta bort adresser till
+listan.
+
+Jag kommer underrätta prenumeranten när dennes status
+har ändrats.
+
+Prenumeration
+--------------
+
+Alla kan prenumerera/sluta prenumerera på listan genom
+att skicka ett brev till:
+
+<#L#>-subscribe@<#H#>
+<#L#>-unsubscribe@<#H#>
+
+</#dE/>
+För "digest" versionen av listan:
+
+<#L#>-digest-subscribe@<#H#>
+<#L#>-digest-unsubscribe@<#H#>
+
+</#E/>
+Prenumeranten kommer få ett bekräftelse brev för
+att vara säker på att personen har den adressen.
+När det är klart blir personen borttagen ur listan.
+
+</#sE/>
+Eftersom denna listan är sluten, kommer jag skicka en andra
+förfrågan till moderatorerna. Eftersom prenumeranten redan har
+bekräftat att den vill vara med på listan, kan du som
+moderator vara säker på att det är rätt adress. Ifall du vill
+ha med personen på listan, svara på bekräftelse (CONFIRM)
+meddelandet. Ifall du inte vill ha med personen, radera bara
+meddelandet istället (eller kontakta personen för ytterligare
+information).
+</#SE/>
+Prenumeration fungerar på samma sätt.
+</#E/>
+
+Användaren kan också:
+
+   <<#L#>-subscribe-mary=host.domain@<#H#>>
+   <<#L#>-unsubscribe-mary=host.domain@<#H#>>
+
+för att få brev skickad till "mary@host.domain". Bara om hon kan
+ta emot brev på den adressen, får hon bekräftelse meddelandet
+och kan svara på det.
+
+Din adress och identitet kommer att vara hemlig för prenumeranten
+om du inte skickar brev direkt till denne.
+
+</#rlE/>
+För att få en lista på prenumeranter på <#L#>@<#H#>,
+skicka ett brev till:
+   <<#L#>-list@<#H#>>
+
+För att få en "transaktionslog" för <#L#>@<#H#>,
+skicka ett brev till:
+   <<#L#>-log@<#H#>>
+
+</#rldE/>
+För "digest" prenumeranter:
+   <<#L#>-digest-list@<#H#>>
+och:
+   <<#L#>-digest-log@<#H#>>
+
+</#rnE/>
+Du kan ändra textfilerna, som listan använder, på distans. För att
+få en lista på filerna och instruktioner om hur du ändrar dem,
+skicka ett e-mail till:
+   <<#L#>-edit@<#H#>>
+
+</#mE/>
+Modererade utskick
+------------------
+När utskick är modererade, kommer ett brev att skickas till dig
+med en kopia på utskick och instruktioner som berättar hur
+utskicket skall godkännas för att komma med på listan. Det
+brevet kommer att ha "MODERATE for ..." som titel.
+
+För att acceptera ett utskick, skicka bara ett svar till 'Reply-To:'
+adressen (sker vanligtvis med "svara" knappen). Du behöver inte
+skicka med brevet du fick skickat till dig, det är bara adressen
+som är viktig.
+
+Ifall du vill avvisa utskicket, skicka ett brev till avsändar-
+adressen ("From:" fältet), där rätt avvisningsadress är inskrivning.
+"Svara alla" brukar använda den adressen. Om du vill skriva ett
+meddelande till författaren, skriv den mellan två rader som börjar
+med tre '%' tecken. Detta kommer att ske anonymt och bara skickas
+till författaren.
+
+Utskicket kommer att behandlas beroende på vilket svar som kommer
+in först. Om en moderator redan har avvisat ett brev som du godkänner
+så kommer brevet ändå att vara avvisat och vice versa.
+
+Ifall ingen moderator svarar inom en viss tid (vanligtvis 5 dagar),
+kommer brevet att returneras till författaren med en förklaring
+om vad som hände.
+</#E/>
+
+Semestrar
+---------
+Ifall du temporärt har en annan adress, vidareskicka alla brev som
+har korrekt "Mailing-List:" fält i brevhuvudet (eller alla brev som
+har titeln "MODERATE for <#L#>@<#H#>"
+eller "CONFIRM subscribe to <#L#>@<#H#>")
+till den nya adressen. Du kan därefter moderera listan från den
+adressen. Alternativt kan du vidareskicka brevet till någon annan
+som modererar listan åt dig. Fråga listägaren först om det är OK.
+
+Ifall du vill att allt skall godkännas automatiskt medan du är
+borta, ställ iordning ditt e-mail system så den gör ett autosvar
+på brev med ovan nämnda titlar.
+
+</#rE/>
+Ifall du försöker administrera listan från en adress som inte är din
+egen, prenumeranten, inte du, kommer frågas efter en bekräftelse.
+Därefter kommer en bekräftelseförfrågan skickas till moderatorerna.
+Detta görs eftersom det är omöjligt att veta ifall det var du som
+skickade originalfrågan.
+
+Observera att originalförfrågan, inklusive din adress, skickas till
+prenumeranten i detta fallet.
+</#E/>
+
+Lycka till!
+
+PS. Kontakta listägaren (<#L#>-owner@<#H#>) ifall du
+har några frågor eller stöter på några problem.
+
+</text/mod-reject#E/>
+Tyvärr, meddelandet (bifogat) accepterades inte av moderatorn.
+Ifall moderatorn har bifogat några kommentarer, står de här nedan.
+</text/mod-request#E/>
+Det bifogade meddelandet skickades till <#L#>@<#H#> listan.
+Ifall du vill godkänna den för vidare distribution skicka e-mail till:
+
+!A
+
+Vanligtvis händer detta automatiskt om du trycker på "svara" (reply)
+knappen. Du kan kontrollera adressen att den börjar med:
+"<#L#>-accept". Ifall det inte fungerar, kopiera adressen och
+klistra in den i "Till" ("To:") fältet i ett nytt brev.
+</#xE/>
+
+Alternativt, tryck här:
+       <mailto:<#A#>>
+</#E/>
+
+Föra att skicka tillbaka brevet till avsändaren, skicka ett
+meddelande till:
+
+!R
+
+Vanligtvis är det enklare att trycka på "svara alla" ("reply-to-all")
+knappen och ta bort alla adresser som inte börjar med:
+"<#L#>-reject".
+</#xE/>
+
+Alternativt, tryck här:
+       <mailto:<#R#>>
+</#E/>
+
+Du behöver inte kopiera brevet i ditt svar. Ifall du vill skicka
+med en kommentar till författaren till ett brev du inte accepterat,
+inkludera kommentaren, i svarsbrevet, mellan två rader som börjar
+med tre procenttecken ('%').
+
+%%% Start kommenter
+%%% Slut kommentar.
+
+Tack för din hjälp!
+
+--- Nedan finner du utskicket.
+
+</text/mod-sub#E/>
+--- Du har blivit (av-)prenumererad av en moderator för
+<#l#>@<#H#> listan.
+
+Ifall du inte tycker om det, skicka ett klagomål, eller annan
+kommentar, till listägaren (<#l#>-owner@<#H#>) så snart som
+möjligt.
+
+</text/mod-timeout#E/>
+Tyvärr har <#L#> listans moderatorer inte
+hanterat din postning, därför skickas den nu tillbaka till dig.
+Ifall detta är fel, skicka om ditt meddelande till listan
+eller kontakta listägaren (<#L#>-owner@<#H#>).
+
+--- Bifogat är brevet du skickade.
+
+</text/mod-sub-confirm#E/>
+Vill du lägga till
+
+!A
+
+till <#l#> listan? Antingen kom detta brevet som svar på
+att du vill lägga till prenumeranten till listan eller
+så har prenumeranten redan bekräftat sin prenumeration.
+
+För att bekräfta, skicka ett tomt brev till denna adress:
+
+!R
+
+Vanligtvis görs det genom "svara" ("reply") knappen.
+Ifall det inte fungerar, kopiera adressen och klistra in den i
+"To:" fältet i ett nytt meddelande.
+</#xE/>
+
+eller tryck här:
+       <mailto:<#R#>>
+</#E/>
+
+Ifall du inte godkänner detta, ignorera detta meddelande.
+
+Tack för din hjälp!
+
+</text/mod-unsub-confirm#E/>
+Någon önskar ta bort:
+
+!A
+
+från <#l#> listan. Ifall du håller med, skicka ett brev
+till denna adress:
+
+!R
+
+Enklast gör du det genom att trycka på "svara" ("reply") knappen.
+Ifall det inte fungerar, kopiera adressen och klistra in den i
+"Till" ("To:") fältet i det nya meddelandet.
+</#xE/>
+
+eller tryck här:
+       <mailto:<#R#>>
+</#E/>
+
+Ifall du inte håller med, ignorera detta brev.
+
+Tack för din hjälp!
+
+</text/sub-bad#E/>
+Oops, det bekräftelsenummret verkar vara felaktigt.
+
+Den vanligaste orsaken till felaktiga bekräftelsenummer är
+att de blivit för gamla. De gäller i max 10 dagar. Var också
+säker på att du använde hela bekräftelsenummret i ditt svar,
+vissa program kan i vissa fel ta bort slutet på adresser när
+de är långa.
+
+Ett nytt bekräftelsenummer har skapats, för att bekräfta att
+du vill ha med
+
+!A
+
+på <#l#> listan, skicka ett brev till denna adress:
+
+!R
+</#xE/>
+
+eller tryck här:
+       <mailto:<#R#>>
+</#E/>
+
+Var noga med att svarsadresser är riktig när du bekräftar
+prenumerationen.
+
+Ursäkta detta extra besvär.
+
+       <#L#>-Owner <<#l#>-owner@<#H#>>
+
+</text/sub-confirm#E/>
+För att bekräfta att du vill ha
+
+!A
+
+adderad till <#l#> listan, skicka ett brev till denna adress:
+
+!R
+
+Enklast görs det genom att trycka på "svara" ("reply") knappen.
+Ifall det inte fungerar, kopiera adressen och klistra in den i
+"Till" ("To:") fältet i ett nytt brev.
+</#xE/>
+
+eller tryck här:
+       <mailto:<#R#>>
+</#E/>
+
+Denna bekräftelse tjänar två syften. Dels säkerställer den att det går
+att skicka brev till dig och dels skyddar den dig mot att andra försöker
+prenumerera någon mot dess vilja.
+
+</#qE/>
+Det är fel på vissa e-mail program vilket gör att de inte kan hantera
+långa adresser. Ifall du inte kan svara på denna förfrågan, skicka
+istället ett meddelande till <<#L#>-request@<#H#>>
+och skriva hela ovan nämnda adress i titel ("Subject:") raden.
+
+</#sE/>
+Denna lista är modererad. Så fort du har svarat på denna bekräftelse
+kommer din förfrågan att skickas till moderatorerna för listan.
+Du kommer att underrättas när din prenumeration är aktiverad.
+
+</text/sub-nop#E/>
+Jag kunde inte utföra din förfrågan.
+
+!A
+
+prenumererar redan på <#l#> listan när jag fick din förfrågan.
+Adressen kommer vara kvar på listan.
+
+</text/sub-ok#E/>
+Uppmärksamma: Adressen
+
+!A
+
+har adderats till <#l#> listan.
+
+Välkommen till <#l#>@<#H#>!
+
+Var vänlig och spara detta meddelande så du minns vilken adress
+som prenumererar på listan, ifall du senare vill avsluta din
+prenumeration.
+
+För att avsluta prenumerationen, skicka ett brev till:
+
+    <<#l#>-unsubscribe-<#t#>@<#H#>>
+
+</text/top/>
+Detta är ett meddelande från ezmlm programmet som har hand om
+<#l#>@<#H#> listan.
+
+</#x/>
+Ägaren till listan kan nås på:
+<#l#>-owner@<#H#>.
+
+</text/unsub-bad#E/>
+Oops, det bekräftelsenummret verkar vara felaktigt.
+
+Den vanligaste orsaken till felaktiga bekräftelsenummer är
+att de blivit för gamla. De gäller i max 10 dagar. Var också
+säker på att du använde hela bekräftelsenummret i ditt svar,
+vissa program kan i vissa fel ta bort slutet på adresser när
+de är långa.
+
+Ett nytt bekräftelsenummer har skapats, för att bekräfta att
+du vill ta bort
+
+!A
+
+från <#l#> listan, skicka ett brev till denna adress:
+
+!R
+</#xE/>
+
+eller klicka här:
+       <mailto:<#R#>>
+</#E/>
+
+Var vänligt att kontrollera svarsadressen noggrant så att den är
+riktig innan du svarar på detta brev.
+
+Ursäkta allt besvär.
+
+       <#l#>-Owner <<#l#>-owner@<#H#>>
+
+</text/unsub-confirm#E/>
+För att bekräfta att du vill ta bort
+
+!A
+
+från <#l#> listan, skicka ett brev till denna adress:
+
+!R
+
+Vanligtvis gör man det med "Svara" ("Reply") knappen.
+Ifall det inte fungerar, kopiera adressen nedan och klistra
+in den i "Till" ("To:") fältet i ett nytt brev.
+</#xE/>
+
+eller tryck här:
+       <mailto:<#R#>>
+</#E/>
+
+För att se vilken adress din prenumeration går till, undersök ett
+meddelande från listan. Varje meddelande har din adress dold i
+dess "return path", t ex mary@xdd.ff.com har får meddelanden
+med "return-path" satt till:
+<<#l#>-return-<nummer>-mary=xdd.ff.com@<#H#>>.
+
+</#qE/>
+Vissa email program är felaktiga och kan inte hantera långa adresser.
+Ifall du inte kan svara på detta meddelande, skicka istället ett
+meddelande till <<#L#>-request@<#H#>> och skriv hela ovan nämnda
+adress i titel ("Subject:") raden.
+
+</text/unsub-nop#E/>
+Tyvärr kan inte din förfrågan utföras eftersom adressen:
+
+!A
+
+var inte med på <#l#> listan.
+
+Ifall du har avslutat din prenumeration, men fortfarande får brev,
+är du prenumererad under en annan adress än den du för närvarande
+använder. Titta i brevhuvudet (header) efter:
+
+'Return-Path: <<#l#>-return-1234-user=host.dom@<#H#>>'
+
+Det visar din prenumerationsadress som "user@host.dom".
+För att avsluta din prenumeration med den adressen, skicka
+ett brev till:
+<#l#>-unsubscribe-user=host.dom@<#H#>
+
+Glöm inte att anpassa user=host.dom till din egen adress.
+
+Ifall meddelandet har en "List-Unsubscribe:" fält i brevhuvudet,
+kan du skicka ett meddelande till adressen i det fältet.
+Det är redan anpassat för din adress.
+
+I vissa email program måste du göra vissa inställningar för att
+se "return path" fältet i brevhuvudet:
+
+I Eudora 4.0, klicka på "Blah blah ..." knappen.
+I PMMail, klicka på "Window->Show entire message/header". 
+
+Ifall det inte fungerar kan vi tyvärr inte göra mer.
+Vidaresänd ett brev från listan, tillsammans med ett meddelande
+om vad du vill ha gjort och en lista som du tror att du kan ha
+prenumererat under till listägaren:
+
+    <<#l#>-owner@<#H#>>
+
+som kan ta hand om det. Det kan dock dröja en liten stund innan du får
+ett svar.
+
+</text/unsub-ok#E/>
+Observera: Jag har tagit bort adressen
+
+!A
+
+från <#l#> listan. Den adressen är inte längre en prenumerant.
+
+</text/edit-do#nE/>
+Var vänlig och editera följande textfil och skicka den till
+denna address:
+
+!R
+
+Ditt mailprogram borde ha en svarsfunktiuon som använder
+denna address automatiskt. Ifall det inte fungerar, kan du
+kopiera addressen och klistra in den i "To:"/"Till:" fältet
+på ett nytt medelande.
+</#xE/>
+
+eller klicka här:
+        mailto:<#R#>
+</#E/>
+
+Jag kan ta bort citeringsmarkeringar (t ex "> ") som din
+mailprogramvara lägger till texten så länge som du inte
+ändrar start och slutraderna.
+
+Start och slutraderna är rader som börjar med %%%. De får inte
+ändras. Ifall ditt mailprogram lägger in tecken före dem så skall
+de stå kvar.
+
+
+</text/edit-list#En/>
+<#L#>-edit.fil kommandot kan användas av en fjärradministratör
+för att editera de textfiler som skapar de svaren jag skickar för
+<#L#>@<#H#> listan.
+
+Här följer en lista på de filer som kan ändras samt en
+kort beskrivning om hur dess innehåll används. För att
+ändra en fil, skicka ett brev till <#L#>-edit.filnamn där
+du byter ut filnamn mot filens namn. Editeringsinstruktioner
+skickas till dig ihop med textfilen.
+
+Fil                 Användninsområde.
+
+bottom              slutet på alla svar. Generell kommando information.
+digest              'administrationsbiten' av en 'digest'.
+faq                 Vanligt förekommande frågor på denna lista.
+get_bad             i stället för medelanden som inte hittas i arkivet.
+help                generell hjälp (mellan top och bottom).
+info                list info. Första raden skall kunna visas separat.
+mod_help            specifik hjälp för listmoderatorer.
+mod_reject          sänds till avsändaren av avvisade medelanden.
+mod_request         till medelandemoderatorerna tillsammans med medelandet.
+mod_sub             till prenumeranter efter att en moderator bekräftat prenumerationen.
+mod_sub_confirm     till prenumerationsmoderatorn för att bekräfta prenumerationer.
+mod_timeout         till sändaren av ett medelande som ingen accepterat/avvisat.
+mod_unsub_confirm   till en administratör för att bekräfta avprenumerationer.
+sub_bad             till prenumeranten ifall bekräftelsen var felaktig.
+sub_confirm         till prenumeranten för att bekräfta prenumerationer.
+sub_nop             till prenumeranten efter en dubbel prenumeration.
+sub_ok              till prenumeranten efter en lyckad prenumeration.
+</#tnE/>
+trailer             adderas till alla utskick innan de kommer till listan.
+</#nE/>
+top                 starten på alla svar. Generell kommando information.
+unsub_bad           till prenumeranten ifall avprenumerationen misslyckades.
+unsub_confirm       till prenumeranten för att bekräfta avprenumeration.
+unsub_nop           till icke-prenumerant efter avprenumeration.
+unsub_ok            till tidigare prenumeration efter avslutad prenumeration.
+
+</text/edit-done#nE/>
+Textfilen uppdaterades korrekt.
+</text/info#E/>
+Ingen information har antecknats om listan.
+</text/faq#E/>
+FAQ - vanligt förekommande frågor på <#l#>@<#H#> listan.
+
+Inga har nedtecknats ännu.
+
diff --git a/ezmlmsubrc b/ezmlmsubrc
new file mode 100644 (file)
index 0000000..e8309f7
--- /dev/null
@@ -0,0 +1,162 @@
+#$Id: ezmlmsubrc,v 1.8 1999/05/11 02:32:40 lindberg Exp $
+#$Name: ezmlm-idx-040 $
+# ezmlm-make -C ezmlmsubrc dir dot local host
+# Options:
+#      -3 mainlist local       (required)
+#      -4 mainlist host        (required) 
+#      -6 sql connect info. Format: 'host:port:user:pw:db:table'
+#      -A main list not archived (affects only bounce texts)
+#      -I main list not indexed  (affects only bounce texts)
+#      -d this sublist is for a digest list
+#
+# NOTE: For sublists of a digest list use the list local name for -3, _not_
+# the digest list. Thus, to set up a sublist for list-digest@host, use
+# -d -3 list -4 host. This is needed to make the texts refer correctly.
+# Also, the "table" in the SQL connect info
+# Should be root, where "root" is the table name used for the main list.
+# A digest sublist is just like any other sublist, but the parent is
+# list-digest, rather than list. You need separate sublists for each and they
+# are independent. However, it's convenient to set them up in parallel, and
+# they will not interfere with one another.
+#
+# NOTE: The list address is always added to the SQL connect info to restrict
+# addresses to those serviced by this sublist.
+#
+# For memory
+</config/>
+F:<#F#>
+D:<#D#>
+T:<#T#>
+L:<#L#>
+H:<#H#>
+C:<#C#>
+0:<#0#>
+3:<#3#>
+4:<#4#>
+5:<#5#>
+6:<#6#>
+7:<#7#>
+8:<#8#>
+9:<#9#>
+########## real stuff starts here
+</sublist/>
+<#3#>@<#4#>
+</+subscribers/>
+</+bounce/>
+</+text/>
+</lock/>
+</lockbounce/>
+</-sql/>
+</sql#6D/>
+<#6#>:<#L#>@<#H#>
+</sql#6d/>
+<#6#>_digest:<#L#>_digest@<#H#>
+# links: dot -> dir/editor
+</:/editor/>
+</:-return-default/bouncer/>
+</editor/>
+|<#B#>/ezmlm-send '<#D#>'
+|<#B#>/ezmlm-warn '<#D#>' || exit 0
+</bouncer/>
+|<#B#>/ezmlm-weed
+</bouncer#D/>
+|<#B#>/ezmlm-return -D '<#D#>'
+</bouncer#d/>
+|<#B#>/ezmlm-return -d '<#D#>'
+</inhost/>
+<#H#>
+</outhost/>
+<#H#>
+</inlocal/>
+<#L#>
+</outlocal/>
+<#L#>
+</mailinglist/>
+contact <#3#>-help@<#4#>; run by ezmlm
+</headeradd/>
+Precedence: bulk
+</headerremove/>
+return-path
+return-receipt-to
+content-length
+precedence
+</text/top/>
+Hi! This is the ezmlm program.
+I'm managing the <#3#>@<#4#>
+</#D/>
+and <#3#>-digest@<#4#>
+mailing lists.
+
+</#d/>
+mailing list.
+</text/bounce-bottom/>
+
+--- Enclosed is a copy of the bounce message I received.
+
+</text/bounce-num/>
+
+</#D/>
+I've kept a list of which messages from the <#3#>@<#4#>
+mailing list have bounced from your address.
+
+</#d/>
+I've kept a list of which messages from
+the <#3#>-digest@<#4#>
+digest list have bounced from your address. In each case, the number
+is the number of the first message in the digest.
+
+</#a/>
+Copies of the messages may be in the archive.
+
+To get message 12345 from the archive, send an empty message to:
+   <<#3#>-get.12345@<#4#>>
+
+To retrieve a set of messages 123-145 (a maximum of 100 per request),
+send an empty message to:
+   <<#3#>-get.123_145@<#4#>>
+
+To receive a subject and author list for the last 100 or so messages,
+send an empty message to:
+   <<#3#>-index@<#4#>>
+
+To receive the thread of messages including message 123,
+send an empty message to:
+   <<#3#>-thread.123@<#4#>>
+
+<//>
+Here are the message numbers:
+
+</text/bounce-probe/>
+
+</#D/>
+Messages to you from the <#3#>@<#4#> mailing list
+</#d/>
+Messages to you from the <#3#>-digest@<#4#> list
+<//>
+seem to have been bouncing. I sent you a warning message, but it bounced.
+I've attached a copy of the bounce message.
+
+This is a probe to check whether your address is reachable. If this
+probe bounces, I will remove your address from the list
+without further notice. You can re-subscribe
+by sending an empty message to the following address:
+</#D/>
+   <<#3#>-subscribe@<#4#>>
+
+</#d/>
+   <<#3#>-digest-subscribe@<#4#>>
+
+</text/bounce-warn/>
+
+Messages to you from the
+</#D/>
+<#3#>@<#4#> mailing list seem to
+</#d/>
+<#3#>-digest@<#4#> digest list seem to
+<//>
+have been bouncing. I've attached a copy of the first bounce
+message I received.
+
+If this message bounces too, I will send you a probe. If the probe bounces,
+I will remove your address from the mailing list, without further notice.
+# End of minimal sublist setup.
diff --git a/ezmlmsubrc.5 b/ezmlmsubrc.5
new file mode 100644 (file)
index 0000000..c32ad33
--- /dev/null
@@ -0,0 +1,79 @@
+.TH ezmlmsubrc 5
+.SH NAME
+ezmlmsubrc \- set up a minimal sublist
+.SH SYNOPSIS
+.B ezmlm-make
+.B \-C ezmlmsubrc
+.B -options
+.I dir dot local host
+.SH DESCRIPTION
+.B ezmlmglrc
+instructs
+.B ezmlm-make(1)
+to create
+.I dir
+and files within it to support the
+.I local\fB@\fIhost
+sublist. The sublist handles bounces, but not subscriptions. It is intended
+for creation of sublists that are part of an SQL database supported distributed
+ezmlm list. For creation of regular sublists, use
+.B ezmlm-make(1)
+with
+.BR ezmlmrc(5) .
+.SH "REQUIRED SWITCHES"
+While these switches can be omitted, the list will not function unless they
+are specified.
+.TP
+.B \-3\fI mainlocal
+Local name of the main list for which this list is a sublist. If the sublist
+is a sublist for digests,
+.I mainlocal
+should refer to the mail list, i.e. stripped of ``-digest''. This is required
+in order for the bounce texts to refer to the correct archive. Use the
+.B \-d
+switch!
+.TP
+.B \-4\fI mainhost
+Host name of the main list for which this list is a sublist.
+.TP
+.B \-6\fI host:port:user:pass:db:table
+SQL connect info. Specifies the host and port to connect to, the user/password
+to use, the database name, and the root table name.
+The host defaults to ``localhost'', the database and table to ``ezmlm''. The
+port default is the default for the particular SQL server type used. For
+sublists disseminating a digest, ``table'' will end in ``_digest''.
+.SH OPTIONS
+.TP
+.B \-d
+This sublist is a sublist of a digest list.
+The
+.B \-3
+argment used should be the local name of the main list, rather than
+the digest list, i.e. the terminal ``-digest'' should be stripped.
+.TP
+.B \-5\fI owner
+The address to which to redirect mail send to
+.IR local-\fBowner@\fIhost .
+The default is the owner address for the main list.
+.SH USAGE
+A common task is to create both a sublist for the main list and a sublist
+for the corresponding digest. Note that the local list names are given
+as is, whereas the main list name always refers to the main list itself
+(and not its digest). The main list is  ``mainloc@mainhost'', the local
+sublist is ``me-sub1@myhost''; the main digest is ``mainloc-digest@mainhost''
+and the local digest sublist is ``me-sub1-digest@myhost''.
+
+.EX
+ezmlm-make -Cezmlmsubrc -3 mainloc -4 mainhost -6 mainhost::user:pw:db:tab
+~/DIR ~me/.qmail-sub1 me-sub1 myhost
+.EE
+
+.EX
+ezmlm-make -Cezmlmsubrc -d -3 mainloc -4 mainhost -6 mainhost::user:pw:db:tab
+\-d ~/DIR ~me/.qmail-sub1-digest me-sub1-digest myhost
+.EE
+.SH "SEE ALSO"
+ezmlm-make(1),
+ezmlm(5),
+ezmlmrc(5)
+
diff --git a/idx.h b/idx.h
new file mode 100644 (file)
index 0000000..59f7867
--- /dev/null
+++ b/idx.h
@@ -0,0 +1,354 @@
+/*$Id: idx.h,v 1.57 1999/11/29 04:54:01 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#ifndef IDX_H
+#define IDX_H
+
+/* Version of this release */
+#define EZIDX_VERSION "ezmlm-idx-0.40\n"
+
+/* Range for '-thread' to protect large archives. A '-thread' search */
+/* will start at most THREAD_BEFORE messages before the action argument */
+/* and go to at most THREAD_AFTER messages after the action argument. */
+#define THREAD_BEFORE 2000
+#define THREAD_AFTER 2000
+
+/* Maximum number of messages returned by get */
+/* You also have to update /text/bottom in ezmlmrc if you change this */
+#define MAXGET 100
+
+/* Number of messages before latest digest to return for list-get.99999_x */
+/* This is still subject to the MAXGET restriction */
+#define HISTGET 30
+
+/* Maximum subject index entries returned by index */
+/* Must be multiple of 100 */
+/* You also have to update /text/bottom in ezmlmrc if you change this */
+#define MAXINDEX 2000
+
+/* Max dir/text file size allowed by -edit */
+#define MAXEDIT 10240
+
+/* Timeout in seconds before a bounce warning is sent. Default is  */
+/* 1000000, i.e. 11.57 days. Setting it lower reduces the number of */
+/* messages in the bouce dir, but makes it more likely that an address */
+/* is unsubscribed due to a temporary error. This compile-time default */
+/* should rarely need changing, as it can be overridden with the ezmlm-warn */
+/* -t switch */
+#define BOUNCE_TIMEOUT 1000000L
+
+/* ezmlm-limit defaults. Convert to moderation or defer if more than  */
+/* LIMMSG messages arrive within LIMSECS */
+#define LIMMSG 30L
+#define LIMSECS 3600L
+
+/* Command names and alternative command names */
+/* all alternates must be defined! */
+/* The language-specific blocks need to undefine and redefine commands */
+
+#define ALT_LIST "list"
+#define ALT_LISTN "listn"
+#define ALT_EDIT "edit"
+#define ALT_FAQ "faq"
+#define ALT_GET "get"
+#define ALT_HELP "help"
+#define ALT_INDEX "index"
+#define ALT_INFO "info"
+#define ALT_LOG "log"
+#define ALT_REQUEST "request"
+#define ALT_SUBSCRIBE "subscribe"
+#define ALT_THREAD "thread"
+#define ALT_UNSUBSCRIBE "unsubscribe"
+#define ALT_QUERY "query"
+
+/* to get alternative command names, you need to undefine and redefine */
+/* them. Do this within a language block and send it to the author for */
+/* inclusing in future versions. If it's here already, just uncomment the */
+/* define for the appropriate language. */
+/* #define LANG_FR 1 */
+
+/* French Version */
+#ifdef LANG_FR
+#undef ALT_SUBSCRIBE
+#define ALT_SUBSCRIBE "-inscription"
+#undef ALT_UNSUBSCRIBE
+#define ALT_UNSUBSCRIBE "-desinscription"
+#endif
+/* end French Version */
+
+/* Text that is used in the outgoing messages (there is some other text, but */
+/* it needs to stay constant in order to comply with rfc1153 */
+
+/* Topics (messages nnn through mmm):\n */
+#define TXT_TOP_TOPICS "Topics"
+#define TXT_TOP_MESSAGES " (messages "
+#define TXT_TOP_THROUGH " through "
+#define TXT_TOP_LAST "):\n"
+
+/* in digest */
+#define TXT_ADMINISTRIVIA "\nAdministrivia:\n\n"
+#define TXT_SUPPRESSED "\n<suppressed>\n\n"
+
+/* for the message author line: 000 by */
+/* keep this short! */
+#define TXT_BY " by: "
+
+/* Since this is now run-time configurable, we'll go with the lowest */
+/* common denominator (per rfc2046). -> ISO-8859-1 if you don't like that */
+#define TXT_DEF_CHARSET "us-ascii"
+
+/* should start with 20 'a' [in place of hash] */
+#define TXT_NOINDEX "aaaaaaaaaaaaaaaaaaaa <- subject index not available for message(s) ->\n"
+
+/* When copy of the message is suppressed (is this really used?)*/
+#define TXT_SUPPRESSED "\n<suppressed>\n\n"
+
+/* Subject: MODERATE for inlocal@inhost */
+#define TXT_MODERATE "MODERATE for "
+
+/* Subject: Returned post for inlocal@inhost */
+/* (used both for rejected and timed-out posts) */
+#define TXT_RETURNED_POST "Returned post for "
+
+/* Subject: CONFIRM subscribe to | unsubscribe from */
+#define TXT_USRCONFIRM "confirm "
+#define TXT_MODCONFIRM "CONFIRM "
+#define TXT_SUBSCRIBE_TO "subscribe to "
+#define TXT_UNSUBSCRIBE_FROM "unsubscribe from "
+
+/* Subject: WELCOME to */
+#define TXT_WELCOME "Subject: WELCOME to "
+
+/* Subject: GOODBYE from */
+#define TXT_GOODBYE "Subject: GOODBYE from "
+
+/* Subject: ezmlm response\n */
+#define TXT_EZMLM_RESPONSE "Subject: ezmlm response\n"
+
+/* Subject: majordomo results\n\n [where "majordomo" is outlocal] */
+#define TXT_RESULTS " results\n\n"
+
+/* Subject: Edit file xxx for list@host */
+#define TXT_EDIT_RESPONSE "Subject: EDIT "
+#define TXT_EDIT_FOR " for "
+
+/* Subject: Editable text files\n */
+#define TXT_EDIT_LIST "Subject: List of editable text files\n"
+
+/* markers for ezmlm-manage text file edit */
+/* MUST start with '%' */
+#define TXT_EDIT_START "%%% START OF TEXT FILE"
+#define TXT_EDIT_END "%%% END OF TEXT FILE"
+
+#define TXT_EDIT_SUCCESS "Subject: Success editing "
+
+/* Text for '-list' command */
+#define TXT_LISTMEMBERS "\nSubscribers to this list are:\n\n"
+
+/* Output formats - letter used to override default */
+#define FORMATS "mrvnx"
+#define MIME 'm'
+#define RFC1153 'r'
+/* ---------------- virgin = MIME without header processing */
+#define VIRGIN 'v'
+/* NATIVE 'n' = VIRGIN without threading */
+#define NATIVE 'n'
+/* MIXED => multipart/mixed MIME instead of multipart/digest. Needed to bypass*/
+/* pine bug when content-transfer-encoding is used (pine fails to show the */
+/* initial encoded text/plain part of mulpart/digest, but not of ../mixed) */
+#define MIXED 'x'
+/* default output format. */
+#define DEFAULT_FORMAT MIME
+
+/* Use MIME enclosure for message to moderate by default (1) or not (0) */
+/* ezmlm-store switches -m/-M override */
+#define MOD_MIME 1
+
+/* Used to add "filname=listname.msgno" to digest part content-type line.
+   This confuses the heck out of Outlook Express 5.0. To circumvent this
+   bug the addition has been removed. Uncomment the next line to get it
+   anyway. */
+/* #define DIGEST_PART_FILENAME */
+
+/* Mode of messages in archive. For ezmlm-0.53 this is 0744, but for  */
+/* "secret" lists it may make more sense to make it 0700.             */
+#define MODE_ARCHIVE 0744
+
+/* ezmlm-get actions  (ACTION_GET also for -get in ezmlm-manage) */
+#define ACTION_GET "get"
+#define ACTION_INDEX "index"
+#define ACTION_THREAD "thread"
+
+/* ezmlm-request actions */
+#define ACTION_REQUEST "request"
+
+/* actions for post acceptance/rejection */
+#define ACTION_ACCEPT "accept-"
+#define ACTION_REJECT "reject-"
+
+/* ezmlm-manage actions */
+#define ACTION_LIST "list"
+#define ACTION_LISTN "listn"
+#define ACTION_HELP "help"
+#define ACTION_INFO "info"
+#define ACTION_FAQ "faq"
+#define ACTION_LOG "log"
+#define ACTION_SUBSCRIBE "subscribe"
+#define ACTION_UNSUBSCRIBE "unsubscribe"
+#define ACTION_QUERY "query"
+#define ACTION_EDIT "edit"
+/* if you change this, you MUST ADJUST LENGTH_ED as well! */
+#define ACTION_ED "ed."
+#define LENGTH_ED 3
+
+/* ACTION_XC has to be a string "-xc." where x is any letter. All commands */
+/* should have different letters. They no longer have to match the first    */
+/* letter of subscribe/unsubscribe. */
+/* The third char of ACTION_SC/TC/UV/VC has to be 'c' */
+
+/* user subscription confirm */
+#define ACTION_SC "sc."
+/* moderator subscription confirm */
+#define ACTION_TC "tc."
+/* user unsubscribe confirm */
+#define ACTION_UC "uc."
+/* moderator unsubscribe confirm */
+#define ACTION_VC "vc."
+
+/* name addition for digest, i.e. list-"digest" Don't change! */
+#define ACTION_DIGEST "digest"
+
+/* name addition for dir/extra db, i.e. list-"allow" */
+#define ACTION_ALLOW "allow"
+/* name addition for dir/blacklist db, i.e. list-"deny" */
+#define ACTION_DENY "deny"
+
+/* defaults for message time out in moderation queue. If modsub is 0 */
+/* or empty, DELAY_DEFAULT is used. If it is set, it is made to be  */
+/* within DELAY_MIN .. DELAY_MAX. All in hours. */
+#define DELAY_MIN 24
+#define DELAY_DEFAULT 120
+#define DELAY_MAX 240
+
+/* Mode of messages in moderation queue. The owner mode is |'d with 7.*/
+/* The group/world mode can be set to anything, but it really doesn't */
+/* make sense to make these messages visible to anyone else.          */
+#define MODE_MOD_MSG 0700
+
+/* name and location of system-wide customized ezmlmrc. This is where */
+/* ezmlm-make looks first (unless the -c switch is specified) before  */
+/* falling back to the (usually unchanged) version in the ezmlm bin   */
+/* directory. */
+#define TXT_ETC_EZMLMRC "/etc/ezmlm/ezmlmrc"
+
+/* same name added to auto_bin. Note leading slash! */
+#define TXT_EZMLMRC "/ezmlmrc"
+
+/* same in dot dir for local config (-c) */
+#define TXT_DOTEZMLMRC ".ezmlmrc"
+
+/* name of config file for ezmlm-cron */
+#define TXT_EZCRONRC "ezcronrc"
+
+/* default timestamp for ezmlm-limit */
+#define TXT_LOOPNUM "loopnum"
+
+/* ezmlm-cgi config file for normal SUID root install */
+#define EZ_CGIRC "/etc/ezmlm/ezcgirc"
+
+/* ezmlm-cgi config file for local install we expect to find the file in PWD */
+#define EZ_CGIRC_LOC ".ezcgirc"
+
+/* default charset for ezmlm-cgi [config file overrides per list] */
+#define EZ_CHARSET "iso-8859-1"
+
+/*------------ Specific to SQL version ------------------------------*/
+/* cookie tag for SQL version of sublisting */
+/* NOTE: Need to include terminal space! */
+#define TXT_TAG "X-Ezauth: "
+
+/* max no of bounces that ezmlm-receipt stores */
+#define MAX_MAIN_BOUNCES 50
+
+/* Length of domain field for SQL version. It does only the text after */
+/* the last '.' in the address, so there is no reason to set it to */
+/* anything other than '3'. We truncate it rather than relying on the */
+/* SQL Server since we can't be sure that the SQL Server doesn't have */
+/* buffer overrun holes and the address is user-controlled */
+#define DOMAIN_LENGTH 3
+
+/* programs used for outgoing mail. Normally, qmail-queue is used. Replace */
+/* with qmail-qmqpc to use only qmqp for outgoing mail. QMQPC is for */
+/* large lists when DIR/qmqpservers is present. Only posts and digests will */
+/* use QMQP. If the normal qmail-qmqpc is used the contents of */
+/* DIR/qmqpcservers are ignored. With a patch, qmail-qmqpc will use the */
+/* servers on it's command line. In this case, the IP addresses listed one */
+/* per line in DIR/qmqpservers will be tried until a working one is found. */
+/* the option is mainly to allow large list clusters on a single host to use */
+/* different QMQPC hosts as exploders.*/
+#define PROG_QMAIL_QUEUE "bin/qmail-queue"
+#define PROG_QMAIL_QMQPC "bin/qmail-qmqpc"
+
+/*---------- Things below this line are not configurable -----------*/
+/* file in DIR that has the qmqpc servers (if any) */
+#define QMQPSERVERS "qmqpservers"
+/* database types */
+#define FLD_DIGEST 1
+#define FLD_ALLOW 2
+#define FLD_DENY 3
+/* Action types */
+#define AC_NONE 0
+#define AC_GET 1
+#define AC_DIGEST 2
+#define AC_THREAD 3
+#define AC_INDEX 4
+#define AC_LIST 5
+#define AC_HELP 6
+#define AC_EDIT 7
+#define AC_DENY 8
+#define AC_LOG 9
+#define AC_SUBSCRIBE 10
+#define AC_UNSUBSCRIBE 11
+#define AC_SC 12
+#define AC_LISTN 13
+
+typedef struct msgentry {      /* one per message in range */
+  unsigned long subnum;                /* subject number */
+  unsigned long authnum;       /* message author number */
+  unsigned int date;           /* yyyymm as number */
+} msgentry;
+
+typedef struct subentry {      /* one per unique subject in message range */
+  void *higher;
+  void *lower;
+  char *sub;                   /* string with terminating '\0' */
+                               /* when building, higher/lower=0 marks end */
+                               /* of branch. When printing, start at the  */
+                               /* beginning of the table and go up until  */
+                               /* sub = 0. */
+  unsigned int sublen;
+  unsigned long firstmsg;      /* the first message with this subject*/
+  unsigned long lastmsg;       /* the last message with this subject*/
+  unsigned char msginthread;   /* number of messages seen in this thread */
+} subentry;
+
+typedef struct authentry {     /* one per unique author in message range */
+  void *higher;
+  void *lower;
+  char *auth;                  /* string with terminating '\0' */
+                               /* when building, higher/lower=0 marks end */
+                               /* of branch. When printing, start at the  */
+                               /* beginning of the table and go up until  */
+                               /* auth = 0. */
+  unsigned long authlen;
+  unsigned long firstmsg;      /* the first message with this author */
+                               /* lastmsg not very useful as author are less */
+                               /* clustered than threads */
+} authentry;
+
+typedef struct dateentry {     /* date yyyymm and 1st message of that date */
+  unsigned int date;
+  unsigned int msg;
+} dateentry;
+
+#endif
+
diff --git a/idx.patch b/idx.patch
new file mode 100644 (file)
index 0000000..7fd82fa
--- /dev/null
+++ b/idx.patch
@@ -0,0 +1,3038 @@
+--- ezmlm-warn.1       1998/02/17 00:32:45     1.1
++++ ezmlm-warn.1       1998/12/21 04:35:16     1.5
+@@ -3,6 +3,15 @@
+ ezmlm-warn \- send out bounce warnings
+ .SH SYNOPSIS
+ .B ezmlm-warn
++[
++.B \-dD
++][
++.B \-t
++.I timeout
++][
++.B \-l
++.I lockout
++]
+ .I dir
+ .SH DESCRIPTION
+ .B ezmlm-warn
+@@ -12,27 +21,91 @@
+ .B ezmlm-warn
+ scans
+-.I dir\fB/bounce
+-for bounce messages received by
+-.BR ezmlm-return .
+-If it sees a distribution bounce for
++.I dir\fB/bounce/d/
++for directories older than
++.I timeout
++days ago (see
++.BR \-t ).
++The directories are created by
++B ezmlm-return
++and contain bounces.
++If
++.B ezmlm-warn
++sees a distribution bounce for
+ .I box\fB@\fIdomain
+-received more than ten days ago,
++received more than
++.I timeout
++days ago,
+ it sends
+ .I box\fB@\fIdomain
+ a list of all the message numbers missed recently,
+ and deletes the bounce.
+ If it sees a warning bounce for
+ .I box\fB@\fIdomain
+-received more than ten days ago,
++received more than
++.I timeout
++days ago,
+ it sends
+ .I box\fB@\fIdomain
+ a probe,
+ and deletes the bounce.
+ .B ezmlm-warn
++uses
++.I dir\fB/bounce/lastd
++to keep track of when it was last run. If insufficient time has
++passed (see
++.BR \-l )
++.B ezmlm-warn
++exits without further action.
++
++.B ezmlm-warn
++keeps files with the bounced message numbers in
++.IR dir\fB/bounce/h .
++Expired files are removed and
++.I dir\fB/bounce/lasth
++keeps track of the last subdirectory scanned.
++
++.B ezmlm-warn
+ will not send a warning or probe to an address that is
+ not currently a subscriber.
++.SH OPTIONS
++.TP
++.B \-d
++process bounces for the digest list, rather than for the main list.
++Digest list bounces are stored in
++.I dir\fB/digest/bounce/
++rather than in
++.IR dir\fB/bounce/ .
++.TP
++.B \-D
++(Default.)
++Process bounces for the main list.
++.TP
++.B \-l \fIlockout
++.B ezmlm-warn
++will abort execution if it was run less than
++.I lockout
++seconds ago. The default is
++.I timeout /
++50, which with the default
++.I timeout
++is 20,000 seconds (approx. 5.6 hours). There is no reason to use this
++switch, except for testing and possibly in combination with
++.BR \-t.
++.TP
++.B \-t \fItimeout
++Bounces received more than
++.I timeout
++days ago are processed. This overrides the default of 1,000,000
++seconds (approximately 10 days)
++and may possibly be useful for very large busy lists. Also, a
++.I timeout
++of zero can be used to send a warning to all addresses for which
++a bounce has been received and a probe for all addresses for which a
++warning has bounces.
++This is useful to rapidly clear
++out bouncing addresses from a (low quality) address list.
+ .SH "SEE ALSO"
+ ezmlm-make(1),
+ ezmlm-return(1),
+--- ezmlm-return.1     1998/02/17 00:33:15     1.1
++++ ezmlm-return.1     1999/08/19 03:57:54     1.6
+@@ -3,12 +3,16 @@
+ ezmlm-return \- handle mailing list bounces
+ .SH SYNOPSIS
+ .B ezmlm-return
++[
++.B \-dD
++]
+ .I dir
+ .SH DESCRIPTION
+ .B ezmlm-return
+ handles bounces for the mailing list
+ stored in
+-.IR dir .
++.I dir
++and, if it exists, the associated digest list.
+ .B ezmlm-return
+ is normally invoked from a
+@@ -21,6 +25,30 @@
+ and
+ .BR HOST
+ environment variables.
++
++.B ezmlm-return
++exits 99, not 0, upon success.
++.SH OPTIONS
++.TP
++.B \-d
++.B ezmlm-return
++will assume the bounce is for a digest list.
++Normally,
++.B ezmlm-return
++will autodetect this from the bounce address. Autodetection makes
++.B ezmlm-return
++less flexible and will be removed in future versions.
++.TP
++.B \-D
++.B ezmlm-return
++will assume that the bounce is for a normal (non-digest) list.
++Normally,
++.B ezmlm-return
++will autodetect this from the bounce address. Autodetection makes
++.B ezmlm-return
++less flexible and will be removed in future versions.
++.B \-D
++will become the default.
+ .SH ADDRESSES
+ .B ezmlm-return
+ handles mail sent to any of the following addresses:
+@@ -59,6 +87,21 @@
+ will remove
+ .I box\fB@\fIdomain
+ from the mailing list.
++.TP
++.I local\fB\-return\-receipt\-\fIcookie\-fImsg\-
++A receipt from the list. This is logged. For SQL supporting lists,
++.I cookie
++is verified and receipt logged only if the cookie is correct. The arrival
++of the receipt shows that qmail at the sending host is running.
++
++For all the above addresses if,
++.I local
++is followed by
++.IR \-digest ,
++bounces are assumed to be from the digest list, and are stored in
++.I dir\fB/digest/bounce
++rather than in
++.I dir \fB/bounce .
+ .SH "SEE ALSO"
+ ezmlm-manage(1),
+ ezmlm-make(1),
+--- ezmlm-send.1       1997/07/23 18:55:45     1.1
++++ ezmlm-send.1       1999/08/19 03:13:36     1.27
+@@ -3,6 +3,11 @@
+ ezmlm-send \- distribute a message to a mailing list
+ .SH SYNOPSIS
+ .B ezmlm-send
++[
++.B \-cCrRvV
++] [
++.B \-h\fI header
++]
+ .I dir
+ .SH DESCRIPTION
+ .B ezmlm-send
+@@ -14,9 +19,34 @@
+ exists,
+ .B ezmlm-send
+ records a copy of the message in the
+-.I dir\fB/archive
++.I dir\fB/archive/
+ directory.
++If
++.I dir\fB/indexed
++exists,
++.B ezmlm-send
++adds the subject, author and time stamp of the message to the index, kept with
++the message in a subdirectory of
++.IR dir\fB/archive/ .
++The subject is processed to make reply-subject entries identical to
++original
++message subject entries.
++The subject index is used for the archive retrieval functions of
++.BR ezmlm-get(1) .  
++Use
++.B ezmlm-idx(1)
++to create a subject index from a preexisting archive.
++
++Subject and author lines are decoded if they are encoded per rfc2047. When
++split lines are unfolded, the number of escape sequences for
++iso-2022-* character sets is minimized. For instance, two
++consequtive toascii sequences are reduced.
++This processing is done for the character set specified in
++.IR dir\fB/charset .
++The result of this process is the same for a given subject, irrespective
++of encoding.
++
+ At the beginning of the message,
+ .B ezmlm-send
+ prints a new
+@@ -27,10 +57,24 @@
+ .B Mailing-List
+ field.
++If
++.I dir\fB/listid
++exists,
++.B ezmlm-send
++will assume that the format is correct and
++create a ``List-ID:'' header by placing the contents after the
++text ``List-ID: ''. 
++
++Next,
++.B ezmlm-send
++prints all the new fields listed in
++.IR dir\fB/headeradd .
++Any tags, ``<#h#>'', ``<#l#>'', or ``<#n#>'' found in these headers
++are replaced by the list host name, list local name, and message number,
++respectively.
++
+ .B ezmlm-send
+-then prints all the new fields listed in
+-.IR dir\fB/headeradd ,
+-followed by an appropriate
++then prints an appropriate
+ .B Delivered-To
+ line.
+@@ -39,6 +83,63 @@
+ .IR dir\fB/headerremove .
+ .B ezmlm-send
++removes MIME parts specified in
++.I dir\fB/mimeremove
++before archiving and distribution of the message.
++
++If
++.I dir\fB/text/trailer
++exists,
++.B ezmlm-send
++adds the trailer to simple text/plain messages in the same encoding as used for
++the the message. However, if the encoding is ``base64'' it is not safe
++to do this and the header is suppressed.
++For composite MIME messages, the trailer is added as a separate
++part, with the character set and encoding specified in
++.IR dir\fB/charset .
++The trailer is not added to multipart/alternative messages.
++Any tags, ``<#h#>'', ``<#l#>'', or ``<#n#>'' found in
++.I dir\fB/text/trailer
++are replaced by the list host name, list local name, and message number,
++respectively.
++
++If
++.I dir\fB/prefix
++exists,
++.B ezmlm-send
++will prefix the subject line with the first line of this
++file. A space will be added to separate
++.B prefix
++from the subject text.
++.B prefix
++is ignored for sublists. If
++.I dir\fB/prefix
++contains a ``#'', the last ``#'' will be replaced by the message number.
++Any prefix starting with text of a
++reply indicator (``Re:'', ``Re[n]:'', etc) will cause problems.
++The prefix may be
++rfc2047 encoded. Rfc2047 Iso-2022-* encoded prefixes
++.I must
++end in ascii.
++
++The prefix feature and especially the message number feature
++modify the message in violation
++with Internet mail standards. The features have been implemented by popular
++demand. Use at your own peril.
++
++.I dir\fB/sequence
++is ignored as of ezmlm-idx-0.32. Use
++.I dir\fB/headeradd
++with substitution to achieve the same goal.
++
++If
++.I dir\fB/qmqpservers
++exists,
++.B ezmlm-send will use
++.B qmail-qmqp(1)
++to send messages.
++
++.B ezmlm-send
+ does not distribute bounce messages:
+ if the environment variable
+ .B SENDER
+@@ -46,6 +147,60 @@
+ .BR #@[] ,
+ .B ezmlm-send
+ rejects the message.
++.SH OPTIONS
++.TP
++.B \-c
++No longer supported. Ignored for backwards compatibility.
++.TP
++.B \-C
++No longer supported. Ignored for backwards compatibility.
++.B ezmlm-send
++has to parse the subscriber database.
++.TP
++.B \-h\fI header
++If the list is a sublist, i.e.
++.I dir\fB/sublist
++exists,
++.I header
++is required in all messages to the list. This option is used
++when ezmlm is used to run a sublist of a lists run by a different
++mailing list
++manager that uses
++.I header
++rather than ``Mailing-List'' to identify messages from the list.
++Anything after the first colon (if present) in
++.I header
++is ignored.
++.TP
++.B \-r
++Copy incoming ``Received:'' headers to the outgoing message.
++.TP
++.B \-R
++(Default.)
++Do not copy incoming ``Received:'' headers, except the one added by
++the (last) listhost, to the outgoing message.
++In some
++cases, especially for sublists,
++the messages can have a large number of ``Received:''
++headers. This may lead to bounces for some users due to
++sendmail ``hopcounts'' set too low somewhere in the mail path. These users can
++subscribe and receive warning and probe messages, but no list messages, unless
++the number of ``Received:'' headers is reduced.
++
++Pre-list ``Received:'' headers are of little interest to normal list
++subscribers. ``Received:'' headers are
++still copied to the archive and available
++to anyone from there for message tracking purposes.
++.TP
++.B \-v
++Display
++.B ezmlm-send
++version information.
++.TP
++.B \-V
++Display
++.B ezmlm-send
++version information.
+ .SH "SUBLISTS"
+ If
+ .I dir\fB/sublist
+@@ -77,10 +232,71 @@
+ does not add its own
+ .B Mailing-List
+ field.
++
++Fourth,
++.B ezmlm-send
++uses the incoming message number for the outgoing message, if the list
++is not archived and the incoming SENDER has the correct format.
++This allows you to refer bounce warning recipients to the main list for
++archive retrieval of the missed messages. If the sublist archives
++message, it is assumed that missed messages will be retrieved from the sublist
++archive.
++
++The list
++still increments
++.I dir\fB/num
++for each message. If the sublist is archived, use of incoming message number
++for archive storage would be a security risk. In this case, the local sublist
++message number is used.
++.SH "OPTION USAGE"
++In general, the use of a prefix is discouraged. It wastes subject line space,
++creates trouble when MUAs add non-standard reply indicators. However, many
++users expect it not because it is useful, but because they are used to it.
++
++The
++.B \-C
++switch prevents posts from being set to SENDER. Rather than just copying
++out subscriber address files,
++.B ezmlm-send
++has to parse them to look for SENDER. This makes it less efficient. Also,
++it is useful for the SENDER to see the post to know that it has made it
++to the list, and it's context to other subscribers, i.e. where it came
++within the traffic of messages on the list.
++
++Avoiding SENDER as a recipient is useful in small lists, such as small
++teams with varying members, where ezmlm serves mainly as an efficient tool
++to keep the team connected without administrator intervention. Here the
++overhead of subscriber list parsing is negligible.
++.SH "CHARACTER SETS"
++If the list is indexed,
++.B ezmlm-send
++will keep a message index. rfc2047-encoded subject and from lines will be
++decoded.
++If
++.I dir\fB/charset
++exists,
++.B ezmlm-send
++will eliminate redundant escape sequences from the headers according to
++the character set specified in this file.
++Only character sets using escape sequences need this support. Currently,
++supported are iso-2022-jp*, iso-2022-kr, and iso-2022-cn*. Only iso-2022-jp
++has been tested extensively.
++
++The character set can be suffixed
++by ``:'' followed by a code. Recognized codes are ``Q'' 
++for ``Quoted-Printable'', and ``B'' for ``base64''.
++
++For
++.BR ezmlm-send ,
++this affects the format of the trailer, if a trailer is specified and if the
++message is a multipart mime message
+ .SH "SEE ALSO"
++ezmlm-get(1),
++ezmlm-idx(1),
+ ezmlm-manage(1),
+ ezmlm-make(1),
+ ezmlm-sub(1),
+ ezmlm-unsub(1),
+ ezmlm-reject(1),
+-ezmlm(5)
++ezmlm(5),
++qmail-qmqp(1)
+--- ezmlm-sub.1        1998/12/06 16:48:14     1.1
++++ ezmlm-sub.1        1999/08/19 03:22:14     1.5
+@@ -3,9 +3,20 @@
+ ezmlm-sub \- manually add addresses to a mailing list
+ .SH SYNOPSIS
+ .B ezmlm-sub
++[
++.B \-HmMnNsSvV
++][
++.B \-h
++.I hash
++]
++.B 
+ .I dir
+ [
+-.I box\fB@\fIdomain ...
++.I box\fB@\fIdomain 
++[
++.I name
++]
++.I ...
+ ]
+ .SH DESCRIPTION
+ .B ezmlm-sub
+@@ -13,12 +24,20 @@
+ .I box\fB@\fIdomain
+ to the mailing list stored in
+ .IR dir .
++.I name
++is added as a comment to the subscription log, if the
++.B \-n
++switch is used.
++
++If no argument is given on the command line,
++.B ezmlm-sub
++processes stdin.
+ If
+ .I box\fB@\fIdomain
+ is already on the mailing list,
+ .B ezmlm-sub
+-leaves it there.
++leaves it there and does not modify the subscription log.
+ .B ezmlm-sub
+ converts
+@@ -28,11 +47,61 @@
+ to the mailing list.
+ .I box\fB@\fIdomain
+-cannot be longer than 400 characters.
++cannot be longer than 400 characters (255 characters with mysql support).
++.SH "GENERAL OPTIONS"
++.TP
++.B \-n
++Assume arguments are pairs of
++.I box\fB@\fIdomain
++and
++.IR name
++(or other subscriber info)
++rather than addresses alone.
++.B ezmlm-sub(1)
++will add the first argument in each pair to the subscriber list. If it is
++a new address,
++.I name
++will be added to the subscription log.
++.TP
++.B \-N
++(Default.)
++Arguments are all addresses of the type
++.IR box\fB@\fIdomain .
++.TP
++.B \-v
++Display
++.B ezmlm-sub(1)
++version information.
++.TP
++.B \-V
++Display
++.B ezmlm-sub(1)
++version information.
++.SH "MYSQL OPTIONS"
++These option is silently ignored in the absence of mysql support.
++.TP
++.B \-h \fIhash
++With mysql support the argument is used as the hash. The argument should
++be between 1 and 99. The hash is used in
++in the distribution of addresses between sublists. As the hash is normally
++between 0 and 52, controlling the hash makes it possible to add addresses
++that cannot be manipulated remotely.
++.TP
++.B \-m
++(Default.)
++Use SQL support if available.
++.TP
++.B \-M
++Do not use SQL support even if available. This option can be used to build
++a normal local subscriber database even for lists with SQL support. Use
++in combination with
++.B ezmlm-list(1)
++to convert an SQL address db to a ezmlm standard address database.
+ .SH "SEE ALSO"
+ ezmlm-list(1),
+ ezmlm-manage(1),
+ ezmlm-make(1),
++ezmlm-receipt(1),
+ ezmlm-send(1),
+ ezmlm-unsub(1),
+ ezmlm(5)
+--- ezmlm-unsub.1      1998/12/06 16:48:14     1.1
++++ ezmlm-unsub.1      1999/08/19 03:22:14     1.5
+@@ -3,6 +3,11 @@
+ ezmlm-unsub \- manually remove addresses from a mailing list
+ .SH SYNOPSIS
+ .B ezmlm-unsub
++[
++.B \-mMHvV
++] [
++.B \-h\fBhash
++]
+ .I dir
+ [
+ .I box\fB@\fIdomain ...
+@@ -14,6 +19,10 @@
+ from the mailing list stored in
+ .IR dir .
++If no command line argument is given,
++.B ezmlm-unsub
++will process stdin instead.
++
+ If
+ .I box\fB@\fIdomain
+ is not on the mailing list,
+@@ -26,10 +35,46 @@
+ to lowercase before removing
+ .I box\fB@\fIdomain
+ from the mailing list.
++.SH "GENERAL OPTIONS"
++.TP
++.B \-v
++Display
++.B ezmlm-unsub(1)
++version information.
++.TP
++.B \-V
++Display
++.B ezmlm-unsub(1)
++version information.
++.SH "MYSQL OPTIONS"
++These option is silently ignored in the absence of mysql support.
++.TP
++.B \-h \fIhash
++With mysql support the argument is used as the hash. The argument should
++be between 1 and 99. The hash is used in
++in the distribution of addresses between sublists. As the hash is normally
++between 0 and 52, controlling the hash makes it possible to remove addresses
++that cannot be manipulated remotely. A hash of 99 is reserved for sublists,
++and a hash of 98 is reserved for ``receipt'' addresses serviced by
++.B ezmlm-receipt(1).
++.TP
++.B \-H
++(Default.)
++The address can be removed if it has a hash in the normal range (0..52).
++.TP
++.B \-m
++(Default.)
++Use SQL support if available.
++.TP
++.B \-M
++Do not use SQL support even if available.
++This option can be used to manipulate
++a normal local subscriber database even for lists with SQL support.
+ .SH "SEE ALSO"
+ ezmlm-list(1),
+ ezmlm-manage(1),
+ ezmlm-make(1),
++ezmlm-receipt(1),
+ ezmlm-send(1),
+ ezmlm-sub(1),
+ ezmlm(5)
+--- ezmlm-list.1       1998/12/06 16:48:14     1.1
++++ ezmlm-list.1       1999/08/19 03:40:27     1.5
+@@ -3,6 +3,11 @@
+ ezmlm-list \- show the addresses on a mailing list
+ .SH SYNOPSIS
+ .B ezmlm-list
++[
++.B \-n\fI msgnum
++] [
++.B \-aAmMnNvV
++]
+ .I dir
+ .SH DESCRIPTION
+ .B ezmlm-list
+@@ -16,6 +21,40 @@
+ of a possibly malicious remote user.
+ .B ezmlm-list
+ does not strip control characters.
++.SH "GENERAL OPTIONS"
++.TP
++.B \-m
++(Default.)
++Use SQL support if available.
++.TP
++.B \-M
++Do not use SQL support even if available.
++This option can be used to manipulate
++a normal local subscriber database even for lists with SQL support.
++.TP
++.B \-n
++Print only the number of subscribers.
++.TP
++.B \-N
++(Default.)
++List subscriber addresses, one per line.
++.TP
++.B \-v
++Display
++.B ezmlm-list(1)
++version information.
++.TP
++.B \-V
++Display
++.B ezmlm-list(1)
++version information.
++.SH "SEE ALSO"
++ezmlm-list(1),
++ezmlm-manage(1),
++ezmlm-make(1),
++ezmlm-send(1),
++ezmlm-sub(1),
++ezmlm(5)
+ .SH "SEE ALSO"
+ ezmlm-sub(1),
+ ezmlm-unsub(1),
+--- ezmlm.5    1997/07/13 22:01:50     1.1
++++ ezmlm.5    1999/05/12 22:41:46     1.24
+@@ -21,7 +21,60 @@
+ handles administrative requests automatically;
+ .B ezmlm-send
+ sends a message to all subscribers listed in
+-.IR dir .
++.I dir
++and also maintains a message archive and message subject index if the list
++is configured to do so.
++.B ezmlm-reject
++rejects messages that have an empty subject, or a subject consisting of
++only a command word;
++.B ezmlm-return
++handles bounces;
++.B ezmlm-warn
++warns users for which messages bounce and eventually removes them from
++the subscriber list.
++.B ezmlm-idx
++can create a subject index from an existing list archive.
++.B ezmlm-get
++manages message, index, and thread retrieval from the archive, as well
++as the generation of message digests;
++.B ezmlm-cron
++provides a restricted interface to cron for the generation of
++digest generation trigger messages;
++.B ezmlm-store
++queues messages of moderated lists and sends a moderation request to
++the moderator(s);
++.B ezmlm-moderate
++processes moderation requests to accept the queued message to the list
++via
++.B ezmlm-send,
++or to return the message to the sender;
++.B ezmlm-clean
++cleans up the moderation queue and returns to the sender
++any messages that have timed-out;
++.B ezmlm-gate
++posts messages that come from a SENDER in an address database, and sends
++remaining messages out for moderation;
++.B ezmlm-check
++is used to diagnose problems with ezmlm mailing list configuration;
++.B ezmlm-issub
++and
++.B ezmlm-issubn
++determine if a SENDER is a subscriber or a member of a
++collection of addresses;
++.B ezmlm-tstdig
++determines if it is time to create a new digest based on the number and
++volume of messages and the amount of time that has passed since the last
++digest was issued;
++.B ezmlm-request
++can be used to answer
++.B ezmlm
++commands in the subject line easing migration from other mailing list
++managers. It can also function as a global interface mimicking
++the interface of other mailing list manager.
++.B ezmlm-glmake
++can set up the global interface, and
++.B ezmlm-glconf
++can create a configuration file for the global interface from your lists.
+ .SH SUBSCRIBERS
+ .I dir\fB/subscribers
+ is a directory containing the subscriber list.
+@@ -69,12 +122,19 @@
+ .B ezmlm-send
+ archives all new messages if
+ .I dir\fB/archived
+-exists.
++exists. If
++.I dir\fB/indexed
++exists,
++.B ezmlm-send
++also maintains a message subject and author index.
+ Messages sent to the mailing list are numbered from 1 upwards,
+ whether or not they are archived.
+ .I dir\fB/num
+-is the number of messages sent so far.
++is the number of messages sent so far followed by ':', followed by the
++cumulative amount of message body that has passed
++.B ezmlm-send
++stored as kbytes * 4 (4 corresponds to 1kb).
+ .I dir\fB/archive
+ has subdirectories,
+@@ -83,11 +143,43 @@
+ .IR dir\fB/archive/\fIm\fB/\fIn .
+ For example, message number 15307 is stored in
+ .IR dir\fB/archive/153/07 .
++The message index is stored in the file
++.B index
++in the same subdirectory of
++.I dir\fB/archive
++holding the corresponding messages.
++Thus, the subject index contains up to 100 entries.
++
++The subject index contains message subjects that are normalized so that
++the original message and all replies have the same entry. The subject index
++is used for advanced message retrieval functions. For safety, the subject
++index is created under a temporary name
++inside
++.I dir\fB/archive
++and then moved into place.
+ .B ezmlm-manage
+ will ignore message files without the owner-execute bit set.
+ .B ezmlm-send
+ turns on the owner-execute bit after safely writing the message to disk.
++
++.B ezmlm-make
++by default adds
++.B ezmlm-get
++to
++.I dir\fB/manager
++to handle 
++.I \-get, \-index,
++and
++.I \-thread
++requests. If
++.B ezmlm-make
++is invoked with a 
++.I digcode
++command line argument, digest creation
++is enabled by putting this argument on the
++.B ezmlm-get
++command line.
+ .SH BOUNCES
+ .I dir\fB/bounce
+ is a directory containing bounce messages.
+@@ -127,9 +219,10 @@
+ sets up
+ .I dir\fB/bouncer
+ to invoke
+-.B ezmlm-return
+-and then
+-.BR ezmlm-warn .
++.BR ezmlm-return .
++.B ezmlm-warn
++is no longer invoked here due to the load it places on systems with many
++large lists with many bounces.
+ .I dir\fB/manager
+ handles incoming administrative requests.
+@@ -137,9 +230,266 @@
+ sets up
+ .I dir\fB/manager
+ to invoke
+-.B ezmlm-manage
++.BR ezmlm-get ,
++.BR ezmlm-manage ,
+ and then
+ .BR ezmlm-warn .
++
++.I dir\fB/moderator
++handles incoming message
++.I accept
++and
++.I reject
++requests for moderated lists.
++.B ezmlm-make
++sets up
++.I dir\fB/moderator
++to invoke
++.BR ezmlm-moderate ,
++and .BR ezmlm-clean .
++.SH DIGESTS
++.B ezmlm-get
++can create digests if it is invoked from the command line, from
++.IR dir\fB/editor ,
++or from
++.IR dir\fB/manager .
++The program functions in slightly different ways in these 3 settings (see
++.BR ezmlm-get(1) ).
++
++To enable automatic digests for a mailing list, use the
++.B ezmlm-make \-d
++switch. To also enable the generation of digests at specific times dictated
++by mailed trigger messages, a
++.I digcode
++should be specified on the
++.B ezmlm-get
++command line.
++This can be done by specifying
++.I digcode
++as a fifth argument to
++.B ezmlm-make
++when setting up the list.
++.I digcode
++must be alphanumeric and is case-insensitive.
++
++To generate trigger messages, use
++.B ezmlm-cron(1)
++as an interface to
++.B crond(8)
++or use
++.B crond
++directly.
++
++.I dir\fB/num
++contains the number of the last message processed, followed by ':' and a
++number that is increased by 1 for each 256 bytes of message body text
++processed. The latter number is used by
++.B ezmlm-tstdig
++to determine if a new digest is due.
++
++.I dir\fB/dignum
++contains the contents of
++.I dir\fB/num
++at the time of the last regular digest creation, followed by a ':',
++followed by a timestamp.
++It is updated after each regular digest is sent.
++
++.I dir\fB/digissue
++contains the issue number of the last regular digest. It is incremented
++for each regular digest sent.
++
++The following user crontab entry (all on one line)
++generates a digest of the list
++.I list@host.domain
++at 1600 every day:
++
++.EX
++  00 16 * * * /var/qmail/bin/qmail-inject list-dig.digcode
++.EE
++
++Alternatively,
++.B ezmlm-cron
++can be used:
++
++.EX
++  % ezmlm-cron -t 16:00 list@host digcode
++.EE
++
++.B ezmlm-get
++can also be run from the shell: To generate a digest to
++.I list-digest@host
++from the list housed in
++.IR ~joe/list :
++
++.EX
++  % ezmlm-get ~joe/list
++.EE
++
++Like other
++.B ezmlm-get
++replies, digest can be sent in several formats. See
++.B ezmlm-get(1)
++for more info.
++.SH MODERATION
++There are three aspects of moderation: moderation of posts, moderation
++of subscriptions, and "remote administration", i.e. giving the
++moderator the right to (un)subscribe any user.
++.B ezmlm
++handles these three aspects separately. The two first aspects enhance
++security, while the third decreases security, but makes list administration
++considerably easier. By default, the moderator database is the same for all
++three functions. While "remote administration" and subscription moderation
++always use the same database, the moderators for message moderation can
++be different.
++
++Even with subscription moderation, the user has to verify the request. This
++is to ensure that the user initiating the request really controls the address.
++.B ezmlm-manage
++options exist to disable the user handshake, which may be useful in some
++circumstances.
++
++For moderation options, the moderators are by stored in a subscriber
++list in
++.IR moddir\fB/subscribers .
++By default
++.I moddir
++is
++.IR dir\fB/mod .
++
++Moderators can be added and removed with:
++
++.EX
++.B ezmlm-sub
++.I moddir
++.I moderator@host
++.EE
++
++.EX
++.B ezmlm-unsub
++.I moddir
++.I moderator@host
++.EE
++
++For subscription moderation, touch
++.IR dir\fB/modsub
++after adding moderator(s).
++For remote administration, touch
++.IR dir\fB/remote .
++If the contents of these files start with a leading forward slash, it is 
++assumed to be the name of
++.B moddir
++subscription
++moderation. If both files exist and start with a forward slash, the
++.I dir\fB/remote
++contents are ignored. Moderators are stored in a subscriber list in the
++.B subscribers
++subdirectory of
++.BR moddir .
++If no directory names are specified,
++the default,
++.IR dir\fB/mod ,
++is used.
++In all cases, the
++.I subscribers
++subdirectory of the base directory must exists/be created.
++
++Moderation of messages is achieved by
++creating
++.I dir\fB/modpost
++and  modifying
++.IR dir\fB/editor
++to invoke
++.B ezmlm-store.
++.B ezmlm-store
++stores the message in
++.IR dir\fB/mod/pending
++and sends a moderation request to all moderators stored in
++.IR moddir .
++
++If
++.I dir\fB/modpost
++does not exist,
++.B ezmlm-store
++posts messages directly, and
++.B ezmlm-clean
++does nothing.
++
++If
++.I dir\fB/modpost
++contains a directory name starting with a forward slash,
++this directory is used as
++.I moddir
++for message moderation.
++Moderators are stored in a subscriber list in the
++.I subscribers
++subdirectory of
++.IR moddir .
++If no directory names are specified,
++the default,
++.IR dir\fB/mod ,
++is used.
++
++.IR dir\fB/moderator
++is linked to
++.IR dot\fB\-accept-default
++and
++.IR dot\fB\-reject-default .
++It handles replies from the moderators.
++
++In addition to a moderator list, the directories
++.IR dir\fB/mod/pending ,
++.IR dir\fB/mod/accepted ,
++and
++.IR dir\fB/mod/rejected
++must exist. These directories contain the message moderation queue.
++
++If
++.IR dir\fB/mod/modtime
++it determines the minimal time in hours that messages wait in the moderation
++queue, before they are returned to sender with the message in
++.IR dir\fB/text/mod-timeout .
++
++If a
++.I \-help
++command is send for a moderator and
++.IR dir\fB/modsub
++or
++.IR dir\fB/remote
++exist, a more detailed help message stored in
++.I dir\fB/text/mod-help
++will be sent together with the regular help. This text should not contain
++secrets.
++If
++.I dir\fB/text/mod-help
++does not exist,
++.I dir\fB/text/help
++will be sent.
++
++If a
++.I \-list
++command is sent for a moderator and
++.IR dir\fB/modsub
++or
++.IR dir\fB/remote
++exist, and the
++.B ezmlm-manage \-l
++command line switch is specified, a subscriber list will be returned.
++
++If an
++.I \-edit.file
++command is sent for a moderator and
++.IR dir\fB/remote
++exist, and the
++.B ezmlm-manage \-d
++command line switch is specified,
++.B text\fB/file
++is returned together with an
++.B ezmlm
++cookie. The remote administrator may return an edited version of the
++file, which will be stored, provided that the cookie is valid.
++See
++.B ezmlm-manage(1)
++for more info.
+ .SH TEXT
+ .I dir\fB/text
+ is a directory
+@@ -187,6 +537,27 @@
+ .B get-bad
+ Rejecting a bad archive retrieval request.
+ .TP
++.B digest
++Text copied into the
++.I Administrativia
++section of the digest. Usually, this will contain subscription info
++for the digest, as well as information on how to post to the list.
++.TP
++.B trailer
++If this files exists, it is copied to the end of all messages to the list.
++.TP
++.B faq
++Sent in response to the
++.I faq
++command. Usually contains frequently asked questions and answers specific
++for the mailing list.
++.TP
++.B info
++Sent in response to the
++.I info
++command. Usually contains a descripition, policy, etc, for the list. The
++first line should in itself be a very brief description of the list.
++.TP
+ .B bounce-warn
+ Pointing out that messages have bounced.
+ .TP
+@@ -198,9 +569,96 @@
+ .B ezmlm-return
+ has kept a list of bounced message numbers.
+ .TP
++.B dig-bounce-num
++Explaining that digest messages have bounced. All other text files are used
++for both the main list and the digest list.
++.TP
+ .B bounce-bottom
+ Separating the bounce message.
++.TP
++.B mod-help
++is set to list moderators issuing a
++.I \-help
++command. It contains instructions for moderators, but it is relatively
++trivial for a non-moderator to read it. Don't put secrets here.
++.TP
++.B mod-reject
++is the returned to the sender of a rejected post.
++.TP
++.B mod-timeout
++is returned if the message timed-out without moderator action.
++.TP
++.B mod-sub
++is added to the text confirming subscription and unsubscription
++instead of
++.B bottom
++and the requesting message, for actions that were approved
++by a moderator. Not copying the requesting message
++hides the moderator identity
++from the subscriber.
++.TP
++.B mod-request
++is the text sent to the moderators to request moderator action on
++a posted message.
++.TP
++.B mod-unsub-confirm
++Requesting that the moderator confirm a request to subscribe.
++If this file does not exist,
++.B sub-confirm
++will be used.
++.TP
++.B mod-unsub-confirm
++Requesting that the moderator confirm a request to unsubscribe.
++If this file does not exist,
++.B unsub-confirm
++will be used.
++.TP
++.B edit-do
++Instructions sent to the remote administrator together with a copy
++of a
++.I dir\fB/text
++file and editing instructions.
++.TP
++.B edit-list
++A list of editable files in
++.I dir\fB/text
++with a one-line description send to a remote administrator in response to a
++.I -edit
++command.
++.TP
++.B edit-done
++Sent to the remote administrator after an edited
++.I dir\fB/text
++file has been successfully saved.
++.PP
++Several tags in the text files are replaced by ezmlm programs.
++All programs replace the tag
++.B <#l#>
++with the name of the list or the list-digest, as appropriate for the request,
++and
++.B <#h#>
++with the hostname for the list.
++.B ezmlm-send
++and
++.B ezmlm-get
++replace
++.B <#n#>
++with the current message number in added headers from
++.I dir\fB/headeradd
++and text files.
++.B ezmlm-get
++does this for digest messages, where the current message is the number of
++the first message in the digest.
++.B ezmlm-manage
++replaces the tag
++.B <#A#>
++anywhere on a line with the subscription address, and
++.B <#R#>
++anywhere on a line
++with the address the subscriber must reply to. Only the first tag on any
++line is processed.
+ .PP
++For backwards compatibility,
+ .B ezmlm-manage
+ replaces the line
+ .B !A
+@@ -213,6 +671,23 @@
+ and
+ .B unsub-confirm
+ with the address that the subscriber must reply to.
++.PP
++.B ezmlm-store
++replaces the tag
++.B <#A#>
++anywhere on a line with the address for accepting the message, and
++.B <#R#>
++anywhere on a line
++with the address for rejecting the message.
++Only the first tag on any line is processed.
++.PP
++For backwards compatibility,
++.B ezmlm-store
++also replaces the line
++.B !A
++with the address for accepting the message and the line
++.B !R
++with the address for rejecting the message.
+ .SH "OUTGOING MESSAGE EDITING"
+ .I dir\fB/headerremove
+ is a list of bad header field names,
+@@ -233,10 +708,61 @@
+ is a list of new header fields.
+ .B ezmlm-send
+ adds these fields to every outgoing message.
+-.B ezmlm-make
++.B ezmlm-send
+ sets up
+ .I dir\fB/headeradd
+-with no new fields.
++to add
++.B X-No-Archive: yes
++and
++.BR Precedence: bulk .
++
++If
++.I dir\fB/mimeremove
++exists,
++.B ezmlm-send
++removed parts with the corresponding content-types from composite MIME
++messages. If the
++.B ezmlm-reject
++.I dir
++argument is specified,
++simple MIME messages of these content-types are rejected.
++
++If
++.I dir\fB/mimereject
++exists, and the
++.B ezmlm-reject
++.I dir
++argument is specified,
++simple MIME messages of these content-types, or
++composite MIME messages with any body part of these content-types are rejected.
++
++If
++.I dir\fB/sequence
++exists, the first line is added as a header to all outgoing messages, followed
++by a space and the message number. The message number is useful for archive
++retrievals, since some mail systems do not reveal the return-path to the user.
++.B NOTE:
++Sublists have their own message counter. Adding a sequence header from a
++sublists will give you the sublist message number which is different from
++the main list message number.
++
++.I dir\fB/prefix
++is a subject prefix. If this file exists, its contents are prefixed to the
++subject of the post in the outgoing message. The archived message is not
++processed. Attempts are made to not duplicate an existing prefix in replies.
++Think twice before using this option.
++A prefix takes unnecessary space on the subject line and most mail clients
++can easily filter on other headers, such as 'Mailing-List:'. If
++.I dir\fB/prefix contains a single '#', this will be replaced by the message
++number. The use of this feature is inadvisable and violates internet mail
++standards. However, it is very popular in e.g. Japan. If you must use this
++feature, make sure you are aware that you may be causing problems to users,
++sublists, etc.
++
++.I dir\fB/text/trailer
++is a message trailer. If this file exists, it's contents are copied to the 
++end of outgoing messages. Only lines terminated with new-line are copied.
++No trailer is copied to the archived version of the message.
+ .SH MISCELLANY
+ The first line of
+ .I dir\fB/mailinglist
+@@ -248,6 +774,27 @@
+ .IR dir\fB/mailinglist ,
+ in every outgoing message.
++If
++.I dir\fB/listid
++exists,
++ezmlm programs create a new
++.B List-ID
++field, showing the contents of the first line of
++.IR dir\fB/listid ,
++in every outgoing message. The list-id should be unique and within name
++space controlled by the owner. It should remain constant even if lists
++move and be of the format
++
++.EX
++List-ID: optional_text <unique_id.domain>
++.EE
++
++This header would result from a
++.I dir\fB/listid
++file containing ``optional_text <unique_id.domain>''. See
++.I http://www.within.com/~chandhok/ietf/listid.shtml
++for more info.
++
+ The first lines of
+ .I dir\fB/outlocal
+ and
+@@ -292,6 +839,44 @@
+ This affects the behavior of
+ .BR ezmlm-send .
++If
++.I dir\fB/qmqpservers
++exists,
++.B ezmlm-send
++and
++.B ezmlm-get
++will use
++.B qmail-qmqpc(1)
++to send posts and digests. Other mail will use the normal qmail mechanism.
++If
++.B qmail-qmqpc
++is modified correctly, server IP addresses listed one per line in
++.I dir\fB/qmqpsevers
++will be tried in order, rather than the default servers specified in
++.IR /var/qmail/control .
++
++If
++.I dir\fB/msgsize
++exists, it is assumed to contain ``max:min'', where ``max'' is the maximum
++size in bytes of an acceptable message body, and ``min'' the corresponding
++minimal size. Either will be ignored if zero or omitted. If the
++.B ezmlm-reject
++command line specifies the list directory, messages not meeting the size
++criteria are rejected.
++
++If
++.I dir\fB/charset
++exists, the first line is assumed to represent a valid MIME character set,
++which is used for all outgoing MIME messages sent by
++.B ezmlm-get 
++and the message moderation programs. The character set string may be suffixed
++with ':' and 'Q' or 'B' to send all outgoing
++text (ezmlm messages, digest table-of-contents, moderation requests, etc)
++encoded in ``Quoted-Printable'' or ``base64'' encoding. By default, no encoding
++is done, which may result in the transmission of characters with the high
++bit set. When encoding is specified, trigger messages and other parts of the
++reply that should not be encoded are sent as separate MIME parts.
++
+ .I dir\fB/lock
+ is an empty file.
+ Any program that reads or writes the subscriber list,
+@@ -304,14 +889,54 @@
+ .B WARNING:
+ .B Log
+ is not protected against system crashes.
+-Log entries may be missing or corrupted if the system goes down.
++Log entries may be missing or corrupted if the system goes down. There is
++Log for each of the accessory address databases as well. Thus, the log
++for digest subscribers is
++.IR dir\fB/digest/Log .
++If enabled, these logs can be retrieved by remote administrators (see
++.BR ezmlm-manage(1) ).
++
++.I dir\fB/digest
++contains items specific for the digest list.
++
++.I dir\fB/digest/subscribers
++contains hash files of digest subscriber addresses.
++
++.IR dir\fB/digest/Log ,
++.IR dir\fB/digest/bounce ,
++.IR dir\fB/digest/lockbounce ,
++and
++.I dir\fB/digest/lock
++have functions for the digest list that mirror that of the corresponding
++files in
++.IR dir .
++
++.I dir\fB/sql
++contains SQL server access information for list that are configured to
++use an SQL database for storage.
++
++.I dir\fB/tstdig
++is a timestamp used temporarily by
++.B ezmlm-tstdig(1)
++to coordinate digesting.
+ .SH "SEE ALSO"
++ezmlm-check(1),
++ezmlm-clean(1),
++ezmlm-gate(1),
++ezmlm-get(1),
++ezmlm-idx(1),
++ezmlm-issub(1),
++ezmlm-issubn(1),
+ ezmlm-list(1),
+ ezmlm-make(1),
+ ezmlm-manage(1),
++ezmlm-moderate(1),
++ezmlm-request(1),
+ ezmlm-return(1),
+ ezmlm-send(1),
++ezmlm-store(1),
+ ezmlm-sub(1),
++ezmlm-tstdig(1),
+ ezmlm-unsub(1),
+ ezmlm-warn(1),
+ dot-qmail(5)
+--- log.c      1998/02/16 04:49:24     1.1
++++ log.c      1998/11/22 22:24:22     1.3
+@@ -6,14 +6,21 @@
+ #include "fmt.h"
+ #include "open.h"
++/* appends (not crash-proof) a line to "Log". The format is: */
++/* "timestamp event address[ comment]\n". address is free of ' ' */
++/* Unprintable chars are changed to '?'. Comment may have spaces */
++
+ static substdio ss;
+ static char buf[1];
+ static char num[FMT_ULONG];
+ static stralloc line = {0};
++static stralloc fn = {0};
+-void log(event,addr)
++void log(dir,event,addr,comment)
++char *dir;
+ char *event;
+ char *addr;
++char *comment;
+ {
+   char ch;
+   int fd;
+@@ -26,9 +33,22 @@
+     if ((ch < 33) || (ch > 126)) ch = '?';
+     if (!stralloc_append(&line,&ch)) return;
+   }
++  if (comment && *comment) {
++    if (!stralloc_cats(&line," ")) return;
++    while (ch = *comment++) {
++      if (ch == '\t')
++        ch = ' ';
++      else 
++        if ((ch < 32) || (ch > 126)) ch = '?';
++      if (!stralloc_append(&line,&ch)) return;
++    }
++  }
+   if (!stralloc_cats(&line,"\n")) return;
+-  fd = open_append("Log");
++  if (!stralloc_copys(&fn,dir)) return;
++  if (!stralloc_cats(&fn,"/Log")) return;
++  if (!stralloc_0(&fn)) return;
++  fd = open_append(fn.s);
+   if (fd == -1) return;
+   substdio_fdbuf(&ss,write,fd,buf,sizeof(buf));
+   substdio_putflush(&ss,line.s,line.len);
+--- MAN        1997/07/02 23:11:57     1.1
++++ MAN        1999/10/10 03:41:20     1.26
+@@ -1,27 +1,67 @@
+ d:::755:::
+ d:::755:/man1::
+ d:::755:/man5::
++c:::644:/man1/:ezmlm-accept.1:
++c:::644:/man1/:ezmlm-archive.1:
++c:::644:/man1/:ezmlm-issubn.1:
+ c:::644:/man5/:ezmlm.5:
++c:::644:/man5/:ezmlmrc.5:
++c:::644:/man5/:ezmlmglrc.5:
++c:::644:/man5/:ezmlmsubrc.5:
+ c:::644:/man1/:ezmlm-list.1:
++c:::644:/man1/:ezmlm-glconf.1:
+ c:::644:/man1/:ezmlm-make.1:
++c:::644:/man1/:ezmlm-mktab.1:
+ c:::644:/man1/:ezmlm-manage.1:
++c:::644:/man1/:ezmlm-moderate.1:
+ c:::644:/man1/:ezmlm-reject.1:
++c:::644:/man1/:ezmlm-request.1:
+ c:::644:/man1/:ezmlm-return.1:
+ c:::644:/man1/:ezmlm-send.1:
++c:::644:/man1/:ezmlm-split.1:
++c:::644:/man1/:ezmlm-store.1:
+ c:::644:/man1/:ezmlm-sub.1:
+ c:::644:/man1/:ezmlm-unsub.1:
+ c:::644:/man1/:ezmlm-warn.1:
+ c:::644:/man1/:ezmlm-weed.1:
++c:::644:/man1/:ezmlm-idx.1:
++c:::644:/man1/:ezmlm-gate.1:
++c:::644:/man1/:ezmlm-tstdig.1:
++c:::644:/man1/:ezmlm-get.1:
++c:::644:/man1/:ezmlm-check.1:
++c:::644:/man1/:ezmlm-clean.1:
++c:::644:/man1/:ezmlm-limit.1:
++c:::644:/man1/:ezmlm-cron.1:
+ d:::755:/cat1::
+ d:::755:/cat5::
+ c:::644:/cat5/:ezmlm.0:
++c:::644:/cat5/:ezmlmrc.0:
++c:::644:/cat5/:ezmlmglrc.0:
++c:::644:/cat5/:ezmlmsubrc.0:
+ c:::644:/cat1/:ezmlm-list.0:
++c:::644:/cat1/:ezmlm-glconf.0:
+ c:::644:/cat1/:ezmlm-make.0:
++c:::644:/cat1/:ezmlm-mktab.0:
+ c:::644:/cat1/:ezmlm-manage.0:
++c:::644:/cat1/:ezmlm-moderate.0:
++c:::644:/cat1/:ezmlm-request.0:
+ c:::644:/cat1/:ezmlm-reject.0:
+ c:::644:/cat1/:ezmlm-return.0:
+ c:::644:/cat1/:ezmlm-send.0:
++c:::644:/cat1/:ezmlm-store.0:
++c:::644:/cat1/:ezmlm-split.0:
+ c:::644:/cat1/:ezmlm-sub.0:
+ c:::644:/cat1/:ezmlm-unsub.0:
+ c:::644:/cat1/:ezmlm-warn.0:
+ c:::644:/cat1/:ezmlm-weed.0:
++c:::644:/cat1/:ezmlm-idx.0:
++c:::644:/cat1/:ezmlm-gate.0:
++c:::644:/cat1/:ezmlm-tstdig.0:
++c:::644:/cat1/:ezmlm-get.0:
++c:::644:/cat1/:ezmlm-check.0:
++c:::644:/cat1/:ezmlm-clean.0:
++c:::644:/cat1/:ezmlm-limit.0:
++c:::644:/cat1/:ezmlm-cron.0:
++c:::644:/cat1/:ezmlm-accept.0:
++c:::644:/cat1/:ezmlm-archive.0:
++c:::644:/cat1/:ezmlm-issubn.0:
+--- BIN        1997/07/02 23:10:49     1.1
++++ BIN        1999/10/10 03:41:20     1.23
+@@ -1,11 +1,31 @@
+ d:::755:::
++c:::755:/:ezmlm-accept:
++c:::755:/:ezmlm-archive:
++c:::755:/:ezmlm-issubn:
++c:::755:/:ezmlm-glconf:
+ c:::755:/:ezmlm-make:
++c:::755:/:ezmlm-mktab:
+ c:::755:/:ezmlm-manage:
+ c:::755:/:ezmlm-send:
++c:::755:/:ezmlm-request:
+ c:::755:/:ezmlm-reject:
+ c:::755:/:ezmlm-return:
+ c:::755:/:ezmlm-warn:
+ c:::755:/:ezmlm-weed:
+ c:::755:/:ezmlm-list:
++c:::755:/:ezmlm-clean:
++c:::755:/:ezmlm-cron:
++c:::755:/:ezmlm-limit:
++c:::755:/:ezmlm-store:
++c:::755:/:ezmlm-split:
++c:::755:/:ezmlm-moderate:
+ c:::755:/:ezmlm-sub:
+ c:::755:/:ezmlm-unsub:
++c:::644:/:ezmlmrc:
++c:::644:/:ezmlmglrc:
++c:::644:/:ezmlmsubrc:
++c:::755:/:ezmlm-idx:
++c:::755:/:ezmlm-check:
++c:::755:/:ezmlm-gate:
++c:::755:/:ezmlm-tstdig:
++c:::755:/:ezmlm-get:
+--- VERSION    1997/10/12 19:29:59     1.1
++++ VERSION    1997/11/08 04:19:44     1.3
+@@ -1 +1,2 @@
+ ezmlm 0.53
++$Name: ezmlm-idx-040 $
+--- Makefile   1997/07/02 22:17:50     1.1
++++ Makefile   1999/12/23 02:42:12     1.117
+@@ -1,7 +1,14 @@
++#$Id: Makefile,v 1.117 1999/12/23 02:42:12 lindberg Exp $
++#$Name: ezmlm-idx-040 $
+ SHELL=/bin/sh
+-
++SQLCC=`head -1 conf-sqlcc`
++SQLLD=`head -1 conf-sqlld`
+ default: it
++clean: \
++TARGETS
++      rm -f `cat TARGETS`
++
+ alloc.0: \
+ alloc.3
+       nroff -man alloc.3 > alloc.0
+@@ -18,6 +25,10 @@
+ compile alloc_re.c alloc.h alloc_re.c byte.h alloc_re.c
+       ./compile alloc_re.c
++author.o: \
++compile author.c mime.h
++      ./compile author.c
++
+ auto-ccld.sh: \
+ conf-cc conf-ld warn-auto.sh
+       ( cat warn-auto.sh; \
+@@ -34,6 +45,14 @@
+ exit.h auto-str.c
+       ./compile auto-str.c
++auto_cron.c: \
++auto-str conf-cron
++      ./auto-str auto_cron `head -1 conf-cron` > auto_cron.c
++
++auto_cron.o: \
++compile auto_cron.c
++      ./compile auto_cron.c
++
+ auto_bin.c: \
+ auto-str conf-bin
+       ./auto-str auto_bin `head -1 conf-bin` > auto_bin.c
+@@ -79,13 +98,18 @@
+       nroff -man case.3 > case.0
+ case.a: \
+-makelib case_diffb.o case_lowerb.o case_startb.o
+-      ./makelib case.a case_diffb.o case_lowerb.o case_startb.o
++makelib case_diffb.o case_diffs.o case_starts.o case_lowerb.o case_startb.o
++      ./makelib case.a case_diffb.o case_lowerb.o case_startb.o \
++      case_diffs.o case_starts.o
+ case_diffb.o: \
+ compile case_diffb.c case.h case_diffb.c
+       ./compile case_diffb.c
++case_diffs.o: \
++compile case_diffs.c case.h
++      ./compile case_diffs.c
++
+ case_lowerb.o: \
+ compile case_lowerb.c case.h case_lowerb.c
+       ./compile case_lowerb.c
+@@ -94,6 +118,15 @@
+ compile case_startb.c case.h case_startb.c
+       ./compile case_startb.c
++checktag.o: \
++compile checktag.c stralloc.h scan.h fmt.h strerr.h cookie.h \
++      errtxt.h subscribe.h conf-sqlcc
++      ./compile checktag.c ${SQLCC}
++
++case_starts.o: \
++compile case_starts.c case.h
++      ./compile case_starts.c
++
+ compile: \
+ make-compile warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \
+@@ -110,6 +143,19 @@
+ surfpcs.h uint32.h surfpcs.h cookie.c
+       ./compile cookie.c
++copy.o: \
++compile copy.c copy.h stralloc.h substdio.h str.h readwrite.h open.h qmail.h \
++strerr.h getln.h case.h errtxt.h mime.h error.h quote.h
++      ./compile copy.c
++
++date2yyyymm.o:\
++compile date2yyyymm.c yyyymm.h
++      ./compile date2yyyymm.c
++
++dateline.o:\
++compile dateline.c yyyymm.h stralloc.h fmt.h
++      ./compile dateline.c
++
+ date822fmt.o: \
+ compile date822fmt.c datetime.h date822fmt.c fmt.h date822fmt.c \
+ date822fmt.h date822fmt.c
+@@ -133,13 +179,46 @@
+       && cat direntry.h2 || cat direntry.h1 ) > direntry.h
+       rm -f trydrent.o
++concatHDR.o: \
++compile concatHDR.c mime.h stralloc.h strerr.h byte.h errtxt.h
++      ./compile concatHDR.c
++
++decodeB.o: \
++compile decodeB.c mime.h uint32.h stralloc.h strerr.h errtxt.h
++      ./compile decodeB.c
++
++decodeHDR.o: \
++compile decodeHDR.c mime.h stralloc.h strerr.h error.h case.h byte.h \
++uint32.h errtxt.h
++      ./compile decodeHDR.c
++
++decodeQ.o: \
++compile decodeQ.c mime.h stralloc.h strerr.h errtxt.h
++      ./compile decodeQ.c
++
++encodeB.o: \
++compile encodeB.c mime.h uint32.h stralloc.h strerr.h errtxt.h
++      ./compile encodeB.c
++
++encodeQ.o: \
++compile encodeQ.c mime.h stralloc.h strerr.h errtxt.h
++      ./compile encodeQ.c
++
++unfoldHDR.o: \
++compile unfoldHDR.c mime.h stralloc.h strerr.h errtxt.h
++      ./compile unfoldHDR.c
++
+ env.0: \
+ env.3
+       nroff -man env.3 > env.0
+ env.a: \
+-makelib envread.o
+-      ./makelib env.a envread.o
++makelib env.o envread.o
++      ./makelib env.a env.o envread.o
++
++env.o: \
++compile env.c env.h str.h
++      ./compile env.c
+ envread.o: \
+ compile envread.c env.h envread.c str.h envread.c
+@@ -169,28 +248,281 @@
+ error_temp.3
+       nroff -man error_temp.3 > error_temp.0
++ezmlm-accept: \
++ezmlm-accept.sh warn-auto.sh conf-bin
++      (cat warn-auto.sh; \
++      echo EZPATH=\'`head -1 conf-bin`\'; \
++      cat ezmlm-accept.sh ) > ezmlm-accept
++
++ezmlm-accept.0: \
++ezmlm-accept.1
++      nroff -man ezmlm-accept.1 > ezmlm-accept.0
++
++ezmlm-archive: \
++load ezmlm-archive.o getconf.o slurpclose.o slurp.o getln.a sig.a \
++strerr.a substdio.a stralloc.a alloc.a error.a str.a fs.a open.a \
++lock.a fd.a getopt.a idxthread.o yyyymm.a
++      ./load ezmlm-archive getconf.o slurpclose.o slurp.o getln.a sig.a \
++      idxthread.o yyyymm.a strerr.a substdio.a stralloc.a alloc.a \
++      error.a str.a fs.a open.a lock.a fd.a getopt.a
++
++ezmlm-archive.0: \
++ezmlm-archive.1
++      nroff -man ezmlm-archive.1 > ezmlm-archive.0
++
++ezmlm-archive.o: \
++compile ezmlm-archive.c alloc.h error.h stralloc.h gen_alloc.h str.h \
++sig.h slurp.h getconf.h strerr.h getln.h substdio.h readwrite.h \
++makehash.h fmt.h strerr.h errtxt.h idx.h idxthread.h sgetopt.h subgetopt.h
++      ./compile ezmlm-archive.c
++
++ezmlm-cgi: \
++load ezmlm-cgi.o getconf.o slurpclose.o slurp.o constmap.o getln.a sig.a \
++mime.a strerr.a substdio.a stralloc.a alloc.a error.a str.a fs.a open.a \
++lock.a fd.a getopt.a env.a case.a datetime.o now.o mime.a wait.a yyyymm.a
++      ./load ezmlm-cgi getconf.o slurpclose.o slurp.o constmap.o getln.a \
++      mime.a sig.a env.a case.a datetime.o now.o mime.a wait.a yyyymm.a \
++      strerr.a substdio.a stralloc.a alloc.a error.a str.a fs.a open.a \
++      lock.a fd.a getopt.a
++
++ezmlm-cgi.0: \
++ezmlm-cgi.1
++      nroff -man ezmlm-cgi.1 > ezmlm-cgi.0
++
++ezmlm-cgi.o: \
++compile ezmlm-cgi.c alloc.h error.h stralloc.h gen_alloc.h str.h \
++sig.h slurp.h getconf.h strerr.h getln.h substdio.h readwrite.h env.h \
++makehash.h fmt.h strerr.h errtxt.h idx.h idxthread.h mime.h \
++constmap.h sgetopt.h subgetopt.h datetime.h now.h fork.h wait.h
++      ./compile ezmlm-cgi.c
++
++ezmlm-check: \
++ezmlm-check.sh warn-auto.sh conf-bin
++      (cat warn-auto.sh; \
++      echo EZPATH=\'`head -1 conf-bin`\'; \
++      echo QMPATH=\'`head -1 conf-qmail`\'; \
++      cat ezmlm-check.sh ) > ezmlm-check
++
++ezmlm-check.0: \
++ezmlm-check.1
++      nroff -man ezmlm-check.1 > ezmlm-check.0
++
++ezmlm-clean: \
++load ezmlm-clean.o auto_qmail.o getconf.o copy.o mime.a \
++now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
++getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a surf.a \
++error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a cookie.o getopt.a
++      ./load ezmlm-clean auto_qmail.o getconf.o copy.o mime.a \
++      now.o datetime.o date822fmt.o slurpclose.o \
++      slurp.o qmail.o quote.o getln.a env.a sig.a strerr.a \
++      substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
++      open.a seek.a wait.a lock.a fd.a cookie.o getopt.a surf.a
++
++ezmlm-clean.0: \
++ezmlm-clean.1
++      nroff -man ezmlm-clean.1 > ezmlm-clean.0
++
++ezmlm-clean.o: \
++compile ezmlm-clean.c error.h stralloc.h gen_alloc.h str.h \
++env.h sig.h slurp.h getconf.h strerr.h byte.h getln.h case.h copy.h mime.h \
++qmail.h substdio.h readwrite.h seek.h quote.h datetime.h now.h cookie.h \
++date822fmt.h direntry.h fmt.h strerr.h errtxt.h idx.h sgetopt.h subgetopt.h
++      ./compile ezmlm-clean.c
++
++ezmlm-cron: \
++load ezmlm-cron.o strerr.a stralloc.a alloc.a error.a open.a auto_qmail.o \
++getopt.a getln.a str.a substdio.a sig.a fs.a open.a fd.a lock.a wait.a \
++case.a auto_cron.o
++      ./load ezmlm-cron getopt.a getln.a strerr.a substdio.a \
++      stralloc.a alloc.a sig.a fs.a open.a fd.a lock.a error.a \
++      wait.a case.a str.a auto_qmail.o auto_cron.o
++
++ezmlm-cron.0: \
++ezmlm-cron.1
++      nroff -man ezmlm-cron.1 > ezmlm-cron.0
++
++ezmlm-cron.o: \
++compile ezmlm-cron.c strerr.h substdio.h stralloc.h error.h str.h \
++fork.h readwrite.h wait.h errtxt.h idx.h sgetopt.h auto_qmail.h \
++fmt.h auto_cron.h
++      ./compile ezmlm-cron.c
++
++ezmlm-gate: \
++load ezmlm-gate.o subdb.a auto_bin.o getopt.a getln.a env.a sig.a strerr.a \
++stralloc.a alloc.a error.a str.a case.a wait.a substdio.a open.a lock.a \
++fs.a getconf.o slurpclose.o slurp.o seek.a conf-sqlld
++      ./load ezmlm-gate subdb.a getconf.o slurpclose.o slurp.o \
++      getopt.a getln.a auto_bin.o env.a sig.a fs.a \
++      strerr.a substdio.a stralloc.a alloc.a error.a str.a case.a wait.a \
++      open.a lock.a seek.a ${SQLLD}
++
++ezmlm-gate.0: \
++ezmlm-gate.1
++      nroff -man ezmlm-gate.1 > ezmlm-gate.0
++
++ezmlm-gate.o: \
++compile ezmlm-gate.c idx.h errtxt.h subscribe.h auto_bin.h \
++sgetopt.h subgetopt.h substdio.h getconf.h \
++env.h sig.h strerr.h stralloc.h alloc.h error.h str.h case.h \
++fork.h wait.h exit.h getln.h open.h
++      ./compile ezmlm-gate.c
++
++ezmlm-get: \
++load ezmlm-get.o idxthread.o subdb.a auto_qmail.o getopt.a now.o getconf.o \
++datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o makehash.o \
++cookie.o surf.a yyyymm.a \
++constmap.o getln.a env.a sig.a strerr.a substdio.a mime.a stralloc.a alloc.a \
++error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a copy.o conf-sqlld
++      ./load ezmlm-get idxthread.o subdb.a auto_qmail.o getopt.a getconf.o \
++      now.o datetime.o date822fmt.o cookie.o makehash.o slurpclose.o slurp.o \
++      yyyymm.a \
++      constmap.o substdio.a copy.o mime.a strerr.a stralloc.a alloc.a \
++      qmail.o quote.o surf.a getln.a env.a sig.a \
++      error.a str.a fs.a case.a \
++      open.a seek.a wait.a lock.a fd.a ${SQLLD}
++
++ezmlm-get.o: \
++compile ezmlm-get.c idx.h errtxt.h error.h getconf.h stralloc.h gen_alloc.h \
++str.h cookie.h env.h sig.h slurp.h strerr.h byte.h getln.h case.h qmail.h \
++substdio.h readwrite.h seek.h quote.h sgetopt.h subgetopt.h datetime.h now.h \
++date822fmt.h fmt.h strerr.h copy.h errtxt.h idx.h idxthread.h mime.h \
++constmap.h makehash.h
++      ./compile ezmlm-get.c
++
++ezmlm-get.0: \
++ezmlm-get.1
++      nroff -man ezmlm-get.1 > ezmlm-get.0
++
++ezmlm-greturn: \
++load ezmlm-greturn.o quote.o getconf.o subdb.a log.o \
++slurpclose.o slurp.o now.o cookie.o surf.a lock.a env.a sig.a \
++strerr.a getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a \
++case.a open.a conf-sqlld
++      ./load ezmlm-greturn quote.o getconf.o subdb.a \
++      log.o slurpclose.o slurp.o now.o cookie.o surf.a lock.a \
++      env.a sig.a strerr.a getln.a substdio.a stralloc.a alloc.a \
++      error.a str.a fs.a case.a open.a ${SQLLD}
++
++ezmlm-greturn.0: \
++ezmlm-greturn.1
++      nroff -man ezmlm-greturn.1 > ezmlm-greturn.0
++
++ezmlm-greturn.o: \
++compile ezmlm-greturn.c stralloc.h gen_alloc.h stralloc.h str.h env.h sig.h \
++slurp.h getconf.h strerr.h byte.h case.h getln.h substdio.h error.h \
++quote.h readwrite.h fmt.h datetime.h now.h cookie.h \
++strerr.h subscribe.h
++      ./compile ezmlm-greturn.c
++
++ezmlm-gwarn: \
++load ezmlm-gwarn.o auto_qmail.o getconf.o mime.a cookie.o subdb.a now.o \
++slurpclose.o slurp.o quote.o datetime.o date822fmt.o qmail.o surf.a \
++case.a strerr.a sig.a getln.a substdio.a stralloc.a alloc.a error.a \
++open.a lock.a str.a fs.a fd.a wait.a copy.o getopt.a conf-sqlld
++      ./load ezmlm-gwarn auto_qmail.o getconf.o mime.a \
++      cookie.o subdb.a getopt.a \
++      now.o slurpclose.o slurp.o quote.o datetime.o date822fmt.o \
++      qmail.o surf.a case.a strerr.a sig.a getln.a substdio.a \
++      stralloc.a alloc.a error.a open.a lock.a str.a fs.a fd.a \
++      wait.a copy.o ${SQLLD}
++
++ezmlm-gwarn.0: \
++ezmlm-gwarn.1
++      nroff -man ezmlm-gwarn.1 > ezmlm-gwarn.0
++
++ezmlm-gwarn.o: \
++compile ezmlm-gwarn.c direntry.h readwrite.h getln.h \
++substdio.h stralloc.h gen_alloc.h slurp.h getconf.h byte.h error.h str.h \
++sig.h now.h datetime.h  date822fmt.h fmt.h cookie.h qmail.h substdio.h \
++qmail.h copy.h mime.h idx.h errtxt.h sgetopt.h subgetopt.h
++      ./compile ezmlm-gwarn.c
++
++ezmlm-idx: \
++load ezmlm-idx.o slurp.o slurpclose.o mime.a wait.a getopt.a \
++getln.a strerr.a sig.h sig.a open.a lock.a substdio.a stralloc.a \
++alloc.a error.a str.a fd.a case.a fs.a getconf.o makehash.o surf.o mime.a
++      ./load ezmlm-idx \
++      mime.a slurp.o slurpclose.o wait.a getln.a strerr.a sig.a open.a \
++      lock.a mime.a substdio.a stralloc.a alloc.a error.a str.a fd.a \
++      getopt.a case.a fs.a getconf.o makehash.o surf.o
++
++ezmlm-idx.o: \
++compile ezmlm-idx.c stralloc.h getconf.h \
++substdio.h subfd.h strerr.h error.h sgetopt.h \
++lock.h sig.h slurp.h open.h getln.h case.h \
++str.h fmt.h readwrite.h exit.h idx.h mime.h errtxt.h uint32.h
++      ./compile ezmlm-idx.c
++
++ezmlm-idx.0: \
++ezmlm-idx.1
++      nroff -man ezmlm-idx.1 > ezmlm-idx.0
++
++ezmlm-glconf: \
++ezmlm-glconf.sh warn-auto.sh conf-bin
++      (cat warn-auto.sh; \
++      echo EZPATH=\'`head -1 conf-bin`\'; \
++      cat ezmlm-glconf.sh ) > ezmlm-glconf
++
++ezmlm-glconf.0: \
++ezmlm-glconf.1
++      nroff -man ezmlm-glconf.1 > ezmlm-glconf.0
++
++ezmlm-issubn: \
++load ezmlm-issubn.o subdb.a getconf.o slurpclose.o slurp.o \
++env.a fs.a strerr.a getln.a getopt.a conf-sqlld \
++substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
++      ./load ezmlm-issubn subdb.a getconf.o slurpclose.o slurp.o \
++      getopt.a env.a fs.a strerr.a \
++      getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
++      open.a lock.a ${SQLLD}
++
++ezmlm-issubn.0: \
++ezmlm-issubn.1
++      nroff -man ezmlm-issubn.1 > ezmlm-issubn.0
++
++ezmlm-issubn.o: \
++compile ezmlm-issubn.c strerr.h subscribe.h env.h errtxt.h sgetopt.h idx.h
++      ./compile ezmlm-issubn.c
++
++ezmlm-limit: \
++load ezmlm-limit.o getconf.o slurpclose.o slurp.o substdio.a stralloc.a \
++alloc.a error.a str.a case.a open.a lock.a getopt.a fs.a sig.a now.o
++      ./load ezmlm-limit getconf.o slurpclose.o slurp.o getopt.a \
++      strerr.a substdio.a stralloc.a alloc.a error.a str.a case.a \
++      open.a lock.a fs.a sig.a now.o
++
++ezmlm-limit.0: \
++ezmlm-limit.1
++      nroff -man ezmlm-limit.1 > ezmlm-limit.0
++
++ezmlm-limit.o: \
++compile ezmlm-limit.c stralloc.h strerr.h substdio.h readwrite.h sig.h lock.h \
++getconf.h fmt.h now.h sgetopt.h error.h errtxt.h idx.h datetime.h
++      ./compile ezmlm-limit.c
++
+ ezmlm-list: \
+-load ezmlm-list.o strerr.a getln.a substdio.a stralloc.a alloc.a \
+-error.a open.a str.a
+-      ./load ezmlm-list strerr.a getln.a substdio.a stralloc.a \
+-      alloc.a error.a open.a str.a 
++load ezmlm-list.o subdb.a fs.a getconf.o slurpclose.o slurp.o \
++strerr.a getln.a substdio.a stralloc.a alloc.a \
++error.a open.a str.a case.a getopt.a conf-sqlld
++      ./load ezmlm-list subdb.a fs.a getconf.o slurpclose.o slurp.o \
++      strerr.a getln.a getopt.a substdio.a stralloc.a \
++      alloc.a error.a open.a str.a case.a ${SQLLD}
+ ezmlm-list.0: \
+ ezmlm-list.1
+       nroff -man ezmlm-list.1 > ezmlm-list.0
+ ezmlm-list.o: \
+-compile ezmlm-list.c stralloc.h gen_alloc.h stralloc.h ezmlm-list.c \
+-substdio.h ezmlm-list.c getln.h ezmlm-list.c strerr.h ezmlm-list.c \
+-error.h ezmlm-list.c readwrite.h ezmlm-list.c exit.h ezmlm-list.c \
+-open.h ezmlm-list.c
++compile ezmlm-list.c stralloc.h gen_alloc.h substdio.h getln.h strerr.h \
++error.h readwrite.h exit.h open.h errtxt.h subscribe.h exit.h sgetopt.h \
++idx.h fmt.h
+       ./compile ezmlm-list.c
+ ezmlm-make: \
+-load ezmlm-make.o auto_bin.o open.a getopt.a substdio.a strerr.a \
+-stralloc.a alloc.a error.a str.a
+-      ./load ezmlm-make auto_bin.o open.a getopt.a substdio.a \
+-      strerr.a stralloc.a alloc.a error.a str.a 
++load ezmlm-make.o auto_bin.o open.a getln.a getopt.a substdio.a strerr.a \
++stralloc.a alloc.a error.a lock.a str.a
++      ./load ezmlm-make auto_bin.o open.a getln.a getopt.a substdio.a \
++      strerr.a stralloc.a alloc.a error.a lock.a str.a
+ ezmlm-make.0: \
+ ezmlm-make.1
+@@ -202,19 +534,20 @@
+ strerr.h ezmlm-make.c exit.h ezmlm-make.c readwrite.h ezmlm-make.c \
+ open.h ezmlm-make.c substdio.h ezmlm-make.c str.h ezmlm-make.c \
+ auto_bin.h ezmlm-make.c ezmlm-make.c ezmlm-make.c ezmlm-make.c \
+-ezmlm-make.c
++errtxt.h idx.h getln.h lock.h
+       ./compile ezmlm-make.c
+ ezmlm-manage: \
+-load ezmlm-manage.o auto_qmail.o getconf.o subscribe.o log.o cookie.o \
++load ezmlm-manage.o auto_qmail.o getconf.o subdb.a log.o cookie.o \
+ now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
+ surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
+-error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a
+-      ./load ezmlm-manage auto_qmail.o getconf.o subscribe.o \
+-      log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
++error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a getopt.a \
++mime.a copy.o conf-sqlld
++      ./load ezmlm-manage subdb.a auto_qmail.o getconf.o copy.o \
++      mime.a log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
+       slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
+       substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
+-      open.a seek.a wait.a lock.a fd.a 
++      open.a seek.a wait.a lock.a fd.a getopt.a ${SQLLD}
+ ezmlm-manage.0: \
+ ezmlm-manage.1
+@@ -231,35 +564,84 @@
+ quote.h ezmlm-manage.c datetime.h ezmlm-manage.c now.h datetime.h \
+ datetime.h now.h ezmlm-manage.c date822fmt.h ezmlm-manage.c fmt.h \
+ ezmlm-manage.c subscribe.h strerr.h strerr.h subscribe.h \
+-ezmlm-manage.c cookie.h ezmlm-manage.c
++sgetopt.h subgetopt.h cookie.h idx.h errtxt.h copy.h
+       ./compile ezmlm-manage.c
++ezmlm-mktab.0: \
++ezmlm-mktab.1
++      nroff -man ezmlm-mktab.1 > ezmlm-mktab.0
++
++ezmlm-moderate: \
++load ezmlm-moderate.o auto_qmail.o getconf.o auto_bin.o copy.o mime.a \
++cookie.o now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
++surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
++error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a getopt.a
++      ./load ezmlm-moderate auto_qmail.o getconf.o copy.o mime.a \
++      cookie.o now.o datetime.o date822fmt.o slurpclose.o \
++      slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
++      substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
++      auto_bin.o open.a seek.a wait.a lock.a fd.a getopt.a
++
++ezmlm-moderate.0: \
++ezmlm-moderate.1
++      nroff -man ezmlm-moderate.1 > ezmlm-moderate.0
++
++ezmlm-moderate.o: \
++compile ezmlm-moderate.c error.h stralloc.h gen_alloc.h str.h \
++env.h sig.h slurp.h getconf.h strerr.h byte.h getln.h case.h \
++qmail.h substdio.h readwrite.h seek.h quote.h datetime.h now.h \
++date822fmt.h fmt.h strerr.h cookie.h errtxt.h idx.h copy.h mime.h \
++subgetopt.h sgetopt.h auto_bin.h fork.h wait.h
++      ./compile ezmlm-moderate.c
++
++ezmlm-request: \
++load ezmlm-request.o subdb.a getconf.o constmap.o getln.a auto_qmail.o qmail.o \
++strerr.a slurpclose.o slurp.o getopt.a env.a open.a fd.a sig.a case.a \
++substdio.a error.a stralloc.a alloc.a str.a case.a fs.a wait.a seek.a \
++date822fmt.o now.o datetime.o quote.o copy.o mime.a conf-sqlld
++      ./load ezmlm-request subdb.a getconf.o constmap.o getln.a auto_qmail.o \
++      qmail.o date822fmt.o datetime.o now.o quote.o \
++      slurpclose.o slurp.o env.a open.a sig.a wait.a getopt.a \
++      strerr.a substdio.a error.a copy.o stralloc.a alloc.a substdio.a \
++      str.a case.a fs.a fd.a sig.a wait.a seek.a mime.a ${SQLLD}
++
++ezmlm-request.0:
++      nroff -man ezmlm-request.1 > ezmlm-request.0
++
++ezmlm-request.o: \
++compile ezmlm-request.c stralloc.h subfd.h strerr.h error.h qmail.h env.h \
++sig.h open.h getln.h case.h str.h readwrite.h exit.h substdio.h quote.h \
++getconf.h constmap.h fmt.h byte.h errtxt.h idx.h datetime.h date822fmt.h \
++subscribe.h now.h copy.h
++      ./compile ezmlm-request.c
++
+ ezmlm-reject: \
+-load ezmlm-reject.o getln.a strerr.a substdio.a error.a stralloc.a \
+-alloc.a str.a getopt.a case.a
+-      ./load ezmlm-reject getln.a strerr.a substdio.a error.a \
+-      stralloc.a alloc.a str.a getopt.a case.a 
++load ezmlm-reject.o getln.a strerr.a substdio.a error.a stralloc.a open.a \
++qmail.o env.a seek.a fd.a wait.a auto_qmail.o \
++alloc.a getconf.o slurp.o slurpclose.o str.a getopt.a case.a constmap.o fs.a
++      ./load ezmlm-reject qmail.o getln.a strerr.a substdio.a error.a fs.a \
++      env.a constmap.o getconf.o slurp.o slurpclose.o stralloc.a alloc.a \
++      seek.a str.a getopt.a case.a open.a fd.a wait.a auto_qmail.o
+ ezmlm-reject.0: \
+ ezmlm-reject.1
+       nroff -man ezmlm-reject.1 > ezmlm-reject.0
+ ezmlm-reject.o: \
+-compile ezmlm-reject.c strerr.h ezmlm-reject.c substdio.h \
+-ezmlm-reject.c readwrite.h ezmlm-reject.c stralloc.h gen_alloc.h \
+-stralloc.h ezmlm-reject.c getln.h ezmlm-reject.c sgetopt.h \
+-subgetopt.h sgetopt.h ezmlm-reject.c
++compile ezmlm-reject.c strerr.h substdio.h readwrite.h stralloc.h gen_alloc.h \
++stralloc.h getln.h sgetopt.h subgetopt.h constmap.h getconf.h errtxt.h \
++scan.h fmt.h idx.h qmail.h env.h seek.h
+       ./compile ezmlm-reject.c
+ ezmlm-return: \
+-load ezmlm-return.o quote.o getconf.o issub.o subscribe.o log.o \
++load ezmlm-return.o quote.o getconf.o subdb.a log.o \
+ slurpclose.o slurp.o now.o cookie.o surf.a lock.a env.a sig.a \
+ strerr.a getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a \
+-case.a open.a
+-      ./load ezmlm-return quote.o getconf.o issub.o subscribe.o \
++case.a open.a conf-sqlld
++      ./load ezmlm-return quote.o getconf.o subdb.a \
+       log.o slurpclose.o slurp.o now.o cookie.o surf.a lock.a \
+       env.a sig.a strerr.a getln.a substdio.a stralloc.a alloc.a \
+-      error.a str.a fs.a case.a open.a 
++      error.a str.a fs.a case.a open.a ${SQLLD}
+ ezmlm-return.0: \
+ ezmlm-return.1
+@@ -270,43 +652,134 @@
+ ezmlm-return.c str.h ezmlm-return.c env.h ezmlm-return.c sig.h \
+ ezmlm-return.c slurp.h ezmlm-return.c getconf.h ezmlm-return.c \
+ strerr.h ezmlm-return.c byte.h ezmlm-return.c case.h ezmlm-return.c \
+-getln.h ezmlm-return.c substdio.h ezmlm-return.c error.h \
++getln.h ezmlm-return.c substdio.h ezmlm-return.c error.h direntry.h \
+ ezmlm-return.c quote.h ezmlm-return.c readwrite.h ezmlm-return.c \
+ fmt.h ezmlm-return.c now.h datetime.h now.h ezmlm-return.c cookie.h \
+ ezmlm-return.c subscribe.h strerr.h strerr.h subscribe.h \
+-ezmlm-return.c issub.h strerr.h strerr.h issub.h ezmlm-return.c
++strerr.h strerr.h
+       ./compile ezmlm-return.c
+ ezmlm-send: \
+ load ezmlm-send.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
+-slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a \
+-substdio.a stralloc.a alloc.a error.a str.a fd.a case.a fs.a
+-      ./load ezmlm-send auto_qmail.o getconf.o qmail.o \
+-      constmap.o slurp.o slurpclose.o wait.a getln.a strerr.a \
+-      sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a \
+-      error.a str.a fd.a case.a fs.a 
++slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a conf-sqlld \
++substdio.a cookie.o stralloc.a alloc.a error.a str.a fd.a case.a fs.a surf.a \
++getopt.a copy.o mime.a subdb.a makehash.o surf.o makehash.o str.a quote.o
++      ./load ezmlm-send subdb.a cookie.o surf.a auto_qmail.o getconf.o \
++      getopt.a qmail.o quote.o constmap.o slurp.o slurpclose.o \
++      wait.a getln.a strerr.a \
++      sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a error.a \
++      fd.a case.a fs.a getopt.a copy.o mime.a makehash.o str.a ${SQLLD}
+ ezmlm-send.0: \
+ ezmlm-send.1
+       nroff -man ezmlm-send.1 > ezmlm-send.0
+ ezmlm-send.o: \
+-compile ezmlm-send.c stralloc.h gen_alloc.h stralloc.h ezmlm-send.c \
+-subfd.h substdio.h subfd.h ezmlm-send.c strerr.h ezmlm-send.c error.h \
+-ezmlm-send.c qmail.h substdio.h substdio.h qmail.h ezmlm-send.c env.h \
+-ezmlm-send.c lock.h ezmlm-send.c sig.h ezmlm-send.c open.h \
+-ezmlm-send.c getln.h ezmlm-send.c case.h ezmlm-send.c scan.h \
+-ezmlm-send.c str.h ezmlm-send.c fmt.h ezmlm-send.c readwrite.h \
+-ezmlm-send.c exit.h ezmlm-send.c substdio.h substdio.h ezmlm-send.c \
+-getconf.h ezmlm-send.c constmap.h ezmlm-send.c
++compile ezmlm-send.c stralloc.h gen_alloc.h copy.h \
++subfd.h substdio.h strerr.h error.h qmail.h env.h makehash.h sgetopt.h \
++lock.h sig.h open.h getln.h case.h scan.h str.h fmt.h readwrite.h quote.h \
++exit.h getconf.h constmap.h byte.h errtxt.h idx.h mime.h subscribe.h \
++uint32.h
+       ./compile ezmlm-send.c
++ezmlm-master: \
++load ezmlm-master.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
++slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a conf-sqlld \
++substdio.a cookie.o stralloc.a alloc.a error.a str.a fd.a case.a fs.a surf.a\
++getopt.a copy.o mime.a subdb.a makehash.o surf.o makehash.o str.a quote.o
++      ./load ezmlm-master subdb.a cookie.o surf.a auto_qmail.o getconf.o \
++      getopt.a qmail.o quote.o constmap.o slurp.o slurpclose.o \
++      wait.a getln.a strerr.a \
++      sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a error.a \
++      fd.a case.a fs.a getopt.a copy.o mime.a makehash.o str.a ${SQLLD}
++
++ezmlm-master.0: \
++ezmlm-master.1
++      nroff -man ezmlm-master.1 > ezmlm-master.0
++
++ezmlm-master.o: \
++compile ezmlm-master.c stralloc.h gen_alloc.h copy.h \
++subfd.h substdio.h strerr.h error.h qmail.h env.h makehash.h sgetopt.h \
++lock.h sig.h open.h getln.h case.h scan.h str.h fmt.h readwrite.h quote.h \
++exit.h getconf.h constmap.h byte.h errtxt.h idx.h mime.h subscribe.h \
++uint32.h
++      ./compile ezmlm-master.c
++
++ezmlm-slave: \
++load ezmlm-slave.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
++slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a conf-sqlld \
++substdio.a cookie.o stralloc.a alloc.a error.a str.a fd.a case.a fs.a surf.a \
++getopt.a copy.o mime.a subdb.a makehash.o surf.o makehash.o str.a quote.o
++      ./load ezmlm-slave subdb.a cookie.o surf.a auto_qmail.o getconf.o \
++      getopt.a qmail.o quote.o constmap.o slurp.o slurpclose.o \
++      wait.a getln.a strerr.a \
++      sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a error.a \
++      fd.a case.a fs.a getopt.a copy.o mime.a makehash.o str.a ${SQLLD}
++
++ezmlm-slave.0: \
++ezmlm-slave.1
++      nroff -man ezmlm-slave.1 > ezmlm-slave.0
++
++ezmlm-slave.o: \
++compile ezmlm-slave.c stralloc.h gen_alloc.h copy.h \
++subfd.h substdio.h strerr.h error.h qmail.h env.h makehash.h sgetopt.h \
++lock.h sig.h open.h getln.h case.h scan.h str.h fmt.h readwrite.h quote.h \
++exit.h getconf.h constmap.h byte.h errtxt.h idx.h mime.h subscribe.h \
++uint32.h
++      ./compile ezmlm-slave.c
++
++ezmlm-split: \
++load ezmlm-split.o auto_qmail.o getconf.o \
++slurpclose.o slurp.o qmail.o quote.o wait.a \
++getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
++error.a str.a fs.a case.a open.a fd.a
++      ./load ezmlm-split auto_qmail.o getconf.o slurpclose.o \
++      slurp.o qmail.o quote.o getln.a env.a sig.a strerr.a \
++      substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
++      open.a fd.a wait.a
++
++ezmlm-split.0: \
++ezmlm-split.1
++      nroff -man ezmlm-split.1 > ezmlm-split.0
++
++ezmlm-split.o: \
++compile ezmlm-split.c error.h stralloc.h gen_alloc.h str.h \
++env.h sig.h getconf.h strerr.h byte.h getln.h case.h \
++qmail.h substdio.h  readwrite.h quote.h \
++fmt.h errtxt.h idx.h uint32.h
++      ./compile ezmlm-split.c
++
++ezmlm-store: \
++load ezmlm-store.o auto_qmail.o getconf.o subdb.a log.o auto_bin.o mime.a \
++cookie.o now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
++surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a conf-sqlld \
++error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a getopt.a copy.o
++      ./load ezmlm-store auto_qmail.o getconf.o subdb.a copy.o mime.a \
++      log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
++      slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
++      substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
++      open.a seek.a wait.a lock.a fd.a getopt.a auto_bin.o ${SQLLD}
++
++ezmlm-store.0: \
++ezmlm-store.1
++      nroff -man ezmlm-store.1 > ezmlm-store.0
++
++ezmlm-store.o: \
++compile ezmlm-store.c error.h stralloc.h gen_alloc.h str.h \
++sgetopt.h subgetopt.h fork.h wait.h auto_bin.h lock.h mime.h \
++env.h sig.h slurp.h getconf.h strerr.h byte.h getln.h case.h \
++qmail.h substdio.h readwrite.h seek.h quote.h datetime.h now.h \
++date822fmt.h fmt.h subscribe.h strerr.h cookie.h errtxt.h idx.h copy.h
++      ./compile ezmlm-store.c
++
+ ezmlm-sub: \
+-load ezmlm-sub.o subscribe.o log.o now.o fs.a strerr.a getln.a \
++load ezmlm-sub.o subdb.a getconf.o slurpclose.o slurp.o \
++log.o now.o fs.a strerr.a getln.a getopt.a fs.a conf-sqlld \
+ substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
+-      ./load ezmlm-sub subscribe.o log.o now.o fs.a strerr.a \
++      ./load ezmlm-sub subdb.a getconf.o slurpclose.o slurp.o \
++      log.o now.o fs.a strerr.a getopt.a fs.a \
+       getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+-      open.a lock.a 
++      open.a lock.a ${SQLLD}
+ ezmlm-sub.0: \
+ ezmlm-sub.1
+@@ -314,57 +787,86 @@
+ ezmlm-sub.o: \
+ compile ezmlm-sub.c strerr.h ezmlm-sub.c subscribe.h strerr.h \
+-strerr.h subscribe.h ezmlm-sub.c log.h ezmlm-sub.c
++getln.h substdio.h stralloc.h readwrite.h \
++strerr.h subscribe.h log.h errtxt.h sgetopt.h scan.h idx.h
+       ./compile ezmlm-sub.c
++ezmlm-test: \
++ezmlm-test.sh warn-auto.sh conf-bin
++      (cat warn-auto.sh; \
++      echo QMPATH=\'`head -1 conf-qmail`\'; \
++      cat ezmlm-test.sh ) > ezmlm-test; \
++      chmod 755 ezmlm-test
++
++ezmlm-test.0: \
++ezmlm-test.1
++      nroff -man ezmlm-test.1 > ezmlm-test.0
++
++ezmlm-tstdig: \
++load ezmlm-tstdig.o getopt.a getconf.o now.o fs.a strerr.a getln.a \
++lock.a \
++substdio.a stralloc.a alloc.a error.a str.a case.a sig.a \
++open.a slurpclose.o slurp.o env.a
++      ./load ezmlm-tstdig getopt.a getconf.o env.a now.o fs.a strerr.a \
++      lock.a getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
++      sig.a slurpclose.o slurp.o open.a 
++
++ezmlm-tstdig.0: \
++ezmlm-tstdig.1
++      nroff -man ezmlm-tstdig.1 > ezmlm-tstdig.0
++
++ezmlm-tstdig.o: \
++compile ezmlm-tstdig.c strerr.h sgetopt.h getconf.h \
++sig.h now.h errtxt.h stralloc.h sig.h env.h fmt.h substdio.h readwrite.h \
++now.h idx.h
++      ./compile ezmlm-tstdig.c
++
+ ezmlm-unsub: \
+-load ezmlm-unsub.o subscribe.o log.o now.o fs.a strerr.a getln.a \
++load ezmlm-unsub.o subdb.a getconf.o slurpclose.o slurp.o \
++log.o now.o fs.a strerr.a getln.a getopt.a fs.a conf-sqlld \
+ substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
+-      ./load ezmlm-unsub subscribe.o log.o now.o fs.a strerr.a \
++      ./load ezmlm-unsub subdb.a getopt.a getconf.o slurpclose.o slurp.o \
++      log.o now.o fs.a strerr.a fs.a \
+       getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+-      open.a lock.a 
++      open.a lock.a ${SQLLD}
+ ezmlm-unsub.0: \
+ ezmlm-unsub.1
+       nroff -man ezmlm-unsub.1 > ezmlm-unsub.0
+ ezmlm-unsub.o: \
+-compile ezmlm-unsub.c strerr.h ezmlm-unsub.c subscribe.h strerr.h \
+-strerr.h subscribe.h ezmlm-unsub.c log.h ezmlm-unsub.c
++compile ezmlm-unsub.c strerr.h subscribe.h \
++log.h errtxt.h sgetopt.h scan.h idx.h readwrite.h stralloc.h substdio.h 
+       ./compile ezmlm-unsub.c
+ ezmlm-warn: \
+-load ezmlm-warn.o auto_qmail.o getconf.o cookie.o issub.o now.o \
++load ezmlm-warn.o auto_qmail.o getconf.o mime.a cookie.o subdb.a now.o \
+ slurpclose.o slurp.o quote.o datetime.o date822fmt.o qmail.o surf.a \
+ case.a strerr.a sig.a getln.a substdio.a stralloc.a alloc.a error.a \
+-open.a lock.a str.a fs.a fd.a wait.a
+-      ./load ezmlm-warn auto_qmail.o getconf.o cookie.o issub.o \
++open.a lock.a str.a fs.a fd.a wait.a copy.o getopt.a conf-sqlld
++      ./load ezmlm-warn auto_qmail.o getconf.o mime.a \
++      cookie.o subdb.a getopt.a \
+       now.o slurpclose.o slurp.o quote.o datetime.o date822fmt.o \
+       qmail.o surf.a case.a strerr.a sig.a getln.a substdio.a \
+       stralloc.a alloc.a error.a open.a lock.a str.a fs.a fd.a \
+-      wait.a 
++      wait.a copy.o ${SQLLD}
+ ezmlm-warn.0: \
+ ezmlm-warn.1
+       nroff -man ezmlm-warn.1 > ezmlm-warn.0
+ ezmlm-warn.o: \
+-compile ezmlm-warn.c ezmlm-warn.c ezmlm-warn.c direntry.h direntry.h \
+-direntry.h ezmlm-warn.c readwrite.h ezmlm-warn.c getln.h ezmlm-warn.c \
+-substdio.h ezmlm-warn.c stralloc.h gen_alloc.h stralloc.h \
+-ezmlm-warn.c slurp.h ezmlm-warn.c getconf.h ezmlm-warn.c byte.h \
+-ezmlm-warn.c error.h ezmlm-warn.c str.h ezmlm-warn.c strerr.h \
+-ezmlm-warn.c sig.h ezmlm-warn.c now.h datetime.h now.h ezmlm-warn.c \
+-datetime.h datetime.h ezmlm-warn.c date822fmt.h ezmlm-warn.c fmt.h \
+-ezmlm-warn.c cookie.h ezmlm-warn.c qmail.h substdio.h substdio.h \
+-qmail.h ezmlm-warn.c
++compile ezmlm-warn.c direntry.h readwrite.h getln.h \
++substdio.h stralloc.h gen_alloc.h slurp.h getconf.h byte.h error.h str.h \
++sig.h now.h datetime.h  date822fmt.h fmt.h cookie.h qmail.h substdio.h \
++qmail.h copy.h mime.h idx.h errtxt.h sgetopt.h subgetopt.h
+       ./compile ezmlm-warn.c
+ ezmlm-weed: \
+-load ezmlm-weed.o getln.a strerr.a substdio.a error.a stralloc.a \
++load ezmlm-weed.o getln.a strerr.a substdio.a error.a case.a stralloc.a \
+ alloc.a str.a
+       ./load ezmlm-weed getln.a strerr.a substdio.a error.a \
+-      stralloc.a alloc.a str.a 
++      case.a stralloc.a alloc.a str.a 
+ ezmlm-weed.0: \
+ ezmlm-weed.1
+@@ -380,6 +882,18 @@
+ ezmlm.5
+       nroff -man ezmlm.5 > ezmlm.0
++ezmlmglrc.0: \
++ezmlmglrc.5
++      nroff -man ezmlmglrc.5 > ezmlmglrc.0
++
++ezmlmrc.0: \
++ezmlmrc.5
++      nroff -man ezmlmrc.5 > ezmlmrc.0
++
++ezmlmsubrc.0: \
++ezmlmsubrc.5
++      nroff -man ezmlmsubrc.5 > ezmlmsubrc.0
++
+ fd.a: \
+ makelib fd_copy.o fd_move.o
+       ./makelib fd.a fd_copy.o fd_move.o
+@@ -496,16 +1010,23 @@
+ byte.h install.c
+       ./compile install.c
++idxthread.o: \
++compile idxthread.c idxthread.h alloc.h error.h stralloc.h str.h lock.h idx.h \
++substdio.h fmt.h readwrite.h idx.h errtxt.h substdio.h byte.h yyyymm.h
++      ./compile idxthread.c
++
+ issub.o: \
+-compile issub.c stralloc.h gen_alloc.h stralloc.h issub.c getln.h \
+-issub.c readwrite.h issub.c substdio.h issub.c open.h issub.c byte.h \
+-issub.c case.h issub.c lock.h issub.c error.h issub.c issub.h \
+-strerr.h issub.h issub.c uint32.h issub.c
+-      ./compile issub.c
++compile issub.c stralloc.h gen_alloc.h getln.h readwrite.h substdio.h \
++open.h byte.h case.h lock.h error.h subscribe.h strerr.h uint32.h fmt.h \
++conf-sqlcc
++      ./compile issub.c ${SQLCC}
+ it: \
++ezmlm-idx ezmlm-accept ezmlm-archive ezmlm-check ezmlm-gate ezmlm-get \
++ezmlm-clean ezmlm-glconf ezmlm-moderate ezmlm-store ezmlm-tstdig \
+ ezmlm-make ezmlm-manage ezmlm-send ezmlm-reject ezmlm-return \
+-ezmlm-warn ezmlm-weed ezmlm-list ezmlm-sub ezmlm-unsub
++ezmlm-warn ezmlm-weed ezmlm-list ezmlm-sub ezmlm-unsub ezmlm-cgi ezmlm-limit \
++ezmlm-issubn ezmlm-cron ezmlm-request ezmlm-test ezmlm-split ezmlmrc
+ load: \
+ make-load warn-auto.sh systype
+@@ -527,6 +1048,10 @@
+ fmt.h log.c open.h log.c
+       ./compile log.c
++logmsg.o: \
++compile logmsg.c stralloc.h fmt.h conf-sqlcc
++      ./compile logmsg.c ${SQLCC}
++
+ make-compile: \
+ make-compile.sh auto-ccld.sh
+       cat auto-ccld.sh make-compile.sh > make-compile
+@@ -542,6 +1067,10 @@
+       cat auto-ccld.sh make-makelib.sh > make-makelib
+       chmod 755 make-makelib
++makehash.o: \
++makehash.c makehash.h surf.h uint32.h stralloc.h
++      ./compile makehash.c
++
+ makelib: \
+ make-makelib warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \
+@@ -549,12 +1078,23 @@
+       chmod 755 makelib
+ man: \
+-ezmlm.0 ezmlm-make.0 ezmlm-manage.0 ezmlm-send.0 ezmlm-reject.0 \
++ezmlm.0 ezmlm-gate.0 ezmlm-idx.0 ezmlm-get.0 ezmlm-check.0 ezmlm-tstdig.0 \
++ezmlm-make.0 ezmlm-manage.0 ezmlm-send.0 ezmlm-reject.0 ezmlm-accept.0 \
+ ezmlm-return.0 ezmlm-warn.0 ezmlm-weed.0 ezmlm-list.0 ezmlm-sub.0 \
+ ezmlm-unsub.0 alloc.0 case.0 datetime.0 direntry.0 env.0 error.0 \
+ error_str.0 error_temp.0 ezmlm.0 fd_copy.0 fd_move.0 getln.0 getln2.0 \
++ezmlm-issubn.0 ezmlm-cron.0 ezmlm-glconf.0 ezmlmglrc.0 ezmlm-test.0 \
++ezmlmsubrc.0 ezmlm-mktab.0 ezmlm-split.0 ezmlm-archive.0 ezmlm-cgi.0 \
+ getopt.0 now.0 sgetopt.0 stralloc.0 subfd.0 subgetopt.0 substdio.0 \
+-substdio_copy.0 substdio_in.0 substdio_out.0 surf.0 surfpcs.0 wait.0
++substdio_copy.0 substdio_in.0 substdio_out.0 surf.0 surfpcs.0 wait.0 \
++ezmlm-clean.0 ezmlm-moderate.0 ezmlm-store.0 ezmlm-request.0 ezmlmrc.0 \
++ezmlm-limit.0
++
++mime.a: \
++makelib concatHDR.o decodeHDR.o unfoldHDR.o \
++decodeQ.o encodeQ.o decodeB.o encodeB.o author.o
++      ./makelib mime.a concatHDR.o decodeHDR.o decodeQ.o encodeQ.o \
++      decodeB.o encodeB.o unfoldHDR.o author.o
+ now.0: \
+ now.3
+@@ -582,6 +1122,16 @@
+ compile open_trunc.c open_trunc.c open_trunc.c open.h open_trunc.c
+       ./compile open_trunc.c
++opensql.o: \
++compile opensql.c error.h strerr.h errtxt.h \
++      str.h case.h stralloc.h subscribe.h conf-sqlcc
++      ./compile opensql.c ${SQLCC}
++
++putsubs.o: \
++compile putsubs.c error.h substdio.h strerr.h readwrite.h \
++str.h open.h case.h errtxt.h stralloc.h subscribe.h qmail.h fmt.h conf-sqlcc
++      ./compile putsubs.c ${SQLCC}
++
+ qmail.o: \
+ compile qmail.c substdio.h qmail.c readwrite.h qmail.c wait.h qmail.c \
+ exit.h qmail.c fork.h qmail.c fd.h qmail.c qmail.h substdio.h \
+@@ -601,6 +1151,12 @@
+ compile scan_ulong.c scan.h scan_ulong.c
+       ./compile scan_ulong.c
++searchlog.o: \
++compile searchlog.c case.h stralloc.h scan.h open.h datetime.h errtxt.h str.h \
++      datetime.h date822fmt.h substdio.h readwrite.h strerr.h error.h \
++      subscribe.h conf-sqlcc
++      ./compile searchlog.c ${SQLCC}
++
+ seek.a: \
+ makelib seek_set.o
+       ./makelib seek.a seek_set.o
+@@ -800,6 +1356,12 @@
+ compile strerr_sys.c error.h strerr_sys.c strerr.h strerr_sys.c
+       ./compile strerr_sys.c
++subdb.a: \
++makelib checktag.o issub.o logmsg.o subscribe.o opensql.o putsubs.o \
++      tagmsg.o searchlog.o
++      ./makelib subdb.a checktag.o issub.o logmsg.o subscribe.o \
++      opensql.o putsubs.o tagmsg.o searchlog.o
++
+ subfd.0: \
+ subfd.3
+       nroff -man subfd.3 > subfd.0
+@@ -818,12 +1380,10 @@
+       ./compile subgetopt.c
+ subscribe.o: \
+-compile subscribe.c stralloc.h gen_alloc.h stralloc.h subscribe.c \
+-getln.h subscribe.c readwrite.h subscribe.c substdio.h subscribe.c \
+-strerr.h subscribe.c open.h subscribe.c byte.h subscribe.c case.h \
+-subscribe.c lock.h subscribe.c error.h subscribe.c uint32.h \
+-subscribe.c subscribe.h strerr.h strerr.h subscribe.h subscribe.c
+-      ./compile subscribe.c
++compile subscribe.c stralloc.h gen_alloc.h stralloc.h \
++getln.h readwrite.h substdio.h strerr.h open.h byte.h case.h \
++lock.h error.h uint32.h subscribe.h idx.h fmt.h conf-sqlcc
++      ./compile subscribe.c ${SQLCC}
+ substdi.o: \
+ compile substdi.c substdio.h substdi.c byte.h substdi.c error.h \
+@@ -889,6 +1449,10 @@
+ find-systype trycpp.c
+       ./find-systype > systype
++tagmsg.o: \
++compile tagmsg.c stralloc.h slurp.h scan.h fmt.h strerr.h cookie.h conf-sqlcc
++      ./compile tagmsg.c ${SQLCC}
++
+ uint32.h: \
+ tryulong32.c compile load uint32.h1 uint32.h2
+       ( ( ./compile tryulong32.c && ./load tryulong32 && \
+@@ -907,3 +1471,124 @@
+ wait_pid.o: \
+ compile wait_pid.c wait_pid.c wait_pid.c error.h wait_pid.c
+       ./compile wait_pid.c
++
++yyyymm.a: \
++makelib date2yyyymm.o dateline.o
++      ./makelib yyyymm.a date2yyyymm.o dateline.o
++
++ch: \
++ezmlmrc.ch
++      cp -f ezmlmrc.ch_GB ezmlmrc
++
++ch_GB: \
++ezmlmrc.ch_GB
++      cp -f ezmlmrc.ch_GB ezmlmrc
++
++cs: \
++ezmlmrc.cs
++      cp -f ezmlmrc.cs ezmlmrc
++
++da: \
++ezmlmrc.da
++      cp -f ezmlmrc.da ezmlmrc
++
++de: \
++ezmlmrc.de
++      cp -f ezmlmrc.de ezmlmrc
++
++en_US: \
++ezmlmrc.en_US
++      cp -f ezmlmrc.en_US ezmlmrc
++
++en: \
++ezmlmrc.en_US
++      cp -f ezmlmrc.en_US ezmlmrc
++
++es: \
++ezmlmrc.es
++      cp -f ezmlmrc.es ezmlmrc
++
++us: \
++ezmlmrc.en_US
++      cp -f ezmlmrc.en_US ezmlmrc
++
++ezmlmrc: \
++ezmlmrc.en_US
++      cp -f ezmlmrc.en_US ezmlmrc
++
++fr: \
++ezmlmrc.fr
++      cp -f ezmlmrc.fr ezmlmrc
++
++id: \
++ezmlmrc.id
++      cp -f ezmlmrc.id ezmlmrc
++
++ita: \
++ezmlmrc.it
++      cp -f ezmlmrc.it ezmlmrc
++
++jp: \
++ezmlmrc.jp
++      cp -f ezmlmrc.jp ezmlmrc
++
++pl: \
++ezmlmrc.pl
++      cp -f ezmlmrc.pl ezmlmrc
++
++pt: \
++ezmlmrc.pt
++      cp -f ezmlmrc.pt ezmlmrc
++
++pt_BR: \
++ezmlmrc.pt_BR
++      cp -f ezmlmrc.pt_BR ezmlmrc
++
++ru: \
++ezmlmrc.ru
++      cp -f ezmlmrc.ru ezmlmrc
++
++sv: \
++ezmlmrc.sv
++      cp -f ezmlmrc.sv ezmlmrc
++
++mysql:
++      ln -sf sub_mysql/ezmlm-mktab ezmlm-mktab
++      ln -sf sub_mysql/checktag.c checktag.c; rm -f checktag.o
++      ln -sf sub_mysql/issub.c issub.c; rm -f issub.o
++      ln -sf sub_mysql/logmsg.c logmsg.c; rm -f logmsg.o
++      ln -sf sub_mysql/subscribe.c subscribe.c; rm -f subscribe.o
++      ln -sf sub_mysql/opensql.c opensql.c; rm -f opensql.o
++      ln -sf sub_mysql/putsubs.c putsubs.c; rm -f putsubs.o
++      ln -sf sub_mysql/tagmsg.c tagmsg.c; rm -f tagmsg.o
++      ln -sf sub_mysql/searchlog.c searchlog.c; rm -f searchlog.o
++      ln -sf sub_mysql/conf-sqlld conf-sqlld; touch conf-sqlld
++      ln -sf sub_mysql/conf-sqlcc conf-sqlcc; touch conf-sqlcc
++
++pgsql:
++      ln -sf sub_pgsql/ezmlm-mktab ezmlm-mktab
++      ln -sf sub_pgsql/checktag.c checktag.c; rm -f checktag.o
++      ln -sf sub_pgsql/issub.c issub.c; rm -f issub.o
++      ln -sf sub_pgsql/logmsg.c logmsg.c; rm -f logmsg.o
++      ln -sf sub_pgsql/subscribe.c subscribe.c; rm -f subscribe.o
++      ln -sf sub_pgsql/opensql.c opensql.c; rm -f opensql.o
++      ln -sf sub_pgsql/putsubs.c putsubs.c; rm -f putsubs.o
++      ln -sf sub_pgsql/tagmsg.c tagmsg.c; rm -f tagmsg.o
++      ln -sf sub_pgsql/searchlog.c searchlog.c; rm -f searchlog.o
++      ln -sf sub_pgsql/conf-sqlld conf-sqlld; touch conf-sqlld
++      ln -sf sub_pgsql/conf-sqlcc conf-sqlcc; touch conf-sqlcc
++
++std:
++      ln -sf sub_std/ezmlm-mktab ezmlm-mktab
++      ln -sf sub_std/checktag.c checktag.c; rm -f checktag.o
++      ln -sf sub_std/issub.c issub.c; rm -f issub.o
++      ln -sf sub_std/logmsg.c logmsg.c; rm -f logmsg.o
++      ln -sf sub_std/subscribe.c subscribe.c; rm -f subscribe.o
++      ln -sf sub_std/opensql.c opensql.c; rm -f opensql.o
++      ln -sf sub_std/putsubs.c putsubs.c; rm -f putsubs.o
++      ln -sf sub_std/tagmsg.c tagmsg.c; rm -f tagmsg.o
++      ln -sf sub_std/searchlog.c searchlog.c; rm -f searchlog.o
++      ln -sf sub_std/conf-sqlld conf-sqlld; touch conf-sqlld
++      ln -sf sub_std/conf-sqlcc conf-sqlcc; touch conf-sqlcc
++
++
+--- constmap.c 1998/05/20 22:37:38     1.1
++++ constmap.c 1998/12/12 18:57:23     1.3
+@@ -18,6 +18,39 @@
+   return h;
+ }
++/* Returns index of string in constmap. 1 = first string, 2 = second ... */
++/* 0 not found. Use for commands */ 
++int constmap_index(cm,s,len)
++struct constmap *cm;
++char *s;
++int len;
++{
++  constmap_hash h;
++  int pos;
++  h = hash(s,len);
++  pos = cm->first[h & cm->mask];
++  while (pos != -1) {
++    if (h == cm->hash[pos])
++      if (len == cm->inputlen[pos])
++        if (!case_diffb(cm->input[pos],len,s))
++        return pos + 1;
++    pos = cm->next[pos];
++  }
++  return 0;
++}
++
++/* returns pointer to sz of string with index "idx". 1 = first, 2 = second...*/
++char *constmap_get(cm,idx)
++struct constmap *cm;
++int idx;
++
++{
++  if (idx <= 0 || idx > cm->num)
++    return 0;
++  else
++    return cm->input[idx-1];
++}
++
+ char *constmap(cm,s,len)
+ struct constmap *cm;
+ char *s;
+@@ -38,6 +71,9 @@
+ }
+ int constmap_init(cm,s,len,flagcolon)
++/* if flagcolon is true, we process only the stuff before the colon on */
++/* each line. Otherwise, it's the entire line. Still, the entire line */
++/* is stored! */
+ struct constmap *cm;
+ char *s;
+ int len;
+--- constmap.h 1998/05/20 22:37:38     1.1
++++ constmap.h 1999/09/29 03:16:11     1.3
+@@ -16,5 +16,6 @@
+ extern int constmap_init();
+ extern void constmap_free();
+ extern char *constmap();
+-
++extern char *constmap_get();
++extern int constmap_index();
+ #endif
+--- error.h    1999/01/09 01:36:11     1.1
++++ error.h    1999/01/09 01:59:56     1.2
+@@ -16,6 +16,7 @@
+ extern int error_pipe;
+ extern int error_perm;
+ extern int error_acces;
++extern int error_notdir;
+ extern char *error_str();
+ extern int error_temp();
+--- error.c    1999/01/09 01:36:42     1.1
++++ error.c    1999/01/09 01:59:56     1.2
+@@ -17,7 +17,7 @@
+ -2;
+ #endif
+-int error_noent = 
++int error_noent =
+ #ifdef ENOENT
+ ENOENT;
+ #else
+@@ -92,4 +92,11 @@
+ EACCES;
+ #else
+ -13;
++#endif
++
++int error_notdir =
++#ifdef ENOTDIR
++ENOTDIR;
++#else
++-14;
+ #endif
+--- ezmlm-weed.c       1999/04/11 19:09:55     1.1
++++ ezmlm-weed.c       1999/12/19 16:49:32     1.4
+@@ -5,18 +5,24 @@
+ #include "substdio.h"
+ #include "getln.h"
+ #include "strerr.h"
++#include "errtxt.h"
+ char buf0[256];
+ substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
+ #define FATAL "ezmlm-weed: fatal: "
++void die_nomem()
++{
++  strerr_die2x(111,FATAL,ERR_NOMEM);
++}
++
+ void get(sa)
+ stralloc *sa;
+ {
+   int match;
+   if (getln(&ss0,sa,&match,'\n') == -1)
+-    strerr_die2sys(111,FATAL,"unable to read input: ");
++    strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+   if (!match) _exit(0);
+ }
+@@ -30,6 +36,9 @@
+ stralloc line7 = {0};
+ stralloc line8 = {0};
++stralloc boundary = {0};
++stralloc dsnline = {0};
++
+ char warn1[] = "    **********************************************";
+ char warn2[] = "    **      THIS IS A WARNING MESSAGE ONLY      **";
+ char warn3[] = "    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **";
+@@ -40,19 +49,46 @@
+ int flagsr = 0;
+ int flagas = 0;
+ int flagbw = 0;
++int flagdsn = 0;
++
++int isboundary()
++/* returns 1 if line.len contains the mime bondary, 0 otherwise */
++{
++    if (line.s[0] == '-' && line.s[1] == '-' && line.len >= boundary.len + 3)
++      if (!byte_diff(line.s + 2,boundary.len,boundary.s))     /* boundary */
++        return 1;
++    return 0;
++}
+ void main()
+ {
+-  int match;
++  unsigned int i,j;
+   for (;;) {
+     get(&line);
+     if (line.len == 1) break;
+-
++    if (line.s[0] == ' ' || line.s[0] == '\t') {      /* continuation */
++      if (flagdsn) {
++      if (!stralloc_catb(&dsnline,line.s,line.len - 1)) die_nomem();
++      continue;
++      }
++    }
++    flagdsn = 0;
+     if (stralloc_starts(&line,"Subject: success notice"))
+       _exit(99);
+     if (stralloc_starts(&line,"Subject: deferral notice"))
+       _exit(99);
++    if (stralloc_starts(&line,"Precedence: bulk"))
++      _exit(99);
++    if (stralloc_starts(&line,"Precedence: junk"))
++      _exit(99);
++/* for Novell Groupwise */
++    if (stralloc_starts(&line,"Subject: Message status - delivered"))
++      _exit(99);
++    if (stralloc_starts(&line,"Subject: Message status - opened"))
++      _exit(99);
++    if (stralloc_starts(&line,"Subject: Out of Office AutoReply:"))
++      _exit(99);
+     if (stralloc_starts(&line,"From: Mail Delivery Subsystem <MAILER-DAEMON@"))
+       flagmds = 1;
+@@ -62,6 +98,71 @@
+       flagsr = 1;
+     if (stralloc_starts(&line,"Auto-Submitted: auto-generated (warning"))
+       flagas = 1;
++    if (case_startb(line.s,line.len,"Content-type: multipart/report"))
++      if (!stralloc_copyb(&dsnline,line.s,line.len - 1)) die_nomem();
++      flagdsn = 1;
++  }                   /* end of header */
++
++  if (flagdsn) {      /* always only one recipient/action */
++    flagdsn = 0;      /* will be set for correct report type */
++    for (i=0; i < dsnline.len; i += 1+byte_chr(dsnline.s+i,dsnline.len-i,';')) {
++      while (dsnline.s[i] == ' ' || dsnline.s[i] == '\t')
++      if (++i >= dsnline.len) break;
++      if (case_startb(dsnline.s + i,dsnline.len - i,"report-type=")) {
++      i += 12;
++      while (dsnline.s[i] ==' ' || dsnline.s[i] =='\t' || dsnline.s[i] =='"')
++        if (++i >= dsnline.len) break;
++      if (case_startb(dsnline.s + i,dsnline.len - i,"delivery-status"))
++        flagdsn = 1;
++      } else if (case_startb(dsnline.s + i,dsnline.len - i,"boundary=")) {
++      i += 9;
++      while (dsnline.s[i] ==' ' || dsnline.s[i] =='\t')
++        if (++i >= dsnline.len) break;
++      if (dsnline.s[i] == '"') {
++        if (++i >= dsnline.len) break;
++        j = i + byte_chr(dsnline.s + i,dsnline.len - i,'"');
++        if (j >= dsnline.len) break;
++      } else {
++        j = i;
++        while (dsnline.s[j] !=' ' && dsnline.s[j] !='\t' &&
++              dsnline.s[j] !=';')
++          if (++j >= dsnline.len) break;
++      }                               /* got boundary */
++      if (!stralloc_copyb(&boundary,dsnline.s+i,j-i)) die_nomem();
++      }
++    }
++  }
++  if (flagdsn && boundary.len) {      /* parse DSN message */
++    get(&line);                       /* if bad format we exit(0) via get() */
++    for (;;) {
++      if (isboundary()) {
++      if (line.len == boundary.len + 5 && line.s[line.len - 1] == '-'
++              && line.s[line.len - 2] == '-')
++        _exit(99);                    /* end: not failure report */
++        get(&line);                   /* Content-type */
++        if (case_startb(line.s,line.len,"content-type:")) {
++        i = 13;
++        while (line.s[i] == ' ' || line.s[i] == '\t')
++              if (++i >= line.len) break;
++        if (case_startb(line.s+i,line.len-i,"message/delivery-status")) {
++          for (;;) {
++            get(&line);
++            if (isboundary()) break;
++            if (case_startb(line.s,line.len,"action:")) {
++              i = 8;
++              while (line.s[i] == ' ' || line.s[i] == '\t')
++                if (++i >= line.len) break;
++              if (case_startb(line.s + i, line.len - i,"failed"))
++                _exit(0);     /* failure notice */
++              else
++                _exit(99);    /* there shouldn't be more than 1 action */
++            }
++            }
++        }
++        }
++      } else
++      get(&line);
++    }
+   }
+   get(&line1);
+--- ezmlm-weed.1       1999/08/01 16:45:46     1.1
++++ ezmlm-weed.1       1999/12/19 16:53:18     1.3
+@@ -7,6 +7,8 @@
+ .B ezmlm-weed
+ reads a mail message from its standard input.
+ If it recognizes the message as an MTA warning message or success message,
++or as a message with precedence ``bulk'' or ``junk'' as generated by
++vacation autoresponders,
+ it exits 99;
+ this will cause
+ .B qmail-alias
+@@ -33,6 +35,13 @@
+    Subject: deferral notice
+ .EE
++Delivery-status notification (DSN, rfc1891). All DSN messages with ``Action''
++other than ``failed'':
++
++.EX
++   Content-type: multipart/report
++.EE
++
+ Warning message from sendmail, MIME form:
+ .EX
+@@ -104,6 +113,21 @@
+ .EX
+    THIS IS A WARNING MESSAGE ONLY
+ .EE
++
++Notification messages from Novell Groupwise:
++
++.EX
++  Subject: Message status - delivered
++.EE
++.br
++.EX
++  Subject: Message status - opened
++.EE
++.br
++.EX
++  Subject: Out of Office AutoReply:
++.EE
++
+ .SH "SEE ALSO"
+ ezmlm-return(1),
+ qmail-command(8)
diff --git a/idxthread.c b/idxthread.c
new file mode 100644 (file)
index 0000000..b8ed2b0
--- /dev/null
@@ -0,0 +1,682 @@
+/*$Id: idxthread.c,v 1.35 1999/11/22 01:47:45 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+/* idxthread.c contains routines to from the ezmlm-idx subject index build */
+/* a structure of unique subjects as well as a table of messages with      */
+/* pointers to the subject. This leads to information on message threads   */
+/* arranged chronologically within the thread, and with the threads        */
+/* arranged chronologically by the first message within the range.         */
+/* idx_mkthreads() will arrange the author list in a similar manner. This  */
+/* saves some space, and takes a little extra time. It's needed when       */
+/* generating an author index. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "error.h"
+#include "alloc.h"
+#include "str.h"
+#include "stralloc.h"
+#include "strerr.h"
+#include "lock.h"
+#include "idx.h"
+#include "errtxt.h"
+#include "substdio.h"
+#include "fmt.h"
+#include "readwrite.h"
+#include "makehash.h"
+#include "yyyymm.h"
+
+#define DATENO 100
+static stralloc line = {0};            /* primary input */
+static stralloc authline = {0};                /* second line of primary input */
+static stralloc dummyind = {0};
+
+static substdio ssindex;
+static char indexbuf[1024];
+
+static char strnum[FMT_ULONG];
+
+struct stat st;
+                       /* if no data, these may be the entire table, so */
+                       /* need to be static */
+static subentry sdummy;
+static authentry adummy;
+
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+int fdlock;
+
+/* NOTE: These do NOT prevent double locking */
+static void lockup(fatal)
+char *fatal;
+{
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    strerr_die2sys(111,fatal,ERR_OPEN_LOCK);
+  if (lock_ex(fdlock) == -1) {
+    close(fdlock);
+    strerr_die2sys(111,fatal,ERR_OBTAIN_LOCK);
+  }
+}
+
+static void unlock()
+{
+    close(fdlock);
+}
+
+static void newsub(psubt,subject,sublen,msg,fatal)
+/* Initializes subentry pointed to by psubt, adds a '\0' to subject,    */
+/* allocates space and copies in subject, and puts a pointer to it in   */
+/* the entry. */
+subentry *psubt;
+char *subject;
+unsigned int sublen;
+unsigned long msg;
+char *fatal;
+{
+  register char *cpfrom, *cpto;
+  register unsigned int cpno;
+
+  psubt->higher = (subentry *) 0;
+  psubt->lower = (subentry *) 0;
+  psubt->firstmsg = msg;
+  psubt->lastmsg = msg;
+  psubt->msginthread = 1;
+  if (!(psubt->sub = alloc ((sublen) * sizeof(char))))
+    die_nomem(fatal);
+  cpto = psubt->sub;
+  cpno = sublen;
+  cpfrom = subject;
+  while (cpno--) *(cpto++) = *(cpfrom++);
+  psubt->sublen = sublen;
+}
+
+static void newauth(pautht,author,authlen,msg,fatal)
+/* Allocates space for author of length authlen+1 adding a terminal '\0' */
+/* and puts the pointer in pautht->auth. Analog to newsub().             */
+authentry *pautht;     /* entry for current message */
+char *author;          /* pointer to author string (not sz!) */
+unsigned int authlen;  /* lenth of author */
+unsigned long msg;
+char *fatal;           /* sz */
+
+{
+  register char *cpfrom, *cpto;
+  register unsigned int cpno;
+
+  pautht->higher = (subentry *) 0;
+  pautht->lower = (subentry *) 0;
+  pautht->firstmsg = msg;
+  if (!(pautht->auth = alloc ((authlen) * sizeof(char))))
+    die_nomem(fatal);
+  cpto = pautht->auth;
+  cpno = authlen;
+  cpfrom = author;
+  while (cpno--) *(cpto++) = *(cpfrom++);
+  pautht->authlen = authlen;
+}
+
+static void init_dummy(fatal)
+char *fatal;
+{
+  unsigned int i;
+
+  if (!stralloc_ready(&dummyind,HASHLEN + 1)) die_nomem(fatal);
+  for (i = 0; i< HASHLEN; i++)
+    dummyind.s[i] = 'a';
+  dummyind.len = HASHLEN;
+  if (!stralloc_append(&dummyind," ")) die_nomem(fatal);
+}
+
+void idx_mkthreads(pmsgtable,psubtable,pauthtable,pdatetable,
+       msg_from,msg_to,msg_latest,locked,fatal)
+/* Threads messages msg_from -> msg_to into pmsgtable & psubtable. When  */
+/* reading the latest index file (containing msg_latest) it locks the    */
+/* directory, unless it is already locked (as in digest creation).       */
+/* msgtable has the subject number 1.. (0 if there is no subject match,  */
+/* which should happen only if the subject index is corrupt.)            */
+
+/* 19971107 Changed to deal with index files that are missing, or have   */
+/* missing entries, not necessarily reflecting missing archive files.    */
+/* This all to make ezmlm-get more robust to get maximal info out of     */
+/* corrupted archives.                                                   */
+
+  msgentry **pmsgtable;                /* table of message<->subject */
+  subentry **psubtable;                /* subject no, len, str char * */
+  authentry **pauthtable;      /* author no, len, str char * */
+  dateentry **pdatetable;      /* message per date */
+  unsigned long msg_from;      /* first message in range */
+  unsigned long msg_to;                /* last message in range */
+  unsigned long msg_latest;    /* latest message in archive (for locking) */
+  int locked;                  /* if already locked */
+  char *fatal;                 /* Program-specific */
+
+{
+  unsigned long idxlatest;     /* need to lock for this (last) index file */
+  unsigned long msg;           /* current msg number */
+  unsigned long endmsg;                /* max msg in this idx file */
+  unsigned long tmpmsg;                /* index entry's msg number */
+  unsigned long idx;           /* current index file no */
+  unsigned long idxto;         /* index containing end of range */
+  unsigned long ulmrange;      /* total # of messages in range */
+  char *subject;               /* subject on line */
+  unsigned int sublen;         /* length of subject */
+  char *auth;
+  unsigned int authlen;
+  unsigned int pos,posa;
+  unsigned long submax;                /* max subject num in subtable */
+  subentry *psubnext;          /* points to next entry in subtable */
+  subentry *psubt;             /* points to entry in subtable */
+  authentry *pauthnext;                /* points to next entry in authtable */
+  authentry *pautht;           /* points to entry in authtable */
+  int fd;                      /* index file handle */
+  int flagmissingindex;                /* current index file is missing */
+  int flagauth;                        /* read index entry has author info */
+  int hasauth;                 /* current msg's entry has author info */
+  msgentry *pmsgt;
+  int res;
+  int match;
+  unsigned int datepos,datemax;
+  unsigned int datetablesize,datetableunit;
+  unsigned int lastdate = 0;
+  unsigned int thisdate;
+  register msgentry *x, *y;
+
+                               /* a few unnecessary sanity checks */
+  if (msg_to > msg_latest)
+    msg_to = msg_latest;
+  if (msg_to < msg_from)
+    strerr_die2x(100,fatal,"Program error: bad range in idx_mkthreads");
+  ulmrange = msg_to - msg_from + 1;
+  if (!(*pmsgtable = (msgentry *) alloc(ulmrange * sizeof(msgentry))))
+        die_nomem(fatal);
+  y = *pmsgtable;
+  x = y + ulmrange;            /* clear */
+  while (--x >= y) {
+    x->subnum = 0;
+    x->authnum = 0;
+    x->date = 0;
+  }
+                               /* max entries - acceptable waste for now */
+  if (!(*psubtable = (subentry *) alloc((ulmrange+1) * sizeof(subentry))))
+        die_nomem(fatal);
+
+  if (!(*pauthtable = (authentry *) alloc((ulmrange+1) * sizeof(authentry))))
+        die_nomem(fatal);
+  datetableunit = DATENO * sizeof(dateentry);
+  datetablesize = datetableunit;
+  if (!(*pdatetable = (dateentry *) alloc(datetablesize)))
+        die_nomem(fatal);
+  datepos = 0;
+  datemax = DATENO - 2;                /* entry 0 and end marker */
+  lastdate = 0;
+
+  idxlatest = msg_latest / 100;
+  idxto = msg_to / 100;
+  submax = 0;
+  psubnext = *psubtable;       /* dummy node to get tree going. Basically, */
+  psubt = &sdummy;             /* assure that subject > psubt-sub and that */
+  init_dummy(fatal);           /* below ok unless HASHLEN > 40 */
+  psubt->sub = "                                       ";
+  psubt->sublen = 40;          /* there is something to hold psubt->higher */
+  psubt->higher = (subentry *) 0;
+  psubt->lower = (subentry *) 0;
+  pauthnext = *pauthtable;
+  pautht = &adummy;
+  pautht->auth = psubt->sub;
+  pautht->authlen = psubt->sublen;
+  pautht->higher = (authentry *) 0;
+  pautht->lower = (authentry *) 0;
+  for (idx = msg_from / 100; idx <= idxto; idx++) {
+                               /* make index file name */
+    if (!stralloc_copys(&line,"archive/")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,idx))) die_nomem(fatal);
+    if (!stralloc_cats(&line,"/index")) die_nomem(fatal);
+    if (!stralloc_0(&line)) die_nomem(fatal);
+    if (!locked && idx == idxlatest)
+      lockup(fatal);
+    flagmissingindex = 0;
+    fd = open_read(line.s);
+    if (fd == -1) {
+      if (errno == error_noent) {      /* this means the index is not here */
+                                       /* but the lists is supposedly indexed*/
+        flagmissingindex = 1;
+      } else
+        strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
+    } else
+      substdio_fdbuf(&ssindex,read,fd,indexbuf,sizeof(indexbuf));
+
+    msg = 100L * idx;                  /* current msg# */
+    endmsg = msg + 99L;                        /* max msg in this index */
+    if (!msg) msg = 1L;                        /* for start to make msg > tmpmsg */
+    tmpmsg = 0L;                       /* msg number of read index line */
+    if (endmsg > msg_to)               /* skip non-asked for subjects */
+      endmsg = msg_to;
+    for (; msg <= endmsg; msg++) {
+      if (!flagmissingindex && (msg > tmpmsg)) {
+        flagauth = 0;
+        if (getln(&ssindex,&line,&match,'\n') == -1)
+          strerr_die3sys(111,fatal,ERR_READ,"index: ");
+        if (!match)
+          flagmissingindex = 1;
+        else {
+          pos = scan_ulong(line.s,&tmpmsg);
+          if (line.s[pos++] == ':') {
+            if (getln(&ssindex,&authline,&match,'\n') == -1)
+              strerr_die3sys(111,fatal,ERR_READ,"index: ");
+            if (!match)
+              flagmissingindex = 1;
+            else {
+              flagauth = 1;
+           }
+            pos++;
+          }
+        }
+      }
+      if (msg < msg_from)      /* Nothing before start of range */
+        continue;
+      if (msg == tmpmsg) {
+        subject = line.s + pos;
+        sublen = line.len - pos;
+       if (sublen <= HASHLEN)
+         strerr_die2x(100,fatal,ERR_BAD_INDEX);
+        hasauth = flagauth;
+      } else {
+        subject = dummyind.s;
+        sublen = dummyind.len;
+        hasauth = 0;
+      }
+      for(;;) {                /* search among already known subjects */
+        res = str_diffn(psubt->sub,subject,HASHLEN);
+        if (res < 0) {
+          if (psubt->higher)
+            psubt = psubt->higher;
+          else {
+            newsub(psubnext,subject,sublen,msg,fatal);
+            psubt->higher = psubnext;
+            psubt = psubnext;
+            psubnext++;
+            break;
+          }
+        } else if (res > 0) {
+          if (psubt->lower)
+            psubt = psubt->lower;
+          else {
+            newsub(psubnext,subject,sublen,msg,fatal);
+            psubt->lower = psubnext;
+            psubt = psubnext;
+            psubnext++;
+            break;
+          }
+        } else {
+          psubt->lastmsg = msg;
+         (psubt->msginthread)++;       /* one more message in thread */
+         break;
+        }
+      }
+                               /* first subnum =1 (=0 is empty for thread) */
+      pmsgt = *pmsgtable + msg - msg_from;
+      pmsgt->subnum = (unsigned int) (psubt - *psubtable + 1);
+      pmsgt->date = lastdate;
+      if (hasauth) {
+       pos = 0;
+       while (authline.s[pos] && authline.s[pos] != ' ') pos++;
+       if (authline.s[++pos]) {
+         thisdate = date2yyyymm(authline.s + pos);
+         if (thisdate) pmsgt->date = thisdate;
+         if (pmsgt->date > lastdate) {
+           lastdate = pmsgt->date;
+           if (datepos >= datemax) {           /* more space */
+             datemax += DATENO;
+             if (!(*pdatetable = (dateentry *) alloc_re(*pdatetable,
+                       datetablesize,datetablesize+datetableunit)))
+               die_nomem(fatal);
+           }
+           (*pdatetable)[datepos].msg = msg;   /* first msg this mo */
+           (*pdatetable)[datepos].date = lastdate;
+           datepos++;
+         }
+         posa = byte_chr(authline.s,authline.len,';');
+         if (authline.len > posa + HASHLEN + 1 && authline.s[pos+1] != ' ') {
+                                       /* old: "; auth", new: ";hash auth" */
+           auth = authline.s + posa + 1;
+           authlen = authline.len - posa - 1;
+         } else {
+           auth = dummyind.s;
+           authlen = dummyind.len;
+         }
+       }
+                       /* allright! Same procedure, but for author */
+        for (;;) {     /* search among already known authors */
+          res = str_diffn(pautht->auth,auth,HASHLEN);
+          if (res < 0) {
+            if (pautht->higher)
+              pautht = pautht->higher;
+            else {
+              newauth(pauthnext,auth,authlen,msg,fatal);
+              pautht->higher = pauthnext;
+              pautht = pauthnext;
+              pauthnext++;
+              break;
+            }
+          } else if (res > 0) {
+            if (pautht->lower)
+              pautht = pautht->lower;
+            else {
+              newauth(pauthnext,auth,authlen,msg,fatal);
+              pautht->lower = pauthnext;
+              pautht = pauthnext;
+              pauthnext++;
+              break;
+            }
+          } else {
+            break;
+          }
+        }                      /* link from message to this author */
+        pmsgt->authnum = (unsigned int) (pautht - *pauthtable + 1);
+        pautht = *pauthtable;
+      }
+
+      psubt = *psubtable;      /* setup psubt. Done here rather than before */
+                               /* the for loop, so that we can start off    */
+                               /* the dummy node. */
+    }
+    if (fd != -1)
+      close(fd);
+    if (!locked && idx == idxlatest)
+      unlock();                        /* 'locked' refers to locked before calling */
+  }
+  psubnext->sub = (char *) 0;          /* end of table marker */
+  pauthnext->auth = (char *) 0;                /* end of table marker */
+  (*pdatetable)[datepos].msg = msg_to + 1;
+  (*pdatetable)[datepos].date = lastdate + 1;
+}
+
+
+void idx_mkthread(pmsgtable,psubtable,pauthtable,msg_from,msg_to,msg_master,
+               msg_latest,locked,fatal)
+/* Works like idx_mkthreads, except that it finds the subject for message   */
+/* msg_master, then identifies messages in the range that have the same     */
+/* subject. msgtable entries with subject 0 do not match, with '1' do match.*/
+
+msgentry **pmsgtable;          /* pointer to table of message<->subject */
+subentry **psubtable;          /* ptr to tbl of subject no, len, str char * */
+authentry **pauthtable;
+unsigned long msg_from;                /* first message in range */
+unsigned long msg_to;          /* last message in range */
+unsigned long msg_latest;      /* latest message in archive (for locking) */
+unsigned long msg_master;      /* master message for single thread, else 0*/
+int locked;                    /* if already locked */
+char *fatal;                   /* Program-specific */
+
+{
+  unsigned long idxlatest;     /* need to lock for this (last) index file */
+  unsigned long idxto;         /* index for last msg in range */
+  unsigned long idx;           /* current index file no */
+  unsigned long msg;           /* index entry's msg number */
+  unsigned long ulmrange;      /* total # of messages in range */
+  subentry *psubt;             /* points to last entry in subtable */
+  int ffound;                  /* msg subject was found in subtable */
+  int flagauth;                        /* there is author info */
+  int firstfound = 1;          /* = 1 until first message in thread found */
+  int res;                     /* comparison result */
+  char *auth;
+  unsigned int authlen;
+  authentry *pauthnext;                /* points to next entry in authtable */
+  authentry *pautht;           /* points to entry in authtable */
+  unsigned int pos;
+  int fd;                      /* index file handle */
+  int match;
+  msgentry *pmsgt;
+  register msgentry *x,*y;
+
+  if ((ulmrange = msg_to - msg_from +1) <= 0)
+    strerr_die2x(100,fatal,"Program error: bad range in idx_mkthreads");
+  if (!(*pmsgtable = (msgentry *) alloc(ulmrange * sizeof(msgentry))))
+         die_nomem(fatal);
+  y = *pmsgtable;
+  x = y + ulmrange;
+  while (--x >= y) {
+    x->subnum = 0;
+    x->authnum = 0;
+    x->date = 0;
+  }
+
+  if (!(*psubtable = (subentry *) alloc(2 * sizeof(subentry))))
+          die_nomem(fatal);
+
+  if (!(*pauthtable = (authentry *) alloc((ulmrange + 1) * sizeof(authentry))))
+          die_nomem(fatal);
+
+  pauthnext = *pauthtable;
+  pautht = &adummy;
+  init_dummy();
+  pautht->auth = "                     ";
+  pautht->authlen = 21;
+  pautht->higher = (authentry *) 0;
+  pautht->lower = (authentry *) 0;
+  idxlatest = msg_latest / 100;
+  idxto = msg_to / 100;
+  idx = msg_master / 100;      /* index for master subject */
+
+                               /* Get master subject */
+  if (!stralloc_copys(&line,"archive/")) die_nomem(fatal);
+  if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,idx))) die_nomem(fatal);
+  if (!stralloc_cats(&line,"/index")) die_nomem(fatal);
+  if (!stralloc_0(&line)) die_nomem(fatal);
+  ffound = 0;
+  if (!locked && idx == idxlatest)
+    lockup(fatal);
+  fd = open_read(line.s);
+  psubt = *psubtable;
+  if (fd == -1) {
+    if (errno != error_noent)
+      strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
+    else
+      strerr_die2x(111,fatal,ERR_NOINDEX);     /* temp - admin can fix! */
+  } else {
+    substdio_fdbuf(&ssindex,read,fd,indexbuf,sizeof(indexbuf));
+    for(;;) {
+      if (getln(&ssindex,&line,&match,'\n') == -1)
+          strerr_die3sys(111,fatal,ERR_OPEN,"index: ");
+      if (!match)
+        break;
+      pos=scan_ulong(line.s,&msg);
+      if (line.s[pos++] == ':') {       /* marker for author info */
+        pos++;
+        flagauth = 1;
+      } else
+        flagauth = 0;
+      if (msg == msg_master) {
+        newsub(psubt,line.s+pos,line.len-pos,msg,fatal);
+                                       /* need to update msg later! */
+        ffound = 1;
+        break;
+      }
+      if (flagauth) {                  /* skip author line */
+        if (getln(&ssindex,&line,&match,'\n') == -1)
+          strerr_die3sys(111,fatal,ERR_OPEN,"index: ");
+      if (!match)
+        break;
+      }
+    }
+    close(fd);
+  }
+  if (!locked && idx == idxlatest)
+    unlock();
+  if (!ffound)
+      strerr_die2x(100,fatal,ERR_NOINDEX);
+  for (idx = msg_from / 100; idx <= idxto; idx++) {
+               /* make index file name */
+    if (!stralloc_copys(&line,"archive/")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,idx))) die_nomem(fatal);
+    if (!stralloc_cats(&line,"/index")) die_nomem(fatal);
+    if (!stralloc_0(&line)) die_nomem(fatal);
+    if (!locked && idx == idxlatest)
+      lockup(fatal);
+    fd = open_read(line.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
+    } else {
+      substdio_fdbuf(&ssindex,read,fd,indexbuf,sizeof(indexbuf));
+      for(;;) {
+        if (getln(&ssindex,&line,&match,'\n') == -1)
+          strerr_die3sys(111,fatal,ERR_READ,"index: ");
+        if (!match)
+          break;
+        pos=scan_ulong(line.s,&msg);
+        if (line.s[pos++] == ':') {
+          pos++;
+          flagauth = 1;
+          if (getln(&ssindex,&authline,&match,'\n') == -1)
+            strerr_die3sys(111,fatal,ERR_READ,"index: ");
+          if (!match)
+            break;
+        } else
+          flagauth = 0;
+        if (msg < msg_from)    /* Nothing before start of range */
+          continue;
+        if (msg > msg_to)      /* Don't do anything after range */
+          break;
+        if (!str_diffn(psubt->sub,line.s+pos,HASHLEN)) {
+          pmsgt = *pmsgtable + msg - msg_from;
+          if (firstfound) {    /* update to first message with this subj */
+            psubt->firstmsg = msg;
+            firstfound = 0;
+          }
+         psubt->lastmsg = msg;
+          pmsgt->subnum = 1;
+          if (flagauth) {
+           if (*authline.s)
+             pmsgt->date = date2yyyymm(authline.s + 1);
+            pos = byte_chr(authline.s,authline.len,';');
+           if (authline.len > pos + HASHLEN + 1 && authline.s[pos+1] != ' ') {
+                                       /* old: "; auth", new: ";hash auth" */
+             auth = authline.s + pos + 1;
+             authlen = authline.len - pos - 1;
+           } else {
+             auth = dummyind.s;
+             authlen = dummyind.len;
+           }
+           for (;;) {          /* search among already known authors */
+             res = str_diffn(pautht->auth,auth,HASHLEN);
+             if (res < 0) {
+               if (pautht->higher)
+                 pautht = pautht->higher;
+               else {
+                 newauth(pauthnext,auth,authlen,msg,fatal);
+                 pautht->higher = pauthnext;
+                 pautht = pauthnext;
+                 pauthnext++;
+                 break;
+               }
+             } else if (res > 0) {
+               if (pautht->lower)
+                 pautht = pautht->lower;
+               else {
+                 newauth(pauthnext,auth,authlen,msg,fatal);
+                 pautht->lower = pauthnext;
+                 pautht = pauthnext;
+                 pauthnext++;
+                 break;
+               }
+             } else {
+               break;
+             }
+           }                   /* link from message to this author */
+           pmsgt->authnum = (unsigned int) (pautht - *pauthtable + 1);
+            pautht = *pauthtable;
+         }
+
+        }
+      }
+      close(fd);
+    }
+    if (!locked && idx == idxlatest)
+      unlock();
+  }
+  ++psubt;
+  psubt->sub = (char *) 0;     /* end of table marker */
+  pauthnext->auth = (char *) 0;        /* end of table marker */
+}
+
+void idx_mklist(pmsgtable,psubtable,pauthtable,msg_from,msg_to,fatal)
+/* Like mkthreads, except that it works without a subject index. The result */
+/* is just a dummy subject and a sequential list of messages. This to allow */
+/* use of the same routines when creating digest from lists that have no    */
+/* subject index (for whatever reason). */
+msgentry **pmsgtable;          /* pointer to table of message<->subject */
+subentry **psubtable;          /* ptr to tbl of subject no, len, str char * */
+authentry **pauthtable;
+unsigned long msg_from;                /* first message in range */
+unsigned long msg_to;          /* last message in range */
+char *fatal;                   /* Program-specific */
+{
+  unsigned long ulmrange;
+  register msgentry *x,*y;
+  subentry *psubt;
+  authentry *pautht;
+
+  if ((ulmrange = msg_to - msg_from +1) <= 0)
+    strerr_die2x(111,fatal,"bad range in idx_mkthreads :");
+
+  if (!(*pmsgtable = (msgentry *) alloc(ulmrange * sizeof(msgentry))))
+         die_nomem(fatal);
+
+  y = *pmsgtable;
+  x = y + ulmrange;
+  while (--x >= y) {
+    x->subnum = 1;
+    x->authnum = 0;
+    x->date = 0;
+  }
+
+  if (!(*psubtable = (subentry *) alloc(2 * sizeof(subentry))))
+          die_nomem(fatal);
+  psubt = *psubtable;
+  newsub(psubt,dummyind.s,dummyind.len,msg_from,fatal);
+  psubt->lastmsg = msg_to;
+  ++psubt;
+  psubt->sub = (char *) 0;
+  if (!(*pauthtable = (authentry *) alloc(sizeof(authentry))))
+          die_nomem(fatal);    /* nodata. Avoid dangling ptr. */
+  pautht = *pauthtable;
+  pautht->auth = 0;            /* tells app that there are no author data */
+  pautht->higher = (authentry *) 0;
+  pautht->lower = (authentry *) 0;
+}
+
+void idx_destroythread(msgtable,subtable,authtable)
+/* Frees space allocated by idxthread routines. This is needed only if */
+/* one does several threadings in one program run. Otherwise, exit()   */
+/* should free all allocated memory, which will be faster. */
+msgentry *msgtable; subentry *subtable; authentry *authtable;
+{
+  subentry *psubt;
+  authentry *pautht;
+
+  psubt = subtable;            /* free subjects */
+  while(psubt->sub) {
+    alloc_free(psubt->sub);
+    psubt++;
+  }
+
+  pautht = authtable;          /* free authors */
+  while(pautht->auth) {
+    alloc_free(pautht->auth);
+    pautht++;
+  }
+
+  alloc_free(subtable);                /* free subtable */
+  alloc_free(authtable);       /* free authtable */
+  alloc_free(msgtable);                /* free msgtable */
+  subtable = (subentry *) 0;   /* kill pointers */
+  authtable = (authentry *) 0;
+  msgtable = (msgentry *) 0;
+}
diff --git a/idxthread.h b/idxthread.h
new file mode 100644 (file)
index 0000000..dc7ff4d
--- /dev/null
@@ -0,0 +1,11 @@
+
+#ifndef IDXTHREAD_H
+#define IDXTHREAD_H
+
+/* threading */
+extern void idx_mkthread();
+extern void idx_mkthreads();
+extern void idx_mklist();
+extern void idx_destroythread();
+
+#endif
diff --git a/issub.c b/issub.c
deleted file mode 100644 (file)
index 81ab6ad5d390b53c6e76aeb811ccbb8b1fe16a20..0000000000000000000000000000000000000000
--- a/issub.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "stralloc.h"
-#include "getln.h"
-#include "readwrite.h"
-#include "substdio.h"
-#include "open.h"
-#include "byte.h"
-#include "case.h"
-#include "lock.h"
-#include "error.h"
-#include "issub.h"
-#include "uint32.h"
-
-static stralloc addr = {0};
-static stralloc line = {0};
-static stralloc fn = {0};
-static int fd;
-static substdio ss;
-static char ssbuf[256];
-
-static int doit(userhost)
-char *userhost;
-{
-  int j;
-  uint32 h;
-  char ch;
-  int match;
-
-  if (!stralloc_copys(&addr,"T")) return -2;
-  if (!stralloc_cats(&addr,userhost)) return -2;
-
-  j = byte_rchr(addr.s,addr.len,'@');
-  if (j == addr.len) return 0;
-  case_lowerb(addr.s + j + 1,addr.len - j - 1);
-
-  h = 5381;
-  for (j = 0;j < addr.len;++j)
-    h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
-  ch = 64 + (h % 53);
-
-  if (!stralloc_0(&addr)) return -2;
-
-  if (!stralloc_copys(&fn,"subscribers/")) return -2;
-  if (!stralloc_catb(&fn,&ch,1)) return -2;
-  if (!stralloc_0(&fn)) return -2;
-
-  fd = open_read(fn.s);
-  if (fd == -1) {
-    if (errno != error_noent) return -3;
-    return 0;
-  }
-  substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
-
-  for (;;) {
-    if (getln(&ss,&line,&match,'\0') == -1) { close(fd); return -3; }
-    if (!match) break;
-    if (line.len == addr.len)
-      if (!byte_diff(line.s,line.len,addr.s)) { close(fd); return 1; }
-  }
-
-  close(fd);
-  return 0;
-}
-
-struct strerr issub_err;
-
-int issub(userhost)
-char *userhost;
-{
-  int fdlock;
-  int r;
-
-  fdlock = open_append("lock");
-  if (fdlock == -1)
-    STRERR_SYS(-1,issub_err,"unable to open lock: ")
-  if (lock_ex(fdlock) == -1) {
-    close(fdlock);
-    STRERR_SYS(-1,issub_err,"unable to obtain lock: ")
-  }
-
-  r = doit(userhost);
-  close(fdlock);
-
-  if (r == -2) STRERR(-1,issub_err,"out of memory")
-  if (r == -3) STRERR_SYS3(-1,issub_err,"unable to read ",fn.s,": ")
-
-  return r;
-}
diff --git a/issub.c b/issub.c
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..8d7ce9e31472e69e1da9ff4ef9c02ca5c230303e
--- /dev/null
+++ b/issub.c
@@ -0,0 +1 @@
+sub_std/issub.c
\ No newline at end of file
diff --git a/log.c b/log.c
index 15e732e..bf3c7a3 100644 (file)
--- a/log.c
+++ b/log.c
@@ -6,14 +6,21 @@
 #include "fmt.h"
 #include "open.h"
 
+/* appends (not crash-proof) a line to "Log". The format is: */
+/* "timestamp event address[ comment]\n". address is free of ' ' */
+/* Unprintable chars are changed to '?'. Comment may have spaces */
+
 static substdio ss;
 static char buf[1];
 static char num[FMT_ULONG];
 static stralloc line = {0};
+static stralloc fn = {0};
 
-void log(event,addr)
+void log(dir,event,addr,comment)
+char *dir;
 char *event;
 char *addr;
+char *comment;
 {
   char ch;
   int fd;
@@ -26,9 +33,22 @@ char *addr;
     if ((ch < 33) || (ch > 126)) ch = '?';
     if (!stralloc_append(&line,&ch)) return;
   }
+  if (comment && *comment) {
+    if (!stralloc_cats(&line," ")) return;
+    while (ch = *comment++) {
+      if (ch == '\t')
+        ch = ' ';
+      else 
+        if ((ch < 32) || (ch > 126)) ch = '?';
+      if (!stralloc_append(&line,&ch)) return;
+    }
+  }
   if (!stralloc_cats(&line,"\n")) return;
 
-  fd = open_append("Log");
+  if (!stralloc_copys(&fn,dir)) return;
+  if (!stralloc_cats(&fn,"/Log")) return;
+  if (!stralloc_0(&fn)) return;
+  fd = open_append(fn.s);
   if (fd == -1) return;
   substdio_fdbuf(&ss,write,fd,buf,sizeof(buf));
   substdio_putflush(&ss,line.s,line.len);
diff --git a/logmsg.c b/logmsg.c
new file mode 120000 (symlink)
index 0000000..e540bca
--- /dev/null
+++ b/logmsg.c
@@ -0,0 +1 @@
+sub_std/logmsg.c
\ No newline at end of file
diff --git a/makehash.c b/makehash.c
new file mode 100644 (file)
index 0000000..efa4971
--- /dev/null
@@ -0,0 +1,136 @@
+/*Id:$*/
+/*Name:$*/
+
+#include "stralloc.h"
+#include "surf.h"
+#include "uint32.h"
+#include "makehash.h"
+
+typedef struct {
+  uint32 seed[32];
+  uint32 sum[8];
+  uint32 out[8];
+  uint32 in[12];
+  int todo;
+} surfpcs;
+
+#define SURFPCS_LEN 32
+
+static void surfpcs_init(s,k)
+surfpcs *s;
+uint32 k[32];
+{
+  int i;
+  for (i = 0;i < 32;++i) s->seed[i] = k[i];
+  for (i = 0;i < 8;++i) s->sum[i] = 0;
+  for (i = 0;i < 12;++i) s->in[i] = 0;
+  s->todo = 0;
+}
+
+static uint32 littleendian[8] = {
+  50462976, 117835012, 185207048, 252579084,
+  319951120, 387323156, 454695192, 522067228
+} ;
+#define end ((unsigned char *) littleendian)
+
+#define data ((unsigned char *) s->in)
+#define outdata ((unsigned char *) s->out)
+
+static void surfpcs_addlc(s,x,n)
+       /* modified from Dan's surfpcs_add by skipping ' ' & '\t' and */
+       /* case-independence */
+surfpcs *s;
+unsigned char *x;
+unsigned int n;
+{
+  register unsigned char ch;
+  int i;
+  while (n--) {
+    ch = *x++;
+    if (ch == ' ' || ch == '\t') continue;
+    if (ch >= 'A' && ch <= 'Z')
+      data[end[s->todo++]] = ch - 'A' + 'a';
+    else
+      data[end[s->todo++]] = ch;
+    if (s->todo == 32) {
+      s->todo = 0;
+      if (!++s->in[8])
+        if (!++s->in[9])
+          if (!++s->in[10])
+            ++s->in[11];
+      surf(s->out,s->in,s->seed);
+      for (i = 0;i < 8;++i)
+       s->sum[i] += s->out[i];
+    }
+  }
+}
+
+static void surfpcs_out(s,h)
+surfpcs *s;
+unsigned char h[32];
+{
+  int i;
+  surfpcs_addlc(s,".",1);
+  while (s->todo) surfpcs_addlc(s,"",1);
+  for (i = 0;i < 8;++i) s->in[i] = s->sum[i];
+  for (;i < 12;++i) s->in[i] = 0;
+  surf(s->out,s->in,s->seed);
+  for (i = 0;i < 32;++i) h[i] = outdata[end[i]];
+}
+
+void makehash(indata,inlen,hash)
+char *indata;
+unsigned int inlen;
+char *hash;
+       /* makes hash[COOKIE=20] from stralloc *indata, ignoring case and */
+       /* SPACE/TAB */
+{
+  unsigned char h[32];
+  surfpcs s;
+  uint32 seed[32];
+  int i;
+
+  for (i = 0;i < 32;++i) seed[i] = 0;
+  surfpcs_init(&s,seed);
+  surfpcs_addlc(&s,indata,inlen);
+  surfpcs_out(&s,h);
+  for (i = 0;i < 20;++i)
+    hash[i] = 'a' + (h[i] & 15);
+}
+
+static stralloc dummy = {0};
+
+void mkauthhash(s,len,h)
+char *s; unsigned int len; char *h;
+/* This is a string that should be the same for all messages from a given   */
+/* author. Doesn't have to be the real rfc822 address. We look for a '@'    */
+/* and grab everything up to the next '>', ' ', or ';'. We go back the same */
+/* way, then take everything up to the '@' or the first '-'. The latter     */
+/* avoids problems with posters that band their addresses.                  */
+{
+  unsigned int i,j,k,l;
+  register char ch;
+
+  j = k = l = 0;
+  i = byte_rchr(s,len,'@');
+  if (i < len) {               /* if not then i=sa->len, j=k=l=0 */
+    j = i;
+    while (++j < len) {                /* if not found, then j=sa->len */
+      ch = s[j];
+      if (ch == '>' || ch == ' ' || ch == ';') break;
+    }
+    k = i;
+    while (k > 0) {            /* k <= i */
+      ch = s[--k];
+      if (ch == '<' || ch == ' ' || ch == ';') break;
+    }
+    l = k;                     /* k <= l <= i; */
+    while (l < i && s[l] != '-') ++l;
+    if (!stralloc_copyb(&dummy,s + k, l - k)) die_nomem();
+    if (!stralloc_catb(&dummy,s + i, j - i)) die_nomem();
+    makehash(dummy.s,dummy.len,h);
+  } else                       /* use entire line if no '@' found */
+    makehash(s,len,h);
+}
+
+
diff --git a/makehash.h b/makehash.h
new file mode 100644 (file)
index 0000000..2d947e1
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef MAKEHASH_H
+#define MAKEHASH_H
+
+#define HASHLEN 20
+
+extern void makehash();
+extern void mkauthhash();
+
+#endif
+
diff --git a/mime.h b/mime.h
new file mode 100644 (file)
index 0000000..27626a0
--- /dev/null
+++ b/mime.h
@@ -0,0 +1,81 @@
+#ifndef MIME_H
+#define MIME_H
+
+#include "stralloc.h"
+
+extern void decodeQ();
+extern void decodeB();
+extern void encodeQ();
+extern void encodeB();
+extern void decodeHDR();
+extern void concatHDR();
+extern int unfoldHDR();
+
+#ifdef WITH_PROTO
+extern unsigned int author_name(char **,char *,unsigned int);
+#else
+extern unsigned int author_name();
+#endif
+
+/* Characters */
+#define ESC 0x1B
+#define SI 0x0F
+#define SO 0x0E
+/* iso-2022-jp back-to-ascii seq */
+#define TOASCII "\x1B(B"
+/* to JP. Last char [B|@] must be added */
+#define TOJP "\x1B$"
+
+/* iso-2022 SI sequence as string */
+#define TOSI "\x0F"
+/* SI \n SO */
+#define SI_LF_SO "\x0F\n\x0E"
+
+/* in these bit 0 determines the number of bytes (1 or 2) in ss2/ss3 codes */
+/* it is 2 for CN,1 for JP, and they are not used for KR  bit 3 for        */
+/* iso-2022 */
+#define CS_2022_MASK 0x08
+#define CS_2022_JP 0x08
+#define CS_2022_KR 0xA0
+#define CS_NONE 0
+#define CS_BAD 0xffff
+
+#define CS_2022_CN 0x09
+/* Other Chinese ones. bit 7 set means MSB of 2-byte seq. No ss2/ss3 consid*/
+#define CS_CN 0x10
+
+#define MIME_NONE 0
+#define MIME_APPLICATION_OCTETSTREAM 1
+#define MIME_MULTI 0x80
+#define MIME_MULTI_ALTERNATIVE 0x81
+#define MIME_MULTI_MIXED 0x82
+#define MIME_MULTI_DIGEST 0x83
+#define MIME_MULTI_SIGNED 0x84
+
+#define MIME_TEXT 0x40
+#define MIME_TEXT_PLAIN 0x41
+#define MIME_TEXT_HTML 0x42
+#define MIME_TEXT_ENRICHED 0x43
+#define MIME_TEXT_VCARD 0x44
+
+#define MIME_MESSAGE 0x20
+#define MIME_MESSAGE_RFC822 0x21
+
+#define CTENC_NONE 0
+#define CTENC_QP 1
+#define CTENC_BASE64 2
+
+/* this is a linked list of mime type info. */
+typedef struct {
+       int level;
+       unsigned int mimetype;
+       unsigned int ctenc;
+       unsigned int cs;                /* charset flag - expand later */
+       void *previous;
+       void *next;
+       stralloc boundary;
+       stralloc charset;
+       stralloc ctype;
+} mime_info;
+
+#endif
diff --git a/opensql.c b/opensql.c
new file mode 120000 (symlink)
index 0000000..5a27e82
--- /dev/null
+++ b/opensql.c
@@ -0,0 +1 @@
+sub_std/opensql.c
\ No newline at end of file
diff --git a/putsubs.c b/putsubs.c
new file mode 120000 (symlink)
index 0000000..79ffb49
--- /dev/null
+++ b/putsubs.c
@@ -0,0 +1 @@
+sub_std/putsubs.c
\ No newline at end of file
diff --git a/qmail-qmqpc.tar.gz b/qmail-qmqpc.tar.gz
new file mode 100644 (file)
index 0000000..4d70ee1
Binary files /dev/null and b/qmail-qmqpc.tar.gz differ
diff --git a/qmail-verh.tar.gz b/qmail-verh.tar.gz
new file mode 100644 (file)
index 0000000..8c8196b
Binary files /dev/null and b/qmail-verh.tar.gz differ
diff --git a/qmail.c b/qmail.c
index 7484331..1ee257b 100644 (file)
--- a/qmail.c
+++ b/qmail.c
@@ -6,18 +6,25 @@
 #include "fd.h"
 #include "qmail.h"
 #include "auto_qmail.h"
+#include "alloc.h"
+#include "stralloc.h"
+#include "idx.h"
 
-static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
+static char *binqqargs[2] = { PROG_QMAIL_QUEUE, 0 } ;
 
-int qmail_open(qq)
+int qmail_open(qq,sa)
 struct qmail *qq;
+stralloc *sa;
 {
   int pim[2];
   int pie[2];
+  unsigned i,j;
+  char **cpp;
 
+  qq->msgbytes = 0L;
   if (pipe(pim) == -1) return -1;
   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
+
   switch(qq->pid = vfork()) {
     case -1:
       close(pim[0]); close(pim[1]);
@@ -28,8 +35,24 @@ struct qmail *qq;
       close(pie[1]);
       if (fd_move(0,pim[0]) == -1) _exit(120);
       if (fd_move(1,pie[0]) == -1) _exit(120);
-      if (chdir(auto_qmail) == -1) _exit(120);
-      execv(*binqqargs,binqqargs);
+      if (chdir(auto_qmail) == -1) _exit(61);
+      j = 2;                           /* empty sa - qmqpc c control args */
+      if (sa) {                                /* count args */
+       for (i = 0; i + 1 < sa->len; i++) {
+         if (sa->s[i] == '\0') j++;
+       }                               /* make space */
+       if (!(cpp = (char **) alloc(j * sizeof (char *)))) _exit(51);
+       cpp[0] = PROG_QMAIL_QMQPC;
+       cpp[j - 1] = (char *) 0;
+       if (sa->len) cpp[1] = sa->s;
+       j = 2;
+       for (i = 0; i + 1 < sa->len; i++) {
+         if (sa->s[i] == '\0')
+           cpp[j++] = sa->s + i + 1;   /* build args */
+       }
+       execv(*cpp,cpp);
+      } else
+       execv(*binqqargs,binqqargs);
       _exit(120);
   }
 
@@ -53,11 +76,17 @@ void qmail_fail(qq) struct qmail *qq;
 void qmail_put(qq,s,len) struct qmail *qq; char *s; int len;
 {
   if (!qq->flagerr) if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1;
+  qq->msgbytes += len;
 }
 
 void qmail_puts(qq,s) struct qmail *qq; char *s;
 {
-  if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1;
+  register int len;
+  if (!qq->flagerr) {
+    len = str_len(s);
+    if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1;
+  }
+  qq->msgbytes += len;
 }
 
 void qmail_from(qq,s) struct qmail *qq; char *s;
@@ -77,27 +106,49 @@ void qmail_to(qq,s) struct qmail *qq; char *s;
   qmail_put(qq,"",1);
 }
 
-int qmail_close(qq)
+char *qmail_close(qq)
 struct qmail *qq;
 {
   int wstat;
+  int exitcode;
 
   qmail_put(qq,"",1);
   if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
   close(qq->fde);
 
-  if (wait_pid(&wstat,qq->pid) != qq->pid) return QMAIL_WAITPID;
-  if (wait_crashed(wstat)) return QMAIL_CRASHED;
-  switch(wait_exitcode(wstat)) {
-    case 0: if (qq->flagerr) return QMAIL_BUG; return 0;
-    case 112: return QMAIL_USAGE;
-    case 115: return QMAIL_TOOLONG;
-    case 103: case 104: case 105: case 106: case 108: return QMAIL_SYS;
-    case 121: return QMAIL_READ;
-    case 122: return QMAIL_WRITE;
-    case 123: return QMAIL_NOMEM;
-    case 124: return QMAIL_TIMEOUT;
-    case 120: return QMAIL_EXECSOFT;
-    default: /* 101 or 102 */ return QMAIL_BUG;
+  if (wait_pid(&wstat,qq->pid) != qq->pid)
+    return "Zqq waitpid surprise (#4.3.0)";
+  if (wait_crashed(wstat))
+    return "Zqq crashed (#4.3.0)";
+  exitcode = wait_exitcode(wstat);
+
+  switch(exitcode) {
+    case 115: /* compatibility */
+    case 11: return "Denvelope address too long for qq (#5.1.3)";
+    case 31: return "Dmail server permanently rejected message (#5.3.0)";
+    case 51: return "Zqq out of memory (#4.3.0)";
+    case 52: return "Zqq timeout (#4.3.0)";
+    case 53: return "Zqq write error or disk full (#4.3.0)";
+    case 0: if (!qq->flagerr) return ""; /* fall through */
+    case 54: return "Zqq read error (#4.3.0)";
+    case 55: return "Zqq unable to read configuration (#4.3.0)";
+    case 56: return "Zqq trouble making network connection (#4.3.0)";
+    case 61: return "Zqq trouble in home directory (#4.3.0)";
+    case 63:
+    case 64:
+    case 65:
+    case 66:
+    case 62: return "Zqq trouble creating files in queue (#4.3.0)";
+    case 71: return "Zmail server temporarily rejected message (#4.3.0)";
+    case 72: return "Zconnection to mail server timed out (#4.4.1)";
+    case 73: return "Zconnection to mail server rejected (#4.4.1)";
+    case 74: return "Zcommunication with mail server failed (#4.4.2)";
+    case 91: /* fall through */
+    case 81: return "Zqq internal bug (#4.3.0)";
+    case 120: return "Zunable to exec qq (#4.3.0)";
+    default:
+      if ((exitcode >= 11) && (exitcode <= 40))
+       return "Dqq permanent problem (#5.3.0)";
+      return "Zqq temporary problem (#4.3.0)";
   }
 }
diff --git a/qmail.h b/qmail.h
index 7864ea1..8906ab0 100644 (file)
--- a/qmail.h
+++ b/qmail.h
@@ -2,35 +2,39 @@
 #define QMAIL_H
 
 #include "substdio.h"
+#include "stralloc.h"
 
 struct qmail {
   int flagerr;
   unsigned long pid;
+  unsigned long msgbytes;
   int fdm;
   int fde;
   substdio ss;
   char buf[1024];
 } ;
 
+#ifdef WITH_PROTO
+
+extern int qmail_open(struct qmail *, stralloc *);
+extern void qmail_put(struct qmail *, char *, int);
+extern void qmail_puts(struct qmail *, char *);
+extern void qmail_from(struct qmail *, char *);
+extern void qmail_to(struct qmail *, char *);
+extern void qmail_fail(struct qmail *);
+extern char *qmail_close(struct qmail *);
+extern unsigned long qmail_qp(struct qmail *);
+
+#else
+
 extern int qmail_open();
 extern void qmail_put();
 extern void qmail_puts();
 extern void qmail_from();
 extern void qmail_to();
 extern void qmail_fail();
-extern int qmail_close();
+extern char *qmail_close();
 extern unsigned long qmail_qp();
-
-#define QMAIL_WAITPID -2
-#define QMAIL_CRASHED -3
-#define QMAIL_USAGE -4
-#define QMAIL_BUG -5
-#define QMAIL_SYS -6
-#define QMAIL_READ -7
-#define QMAIL_WRITE -8
-#define QMAIL_NOMEM -9
-#define QMAIL_EXECSOFT -11
-#define QMAIL_TIMEOUT -13
-#define QMAIL_TOOLONG -14
+#endif
 
 #endif
diff --git a/searchlog.c b/searchlog.c
new file mode 120000 (symlink)
index 0000000..c6f1952
--- /dev/null
@@ -0,0 +1 @@
+sub_std/searchlog.c
\ No newline at end of file
diff --git a/sub_mysql/README b/sub_mysql/README
new file mode 100644 (file)
index 0000000..28d028e
--- /dev/null
@@ -0,0 +1,153 @@
+$Id: README,v 1.3 1999/02/20 20:05:19 lindberg Exp $
+$Name: ezmlm-idx-040 $
+INFORMATION ON BUILDING/USING EZMLM WITH MYSQL SUPPORT
+
+(c) 1999,      Frederik Lindberg,
+               lindberg@id.wustl.edu
+               You may use under GPL.
+
+For information on MySQL, see http://www.tcx.se.
+
+Most of this information is available in FAQ.idx.
+
+If you are interested in contributing/testing a subscriber db interface
+for another SQL server, please see sub_std/README and the routines here,
+and contact lindberg@id.wustl.edu (it may already be in process). See end
+of this file for other ways to contribute.
+
+conf-mysql must be edited to reflect your system. On many systems, you
+also need to include ``-lsocket'', as well as change the paths to the
+/usr/local equivalents. For the i386.rpm-based systems, you need at
+least MySQL-devel to build the files. Look at your mysql docs for more info.
+
+TABLES USED FOR (My)SQL SUPPORT
+
+The basic philosophy is that the database can be on any host (if you use
+SENDER restrictions, connectivity to the main host is more important than
+to the sublists), and you choose the database and "table root" names. The
+default database is ``ezmlm'' and the default table root is ``list''. Each
+list has a separate table root. Any number of lists can share a database.
+
+The main list address table is named with the table root only, others have
+that name with various suffixes. In the following ``list'' is used as the
+table root.
+
+ADDRESS TABLES
+list           subscriber addresses
+list_slog      subscriber address log
+list_allow     subscriber aliases for posts on SENDER checked lists.
+list_allow_slog        subscriber log for list_allow
+list_deny      blacklisted addresses for posts on SENDER checked lists.
+list_deny_slog log for list_deny.
+list_mod       moderator addresses
+list_mod_slog  log for list_mod
+list_digest    subscriber log for digest list.
+list_digest_slog       log for list_digest
+
+MESSAGE LOGGING TABLES
+list_cookie    message cookie table for main list
+list_mlog      message logging table for main list
+list_digest_cookie     message cookie table for digest list
+list_digest_mlog       message logging table for digest list
+
+SUBLIST SPLIT TABLES
+list_name      sublist split table for main list
+list_digest_name       sublist split table for digest list.
+
+
+ezmlm-mktab(1) is a script that outputs the table definintions. Look at
+the output for a detailed field description.
+
+The address tables contain (address,domain,hash,h,num). For normal
+lists only the address field is used. For main->sublist clusters, the other
+fields are used for load splitting. The domain is the first up to 3 characters
+of the last part of the domain name. The hash is a address hash [0-52] differnt
+from the one used by ezmlm for splitting within DIR/subscribers. When using
+the address field as a primary key, the size of the index was unreasonable.
+Therefore, ``num'' is used as a dummy primary key, and ``h'' (a 32 bit hash
+of the address) is used as an index. This markedly speeds up (un)sub with
+large (>30,000 rows) subscriber tables.
+
+The *_slog tables contain the same info as DIR/Log, i.e. address, timestamp,
+entry-type, entry-direction, and fromline. The entry-type is the first letter
+of the type of entry (probe, manual, `` '' for normal), entry-direction is
+``+'' for addition, ``-'' for removal. Fromline is the From: header contents
+taken from the subscribe confirm message or from ezmlm-sub (if used with -n).
+It is blank for all address removals, and may be blank also for additions. It
+is used by the list-log.xx command. It is trivial to JOIN this table with the
+address table to get e.g. subsciber names, subscription dates, etc. These
+tables also have the 32-bit hash ``h'' as an index. Joins should be done on
+``h'' as well as ``address'' for better performance.
+
+The *_cookie tables contain message number, timestamp, and cookie. For each
+message a pseudo-random cookie is generated that is ``impossible'' to guess
+beforehand. For lists with sublists, this is used as basic authentication,
+i.e. the sublist will refuse to process a message that doesn't contain the
+correct cookie or that the sublist has already successfully processed.
+
+The *_mlog tables contain log entries from main and sublists. These are
+timestamp, listno, done. Listno is the lowest listnumber for an active list
+entry with the name of this sublist as looked up in the *_name table. Done
+is -1 for bounce, 0 for arrived, 1 for finished processing, and 2 for receipt
+received. The routines are set up so that only the first attempt for each
+combination (listno,code) is logged.
+
+The *_name tables contain listno,name,domain,hash_lo,hash_hi,msgnum_lo,
+msgnum_hi,notuse. Listno is auto_increment and unique. Name is the name of the
+sublist. domain is the last up to 3 characters of the top domain name for
+addresses served by this list (default = ''). It is is '', the list servers
+all_domains_that_are_not_served_by_another list (in addition to domain '').
+Of the addresses that match the domain criterion, the list serves the subset
+with hash between hash_lo and hash_hi (defaults 0, 52). Any entry is ingnored if
+notuse != 0 OR the current message number is not between msgnum_lo and
+msgnum_hi.
+
+For normal lists that are not distributed (i.e. they are a single list),
+entries in the *_name tables are not needed and logging is not very
+relevant.
+
+For most lists, the only addresses that are stored in the SQL database are
+the subscribers of list and digest, and the ``allow'' aliases. It is NOT
+normally advisable to store moderator addresses there, since they are
+needed only at the main list and secrecy is more important. ``Deny'' addresses
+are few and again only needed at the main list. ``Allow'' are put in the
+SQL database when using the default ezmlmrc file only to make all relevant
+addresses manipulatable via the SQL server. The other tables are created, in
+case they are  wanted (the cost for having them as empty table is zero). The
+basedir/sql file is the decision point. If it exists, an SQL table is used;
+if not a local ezmlm db is used.
+
+CONTRIBUTIONS REQESTED
+
+I would be very grateful if there are users out there willing to do any of
+the following and contribute it to this package. Please check with me first
+(lindberg@id.wustl.edu), as the project may already be in progress/done.
+
+1. Interfaces for other SQL servers. Oracle, SyBase, ...
+
+2. A GUI admin utility to add/remove/manipulate the sublist split, essentially
+   by modifying list_[digest_]name in a safe way. Ideally WWW if it can be
+   done securely. If you use some standard interface (JDBC/DBD) it would be
+   useful also with other SQL severs. This could even be an Access program
+   using ODBC, although writing it for a platform running qmail/ezmlm makes
+   most sense.
+
+3. a WWW GUI that allows users to subscribe/unsubscribe in a safe way. A random
+   password would be created the first time and stored in a new address->pw
+   table and mailed to the subscriber address. With that password, the user
+   would be able to [un]subscribe to lists, edit the name (for compatibility
+   implemented by adding a subscribe line to list_[digest]slog). Add/remove
+   aliases. Ideally, it should also allow searching by subscriber name. This
+   would search *_slog.fromline. If less that 'x' alternatives are found, the
+   user would be presented with names (not addresses), allowing the user to
+   cause the subscription name and password to be sent to the respective
+   subscription address. With that info, the subscriber can then unsubscribe,
+   even if s/he has forgotten the subscription address. It is complicated
+   slightly by the fact that ``fromline'' is the crude line and needs to be
+   rfc822 parsed. Again, use of a standard interface is encouraged to make it
+   compatible also with other SQL servers.
+
+The aim of all this is to make it easy to use ezmlm to run very large lists,
+easy to set up sites that handle subscriber interaction, archive access, etc,
+and hopefully easier to integrate many ezmlm as done by some WWW sites today.
+
diff --git a/sub_mysql/checktag.c b/sub_mysql/checktag.c
new file mode 100644 (file)
index 0000000..05de3ce
--- /dev/null
@@ -0,0 +1,104 @@
+/*$Id: checktag.c,v 1.11 1999/11/10 04:08:27 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "scan.h"
+#include "fmt.h"
+#include "cookie.h"
+#include "makehash.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include <mysql.h>
+
+static stralloc key = {0};
+static stralloc line = {0};
+static stralloc quoted = {0};
+static char strnum[FMT_ULONG];
+static char newcookie[COOKIE];
+
+char *checktag (dir,num,listno,action,seed,hash)
+/* reads dir/sql. If not present, returns success (NULL). If dir/sql is    */
+/* present, checks hash against the cookie table. If match, returns success*/
+/* (NULL), else returns "". If error, returns error string. */
+
+char *dir;                             /* the db base dir */
+unsigned long num;                     /* message number */
+unsigned long listno;                  /* bottom of range => slave */
+char *action;
+char *seed;                            /* cookie base */
+char *hash;                            /* cookie */
+{
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  char *table = (char *) 0;
+  char *r;
+
+  if ((r = opensql(dir,&table))) {
+    if (*r) return r;
+    if (!seed) return (char *) 0;              /* no data - accept */
+
+    strnum[fmt_ulong(strnum,num)] = '\0';      /* message nr ->string*/
+
+    switch(slurp("key",&key,32)) {
+      case -1:
+       return ERR_READ_KEY;
+      case 0:
+       return ERR_NOEXIST_KEY;
+    }
+
+    cookie(newcookie,key.s,key.len,strnum,seed,action);
+    if (byte_diff(hash,COOKIE,newcookie)) return "";
+    else return (char *) 0;
+
+  } else {
+
+/* SELECT msgnum FROM table_cookie WHERE msgnum=num and cookie='hash' */
+/* succeeds only is everything correct. 'hash' is quoted since it is  */
+/*  potentially hostile. */
+    if (listno) {                      /* only for slaves */
+      if (!stralloc_copys(&line,"SELECT listno FROM ")) return ERR_NOMEM;
+      if (!stralloc_cats(&line,table)) return ERR_NOMEM;
+      if (!stralloc_cats(&line,"_mlog WHERE listno=")) return ERR_NOMEM;
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,listno)))
+       return ERR_NOMEM;
+      if (!stralloc_cats(&line," AND msgnum=")) return ERR_NOMEM;
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,num))) return ERR_NOMEM;
+      if (!stralloc_cats(&line," AND done > 3")) return ERR_NOMEM;
+      if (mysql_real_query((MYSQL *) psql,line.s,line.len) != 0)
+       return mysql_error((MYSQL *) psql);                     /* query */
+      if (!(result = mysql_use_result((MYSQL *) psql)))                /* use result */
+       return mysql_error((MYSQL *) psql);
+      if ((row = mysql_fetch_row(result)))
+       return "";                                      /*already done */
+      else                                             /* no result */
+        if (!mysql_eof(result))
+         return mysql_error((MYSQL *) psql);
+      mysql_free_result(result);                       /* free res */
+    }
+
+    if (!stralloc_copys(&line,"SELECT msgnum FROM ")) return ERR_NOMEM;
+    if (!stralloc_cats(&line,table)) return ERR_NOMEM;
+    if (!stralloc_cats(&line,"_cookie WHERE msgnum=")) return ERR_NOMEM;
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,num))) return ERR_NOMEM;
+    if (!stralloc_cats(&line," and cookie='")) return ERR_NOMEM;
+    if (!stralloc_ready(&quoted,COOKIE * 2 + 1)) return ERR_NOMEM;
+    quoted.len = mysql_escape_string(quoted.s,hash,COOKIE);
+    if (!stralloc_cat(&line,&quoted)) return ERR_NOMEM;
+    if (!stralloc_cats(&line,"'")) return ERR_NOMEM;
+
+    if (mysql_real_query((MYSQL *) psql,line.s,line.len) != 0) /* select */
+       return mysql_error((MYSQL *) psql);
+    if (!(result = mysql_use_result((MYSQL *) psql)))
+       return mysql_error((MYSQL *) psql);
+    if (!mysql_fetch_row(result)) {
+    if (!mysql_eof(result))            /* some error occurred */
+       return mysql_error((MYSQL *) psql);
+      mysql_free_result(result);       /* eof => query ok, but null result*/
+      return "";                       /* not parent => perm error */
+    }
+    mysql_free_result(result);         /* success! cookie matches */
+    if (listno)
+      (void) logmsg(dir,num,listno,0L,3);      /* non-ess mysql logging */
+    return (char *)0;
+  }
+}
diff --git a/sub_mysql/conf-sqlcc b/sub_mysql/conf-sqlcc
new file mode 100644 (file)
index 0000000..45507d5
--- /dev/null
@@ -0,0 +1,4 @@
+-I/usr/include/mysql
+
+# the top line will be used when compiling. Edit to reflect your mysql
+# installation. This is for a the MySQL-3.22.10.i386 rpm.
diff --git a/sub_mysql/conf-sqlld b/sub_mysql/conf-sqlld
new file mode 100644 (file)
index 0000000..0e0a69f
--- /dev/null
@@ -0,0 +1,4 @@
+-L/usr/lib/mysql -lmysqlclient -lnsl -lm
+
+# the top line will be used when linking. Edit to reflect your mysql
+# installation. This is for a the MySQL-3.22.10.i386 rpm.
diff --git a/sub_mysql/ezmlm-mktab b/sub_mysql/ezmlm-mktab
new file mode 100755 (executable)
index 0000000..8fea02b
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/sh
+# Simple script to generate input to mysql to generate tables for a list
+# All tables are created, even though it is not advisable to put e.g. the
+# moderator table in the SQL database nor is it very useful to put the
+# blacklist/deny table there. The subscriber lists for the main and digest
+# lists should be there, and it's reasonable to put the "extra" list there
+# if used.
+ECHO='echo'
+CAT='cat'
+CUT='cut'
+
+
+CREATE='y'
+DROP='n'
+TROOT='list'
+# size of std cookie
+COOKIE='20'
+
+                                       # not everyone has getopt :-(
+while [ "`${ECHO} "$1" | ${CUT} -c1`" = "-" ]; do
+       case "$1" in
+               -c)     CREATE='y'; shift;;
+               -C)     CREATE='n'; shift;;
+               -d)     DROP='y'; shift;;
+               -D)     DROP='n'; shift;;
+               -cd|-dc)        CREATE='y'; DROP='y'; shift;;
+               -cD|-Dc)        CREATE='y'; DROP='n'; shift;;
+               -Cd|-dC)        CREATE='n'; DROP='y'; shift;;
+               -CD|-DC)        CREATE='n'; DROP='n'; shift;;
+               --)     shift; break;;
+               *)      echo "usage: emzlm-mktab [-cCdD] table_toot"; exit 100;;
+       esac
+done
+
+[ ! -z "$1" ] && TROOT="$1";
+
+
+if [ "$DROP" = "y" ]; then
+  cat <<EOF
+
+/* drop old tables. This may fail unless you use mysql -f */
+/* Usage: */
+/* ezmlm-mktab [-d] troot | mysql -hhost -uuserid -ppw datab -f */
+
+DROP TABLE ${TROOT};
+DROP TABLE ${TROOT}_slog;
+DROP TABLE ${TROOT}_digest;
+DROP TABLE ${TROOT}_digest_slog;
+DROP TABLE ${TROOT}_mod;
+DROP TABLE ${TROOT}_mod_slog;
+DROP TABLE ${TROOT}_allow;
+DROP TABLE ${TROOT}_allow_slog;
+DROP TABLE ${TROOT}_deny;
+DROP TABLE ${TROOT}_deny_slog;
+/* eliminated name table - no need */
+DROP TABLE ${TROOT}_cookie;
+DROP TABLE ${TROOT}_mlog;
+DROP TABLE ${TROOT}_digest_cookie;
+DROP TABLE ${TROOT}_digest_mlog;
+
+EOF
+
+fi
+
+if [ $CREATE = 'y' ]; then
+  cat << EOF
+
+/* Main address table */
+/* Need varchar. Domain = 3 chars => fixed length, as opposed to varchar */
+/* Always select on domain and hash, so that one index should do         */
+/* primary key(address) is very inefficient for MySQL. */
+/* MySQL tables do not need a primary key. Other RDBMS require one. For  */
+/* the log tables, just add an INT AUTO_INCREMENT. For the address table,*/
+/* do that or use address as a primary key. */
+
+create TABLE ${TROOT} (
+       hash            TINYINT UNSIGNED NOT NULL,
+       address         VARCHAR(255) NOT NULL,
+       INDEX h (hash),
+       INDEX a (address(12)));
+
+/* Subscription log table. No addr idx to make insertion fast, since that is */
+/* almost the only thing we do with this table */
+create TABLE ${TROOT}_slog (
+       tai             TIMESTAMP,
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR(1) NOT NULL,
+       etype           CHAR(1) NOT NULL,       
+       INDEX (tai));
+
+/* digest list table */
+create TABLE ${TROOT}_digest (
+       hash            TINYINT UNSIGNED NOT NULL,
+       address         VARCHAR(255) NOT NULL,
+       INDEX h (hash),
+       INDEX a (address(12)));
+
+/* digest list subscription log */
+create TABLE ${TROOT}_digest_slog (
+       tai             TIMESTAMP,
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR(1) NOT NULL,
+       etype           CHAR(1) NOT NULL,       
+       INDEX (tai));
+
+/* moderator addresses */
+create TABLE ${TROOT}_mod (
+       hash            TINYINT UNSIGNED NOT NULL,
+       address         VARCHAR(255) NOT NULL,
+       INDEX h(hash),
+       INDEX a(address(12)));
+
+/* moderator subscription log */
+create TABLE ${TROOT}_mod_slog (
+       tai             TIMESTAMP,
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR(1) NOT NULL,
+       etype           CHAR(1) NOT NULL,       
+       INDEX (tai));
+
+/* "allow" address table */
+create TABLE ${TROOT}_allow (
+       hash            TINYINT UNSIGNED NOT NULL,
+       address         VARCHAR(255) NOT NULL,
+       INDEX h(hash),
+       INDEX a(address(12)));
+
+/* extra address table log */
+create TABLE ${TROOT}_allow_slog (
+       tai             TIMESTAMP,
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR(1) NOT NULL,
+       etype           CHAR(1) NOT NULL,       
+       INDEX (tai));
+
+/* blacklist address table */
+create TABLE ${TROOT}_deny (
+       hash            TINYINT UNSIGNED NOT NULL,
+       address         VARCHAR(255) NOT NULL,
+       INDEX h(hash),
+       INDEX a(address(12)));
+
+/* blacklist subscription log */
+create TABLE ${TROOT}_deny_slog (
+       tai             TIMESTAMP,
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR(1) NOT NULL,
+       etype           CHAR(1) NOT NULL,       
+       INDEX (tai));
+
+/* main list inserts a cookie here. Sublists check it */
+CREATE TABLE ${TROOT}_cookie (
+       msgnum          INTEGER UNSIGNED NOT NULL,
+       tai             TIMESTAMP NOT NULL,
+       cookie          CHAR($COOKIE) NOT NULL,
+       chunk           TINYINT UNSIGNED NOT NULL DEFAULT 0,
+       bodysize        INTEGER UNSIGNED NOT NULL DEFAULT 0,
+       PRIMARY KEY (msgnum));
+
+/* main and sublist log here when the message is done */
+/* done=0 for arrived, done=4 for sent, 5 for receit. */
+/* tai reflects last change */
+CREATE TABLE ${TROOT}_mlog (
+       msgnum          INTEGER UNSIGNED NOT NULL,
+       listno          INTEGER UNSIGNED NOT NULL,
+       tai             TIMESTAMP,
+       subs            INTEGER UNSIGNED NOT NULL DEFAULT 0,
+       done            TINYINT NOT NULL DEFAULT 0,
+       PRIMARY KEY listmsg (listno,msgnum,done));
+
+/* ezmlm-get when creating a digests inserts a cookie here. Sublists check it */
+CREATE TABLE ${TROOT}_digest_cookie (
+       msgnum          INTEGER UNSIGNED NOT NULL,
+       tai             TIMESTAMP NOT NULL,
+       cookie          CHAR($COOKIE) NOT NULL,
+       chunk           TINYINT UNSIGNED NOT NULL DEFAULT 0,
+       bodysize        INTEGER UNSIGNED NOT NULL DEFAULT 0,
+       PRIMARY KEY (msgnum));
+
+/* ezmlm-get and digest sublists log here when the message is done */
+/* done=0 for arrived, done=4 for sent, 5 for receit. */
+/* tai reflects last change */
+CREATE TABLE ${TROOT}_digest_mlog (
+       msgnum          INTEGER UNSIGNED NOT NULL,
+       listno          INTEGER UNSIGNED NOT NULL,
+       tai             TIMESTAMP,
+       subs            INT UNSIGNED NOT NULL DEFAULT 0,
+       done            TINYINT NOT NULL DEFAULT 0,
+       PRIMARY KEY listmsg (listno,msgnum,done));
+
+EOF
+
+fi
+exit 0
+
diff --git a/sub_mysql/issub.c b/sub_mysql/issub.c
new file mode 100644 (file)
index 0000000..a2b760c
--- /dev/null
@@ -0,0 +1,174 @@
+/*$Id: issub.c,v 1.16 1999/12/11 03:04:19 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "strerr.h"
+#include "error.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "errtxt.h"
+#include <mysql.h>
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static stralloc addr = {0};
+static stralloc lcaddr = {0};
+static stralloc line = {0};
+static stralloc quoted = {0};
+static stralloc fn = {0};
+static substdio ss;
+static char ssbuf[512];
+static char szh[FMT_ULONG];
+
+char *issub(dbname,userhost,tab,fatal)
+/* Returns (char *) to match if userhost is in the subscriber database     */
+/* dbname, 0 otherwise. dbname is a base directory for a list and may NOT  */
+/* be NULL        */
+/* NOTE: The returned pointer is NOT VALID after a subsequent call to issub!*/
+
+char *dbname;          /* directory to basedir */
+char *userhost;
+char *tab;             /* override table name */
+char *fatal;
+
+{
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  char *ret;
+  char *table;
+  unsigned long *lengths;
+
+  int fd;
+  unsigned int j;
+  uint32 h,lch;
+  char ch,lcch;
+  int match;
+
+  table = tab;
+  if ((ret = opensql(dbname,&table))) {
+    if (*ret) strerr_die2x(111,fatal,ret);
+                                               /* fallback to local db */
+
+    if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
+    if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
+
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len) return 0;
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+    if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+    case_lowerb(lcaddr.s + 1,j - 1);   /* totally lc version of addr */
+
+    h = 5381;
+    lch = h;                   /* make hash for both for backwards comp */
+    for (j = 0;j < addr.len;++j) {     /* (lcaddr.len == addr.len) */
+      h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+      lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+    }
+    ch = 64 + (h % 53);
+    lcch = 64 + (lch % 53);
+
+    if (!stralloc_0(&addr)) die_nomem(fatal);
+    if (!stralloc_0(&lcaddr)) die_nomem(fatal);
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
+    if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
+    if (!stralloc_0(&fn)) die_nomem(fatal);
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
+    } else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1)
+          strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+        if (!match) break;
+        if (line.len == lcaddr.len)
+          if (!case_diffb(line.s,line.len,lcaddr.s))
+            { close(fd); return line.s+1; }
+      }
+
+      close(fd);
+    }
+       /* here if file not found or (file found && addr not there) */
+
+    if (ch == lcch) return 0;
+
+       /* try case sensitive hash for backwards compatibility */
+    fn.s[fn.len - 2] = ch;
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
+      return 0;
+    }
+    substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+    for (;;) {
+      if (getln(&ss,&line,&match,'\0') == -1)
+        strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+      if (!match) break;
+      if (line.len == addr.len)
+        if (!case_diffb(line.s,line.len,addr.s))
+          { close(fd); return line.s+1; }
+    }
+
+    close(fd);
+
+    return 0;
+  } else {                                             /* SQL version  */
+       /* SELECT address FROM list WHERE address = 'userhost' AND hash */
+       /* BETWEEN 0 AND 52. Without the hash restriction, we'd make it */
+       /* even easier to defeat. Just faking sender to the list name would*/
+       /* work. Since sender checks for posts are bogus anyway, I don't */
+       /* know if it's worth the cost of the "WHERE ...". */
+
+    if (!stralloc_copys(&addr,userhost)) die_nomem(fatal);
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len) return 0;
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+
+    if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line," WHERE address = '")) die_nomem(fatal);
+    if (!stralloc_ready(&quoted,2 * addr.len + 1)) die_nomem(fatal);
+    if (!stralloc_catb(&line,quoted.s,
+       mysql_escape_string(quoted.s,userhost,addr.len))) die_nomem(fatal);
+    if (!stralloc_cats(&line,"'"))
+               die_nomem(fatal);
+    if (mysql_real_query((MYSQL *) psql,line.s,line.len))      /* query */
+               strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    if (!(result = mysql_use_result((MYSQL *) psql)))
+               strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    row = mysql_fetch_row(result);
+    ret = (char *) 0;
+    if (!row) {                /* we need to return the actual address as other */
+                       /* dbs may accept user-*@host, but we still want */
+                       /* to make sure to send to e.g the correct moderator*/
+                       /* address. */
+      if (!mysql_eof(result))
+               strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    } else {
+      if (!(lengths = mysql_fetch_lengths(result)))
+               strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if (!stralloc_copyb(&line,row[0],lengths[0])) die_nomem(fatal);
+      if (!stralloc_0(&line)) die_nomem(fatal);
+      ret = line.s;
+      while ((row = mysql_fetch_row(result))); /* maybe not necessary */
+      mysql_free_result(result);
+    }
+    return ret;
+  }
+}
diff --git a/sub_mysql/logmsg.c b/sub_mysql/logmsg.c
new file mode 100644 (file)
index 0000000..ca601e8
--- /dev/null
@@ -0,0 +1,54 @@
+/*$Id: logmsg.c,v 1.10 1999/11/10 04:08:27 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "errtxt.h"
+#include <mysql.h>
+#include <mysqld_error.h>
+
+static stralloc logline = {0};
+static char strnum[FMT_ULONG];
+
+char *logmsg(dir,num,listno,subs,done)
+/* creates an entry for message num and the list listno and code "done". */
+/* Returns NULL on success, "" if dir/sql was not found, and the error   */
+/* string on error.   NOTE: This routine does nothing for non-sql lists! */
+char *dir;
+unsigned long num;
+unsigned long listno;
+unsigned long subs;
+int done;
+{
+  char *table = (char *) 0;
+  char *ret;
+
+  if ((ret = opensql(dir,&table))) {
+    if (*ret)
+      return ret;
+    else
+      return (char *) 0;       /* no SQL => success */
+  }
+  if (!stralloc_copys(&logline,"INSERT INTO ")) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,table)) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,"_mlog (msgnum,listno,subs,done) VALUES ("))
+       return ERR_NOMEM;
+  if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,num))) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,",")) return ERR_NOMEM;
+  if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,listno)))
+       return ERR_NOMEM;
+  if (!stralloc_cats(&logline,",")) return ERR_NOMEM;
+  if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,subs))) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,",")) return ERR_NOMEM;
+  if (done < 0) {
+    done = - done;
+    if (!stralloc_append(&logline,"-")) return ERR_NOMEM;
+  }
+  if (!stralloc_catb(&logline,strnum,fmt_uint(strnum,done))) return ERR_NOMEM;
+  if (!stralloc_append(&logline,")")) return ERR_NOMEM;
+
+  if (mysql_real_query((MYSQL *) psql,logline.s,logline.len))  /* log query */
+    if (mysql_errno((MYSQL *) psql) != ER_DUP_ENTRY)   /* ignore dups */
+       return mysql_error((MYSQL *) psql);
+  return (char *) 0;
+}
diff --git a/sub_mysql/opensql.c b/sub_mysql/opensql.c
new file mode 100644 (file)
index 0000000..4b903a9
--- /dev/null
@@ -0,0 +1,112 @@
+/*$Id: opensql.c,v 1.6 1999/11/14 21:29:29 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include <mysql.h>
+
+static stralloc myp = {0};
+static stralloc ers = {0};
+static stralloc fn = {0};
+static stralloc ourdb = {0};
+static char *ourtable = (char *) 0;
+
+char *opensql(dbname,table)
+/* reads the file dbname/sql, and if the file exists, parses it into the    */
+/* components. The string should be host:port:user:pw:db:table.         If  */
+/* the file does not exists, returns "". On success returns NULL. On error  */
+/* returns error string for temporary error. If table is NULL it is         */
+/* left alone. If *table is not null, it overrides the table in the sql     */
+/* file. If we already opended dbname the cached info is used, rather than  */
+/* rereading the file. Note that myp is static and all pointers point to it.*/
+char *dbname;  /* database directory */
+char **table;  /* table root_name */
+
+{
+  char *host = (char *) 0;
+  unsigned long port = 0L;
+  char *db = "ezmlm";          /* default */
+  char *user = (char *) 0;
+  char *pw = (char *) 0;
+  unsigned int j;
+  char *cp;
+
+  if (!stralloc_copys(&fn,dbname)) return ERR_NOMEM;
+  if (fn.len == ourdb.len && !str_diffn(ourdb.s,fn.s,fn.len)) {
+    if (table) {
+      if (*table) ourtable = *table;
+      else *table = ourtable;
+    }
+    return 0;
+  }
+  if (!stralloc_cats(&fn,"/sql")) return ERR_NOMEM;
+  if (!stralloc_0(&fn)) return ERR_NOMEM;
+               /* host:port:db:table:user:pw:name */
+
+  myp.len = 0;
+  switch (slurp(fn.s,&myp,128)) {
+       case -1:        if (!stralloc_copys(&ers,ERR_READ)) return ERR_NOMEM;
+                       if (!stralloc_cat(&ers,&fn)) return ERR_NOMEM;
+                       if (!stralloc_0(&ers)) return ERR_NOMEM;
+                       return ers.s;
+       case 0: return "";
+  }
+  if (!stralloc_copy(&ourdb,&fn)) return ERR_NOMEM;
+  if (!stralloc_append(&myp,"\n")) return ERR_NOMEM;
+  for (j=0; j< myp.len; ++j) {
+    if (myp.s[j] == '\n') { myp.s[j] = '\0'; break; }
+  }
+                                               /* get connection parameters */
+  if (!stralloc_0(&myp)) return ERR_NOMEM;
+  host = myp.s;
+  if (myp.s[j = str_chr(myp.s,':')]) {
+    cp = myp.s + j++;
+    *(cp++) = '\0';
+    scan_ulong(cp,&port);
+    if (myp.s[j += str_chr(myp.s+j,':')]) {
+      j++;
+      user = myp.s + j;
+      if (myp.s[j += str_chr(myp.s+j,':')]) {
+        pw = myp.s + j++;
+       *(pw++) = '\0';
+       if (myp.s[j += str_chr(myp.s+j,':')]) {
+          db = myp.s + j++;
+         *(db++) = '\0';
+         if (myp.s[j += str_chr(myp.s+j,':')]) {
+           ourtable = myp.s + j++;
+           *(ourtable++) = '\0';
+         }
+       }
+      }
+    }
+  }
+  if (host && !*host) host = (char *) 0;
+  if (user && !*user) user = (char *) 0;
+  if (pw && !*pw) pw = (char *) 0;
+  if (db && !*db) db = (char *) 0;
+  if (ourtable && !*ourtable) ourtable = (char *) 0;
+  if (table) {
+    if (*table) ourtable = *table;
+    else *table = ourtable;
+    if (!*table) return ERR_NO_TABLE;
+  }
+  if (!psql) {
+    if (!((MYSQL *) psql = mysql_init((MYSQL *) 0)))
+        return ERR_NOMEM;                                      /* init */
+    if (!(mysql_real_connect((MYSQL *) psql, host, user, pw, db,
+       (unsigned int) port, 0, CLIENT_COMPRESS)))              /* conn */
+               return mysql_error((MYSQL *) psql);
+  }
+  return (char *) 0;
+}
+
+void closesql()
+/* close connection to SQL server, if open */
+{
+       if (psql) mysql_close((MYSQL *) psql);
+       psql = (void *) 0;                      /* destroy pointer */
+       ourdb.len = 0;                          /* destroy cache */
+       return;
+}
+
diff --git a/sub_mysql/putsubs.c b/sub_mysql/putsubs.c
new file mode 100644 (file)
index 0000000..2a98dfc
--- /dev/null
@@ -0,0 +1,128 @@
+#include "error.h"
+#include "strerr.h"
+#include "readwrite.h"
+#include "str.h"
+#include "fmt.h"
+#include "stralloc.h"
+#include "open.h"
+#include "substdio.h"
+#include "case.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include "qmail.h"
+#include <mysql.h>
+
+static substdio ssin;
+static char inbuf[512];
+char strnum[FMT_ULONG];
+static stralloc line = {0};
+static stralloc domains = {0};
+static stralloc quoted = {0};
+static stralloc fn = {0};
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static void die_write(fatal)
+char *fatal;
+{
+  strerr_die3x(111,fatal,ERR_WRITE,"stdout");
+}
+
+unsigned long putsubs(dbname,hash_lo,hash_hi,
+       subwrite,flagsql,fatal)
+/* Outputs all userhostesses in 'dbname' to stdout. If userhost is not null */
+/* that userhost is excluded. 'dbname' is the base directory name. For the  */
+/* mysql version, dbname is the directory where the file "sql" with mysql   */
+/* access info is found. If this file is not present or if flagmysql is not */
+/* set, the routine falls back to the old database style. subwrite must be a*/
+/* function returning >=0 on success, -1 on error, and taking arguments     */
+/* (char* string, unsigned int length). It will be called once per address  */
+/* and should take care of newline or whatever needed for the output form.  */
+
+char *dbname;          /* database base dir */
+unsigned long hash_lo;
+unsigned long hash_hi;
+int subwrite();                /* write function. */
+int flagsql;
+char *fatal;           /* fatal error string */
+
+{
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  char *table = (char *) 0;
+  unsigned long *lengths;
+
+  unsigned int i;
+  int fd;
+  unsigned long no = 0L;
+  int match;
+  unsigned int pos = 0;
+  unsigned int hashpos;
+  char *ret = (char *) 0;
+
+  if (!flagsql || (ret = opensql(dbname,&table))) {
+    if (flagsql && *ret) strerr_die2x(111,fatal,ret);
+                                               /* fallback to local db */
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_catb(&fn,"/subscribers/?",15)) die_nomem(fatal);
+                               /* NOTE: Also copies terminal '\0' */
+    hashpos = fn.len - 2;
+    if (hash_lo > 52) hash_lo = 52;
+    if (hash_hi > 52) hash_hi = 52;
+    if (hash_hi < hash_lo) hash_hi = hash_lo;
+
+    for (i = hash_lo;i <= hash_hi;++i) {
+      fn.s[hashpos] = 64 + i;  /* hash range 0-52 */
+      fd = open_read(fn.s);
+      if (fd == -1) {
+        if (errno != error_noent)
+         strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+      } else {
+        substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+        for (;;) {
+          if (getln(&ssin,&line,&match,'\0') == -1)
+            strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+          if (!match)
+            break;
+          if (subwrite(line.s + 1,line.len - 2) == -1) die_write(fatal);
+          no++;
+        }
+        close(fd);
+      }
+    }
+    return no;
+
+  } else {                                     /* SQL Version */
+
+                                               /* main query */
+    if (!stralloc_copys(&line,"SELECT address FROM "))
+               die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line," WHERE hash BETWEEN ")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,hash_lo)))
+               die_nomem(fatal);
+    if (!stralloc_cats(&line," AND ")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,hash_hi)))
+               die_nomem(fatal);
+    if (mysql_real_query((MYSQL *) psql,line.s,line.len))      /* query */
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    if (!(result = mysql_use_result((MYSQL *) psql)))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    no = 0;
+    while ((row = mysql_fetch_row(result))) {
+       /* this is safe even if someone messes with the address field def */
+    if (!(lengths = mysql_fetch_lengths(result)))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if (subwrite(row[0],lengths[0]) == -1) die_write(fatal);
+      no++;                                    /* count for list-list fxn */
+    }
+    if (!mysql_eof(result))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    mysql_free_result(result);
+    return no;
+  }
+}
diff --git a/sub_mysql/searchlog.c b/sub_mysql/searchlog.c
new file mode 100644 (file)
index 0000000..8d8278f
--- /dev/null
@@ -0,0 +1,163 @@
+/*$Id: searchlog.c,v 1.15 1999/11/10 04:08:27 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "case.h"
+#include "scan.h"
+#include "stralloc.h"
+#include "str.h"
+#include "open.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "error.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include <mysql.h>
+
+static stralloc line = {0};
+static stralloc outline = {0};
+static char date[DATE822FMT];
+static datetime_sec when;
+static struct datetime dt;
+static substdio ssin;
+static char inbuf[256];
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static void lineout(subwrite,fatal)
+int subwrite();
+char *fatal;
+{
+  (void) scan_ulong(line.s,&when);
+  datetime_tai(&dt,when);              /* there is always at least a '\n' */
+  if (!stralloc_copyb(&outline,date,date822fmt(date,&dt) - 1))
+       die_nomem(fatal);
+  if (!stralloc_cats(&outline,": ")) die_nomem(fatal);
+  if (!stralloc_catb(&outline,line.s,line.len - 1)) die_nomem(fatal);
+  if (subwrite(outline.s,outline.len) == -1)
+       strerr_die3x(111,fatal,ERR_WRITE,"output");
+  return;
+}
+
+void searchlog(dir,search,subwrite,fatal)
+/* opens dir/Log, and outputs via subwrite(s,len) any line that matches   */
+/* search. A '_' is search is a wildcard. Any other non-alphanum/'.' char */
+/* is replaced by a '_'. mysql version. Falls back on "manual" search of  */
+/* local Log if no mysql connect info. */
+
+char *dir;             /* work directory */
+char *search;          /* search string */
+int subwrite();                /* output fxn */
+char *fatal;           /* fatal */
+{
+
+  register unsigned char x;
+  register unsigned char y;
+  register unsigned char *cp;
+  register unsigned char *cpsearch;
+  unsigned register char *cps;
+  unsigned register char ch;
+  unsigned char *cplast, *cpline;
+  unsigned int searchlen;
+  int fd,match;
+  char *ret;
+
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  char *table = (char *) 0;
+  char **ptable = &table;
+  char *sublist = (char *) 0;
+  unsigned long *lengths;
+
+  if (!search) search = "";    /* defensive */
+  searchlen = str_len(search);
+  case_lowerb(search,searchlen);
+  cps = (unsigned char *) search;
+  while ((ch = *(cps++))) {    /* search is potentially hostile */
+    if (ch >= 'a' && ch <= 'z') continue;
+    if (ch >= '0' && ch <= '9') continue;
+    if (ch == '.' || ch == '_') continue;
+    *(cps - 1) = '_';          /* will match char specified as well */
+  }
+
+  if ((ret = opensql(dir,ptable))) {
+    if (*ret) strerr_die2x(111,fatal,ret);
+                                               /* fallback to local log */
+  if (!stralloc_copys(&line,dir)) die_nomem(fatal);
+  if (!stralloc_cats(&line,"/Log")) die_nomem(fatal);
+  if (!stralloc_0(&line)) die_nomem(fatal);
+  fd = open_read(line.s);
+  if (fd == -1)
+    if (errno != error_noent)
+       strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
+    else
+        strerr_die3x(100,fatal,line.s,ERR_NOEXIST);
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,fatal,ERR_READ_INPUT);
+    if (!match) break;
+    if (!searchlen) {
+      lineout(subwrite,fatal);
+    } else {
+      cpline = (unsigned char *) line.s - 1;
+      cplast = cpline + line.len - searchlen; /* line has \0 at the end */
+      while ((cp = ++cpline) <= cplast) {
+       cpsearch = (unsigned char *) search;
+       for (;;) {
+         x = *cpsearch++;
+         if (!x) break;
+         y = *cp++ - 'A';
+         if (y <= (unsigned char) ('Z' - 'A')) y += 'a'; else y += 'A';
+         if (x != y && x != '_') break;                /* '_' = wildcard */
+       }
+       if (!x) {
+         lineout(subwrite,fatal);
+         break;
+       }
+      }
+    }
+  }
+  close(fd);
+  } else {
+
+/* SELECT (*) FROM list_slog WHERE fromline LIKE '%search%' OR address   */
+/* LIKE '%search%' ORDER BY tai; */
+/* The '*' is formatted to look like the output of the non-mysql version */
+/* This requires reading the entire table, since search fields are not   */
+/* indexed, but this is a rare query and time is not of the essence.     */
+
+    if (!stralloc_cats(&line,"SELECT CONCAT(FROM_UNIXTIME(UNIX_TIMESTAMP(tai)),"
+       "'-0000: ',UNIX_TIMESTAMP(tai),' ',edir,etype,' ',address,' ',"
+       "fromline) FROM ")) die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"_slog ")) die_nomem(fatal);
+    if (*search) {     /* We can afford to wait for LIKE '%xx%' */
+      if (!stralloc_cats(&line,"WHERE fromline LIKE '%")) die_nomem(fatal);
+      if (!stralloc_cats(&line,search)) die_nomem(fatal);
+      if (!stralloc_cats(&line,"%' OR address LIKE '%")) die_nomem(fatal);
+      if (!stralloc_cats(&line,search)) die_nomem(fatal);
+      if (!stralloc_cats(&line,"%'")) die_nomem(fatal);
+    }  /* ordering by tai which is an index */
+      if (!stralloc_cats(&line," ORDER by tai")) die_nomem(fatal);
+
+    if (mysql_real_query((MYSQL *) psql,line.s,line.len))      /* query */
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    if (!(result = mysql_use_result((MYSQL *) psql)))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    while ((row = mysql_fetch_row(result))) {
+    if (!(lengths = mysql_fetch_lengths(result)))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if (subwrite(row[0],lengths[0]) == -1) die_write(fatal);
+    }
+    if (!mysql_eof(result))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+    mysql_free_result(result);
+  }
+}
diff --git a/sub_mysql/subscribe.c b/sub_mysql/subscribe.c
new file mode 100644 (file)
index 0000000..f309253
--- /dev/null
@@ -0,0 +1,376 @@
+/*$Id: subscribe.c,v 1.22 1999/11/10 04:08:27 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "strerr.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "lock.h"
+#include "error.h"
+#include "subscribe.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "errtxt.h"
+#include "log.h"
+#include "idx.h"
+#include <mysql.h>
+#include <mysqld_error.h>
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static stralloc addr = {0};
+static stralloc lcaddr = {0};
+static stralloc line = {0};
+static stralloc domain = {0};
+static stralloc logline = {0};
+static stralloc quoted = {0};
+static stralloc fnnew = {0};
+static stralloc fn = {0};
+static stralloc fnlock = {0};
+static char szh[FMT_ULONG];
+
+void die_read(fatal)
+char *fatal;
+{
+  strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+}
+
+void die_write(fatal)
+char *fatal;
+{
+  strerr_die4sys(111,fatal,ERR_WRITE,fnnew.s,": ");
+}
+
+static int fd;
+static substdio ss;
+static char ssbuf[256];
+static int fdnew;
+static substdio ssnew;
+static char ssnewbuf[256];
+
+int subscribe(dbname,userhost,flagadd,comment,event,flagmysql,
+       forcehash,tab,fatal)
+/* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database  */
+/* dbname. Comment is e.g. the subscriber from line or name. It is added to  */
+/* the log. Event is the action type, e.g. "probe", "manual", etc. The       */
+/* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0   */
+/* on failure. If flagmysql is set and the file "sql" is found in the        */
+/* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */
+/* >=0 it is used in place of the calculated hash. This makes it possible to */
+/* add addresses with a hash that does not exist. forcehash has to be 0..99. */
+/* for unsubscribes, the address is only removed if forcehash matches the    */
+/* actual hash. This way, ezmlm-manage can be prevented from touching certain*/
+/* addresses that can only be removed by ezmlm-unsub. Usually, this would be */
+/* used for sublist addresses (to avoid removal) and sublist aliases (to     */
+/* prevent users from subscribing them (although the cookie mechanism would  */
+/* prevent the resulting duplicate message from being distributed. */
+
+char *dbname;
+char *userhost;
+int flagadd;
+char *comment;
+char *event;
+int flagmysql;
+int forcehash;
+char *tab;
+char *fatal;
+{
+  int fdlock;
+
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  char *cp,*cpafter,*cpat;
+  char szhash[3] = "00";
+  char *r = (char *) 0;
+  char *table = (char *) 0;
+  char **ptable = &table;
+
+  unsigned int j;
+  uint32 h,lch;
+  unsigned char ch,lcch;
+  int match;
+  int flagwasthere;
+
+  if (userhost[str_chr(userhost,'\n')])
+    strerr_die2x(100,fatal,ERR_ADDR_NL);
+
+  if (tab) ptable = &tab;
+
+  if (!flagmysql || (r = opensql(dbname,ptable))) {
+    if (r && *r) strerr_die2x(111,fatal,r);
+                                               /* fallback to local db */
+    if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
+    if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
+    if (addr.len > 401)
+      strerr_die2x(100,fatal,ERR_ADDR_LONG);
+
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len)
+      strerr_die2x(100,fatal,ERR_ADDR_AT);
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+    if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+    case_lowerb(lcaddr.s + 1,j - 1);   /* make all-lc version of address */
+
+    if (forcehash >= 0 && forcehash <= 52) {
+      ch = lcch = (unsigned char) forcehash;
+    } else {
+      h = 5381;
+      lch = h;
+      for (j = 0;j < addr.len;++j) {
+        h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+        lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+      }
+      lcch = 64 + (lch % 53);
+      ch = 64 + (h % 53);
+    }
+
+    if (!stralloc_0(&addr)) die_nomem(fatal);
+    if (!stralloc_0(&lcaddr)) die_nomem(fatal);
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_copys(&fnlock,dbname)) die_nomem(fatal);
+
+    if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
+    if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
+    if (!stralloc_copy(&fnnew,&fn)) die_nomem(fatal);
+       /* code later depends on fnnew = fn + 'n' */
+    if (!stralloc_cats(&fnnew,"n")) die_nomem(fatal);
+    if (!stralloc_cats(&fnlock,"/lock")) die_nomem(fatal);
+    if (!stralloc_0(&fnnew)) die_nomem(fatal);
+    if (!stralloc_0(&fn)) die_nomem(fatal);
+    if (!stralloc_0(&fnlock)) die_nomem(fatal);
+
+    fdlock = open_append(fnlock.s);
+    if (fdlock == -1)
+      strerr_die4sys(111,fatal,ERR_OPEN,fnlock.s,": ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,fatal,ERR_OBTAIN,fnlock.s,": ");
+
+                               /* do lower case hashed version first */
+    fdnew = open_trunc(fnnew.s);
+    if (fdnew == -1) die_write(fatal);
+    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+    flagwasthere = 0;
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent) { close(fdnew); die_read(fatal); }
+    }
+    else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1) {
+         close(fd); close(fdnew); die_read(fatal);
+        }
+        if (!match) break;
+        if (line.len == addr.len)
+          if (!case_diffb(line.s,line.len,addr.s)) {
+           flagwasthere = 1;
+           if (!flagadd)
+             continue;
+         }
+        if (substdio_bput(&ssnew,line.s,line.len) == -1) {
+         close(fd); close(fdnew); die_write(fatal);
+        }
+      }
+
+      close(fd);
+    }
+
+    if (flagadd && !flagwasthere)
+      if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
+        close(fdnew); die_write(fatal);
+      }
+
+    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
+    if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
+    close(fdnew);
+
+    if (rename(fnnew.s,fn.s) == -1)
+      strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
+
+    if ((ch == lcch) || flagwasthere) {
+      close(fdlock);
+      if (flagadd ^ flagwasthere) {
+        if (!stralloc_0(&addr)) die_nomem(fatal);
+        log(dbname,event,addr.s+1,comment);
+        return 1;
+      }
+      return 0;
+    }
+
+                       /* If unsub and not found and hashed differ, OR */
+                       /* sub and not found (so added with new hash) */
+                       /* do the 'case-dependent' hash */
+
+    fn.s[fn.len - 2] = ch;
+    fnnew.s[fnnew.len - 3] = ch;
+    fdnew = open_trunc(fnnew.s);
+    if (fdnew == -1) die_write(fatal);
+    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent) { close(fdnew); die_read(fatal); }
+    } else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1)
+          { close(fd); close(fdnew); die_read(fatal); }
+        if (!match) break;
+        if (line.len == addr.len)
+          if (!case_diffb(line.s,line.len,addr.s)) {
+            flagwasthere = 1;
+            continue;  /* always want to remove from case-sensitive hash */
+          }
+        if (substdio_bput(&ssnew,line.s,line.len) == -1)
+          { close(fd); close(fdnew); die_write(fatal); }
+      }
+
+      close(fd);
+    }
+
+    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
+    if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
+    close(fdnew);
+
+    if (rename(fnnew.s,fn.s) == -1)
+      strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
+
+    close(fdlock);
+    if (flagadd ^ flagwasthere) {
+      if (!stralloc_0(&addr)) die_nomem(fatal);
+      log(dbname,event,addr.s+1,comment);
+      return 1;
+    }
+    return 0;
+
+  } else {                             /* SQL version */
+    domain.len = 0;                    /* clear domain */
+                                       /* lowercase and check address */
+    if (!stralloc_copys(&addr,userhost)) die_nomem(fatal);
+    if (addr.len > 255)                        /* this is 401 in std ezmlm. 255 */
+                                       /* should be plenty! */
+      strerr_die2x(100,fatal,ERR_ADDR_LONG);
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len)
+      strerr_die2x(100,fatal,ERR_ADDR_AT);
+    cpat = addr.s + j;
+    case_lowerb(cpat + 1,addr.len - j - 1);
+    if (!stralloc_ready(&quoted,2 * addr.len + 1)) die_nomem(fatal);
+    quoted.len = mysql_escape_string(quoted.s,addr.s,addr.len);
+       /* stored unescaped, so it should be ok if quoted.len is >255, as */
+       /* long as addr.len is not */
+
+    if (forcehash < 0) {
+      if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+      case_lowerb(lcaddr.s,j);         /* make all-lc version of address */
+      h = 5381;
+      for (j = 0;j < lcaddr.len;++j) {
+        h = (h + (h << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+      }
+      ch = (h % 53);                   /* 0 - 52 */
+    } else
+      ch = (forcehash % 100);
+
+    szhash[0] = '0' + ch / 10;         /* hash for sublist split */
+    szhash[1] = '0' + (ch % 10);
+
+    if (flagadd) {
+      if (!stralloc_copys(&line,"LOCK TABLES ")) die_nomem(fatal);
+      if (!stralloc_cats(&line,table)) die_nomem(fatal);
+      if (!stralloc_cats(&line," WRITE")) die_nomem(fatal);
+      if (mysql_real_query((MYSQL *) psql,line.s,line.len))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(fatal);
+      if (!stralloc_cats(&line,table)) die_nomem(fatal);
+      if (!stralloc_cats(&line," WHERE address='")) die_nomem(fatal);
+      if (!stralloc_cat(&line,&quoted)) die_nomem(fatal);      /* addr */
+      if (!stralloc_cats(&line,"'")) die_nomem(fatal);
+      if (mysql_real_query((MYSQL *) psql,line.s,line.len))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if (!(result = mysql_use_result((MYSQL *) psql)))
+       strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if ((row = mysql_fetch_row(result))) {                   /* there */
+       while (mysql_fetch_row(result));                        /* use'm up */
+       mysql_free_result(result);
+       if (mysql_query((MYSQL *) psql,"UNLOCK TABLES"))
+         strerr_die2x(111,"fatal",mysql_error((MYSQL *) psql));
+        return 0;                                              /* there */
+      } else {                                                 /* not there */
+       mysql_free_result(result);
+       if (mysql_errno((MYSQL *) psql))                        /* or ERROR */
+         strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+       if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(fatal);
+       if (!stralloc_cats(&line,table)) die_nomem(fatal);
+       if (!stralloc_cats(&line," (address,hash) VALUES ('"))
+               die_nomem(fatal);
+       if (!stralloc_cat(&line,&quoted)) die_nomem(fatal);     /* addr */
+       if (!stralloc_cats(&line,"',")) die_nomem(fatal);
+       if (!stralloc_cats(&line,szhash)) die_nomem(fatal);     /* hash */
+       if (!stralloc_cats(&line,")")) die_nomem(fatal);
+        if (mysql_real_query((MYSQL *) psql,line.s,line.len))  /* INSERT */
+         strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+       if (mysql_query((MYSQL *) psql,"UNLOCK TABLES"))
+         strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      }
+    } else {                                                   /* unsub */
+      if (!stralloc_copys(&line,"DELETE FROM ")) die_nomem(fatal);
+      if (!stralloc_cats(&line,table)) die_nomem(fatal);
+      if (!stralloc_cats(&line," WHERE address='")) die_nomem(fatal);
+      if (!stralloc_cat(&line,&quoted)) die_nomem(fatal);      /* addr */
+      if (forcehash >= 0) {
+       if (!stralloc_cats(&line,"' AND hash=")) die_nomem(fatal);
+       if (!stralloc_cats(&line,szhash)) die_nomem(fatal);
+      } else {
+        if (!stralloc_cats(&line,"' AND hash BETWEEN 0 AND 52"))
+               die_nomem(fatal);
+      }
+      if (mysql_real_query((MYSQL *) psql,line.s,line.len))
+         strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
+      if (mysql_affected_rows((MYSQL *) psql) == 0)
+       return 0;                               /* address wasn't there*/
+    }
+
+               /* log to subscriber log */
+               /* INSERT INTO t_slog (address,edir,etype,fromline) */
+               /* VALUES('address',{'+'|'-'},'etype','[comment]') */
+
+    if (!stralloc_copys(&logline,"INSERT INTO ")) die_nomem(fatal);
+    if (!stralloc_cats(&logline,table)) die_nomem(fatal);
+    if (!stralloc_cats(&logline,
+       "_slog (address,edir,etype,fromline) VALUES ('")) die_nomem(fatal);
+    if (!stralloc_cat(&logline,&quoted)) die_nomem(fatal);
+    if (flagadd) {                                             /* edir */
+      if (!stralloc_cats(&logline,"','+','")) die_nomem(fatal);
+    } else {
+      if (!stralloc_cats(&logline,"','-','")) die_nomem(fatal);
+    }
+    if (*(event + 1))  /* ezmlm-0.53 uses '' for ezmlm-manage's work */
+      if (!stralloc_catb(&logline,event+1,1)) die_nomem(fatal);        /* etype */
+    if (!stralloc_cats(&logline,"','")) die_nomem(fatal);
+    if (comment && *comment) {
+       j = str_len(comment);
+       if (!stralloc_ready(&quoted,2 * j + 1)) die_nomem(fatal);
+       quoted.len = mysql_escape_string(quoted.s,comment,j);   /* from */
+       if (!stralloc_cat(&logline,&quoted)) die_nomem(fatal);
+    }
+    if (!stralloc_cats(&logline,"')")) die_nomem(fatal);
+
+    if (mysql_real_query((MYSQL *) psql,logline.s,logline.len))
+               ;                               /* log (ignore errors) */
+    if (!stralloc_0(&addr))
+               ;                               /* ignore errors */
+    log(dbname,event,addr.s,comment);          /* also log to old log */
+    return 1;                                  /* desired effect */
+  }
+}
diff --git a/sub_mysql/tagmsg.c b/sub_mysql/tagmsg.c
new file mode 100644 (file)
index 0000000..55b57e7
--- /dev/null
@@ -0,0 +1,92 @@
+/*$Id: tagmsg.c,v 1.11 1999/11/10 04:08:27 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "scan.h"
+#include "fmt.h"
+#include "strerr.h"
+#include "cookie.h"
+#include "slurp.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include "makehash.h"
+#include <mysql.h>
+#include <mysqld_error.h>
+
+static stralloc line = {0};
+static stralloc key = {0};
+static char hash[COOKIE];
+static char strnum[FMT_ULONG]; /* message number as sz */
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(100,fatal,ERR_NOMEM);
+}
+
+void tagmsg(dir,msgnum,seed,action,hashout,bodysize,chunk,fatal)
+/* This routine creates a cookie from num,seed and the */
+/* list key and returns that cookie in hashout. The use of sender/num and */
+/* first char of action is used to make cookie differ between messages,   */
+/* the key is the secret list key. The cookie will be inserted into       */
+/* table_cookie where table and other data is taken from dir/sql. We log  */
+/* arrival of the message (done=0). */
+
+char *dir;                     /* db base dir */
+unsigned long msgnum;          /* number of this message */
+char *seed;                    /* seed. NULL ok, but less entropy */
+char *action;                  /* to make it certain the cookie differs from*/
+                               /* one used for a digest */
+char *hashout;                 /* calculated hash goes here */
+unsigned long bodysize;
+unsigned long chunk;
+char *fatal;
+{
+  char *table = (char *) 0;
+  char *ret;
+  unsigned int i;
+
+  strnum[fmt_ulong(strnum,msgnum)] = '\0';     /* message nr ->string*/
+
+    switch(slurp("key",&key,32)) {
+      case -1:
+       strerr_die3sys(111,fatal,ERR_READ,"key: ");
+      case 0:
+       strerr_die3x(100,fatal,"key",ERR_NOEXIST);
+    }
+    cookie(hash,key.s,key.len,strnum,seed,action);
+    for (i = 0; i < COOKIE; i++)
+      hashout[i] = hash[i];
+
+  if ((ret = opensql(dir,&table))) {
+    if (*ret) strerr_die2x(111,fatal,ret);
+    return;                            /* no sql => success */
+
+  } else {
+    if (chunk >= 53L) chunk = 0L;      /* sanity */
+
+       /* INSERT INTO table_cookie (msgnum,cookie) VALUES (num,cookie) */
+       /* (we may have tried message before, but failed to complete, so */
+       /* ER_DUP_ENTRY is ok) */
+    if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"_cookie (msgnum,cookie,bodysize,chunk) VALUES ("))
+               die_nomem(fatal);
+    if (!stralloc_cats(&line,strnum)) die_nomem(fatal);
+    if (!stralloc_cats(&line,",'")) die_nomem(fatal);
+    if (!stralloc_catb(&line,hash,COOKIE)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"',")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,bodysize)))
+               die_nomem(fatal);
+    if (!stralloc_cats(&line,",")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,chunk))) die_nomem(fatal);
+    if (!stralloc_cats(&line,")")) die_nomem(fatal);
+    if (mysql_real_query((MYSQL *) psql,line.s,line.len) != 0)
+      if (mysql_errno((MYSQL *) psql) != ER_DUP_ENTRY) /* ignore dups */
+        strerr_die2x(111,fatal,mysql_error((MYSQL *) psql)); /* cookie query */
+
+    if (! (ret = logmsg(dir,msgnum,0L,0L,1))) return;  /* log done=1*/
+    if (*ret) strerr_die2x(111,fatal,ret);
+  }
+
+  return;
+}
diff --git a/sub_mysql/to40x b/sub_mysql/to40x
new file mode 100755 (executable)
index 0000000..5f6f872
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+cat <<EOF
+ALTER TABLE ${1}_cookie
+        ADD COLUMN chunk TINYINT NOT NULL DEFAULT 0;
+ALTER TABLE ${1}_cookie
+        ADD COLUMN bodysize INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE ${1}_mlog
+        ADD COLUMN subs INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE ${1}_digest_cookie
+        ADD COLUMN chunk TINYINT NOT NULL DEFAULT 0;
+ALTER TABLE ${1}_digest_cookie
+        ADD COLUMN bodysize INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE ${1}_digest_mlog
+        ADD COLUMN subs INTEGER NOT NULL DEFAULT 0;
+
diff --git a/sub_pgsql/README b/sub_pgsql/README
new file mode 100644 (file)
index 0000000..942d15c
--- /dev/null
@@ -0,0 +1,169 @@
+$Id: README,v 1.1 1999/08/21 02:00:37 lindberg Exp $
+$Name: ezmlm-idx-040 $
+INFORMATION ON BUILDING/USING EZMLM WITH POSTGRESQL SUPPORT
+
+Original source:
+(c) 1999,      Frederik Lindberg,
+               lindberg@id.wustl.edu
+               You may use under GPL.
+
+and for PostgreSQL modifications:
+(c) 1999, Magnus Stålåker
+        stalaker@umc.se
+
+For information on PostgreSQL, see http://www.postgresql.org/
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+NOTICE! This is a untested beta! USE WITH CAUTION!
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+This version of the PostgresSQL supports the basic subscriber address
+database and subscription logging. The log that is searched by the -log.
+command is the local listdir/Log file, NOT the SQL database. Message
+logging is not supported, and central admin of list clusters is not
+supported. Still, the current support covers the functionality used in
+99% of lists.
+
+Most of this information is available in FAQ.idx.
+
+If you are interested in contributing/testing a subscriber db interface
+for another SQL server, please see sub_std/README and the routines here,
+and contact lindberg@id.wustl.edu (it may already be in process). See end
+of this file for other ways to contribute.
+
+conf-mysql must be edited to reflect your system. On many systems, you
+also need to include ``-lsocket'', as well as change the paths to the
+/usr/local equivalents. For the i386.rpm-based systems, you need at
+least MySQL-devel to build the files. Look at your mysql docs for more info.
+
+TABLES USED FOR (My)SQL SUPPORT
+
+The basic philosophy is that the database can be on any host (if you use
+SENDER restrictions, connectivity to the main host is more important than
+to the sublists), and you choose the database and "table root" names. The
+default database is ``ezmlm'' and the default table root is ``list''. Each
+list has a separate table root. Any number of lists can share a database.
+
+The main list address table is named with the table root only, others have
+that name with various suffixes. In the following ``list'' is used as the
+table root.
+
+ADDRESS TABLES
+list           subscriber addresses
+list_slog      subscriber address log
+list_allow     subscriber aliases for posts on SENDER checked lists.
+list_allow_slog        subscriber log for list_allow
+list_deny      blacklisted addresses for posts on SENDER checked lists.
+list_deny_slog log for list_deny.
+list_mod       moderator addresses
+list_mod_slog  log for list_mod
+list_digest    subscriber log for digest list.
+list_digest_slog       log for list_digest
+
+MESSAGE LOGGING TABLES (not supported by this version of the interface)
+list_cookie    message cookie table for main list
+list_mlog      message logging table for main list
+list_digest_cookie     message cookie table for digest list
+list_digest_mlog       message logging table for digest list
+
+SUBLIST SPLIT TABLES (not supported by this version of the interface)
+list_name      sublist split table for main list
+list_digest_name       sublist split table for digest list.
+
+
+ezmlm-mktab(1) is a script that outputs the table definintions. Look at
+the output for a detailed field description.
+
+The address tables contain (address,domain,hash,h,num). For normal
+lists only the address field is used. For main->sublist clusters, the other
+fields are used for load splitting. The domain is the first up to 3 characters
+of the last part of the domain name. The hash is a address hash [0-52] differnt
+from the one used by ezmlm for splitting within DIR/subscribers. When using
+the address field as a primary key, the size of the index was unreasonable.
+Therefore, ``num'' is used as a dummy primary key, and ``h'' (a 32 bit hash
+of the address) is used as an index. This markedly speeds up (un)sub with
+large (>30,000 rows) subscriber tables.
+
+The *_slog tables contain the same info as DIR/Log, i.e. address, timestamp,
+entry-type, entry-direction, and fromline. The entry-type is the first letter
+of the type of entry (probe, manual, `` '' for normal), entry-direction is
+``+'' for addition, ``-'' for removal. Fromline is the From: header contents
+taken from the subscribe confirm message or from ezmlm-sub (if used with -n).
+It is blank for all address removals, and may be blank also for additions. It
+is used by the list-log.xx command. It is trivial to JOIN this table with the
+address table to get e.g. subsciber names, subscription dates, etc. These
+tables also have the 32-bit hash ``h'' as an index. Joins should be done on
+``h'' as well as ``address'' for better performance.
+
+The *_cookie tables contain message number, timestamp, and cookie. For each
+message a pseudo-random cookie is generated that is ``impossible'' to guess
+beforehand. For lists with sublists, this is used as basic authentication,
+i.e. the sublist will refuse to process a message that doesn't contain the
+correct cookie or that the sublist has already successfully processed.
+
+The *_mlog tables contain log entries from main and sublists. These are
+timestamp, listno, done. Listno is the lowest listnumber for an active list
+entry with the name of this sublist as looked up in the *_name table. Done
+is -1 for bounce, 0 for arrived, 1 for finished processing, and 2 for receipt
+received. The routines are set up so that only the first attempt for each
+combination (listno,code) is logged.
+
+The *_name tables contain listno,name,domain,hash_lo,hash_hi,msgnum_lo,
+msgnum_hi,notuse. Listno is auto_increment and unique. Name is the name of the
+sublist. domain is the last up to 3 characters of the top domain name for
+addresses served by this list (default = ''). It is is '', the list servers
+all_domains_that_are_not_served_by_another list (in addition to domain '').
+Of the addresses that match the domain criterion, the list serves the subset
+with hash between hash_lo and hash_hi (defaults 0, 52). Any entry is ingnored if
+notuse != 0 OR the current message number is not between msgnum_lo and
+msgnum_hi.
+
+For normal lists that are not distributed (i.e. they are a single list),
+entries in the *_name tables are not needed and logging is not very
+relevant.
+
+For most lists, the only addresses that are stored in the SQL database are
+the subscribers of list and digest, and the ``allow'' aliases. It is NOT
+normally advisable to store moderator addresses there, since they are
+needed only at the main list and secrecy is more important. ``Deny'' addresses
+are few and again only needed at the main list. ``Allow'' are put in the
+SQL database when using the default ezmlmrc file only to make all relevant
+addresses manipulatable via the SQL server. The other tables are created, in
+case they are  wanted (the cost for having them as empty table is zero). The
+basedir/sql file is the decision point. If it exists, an SQL table is used;
+if not a local ezmlm db is used.
+
+CONTRIBUTIONS REQESTED
+
+I would be very grateful if there are users out there willing to do any of
+the following and contribute it to this package. Please check with me first
+(lindberg@id.wustl.edu), as the project may already be in progress/done.
+
+1. Interfaces for other SQL servers. Oracle, SyBase, ...
+
+2. A GUI admin utility to add/remove/manipulate the sublist split, essentially
+   by modifying list_[digest_]name in a safe way. Ideally WWW if it can be
+   done securely. If you use some standard interface (JDBC/DBD) it would be
+   useful also with other SQL severs. This could even be an Access program
+   using ODBC, although writing it for a platform running qmail/ezmlm makes
+   most sense.
+
+3. a WWW GUI that allows users to subscribe/unsubscribe in a safe way. A random
+   password would be created the first time and stored in a new address->pw
+   table and mailed to the subscriber address. With that password, the user
+   would be able to [un]subscribe to lists, edit the name (for compatibility
+   implemented by adding a subscribe line to list_[digest]slog). Add/remove
+   aliases. Ideally, it should also allow searching by subscriber name. This
+   would search *_slog.fromline. If less that 'x' alternatives are found, the
+   user would be presented with names (not addresses), allowing the user to
+   cause the subscription name and password to be sent to the respective
+   subscription address. With that info, the subscriber can then unsubscribe,
+   even if s/he has forgotten the subscription address. It is complicated
+   slightly by the fact that ``fromline'' is the crude line and needs to be
+   rfc822 parsed. Again, use of a standard interface is encouraged to make it
+   compatible also with other SQL servers.
+
+The aim of all this is to make it easy to use ezmlm to run very large lists,
+easy to set up sites that handle subscriber interaction, archive access, etc,
+and hopefully easier to integrate many ezmlm as done by some WWW sites today.
+
diff --git a/sub_pgsql/checktag.c b/sub_pgsql/checktag.c
new file mode 100644 (file)
index 0000000..330c236
--- /dev/null
@@ -0,0 +1,106 @@
+/*$Id: checktag.c,v 1.3 1999/12/23 02:40:57 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "scan.h"
+#include "fmt.h"
+#include "cookie.h"
+#include "makehash.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static stralloc key = {0};
+static stralloc line = {0};
+static strnum[FMT_ULONG];
+static newcookie[COOKIE];
+
+char *checktag (dir,num,listno,action,seed,hash)
+/* reads dir/sql. If not present, returns success (NULL). If dir/sql is    */
+/* present, checks hash against the cookie table. If match, returns success*/
+/* (NULL), else returns "". If error, returns error string. */
+
+
+char *dir;                             /* the db base dir */
+unsigned long num;                     /* message number */
+unsigned long listno;                  /* bottom of range => slave */
+char *action;
+char *seed;                            /* cookie base */
+char *hash;                            /* cookie */
+{
+  PGresult *result;
+  /*  int row; */
+  char *table = (char *) 0;
+  char *r;
+
+  if ((r = opensql(dir,&table))) {
+    if (*r) return r;
+    if (!seed) return (char *) 0;              /* no data - accept */
+
+    strnum[fmt_ulong(strnum,num)] = '\0';      /* message nr ->string*/
+
+    switch(slurp("key",&key,32)) {
+    case -1:
+      return ERR_READ_KEY;
+    case 0:
+      return ERR_NOEXIST_KEY;
+    }
+
+    cookie(newcookie,key.s,key.len,strnum,seed,action);
+    if (byte_diff(hash,COOKIE,newcookie)) return "";
+    else return (char *) 0;
+
+  } else {
+
+    /* SELECT msgnum FROM table_cookie WHERE msgnum=num and cookie='hash' */
+    /* succeeds only is everything correct. 'hash' is quoted since it is  */
+    /* potentially hostile. */
+    if (listno) {                      /* only for slaves */
+      if (!stralloc_copys(&line,"SELECT listno FROM ")) return ERR_NOMEM;
+      if (!stralloc_cats(&line,table)) return ERR_NOMEM;
+      if (!stralloc_cats(&line,"_mlog WHERE listno=")) return ERR_NOMEM;
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,listno)))
+       return ERR_NOMEM;
+      if (!stralloc_cats(&line," AND msgnum=")) return ERR_NOMEM;
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,num))) return ERR_NOMEM;
+      if (!stralloc_cats(&line," AND done > 3")) return ERR_NOMEM;
+
+      if (!stralloc_0(&line)) return ERR_NOMEM;
+      result = PQexec( psql, line.s );
+      if(result == NULL)
+       return (PQerrorMessage(psql));
+      if( PQresultStatus(result) != PGRES_TUPLES_OK)
+       return (char *) (PQresultErrorMessage(result));
+      if( PQntuples(result) > 0 ) {
+       PQclear(result);
+       return("");
+      } else
+       PQclear(result);
+    }
+
+    if (!stralloc_copys(&line,"SELECT msgnum FROM ")) return ERR_NOMEM;
+    if (!stralloc_cats(&line,table)) return ERR_NOMEM;
+    if (!stralloc_cats(&line,"_cookie WHERE msgnum=")) return ERR_NOMEM;
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,num))) return ERR_NOMEM;
+    if (!stralloc_cats(&line," and cookie='")) return ERR_NOMEM;
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,hash))) return ERR_NOMEM;
+    if (!stralloc_cats(&line,"'")) return ERR_NOMEM;
+
+    if (!stralloc_0(&line)) return ERR_NOMEM;
+    result = PQexec(psql,line.s);
+    if (result == NULL)
+      return (PQerrorMessage(psql));
+    if (PQresultStatus(result) != PGRES_TUPLES_OK)
+      return (char *) (PQresultErrorMessage(result));
+    if(PQntuples(result) < 0) {
+      PQclear( result );
+      return("");
+    }
+
+    PQclear(result);
+    if (listno)
+      (void) logmsg(dir,num,listno,0L,3);      /* non-ess mysql logging */
+    return (char *)0;
+  }
+}
diff --git a/sub_pgsql/conf-sqlcc b/sub_pgsql/conf-sqlcc
new file mode 100644 (file)
index 0000000..886cf2f
--- /dev/null
@@ -0,0 +1,6 @@
+-I/usr/include/pgsql
+
+# the top line will be used when compiling. Edit to reflect your PostgreSQL
+# installation. The above is correct for RedHat rpms.
+# -I/usr/local/pgsql/include would be correct for BSD installations.
diff --git a/sub_pgsql/conf-sqlld b/sub_pgsql/conf-sqlld
new file mode 100644 (file)
index 0000000..610f5cd
--- /dev/null
@@ -0,0 +1,4 @@
+-L/usr/local/pgsql/lib -lpq -lcrypt
+
+# the top line will be used when linking. Edit to reflect your PostgreSQL
+# installation. 
diff --git a/sub_pgsql/ezmlm-mktab b/sub_pgsql/ezmlm-mktab
new file mode 100755 (executable)
index 0000000..9ac2b33
--- /dev/null
@@ -0,0 +1,216 @@
+#!/bin/sh
+# Simple script to generate input to psql to generate tables for a list
+# All tables are created, even though it is not advisable to put e.g. the
+# moderator table in the SQL database nor is it very useful to put the
+# blacklist/deny table there. The subscriber lists for the main and digest
+# lists should be there, and it's reasonable to put the "extra" list there
+# if used.
+ECHO='echo'
+CAT='cat'
+CUT='cut'
+
+CREATE='y'
+DROP='n'
+TROOT='list'
+# size of std cookie
+COOKIE='20'
+
+                                       # not everyone has getopt :-(
+while [ "`${ECHO} "$1" | ${CUT} -c1`" = "-" ]; do
+       case "$1" in
+               -c)     CREATE='y'; shift;;
+               -C)     CREATE='n'; shift;;
+               -d)     DROP='y'; shift;;
+               -D)     DROP='n'; shift;;
+               -cd|-dc)        CREATE='y'; DROP='y'; shift;;
+               -cD|-Dc)        CREATE='y'; DROP='n'; shift;;
+               -Cd|-dC)        CREATE='n'; DROP='y'; shift;;
+               -CD|-DC)        CREATE='n'; DROP='n'; shift;;
+               --)     shift; break;;
+               *)      echo "usage: emzlm-mktab [-cCdD] table_toot"; exit 100;;
+       esac
+done
+
+[ ! -z "$1" ] && TROOT="$1";
+
+if [ "$DROP" = "y" ]; then
+  cat <<EOF
+
+/* drop old tables.  */
+/* Usage: */
+/* ezmlm-mktab [-d] troot | psql -h host -u userid -d database */
+
+DROP TABLE ${TROOT};
+DROP TABLE ${TROOT}_slog;
+DROP TABLE ${TROOT}_digest;
+DROP TABLE ${TROOT}_digest_slog;
+DROP TABLE ${TROOT}_mod;
+DROP TABLE ${TROOT}_mod_slog;
+DROP TABLE ${TROOT}_allow;
+DROP TABLE ${TROOT}_allow_slog;
+DROP TABLE ${TROOT}_deny;
+DROP TABLE ${TROOT}_deny_slog;
+DROP TABLE ${TROOT}_name;
+DROP TABLE ${TROOT}_cookie;
+DROP TABLE ${TROOT}_mlog;
+DROP TABLE ${TROOT}_digest_name;
+DROP TABLE ${TROOT}_digest_cookie;
+DROP TABLE ${TROOT}_digest_mlog;
+DROP SEQUENCE ${TROOT}_name_listno_seq;
+DROP SEQUENCE ${TROOT}_digest_name_listno_seq;
+
+EOF
+
+fi
+
+if [ $CREATE = 'y' ]; then
+  cat << EOF
+
+/* Main address table */
+create TABLE ${TROOT} (
+       hash            INT4 NOT NULL,
+       address         VARCHAR(255) PRIMARY KEY );
+
+/* Subscription log table. No addr idx to make insertion fast, since that is */
+/* almost the only thing we do with this table */
+create TABLE ${TROOT}_slog (
+       tai             TIMESTAMP DEFAULT now(),
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR NOT NULL,
+       etype           CHAR NOT NULL
+       );
+
+/* digest list table */
+create TABLE ${TROOT}_digest (
+       hash            INT4 NOT NULL,
+       address         TEXT NOT NULL
+       );
+
+/* digest list subscription log */
+create TABLE ${TROOT}_digest_slog (
+       tai             TIMESTAMP DEFAULT now(),
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR NOT NULL,
+       etype           CHAR NOT NULL
+       );
+
+/* moderator addresses */
+create TABLE ${TROOT}_mod (
+       hash            INT4 NOT NULL,
+       address         TEXT NOT NULL
+);
+
+/* moderator subscription log */
+create TABLE ${TROOT}_mod_slog (
+       tai             TIMESTAMP DEFAULT now(),
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR NOT NULL,
+       etype           CHAR NOT NULL
+       );
+
+/* "allow" address table */
+create TABLE ${TROOT}_allow (
+       hash            INT4 NOT NULL,
+       address         VARCHAR(255) NOT NULL
+       );
+
+/* extra address table log */
+create TABLE ${TROOT}_allow_slog (
+       tai             TIMESTAMP DEFAULT now(),
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR NOT NULL,
+       etype           CHAR NOT NULL
+       );
+
+/* blacklist address table */
+create TABLE ${TROOT}_deny (
+       hash            INT4  NOT NULL,
+       address         VARCHAR(255) NOT NULL
+       );
+
+/* blacklist subscription log */
+create TABLE ${TROOT}_deny_slog (
+       tai             TIMESTAMP,
+       address         VARCHAR(255) NOT NULL,
+       fromline        VARCHAR(255) NOT NULL,
+       edir            CHAR NOT NULL,
+       etype           CHAR NOT NULL
+       );
+
+/* sublist restriction table */
+/* notuse != 0 => defer message. = 0 => process message */
+/* no reason for index - will always be small */
+create TABLE ${TROOT}_name (
+       listno          SERIAL,
+       name            VARCHAR(255) NOT NULL,
+       notuse          INT4 NOT NULL DEFAULT 0,
+       msgnum_lo       INT8 NOT NULL DEFAULT 0,
+       msgnum_hi       INT8 NOT NULL DEFAULT 4294967295,
+       hash_lo         INT4 NOT NULL DEFAULT 0,
+       hash_hi         INT4 NOT NULL DEFAULT 52,
+       domain          CHAR(3) NOT NULL DEFAULT '',
+       PRIMARY KEY (listno));
+
+/* main list inserts a cookie here. Sublists check it */
+CREATE TABLE ${TROOT}_cookie (
+       msgnum          INT4 NOT NULL,
+       tai             TIMESTAMP NOT NULL DEFAULT now(),
+       cookie          CHAR(20) NOT NULL,
+       chunk           INT4 NOT NULL DEFAULT 0,
+       bodysize        INT4 NOT NULL DEFAULT 0,
+       PRIMARY KEY (msgnum));
+
+/* main and sublist log here when the message is done */
+/* done=0 for arrived, done=1 for sent. tai reflects last change, as e.g. */
+/* done=0 may be overwritten in case first delivery to the list fails.    */
+CREATE TABLE ${TROOT}_mlog (
+       msgnum    INT4 NOT NULL,
+       listno    INT4 NOT NULL,
+       tai       TIMESTAMP DEFAULT now(),
+       subs      INT4 NOT NULL DEFAULT 0,
+       done      INT4 NOT NULL DEFAULT 0,
+       PRIMARY KEY (listno,msgnum,done));
+
+/* digest sublist restriction table */
+/* notuse != 0 => defer message. = 0 => process message */
+/* no index, since table unlikely to have >30 or so rows */
+create TABLE ${TROOT}_digest_name (
+       listno          SERIAL,
+       name            VARCHAR(255) NOT NULL,
+       notuse          INT4 NOT NULL DEFAULT 0,
+       msgnum_lo       INT8 NOT NULL DEFAULT 0,
+       msgnum_hi       INT8 NOT NULL DEFAULT 4294967295,
+       hash_lo         INT4 NOT NULL DEFAULT 0,
+       hash_hi         INT4 NOT NULL DEFAULT 52,
+       domain          CHAR(3) NOT NULL DEFAULT '',
+       PRIMARY KEY (listno));
+
+/* ezmlm-get when creating a digests inserts a cookie here. Sublists check it*/
+CREATE TABLE ${TROOT}_digest_cookie (
+       msgnum          INT4 NOT NULL,
+       tai             TIMESTAMP NOT NULL DEFAULT now(),
+       cookie          CHAR(20) NOT NULL,
+       chunk           INT4 NOT NULL DEFAULT 0,
+       bodysize        INT4 NOT NULL DEFAULT 0,
+       PRIMARY KEY (msgnum));
+
+/* ezmlm-get and digest sublists log here when the message is done */
+/* done=0 for arrived, done=1 for sent. tai reflects last change */
+CREATE TABLE ${TROOT}_digest_mlog (
+       msgnum          INT4 NOT NULL,
+       listno          INT4 NOT NULL,
+       tai             TIMESTAMP DEFAULT now(),
+       subs            INT4 NOT NULL DEFAULT 0,
+       done            INT4 NOT NULL DEFAULT 0,
+       PRIMARY KEY (listno,msgnum,done));
+
+EOF
+
+fi
+exit 0
+
+
diff --git a/sub_pgsql/issub.c b/sub_pgsql/issub.c
new file mode 100644 (file)
index 0000000..42b744d
--- /dev/null
@@ -0,0 +1,165 @@
+/*$Id: issub.c,v 1.4 1999/12/11 03:04:03 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "strerr.h"
+#include "error.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "errtxt.h"
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static stralloc addr = {0};
+static stralloc lcaddr = {0};
+static stralloc line = {0};
+static stralloc fn = {0};
+static substdio ss;
+static char ssbuf[512];
+
+char *issub(dbname,userhost,tab,fatal)
+/* Returns (char *) to match if userhost is in the subscriber database     */
+/* dbname, 0 otherwise. dbname is a base directory for a list and may NOT  */
+/* be NULL        */
+/* NOTE: The returned pointer is NOT VALID after a subsequent call to issub!*/
+
+char *dbname;          /* directory to basedir */
+char *userhost;
+char *tab;             /* override table name */
+char *fatal;
+
+{
+  PGresult *result;
+  char *ret;
+  char *table;
+
+  int fd;
+  unsigned int j;
+  uint32 h,lch;
+  char ch,lcch;
+  int match;
+
+  table = tab;
+  if ((ret = opensql(dbname,&table))) {
+    if (*ret) strerr_die2x(111,fatal,ret);
+                                               /* fallback to local db */
+
+    if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
+    if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
+
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len) return 0;
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+    if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+    case_lowerb(lcaddr.s + 1,j - 1);   /* totally lc version of addr */
+
+    h = 5381;
+    lch = h;                   /* make hash for both for backwards comp */
+    for (j = 0;j < addr.len;++j) {     /* (lcaddr.len == addr.len) */
+      h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+      lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+    }
+    ch = 64 + (h % 53);
+    lcch = 64 + (lch % 53);
+
+    if (!stralloc_0(&addr)) die_nomem(fatal);
+    if (!stralloc_0(&lcaddr)) die_nomem(fatal);
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
+    if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
+    if (!stralloc_0(&fn)) die_nomem(fatal);
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
+    } else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1)
+          strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+        if (!match) break;
+        if (line.len == lcaddr.len)
+          if (!case_diffb(line.s,line.len,lcaddr.s))
+            { close(fd); return line.s+1; }
+      }
+
+      close(fd);
+    }
+       /* here if file not found or (file found && addr not there) */
+
+    if (ch == lcch) return 0;
+
+       /* try case sensitive hash for backwards compatibility */
+    fn.s[fn.len - 2] = ch;
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
+      return 0;
+    }
+    substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+    for (;;) {
+      if (getln(&ss,&line,&match,'\0') == -1)
+        strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+      if (!match) break;
+      if (line.len == addr.len)
+        if (!case_diffb(line.s,line.len,addr.s))
+          { close(fd); return line.s+1; }
+    }
+
+    close(fd);
+
+    return 0;
+  } else {                                             /* SQL version  */
+       /* SELECT address FROM list WHERE address = 'userhost' AND hash */
+       /* BETWEEN 0 AND 52. Without the hash restriction, we'd make it */
+       /* even easier to defeat. Just faking sender to the list name would*/
+       /* work. Since sender checks for posts are bogus anyway, I don't */
+       /* know if it's worth the cost of the "WHERE ...". */
+
+    if (!stralloc_copys(&addr,userhost)) die_nomem(fatal);
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len) return 0;
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+
+    if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line," WHERE address ~* '^")) die_nomem(fatal);
+    if (!stralloc_cat(&line,&addr)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"$'")) die_nomem(fatal);
+
+    if (!stralloc_0(&line)) die_nomem(fatal);
+    result = PQexec(psql,line.s);
+    if (result == NULL)
+      strerr_die2x(111,fatal,PQerrorMessage(psql));
+    if (PQresultStatus(result) != PGRES_TUPLES_OK )
+      strerr_die2x(111,fatal,PQresultErrorMessage(result));
+
+    /* No data returned in QUERY */
+    if (PQntuples(result) < 1)
+      return (char *)0;
+
+    if (!stralloc_copyb(&line,PQgetvalue(result,0,0),PQgetlength(result,0,0)))
+       die_nomem(fatal);
+    if (!stralloc_0(&line)) die_nomem(fatal);
+
+    PQclear(result);
+    return line.s;
+  }
+}
+
diff --git a/sub_pgsql/logmsg.c b/sub_pgsql/logmsg.c
new file mode 100644 (file)
index 0000000..8f734ec
--- /dev/null
@@ -0,0 +1,83 @@
+/*$Id: logmsg.c,v 1.3 1999/12/23 02:40:57 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "errtxt.h"
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static stralloc logline = {0};
+static strnum[FMT_ULONG];
+
+char *logmsg(dir,num,listno,subs,done)
+/* creates an entry for message num and the list listno and code "done". */
+/* Returns NULL on success, "" if dir/sql was not found, and the error   */
+/* string on error.   NOTE: This routine does nothing for non-sql lists! */
+char *dir;
+unsigned long num;
+unsigned long listno;
+unsigned long subs;
+int done;
+{
+  char *table = (char *) 0;
+  char *ret;
+
+  PGresult *result;
+  PGresult *result2;
+
+  if ((ret = opensql(dir,&table))) {
+    if (*ret)
+      return ret;
+    else
+      return (char *) 0;       /* no SQL => success */
+  }
+  if (!stralloc_copys(&logline,"INSERT INTO ")) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,table)) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,"_mlog (msgnum,listno,subs,done) VALUES ("))
+       return ERR_NOMEM;
+  if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,num))) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,",")) return ERR_NOMEM;
+  if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,listno)))
+       return ERR_NOMEM;
+  if (!stralloc_cats(&logline,",")) return ERR_NOMEM;
+  if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,subs))) return ERR_NOMEM;
+  if (!stralloc_cats(&logline,",")) return ERR_NOMEM;
+  if (done < 0) {
+    done = - done;
+    if (!stralloc_append(&logline,"-")) return ERR_NOMEM;
+  }
+  if (!stralloc_catb(&logline,strnum,fmt_uint(strnum,done))) return ERR_NOMEM;
+  if (!stralloc_append(&logline,")")) return ERR_NOMEM;
+
+  if (!stralloc_0(&logline)) return ERR_NOMEM;
+  result = PQexec(psql,logline.s);
+  if(result==NULL)
+    return (PQerrorMessage(psql));
+  if(PQresultStatus(result) != PGRES_COMMAND_OK) { /* Check if duplicate */
+    if (!stralloc_copys(&logline,"SELECT msgnum FROM ")) return ERR_NOMEM;
+    if (!stralloc_cats(&logline,table)) return ERR_NOMEM;
+    if (!stralloc_cats(&logline,"_mlog WHERE msgnum = ")) return ERR_NOMEM;
+    if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,num)))
+      return ERR_NOMEM;
+    if (!stralloc_cats(&logline," AND listno = ")) return ERR_NOMEM;
+    if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,listno)))
+      return ERR_NOMEM;
+    if (!stralloc_cats(&logline," AND done = ")) return ERR_NOMEM;
+    if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,done)))
+      return ERR_NOMEM;
+    /* Query */
+    if (!stralloc_0(&logline)) return ERR_NOMEM;
+    result2 = PQexec(psql,logline.s);
+    if (result2 == NULL)
+      return (PQerrorMessage(psql));
+    if (PQresultStatus(result2) != PGRES_TUPLES_OK)
+      return (char *) (PQresultErrorMessage(result2));
+    /* No duplicate, return ERROR from first query */
+    if (PQntuples(result2)<1)
+      return (char *) (PQresultErrorMessage(result));
+    PQclear(result2);
+  }
+  PQclear(result);
+  return (char *) 0;
+}
diff --git a/sub_pgsql/opensql.c b/sub_pgsql/opensql.c
new file mode 100644 (file)
index 0000000..265822a
--- /dev/null
@@ -0,0 +1,111 @@
+/*$Id: opensql.c,v 1.4 1999/11/28 19:55:31 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include <unistd.h>
+#include <libpq-fe.h> 
+
+static stralloc myp = {0};
+static stralloc ers = {0};
+static stralloc fn = {0};
+static stralloc ourdb = {0};
+static char *ourtable = (char *) 0;
+
+char *opensql(dbname,table)
+/* reads the file dbname/sql, and if the file exists, parses it into the    */
+/* components. The string should be host:port:user:pw:db:table.         If  */
+/* the file does not exists, returns "". On success returns NULL. On error  */
+/* returns error string for temporary error. If table is NULL it is         */
+/* left alone. If *table is not null, it overrides the table in the sql     */
+/* file. If we already opended dbname the cached info is used, rather than  */
+/* rereading the file. Note that myp is static and all pointers point to it.*/
+char *dbname;  /* database directory */
+char **table;  /* table root_name */
+
+{
+  char *host = (char *) 0;
+  char *port = (char *) 0;
+  char *db = "ezmlm";          /* default */
+  char *user = (char *) 0;
+  char *pw = (char *) 0;
+  unsigned int j;
+
+  if (!stralloc_copys(&fn,dbname)) return ERR_NOMEM;
+  if (fn.len == ourdb.len && !str_diffn(ourdb.s,fn.s,fn.len)) {
+    if (table) {
+      if (*table) ourtable = *table;
+      else *table = ourtable;
+    }
+    return 0;
+  }
+  if (!stralloc_cats(&fn,"/sql")) return ERR_NOMEM;
+  if (!stralloc_0(&fn)) return ERR_NOMEM;
+               /* host:port:db:table:user:pw:name */
+
+  myp.len = 0;
+  switch (slurp(fn.s,&myp,128)) {
+       case -1:        if (!stralloc_copys(&ers,ERR_READ)) return ERR_NOMEM;
+                       if (!stralloc_cat(&ers,&fn)) return ERR_NOMEM;
+                       if (!stralloc_0(&ers)) return ERR_NOMEM;
+                       return ers.s;
+       case 0: return "";
+  }
+  if (!stralloc_copy(&ourdb,&fn)) return ERR_NOMEM;
+  if (!stralloc_append(&myp,"\n")) return ERR_NOMEM;
+  for (j=0; j< myp.len; ++j) {
+    if (myp.s[j] == '\n') { myp.s[j] = '\0'; break; }
+  }
+                                               /* get connection parameters */
+  if (!stralloc_0(&myp)) return ERR_NOMEM;
+  host = myp.s;
+  if (myp.s[j = str_chr(myp.s,':')]) {
+    port = myp.s + j++;
+    *(port++) = '\0';
+    if (myp.s[j += str_chr(myp.s+j,':')]) {
+      user = myp.s + j++;
+      *(user++) = '\0';
+      if (myp.s[j += str_chr(myp.s+j,':')]) {
+        pw = myp.s + j++;
+       *(pw++) = '\0';
+       if (myp.s[j += str_chr(myp.s+j,':')]) {
+          db = myp.s + j++;
+         *(db++) = '\0';
+         if (myp.s[j += str_chr(myp.s+j,':')]) {
+           ourtable = myp.s + j++;
+           *(ourtable++) = '\0';
+         }
+       }
+      }
+    }
+  }
+
+  if (host && !*host) host = (char *) 0;
+  if (user && !*user) user = (char *) 0;
+  if (pw && !*pw) pw = (char *) 0;
+  if (db && !*db) db = (char *) 0;
+  if (ourtable && !*ourtable) ourtable = (char *) 0;
+  if (table) {
+    if (*table) ourtable = *table;
+    else *table = ourtable;
+    if (!*table) return ERR_NO_TABLE;
+  }
+  if (!psql) {
+    /* Make connection to database */
+    psql = PQsetdbLogin( host, port, NULL, NULL, db, user, pw);
+    /* Check  to see that the backend connection was successfully made */
+    if (PQstatus(psql) == CONNECTION_BAD)
+      return PQerrorMessage(psql);
+  }
+  return (char *) 0;
+}
+
+void closesql()
+/* close connection to SQL server, if open */
+{
+  if (psql) PQfinish(psql);
+  psql = (void *) 0; /* Destroy pointer */
+  return;
+}
+
diff --git a/sub_pgsql/putsubs.c b/sub_pgsql/putsubs.c
new file mode 100644 (file)
index 0000000..c53213c
--- /dev/null
@@ -0,0 +1,128 @@
+#include "error.h"
+#include "strerr.h"
+#include "readwrite.h"
+#include "str.h"
+#include "fmt.h"
+#include "stralloc.h"
+#include "open.h"
+#include "substdio.h"
+#include "case.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include "qmail.h"
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static substdio ssin;
+static char inbuf[512];
+char strnum[FMT_ULONG];
+static stralloc line = {0};
+static stralloc fn = {0};
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static void die_write(fatal)
+char *fatal;
+{
+  strerr_die3x(111,fatal,ERR_WRITE,"stdout");
+}
+
+unsigned long putsubs(dbname,hash_lo,hash_hi,
+       subwrite,flagsql,fatal)
+/* Outputs all userhostesses in 'dbname' to stdout. If userhost is not null */
+/* that userhost is excluded. 'dbname' is the base directory name. For the  */
+/* mysql version, dbname is the directory where the file "sql" with mysql   */
+/* access info is found. If this file is not present or if flagmysql is not */
+/* set, the routine falls back to the old database style. subwrite must be a*/
+/* function returning >=0 on success, -1 on error, and taking arguments     */
+/* (char* string, unsigned int length). It will be called once per address  */
+/* and should take care of newline or whatever needed for the output form.  */
+
+char *dbname;          /* database base dir */
+unsigned long hash_lo;
+unsigned long hash_hi;
+int subwrite();                /* write function. */
+int flagsql;
+char *fatal;           /* fatal error string */
+
+{
+  PGresult *result;
+  int row_nr;
+  int length;
+  char *row;
+  char *table = (char *) 0;
+
+  unsigned int i;
+  int fd;
+  unsigned long no = 0L;
+  int match;
+  unsigned int hashpos;
+  char *ret = (char *) 0;
+
+  if (!flagsql || (ret = opensql(dbname,&table))) {
+    if (flagsql && *ret) strerr_die2x(111,fatal,ret);
+                                               /* fallback to local db */
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_catb(&fn,"/subscribers/?",15)) die_nomem(fatal);
+                               /* NOTE: Also copies terminal '\0' */
+    hashpos = fn.len - 2;
+    if (hash_lo > 52) hash_lo = 52;
+    if (hash_hi > 52) hash_hi = 52;
+    if (hash_hi < hash_lo) hash_hi = hash_lo;
+
+    for (i = hash_lo;i <= hash_hi;++i) {
+      fn.s[hashpos] = 64 + i;  /* hash range 0-52 */
+      fd = open_read(fn.s);
+      if (fd == -1) {
+        if (errno != error_noent)
+         strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+      } else {
+        substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+        for (;;) {
+          if (getln(&ssin,&line,&match,'\0') == -1)
+            strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+          if (!match)
+            break;
+          if (subwrite(line.s + 1,line.len - 2) == -1) die_write(fatal);
+          no++;
+        }
+        close(fd);
+      }
+    }
+    return no;
+
+  } else {                                     /* SQL Version */
+
+                                               /* main query */
+    if (!stralloc_copys(&line,"SELECT address FROM "))
+               die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line," WHERE hash BETWEEN ")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,hash_lo)))
+               die_nomem(fatal);
+    if (!stralloc_cats(&line," AND ")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,hash_hi)))
+      die_nomem(fatal);
+    if (!stralloc_0(&line)) die_nomem(fatal);
+    result = PQexec(psql,line.s);
+    if (result == NULL)
+      strerr_die2x(111,fatal,PQerrorMessage(psql));
+    if (PQresultStatus(result) != PGRES_TUPLES_OK)
+      strerr_die2x(111,fatal,PQresultErrorMessage(result));
+
+    no = 0;
+    for (row_nr=0;row_nr<PQntuples(result);row_nr++) {
+      /* this is safe even if someone messes with the address field def */
+      length = PQgetlength(result,row_nr,0);
+      row = PQgetvalue(result,row_nr,0);
+      if (subwrite(row,length) == -1) die_write(fatal);
+      no++;                                    /* count for list-list fxn */
+    }
+    PQclear(result);
+    return no;
+  }
+}
diff --git a/sub_pgsql/searchlog.c b/sub_pgsql/searchlog.c
new file mode 100644 (file)
index 0000000..efbad0a
--- /dev/null
@@ -0,0 +1,166 @@
+/*$Id: searchlog.c,v 1.2 1999/10/07 23:31:01 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "case.h"
+#include "scan.h"
+#include "stralloc.h"
+#include "str.h"
+#include "open.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "error.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static stralloc line = {0};
+static stralloc outline = {0};
+static date[DATE822FMT];
+static datetime_sec when;
+static struct datetime dt;
+static substdio ssin;
+static char inbuf[256];
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static void lineout(subwrite,fatal)
+int subwrite();
+char *fatal;
+{
+  (void) scan_ulong(line.s,&when);
+  datetime_tai(&dt,when);              /* there is always at least a '\n' */
+  if (!stralloc_copyb(&outline,date,date822fmt(date,&dt) - 1))
+       die_nomem(fatal);
+  if (!stralloc_cats(&outline,": ")) die_nomem(fatal);
+  if (!stralloc_catb(&outline,line.s,line.len - 1)) die_nomem(fatal);
+  if (subwrite(outline.s,outline.len) == -1)
+       strerr_die3x(111,fatal,ERR_WRITE,"output");
+  return;
+}
+
+void searchlog(dir,search,subwrite,fatal)
+/* opens dir/Log, and outputs via subwrite(s,len) any line that matches   */
+/* search. A '_' is search is a wildcard. Any other non-alphanum/'.' char */
+/* is replaced by a '_'. mysql version. Falls back on "manual" search of  */
+/* local Log if no mysql connect info. */
+
+char *dir;             /* work directory */
+char *search;          /* search string */
+int subwrite();                /* output fxn */
+char *fatal;           /* fatal */
+{
+
+  register unsigned char x;
+  register unsigned char y;
+  register unsigned char *cp;
+  register unsigned char *cpsearch;
+  unsigned register char *cps;
+  unsigned register char ch;
+  unsigned char *cplast, *cpline;
+  unsigned int searchlen;
+  int fd,match;
+  char *ret;
+
+  PGresult *result;
+  int row_nr;
+  int length;
+  char *row;
+
+  char *table = (char *) 0;
+
+  if (!search) search = "";    /* defensive */
+  searchlen = str_len(search);
+  case_lowerb(search,searchlen);
+  cps = (unsigned char *) search;
+  while ((ch = *(cps++))) {    /* search is potentially hostile */
+    if (ch >= 'a' && ch <= 'z') continue;
+    if (ch >= '0' && ch <= '9') continue;
+    if (ch == '.' || ch == '_') continue;
+    *(cps - 1) = '_';          /* will match char specified as well */
+  }
+
+  if ((ret = opensql(dir,&table))) {
+    if (*ret) strerr_die2x(111,fatal,ret);
+                                               /* fallback to local log */
+  if (!stralloc_copys(&line,dir)) die_nomem(fatal);
+  if (!stralloc_cats(&line,"/Log")) die_nomem(fatal);
+  if (!stralloc_0(&line)) die_nomem(fatal);
+  fd = open_read(line.s);
+  if (fd == -1)
+    if (errno != error_noent)
+       strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
+    else
+        strerr_die3x(100,fatal,line.s,ERR_NOEXIST);
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,fatal,ERR_READ_INPUT);
+    if (!match) break;
+    if (!searchlen) {
+      lineout(subwrite,fatal);
+    } else {
+      cpline = (unsigned char *) line.s - 1;
+      cplast = cpline + line.len - searchlen; /* line has \0 at the end */
+      while ((cp = ++cpline) <= cplast) {
+       cpsearch = (unsigned char *) search;
+       for (;;) {
+         x = *cpsearch++;
+         if (!x) break;
+         y = *cp++ - 'A';
+         if (y <= (unsigned char) ('Z' - 'A')) y += 'a'; else y += 'A';
+         if (x != y && x != '_') break;                /* '_' = wildcard */
+       }
+       if (!x) {
+         lineout(subwrite,fatal);
+         break;
+       }
+      }
+    }
+  }
+  close(fd);
+  } else {
+
+/* SELECT (*) FROM list_slog WHERE fromline LIKE '%search%' OR address   */
+/* LIKE '%search%' ORDER BY tai; */
+/* The '*' is formatted to look like the output of the non-mysql version */
+/* This requires reading the entire table, since search fields are not   */
+/* indexed, but this is a rare query and time is not of the essence.     */
+       
+    if (!stralloc_cats(&line,"SELECT tai::datetime||': '||tai::int8||' '"
+    "||address||' '||edir||etype||' '||fromline FROM ")) die_nomem(fatal);
+
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"_slog ")) die_nomem(fatal);
+    if (*search) {     /* We can afford to wait for LIKE '%xx%' */
+      if (!stralloc_cats(&line,"WHERE fromline LIKE '%")) die_nomem(fatal);
+      if (!stralloc_cats(&line,search)) die_nomem(fatal);
+      if (!stralloc_cats(&line,"%' OR address LIKE '%")) die_nomem(fatal);
+      if (!stralloc_cats(&line,search)) die_nomem(fatal);
+      if (!stralloc_cats(&line,"%'")) die_nomem(fatal);
+    }  /* ordering by tai which is an index */
+    if (!stralloc_cats(&line," ORDER by tai")) die_nomem(fatal);
+      
+    if (!stralloc_0(&line)) die_nomem(fatal);  
+    result = PQexec(psql,line.s);
+    if (result == NULL)
+      strerr_die2x(111,fatal,PQerrorMessage(psql));
+    if (PQresultStatus(result) != PGRES_TUPLES_OK)
+      strerr_die2x(111,fatal,PQresultErrorMessage(result));
+    
+    for(row_nr=0; row_nr<PQntuples(result); row_nr++) {
+      row = PQgetvalue(result,row_nr,0);
+      length = PQgetlength(result,row_nr,0);
+      if (subwrite(row,length) == -1) die_write(fatal);
+    }
+    PQclear(result);
+       
+  }
+}
diff --git a/sub_pgsql/subscribe.c b/sub_pgsql/subscribe.c
new file mode 100644 (file)
index 0000000..3237be1
--- /dev/null
@@ -0,0 +1,371 @@
+/*$Id: subscribe.c,v 1.3 1999/10/07 23:31:01 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "strerr.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "lock.h"
+#include "error.h"
+#include "subscribe.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "errtxt.h"
+#include "log.h"
+#include "idx.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static stralloc addr = {0};
+static stralloc lcaddr = {0};
+static stralloc line = {0};
+static stralloc domain = {0};
+static stralloc logline = {0};
+static stralloc fnnew = {0};
+static stralloc fn = {0};
+static stralloc fnlock = {0};
+
+void die_read(fatal)
+char *fatal;
+{
+  strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+}
+
+void die_write(fatal)
+char *fatal;
+{
+  strerr_die4sys(111,fatal,ERR_WRITE,fnnew.s,": ");
+}
+
+static int fd;
+static substdio ss;
+static char ssbuf[256];
+static int fdnew;
+static substdio ssnew;
+static char ssnewbuf[256];
+
+int subscribe(dbname,userhost,flagadd,comment,event,flagsql,
+       forcehash,tab,fatal)
+/* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database  */
+/* dbname. Comment is e.g. the subscriber from line or name. It is added to  */
+/* the log. Event is the action type, e.g. "probe", "manual", etc. The       */
+/* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0   */
+/* on failure. If flagmysql is set and the file "sql" is found in the        */
+/* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */
+/* >=0 it is used in place of the calculated hash. This makes it possible to */
+/* add addresses with a hash that does not exist. forcehash has to be 0..99. */
+/* for unsubscribes, the address is only removed if forcehash matches the    */
+/* actual hash. This way, ezmlm-manage can be prevented from touching certain*/
+/* addresses that can only be removed by ezmlm-unsub. Usually, this would be */
+/* used for sublist addresses (to avoid removal) and sublist aliases (to     */
+/* prevent users from subscribing them (although the cookie mechanism would  */
+/* prevent the resulting duplicate message from being distributed. */
+
+char *dbname;
+char *userhost;
+int flagadd;
+char *comment;
+char *event;
+int flagsql;
+int forcehash;
+char *tab;
+char *fatal;
+{
+  int fdlock;
+
+  PGresult *result;
+
+  char *cpat;
+  char szhash[3] = "00";
+  char *r = (char *) 0;
+  char *table = (char *) 0;
+  char **ptable = &table;
+
+  unsigned int j;
+  uint32 h,lch;
+  unsigned char ch,lcch;
+  int match;
+  int flagwasthere;
+
+  if (userhost[str_chr(userhost,'\n')])
+    strerr_die2x(100,fatal,ERR_ADDR_NL);
+
+  if (tab) ptable = &tab;
+
+  if (!flagsql || (r = opensql(dbname,ptable))) {
+    if (r && *r) strerr_die2x(111,fatal,r);
+                                               /* fallback to local db */
+    if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
+    if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
+    if (addr.len > 401)
+      strerr_die2x(100,fatal,ERR_ADDR_LONG);
+
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len)
+      strerr_die2x(100,fatal,ERR_ADDR_AT);
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+    if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+    case_lowerb(lcaddr.s + 1,j - 1);   /* make all-lc version of address */
+
+    if (forcehash >= 0 && forcehash <= 52) {
+      ch = lcch = (unsigned char) forcehash;
+    } else {
+      h = 5381;
+      lch = h;
+      for (j = 0;j < addr.len;++j) {
+        h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+        lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+      }
+      lcch = 64 + (lch % 53);
+      ch = 64 + (h % 53);
+    }
+
+    if (!stralloc_0(&addr)) die_nomem(fatal);
+    if (!stralloc_0(&lcaddr)) die_nomem(fatal);
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_copys(&fnlock,dbname)) die_nomem(fatal);
+
+    if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
+    if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
+    if (!stralloc_copy(&fnnew,&fn)) die_nomem(fatal);
+       /* code later depends on fnnew = fn + 'n' */
+    if (!stralloc_cats(&fnnew,"n")) die_nomem(fatal);
+    if (!stralloc_cats(&fnlock,"/lock")) die_nomem(fatal);
+    if (!stralloc_0(&fnnew)) die_nomem(fatal);
+    if (!stralloc_0(&fn)) die_nomem(fatal);
+    if (!stralloc_0(&fnlock)) die_nomem(fatal);
+
+    fdlock = open_append(fnlock.s);
+    if (fdlock == -1)
+      strerr_die4sys(111,fatal,ERR_OPEN,fnlock.s,": ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,fatal,ERR_OBTAIN,fnlock.s,": ");
+
+                               /* do lower case hashed version first */
+    fdnew = open_trunc(fnnew.s);
+    if (fdnew == -1) die_write(fatal);
+    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+    flagwasthere = 0;
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent) { close(fdnew); die_read(fatal); }
+    }
+    else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1) {
+         close(fd); close(fdnew); die_read(fatal);
+        }
+        if (!match) break;
+        if (line.len == addr.len)
+          if (!case_diffb(line.s,line.len,addr.s)) {
+           flagwasthere = 1;
+           if (!flagadd)
+             continue;
+         }
+        if (substdio_bput(&ssnew,line.s,line.len) == -1) {
+         close(fd); close(fdnew); die_write(fatal);
+        }
+      }
+
+      close(fd);
+    }
+
+    if (flagadd && !flagwasthere)
+      if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
+        close(fdnew); die_write(fatal);
+      }
+
+    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
+    if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
+    close(fdnew);
+
+    if (rename(fnnew.s,fn.s) == -1)
+      strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
+
+    if ((ch == lcch) || flagwasthere) {
+      close(fdlock);
+      if (flagadd ^ flagwasthere) {
+        if (!stralloc_0(&addr)) die_nomem(fatal);
+        log(dbname,event,addr.s+1,comment);
+        return 1;
+      }
+      return 0;
+    }
+
+                       /* If unsub and not found and hashed differ, OR */
+                       /* sub and not found (so added with new hash) */
+                       /* do the 'case-dependent' hash */
+
+    fn.s[fn.len - 2] = ch;
+    fnnew.s[fnnew.len - 3] = ch;
+    fdnew = open_trunc(fnnew.s);
+    if (fdnew == -1) die_write(fatal);
+    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent) { close(fdnew); die_read(fatal); }
+    } else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1)
+          { close(fd); close(fdnew); die_read(fatal); }
+        if (!match) break;
+        if (line.len == addr.len)
+          if (!case_diffb(line.s,line.len,addr.s)) {
+            flagwasthere = 1;
+            continue;  /* always want to remove from case-sensitive hash */
+          }
+        if (substdio_bput(&ssnew,line.s,line.len) == -1)
+          { close(fd); close(fdnew); die_write(fatal); }
+      }
+
+      close(fd);
+    }
+
+    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
+    if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
+    close(fdnew);
+
+    if (rename(fnnew.s,fn.s) == -1)
+      strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
+
+    close(fdlock);
+    if (flagadd ^ flagwasthere) {
+      if (!stralloc_0(&addr)) die_nomem(fatal);
+      log(dbname,event,addr.s+1,comment);
+      return 1;
+    }
+    return 0;
+
+  } else {                             /* SQL version */
+    domain.len = 0;                    /* clear domain */
+                                       /* lowercase and check address */
+    if (!stralloc_copys(&addr,userhost)) die_nomem(fatal);
+    if (addr.len > 255)                        /* this is 401 in std ezmlm. 255 */
+                                       /* should be plenty! */
+      strerr_die2x(100,fatal,ERR_ADDR_LONG);
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len)
+      strerr_die2x(100,fatal,ERR_ADDR_AT);
+    cpat = addr.s + j;
+    case_lowerb(cpat + 1,addr.len - j - 1);
+
+    if (forcehash < 0) {
+      if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+      case_lowerb(lcaddr.s,j);         /* make all-lc version of address */
+      h = 5381;
+      for (j = 0;j < lcaddr.len;++j) {
+        h = (h + (h << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+      }
+      ch = (h % 53);                   /* 0 - 52 */
+    } else
+      ch = (forcehash % 100);
+
+    szhash[0] = '0' + ch / 10;         /* hash for sublist split */
+    szhash[1] = '0' + (ch % 10);
+
+    if (flagadd) {
+      if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(fatal);
+      if (!stralloc_cats(&line,table)) die_nomem(fatal);
+      if (!stralloc_cats(&line," WHERE address ~* '^")) die_nomem(fatal);
+      if (!stralloc_cat(&line,&addr)) die_nomem(fatal);        /* addr */
+      if (!stralloc_cats(&line,"$'")) die_nomem(fatal);
+      if (!stralloc_0(&line)) die_nomem(fatal);
+      result = PQexec(psql,line.s);
+      if (result == NULL)
+       strerr_die2x(111,fatal,PQerrorMessage(psql));
+      if (PQresultStatus(result) != PGRES_TUPLES_OK)
+       strerr_die2x(111,fatal,PQresultErrorMessage(result));
+
+      if (PQntuples(result)>0) {                       /* there */
+       PQclear(result);
+        return 0;                                              /* there */
+      } else {                                                 /* not there */
+       PQclear(result);
+       if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(fatal);
+       if (!stralloc_cats(&line,table)) die_nomem(fatal);
+       if (!stralloc_cats(&line," (address,hash) VALUES ('"))
+               die_nomem(fatal);
+       if (!stralloc_cat(&line,&addr)) die_nomem(fatal);       /* addr */
+       if (!stralloc_cats(&line,"',")) die_nomem(fatal);
+       if (!stralloc_cats(&line,szhash)) die_nomem(fatal);     /* hash */
+       if (!stralloc_cats(&line,")")) die_nomem(fatal);
+       if (!stralloc_0(&line)) die_nomem(fatal);
+       result = PQexec(psql,line.s);
+       if (result == NULL)
+         strerr_die2x(111,fatal,PQerrorMessage(psql));
+       if (PQresultStatus(result) != PGRES_COMMAND_OK)
+         strerr_die2x(111,fatal,PQresultErrorMessage(result));
+      }
+    } else {                                                   /* unsub */
+      if (!stralloc_copys(&line,"DELETE FROM ")) die_nomem(fatal);
+      if (!stralloc_cats(&line,table)) die_nomem(fatal);
+      if (!stralloc_cats(&line," WHERE address ~* '^")) die_nomem(fatal);
+      if (!stralloc_cat(&line,&addr)) die_nomem(fatal);        /* addr */
+      if (forcehash >= 0) {
+       if (!stralloc_cats(&line,"$' AND hash=")) die_nomem(fatal);
+       if (!stralloc_cats(&line,szhash)) die_nomem(fatal);
+      } else {
+        if (!stralloc_cats(&line,"$' AND hash BETWEEN 0 AND 52"))
+               die_nomem(fatal);
+      }
+      
+      if (!stralloc_0(&line)) die_nomem(fatal);
+      result = PQexec(psql,line.s);
+      if (result == NULL)
+       strerr_die2x(111,fatal,PQerrorMessage(psql));
+      if (PQresultStatus(result) != PGRES_COMMAND_OK)
+       strerr_die2x(111,fatal,PQresultErrorMessage(result));
+      if (atoi(PQcmdTuples(result))<1)
+       return 0;                               /* address wasn't there*/
+      PQclear(result);
+    }
+
+               /* log to subscriber log */
+               /* INSERT INTO t_slog (address,edir,etype,fromline) */
+               /* VALUES('address',{'+'|'-'},'etype','[comment]') */
+
+    if (!stralloc_copys(&logline,"INSERT INTO ")) die_nomem(fatal);
+    if (!stralloc_cats(&logline,table)) die_nomem(fatal);
+    if (!stralloc_cats(&logline,
+       "_slog (address,edir,etype,fromline) VALUES ('")) die_nomem(fatal);
+    if (!stralloc_cat(&logline,&addr)) die_nomem(fatal);
+    if (flagadd) {                                             /* edir */
+      if (!stralloc_cats(&logline,"','+','")) die_nomem(fatal);
+    } else {
+      if (!stralloc_cats(&logline,"','-','")) die_nomem(fatal);
+    }
+    if (*(event + 1))  /* ezmlm-0.53 uses '' for ezmlm-manage's work */
+      if (!stralloc_catb(&logline,event+1,1)) die_nomem(fatal);        /* etype */
+    if (!stralloc_cats(&logline,"','")) die_nomem(fatal);
+    if (comment && *comment) {
+      if (!stralloc_cats(&logline,comment)) die_nomem(fatal);
+    }
+    if (!stralloc_cats(&logline,"')")) die_nomem(fatal);
+
+    if (!stralloc_0(&logline)) die_nomem(fatal);
+    result = PQexec(psql,logline.s);           /* log (ignore errors) */
+    PQclear(result);
+
+    if (!stralloc_0(&addr))
+               ;                               /* ignore errors */
+    log(dbname,event,addr.s,comment);          /* also log to old log */
+    return 1;                                  /* desired effect */
+  }
+}
diff --git a/sub_pgsql/tagmsg.c b/sub_pgsql/tagmsg.c
new file mode 100644 (file)
index 0000000..a99797a
--- /dev/null
@@ -0,0 +1,115 @@
+/*$Id: tagmsg.c,v 1.2 1999/10/07 23:31:01 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "scan.h"
+#include "fmt.h"
+#include "strerr.h"
+#include "cookie.h"
+#include "slurp.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include "makehash.h"
+#include <unistd.h>
+#include <libpq-fe.h>
+
+static stralloc line = {0};
+static stralloc key = {0};
+static char hash[COOKIE];
+static char strnum[FMT_ULONG]; /* message number as sz */
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(100,fatal,ERR_NOMEM);
+}
+
+void tagmsg(dir,msgnum,seed,action,hashout,bodysize,chunk,fatal)
+/* This routine creates a cookie from num,seed and the */
+/* list key and returns that cookie in hashout. The use of sender/num and */
+/* first char of action is used to make cookie differ between messages,   */
+/* the key is the secret list key. The cookie will be inserted into       */
+/* table_cookie where table and other data is taken from dir/sql. We log  */
+/* arrival of the message (done=0). */
+
+char *dir;                     /* db base dir */
+unsigned long msgnum;          /* number of this message */
+char *seed;                    /* seed. NULL ok, but less entropy */
+char *action;                  /* to make it certain the cookie differs from*/
+                               /* one used for a digest */
+char *hashout;                 /* calculated hash goes here */
+unsigned long bodysize;
+unsigned long chunk;
+char *fatal;
+{
+  PGresult *result;
+  PGresult *result2; /* Need for dupicate check */
+  char *table = (char *) 0;
+  char *ret;
+  unsigned int i;
+
+  strnum[fmt_ulong(strnum,msgnum)] = '\0';     /* message nr ->string*/
+
+    switch(slurp("key",&key,32)) {
+      case -1:
+       strerr_die3sys(111,fatal,ERR_READ,"key: ");
+      case 0:
+       strerr_die3x(100,fatal,"key",ERR_NOEXIST);
+    }
+    cookie(hash,key.s,key.len,strnum,seed,action);
+    for (i = 0; i < COOKIE; i++)
+      hashout[i] = hash[i];
+
+  if ((ret = opensql(dir,&table))) {
+    if (*ret) strerr_die2x(111,fatal,ret);
+    return;                            /* no sql => success */
+
+  } else {
+    if (chunk >= 53L) chunk = 0L;      /* sanity */
+
+       /* INSERT INTO table_cookie (msgnum,cookie) VALUES (num,cookie) */
+       /* (we may have tried message before, but failed to complete, so */
+       /* ER_DUP_ENTRY is ok) */
+    if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(fatal);
+    if (!stralloc_cats(&line,table)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"_cookie (msgnum,cookie,bodysize,chunk) VALUES ("))
+      die_nomem(fatal);
+    if (!stralloc_cats(&line,strnum)) die_nomem(fatal);
+    if (!stralloc_cats(&line,",'")) die_nomem(fatal);
+    if (!stralloc_catb(&line,hash,COOKIE)) die_nomem(fatal);
+    if (!stralloc_cats(&line,"',")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,bodysize)))
+      die_nomem(fatal);
+    if (!stralloc_cats(&line,",")) die_nomem(fatal);
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,chunk))) die_nomem(fatal);
+    if (!stralloc_cats(&line,")")) die_nomem(fatal);
+    
+    if (!stralloc_0(&line)) die_nomem(fatal);
+    result = PQexec(psql,line.s);
+    if (result == NULL)
+      strerr_die2x(111,fatal,PQerrorMessage(psql));
+    if (PQresultStatus(result) != PGRES_COMMAND_OK) { /* Possible tuplicate */
+      if (!stralloc_copys(&line,"SELECT msgnum FROM ")) die_nomem(fatal);
+      if (!stralloc_cats(&line,table)) die_nomem(fatal);         
+      if (!stralloc_cats(&line,"_cookie WHERE msgnum = ")) die_nomem(fatal);
+      if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) 
+       die_nomem(fatal);
+      /* Query */
+      if (!stralloc_0(&line)) die_nomem(fatal);
+      result2 = PQexec(psql,line.s);
+      if (result2 == NULL)
+       strerr_die2x(111,fatal,PQerrorMessage(psql));
+      if (PQresultStatus(result2) != PGRES_TUPLES_OK)
+       strerr_die2x(111,fatal,PQresultErrorMessage(result2));
+      /* No duplicate, return ERROR from first query */
+      if (PQntuples(result2)<1) 
+       strerr_die2x(111,fatal,PQresultErrorMessage(result));
+      PQclear(result2);
+    }
+    PQclear(result);
+
+    if (! (ret = logmsg(dir,msgnum,0L,0L,1))) return;  /* log done=1*/
+    if (*ret) strerr_die2x(111,fatal,ret);
+  }
+
+  return;
+}
diff --git a/sub_std/README b/sub_std/README
new file mode 100644 (file)
index 0000000..d721a22
--- /dev/null
@@ -0,0 +1,31 @@
+$Id: README,v 1.2 1999/02/20 20:03:11 lindberg Exp $
+$Name: ezmlm-idx-040 $
+STANDARD EZMLM DATABASE INTERFACE
+
+These files comprise the ezmlm subscriber database interface. issub.c and
+subscribe.c are backwards compatible with ezmlm-0.53 in terms of function,
+but use a case-insensitive hash for address storage, and take a few
+extra args to be plug-in compatible with alternative interfaces. Logging to
+the subscriber log (DIR/Log) is done from subscribe.c rather than externally,
+and the subscriber from line is logged as well.
+
+searchlog.c allows access to DIR/Log.
+
+putsubs.c does all output of subscriber addresses, both to qmail for subscibers,
+moderator sendouts, etc, and to list subscribers in reply to the -list command.
+
+Alternative subscriber db interface routines should perform equivalent services,
+and fall back to these services if the alternative interface is not configured,
+e.g. if DIR/sql doe not exist.
+
+HOW TO SET UP A CLUSTER OF LIST AND SUBLISTS WITH STD DBS
+
+See FAQ.idx.
+
+See SQL-enabled ezmlm list documentation in FAQ.idx for a different
+trade-off between these points.
+
+
+
+
+
diff --git a/sub_std/checktag.c b/sub_std/checktag.c
new file mode 100644 (file)
index 0000000..f84350a
--- /dev/null
@@ -0,0 +1,46 @@
+/*$Id: checktag.c,v 1.4 1999/10/12 23:38:36 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "scan.h"
+#include "fmt.h"
+#include "cookie.h"
+#include "makehash.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "subscribe.h"
+
+static stralloc key = {0};
+static stralloc line = {0};
+static stralloc quoted = {0};
+static char strnum[FMT_ULONG];
+static char newcookie[COOKIE];
+
+char *checktag (dir,num,listno,action,seed,hash)
+/* reads dir/sql. If not present, checks that the hash of seed matches */
+/* hash and returns success (NULL). If not match returns "". If error, */
+/* returns error string */
+
+char *dir;                             /* the db base dir */
+unsigned long num;                     /* message number */
+unsigned long listno;                  /* bottom of range => slave */
+char *action;
+char *seed;                            /* cookie base */
+char *hash;                            /* cookie */
+{
+
+    if (!seed) return (char *) 0;              /* no data - accept */
+
+    strnum[fmt_ulong(strnum,num)] = '\0';      /* message nr ->string*/
+
+    switch(slurp("key",&key,32)) {
+      case -1:
+       return ERR_READ_KEY;
+      case 0:
+       return ERR_NOEXIST_KEY;
+    }
+
+    cookie(newcookie,key.s,key.len,strnum,seed,action);
+    if (byte_diff(hash,COOKIE,newcookie)) return "";
+    else return (char *) 0;
+
+}
diff --git a/sub_std/conf-sqlcc b/sub_std/conf-sqlcc
new file mode 100644 (file)
index 0000000..ce074b1
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+# the top line will be used when compiling. This version is for
+# linking without mysql support.
diff --git a/sub_std/conf-sqlld b/sub_std/conf-sqlld
new file mode 100644 (file)
index 0000000..dcfa3d1
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+# the top line will be used when linking. This version is for
+# linking without mysql support.
diff --git a/sub_std/ezmlm-mktab b/sub_std/ezmlm-mktab
new file mode 100755 (executable)
index 0000000..828448a
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+echo
+echo "This is the standard database version. You do not need to"
+echo "create any separate databases for this version."
+echo
+exit 1
+
diff --git a/sub_std/issub.c b/sub_std/issub.c
new file mode 100644 (file)
index 0000000..22e6367
--- /dev/null
@@ -0,0 +1,120 @@
+/*$Id: issub.c,v 1.4 1999/10/12 23:38:36 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "strerr.h"
+#include "error.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "errtxt.h"
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static stralloc addr = {0};
+static stralloc lcaddr = {0};
+static stralloc line = {0};
+static stralloc quoted = {0};
+static stralloc fn = {0};
+static substdio ss;
+static char ssbuf[512];
+static char szh[FMT_ULONG];
+
+char *issub(dbname,userhost,tab,fatal)
+/* Returns (char *) to match if userhost is in the subscriber database     */
+/* dbname, 0 otherwise. dbname is a base directory for a list and may NOT  */
+/* be NULL        */
+/* NOTE: The returned pointer is NOT VALID after a subsequent call to issub!*/
+
+char *dbname;          /* directory to basedir */
+char *userhost;
+char *tab;             /* override table name */
+char *fatal;
+
+{
+
+  int fd;
+  unsigned int j;
+  uint32 h,lch;
+  char ch,lcch;
+  int match;
+
+    if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
+    if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
+
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len) return (char *) 0;
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+    if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+    case_lowerb(lcaddr.s + 1,j - 1);   /* totally lc version of addr */
+
+    h = 5381;
+    lch = h;                   /* make hash for both for backwards comp */
+    for (j = 0;j < addr.len;++j) {     /* (lcaddr.len == addr.len) */
+      h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+      lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+    }
+    ch = 64 + (h % 53);
+    lcch = 64 + (lch % 53);
+
+    if (!stralloc_0(&addr)) die_nomem(fatal);
+    if (!stralloc_0(&lcaddr)) die_nomem(fatal);
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
+    if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
+    if (!stralloc_0(&fn)) die_nomem(fatal);
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
+    } else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1)
+          strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+        if (!match) break;
+        if (line.len == lcaddr.len)
+          if (!case_diffb(line.s,line.len,lcaddr.s))
+            { close(fd); return line.s+1; }
+      }
+
+      close(fd);
+    }
+       /* here if file not found or (file found && addr not there) */
+
+    if (ch == lcch) return (char *) 0;
+
+       /* try case sensitive hash for backwards compatibility */
+    fn.s[fn.len - 2] = ch;
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+        strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
+      return (char *) 0;
+    }
+    substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+    for (;;) {
+      if (getln(&ss,&line,&match,'\0') == -1)
+        strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+      if (!match) break;
+      if (line.len == addr.len)
+        if (!case_diffb(line.s,line.len,addr.s))
+          { close(fd); return line.s+1; }
+    }
+
+    close(fd);
+
+    return (char *) 0;
+}
diff --git a/sub_std/logmsg.c b/sub_std/logmsg.c
new file mode 100644 (file)
index 0000000..594a96a
--- /dev/null
@@ -0,0 +1,19 @@
+/*$Id: logmsg.c,v 1.3 1999/10/12 23:38:36 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "errtxt.h"
+
+static stralloc logline = {0};
+static strnum[FMT_ULONG];
+
+char *logmsg(dir,num,listno,subs,done)
+char *dir;
+unsigned long num;
+unsigned long listno;
+unsigned long subs;
+int done;
+{
+      return (char *) 0;       /* no SQL => success */
+}
diff --git a/sub_std/opensql.c b/sub_std/opensql.c
new file mode 100644 (file)
index 0000000..9601da3
--- /dev/null
@@ -0,0 +1,27 @@
+/*$Id: opensql.c,v 1.3 1999/10/12 23:38:36 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "strerr.h"
+#include "errtxt.h"
+#include "subscribe.h"
+
+static stralloc myp = {0};
+static stralloc ers = {0};
+static stralloc fn = {0};
+static stralloc ourdb = {0};
+static char *ourtable = (char *) 0;
+
+char *opensql(dbname,table)
+char *dbname;  /* database directory */
+char **table;  /* table root_name */
+
+{
+       return 0;
+}
+
+void closesql()
+/* close connection to SQL server, if open */
+{
+       return;
+}
+
diff --git a/sub_std/putsubs.c b/sub_std/putsubs.c
new file mode 100644 (file)
index 0000000..d3de6bc
--- /dev/null
@@ -0,0 +1,86 @@
+#include "error.h"
+#include "strerr.h"
+#include "readwrite.h"
+#include "str.h"
+#include "fmt.h"
+#include "stralloc.h"
+#include "open.h"
+#include "substdio.h"
+#include "case.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include "qmail.h"
+
+static substdio ssin;
+static char inbuf[512];
+char strnum[FMT_ULONG];
+static stralloc line = {0};
+static stralloc domains = {0};
+static stralloc quoted = {0};
+static stralloc fn = {0};
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static void die_write(fatal)
+char *fatal;
+{
+  strerr_die3x(111,fatal,ERR_WRITE,"stdout");
+}
+
+unsigned long putsubs(dbname,hash_lo,hash_hi,
+       subwrite,flagsql,fatal)
+/* Outputs all userhostesses in 'dbname' to stdout. If userhost is not null */
+/* that userhost is excluded. 'dbname' is the base directory name. */
+/* subwrite must be a*/
+/* function returning >=0 on success, -1 on error, and taking arguments     */
+/* (char* string, unsigned int length). It will be called once per address  */
+/* and should take care of newline or whatever needed for the output form.  */
+
+char *dbname;          /* database base dir */
+unsigned long hash_lo;
+unsigned long hash_hi;
+int subwrite();                /* write function. */
+int flagsql;
+char *fatal;           /* fatal error string */
+
+{
+
+  unsigned int i;
+  int fd;
+  unsigned long no = 0L;
+  int match;
+  unsigned int hashpos;
+
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_catb(&fn,"/subscribers/?",15)) die_nomem(fatal);
+                               /* NOTE: Also copies terminal '\0' */
+    hashpos = fn.len - 2;
+    if (hash_lo > 52) hash_lo = 52;
+    if (hash_hi > 52) hash_hi = 52;
+    if (hash_hi < hash_lo) hash_hi = hash_lo;
+
+    for (i = hash_lo;i <= hash_hi;++i) {
+      fn.s[hashpos] = 64 + i;  /* hash range 0-52 */
+      fd = open_read(fn.s);
+      if (fd == -1) {
+        if (errno != error_noent)
+         strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+      } else {
+        substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+        for (;;) {
+          if (getln(&ssin,&line,&match,'\0') == -1)
+            strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+          if (!match)
+            break;
+          if (subwrite(line.s + 1,line.len - 2) == -1) die_write(fatal);
+          no++;
+        }
+        close(fd);
+      }
+    }
+    return no;
+}
diff --git a/sub_std/searchlog.c b/sub_std/searchlog.c
new file mode 100644 (file)
index 0000000..1cee6f9
--- /dev/null
@@ -0,0 +1,114 @@
+/*$Id: searchlog.c,v 1.6 1999/10/09 14:22:38 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "case.h"
+#include "scan.h"
+#include "stralloc.h"
+#include "str.h"
+#include "open.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "error.h"
+#include "errtxt.h"
+#include "subscribe.h"
+
+static stralloc line = {0};
+static stralloc outline = {0};
+static char date[DATE822FMT];
+static datetime_sec when;
+static struct datetime dt;
+static substdio ssin;
+static char inbuf[256];
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(100,fatal,ERR_NOMEM);
+}
+
+static void lineout(subwrite,fatal)
+int subwrite();
+char *fatal;
+{
+  (void) scan_ulong(line.s,&when);
+  datetime_tai(&dt,when);              /* there is always at least a '\n' */
+  if (!stralloc_copyb(&outline,date,date822fmt(date,&dt) - 1))
+       die_nomem(fatal);
+  if (!stralloc_cats(&outline,": ")) die_nomem(fatal);
+  if (!stralloc_catb(&outline,line.s,line.len - 1)) die_nomem(fatal);
+  if (subwrite(outline.s,outline.len) == -1)
+       strerr_die3x(111,fatal,ERR_WRITE,"output");
+  return;
+}
+
+void searchlog(dir,search,subwrite,fatal)
+/* opens dir/Log, and outputs via subwrite(s,len) any line that matches */
+/* search. A '_' is search is a wildcard. Any other non-alphanum/'.' char */
+/* is replaced by a '_' */
+
+char *dir;             /* work directory */
+char *search;          /* search string */
+int subwrite();                /* output fxn */
+char *fatal;           /* fatal */
+{
+
+  register unsigned char x;
+  register unsigned char y;
+  register unsigned char *cp;
+  register unsigned char *cpsearch;
+  unsigned register char *cps;
+  unsigned register char ch;
+  unsigned char *cplast, *cpline;
+  unsigned int searchlen;
+  int fd,match;
+
+  searchlen = str_len(search);
+  case_lowerb(search,searchlen);
+  cps = (unsigned char *) search;
+  while ((ch = *(cps++))) {            /* search is potentially hostile */
+    if (ch >= 'a' && ch <= 'z') continue;
+    if (ch >= '0' && ch <= '9') continue;
+    if (ch == '.' || ch == '_') continue;
+    *(cps - 1) = '_';                  /* will [also] match char specified */
+  }
+
+  if (!stralloc_copys(&line,dir)) die_nomem(fatal);
+  if (!stralloc_cats(&line,"/Log")) die_nomem(fatal);
+  if (!stralloc_0(&line)) die_nomem(fatal);
+  fd = open_read(line.s);
+  if (fd == -1)
+    if (errno != error_noent)
+       strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
+    else
+        strerr_die3x(100,fatal,line.s,ERR_NOEXIST);
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,fatal,ERR_READ_INPUT);
+    if (!match) break;
+    if (!searchlen) {
+      lineout(subwrite,fatal);
+    } else {           /* simple case-insensitive search */
+      cpline = (unsigned char *) line.s - 1;
+      cplast = cpline + line.len - searchlen; /* line has \0 at the end */
+      while ((cp = ++cpline) <= cplast) {
+       cpsearch = (unsigned char *) search;
+       for (;;) {
+         x = *cpsearch++;
+         if (!x) break;
+         y = *cp++ - 'A';
+         if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+         if (x != y && x != '_') break;                /* '_' = wildcard */
+       }
+       if (!x) {
+         lineout(subwrite,fatal);
+         break;
+       }
+      }
+    }
+  }
+  close(fd);
+}
diff --git a/sub_std/subscribe.c b/sub_std/subscribe.c
new file mode 100644 (file)
index 0000000..278bc8e
--- /dev/null
@@ -0,0 +1,244 @@
+/*$Id: subscribe.c,v 1.5 1999/10/12 23:38:36 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "strerr.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "lock.h"
+#include "error.h"
+#include "subscribe.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "errtxt.h"
+#include "log.h"
+#include "idx.h"
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,ERR_NOMEM);
+}
+
+static stralloc addr = {0};
+static stralloc lcaddr = {0};
+static stralloc line = {0};
+static stralloc domain = {0};
+static stralloc logline = {0};
+static stralloc quoted = {0};
+static stralloc fnnew = {0};
+static stralloc fn = {0};
+static stralloc fnlock = {0};
+static char szh[FMT_ULONG];
+
+void die_read(fatal)
+char *fatal;
+{
+  strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
+}
+
+void die_write(fatal)
+char *fatal;
+{
+  strerr_die4sys(111,fatal,ERR_WRITE,fnnew.s,": ");
+}
+
+static int fd;
+static substdio ss;
+static char ssbuf[256];
+static int fdnew;
+static substdio ssnew;
+static char ssnewbuf[256];
+
+int subscribe(dbname,userhost,flagadd,comment,event,flagmysql,
+       forcehash,tab,fatal)
+/* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database  */
+/* dbname. Comment is e.g. the subscriber from line or name. It is added to  */
+/* the log. Event is the action type, e.g. "probe", "manual", etc. The       */
+/* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0   */
+/* on failure. If flagmysql is set and the file "sql" is found in the        */
+/* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */
+/* >=0 it is used in place of the calculated hash. This makes it possible to */
+/* add addresses with a hash that does not exist. forcehash has to be 0..99. */
+/* for unsubscribes, the address is only removed if forcehash matches the    */
+/* actual hash. This way, ezmlm-manage can be prevented from touching certain*/
+/* addresses that can only be removed by ezmlm-unsub. Usually, this would be */
+/* used for sublist addresses (to avoid removal) and sublist aliases (to     */
+/* prevent users from subscribing them (although the cookie mechanism would  */
+/* prevent the resulting duplicate message from being distributed. */
+
+char *dbname;
+char *userhost;
+int flagadd;
+char *comment;
+char *event;
+int flagmysql;
+int forcehash;
+char *tab;
+char *fatal;
+{
+  int fdlock;
+
+  char szhash[3] = "00";
+
+  unsigned int j;
+  uint32 h,lch;
+  unsigned char ch,lcch;
+  int match;
+  int flagwasthere;
+
+  if (userhost[str_chr(userhost,'\n')])
+    strerr_die2x(100,fatal,ERR_ADDR_NL);
+
+    if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
+    if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
+    if (addr.len > 401)
+      strerr_die2x(100,fatal,ERR_ADDR_LONG);
+
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j == addr.len)
+      strerr_die2x(100,fatal,ERR_ADDR_AT);
+    case_lowerb(addr.s + j + 1,addr.len - j - 1);
+    if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
+    case_lowerb(lcaddr.s + 1,j - 1);   /* make all-lc version of address */
+
+    if (forcehash >= 0 && forcehash <= 52) {
+      ch = lcch = (unsigned char) forcehash;
+    } else {
+      h = 5381;
+      lch = h;
+      for (j = 0;j < addr.len;++j) {
+        h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+        lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
+      }
+      lcch = 64 + (lch % 53);
+      ch = 64 + (h % 53);
+    }
+
+    if (!stralloc_0(&addr)) die_nomem(fatal);
+    if (!stralloc_0(&lcaddr)) die_nomem(fatal);
+    if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
+    if (!stralloc_copys(&fnlock,dbname)) die_nomem(fatal);
+
+    if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
+    if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
+    if (!stralloc_copy(&fnnew,&fn)) die_nomem(fatal);
+       /* code later depends on fnnew = fn + 'n' */
+    if (!stralloc_cats(&fnnew,"n")) die_nomem(fatal);
+    if (!stralloc_cats(&fnlock,"/lock")) die_nomem(fatal);
+    if (!stralloc_0(&fnnew)) die_nomem(fatal);
+    if (!stralloc_0(&fn)) die_nomem(fatal);
+    if (!stralloc_0(&fnlock)) die_nomem(fatal);
+
+    fdlock = open_append(fnlock.s);
+    if (fdlock == -1)
+      strerr_die4sys(111,fatal,ERR_OPEN,fnlock.s,": ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,fatal,ERR_OBTAIN,fnlock.s,": ");
+
+                               /* do lower case hashed version first */
+    fdnew = open_trunc(fnnew.s);
+    if (fdnew == -1) die_write(fatal);
+    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+    flagwasthere = 0;
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent) { close(fdnew); die_read(fatal); }
+    }
+    else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1) {
+         close(fd); close(fdnew); die_read(fatal);
+        }
+        if (!match) break;
+        if (line.len == addr.len)
+          if (!case_diffb(line.s,line.len,addr.s)) {
+           flagwasthere = 1;
+           if (!flagadd)
+             continue;
+         }
+        if (substdio_bput(&ssnew,line.s,line.len) == -1) {
+         close(fd); close(fdnew); die_write(fatal);
+        }
+      }
+
+      close(fd);
+    }
+
+    if (flagadd && !flagwasthere)
+      if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
+        close(fdnew); die_write(fatal);
+      }
+
+    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
+    if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
+    close(fdnew);
+
+    if (rename(fnnew.s,fn.s) == -1)
+      strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
+
+    if ((ch == lcch) || flagwasthere) {
+      close(fdlock);
+      if (flagadd ^ flagwasthere) {
+        if (!stralloc_0(&addr)) die_nomem(fatal);
+        log(dbname,event,addr.s+1,comment);
+        return 1;
+      }
+      return 0;
+    }
+
+                       /* If unsub and not found and hashed differ, OR */
+                       /* sub and not found (so added with new hash) */
+                       /* do the 'case-dependent' hash */
+
+    fn.s[fn.len - 2] = ch;
+    fnnew.s[fnnew.len - 3] = ch;
+    fdnew = open_trunc(fnnew.s);
+    if (fdnew == -1) die_write(fatal);
+    substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+    fd = open_read(fn.s);
+    if (fd == -1) {
+      if (errno != error_noent) { close(fdnew); die_read(fatal); }
+    } else {
+      substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+      for (;;) {
+        if (getln(&ss,&line,&match,'\0') == -1)
+          { close(fd); close(fdnew); die_read(fatal); }
+        if (!match) break;
+        if (line.len == addr.len)
+          if (!case_diffb(line.s,line.len,addr.s)) {
+            flagwasthere = 1;
+            continue;  /* always want to remove from case-sensitive hash */
+          }
+        if (substdio_bput(&ssnew,line.s,line.len) == -1)
+          { close(fd); close(fdnew); die_write(fatal); }
+      }
+
+      close(fd);
+    }
+
+    if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
+    if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
+    close(fdnew);
+
+    if (rename(fnnew.s,fn.s) == -1)
+      strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
+
+    close(fdlock);
+    if (flagadd ^ flagwasthere) {
+      if (!stralloc_0(&addr)) die_nomem(fatal);
+      log(dbname,event,addr.s+1,comment);
+      return 1;
+    }
+    return 0;
+
+}
diff --git a/sub_std/tagmsg.c b/sub_std/tagmsg.c
new file mode 100644 (file)
index 0000000..0919346
--- /dev/null
@@ -0,0 +1,55 @@
+/*$Id: tagmsg.c,v 1.3 1999/10/12 23:38:36 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "scan.h"
+#include "fmt.h"
+#include "strerr.h"
+#include "cookie.h"
+#include "slurp.h"
+#include "errtxt.h"
+#include "subscribe.h"
+#include "makehash.h"
+
+static stralloc line = {0};
+static stralloc key = {0};
+static char hash[COOKIE];
+static char strnum[FMT_ULONG]; /* message number as sz */
+
+static void die_nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(100,fatal,ERR_NOMEM);
+}
+
+void tagmsg(dir,msgnum,seed,action,hashout,bodysize,chunk,fatal)
+/* This routine creates a cookie from num,seed and the */
+/* list key and returns that cookie in hashout. The use of sender/num and */
+/* first char of action is used to make cookie differ between messages,   */
+/* the key is the secret list key. */
+
+char *dir;                     /* db base dir */
+unsigned long msgnum;          /* number of this message */
+char *seed;                    /* seed. NULL ok, but less entropy */
+char *action;                  /* to make it certain the cookie differs from*/
+                               /* one used for a digest */
+char *hashout;                 /* calculated hash goes here */
+unsigned long bodysize;
+unsigned long chunk;
+char *fatal;
+{
+  unsigned int i;
+
+  strnum[fmt_ulong(strnum,msgnum)] = '\0';     /* message nr ->string*/
+
+    switch(slurp("key",&key,32)) {
+      case -1:
+       strerr_die3sys(111,fatal,ERR_READ,"key: ");
+      case 0:
+       strerr_die3x(100,fatal,"key",ERR_NOEXIST);
+    }
+    cookie(hash,key.s,key.len,strnum,seed,action);
+    for (i = 0; i < COOKIE; i++)
+      hashout[i] = hash[i];
+
+    return;
+}
deleted file mode 100644 (file)
index fee21e2587928759fea8dc2633229f15f52bbd79..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,127 +0,0 @@
-#include "stralloc.h"
-#include "getln.h"
-#include "readwrite.h"
-#include "substdio.h"
-#include "strerr.h"
-#include "open.h"
-#include "byte.h"
-#include "case.h"
-#include "lock.h"
-#include "error.h"
-#include "uint32.h"
-#include "subscribe.h"
-
-static stralloc addr = {0};
-static stralloc line = {0};
-static stralloc fnnew = {0};
-static stralloc fn = {0};
-
-static int fd;
-static substdio ss;
-static char ssbuf[256];
-static int fdnew;
-static substdio ssnew;
-static char ssnewbuf[256];
-
-static int doit(userhost,flagadd)
-char *userhost;
-int flagadd;
-{
-  int j;
-  uint32 h;
-  char ch;
-  int match;
-  int flagwasthere;
-
-  if (userhost[str_chr(userhost,'\n')]) return -8;
-  if (!stralloc_copys(&addr,"T")) return -2;
-  if (!stralloc_cats(&addr,userhost)) return -2;
-  if (addr.len > 401) return -7;
-
-  j = byte_rchr(addr.s,addr.len,'@');
-  if (j == addr.len) return -6;
-  case_lowerb(addr.s + j + 1,addr.len - j - 1);
-
-  h = 5381;
-  for (j = 0;j < addr.len;++j)
-    h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
-  ch = 64 + (h % 53);
-
-  if (!stralloc_0(&addr)) return -2;
-
-  if (!stralloc_copys(&fn,"subscribers/")) return -2;
-  if (!stralloc_catb(&fn,&ch,1)) return -2;
-  if (!stralloc_copy(&fnnew,&fn)) return -2;
-  if (!stralloc_cats(&fnnew,"n")) return -2;
-  if (!stralloc_0(&fnnew)) return -2;
-  if (!stralloc_0(&fn)) return -2;
-
-  fdnew = open_trunc(fnnew.s);
-  if (fdnew == -1) return -4;
-  substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
-
-  flagwasthere = 0;
-
-  fd = open_read(fn.s);
-  if (fd == -1) {
-    if (errno != error_noent) { close(fdnew); return -3; }
-  }
-  else {
-    substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
-
-    for (;;) {
-      if (getln(&ss,&line,&match,'\0') == -1) { close(fd); close(fdnew); return -3; }
-      if (!match) break;
-      if (line.len == addr.len)
-        if (!byte_diff(line.s,line.len,addr.s)) {
-         flagwasthere = 1;
-         if (!flagadd)
-           continue;
-       }
-      if (substdio_bput(&ssnew,line.s,line.len) == -1) { close(fd); close(fdnew); return -4; }
-    }
-
-    close(fd);
-  }
-
-  if (flagadd && !flagwasthere)
-    if (substdio_bput(&ssnew,addr.s,addr.len) == -1) { close(fdnew); return -4; }
-
-  if (substdio_flush(&ssnew) == -1) { close(fdnew); return -4; }
-  if (fsync(fdnew) == -1) { close(fdnew); return -4; }
-  close(fdnew);
-
-  if (rename(fnnew.s,fn.s) == -1) return -5;
-  return flagadd ^ flagwasthere;
-}
-
-struct strerr subscribe_err;
-
-int subscribe(userhost,flagadd)
-char *userhost;
-int flagadd;
-{
-  int fdlock;
-  int r;
-
-  fdlock = open_append("lock");
-  if (fdlock == -1)
-    STRERR_SYS(-1,subscribe_err,"unable to open lock: ")
-  if (lock_ex(fdlock) == -1) {
-    close(fdlock);
-    STRERR_SYS(-1,subscribe_err,"unable to obtain lock: ")
-  }
-
-  r = doit(userhost,flagadd);
-  close(fdlock);
-
-  if (r == -2) STRERR(-1,subscribe_err,"out of memory")
-  if (r == -3) STRERR_SYS3(-1,subscribe_err,"unable to read ",fn.s,": ")
-  if (r == -4) STRERR_SYS3(-1,subscribe_err,"unable to write ",fnnew.s,": ")
-  if (r == -5) STRERR_SYS3(-1,subscribe_err,"unable to move temporary file to ",fn.s,": ")
-  if (r == -6) STRERR(-2,subscribe_err,"address does not contain @")
-  if (r == -7) STRERR(-2,subscribe_err,"address is too long")
-  if (r == -8) STRERR(-2,subscribe_err,"address contains newline")
-
-  return r;
-}
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..1c16baebb9c8c280121683a4258c4b918926504a
--- /dev/null
@@ -0,0 +1 @@
+sub_std/subscribe.c
\ No newline at end of file
index 48a2f1c..ee43887 100644 (file)
@@ -1,10 +1,60 @@
+/*$Id: subscribe.h,v 1.14 1999/10/03 19:23:31 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
 #ifndef SUBSCRIBE_H
 #define SUBSCRIBE_H
 
-#include "strerr.h"
+#include "stralloc.h"
 
-extern struct strerr subscribe_err;
+/* these are the subroutines used for interfacing with the subscriber and  */
+/* moderator address databases. For the put/to address output routines     */
+/* the 'username' if defined is omitted from the output. flagadd = 1 adds  */
+/* a subscriber, flagadd = 0 removes the address. To use e.g. a SQL data-  */
+/* base for addresses, just replace these routines and rebuild ezmlm.      */
+
+#ifdef WITH_PROTO
+
+extern int subscribe(char *dir,char *username,int flagadd,char *from,
+       char *event, int flagmysql, int forcehash,
+       char *table_override, char *FATAL);
+
+extern char *issub(char *dir,char *username, char *table_override, char *FATAL);
+
+extern unsigned long putsubs(char *dir,
+       unsigned long hash_lo, unsigned long hash_hi,
+       int subwrite(), int flagsql, char *fatal);
+
+/*             int subwrite(char *string, unsigned int length); */
+
+extern void tagmsg(char *dir, unsigned long msgnum,
+       char *seed, char *action, char *hashout,
+       unsigned long bodysize, unsigned long chunk, char *fatal);
+
+extern char *logmsg(char *dir, unsigned long msgnum, unsigned long,
+       unsigned long subs, int done);
+
+extern char *checktag(char *dir, unsigned long msgnum, unsigned long listno,
+       char *action, char *seed, char *hash);
+
+extern void searchlog(char *dir, char *search, int subwrite(), char *fatal);
+
+extern char *opensql(char *dir, char **table);
+
+extern void closesql();
+
+#else
 
 extern int subscribe();
+extern char *issub();
+extern unsigned long putsubs();
+extern void tagmsg();
+extern char *logmsg();
+char *checktag();
+extern int subreceipt();
+extern char *getlistno();
+extern char *opensql();
+extern void closesql();
+extern void searchlog();
 
 #endif
+extern void *psql;             /* contains SQL handle */
+#endif
diff --git a/tagmsg.c b/tagmsg.c
new file mode 120000 (symlink)
index 0000000..4f795c9
--- /dev/null
+++ b/tagmsg.c
@@ -0,0 +1 @@
+sub_std/tagmsg.c
\ No newline at end of file
diff --git a/unfoldHDR.c b/unfoldHDR.c
new file mode 100644 (file)
index 0000000..6004f0b
--- /dev/null
@@ -0,0 +1,303 @@
+/*$Id: unfoldHDR.c,v 1.14 1999/11/06 05:25:14 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+
+#include "stralloc.h"
+#include "strerr.h"
+#include "case.h"
+#include "byte.h"
+#include "errtxt.h"
+#include "mime.h"
+
+static stralloc tmpdata = {0};
+
+static int trimre(cpp,cpend,prefix,fatal)
+char **cpp;
+char *cpend;
+stralloc *prefix;
+char *fatal;
+
+{
+  int r = 0;
+  register char *cp;
+  char *cpnew;
+  int junk;
+  unsigned int i,j;
+  unsigned int serial;
+
+  cp = *cpp;
+  serial = prefix->len;                /* pointer to serial number */
+  if (serial)
+    serial = byte_rchr(prefix->s,prefix->len,'#');
+
+  junk = 1;
+  while (junk) {
+    junk = 0;
+    while (cp <= cpend && (*cp == ' ' || *cp == '\t')) cp++;
+    cpnew = cp;
+    while (++cpnew <= cpend) { /* /(..+:\s)/ is a reply indicator */
+      if (*cpnew == ' ') {
+        if (cpnew < cp + 3) break;     /* at least 3 char before ' ' */
+       if (*(cpnew - 1) != ':') break; /* require ':' before ' ' */
+       if (cpnew > cp + 5) {           /* if > 4 char before ':' require */
+         register char ch;
+         ch = *(cpnew - 2);            /* XX^3, XX[3], XX(3) */
+         if (ch != ')' && ch != ']' && (ch < '0' || ch > '9'))
+           break;
+       }
+       junk = 1;
+       r |= 1;
+       cp = cpnew + 1;
+        break;
+      }
+    }
+       /* prefix removal is complicated by the inconsistent handling of ' ' */
+       /* when there are rfc2047-encoded words in the subject. We first     */
+       /* compare prefix before "serial" ignoring space, then skip the      */
+       /* number, then compare after "serial". If both matched we've found  */
+       /* the prefix. */
+    if (serial) {
+      cpnew = cp;
+      i = 0;
+      while (i < serial && cpnew <= cpend) {
+        if (*cpnew != ' ') {
+          if (prefix->s[i] == ' ') {
+            ++i;
+            continue;
+          }
+          if (*cpnew != prefix->s[i]) break;
+          ++i;
+        }
+        ++cpnew;
+      }
+      if (i == serial) {               /* match before serial */
+        j = prefix->len;
+        if (serial != j) {             /* got a '#' */
+          while (cpnew <= cpend &&     /* skip number/space */
+               *cpnew == ' ' || (*cpnew <= '9' && *cpnew >= '0')) ++cpnew;
+          i = serial + 1;
+          while (i < j && cpnew <= cpend) {
+            if (*cpnew != ' ') {
+              if (prefix->s[i] == ' ') {
+                ++i;
+                continue;
+              }
+              if (*cpnew != prefix->s[i]) break;
+              ++i;
+            }
+            ++cpnew;
+          }
+        }
+        if (i == j) {
+          cp = cpnew;
+          junk = 1;
+          r |= 2;
+        }
+      }
+    }
+  }
+  *cpp = cp;
+  return r;
+}
+
+static int trimend(indata,np,fatal)
+char *indata;
+unsigned int *np;
+char *fatal;
+       /* looks at indata of length n from the end removing LWSP & '\n' */
+       /* and any trailing '-Reply'. Sets n to new length and returns:  */
+       /* 0 - not reply, 1 - reply. */
+{
+  char *cplast;
+  int junk;
+  int r = 0;
+
+  if (*np == 0) return 0;
+  cplast = indata + *np - 1;   /* points to last char on line */
+  junk = 1;
+  while (junk) {
+    junk = 0;
+    while (cplast >= indata &&
+             (*cplast == ' ' || *cplast == '\t' ||
+              *cplast == '\r' || *cplast == '\n')) 
+            --cplast;
+    if (cplast - indata  >= 5 && case_startb(cplast - 5,6,"-Reply")) {
+      cplast -= 6;
+      r = 1;
+      junk = 1;
+    }
+  }
+  *np = (unsigned int) (cplast - indata + 1);  /* new length */
+  return r;
+}
+
+int unfoldHDR(indata,n,outdata,charset,prefix,flagtrimsub,fatal)
+char *indata;
+unsigned int n;
+stralloc *outdata;
+char *charset;
+stralloc *prefix;
+int flagtrimsub;
+char *fatal;
+       /* takes a header as indata. Removal of reply-indicators is done */
+       /* but removal of line breaks and Q and B decoding should have   */
+       /* been done. Returns a */
+       /* single line header without trailing \n or \0. Mainly, we      */
+       /* remove redundant shift codes   */
+       /* returns 0 = no reply no prefix */
+       /*         1 = reply no prefix    */
+       /*         2 = no reply, prefix   */
+       /*         3 = reply & pefix      */
+{
+  int r = 0;
+  char *cp,*cpesc,*cpnext,*cpend,*cpout;
+  char state,cset,newcset;
+  int reg,newreg;
+
+  cp = indata;         /* JIS X 0201 -> ISO646 us-ascii */
+  cpend = cp + n - 1;
+  cpnext = cp;
+  if (!stralloc_copys(&tmpdata,"")) die_nomem(fatal);
+  if (!stralloc_ready(&tmpdata,n)) die_nomem(fatal);
+
+  if(!case_diffb(charset,11,"iso-2022-jp")) {
+       /* iso-2022-jp-2 (rfc1554) and its subset iso-2022-jp. The reg #s */
+       /* are from the rfc. Don't ask why they have multiple length G0   */
+       /* charset designations ... JIS X 0201-roman is identical to      */
+       /* iso646 us-ascii except for currency and tilde. Making them the */
+       /* same increases hits without significant loss. JIS X 0208-1978  */
+       /* is superceded by JIS X 0208-1983 and converted here as well.   */
+
+    while (cp < cpend) {
+      if (*cp++ != ESC) continue;
+      if (*cp == '(') {
+        if (++cp > cpend) break;
+        if (*cp == 'J') *cp = 'B';
+        ++cp;
+      } else if (*cp == '$') {
+        if (++cp > cpend) break;
+        if (*cp == '@') *cp = 'B';
+        ++cp;
+      }
+    }
+               /* eliminate redundant ESC seqs */
+    cp = indata;
+    cpnext = cp;
+    reg = 6;
+    while (cp < cpend) {
+      if (*cp++ != ESC) continue;
+      cpesc = cp - 1;
+      if (*cp == '$') {
+        if (++cp > cpend) break;
+        if (*cp == 'B') newreg = 87;
+        else if (*cp == 'A') newreg = 58;
+        else if (*cp == '(') {
+          if (++cp > cpend) break;
+          if (*cp == 'C') newreg = 149;
+          else if (*cp == 'D') newreg = 159;
+          else continue;
+        } else continue;
+      } else if (*cp == '(') {
+        if (++cp > cpend) break;
+        if (*cp == 'B') newreg = 6;
+        else continue;
+      } else continue;
+      if (++cp > cpend) break;
+      while (*cp == ' ' || *cp == '\t')
+        if (++cp >= cpend) break;      /* skip space */
+      if (*cp == ESC)                  /* maybe another G0 designation */
+        if (*(cp+1) == '(' || *(cp+1) == '$') {         /* yep! */
+          if (!stralloc_catb(&tmpdata,cpnext,cpesc-cpnext)) die_nomem(fatal);
+          cpnext = cp;
+         continue;
+      }
+      if (reg == newreg) {
+        if (!stralloc_catb(&tmpdata,cpnext,cpesc-cpnext)) die_nomem(fatal);
+        cpnext = cp;
+      } else {
+        reg = newreg;
+      }                /* copy remainder of line */
+    }
+    if (!stralloc_catb(&tmpdata,cpnext,cpend - cpnext + 1)) die_nomem(fatal);
+    if (reg != 6) {    /* need to return to us-ascii at the end of the line */
+      if (!stralloc_cats(&tmpdata,TOASCII)) die_nomem(fatal);
+    } else {           /* maybe "-Reply at the end?" */
+      r = trimend(tmpdata.s,&(tmpdata.len),fatal);
+    }
+
+  } else if (!case_diffb(charset,11,"iso-2022-cn") ||
+             !case_diffb(charset,11,"iso-2022-kr")) {
+       /* these use SI/SO and ESC $ ) x as the SO designation. In -cn and */
+       /* -cn-ext, 'x' can be a number of different letters. In -kr it's  */
+       /* always 'C'. This routine may work also for other iso-2022 sets  */
+       /* also handles iso-2022-cn-ext */
+    cpesc = (char *) 0;        /* points to latest ESC */
+    state = SI;                /* us-ascii */
+    --cp;              /* set up for loop */
+
+    while (++cp <= cpend) {
+      if (*cp == SI || *cp == SO) {
+        if (state == *cp) {             /* already in state. Skip shift seq */
+          if (!stralloc_catb(&tmpdata,cpnext,cp-cpnext-1)) die_nomem(fatal);
+          cpnext = cp;
+        } else                         /* set new state */
+          state = *cp;
+        if (++cp > cpend) break;
+        continue;
+      }
+      if (*cp != ESC) continue;
+      if (cp + 3 > cpend) break;       /* not space for full SO-designation */
+      cpesc = cp;
+      if (*cp != '$') continue;
+      if (++cp > cpend) break;
+      if (*cp != ')') continue;
+      if (++cp > cpend) break;
+      newcset = *cp;
+      if (++cp > cpend) break;
+      while (cp <= cpend && (*cp == ' ' || *cp == '\t')) ++cp;
+      if (cp + 3 > cpend) break;       /* no space for full SO-designation */
+      if ((*cp == ESC && *(cp+1) == '$' && *(cp+2) == ')')
+               || (newcset == cset)) {
+                       /* skip if a second SO-designation right after or */
+                       /* this SO-designation is already active, skip */
+        if (!stralloc_catb(&tmpdata,cpnext,cpesc-cpnext)) die_nomem(fatal);
+        --cp;          /* "unpeek" so that next iteration will see char */
+        cpnext = cpesc + 4;
+        continue;
+      } else {
+        cset = newcset;
+        continue;
+      }
+    }
+                       /* get remainder of line */
+    if (!stralloc_catb(&tmpdata,cpnext,cpend - cpnext + 1)) die_nomem(fatal);
+    if (state != SI)   /* need to end in ascii */
+      if (!stralloc_cats(&tmpdata,TOSI)) die_nomem(fatal);
+    else               /* ascii end; maybe "-Reply" at the end? */
+      r = trimend(tmpdata.s,&(tmpdata.len),fatal);
+
+  } else {             /* other character sets = no special treatment */
+    r = trimend(cp,&n,fatal);          /* -reply */
+    if (!stralloc_copyb(&tmpdata,cp,n)) die_nomem(fatal);
+  }
+
+  cp = tmpdata.s;
+  n = tmpdata.len;
+  cpend = cp + n - 1;
+  if (flagtrimsub) {    /* remove leading reply indicators & prefix*/
+    r |= trimre(&cp,cpend,prefix,fatal);
+    n = (unsigned int) (cpend-cp+1);
+  }
+                       /* there shouldn't be '\0' or '\n', but make sure as */
+                       /* it would break the message index */
+  if (!stralloc_copys(outdata,"")) die_nomem(fatal);
+  if (!stralloc_ready(outdata,n)) die_nomem(fatal);
+  outdata->len = n;
+  cpout = outdata->s;
+  while (n--) {                /* '\n' and '\0' would break the subject index */
+    if (!*cp || *cp == '\n') *cpout = ' ';
+    else *cpout = *cp;
+    ++cp; ++cpout;
+  }
+  return r;
+}
+
diff --git a/yyyymm.h b/yyyymm.h
new file mode 100644 (file)
index 0000000..ffbf6b8
--- /dev/null
+++ b/yyyymm.h
@@ -0,0 +1,15 @@
+#ifndef YYYYMM_H
+#define YYYYMM_H
+
+#ifdef WITH_PROTO
+#include "stralloc.h"
+
+extern unsigned int date2yyyymm(char *);
+extern int dateline(stralloc *, unsigned long);
+#else
+extern unsigned int date2yyyymm();
+extern int dateline();
+#endif
+
+#endif
+