1
0
Fork 0

qmail 1.02

This commit is contained in:
D. J. Bernstein 1998-05-07 00:00:00 +02:00 committed by Rolf Eike Beer
parent af9625ddf9
commit d15ee51c9d
134 changed files with 5642 additions and 5536 deletions

6
BLURB2
View file

@ -16,11 +16,11 @@ out-of-the-box configuration.
* qmail automatically prevents mailing list loops, even across hosts. * qmail automatically prevents mailing list loops, even across hosts.
* qlist, included in the qmail package, deals with subscription requests
safely and automatically.
* qmail allows inconceivably gigantic mailing lists. No random limits. * qmail allows inconceivably gigantic mailing lists. No random limits.
* qmail handles aliasing and forwarding with the same simple mechanism. * qmail handles aliasing and forwarding with the same simple mechanism.
For example, Postmaster is controlled by ~alias/.qmail-postmaster. This For example, Postmaster is controlled by ~alias/.qmail-postmaster. This
means that cross-host loop detection also applies to aliases. means that cross-host loop detection also applies to aliases.
* qmail supports the ezmlm mailing list manager, which easily and
automatically handles bounces, subscription requests, and archives.

15
BLURB3
View file

@ -3,7 +3,7 @@ Here are some of qmail's features.
Setup: Setup:
* automatic adaptation to your UNIX variant---no configuration needed * automatic adaptation to your UNIX variant---no configuration needed
* AIX, BSD/OS, FreeBSD, HP/UX, Irix, Linux, OSF/1, SunOS, Solaris, and more * AIX, BSD/OS, FreeBSD, HP/UX, Irix, Linux, OSF/1, SunOS, Solaris, and more
* automatic per-host configuration (qmail-config) * automatic per-host configuration (config, config-fast)
* quick installation---no big list of decisions to make * quick installation---no big list of decisions to make
Security: Security:
@ -16,11 +16,12 @@ Security:
Message construction (qmail-inject): Message construction (qmail-inject):
* RFC 822, RFC 1123 * RFC 822, RFC 1123
* full support for address groups * full support for address groups
* automatic conversion of old-style headers to RFC 822 format * automatic conversion of old-style address lists to RFC 822 format
* sendmail hook for compatibility with current user agents
* header line length limited only by memory * header line length limited only by memory
* host masquerading (control/defaulthost) * host masquerading (control/defaulthost)
* user masquerading (MAILUSER, MAILHOST) * user masquerading ($MAILUSER, $MAILHOST)
* sendmail hook for compatibility with current user agents * automatic Mail-Followup-To creation ($QMAILMFTFILE)
SMTP service (qmail-smtpd): SMTP service (qmail-smtpd):
* RFC 821, RFC 1123, RFC 1651, RFC 1652, RFC 1854 * RFC 821, RFC 1123, RFC 1651, RFC 1652, RFC 1854
@ -67,11 +68,13 @@ SMTP delivery (qmail-remote):
Forwarding and mailing lists (qmail-local): Forwarding and mailing lists (qmail-local):
* address wildcards (.qmail-default, .qmail-foo-default, etc.) * address wildcards (.qmail-default, .qmail-foo-default, etc.)
* sendmail/smail /etc/aliases compatibility (qmsmac, available separately) * sendmail .forward compatibility (dot-forward, available separately)
* fast forwarding databases (fastforward, available separately)
* sendmail /etc/aliases compatibility (fastforward/newaliases)
* mailing list owners---automatically divert bounces and vacation messages * mailing list owners---automatically divert bounces and vacation messages
* VERPs---automatic recipient identification for mailing list bounces * VERPs---automatic recipient identification for mailing list bounces
* Delivered-To---automatic loop prevention, even across hosts * Delivered-To---automatic loop prevention, even across hosts
* automatic subscription management (qlist) * automatic mailing list management (ezmlm, available separately)
Local delivery (qmail-local): Local delivery (qmail-local):
* user-controlled address hierarchy---fred controls fred-anything * user-controlled address hierarchy---fred controls fred-anything

4
BLURB4
View file

@ -4,7 +4,7 @@ against the competition in five different speed measurements.
* Scheduling: I sent a message to 8192 ``trash'' recipients on my home * Scheduling: I sent a message to 8192 ``trash'' recipients on my home
machine. All the deliveries were done in a mere 78 seconds---a rate of machine. All the deliveries were done in a mere 78 seconds---a rate of
over 9 MILLION deliveries a day! Compare this to the speed advertised over 9 million deliveries a day! Compare this to the speed advertised
for Zmailer's scheduling: 1.1 million deliveries a day on a for Zmailer's scheduling: 1.1 million deliveries a day on a
SparcStation-10/50. (My home machine is a 16MB Pentium-100 under BSD/OS, SparcStation-10/50. (My home machine is a 16MB Pentium-100 under BSD/OS,
with the default qmail configuration. qmail's logs were piped through with the default qmail configuration. qmail's logs were piped through
@ -14,7 +14,7 @@ accustamp and written to disk as usual.)
it physically writes the message to disk before it announces success--- it physically writes the message to disk before it announces success---
that way, mail doesn't get lost if the power goes out. I tried sending a that way, mail doesn't get lost if the power goes out. I tried sending a
message to 1024 local mailboxes on the same disk on my home machine; all message to 1024 local mailboxes on the same disk on my home machine; all
the deliveries were done in 25.5 seconds. That's more than 3.4 MILLION the deliveries were done in 25.5 seconds. That's more than 3.4 million
deliveries a day! Sending 1024 copies to a _single_ mailbox was just as deliveries a day! Sending 1024 copies to a _single_ mailbox was just as
fast. Compare these figures to Zmailer's advertised rate for throwing fast. Compare these figures to Zmailer's advertised rate for throwing
recipients away without even delivering the message---only 0.48 million recipients away without even delivering the message---only 0.48 million

217
CHANGES
View file

@ -1,3 +1,220 @@
19980430 version: qmail 1.02.
19980430 doc: updated SECURITY.
19980430 doc: fixed FAQ 4.9. tnx KB.
19980430 code: changed quote2() to avoid quoting <>.
19980429 code: changed quote_need() to quote empty local parts. tnx HHO.
19980428 doc: added status notes to INSTALL and UPGRADE.
19980428 code: skip setting environment in sendmail.c if PROTO is set.
19980428 code: eliminated recipientmap.
19980428 code: added virtual users to qmail-send.c. tnx RN.
19980428 code: eliminated domain from rewrite() in qmail-send.c.
19980428 code: added binm1, binm1+df, binm2, binm2+df, binm3, binm3+df.
19980428 doc: eliminated most Mailbox references from INSTALL, UPGRADE.
19980428 code: added config-fast.
19980428 code: renamed qmail-config as config.
19980428 code: supported QMAILMFTFILE in qmail-inject.c.
19980428 code: recognized Mail-Followup-To in hfield.c.
19980428 code: replaced rwrecip() with rwappend() in qmail-inject.c.
19980428 code: cleaned up doheaderfile() in qmail-inject.c.
19980426 code: eliminated -type test from qmail-qstat to speed it up.
tnx FT.
19980421 doc: eliminated remove-rcpthosts comments from FAQ.
19980421 doc: updated FAQ 4.3 to point to Russ Allbery's FAQ.
19980421 doc: took account of /var/qmail/boot in INSTALL, UPGRADE, and
INSTALL.vsm.
19980421 code: added /var/qmail/boot, with home, home+df, proc, proc+df.
19980421 doc: skipped make and make man in INSTALL.
19980420 doc: cleaned up mbox description in SENDMAIL.
19980420 code: changed QMQP port to official port 628.
19980402 doc: updated qmsmac references to fastforward.
19980402 doc: replaced qmail-upgrade man page with doc/SENDMAIL.
19980402 code: added qmqpservers output to qmail-showctl.
19980402 code: added qmail-qmqpd.
19980402 code: added qmail-qmqpc.
19980304 code: eliminated del_saywhynoexit in qmail-send.c.
19980304 code: eliminated concurrencynodel in qmail-send.c.
19980222 code: added status() to qmail-send.c.
19980222 code: added concurrencyused to qmail-send.c.
19980128 doc: added note to qmail-getpw.9 about ETXTBSY.
19980127 code: eliminated err_seenmail() in qmail-smtpd.c. tnx PO.
19980126 doc: used $DEFAULT in FAQ where possible.
19980126 code: added DEFAULT in qmail-local.
19980126 code: added -/ to qmail-pw2u.
19980126 code: revamped qmeopen() as qmesearch() with more sensible
semantics, separating dash from ext cleanly.
19980126 code: split qmeexists() out of qmeopen() in qmail-local.c.
19980126 code: introduced safeext in qmail-local.c.
19980126 code: changed ~alias to mode 2755, to put files into group
qmail rather than group nofiles under System V.
19980126 doc: switched to /var/qmail/rc in INSTALL*, UPGRADE, FAQ.
19980126 code: added rc.
19980119 doc: added .qmail creation warning to condredirect.1.
19980118 code: made auto_uids.c creation atomic in Makefile. tnx HHO.
19980118 doc: added PIC.*.
19980117 portability problem: Solaris 2.5.1 incorrectly converts
O_NDELAY into O_NONBLOCK for sockets, so that ndelay_off()
fails to undo ndelay_on(). impact: none, since all the network
readers here use select() via timeoutread(). fix: use
O_NONBLOCK if it is defined.
19980115 code: reformatted qmail-qmtpd.c.
19980115 doc: changed tcpcontrol references in FAQ.
19980115 doc: documented morercpthosts in qmail-qmtpd.9.
19980115 code: eliminated unused datetime in qmail-qmtpd.c.
19980115 code: eliminated sigalrm() in qmail-qmtpd.c.
19980115 code: used rcpthosts() in qmail-smtpd.c, qmail-qmtpd.c.
19980115 code: introduced rcpthosts.c.
19980115 code: added morercpthosts.cdb support to qmail-showctl.
19980115 code: added morercpthosts support to qmail-showctl.
19980115 code: do_lst now returns file-exists in qmail-showctl.
19980112 doc: documented morercpthosts in qmail-smtpd.9.
19980112 code: added qmail-newmrh.
19980112 code: used commands.c in qmail-popup.
19980112 code: used commands.c in qmail-pop3d.
19980112 code: introduced fakehelo in qmail-smtpd.
19980112 code: moved flagbarf setting out of bmfcheck().
19980112 code: allowed more address misformatting in qmail-smtpd.
19980112 code: eliminated qmail@pobox.com help address in qmail-smtpd.
19980112 code: reorganized qmail-smtpd.
19980112 code: reformatted qmail-smtpd.
19980112 code: used commands.c in qmail-smtpd.
19980112 code: switched from 0 to "" for no arg in commands().
19980112 code: added commands.c.
19971230 doc: added -s to FreeBSD commands in INSTALL.ids. tnx TM.
19971224 doc: added pointer to qmail pictures in README.
19971223 doc: added note in FAQ about qmail-pop3d using maildir.
19971219 code: added HOST2, HOST3, HOST4.
19971219 code: renamed extx as x in qmail-local.c.
19971219 doc: partitioned qmail-command.0.
19971219 doc: updated FAQ 4.3 to point to newer majordomo patches.
19971219 doc: eliminated qlist2 from FAQ.
19971219 doc: eliminated qlist discussion from SECURITY.
19971219 code: moved qlist, qlist2 to separate package.
19971213 doc: added FAQ 4.10 on qmail-users generally.
19971213 doc: added FAQ 4.9 on dealing with NFS outages.
19971031 doc: added Linux and FreeBSD commands to INSTALL.ids. tnx TM.
19971026 doc: added note about smtplf in qmail-smtpd.8. tnx S2S.
19971014 doc: some tweaks to THOUGHTS.
19971012 doc: used MAILER-DAEMON in UUCP example in INSTALL.
19971003 code: eliminated dataline and getln() from qmail-remote.c.
19971003 code: revamped blast() in qmail-remote.c.
19971002 doc: added FAQ entries for .forward and /etc/aliases.
19971002 doc: rewrote INSTALL.mbox and INSTALL.vsm.
19971002 doc: renamed INSTALL.qsmhook as INSTALL.vsm.
19971002 doc: emphasized the qmail-popup argv0 in FAQ.
19971001 doc: added dot-forward note to BLURB3.
19971001 doc: added more configuration notes to qmail-upgrade.9.
19971001 doc: added note in INSTALL.qsmhook about dot-forward.
19970930 code: token822_parse() now supports backslash as a quoting
character in atoms.
19970929 doc: suggested symbolic links in INSTALL.mbox.
19970925 doc: added note to INTERNALS about bounce stability.
19970925 doc: added section to THOUGHTS discussing CNAME lookups.
19970925 code: qmail-remote no longer does CNAME lookup on sender. tnx
C2F.
19970923 portability problem: under SCO OSR5, splogger needs socket
libraries. impact: couldn't compile. fix: socket.lib. tnx RB.
19970906 portability problem: under RISC/OS, Mail invokes sendmail -bm.
impact: can't send mail using Mail on RISC/OS. fix: ignore -bm.
tnx NW.
19970813 code: implemented databytes in qmail-qmtpd.
19970813 code: implemented databytes. tnx M4S for sample code.
19970813 code: replaced execvp() with execv() for sh in qmail-local.
19970813 doc: said in qmail-control.9 that recipientmap allows comments.
19970813 code: used strerr in qmail-local.c.
19970813 code: changed timeoutread(), timeoutwrite() interface.
19970813 code: eliminated shutdown() in timeoutread(), timeoutwrite().
19970813 code: revamped I/O in qmail-smtpd.c.
19970813 code: used timeoutread(), timeoutwrite() in qmail-smtpd.c.
19970813 code: simplified getcontrol() logic in qmail-remote.c; some
out-of-memory messages are now cannot-read-control messages.
19970813 code: eliminated scan_nbblong().
19970813 code: reformatted qmail-remote.c.
19970813 code: renamed flaganyrecipok as flagbother in qmail-remote.c.
19970813 code: integrated status report into quit() in qmail-remote.c.
19970813 code: revamped smtpcode() in qmail-remote.c.
19970813 code: added flagcritical in qmail-remote.c. eliminates
possible-duplicate warning if dot has not yet been sent.
19970813 code: revamped I/O in qmail-remote.c.
19970813 code: quit immediately after sending QUIT in qmail-remote.c.
19970813 code: made many more globals in qmail-remote.c.
19970813 code: switched qmail-remote.c from subfdin to home-grown.
19970813 code: switched qmail-remote.c from subfdout to subfdoutsmall.
19970813 code: added LAST support to qmail-pop3d.
19970812 code: changed qmail_close() success return from 0 to "".
19970812 code: revamped I/O in qmail-qmtpd.c.
19970812 code: added qmail-tcpok.
19970812 code: used strerr in maildirmake.c.
19970812 code: reformatted maildirmake.c.
19970812 code: printed qp in condredirect.c.
19970812 code: printed qqx in condredirect.c.
19970812 code: revamped I/O in condredirect.c.
19970812 code: reformatted condredirect.c.
19970812 code: used strerr in preline.c.
19970812 code: revamped I/O in preline.c.
19970812 code: reformatted preline.c.
19970812 code: printed qp in forward.c.
19970812 code: printed qqx in forward.c.
19970812 code: revamped I/O in forward.c.
19970812 code: used strerr in forward.c.
19970812 code: reformatted forward.c.
19970812 code: used strerr in predate.c.
19970812 code: forced failure in qmail-qmtpd if no recipients; saves
time for qmail-send.
19970812 code: added smtpd() to sendmail.c.
19970812 code: added mailq() to sendmail.c.
19970812 code: added die_usage() to sendmail.c.
19970812 code: reformatted sendmail.c.
19970812 code: used byte_zero() in qmail-popup.c.
19970812 code: reformatted qmail-popup.c.
19970812 code: eliminated unused header files in qmail-popup.c.
19970812 code: changed I/O system in qmail-popup.c to match qmail-pop3d.
19970812 doc: pointed people to the mailing list in INSTALL and UPGRADE.
19970810 code: added TXTBSY check to qmail-getpw.c. this gives vendors
the opportunity to make getpwnam() reliable.
19970810 code: moved non-deleted messages from new/ to cur/ in
qmail-pop3d. tnx to various people.
19970810 code: introduced list() in qmail-pop3d.c.
19970810 code: reformatted qmail-pop3d.c.
19970810 code: merged dataline and newname into line in qmail-pop3d.c.
19970810 code: chopped filenames in qmail-pop3d at colons for UIDL. tnx
to various people.
19970810 code: eliminated printint(), printlong() in qmail-pop3d.c.
19970810 code: revamped I/O in qmail-pop3d.c.
19970810 code: used timeoutread(), timeoutwrite() in qmail-pop3d.c.
19970810 code: eliminated die_prot() in qmail-pop3d.c.
19970810 code: eliminated unused header files in qmail-pop3d.c.
19970810 code: switched qmail-pop3d to use maildir.c. tnx MD.
19970809 code: added uid/gid printing to qmail-showctl. tnx PGF.
19970808 code: switched control.c from scan_nbblong to scan_ulong.
19970808 code: cleaned up wait_pid to use waitpid() when possible, and
to support at least one extra child otherwise.
19970807 code: in qmail-smtpd, treat long envelope addresses as a syntax
error, instead of waiting for qmail-queue to reject them.
19970803 code: changed condredirect, forward, qlist, qmail-inject,
qmail-local, qmail-qmtpd, qmail-send, qmail-smtpd, qreceipt for
new qmail_close() interface.
19970803 code: revised qmail_close() to handle qmail-queue exit codes.
19970802 doc: documented SMTP-related exit codes in qmail-queue.8.
19970802 doc: documented qmail-queue exit codes in qmail-queue.8.
19970802 code: revamped qmail-queue exit codes.
19970802 doc: noted linking restrictions in qmail-queue.8.
19970802 doc: rewrote INSTALL.mbox.
19970802 doc: split INSTALL.maildir off of INSTALL.mbox.
19970802 code: added /var/qmail/doc/ creation to qmail-hier.
19970802 doc: added ezmlm note to FAQ.
19970802 doc: replaced qlist blurbs with ezmlm blurbs in BLURB*.
19970802 doc: added various notes to qmail-start.9.
19970728 doc: eliminated RFC*.
19970714 doc: added daemontools notes to FAQ.
19970714 code: eliminated ESMTP parameter syntax checking.
19970701 doc: changed ``forwarded'' to ``resent'' in qmail-header.5.
19970629 code: reformatted constmap.c.
19970628 code: changed straynewline() message in qmail-smtpd.c to point
to http://pobox.com/~djb/smtplf.html. tnx RDM.
19970609 doc: added preline to vacation example in dot-qmail.9. tnx C2S.
19970421 code: cleaned up slurpclose to handle interrupts.
19970421 code: set qmail-popup to mode 711. tnx MD.
19970421 doc: fixed qmail-local -n example in dot-qmail.9.
19970415 version: qmail 1.01. 19970415 version: qmail 1.01.
19970414 doc: tightened up qmail-upgrade.7. 19970414 doc: tightened up qmail-upgrade.7.
19970414 code: rewrote rewrite(). 19970414 code: rewrote rewrite().

180
FAQ
View file

@ -21,6 +21,10 @@
4.4. How do I use procmail with qmail? 4.4. How do I use procmail with qmail?
4.5. How do I use elm's filter with qmail? 4.5. How do I use elm's filter with qmail?
4.6. How do I create aliases with dots? 4.6. How do I create aliases with dots?
4.7. How do I use sendmail's .forward files with qmail?
4.8. How do I use sendmail's /etc/aliases with qmail?
4.9. How do I make qmail defer messages during NFS or NIS outages?
4.10. How do I change which account controls an address?
5. Setting up servers 5. Setting up servers
5.1. How do I run qmail-smtpd under tcpserver? 5.1. How do I run qmail-smtpd under tcpserver?
@ -31,7 +35,7 @@
6. Configuring MUAs to work with qmail 6. Configuring MUAs to work with qmail
6.1. How do I make BSD mail generate a Date with the local time zone? 6.1. How do I make BSD mail generate a Date with the local time zone?
6.2. How do I stop pine from crashing? 6.2. How do I make pine work with qmail?
6.3. How do I make MH work with qmail? 6.3. How do I make MH work with qmail?
6.4. How do I stop Sun's dtcm from hanging? 6.4. How do I stop Sun's dtcm from hanging?
@ -41,6 +45,8 @@
7.3. How do I rejuvenate a message? 7.3. How do I rejuvenate a message?
7.4. How do I organize a big network? 7.4. How do I organize a big network?
7.5. How do I back up and restore the queue disk? 7.5. How do I back up and restore the queue disk?
7.6. How do I run a supervised copy of qmail?
7.7. How do I avoid syslog?
8. Miscellany 8. Miscellany
8.1. How do I tell qmail to do more deliveries at once? 8.1. How do I tell qmail to do more deliveries at once?
@ -102,11 +108,11 @@ Answer: Put
into control/virtualdomains and into control/virtualdomains and
|preline -df /usr/bin/uux - -r -gC -a"$SENDER" gonzo!rmail "($EXT2@$HOST)" |preline -df /usr/bin/uux - -r -gC
-a"${SENDER:-MAILER-DAEMON}" gonzo!rmail "($DEFAULT@$HOST)"
into ~alias/.qmail-uucp-default. (For some UUCP software you will need (all on one line) into ~alias/.qmail-uucp-default. (For some UUCP
to use -d instead of -df. Also, you may need to insert a space between software you will need to use -d instead of -df.) If qmail-send is
-a and "$SENDER" for bounces to work properly.) If qmail-send is
running, give it a HUP. running, give it a HUP.
@ -120,9 +126,9 @@ showed that a message was deferred for this reason. Why is qmail doing
CNAME lookups, anyway? CNAME lookups, anyway?
Answer: The SMTP standard does not permit aliased hostnames, so qmail Answer: The SMTP standard does not permit aliased hostnames, so qmail
has to do a CNAME lookup in DNS for every sender and recipient host. If has to do a CNAME lookup in DNS for every recipient host. If the
the relevant DNS server is down, qmail defers the message. It will try relevant DNS server is down, qmail defers the message. It will try again
again soon. soon.
@ -135,7 +141,8 @@ an MX from af.mil to pokey.af.mil, but how do I get pokey to treat
af.mil as a name for the local host? af.mil as a name for the local host?
Answer: Add af.mil to /var/qmail/control/locals and to Answer: Add af.mil to /var/qmail/control/locals and to
/var/qmail/control/rcpthosts. If qmail-send is running, give it a HUP. /var/qmail/control/rcpthosts. If qmail-send is running, give it a HUP
(or do svc -h /var/run/qmail if qmail is supervised).
3.2. How do I set up a virtual domain? I'd like any mail for 3.2. How do I set up a virtual domain? I'd like any mail for
@ -147,7 +154,8 @@ Answer: Put
nowhere.mil:bob nowhere.mil:bob
into control/virtualdomains. Add nowhere.mil to control/rcpthosts. If into control/virtualdomains. Add nowhere.mil to control/rcpthosts. If
qmail-send is running, give it a HUP. qmail-send is running, give it a HUP (or do svc -h /var/run/qmail if
qmail is supervised).
Now mail for whatever@nowhere.mil will be delivered locally to Now mail for whatever@nowhere.mil will be delivered locally to
bob-whatever. Bob can set up ~bob/.qmail-default to catch all the bob-whatever. Bob can set up ~bob/.qmail-default to catch all the
@ -165,7 +173,8 @@ Answer: Put two lines into control/virtualdomains:
everywhere.org:bob-everywhere everywhere.org:bob-everywhere
Add nowhere.mil and everywhere.org to control/rcpthosts. If qmail-send Add nowhere.mil and everywhere.org to control/rcpthosts. If qmail-send
is running, give it a HUP. is running, give it a HUP (or do svc -h /var/run/qmail if qmail is
supervised).
Now Bob can set up separate .qmail-nowhere-* and everywhere-* files. He Now Bob can set up separate .qmail-nowhere-* and everywhere-* files. He
can even set up .qmail-nowhere-default and .qmail-everywhere-default. can even set up .qmail-nowhere-default and .qmail-everywhere-default.
@ -191,21 +200,22 @@ forwarded to a bunch of people.
Answer: Put a list of addresses into ~me/.qmail-sos, one per line. Then Answer: Put a list of addresses into ~me/.qmail-sos, one per line. Then
incoming mail for me-sos will be forwarded to each of those addresses. incoming mail for me-sos will be forwarded to each of those addresses.
You should also touch ~me/.qmail-sos-owner so that bounces come back to You should also touch ~me/.qmail-sos-owner so that bounces come back to
you rather than the original sender. If you want subscriptions to be you rather than the original sender.
handled automatically, put
| qlist2 sos my.host.name Alternative: ezmlm (http://pobox.com/~djb/ezmlm.html) is a modern
mailing list manager, supporting automatic subscriptions, confirmations,
into ~me/.qmail-sos-request. Anyone who wants to subscribe can simply archives, fully automatic bounce handling (including warnings to
send a message to me-sos-request@my.host.name. subscribers saying which messages they've missed), and more.
4.3. How do I use majordomo with qmail? 4.3. How do I use majordomo with qmail?
Answer: You need to patch majordomo so that it creates qmail-style Answer: See ftp://ftp.eyrie.org/pub/software/majordomo/mjqmail and
lists. See ftp://koobera.math.uic.edu/pub/software/majordomo+qmail.gz. http://www.qmail.org for various methods. majordomo 2.0 is expected to
Exception: qmsmac understands sendmail-style :include: files, so you support qmail directly.
shouldn't patch majordomo if you're using qmsmac.
Beware that majordomo's lists are not crashproof.
4.4. How do I use procmail with qmail? 4.4. How do I use procmail with qmail?
@ -237,6 +247,48 @@ Answer: Use .qmail-p:d:q:bach. Dots are converted to colons, and
uppercase is converted to lowercase. uppercase is converted to lowercase.
4.7. How do I use sendmail's .forward files with qmail?
Answer: Install the dot-forward package
(http://pobox.com/~djb/dot-forward.html).
4.8. How do I use sendmail's /etc/aliases with qmail?
Answer: Install the fastforward package
(http://pobox.com/~djb/fastforward.html).
4.9. How do I make qmail defer messages during NFS or NIS outages? If
~joe suddenly disappears, I'd like mail for joe to be deferred.
Answer: Build a qmail-users database, so that qmail no longer checks
home directories and the password database. This takes three steps.
First, put your complete user list (including local and NIS passwords)
into /var/qmail/users/passwd. Second, run
# qmail-pw2u -h < /var/qmail/users/passwd > /var/qmail/users/assign
Here -h means that every user must have a home directory; if you happen
to run qmail-pw2u during an NFS outage, it will print an error message
and stop. Third, run
# qmail-newu
Make sure to rebuild the database whenever you change your user list.
4.10. How do I change which account controls an address? I set up
~alias/.qmail-www, but qmail is looking at ~www/.qmail instead.
Answer: If you do
# chown root ~www
then qmail will no longer consider www to be a user; see qmail-getpw.0.
For more precise control over address assignments, see qmail-users.0.
5. Setting up servers 5. Setting up servers
@ -284,17 +336,20 @@ replacing 7770 and 2108 with the qmaild uid and nofiles gid. See
question 5.1 for more details. question 5.1 for more details.
5.3. How do I set up qmail-pop3d? 5.3. How do I set up qmail-pop3d? My old POP server works with mbox
delivery; I'd like to switch to maildir delivery.
Answer: Four steps. First, install the checkpassword program Answer: Four steps. First, install the checkpassword program
(http://pobox.com/~djb/checkpwd.html). Second, make sure you have a (http://pobox.com/~djb/checkpwd.html). Second, make sure you have a
pop3 110/tcp pop3 110/tcp
line in /etc/services. Third, put (all on one line) line in /etc/services. Third, put (all on one line, including
qmail-popup twice)
pop3 stream tcp nowait root /var/qmail/bin/qmail-popup pop3 stream tcp nowait root
qmail-popup YOURHOST /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir /var/qmail/bin/qmail-popup qmail-popup
YOURHOST /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir
into /etc/inetd.conf, and give inetd a HUP; replace YOURHOST with your into /etc/inetd.conf, and give inetd a HUP; replace YOURHOST with your
host's fully qualified domain name. Fourth, set up Maildir delivery for host's fully qualified domain name. Fourth, set up Maildir delivery for
@ -315,8 +370,7 @@ otherwise an eavesdropper can steal passwords.
5.4. How do I allow selected clients to use this host as a relay? I see 5.4. How do I allow selected clients to use this host as a relay? I see
that qmail-smtpd rejects messages to any host not listed in that qmail-smtpd rejects messages to any host not listed in
control/rcpthosts. I know I could entirely disable this feature by control/rcpthosts.
removing control/rcpthosts, but I want to be more selective.
Answer: Three steps. First, install tcp-wrappers, available separately, Answer: Three steps. First, install tcp-wrappers, available separately,
including hosts_options. Second, change your qmail-smtpd line in including hosts_options. Second, change your qmail-smtpd line in
@ -336,21 +390,21 @@ ignores control/rcpthosts when RELAYCLIENT is set. (It also appends
RELAYCLIENT to each envelope recipient address. See question 5.5 for an RELAYCLIENT to each envelope recipient address. See question 5.5 for an
application.) application.)
Alternative procedure, if you are using tcpserver: Install tcpcontrol Alternative procedure, if you are using tcpserver 0.80 or above: Create
(http://pobox.com/~djb/tcpcontrol.html). Create /etc/tcp.smtp containing /etc/tcp.smtp containing
1.2.3.6:allow,RELAYCLIENT="" 1.2.3.6:allow,RELAYCLIENT=""
127.:allow,RELAYCLIENT="" 127.:allow,RELAYCLIENT=""
to allow clients with IP addresses 1.2.3.6 and 127.*. Run to allow clients with IP addresses 1.2.3.6 and 127.*. Run
tcpmakectl /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
Finally, insert Finally, insert
tcpcontrol /etc/tcp.smtp.cdb -x /etc/tcp.smtp.cdb
before /var/qmail/bin/qmail-smtpd in your tcpserver line. after tcpserver in your qmail-smtpd invocation.
5.5. How do I fix up messages from broken SMTP clients? 5.5. How do I fix up messages from broken SMTP clients?
@ -358,7 +412,7 @@ before /var/qmail/bin/qmail-smtpd in your tcpserver line.
Answer: Three steps. First, put Answer: Three steps. First, put
| [ "@$HOST" = "@fixme" ] || ( echo Permission denied; exit 100 ) | [ "@$HOST" = "@fixme" ] || ( echo Permission denied; exit 100 )
| qmail-inject -f "$SENDER" -- "$EXT2" | qmail-inject -f "$SENDER" -- "$DEFAULT"
into ~alias/.qmail-fixup-default. Second, put into ~alias/.qmail-fixup-default. Second, put
@ -371,12 +425,12 @@ string ``@fixme'':
tcp-env: 1.2.3.6, 1.2.3.7: setenv = RELAYCLIENT @fixme tcp-env: 1.2.3.6, 1.2.3.7: setenv = RELAYCLIENT @fixme
Here 1.2.3.6 and 1.2.3.7 are the clients' IP addresses. If you are using Here 1.2.3.6 and 1.2.3.7 are the clients' IP addresses. If you are using
tcpserver and tcpcontrol instead of inetd and tcpd, put tcpserver instead of inetd and tcpd, put
1.2.3.6:allow,RELAYCLIENT="@fixme" 1.2.3.6:allow,RELAYCLIENT="@fixme"
1.2.3.7:allow,RELAYCLIENT="@fixme" 1.2.3.7:allow,RELAYCLIENT="@fixme"
into /etc/tcp.smtp, and run tcpmakectl as in question 5.4. into /etc/tcp.smtp, and run tcprules as in question 5.4.
@ -395,8 +449,7 @@ into your .mailrc or your system-wide Mail.rc. Beware that BSD mail is
neither secure nor reliable. neither secure nor reliable.
6.2. How do I stop pine from crashing? When I ask any version of pine 6.2. How do I make pine work with qmail?
past 3.91 to send mail, it crashes.
Answer: Put Answer: Put
@ -432,14 +485,25 @@ sendmail, it was always tricky to kill sendmail without risking the loss
of current deliveries; what should I do with qmail-send? of current deliveries; what should I do with qmail-send?
Answer: Go ahead and kill the qmail-send process. It will shut down Answer: Go ahead and kill the qmail-send process. It will shut down
cleanly. Wait for ``exiting'' to show up in the log. To restart it, run cleanly. Wait for ``exiting'' to show up in the log. To restart qmail,
qmail-start the same way as it's run from your system boot scripts. run /var/qmail/rc the same way it is run from your system boot scripts,
with the proper PATH, resource limits, etc.
Alternative, if qmail is supervised: svc -t /var/run/qmail. The
supervise process will kill qmail, wait for it to stop, and restart it.
Use -d instead of -t if you don't want qmail to restart automatically;
to manually restart it, use -u.
7.2. How do I manually run the queue? I'd like qmail to try delivering 7.2. How do I manually run the queue? I'd like qmail to try delivering
all the remote messages right now. all the remote messages right now.
Answer: Give the qmail-send process an ALRM. Answer: Give the qmail-send process an ALRM. (Do svc -a /var/run/qmail
if qmail is supervised.)
You may want to run qmail-tcpok first, to guarantee that qmail-remote
will try all addresses. Normally, if an address fails repeatedly,
qmail-remote leaves it alone for an hour.
7.3. How do I rejuvenate a message? Somebody broke into Eric's computer 7.3. How do I rejuvenate a message? Somebody broke into Eric's computer
@ -521,6 +585,42 @@ battery backups. RAID boxes let you replace dead disks without losing
any data. any data.
7.6. How do I run a supervised copy of qmail? svc sounds useful.
Answer: Install daemontools (http://pobox.com/~djb/daemontools.html).
Create a /var/run/qmail directory. Change
/var/qmail/rc
to
supervise /var/run/qmail /var/qmail/rc
in your boot scripts. Make sure that supervise is in the startup PATH.
Now you can use svc to stop or restart qmail, and svstat to check
whether qmail is running.
7.7. How do I avoid syslog? It chews up a lot of CPU time and isn't
reliable.
Answer: Install daemontools (http://pobox.com/~djb/daemontools.html).
Make a /var/log/qmail directory, owned by qmaill, mode 2700. Do
qmail-start ./Mailbox /usr/local/bin/accustamp \
| setuser qmaill /usr/local/bin/cyclog /var/log/qmail &
in /var/qmail/rc.
If you are logging tcpserver connections, make a /var/log/smtpd
directory, and use cyclog /var/log/smtpd for tcpserver. You shouldn't
run several copies of cyclog with the same log directory.
By default, cyclog keeps 10 automatically rotated log files, each
containing up to 100KB of log data. To keep 20 files with 1MB each, use
cyclog -s 1000000 -n 20.
8. Miscellany 8. Miscellany

55
FILES
View file

@ -9,23 +9,26 @@ INSTALL.alias
INSTALL.boot INSTALL.boot
INSTALL.ctl INSTALL.ctl
INSTALL.ids INSTALL.ids
INSTALL.maildir
INSTALL.mbox INSTALL.mbox
INSTALL.qsmhook INSTALL.vsm
UPGRADE UPGRADE
THOUGHTS THOUGHTS
TODO TODO
THANKS THANKS
CHANGES CHANGES
RFCHCSC
RFCLOOPS
RFCMXPS
RFCNETSTR
RFCNRUDT
RFCQMTP
RFCQSBMF
RFCVERP
SECURITY SECURITY
INTERNALS INTERNALS
SENDMAIL
PIC.local2alias
PIC.local2ext
PIC.local2local
PIC.local2rem
PIC.local2virt
PIC.nullclient
PIC.relaybad
PIC.relaygood
PIC.rem2local
FILES FILES
VERSION VERSION
SYSDEPS SYSDEPS
@ -58,7 +61,6 @@ mailsubj.1
mbox.5 mbox.5
preline.1 preline.1
qbiff.1 qbiff.1
qlist.1
qmail-clean.8 qmail-clean.8
qmail-command.8 qmail-command.8
qmail-control.9 qmail-control.9
@ -69,10 +71,13 @@ qmail-limits.9
qmail-local.8 qmail-local.8
qmail-log.5 qmail-log.5
qmail-lspawn.8 qmail-lspawn.8
qmail-newmrh.9
qmail-newu.9 qmail-newu.9
qmail-pop3d.8 qmail-pop3d.8
qmail-popup.8 qmail-popup.8
qmail-pw2u.9 qmail-pw2u.9
qmail-qmqpc.8
qmail-qmqpd.8
qmail-qmtpd.8 qmail-qmtpd.8
qmail-qread.8 qmail-qread.8
qmail-qstat.8 qmail-qstat.8
@ -83,24 +88,28 @@ qmail-send.9
qmail-showctl.8 qmail-showctl.8
qmail-smtpd.8 qmail-smtpd.8
qmail-start.9 qmail-start.9
qmail-tcpok.8
qmail-tcpto.8 qmail-tcpto.8
qmail-upgrade.9
qmail-users.9 qmail-users.9
qmail.7 qmail.7
qreceipt.1 qreceipt.1
splogger.8 splogger.8
tcp-env.1 tcp-env.1
config.sh
config-fast.sh
qmail-clean.c qmail-clean.c
qmail-config.sh
qmail-getpw.c qmail-getpw.c
qmail-hier.c qmail-hier.c
qmail-inject.c qmail-inject.c
qmail-local.c qmail-local.c
qmail-lspawn.c qmail-lspawn.c
qmail-newmrh.c
qmail-newu.c qmail-newu.c
qmail-pop3d.c qmail-pop3d.c
qmail-popup.c qmail-popup.c
qmail-pw2u.c qmail-pw2u.c
qmail-qmqpc.c
qmail-qmqpd.c
qmail-qmtpd.c qmail-qmtpd.c
qmail-qread.c qmail-qread.c
qmail-qstat.sh qmail-qstat.sh
@ -111,6 +120,7 @@ qmail-send.c
qmail-showctl.c qmail-showctl.c
qmail-smtpd.c qmail-smtpd.c
qmail-start.c qmail-start.c
qmail-tcpok.c
qmail-tcpto.c qmail-tcpto.c
spawn.c spawn.c
dnscname.c dnscname.c
@ -122,7 +132,6 @@ hostname.c
ipmeprint.c ipmeprint.c
tcp-env.c tcp-env.c
sendmail.c sendmail.c
qlist.c
qreceipt.c qreceipt.c
qsmhook.c qsmhook.c
qbiff.c qbiff.c
@ -137,13 +146,14 @@ splogger.c
qail.sh qail.sh
elq.sh elq.sh
pinq.sh pinq.sh
qlist2.sh
qmail-upq.sh qmail-upq.sh
datemail.sh datemail.sh
mailsubj.sh mailsubj.sh
qlx.h qlx.h
constmap.h rcpthosts.h
constmap.c rcpthosts.c
commands.h
commands.c
dnsdoe.h dnsdoe.h
dnsdoe.c dnsdoe.c
fmtqfn.h fmtqfn.h
@ -171,6 +181,16 @@ trynpbg1.c
trysyslog.c trysyslog.c
conf-cc conf-cc
conf-ld conf-ld
home.sh
home+df.sh
proc.sh
proc+df.sh
binm1.sh
binm2.sh
binm3.sh
binm1+df.sh
binm2+df.sh
binm3+df.sh
find-systype.sh find-systype.sh
make-compile.sh make-compile.sh
make-load.sh make-load.sh
@ -348,7 +368,6 @@ fmt_ulong.c
scan.h scan.h
scan_ulong.c scan_ulong.c
scan_8long.c scan_8long.c
scan_nbblong.c
slurpclose.h slurpclose.h
slurpclose.c slurpclose.c
quote.h quote.h
@ -399,3 +418,5 @@ maildir.5
maildir.h maildir.h
maildir.c maildir.c
tcp-environ.5 tcp-environ.5
constmap.h
constmap.c

84
INSTALL
View file

@ -16,80 +16,100 @@ Installation steps that won't interfere with sendmail:
1. Create the qmail home directory: 1. Create the qmail home directory:
# mkdir /var/qmail # mkdir /var/qmail
2. Read INSTALL.ids. You must set up the qmail group and the qmail 2. Read INSTALL.ids. You must set up the qmail group and the qmail
users before compiling the programs. users before compiling the programs.
3. Compile the programs:
# make 3. Compile the programs and create the qmail directory tree:
4. Create the formatted man pages, *.0:
# make man
5. Create the qmail directory tree:
# make setup # make setup
6. Run instcheck to make sure it doesn't print any warnings:
4. Run instcheck to make sure it doesn't print any warnings:
# make check # make check
7. Read INSTALL.ctl and FAQ. Minimal survival command:
# ./qmail-config 5. Read INSTALL.ctl and FAQ. Minimal survival command:
8. Read INSTALL.alias. Minimal survival command: # ./config
6. Read INSTALL.alias. Minimal survival command:
# (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root) # (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root)
# chmod 644 ~alias/.qmail* # chmod 644 ~alias/.qmail*
9. Read INSTALL.mbox.
10. Read qmail-upgrade.0. This is what your users will need to know 7. Read INSTALL.mbox and INSTALL.vsm.
about the switch from sendmail to qmail.
8. Read INSTALL.maildir.
9. Read SENDMAIL. This is what your users will want to know about the
switch from sendmail to qmail.
10. Copy /var/qmail/boot/home (or proc) to /var/qmail/rc.
Pre-upgrade tests: Pre-upgrade tests:
11. Enable deliveries of messages injected into qmail: 11. Enable deliveries of messages injected into qmail:
# env - PATH="/var/qmail/bin:$PATH" \ # /var/qmail/rc &
qmail-start ./Mailbox splogger qmail &
Make sure to include the ./ in ./Mailbox.
12. Look for a 12. Look for a
qmail: running qmail: status: local 0/10 remote 0/20
line in syslog. qmail-send always prints either ``cannot start'' or line in syslog. qmail-send always prints either ``cannot start'' or
``running''. (The big number is a splogger timestamp.) ``status''. (The big number is a splogger timestamp.)
13. Do a ps and look for the qmail daemons. There should be four of 13. Do a ps and look for the qmail daemons. There should be four of
them, all idle: qmail-send, running as qmails; qmail-lspawn, running them, all idle: qmail-send, running as qmails; qmail-lspawn, running
as root; qmail-rspawn, running as qmailr; and qmail-clean, running as root; qmail-rspawn, running as qmailr; and qmail-clean, running
as qmailq. You will also see the splogger process. as qmailq. You will also see splogger, running as qmaill.
14. Local-local test: Send yourself an empty message. (Replace ``me'' 14. Local-local test: Send yourself an empty message. (Replace ``me''
with your username. Make sure to include the ``to:'' colon.) with your username. Make sure to include the ``to:'' colon.)
% echo to: me | /var/qmail/bin/qmail-inject % echo to: me | /var/qmail/bin/qmail-inject
The message will show up immediately in ~/Mailbox, and syslog will The message will show up immediately in your mailbox, and syslog
show something like this: will show something like this:
qmail: new msg 53 qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666 qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
qmail: starting delivery 1: msg 53 to local me@domain qmail: starting delivery 1: msg 53 to local me@domain
qmail: status: local 1/10 remote 0/20
qmail: delivery 1: success: did_1+0+0/ qmail: delivery 1: success: did_1+0+0/
qmail: status: local 0/10 remote 0/20
qmail: end msg 53 qmail: end msg 53
(53 is an inode number; 20345 is a process ID; your numbers will (53 is an inode number; 20345 is a process ID; your numbers will
probably be different.) probably be different.)
15. Local-error test: Send a message to a nonexistent local address. 15. Local-error test: Send a message to a nonexistent local address.
% echo to: nonexistent | /var/qmail/bin/qmail-inject % echo to: nonexistent | /var/qmail/bin/qmail-inject
qmail: new msg 53 qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666 qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
qmail: starting delivery 2: msg 53 to local nonexistent@domain qmail: starting delivery 2: msg 53 to local nonexistent@domain
qmail: status: local 1/10 remote 0/20
qmail: delivery 2: failure: No_such_address.__#5.1.1_/ qmail: delivery 2: failure: No_such_address.__#5.1.1_/
qmail: status: local 0/10 remote 0/20
qmail: bounce msg 53 qp 20357 qmail: bounce msg 53 qp 20357
qmail: end msg 53 qmail: end msg 53
qmail: new msg 54 qmail: new msg 54
qmail: info msg 54: bytes 743 from <> qp 20357 uid 666 qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
qmail: starting delivery 3: msg 54 to local me@domain qmail: starting delivery 3: msg 54 to local me@domain
qmail: status: local 1/10 remote 0/20
qmail: delivery 3: success: did_1+0+0/ qmail: delivery 3: success: did_1+0+0/
qmail: status: local 0/10 remote 0/20
qmail: end msg 54 qmail: end msg 54
You will now have a bounce message in ~/Mailbox. You will now have a bounce message in your mailbox.
16. Local-remote test: Send an empty message to your account on another 16. Local-remote test: Send an empty message to your account on another
machine. machine.
% echo to: me@wherever | /var/qmail/bin/qmail-inject % echo to: me@wherever | /var/qmail/bin/qmail-inject
qmail: new msg 53 qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666 qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
qmail: starting delivery 4: msg 53 to remote me@wherever qmail: starting delivery 4: msg 53 to remote me@wherever
qmail: status: local 0/10 remote 1/20
qmail: delivery 4: success: 1.2.3.4_accepted_message./... qmail: delivery 4: success: 1.2.3.4_accepted_message./...
qmail: status: local 0/10 remote 0/20
qmail: end msg 53 qmail: end msg 53
There will be a pause between ``starting delivery'' and ``success''; There will be a pause between ``starting delivery'' and ``success'';
SMTP is slow. Check that the message is in your mailbox on the other SMTP is slow. Check that the message is in your mailbox on the other
machine. machine.
17. Local-postmaster test: Send mail to postmaster, any capitalization. 17. Local-postmaster test: Send mail to postmaster, any capitalization.
% echo to: POSTmaster | /var/qmail/bin/qmail-inject % echo to: POSTmaster | /var/qmail/bin/qmail-inject
Look for the message in ~alias/Mailbox. Look for the message in the alias mailbox, normally ~alias/Mailbox.
18. Double-bounce test: Send a message with a completely bad envelope. 18. Double-bounce test: Send a message with a completely bad envelope.
% /var/qmail/bin/qmail-inject -f nonexistent % /var/qmail/bin/qmail-inject -f nonexistent
To: unknownuser To: unknownuser
@ -98,7 +118,8 @@ Pre-upgrade tests:
This is a test. This is only a test. This is a test. This is only a test.
% %
(Use end-of-file, not dot, to end the message.) Look for the double (Use end-of-file, not dot, to end the message.) Look for the double
bounce in ~alias/Mailbox. bounce in the alias mailbox.
19. Group membership test: 19. Group membership test:
% cat > ~me/.qmail-groups % cat > ~me/.qmail-groups
|groups >> MYGROUPS; exit 0 |groups >> MYGROUPS; exit 0
@ -112,28 +133,35 @@ Upgrading from sendmail to qmail:
20. Read INSTALL.boot. You must replace the sendmail invocation in your 20. Read INSTALL.boot. You must replace the sendmail invocation in your
boot scripts with an appropriate qmail invocation. boot scripts with an appropriate qmail invocation.
21. Kill the sendmail daemon. You should first kill -STOP the daemon; if 21. Kill the sendmail daemon. You should first kill -STOP the daemon; if
any children are running, you should kill -CONT, wait, kill -STOP any children are running, you should kill -CONT, wait, kill -STOP
again, and repeat ad nauseam. If there aren't any children, kill again, and repeat ad nauseam. If there aren't any children, kill
-TERM and then kill -CONT. -TERM and then kill -CONT.
22. Replace sendmail with a link to qmail's ``sendmail'' wrapper: 22. Replace sendmail with a link to qmail's ``sendmail'' wrapper:
# mv /usr/lib/sendmail /usr/lib/sendmail.bak # mv /usr/lib/sendmail /usr/lib/sendmail.bak
# ln -s /var/qmail/bin/sendmail /usr/lib/sendmail # ln -s /var/qmail/bin/sendmail /usr/lib/sendmail
23. Set up qmail-smtpd in /etc/inetd.conf (all on one line): 23. Set up qmail-smtpd in /etc/inetd.conf (all on one line):
smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env
tcp-env /var/qmail/bin/qmail-smtpd tcp-env /var/qmail/bin/qmail-smtpd
Also comment out comsat in /etc/inetd.conf. Also comment out comsat in /etc/inetd.conf.
24. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons 24. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons
are running.) are running.)
25. Try to flush the sendmail queue: 25. Try to flush the sendmail queue:
# /usr/lib/sendmail.bak -q # /usr/lib/sendmail.bak -q
You can safely run sendmail.bak -q (or even sendmail.bak -q15m) You can safely run sendmail.bak -q (or even sendmail.bak -q15m)
while qmail is running. Do this until the sendmail queue is empty. while qmail is running. Do this until the sendmail queue is empty.
This may take several days. This may take several days.
26. Disable all the sendmail and binmail programs in your system. The 26. Disable all the sendmail and binmail programs in your system. The
safest approach is to chmod 0 everything. Some locations to check: safest approach is to chmod 0 everything. Some locations to check:
/usr/sbin/sendmail, /usr/lib/sendmail.bak, /usr/lib/sendmail.mx, /usr/sbin/sendmail, /usr/lib/sendmail.bak, /usr/lib/sendmail.mx,
/bin/mail, /usr/libexec/mail.local. /bin/mail, /usr/libexec/mail.local.
27. Make sure that ``mail'' still invokes a reasonable mailer. Under 27. Make sure that ``mail'' still invokes a reasonable mailer. Under
SVR4 you may want to link mail to mailx. SVR4 you may want to link mail to mailx.
@ -166,16 +194,20 @@ Post-upgrade tests (can be done immediately after step 24):
Connection closed by foreign host. Connection closed by foreign host.
% %
Look for the message in your mailbox. Look for the message in your mailbox.
29. Remote-local test: Send yourself some mail from another machine. 29. Remote-local test: Send yourself some mail from another machine.
30. Remote-error test: I think you can figure this one out. 30. Remote-error test: I think you can figure this one out.
31. UA test: Try sending mail, first to a local account, then to a 31. UA test: Try sending mail, first to a local account, then to a
remote account, with your normal user agent. remote account, with your normal user agent.
32. Remote-postmaster test: Send mail from another machine to 32. Remote-postmaster test: Send mail from another machine to
PoStMaStEr@domain. Look for the message in ~alias/Mailbox. PoStMaStEr@domain. Look for the message in the alias mailbox.
That's it! To report success: That's it! To report success:
% ( echo 'First M. Last'; cat `cat SYSDEPS` ) \ % ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
| mail djb-qst@koobera.math.uic.edu | mail djb-qst@cr.yp.to
Replace First M. Last with your name. If you have questions about qmail, Replace First M. Last with your name. If you have questions about qmail,
contact qmail@pobox.com. join the qmail mailing list; see http://pobox.com/~djb/qmail.html.

View file

@ -2,12 +2,12 @@ qmail lets each user control all addresses of the form user-anything.
Addresses that don't start with a username are controlled by a special Addresses that don't start with a username are controlled by a special
user, alias. Delivery instructions for foo go into ~alias/.qmail-foo; user, alias. Delivery instructions for foo go into ~alias/.qmail-foo;
delivery instructions for user-foo go into ~user/.qmail-foo. See delivery instructions for user-foo go into ~user/.qmail-foo. See
qmail-upgrade.0 and dot-qmail.0 for the full story. dot-qmail.0 for the full story.
qmail doesn't have any built-in support for /etc/aliases. If you have a qmail doesn't have any built-in support for /etc/aliases. If you have a
big /etc/aliases and you'd like to keep it, install the qmsmac package, big /etc/aliases and you'd like to keep it, install the fastforward
available separately. /etc/aliases should already include the aliases package, available separately. /etc/aliases should already include the
discussed below---Postmaster, MAILER-DAEMON, and root. aliases discussed below---Postmaster, MAILER-DAEMON, and root.
If you don't have a big /etc/aliases, you'll find it easier to use If you don't have a big /etc/aliases, you'll find it easier to use
qmail's native alias mechanism. Here's a checklist of aliases you should qmail's native alias mechanism. Here's a checklist of aliases you should

View file

@ -10,7 +10,6 @@ Find sendmail in your boot scripts. It's usually in either /etc/rc or
-q15m means it should run the queue every 15 minutes; you may see a -q15m means it should run the queue every 15 minutes; you may see a
different number. Comment out this line, and replace it with different number. Comment out this line, and replace it with
env - PATH="/var/qmail/bin:$PATH" \ csh -cf '/var/qmail/rc &'
csh -cf 'qmail-start ./Mailbox splogger qmail &'
That's it. (Make sure you include the ./ and the &.) That's it. (Make sure you include the &.)

View file

@ -10,13 +10,22 @@ But this is all optional---if control/smtpgreeting doesn't exist, qmail
will do something reasonable by default. You shouldn't worry much about will do something reasonable by default. You shouldn't worry much about
configuration right now. You can always come back and tune things later. configuration right now. You can always come back and tune things later.
There's one big exception. You MUST tell qmail your hostname. The easy There's one big exception. You MUST tell qmail your hostname. Just run
way to do this is to run the qmail-config script: the config-fast script:
# ./qmail-config # ./config-fast your.full.host.name
qmail-config finds your fully-qualified hostname in DNS and puts it into config-fast puts your.full.host.name into control/me. It also puts it
control/me. It also selects good defaults for a few other controls. into control/locals and control/rcpthosts, so that qmail will accept
mail for your.full.host.name.
You can instead use the config script, which looks up your host name in
DNS:
# ./config
config also looks up your local IP addresses in DNS to decide which
hosts to accept mail for.
(Why doesn't qmail do these lookups on the fly? This was a deliberate (Why doesn't qmail do these lookups on the fly? This was a deliberate
design decision. qmail does all its local functions---header rewriting, design decision. qmail does all its local functions---header rewriting,

View file

@ -1,6 +1,7 @@
Here's how to set up the qmail groups and the qmail users. Here's how to set up the qmail groups and the qmail users.
On some systems there are commands that make this easy. Solaris: On some systems there are commands that make this easy. Solaris and
Linux:
# groupadd nofiles # groupadd nofiles
# useradd -g nofiles -d /var/qmail/alias alias # useradd -g nofiles -d /var/qmail/alias alias
@ -12,6 +13,18 @@ On some systems there are commands that make this easy. Solaris:
# useradd -g qmail -d /var/qmail qmailr # useradd -g qmail -d /var/qmail qmailr
# useradd -g qmail -d /var/qmail qmails # useradd -g qmail -d /var/qmail qmails
FreeBSD 2.2:
# pw groupadd nofiles
# pw useradd alias -g nofiles -d /var/qmail/alias -s /nonexistent
# pw useradd qmaild -g nofiles -d /var/qmail -s /nonexistent
# pw useradd qmaill -g nofiles -d /var/qmail -s /nonexistent
# pw useradd qmailp -g nofiles -d /var/qmail -s /nonexistent
# pw groupadd qmail
# pw useradd qmailq -g qmail -d /var/qmail -s /nonexistent
# pw useradd qmailr -g qmail -d /var/qmail -s /nonexistent
# pw useradd qmails -g qmail -d /var/qmail -s /nonexistent
BSDI 2.0: BSDI 2.0:
# addgroup nofiles # addgroup nofiles

64
INSTALL.maildir Normal file
View file

@ -0,0 +1,64 @@
This file points out some reasons that you might want to switch from
mbox format to a new format, maildir.
1. The trouble with mbox
The mbox format---the format of ~user/Mailbox, understood by BSD Mail
and lots of other MUAs---is inherently unreliable.
Think about it: what happens if the system crashes while a program is
appending a new message to ~user/Mailbox? The message will be truncated.
Even worse, if it was truncated in the middle of a line, it will end up
being merged with the next message! Sure, the mailer understands that it
wasn't successful, so it'll try delivering the message again later, but
it can't fix your corrupted mbox.
Other formats, such as mh folders, are just as unreliable.
qmail supports maildir, a crashproof format for incoming mail messages.
maildir is fast and easy for MUAs to use. Even better, maildir works
wonders over NFS---see below.
I don't want to cram maildir down people's throats, so it's not the
default. Nevertheless, I encourage you to start asking for maildir
versions of your favorite MUAs, and to switch over to maildir as soon as
you can.
WARNING: qmail uses flock() to lock ~user/Mailbox. This agrees with the
modern mail.local locking choice. If your MUA doesn't use flock(), your
best bet is to switch to maildir, and to set up synchronous maildir2mbox
execution, as described below.
2. Sun's Network F_ail_u_re System
Anyone who tells you that mail can be safely delivered in mbox format
over NFS is pulling your leg---as explained above, mbox format is
inherently unreliable even on a single machine.
Anyway, NFS is the most unreliable computing environment ever invented,
and qmail doesn't even pretend to support mbox over NFS.
You should switch to maildir, which works fine over NFS without any
locking. You can safely read your mail over NFS if it's in maildir
format. Any number of machines can deliver mail to you at the same time.
(On the other hand, for efficiency, it's better to get NFS out of the
picture---your mail should be delivered on the server that contains your
home directory.)
Here's how to set up qmail to use maildir for your incoming mail:
% maildirmake $HOME/Maildir
% echo ./Maildir/ > ~/.qmail
Make sure you include the trailing slash on Maildir/.
The system administrator can set up Maildir as the default for everybody
by creating a maildir in the new-user template directory and replacing
./Mailbox with ./Maildir/ in /var/qmail/rc.
Until your MUA supports maildir, you'll probably want to convert maildir
format to (gaaack) mbox format. I've supplied a maildir2mbox utility
that does the trick, along with some tiny qail and elq and pinq wrappers
that call maildir2mbox before calling Mail or elm or pine.

View file

@ -3,110 +3,46 @@ provides user-controlled mailing lists, cross-host alias loop detection,
and many other important qmail features. and many other important qmail features.
There's one part of qmail-local that you need to know about right now: There's one part of qmail-local that you need to know about right now:
qmail-local doesn't support an insecure central mail spool. It delivers qmail-local delivers mail by default into ~user/Mailbox (in mbox format,
mail by default into ~user/Mailbox (in mbox format). using flock), rather than /var/spool/mail/user.
This file explains what you should do to deal with this change. It also For an explanation of the problems with /var/spool/mail, and a list of
points out some reasons that you might want to make an even bigger options if you want to continue delivering to /var/spool/mail, see
change, switching from mbox format to a new format, maildir. INSTALL.vsm.
If you desperately don't want to change anything, see INSTALL.qsmhook. This file explains how to handle the change to ~user/Mailbox. The basic
procedure is simple:
* Move each /var/spool/mail/user to ~user/Mailbox. For safety, do
this in single-user mode.
Contents: * As root, set up a symbolic link from /var/spool/mail/user to
1. Throw away /usr/spool/mail! ~user/Mailbox for each user. /var/spool/mail should be mode 1777,
2. The trouble with mbox so users will not be able to accidentally remove these links.
3. Sun's Network F_ail_u_re System
An alternative to symbolic links is hlfsd. Consult the documentation for
hlfsd if it is included in your operating system.
1. Throw away /usr/spool/mail! If /var/spool/mail is large, you can gain extra speed by configuring
your mail software to look at ~user/Mailbox directly:
/usr/spool/mail, often called /var/spool/mail or /var/mail, is a * Most MUAs: Put ``setenv MAIL $HOME/Mailbox'' in your system-wide
security disaster. A user's mailbox belongs in his home directory, not a .cshrc and ``MAIL=$HOME/Mailbox; export MAIL'' in your system-wide
shared directory. Even if you don't install qmail, you should destroy .profile.
/usr/spool/mail. This takes four steps:
A. Convince your local mailer to deliver to ~user/Mailbox. If you're * elm: Change "mailbox" to "Mailbox" around line 388 of newmbox.c and
using something like procmail, this is easy---just change SYSTEM_MBOX recompile. (elm looks at $MAIL, but without this change elm will
in config.h. If you're installing qmail, you don't have to do fail if two users try to read mail simultaneously.)
anything. Otherwise, take a look at hlfsd from
ftp.cs.columbia.edu/pub/amd.
B. Move each /usr/spool/mail/user to ~user/Mailbox. For safety, do * pine: Put ``inbox-path=Mailbox'' in your system-wide pine.conf.
this in single-user mode---you don't want to risk corrupting (For pine versions more recent than 3.91, see also FAQ 6.2.)
mailboxes. (qmail makes it easy to turn off deliveries temporarily:
just kill the qmail-send daemon. But you aren't running qmail yet.)
When you're done, remove /usr/spool/mail.
C. Put ``setenv MAIL $HOME/Mailbox'' in your system-wide .cshrc, * procmail: Change SYSTEM_MBOX in config.h and recompile.
``MAIL=$HOME/Mailbox; export MAIL'' in your system-wide .profile,
``inbox-path=Mailbox'' in your system-wide pine.conf. If you're using
qpopper 2.2, you'll have to recompile with -DHOMEDIRMAIL in CFLAGS
and with /.mail changed to /Mailbox in pop_dropcopy.c. If you're
using elm on a multiuser system, you'll have to recompile elm with
"mailbox" changed to "Mailbox" around line 388 of newmbox.c.
D. Announce the change. * qpopper 2.2: Change /.mail to /Mailbox in pop_dropcopy.c and
recompile with -DHOMEDIRMAIL in CFLAGS.
Some vendors, in a misguided attempt to solve the security problems of Some vendors, in a misguided attempt to solve the security problems of
/usr/spool/mail, have made all MUAs (e.g., /usr/ucb/Mail) setgid mail. /var/spool/mail, have made all their mail software setgid mail. After
After you get rid of /usr/spool/mail, you can also disable those you move the mailboxes, you can---and, for security, should---remove
setgid-mail bits. those setgid-mail bits.
2. The trouble with mbox
The mbox format---the format of ~user/Mailbox, understood by BSD Mail
and lots of other MUAs---is inherently unreliable.
Think about it: what happens if the system crashes while a program is
appending a new message to ~user/Mailbox? The message will be truncated.
Even worse, if it was truncated in the middle of a line, it will end up
being merged with the next message! Sure, the mailer understands that it
wasn't successful, so it'll try delivering the message again later, but
it can't fix your corrupted mbox.
Other formats, such as mh folders, are just as unreliable.
qmail supports maildir, a crashproof format for incoming mail messages.
maildir is fast and easy for MUAs to use. Even better, maildir works
wonders over NFS---see below.
I don't want to cram maildir down people's throats, so it's not the
default. Nevertheless, I encourage you to start asking for maildir
versions of your favorite MUAs, and to switch over to maildir as soon as
you can.
WARNING: qmail uses flock() to lock ~user/Mailbox. This agrees with the
modern mail.local locking choice. If your MUA doesn't use flock(), your
best bet is to switch to maildir, and to set up synchronous maildir2mbox
execution, as described below.
3. Sun's Network F_ail_u_re System
Anyone who tells you that mail can be safely delivered in mbox format
over NFS is pulling your leg---as explained above, mbox format is
inherently unreliable even on a single machine.
Anyway, NFS is the most unreliable computing environment ever invented,
and qmail doesn't even pretend to support mbox over NFS.
You should switch to maildir, which works fine over NFS without any
locking. You can safely read your mail over NFS if it's in maildir
format. Any number of machines can deliver mail to you at the same time.
(On the other hand, for efficiency, it's better to get NFS out of the
picture---your mail should be delivered on the server that contains your
home directory.)
Here's how to set up qmail to use maildir for your incoming mail:
% maildirmake $HOME/Maildir
% echo ./Maildir/ > ~/.qmail
Make sure you include the trailing slash on Maildir/.
Until your MUA supports maildir, you'll probably want to convert maildir
format to (gaaack) mbox format. I've supplied a maildir2mbox utility
that does the trick, along with some tiny qail and elq and pinq wrappers
that call maildir2mbox before calling Mail or elm or pine.

View file

@ -1,53 +0,0 @@
You can set up qmail to use the same local delivery agent as sendmail,
through a mechanism called qsmhook. This file says how.
Before you do this, may I ask why? If you simply don't want the hassle
of moving user mailboxes from /usr/spool/mail to ~, please reconsider---
/usr/spool/mail has always been a big security problem. See, for
example, CERT advisory 95:02.
If you're trying to preserve /etc/aliases and ~user/.forward, you're
looking the wrong way---those are handled by sendmail internally, not by
the local delivery agent. You can use your old /etc/aliases with qmail
by installing the qmsmac package.
Perhaps you've set up an advanced agent like procmail. But most people
use procmail for nothing more than sorting mail into several mailboxes;
and that's much easier with qmail's local forwarding mechanism, which
gives each user control over user-anything. If you have a few users who
really do need procmail, they can easily run procmail from their own
.qmail files.
Do you still want to set up qsmhook? Send me some e-mail and let me know
why. Perhaps I can provide something for you in a future qmail release.
Here's what to do. First, tack ``:alias'' onto the end of each address
in /var/qmail/control/locals, and put the results into
/var/qmail/control/virtualdomains. For example, if control/locals has
localhost
silverton.berkeley.edu
then control/virtualdomains should now have (without extra spaces)
localhost:alias
silverton.berkeley.edu:alias
Second, cp /dev/null control/locals.
Third, put a line into ~alias/.qmail-default, based on sendmail's Mlocal
line. For example, if sendmail.cf has
Mlocal, P=/bin/mail, F=lsDFMPrmn, S=10, R=20, A=mail -d $u
then ~alias/.qmail-default should have
|qsmhook -x alias- -lsDFMPmn /bin/mail -r %g -d %u
As another example, if sendmail.cf has
Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnP, S=10, R=20, A=mail.local -d $u
then ~alias/.qmail-default should have
|qsmhook -x alias- -lsDFMmnP /usr/lib/mail.local -f %g -d %u

50
INSTALL.vsm Normal file
View file

@ -0,0 +1,50 @@
UNIX has traditionally delivered mail into a central spool directory,
/var/spool/mail. (The original name was /usr/spool/mail; some systems
now use /var/mail.) There are two basic problems with /var/spool/mail:
* It's slow. On systems with thousands of users, /var/spool/mail has
thousands of entries. A few UNIX systems support fast operations on
large directories, but most don't.
* It's insecure. Writing code that works safely in a world-writable
directory is not easy. See, for example, CERT advisory 95:02.
These may not be problems at your site, so you may want to leave your
mailboxes in /var/spool/mail.
This file explains several ways that you can configure qmail to use
existing /var/spool/mail delivery tools. Please note that I do not vouch
for the security or reliability of any of those tools.
1. What to configure
The qmail system is started from /var/qmail/rc with
qmail-start ./Mailbox splogger qmail
The first argument to qmail-start, ./Mailbox, is the default delivery
instruction. You can change it to run a program such as binmail or
procmail. (See dot-qmail.0 for the format of delivery instructions.)
2. Using procmail
You may already have installed procmail for mail filtering. procmail
delivers to /var/spool/mail by default.
To set up qmail to use procmail, simply copy /var/qmail/boot/proc to
/var/qmail/rc.
Note that procmail must be in your system's boot PATH; if it isn't, you
will have edit /var/qmail/rc to include the full path.
3. Using sendmail's delivery agent
sendmail uses binmail to deliver to /var/spool/mail. binmail is shipped
with the operating system as /bin/mail or /usr/libexec/mail.local.
There is some variation in binmail syntax among systems. The most common
interfaces are shown in /var/qmail/boot/binm1, /var/qmail/boot/binm2,
and /var/qmail/boot/binm3.

View file

@ -96,7 +96,8 @@ qmail-send may at its leisure try to deliver a message to a NOT DONE
address. If the message is successfully delivered, qmail-send marks the address. If the message is successfully delivered, qmail-send marks the
address as DONE. If the delivery attempt meets with permanent failure, address as DONE. If the delivery attempt meets with permanent failure,
qmail-send first appends a note to bounce/457, creating bounce/457 if qmail-send first appends a note to bounce/457, creating bounce/457 if
necessary; then it marks the address as DONE. necessary; then it marks the address as DONE. Note that bounce/457 is
not crashproof.
qmail-send may handle bounce/457 at any time, as follows: it (1) injects qmail-send may handle bounce/457 at any time, as follows: it (1) injects
a new bounce message, created from bounce/457 and mess/457; (2) deletes a new bounce message, created from bounce/457 and mess/457; (2) deletes

1276
Makefile

File diff suppressed because it is too large Load diff

37
PIC.local2alias Normal file
View file

@ -0,0 +1,37 @@
Original message:
To: help
Hi.
qmail-inject Fill in the complete envelope and header:
| (envelope) from joe@heaven.af.mil to help@heaven.af.mil
| From: joe@heaven.af.mil
| To: help@heaven.af.mil
|
| Hi.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, help@heaven.af.mil.
| Is heaven.af.mil in locals? Yes.
| Deliver locally to help@heaven.af.mil.
V
qmail-lspawn ./Mailbox
| Look at mailbox name, help.
| Is help listed in qmail-users? No.
| Is there a help account? No.
| Give control of the message to alias.
| Run qmail-local.
V
qmail-local alias ~alias help - help heaven.af.mil joe@heaven.af.mil ./Mailbox
Does ~alias/.qmail-help exist? Yes: "john".
Forward message to john.

41
PIC.local2ext Normal file
View file

@ -0,0 +1,41 @@
Original message:
To: fred-sos
Hi.
qmail-inject Fill in the complete envelope and header:
| (envelope) from joe@heaven.af.mil to fred-sos@heaven.af.mil
| From: joe@heaven.af.mil
| To: fred-sos@heaven.af.mil
|
| Hi.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, fred-sos@heaven.af.mil.
| Is heaven.af.mil in locals? Yes.
| Deliver locally to fred-sos@heaven.af.mil.
V
qmail-lspawn ./Mailbox
| Look at mailbox name, fred-sos.
| Is fred-sos listed in qmail-users? No.
| Is there a fred-sos account? No.
| Is there a fred account? Yes.
| Is fred's uid nonzero? Yes.
| Is ~fred visible to the qmailp user? Yes.
| Is ~fred owned by fred? Yes.
| Give control of the message to fred.
| Run qmail-local.
V
qmail-local fred ~fred fred-sos - sos heaven.af.mil joe@heaven.af.mil ./Mailbox
Does ~fred/.qmail-sos exist? Yes: "./Extramail".
Write message to ./Extramail in mbox format.

40
PIC.local2local Normal file
View file

@ -0,0 +1,40 @@
Original message:
To: fred
Hi.
qmail-inject Fill in the complete envelope and header:
| (envelope) from joe@heaven.af.mil to fred@heaven.af.mil
| From: joe@heaven.af.mil
| To: fred@heaven.af.mil
|
| Hi.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, fred@heaven.af.mil.
| Is heaven.af.mil in locals? Yes.
| Deliver locally to fred@heaven.af.mil.
V
qmail-lspawn ./Mailbox
| Look at mailbox name, fred.
| Is fred listed in qmail-users? No.
| Is there a fred account? Yes.
| Is fred's uid nonzero? Yes.
| Is ~fred visible to the qmailp user? Yes.
| Is ~fred owned by fred? Yes.
| Give control of the message to fred.
| Run qmail-local.
V
qmail-local fred ~fred fred '' '' heaven.af.mil joe@heaven.af.mil ./Mailbox
Does ~fred/.qmail exist? No.
Write message to ./Mailbox in mbox format.

38
PIC.local2rem Normal file
View file

@ -0,0 +1,38 @@
Original message:
To: bill@irs.gov
Hi.
qmail-inject Fill in the complete envelope and header:
| (envelope) from joe@heaven.af.mil to bill@irs.gov
| From: joe@heaven.af.mil
| To: bill@irs.gov
|
| Hi.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, bill@irs.gov.
| Is irs.gov in locals? No.
| Is bill@irs.gov in virtualdomains? No.
| Is irs.gov in virtualdomains? No.
| Is .gov in virtualdomains? No.
| Deliver remotely to bill@irs.gov.
V
qmail-rspawn Run qmail-remote.
|
V
qmail-remote Look at host name, irs.gov.
Is irs.gov listed in smtproutes? No.
Look up DNS MX/A for irs.gov and connect to it by SMTP:
MAIL FROM:<joe@heaven.af.mil>
RCPT TO:<bill@irs.gov>

44
PIC.local2virt Normal file
View file

@ -0,0 +1,44 @@
Original message:
To: dude@tommy.gov
Hi.
qmail-inject Fill in the complete envelope and header:
| (envelope) from joe@heaven.af.mil to dude@tommy.gov
| From: joe@heaven.af.mil
| To: dude@tommy.gov
|
| Hi.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, dude@tommy.gov.
| Is tommy.gov in locals? No.
| Is dude@tommy.gov in virtualdomains? No.
| Is tommy.gov in virtualdomains? Yes: "tommy.gov:fred".
| Deliver locally to fred-dude@tommy.gov.
V
qmail-lspawn ./Mailbox
| Look at mailbox name, fred-dude.
| Is fred-dude listed in qmail-users? No.
| Is there a fred-dude account? No.
| Is there a fred account? Yes.
| Is fred's uid nonzero? Yes.
| Is ~fred visible to the qmailp user? Yes.
| Is ~fred owned by fred? Yes.
| Give control of the message to fred.
| Run qmail-local.
V
qmail-local fred ~fred fred-dude - dude tommy.gov joe@heaven.af.mil ./Mailbox
Does ~fred/.qmail-dude exist? No.
Does ~fred/.qmail-default exist? Yes: "./Mail.tommy".
Write message to ./Mail.tommy in mbox format.

38
PIC.nullclient Normal file
View file

@ -0,0 +1,38 @@
Original message:
To: bill@irs.gov
Hi.
qmail-inject Fill in the complete envelope and header:
| (envelope) from joe@heaven.af.mil to bill@irs.gov
| From: joe@heaven.af.mil
| To: bill@irs.gov
|
| Hi.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, bill@irs.gov.
| Is irs.gov in locals? No.
| Is bill@irs.gov in virtualdomains? No.
| Is irs.gov in virtualdomains? No.
| Is .gov in virtualdomains? No.
| Deliver remotely to bill@irs.gov.
V
qmail-rspawn Run qmail-remote.
|
V
qmail-remote Look at host name, irs.gov.
Is irs.gov listed in smtproutes? Yes: ":bigbang.af.mil".
Look up DNS A for bigbang.af.mil and connect by SMTP:
MAIL FROM:<joe@heaven.af.mil>
RCPT TO:<bill@irs.gov>

8
PIC.relaybad Normal file
View file

@ -0,0 +1,8 @@
qmail-smtpd Receive message by SMTP from another host:
MAIL FROM:<spammer@aol.com>
RCPT TO:<bill@irs.gov>
Is $RELAYCLIENT set? No.
Is irs.gov in rcpthosts? No.
Reject RCPT.

33
PIC.relaygood Normal file
View file

@ -0,0 +1,33 @@
qmail-smtpd Receive message by SMTP from another host:
| MAIL FROM:<joe@heaven.af.mil>
| RCPT TO:<bill@irs.gov>
|
| Is $RELAYCLIENT set? Yes: "".
| Accept RCPT.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, bill@irs.gov.
| Is irs.gov in locals? No.
| Is bill@irs.gov in virtualdomains? No.
| Is irs.gov in virtualdomains? No.
| Is .gov in virtualdomains? No.
| Deliver remotely to bill@irs.gov.
V
qmail-rspawn Run qmail-remote.
|
V
qmail-remote Look at host name, irs.gov.
Is irs.gov listed in smtproutes? No.
Look up DNS MX/A for irs.gov and connect to it by SMTP:
MAIL FROM:<joe@heaven.af.mil>
RCPT TO:<bill@irs.gov>

36
PIC.rem2local Normal file
View file

@ -0,0 +1,36 @@
qmail-smtpd Receive message by SMTP from another host:
| MAIL FROM:<bill@irs.gov>
| RCPT TO:<joe@heaven.af.mil>
|
| Is $RELAYCLIENT set? No.
| Is heaven.af.mil in rcpthosts? Yes.
| Accept RCPT.
V
qmail-queue Store message safely on disk.
Trigger qmail-send.
|
V
qmail-send Look at envelope recipient, joe@heaven.af.mil.
| Is heaven.af.mil in locals? Yes.
| Deliver locally to joe@heaven.af.mil.
V
qmail-lspawn ./Mailbox
| Look at mailbox name, joe.
| Is joe listed in qmail-users? No.
| Is there a joe account? Yes.
| Is joe's uid nonzero? Yes.
| Is ~joe visible to the qmailp user? Yes.
| Is ~joe owned by joe? Yes.
| Give control of the message to joe.
| Run qmail-local.
V
qmail-local joe ~joe joe '' '' heaven.af.mil bill@irs.gov ./Mailbox
Does ~joe/.qmail exist? No.
Write message to ./Mailbox in mbox format.

358
README
View file

@ -1,6 +1,6 @@
qmail 1.01 qmail 1.02
19970413 19980430
Copyright 1997 Copyright 1998
D. J. Bernstein, qmail@pobox.com D. J. Bernstein, qmail@pobox.com
qmail is a secure, reliable, efficient, simple message transfer agent. qmail is a secure, reliable, efficient, simple message transfer agent.
@ -9,191 +9,261 @@ typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and
BLURB4 for more detailed advertisements. BLURB4 for more detailed advertisements.
INSTALL says how to set up and test qmail. If you're upgrading from INSTALL says how to set up and test qmail. If you're upgrading from
1.00, read UPGRADE instead. 1.01, read UPGRADE instead.
See PIC.* for some ``end-to-end'' pictures of mail flowing through the
qmail system.
See http://pobox.com/~djb/qmail.html for other qmail-related software See http://pobox.com/~djb/qmail.html for other qmail-related software
and a pointer to the qmail mailing list. and a pointer to the qmail mailing list.
Other documentation here: RFC* explain solutions to several Internet Other documentation: http://pobox.com/~djb/proto.html shows solutions to
mail problems; all of these except RFCMXPS are implemented in qmail. several Internet mail problems; many of these solutions are implemented
CHANGES and THANKS show how qmail has changed since it was first in qmail. CHANGES and THANKS show how qmail has changed since it was
released. SECURITY, INTERNALS, THOUGHTS, and TODO record many of the first released. SECURITY, INTERNALS, THOUGHTS, and TODO record many of
qmail design decisions. the qmail design decisions.
The rest of this file is a list of systypes where various versions of The rest of this file is a list of systypes where various versions of
qmail have been reported to work. 0.90 was the first gamma version. 0.96 qmail have been reported to work. 0.96 was the final gamma version; 1.00
was the final gamma version. 1.00 had exactly the same code as 0.96. To had exactly the same code as 0.96. To see your systype, make systype;
see your systype, make systype; cat systype. cat systype.
1.00: a.ux-3.0-svr2-:-:-:mc68030-:- (tnx RF) 1.00: a.ux-3.0-svr2-:-:-:mc68030-:- (tnx RF)
0.96: aix-3-2-:-:-:000011216700-:- (tnx JLB) 1.01: aix-3-2-:-:-:000000406300-:- (tnx DG)
0.91: aix-3-2-:-:-:000109257500-:- 1.01: aix-3-2-:-:-:000011216700-:- (tnx JLB)
0.96: aix-4-1-:-:-:000088581000-:- (tnx HJB) 1.01: aix-4-1-:-:-:000041574c00-:- (tnx M2H)
0.95: aix-4-1-:-:-:00061176a600-:- (tnx JS) 1.01: aix-4-1-:-:-:000088581000-:- (tnx HJB)
1.00: aix-4-1-:-:-:00910033a000-:- (tnx K2J) 1.01: aix-4-1-:-:-:002b51134c00-:- (tnx MP)
0.91: aix-4-2-:-:-:006030934c00-:- 1.00: aix-4-1-:-:-:00910033a000-:- (tnx KJJ)
1.00: bsd.os-2.0.1-:i386-:-:i486-:- (tnx KR) 1.01: aix-4-2-:-:-:000055247900-:- (tnx JLB)
0.93: bsd.os-2.0.1-:i386-:-:pentium-:- 1.01: aix-4-2-:-:-:000062295800-:- (tnx TD)
1.01: aix-4-2-:-:-:000136094c00-:- (tnx T2U)
1.00: aix-4-2-:-:-:000205254600-:- (tnx MGM)
1.01: aix-4-2-:-:-:005255bc4c00-:- (tnx DS)
1.01: aix-4-2-:-:-:006030944c00-:-
1.01: bsd.386-1.1-0-:i386-:-:i386-:- (tnx T2M)
1.01: bsd.os-2.0-:i386-:-:pentium-:- (tnx MSS)
1.01: bsd.os-2.0.1-:i386-:-:i486-:- (tnx KR)
0.96: bsd.os-2.1-:i386-:-:-:- (tnx DAR) 0.96: bsd.os-2.1-:i386-:-:-:- (tnx DAR)
1.00: bsd.os-2.1-:i386-:-:i486-:- (tnx RJC) 1.00: bsd.os-2.1-:i386-:-:i486-:- (tnx RJC)
0.96: bsd.os-2.1-:i386-:-:pentium-:- (tnx DAR) 0.96: bsd.os-2.1-:i386-:-:pentium-:- (tnx UO)
1.01: bsd.os-3.0-:i386-:-:-:- (tnx VU)
1.01: bsd.os-3.0-:i386-:-:pentium-:- (tnx RJO)
1.01: bsd.os-3.1-:i386-:-:pentium-:- (tnx ABC)
1.01: bsd.os-3.1-:i386-:-:pentium.ii-:- (tnx UO)
0.96: dgux-5.4r2.01-generic-:-:-:aviion-:- (tnx HWM) 0.96: dgux-5.4r2.01-generic-:-:-:aviion-:- (tnx HWM)
0.93: dgux-5.4r3.10-generic-:-:-:aviion-:- (tnx HWM) 1.01: freebsd-2.1.0-release-:i386-:-:i486-dx-:- (tnx VV)
0.91: freebsd-2.0.5-release-:i386-:-:-:- (tnx TG) 1.01: freebsd-2.1.0-release-:i386-:-:i486.dx2-:- (tnx JLB)
0.92: freebsd-2.1-stable-:i386-:-:pentium.815\100-:- 1.00: freebsd-2.1.0-release-:i386-:-:i486dx-:- (tnx chrisj=???)
0.90: freebsd-2.1.0-release-:i386-:-:cy486dlc-:- (tnx G2A) 1.01: freebsd-2.1.0-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MBS)
1.00: freebsd-2.1.0-release-:i386-:-:i486-dx2-:- (tnx JLB) 1.01: freebsd-2.1.5-release-:i386-:-:i486-dx-:- (tnx B1F)
1.00: freebsd-2.1.0-release-:i386-:-:i486-dx-:- (tnx chrisj=???) 0.96: freebsd-2.1.5-release-:i386-:-:i486dx-:- (tnx FN)
0.96: freebsd-2.1.0-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MBS) 1.01: freebsd-2.1.5-release-:i386-:-:unknown.-:- (tnx BMF)
0.95: freebsd-2.1.5-release-:i386-:-:-:- (tnx NAA)
0.96: freebsd-2.1.5-release-:i386-:-:i486-dx-:- (tnx FN)
0.95: freebsd-2.1.5-release-:i386-:-:pentium.510\60.or.567\66-:- (tnx M2L)
0.95: freebsd-2.1.5-release-:i386-:-:pentium.735\90-:- (tnx AG)
0.92: freebsd-2.1.5-stable-:i386-:-:pentium.735\90.or.815\100-:- (tnx FE)
1.00: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM) 1.00: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM)
0.96: freebsd-2.1.6-release-:i386-:-:Pentium-Pro.150-:- (tnx CH) 0.96: freebsd-2.1.6-release-:i386-:-:Pentium-Pro.150-:- (tnx CH)
0.96: freebsd-2.1.6-release-:i386-:-:i486-dx-:- (tnx HCJ) 1.01: freebsd-2.1.6-release-:i386-:-:cy486dlc-:- (tnx M3H)
0.95: freebsd-2.1.6-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx M2L)
0.96: freebsd-2.1.6.1-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MF) 0.96: freebsd-2.1.6.1-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MF)
1.01: freebsd-2.1.7-release-:i386-:-:i486-dx-:- (tnx AAF)
1.00: freebsd-2.1.7-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx JBB) 1.00: freebsd-2.1.7-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx JBB)
0.95: freebsd-2.2-beta_a-:i386-:-:i486-dx2-:- (tnx DA) 1.01: freebsd-2.1.7-release-:i386-:-:pentium.815\100-:- (tnx B1F)
0.91: freebsd-2.2-current-:i386-:-:i486-dx-:- (tnx DC) 1.01: freebsd-2.2-970422-releng-:i386-:-:-:- (tnx TM)
1.00: freebsd-2.2-release-:i386-:-:-:- (tnx MT) 1.00: freebsd-2.2-release-:i386-:-:-:- (tnx MT)
1.00: freebsd-2.2.1-release-:i386-:-:-:- (tnx TM) 1.01: freebsd-2.2-stable-:i386-:-:cyrix.5x86-:- (tnx A2B)
1.00: freebsd-2.2.1-release-:i386-:-:i486-dx2-:- (tnx BR) 1.01: freebsd-2.2-stable-:i386-:-:pentium-:- (tnx gary@systemics=???)
1.00: freebsd-2.2.1-release-:i386-:-:pentium-:- (tnx IW) 1.01: freebsd-2.2.1-release-:i386-:-:-:- (tnx M2R)
0.92: freebsd-3.0-current-:i386-:-:pentium-:- (tnx J2M) 1.01: freebsd-2.2.1-release-:i386-:-:i486-dx-:- (tnx PGR)
1.00: freebsd-2.2.1-release-:i386-:-:i486.dx2-:- (tnx BR)
1.01: freebsd-2.2.1-release-:i386-:-:pentium-:- (tnx REB)
1.01: freebsd-2.2.1-release-:i386-:-:pentium.pro-:- (tnx JS)
1.01: freebsd-2.2.2-release-:i386-:-:amd.am5x86.write-through-:- (tnx AGB)
1.01: freebsd-2.2.2-release-:i386-:-:i486-dx-:- (tnx A2L)
1.01: freebsd-2.2.2-release-:i386-:-:i486.dx2-:- (tnx D3S)
1.01: freebsd-2.2.2-release-:i386-:-:pentium-:- (tnx B2F)
1.01: freebsd-2.2.2-release-:i386-:-:pentium.pro-:- (tnx M2G)
1.01: freebsd-2.2.5-release-:i386-:-:i486-dx-:- (tnx R2N)
1.01: freebsd-2.2.5-release-:i386-:-:i486.dx2-:- (tnx AY)
1.01: freebsd-2.2.5-release-:i386-:-:pentium.pro-:- (tnx AI)
1.01: freebsd-2.2.5-stable-:i386-:-:i486.dx2-:- (tnx JK)
1.01: freebsd-2.2.5-stable-:i386-:-:pentium-:- (tnx root@defiant=???)
1.01: freebsd-2.2.6-release-:i386-:-:-:- (tnx TM)
1.01: freebsd-2.2.6-release-:i386-:-:amd.am5x86.write-through-:- (tnx root@skully=???)
1.00: freebsd-3.0-970209-snap-:i386-:-:-:- (tnx YF)
1.01: freebsd-3.0-970428-snap-:i386-:-:pentium-:- (tnx M3S)
1.01: freebsd-3.0-970807-snap-:i386-:-:amd.k6-:- (tnx KMD)
1.01: freebsd-3.0-980309-snap-:i386-:-:pentium-:- (tnx MM)
1.01: freebsd-3.0-current-:i386-:-:pentium-:- (tnx KB)
1.01: hp-ux-a.09.05-a-:-:-:9000.712-:- (tnx SV)
1.01: hp-ux-a.09.07-a-:-:-:9000.712-:- (tnx LB)
1.00: hp-ux-b.09.00-a-:-:-:9000.360-:- (tnx VV) 1.00: hp-ux-b.09.00-a-:-:-:9000.360-:- (tnx VV)
0.91: hp-ux-b.10.01-a-:-:-:9000.712-:- (tnx S2R) 1.01: hp-ux-b.10.20-a-:-:-:9000.755-:- (tnx BCK)
0.94: hp-ux-b.10.01-a-:-:-:9000.715-:- (tnx BG) 1.01: irix-5.3-11091812-:-:-:ip22-:- (tnx JL)
0.91: hp-ux-b.10.01-a-:-:-:9000.801-:- (tnx S2T) 1.01: irix-6.2-03131015-:-:-:ip22-:- (tnx DS)
0.93: irix-5.3-02091401-:-:-:ip22-:- (tnx PW) 1.01: irix64-6.2-03131016-:-:-:ip19-:- (tnx AH)
0.91: irix-5.3-11091811-:sgi-:-:ip19-:- (tnx BS) 1.01: irix64-6.2-06101031-:-:-:ip28-:- (tnx DB)
0.96: irix-5.3-11091812-:-:-:ip22-:- (tnx JL) 1.01: linux-1.2.13-:i386-:-:i486-:- (tnx RF)
1.00: irix-6.2-03131015-:-:-:ip22-:- (tnx SAS) 1.01: linux-1.2.13-:i386-:-:pentium-:- (tnx MEE)
0.92: linux-1.2.13-:i386-:-:i386-:- (tnx RN) 1.01: linux-1.99.4-:i386-:-:pentium-:- (tnx C2H)
1.00: linux-1.2.13-:i386-:-:i486-:- (tnx RF) 1.01: linux-2.0.0-:i386-:-:i486-:- (tnx kragen@gentle=???)
0.96: linux-1.2.13-:i386-:-:pentium-:- (tnx MEE) 1.01: linux-2.0.0-:i386-:-:pentium-:- (tnx MJD)
0.91: linux-1.3.90-:alpha-:-:alpha-:- (tnx ES) 1.01: linux-2.0.6-:i386-:-:pentium-:-
0.93: linux-1.99.6-:i386-:-:pentium-:- (tnx TG)
0.94: linux-2.0.0-:i386-:-:i486-:- (tnx PCO)
1.00: linux-2.0.0-:i386-:-:pentium-:- (tnx JJR)
0.91: linux-2.0.1-:alpha-:-:alpha-:- (tnx BET)
0.90: linux-2.0.1-:i386-:-:i486-:- (tnx DF)
0.93: linux-2.0.1-:i386-:-:pentium-:- (tnx FW)
0.95: linux-2.0.6-:i386-:-:pentium-:-
1.00: linux-2.0.6-:i386-:-:ppro-:- (tnx MR) 1.00: linux-2.0.6-:i386-:-:ppro-:- (tnx MR)
0.96: linux-2.0.7-:i386-:-:i486-:- (tnx TLM) 1.01: linux-2.0.7-:i386-:-:i486-:- (tnx TLM)
0.90: linux-2.0.7-:i386-:-:pentium-:- (tnx K2J) 1.01: linux-2.0.9-:i386-:-:i486-:- (tnx VBM)
0.94: linux-2.0.8-:i386-:-:i486-:- (tnx EP)
0.90: linux-2.0.9-:i386-:-:pentium-:- (tnx JM)
0.90: linux-2.0.10-:i386-:-:i486-:- (tnx JL)
0.90: linux-2.0.10-:i386-:-:pentium-:- (tnx S2R)
0.90: linux-2.0.11-:i386-:-:i486-:- (tnx ET)
0.92: linux-2.0.12-:i386-:-:i486-:- (tnx TRR)
0.90: linux-2.0.12-:i386-:-:ppro-:- (tnx SS)
0.96: linux-2.0.13-:i386-:-:pentium-:- (tnx BW) 0.96: linux-2.0.13-:i386-:-:pentium-:- (tnx BW)
0.90: linux-2.0.14-:i386-:-:pentium-:- (tnx PS) 1.01: linux-2.0.15-:i386-:-:i486-:- (tnx JCD)
0.91: linux-2.0.15-:i386-:-:pentium-:- (tnx CL) 1.01: linux-2.0.18-:i386-:-:i486-:- (tnx tk@avalon=???)
0.90: linux-2.0.16-:i386-:-:i486-:- (tnx AP) 1.01: linux-2.0.18-:i386-:-:pentium-:- (tnx root@webtvchat=???)
0.91: linux-2.0.17-:i386-:-:i486-:- (tnx JL)
0.91: linux-2.0.17-:i386-:-:pentium-:- (tnx SS)
0.95: linux-2.0.18-:i386-:-:i386-:- (tnx RN)
1.00: linux-2.0.18-:i386-:-:i486-:- (tnx JMS)
1.00: linux-2.0.18-:i386-:-:pentium-:- (tnx D2S)
0.92: linux-2.0.19-:alpha-:-:alpha-:- (tnx BET)
0.92: linux-2.0.20-:i386-:-:pentium-:- (tnx RJH)
0.92: linux-2.0.21-:i386-:-:i486-:- (tnx DCC)
0.91: linux-2.0.21-:i386-:-:pentium-:- (tnx root@contact=???)
0.92: linux-2.0.22-:i386-:-:i486-:- (tnx root@attila=???)
1.00: linux-2.0.22-:i386-:-:pentium-:- (tnx MDI) 1.00: linux-2.0.22-:i386-:-:pentium-:- (tnx MDI)
1.00: linux-2.0.23-:i386-:-:i486-:- (tnx B2L) 1.00: linux-2.0.23-:i386-:-:i486-:- (tnx B2L)
0.93: linux-2.0.23-:i386-:-:pentium-:- (tnx IW) 1.01: linux-2.0.24-:i386-:-:i486-:- (tnx GLM)
0.93: linux-2.0.24-:i386-:-:i486-:- (tnx root@cerberus=???)
1.00: linux-2.0.24-:i386-:-:pentium-:- (tnx VV) 1.00: linux-2.0.24-:i386-:-:pentium-:- (tnx VV)
0.96: linux-2.0.25-:i386-:-:i486-:- (tnx BDB) 0.96: linux-2.0.25-:i386-:-:i486-:- (tnx BDB)
1.00: linux-2.0.25-:i386-:-:pentium-:- (tnx AP) 1.01: linux-2.0.25-:i386-:-:pentium-:- (tnx KA)
0.93: linux-2.0.25-:i386-:-:ppro-:- (tnx CS) 0.93: linux-2.0.26-:i386-:-:i486-:- (tnx blynch@texas=???)
0.93: linux-2.0.26-:i386-:-:i486-:- (tnx blynch=???) 1.01: linux-2.0.26-:i386-:-:pentium-:- (tnx robbie@opus=???)
0.96: linux-2.0.26-:i386-:-:pentium-:- (tnx ESM)
0.93: linux-2.0.26-:i386-:-:ppro-:- (tnx modus@enews=???)
1.00: linux-2.0.27-:-:-:sparc-:- (tnx SVD) 1.00: linux-2.0.27-:-:-:sparc-:- (tnx SVD)
1.00: linux-2.0.27-:i386-:-:i486-:- (tnx FPL) 1.00: linux-2.0.27-:i386-:-:i386-:- (tnx ECG)
1.00: linux-2.0.27-:i386-:-:pentium-:- (tnx root@bullwinkle=???) 1.01: linux-2.0.27-:i386-:-:i486-:- (tnx BN)
1.00: linux-2.0.27-:i386-:-:ppro-:- (tnx DE) 1.01: linux-2.0.27-:i386-:-:pentium-:- (tnx EK)
1.00: linux-2.0.28-:i386-:-:i486-:- (tnx H2S) 1.01: linux-2.0.27-:i386-:-:ppro-:- (tnx L3L)
1.01: linux-2.0.28-:i386-:-:i486-:- (tnx AAF)
1.00: linux-2.0.28-:i386-:-:pentium-:- (tnx root@duggy=???) 1.00: linux-2.0.28-:i386-:-:pentium-:- (tnx root@duggy=???)
1.00: linux-2.0.28-:i386-:-:ppro-:- (tnx KJS) 1.01: linux-2.0.28-:i386-:-:ppro-:- (tnx S3T)
1.00: linux-2.0.29-:i386-:-:i486-:- (tnx PK) 1.01: linux-2.0.28-osfmach3-:-:-:ppc-:- (tnx CG)
1.00: linux-2.0.29-:i386-:-:pentium-:- (tnx CL) 1.01: linux-2.0.29-:alpha-:-:alpha-:- (tnx MB)
1.01: linux-2.0.29-:i386-:-:i386-:- (tnx AJK)
1.01: linux-2.0.29-:i386-:-:i486-:- (tnx FPL)
1.01: linux-2.0.29-:i386-:-:pentium-:- (tnx FW)
1.00: linux-2.0.29-:i386-:-:ppro-:- (tnx MMM) 1.00: linux-2.0.29-:i386-:-:ppro-:- (tnx MMM)
0.92: linux-2.1.1-:i386-:-:i486-:- (tnx nick@sga2=???) 1.01: linux-2.0.30-:-:-:sparc-:- (tnx J2P)
0.92: linux-2.1.7-:i386-:-:ppro-:- (tnx JL) 1.01: linux-2.0.30-:alpha-:-:alpha-:- (tnx WS)
0.92: linux-2.1.9-:i386-:-:pentium-:- (tnx AC) 1.01: linux-2.0.30-:i386-:-:i386-:- (tnx OK)
0.92: linux-2.1.10-:i386-:-:ppro-:- (tnx S3T) 1.00: linux-2.0.30-:i386-:-:i486-:- (tnx KUT)
1.01: linux-2.0.30-:i386-:-:i486-:- (tnx PK)
1.01: linux-2.0.30-:i386-:-:pentium-:- (tnx AV)
1.00: linux-2.0.30-:i386-:-:ppro-:- (tnx root@gate=???)
1.01: linux-2.0.30-osfmach3-:-:-:ppc-:- (tnx PTW)
1.01: linux-2.0.30u11-:i386-:-:pentium-:- (tnx JTB)
1.01: linux-2.0.31-:i386-:-:i486-:- (tnx SAE)
1.01: linux-2.0.31-:i386-:-:pentium-:- (tnx B3W)
1.01: linux-2.0.31-:i386-:-:ppro-:- (tnx JAK)
1.01: linux-2.0.32-:-:-:ie86-:- (tnx root@vmlinuz=???)
1.01: linux-2.0.32-:alpha-:-:alpha-:- (tnx NR)
1.01: linux-2.0.32-:i386-:-:i486-:- (tnx SC)
1.01: linux-2.0.32-:i386-:-:pentium-:- (tnx HT)
1.01: linux-2.0.32-:i386-:-:ppro-:- (tnx RK)
1.01: linux-2.0.33-:i386-:-:i486-:- (tnx RAB)
1.01: linux-2.0.33-:i386-:-:pentium-:- (tnx AF)
1.01: linux-2.0.33-:i386-:-:ppro-:- (tnx B2W)
1.01: linux-2.1.9-:i386-:-:i486-:- (tnx SJB)
1.01: linux-2.1.10-:i386-:-:i486-:- (tnx JB)
0.96: linux-2.1.13-:i386-:-:i486-:- (tnx ML) 0.96: linux-2.1.13-:i386-:-:i486-:- (tnx ML)
0.93: linux-2.1.13-:i386-:-:ppro-:- (tnx JL)
0.96: linux-2.1.14-:i386-:-:pentium-:- (tnx SCW) 0.96: linux-2.1.14-:i386-:-:pentium-:- (tnx SCW)
0.96: linux-2.1.23-:i386-:-:pentium-:- (tnx JF) 0.96: linux-2.1.23-:i386-:-:pentium-:- (tnx JF)
1.01: linux-2.1.24-:-:-:ppc-:- (tnx meta=???)
0.96: linux-2.1.25-:i386-:-:i486-:- (tnx JBF) 0.96: linux-2.1.25-:i386-:-:i486-:- (tnx JBF)
0.96: linux-2.1.25-:i386-:-:pentium-:- (tnx UO) 0.96: linux-2.1.25-:i386-:-:pentium-:- (tnx UO)
1.00: linux-2.1.26-:i386-:-:i486-:- (tnx DK) 1.00: linux-2.1.26-:i386-:-:i486-:- (tnx DK)
1.00: linux-2.1.27-:i386-:-:pentium-:- (tnx JF) 1.00: linux-2.1.27-:i386-:-:pentium-:- (tnx JF)
1.01: linux-2.1.28-:i386-:-:i486-:- (tnx HDG)
1.00: linux-2.1.28-:i386-:-:pentium-:- (tnx RGS) 1.00: linux-2.1.28-:i386-:-:pentium-:- (tnx RGS)
1.00: linux-2.1.29-:i386-:-:i486-:- (tnx SJW) 1.00: linux-2.1.29-:i386-:-:i486-:- (tnx SJW)
0.93: netbsd-1.2-:amiga-:-:amiga.4000.(m68040.cpu/mmu/fpu)-:- (tnx OS) 1.01: linux-2.1.35-:i386-:-:pentium-:- (tnx JF)
1.01: linux-2.1.36-:i386-:-:i486-:- (tnx ML)
1.01: linux-2.1.42-:i386-:-:i486-:- (tnx wtanaka=???)
1.01: linux-2.1.46-:i386-:-:pentium-:- (tnx VR)
1.01: linux-2.1.51-:i386-:-:pentium-:- (tnx KO)
1.01: linux-2.1.61-:i386-:-:i486-:- (tnx RO)
1.01: linux-2.1.65-:i386-:-:i486-:- (tnx F2T)
1.01: linux-2.1.71-:i386-:-:ppro-:- (tnx MJG)
1.01: linux-2.1.78-:i386-:-:pentium-:- (tnx AS)
1.01: linux-2.1.82-:i386-:-:pentium-:- (tnx AY)
1.01: linux-2.1.85-:i386-:-:pentium-:- (tnx PJH)
1.00: machten-4-0.4-:-:-:powerpc-:- (tnx RAM)
1.01: netbsd-1.1-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GL)
1.01: netbsd-1.2-:hp300-:-:-:- (tnx ML)
1.01: netbsd-1.2-:i386-:-:i486dx.(genuineintel.486-class.cpu)-:- (tnx T2K)
0.96: netbsd-1.2-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GH) 0.96: netbsd-1.2-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GH)
0.91: netbsd-1.2-:mac68k-:-:apple.macintosh.se/30..(68030)-:- (tnx hauke=???) 1.01: netbsd-1.2.1-:mac68k-:-:apple.macintosh.se/30..(68030)-:- (tnx HM)
0.92: netbsd-1.2b-:i386-:-:-:- (tnx MG) 1.01: netbsd-1.2.1-:sparc-:-:fmi,mb86904.@.110.mhz,.on-chip.fpu-:- (tnx ZU)
0.93: netbsd-1.2b-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx MG)
0.96: netbsd-1.2c-:pmax-:-:-:- (tnx JLW) 0.96: netbsd-1.2c-:pmax-:-:-:- (tnx JLW)
0.92: nextstep-3.3-:hppa-:-:7100lc-:- 1.01: netbsd-1.3-:hp300-:-:hp.9000/433.(33mhz.mc68040.cpu+mmu+fpu,.4k.on-chip.physical.i/d.caches)-:- (tnx TB)
0.92: nextstep-3.3-:i386-:-:pentium-:- 1.01: netbsd-1.3.1-:sun3-:-:sun.3/60-:- (tnx MBS)
0.91: openbsd-1.2-:openbsd.m68k-:-:mac68k-:- (tnx AKB) 1.01: netbsd-1.3_alpha-:i386-:-:intel.pentium.(p54c).(586-class)-:- (tnx GL)
0.95: openbsd-2.0-:openbsd.i386-:-:i386-:- (tnx JPH) 1.01: nextstep-3.1-:mc680x0-:-:68040-:- (tnx JRY)
0.96: openbsd-2.0-:openbsd.m68k-:-:mac68k-:- (tnx AKB) 1.01: nextstep-3.3-:hppa-:-:7100lc-:-
1.01: nextstep-3.3-:i386-:-:pentium-:- (tnx HM)
1.01: nextstep-3.3-:mc680x0-:-:68040-:- (tnx WEB)
1.01: nextstep-4.1-:mc680x0-:-:68040-:- (tnx FN)
1.00: openbsd-2.0-hoth#0-:openbsd.i386-:-:i386-:- (tnx MBS) 1.00: openbsd-2.0-hoth#0-:openbsd.i386-:-:i386-:- (tnx MBS)
1.00: openbsd-2.0-mr_potatoe_head#2-:openbsd.i386-:-:i386-:- (JJMK) 1.00: openbsd-2.0-mr_potatoe_head#2-:openbsd.i386-:-:i386-:- (tnx JJMK)
0.96: openbsd-2.0-puma#1-:openbsd.m68k-:-:mac68k-:- (tnx AB) 0.96: openbsd-2.0-puma#1-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
0.92: osf1-v3.0-347-:-:-:alpha-:- (tnx B2H) 1.01: openbsd-2.1-asgard#1-:openbsd.i386-:-:i386-:- (tnx ETT)
0.92: osf1-v4.0-386-:-:-:alpha-:- (tnx WW) 1.01: openbsd-2.1-generic#71-:openbsd.sparc-:-:sparc-:- (tnx MMM2)
0.96: sco_sv-3.2-2-:-:-:i386-:- (tnx DEH) 1.01: openbsd-2.1-katana#2-:openbsd.i386-:-:i386-:- (tnx CHR)
0.96: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4c-:sun4c- (tnx MBS) 1.01: openbsd-2.1-puma#0-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
0.91: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4m-:sun4m- (tnx IS) 1.01: openbsd-2.2-ele#2-:openbsd.i386-:-:i386-:- (tnx RC)
0.92: sunos-4.1.3_u1-2-:sparc-:sun4-:sun4c-:sun4c- (tnx ANR) 1.01: openbsd-2.2-generic#424-:openbsd.i386-:-:i386-:- (tnx ETT)
0.93: sunos-4.1.3_u1-4-:sparc-:sun4-:sun4m-:sun4m- (tnx J2B) 1.01: osf1-v2.0-240-:-:-:alpha-:- (tnx JF)
1.00: osf1-v3.2-148-:-:-:alpha-:- (tnx DL)
1.01: osf1-v3.2-148-:-:-:alpha-:- (tnx RSK)
1.01: osf1-v3.2-41-:-:-:alpha-:- (tnx MSD)
1.01: osf1-v3.2-mp-4.2-:-:-:alpha-:- (tnx MSD)
1.01: osf1-v4.0-386-:-:-:alpha-:- (tnx TEE)
1.01: osf1-v4.0-464-:-:-:alpha-:- (tnx AWB)
1.01: osf1-v4.0-564-:-:-:alpha-:- (tnx A2P)
1.01: osf1-v4.0-564.32-:-:-:alpha-:- (tnx TLF)
1.01: osf1-v4.0-878-:-:-:alpha-:- (tnx BJM)
1.01: sco_sv-3.2-2-:-:-:i386-:- (tnx PW)
1.01: sinix-l-5.41-d0005-:-:-:mx300i-:- (tnx IH)
1.01: sunos-4.1.1-1-:mc68020-:sun3-:sun3-:sun3- (tnx JWB)
1.01: sunos-4.1.1-1-:mc68020-:sun3-:sun3x-:sun3x- (tnx TT)
1.01: sunos-4.1.3-jl-2-:sparc-:sun4-:sun4c-:sun4c- (tnx T2K)
1.01: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4c-:sun4c- (tnx MBS)
1.01: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4m-:sun4m- (tnx RSK)
1.01: sunos-4.1.3_u1-10-:sparc-:sun4-:sun4m-:sun4m- (tnx aoki=???)
1.00: sunos-4.1.3_u1-4-:unknown-:sun4-:sun4m-:sun4m- (tnx J2B) 1.00: sunos-4.1.3_u1-4-:unknown-:sun4-:sun4m-:sun4m- (tnx J2B)
0.91: sunos-4.1.3_u1-9-:sparc-:sun4-:sun4m-:sun4m- (tnx TA) 1.01: sunos-4.1.3_u1-6-:sparc-:sun4-:sun4m-:sun4m- (tnx RD)
0.91: sunos-4.1.4-2-:sparc-:sun4-:sun4c-:sun4c- (tnx ST) 1.01: sunos-4.1.4-1-:unknown-:sun4-:sun4m-:sun4m- (tnx M3S)
0.95: sunos-4.1.4-2-:sparc-:sun4-:sun4m-:sun4m- (tnx TG) 1.01: sunos-4.1.4-2-:sparc-:sun4-:sun4m-:sun4m-
0.95: sunos-4.1c-4.1.3-:sparc-:sun4-:sun4-:s4000- (tnx DBK) 1.01: sunos-5.3-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx JDJ)
0.91: sunos-5.4-generic-:sparc-:sun4-:sun4m-:sun4m- 1.01: sunos-5.4-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx jimo=???)
0.96: sunos-5.4-generic_101945-10-:sparc-:sun4-:sun4m-:sun4m- (tnx W2K) 0.96: sunos-5.4-generic_101945-10-:sparc-:sun4-:sun4m-:sun4m- (tnx W2K)
0.93: sunos-5.4-generic_101945-23-:-:sun4-:sun4m-:sun4m- (tnx JP)
0.92: sunos-5.4-generic_101945-23-:sparc-:-:sun4c-:- (tnx BH)
1.00: sunos-5.4-generic_101945-34-:sparc-:sun4-:sun4m-:sun4m- (tnx ACB) 1.00: sunos-5.4-generic_101945-34-:sparc-:sun4-:sun4m-:sun4m- (tnx ACB)
0.92: sunos-5.4-generic_101945-36-:-:sun4-:sun4c-:sun4c- (tnx JP)
0.95: sunos-5.4-generic_101945-43-:-:sun4-:sun4m-:sun4m- (tnx JP)
0.90: sunos-5.4-generic_101946-07-:-:i86pc-:i86pc-:i86pc- (tnx CR)
0.96: sunos-5.4-generic_101946-35-:i386-:i86pc-:i86pc-:i86pc- (tnx CK) 0.96: sunos-5.4-generic_101946-35-:i386-:i86pc-:i86pc-:i86pc- (tnx CK)
0.96: sunos-5.5-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx M2G) 1.01: sunos-5.5-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx seong=???)
0.93: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CG) 1.01: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx SPM)
1.00: sunos-5.5-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx SG) 1.01: sunos-5.5-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM)
1.00: sunos-5.5-generic_103093-02-:sparc-:sun4-:sun4m-:sun4m- (tnx RF) 1.01: sunos-5.5-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx YC)
0.91: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4d-:sun4d- (tnx M3S) 1.01: sunos-5.5-generic_103093-02-:sparc-:sun4-:sun4m-:sun4m- (tnx RF)
0.96: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM) 0.96: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM)
0.91: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4u-:sun4u- (tnx olav=???) 1.01: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (tnx ERH)
0.92: sunos-5.5-generic_103093-05-:sparc-:sun4-:sun4m-:sun4m- (tnx KE) 1.01: sunos-5.5-generic_103093-10-:sparc-:sun4-:sun4d-:sun4d- (tnx KT)
0.93: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4d-:sun4d- (tnx KT) 1.01: sunos-5.5-generic_103094-05-:i386-:i86pc-:i86pc-:i86pc- (tnx M2G)
1.00: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (tnx TEE) 1.01: sunos-5.5.1-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx cro=???)
0.94: sunos-5.5-generic_103094-01-:i386-:i86pc-:i86pc-:i86pc- (tnx MD) 1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CG)
0.91: sunos-5.5-generic_103094-03-:i386-:i86pc-:i86pc-:i86pc- (tnx ding=???) 1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx MBS)
1.00: sunos-5.5.1-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx DML) 1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4u-:sun4u-
1.00: sunos-5.5.1-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx JMT)
1.00: sunos-5.5.1-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx TH)
0.96: sunos-5.5.1-generic_103640-02-:sparc-:sun4-:sun4m-:sun4m- (tnx SGC) 0.96: sunos-5.5.1-generic_103640-02-:sparc-:sun4-:sun4m-:sun4m- (tnx SGC)
0.95: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4m-:sun4m- (tnx MRG)
1.00: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4u-:sun4u- (tnx EG) 1.00: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4u-:sun4u- (tnx EG)
1.00: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4m-:sun4m- (tnx L2L) 1.00: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4m-:sun4m- (tnx L2L)
0.96: sunos-5.5.1-generic_patch-:i386-:i86pc-:i86pc-:i86pc- (tnx DK) 1.01: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4u-:sun4u- (tnx KY)
1.01: sunos-5.5.1-generic_103640-06-:sparc-:sun4-:sun4u-:sun4u- (tnx RA)
1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4c-:sun4c- (tnx RA)
1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4d-:sun4d- (tnx MS)
1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4m-:sun4m- (tnx S2P)
1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4u-:sun4u- (tnx CM)
1.01: sunos-5.5.1-generic_103640-12-:sparc-:sun4-:sun4m-:sun4m- (tnx IK)
1.01: sunos-5.5.1-generic_103640-18-:sparc-:sun4-:sun4u-:sun4u- (tnx PMH)
1.01: sunos-5.5.1-generic_103641-08-:i386-:i86pc-:i86pc-:i86pc- (tnx TL)
1.01: sunos-5.5.1-generic_103641-12-:i386-:i86pc-:i86pc-:i86pc- (tnx JS)
1.01: sunos-5.5.1-generic_105428-01-:sparc-:sun4-:sun4u-:sun4u- (tnx BCM)
0.96: sunos-5.5.1-generic_patch-:i386-:i86pc-:i86pc-:i86pc- (tnx D2K)
1.01: sunos-5.6-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx DS)
1.01: sunos-5.6-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx BDM)
1.01: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx RPS)
1.01: sunos-5.6-generic_105182-01-:i386-:i86pc-:i86pc-:i86pc- (tnx JFK)
1.01: sunos-5.6-generic_105182-04-:i386-:i86pc-:i86pc-:i86pc- (tnx YC)
0.96: ultrix-4.3-1-:pmax-:-:risc-:- (tnx YF) 0.96: ultrix-4.3-1-:pmax-:-:risc-:- (tnx YF)
1.01: ultrix-4.4-0-:-:-:risc-:- (tnx RSK)
1.01: unix_sv-4.2mp-2.1.2-:i386-:-:i386-:- (tnx J2W)

37
RFCHCSC
View file

@ -1,37 +0,0 @@
The Hash Convention For Mail System Status Codes (HCMSSC)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
RFC 1893 defines codes for mail delivery failures. For example,
code 5.1.1 means that the specified mailbox does not exist.
The qmail package sprays these codes all over the place, by adding a
code to the text of every error message, preceded by a hash mark and
surrounded by parentheses. It avoids using hash marks elsewhere.
2. Examples
Here is a typical HCMSSC SMTP error message:
421 load average too high, please come back later (#4.3.2)
Here is part of a typical HCMSSC bounce message:
<mail-loop@silverton.berkeley.edu>:
This is looping; it already has my Delivered-To line. (#5.7.1)
But qmail doesn't use HCMSSC when it repeats another MTA's error
message:
<foo@heaven.af.mil>:
127.3.4.5 does not like recipient.
Remote host said: 550 <foo>... User unknown (#5.1.1)
3. Security considerations
Don't take drastic action upon seeing "(#"; it might not be HCMSSC.

338
RFCLOOPS
View file

@ -1,338 +0,0 @@
Tools in the war on mail loops
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
An automailer means any program that receives a mail message and
automatically sends one or more mail messages. This term is meant to
include not only a mail-based server, such as a mailing list exploder
or a vacation program, but also an SMTP server, which receives a
message from the network and relays it to a local or remote user.
In a network full of automailers, any mistake can cause a mail loop.
Since some automailers generate several outputs in response to a
single input, a loop can produce an exponential explosion of mail.
All the automailers in the qmail package follow a general philosophy
designed to prevent mail loops and limit the damage from any loops
that do occur. These automailers have been repeatedly observed to
fail safe: they stop loops in the face of typical failures by other
hosts. This document explains the philosophy and describes the
automailers.
To some extent the philosophy here simply repeats and amplifies
standard practice as codified in RFC 974 and RFC 1123. Unfortunately,
the standards do not adequately control bounce loops, since they do
not recognize that postmasters want to see double bounces; they do
not adequately control relaying loops; and they do not prevent
cross-host forwarding loops.
Terminology: The mail message received by an automailer is called
input. The mail messages sent by an automailer are called outputs.
For simplicity, this document focuses on the case that the input has
just one envelope recipient.
REMINDER: This document describes the automailers in the qmail
package. Other packages include automailers that do not fit the
descriptions given here.
Beware that the war on mail loops can never be won: any method of
preventing mail loops can be subverted by other hosts. I welcome
further development of techniques that work well in practice.
2. Basics
The output from an automailer is always further down the following
list than the input.
0 hops, <sender> is neither <> nor <#@[]> normal messages
1 hop, <sender> is neither <> nor <#@[]>
2 hops, <sender> is neither <> nor <#@[]>
etc.
0 hops, <sender> is <> bounces
1 hop, <sender> is <>
2 hops, <sender> is <>
etc.
0 hops, <sender> is <#@[]> double bounces
1 hop, <sender> is <#@[]>
2 hops, <sender> is <#@[]>
etc.
Here sender means the envelope sender address. Hops means the number
of Received and Delivered-To fields in the header. See sections 3.3
and 3.4 for an explanation of <> and <#@[]>.
Consequently, no automailer ever generates an entirely new normal
message in response to a normal message. If the output is a normal
message, it always has more hops than the input.
When input and output are both normal messages, both bounces, or both
double bounces, the output header is essentially the same as the
input header. However, when an automailer moves from a normal message
to a bounce, or from a bounce to a double bounce, it generates an
entirely new header.
An automailer may refuse to operate if the input has too many hops.
The definition of too many hops depends on the automailer. This
practice is called hop counting. Note that some existing messages
legitimately take as many as 20 hops. One automailer uses a limit of
100 hops; this will be adequate for all messages in the foreseeable
future.
Hop counting is a weapon of last resort. It will, if correctly
implemented, prevent all infinite loops; however, even a finite loop
can do practically infinite damage, as illustrated in section 4.3.
3. Pre-delivery automailers
Conceptually: The input is a message that has not yet reached its
envelope recipient address. It is fed to a relay, which attempts to
deliver the message directly to, or at least closer to, that address;
if the relay fails permanently, the message is fed to a bouncer or a
double-bouncer. Relays, bouncers, and double-bouncers are examples of
pre-delivery automailers.
A pre-delivery automailer produces at most one output.
The basic weapon against pre-delivery mail loops is gravity. A normal
message always moves closer to its envelope recipient, according to a
notion of distance defined in section 3.1. If it bounces before
reaching the recipient, it turns into a bounce message, which always
moves closer to the original envelope sender. If that in turn
bounces, it turns into a double bounce, which always moves closer to
a local postmaster. (Triple bounces do not exist.)
3.1. Distance
The distance from a DNS domain D to a recipient U@R is defined as
follows, when R has an MX list: the minimum preference of D in the
MX list, or 100000 if D does not appear in the list.
When R has no MX records, the distance from R to U@R is defined as 0,
and the distance from any other domain to U@R is defined as 100000.
Exception: If R is an alias, i.e., if R has a CNAME record, the
distance from any domain to U@R is defined as 500000.
The distance from a host H to U@R is defined as the minimum distance
to U@R from any domain that touches H. (``D touches H'' means ``D has
an A record listing one of H's IP addresses.'')
Exception: If H does not accept mail from the network, its distance
to any recipient is defined as 999999.
3.2. Relays
A relay is a pre-delivery automailer that sends the output towards
the envelope recipient. What this means for intra-host relays is not
discussed here. What this means for cross-host relays is the
following: if the relay is at host H, and it sends its output to host
T, then the distance from T to the output envelope recipient is
always smaller than the distance from H to the input envelope
recipient.
The following facts guarantee that certain cross-host relay behavior
is safe. For proofs of these facts, see Appendix A.
Fact 1: If R is an alias for X, X is not an alias, D touches T,
and T accepts mail from the network, then the distance from T to
U@X is smaller than the distance from H to U@R.
Fact 2: If R is not an alias, R has no MX records, H is not
touched by R, T is touched by R, and T accepts mail from the
network, then T is closer to U@R than H is.
Fact 3: If R is not an alias, R has an MX record with domain X and
preference p, H is not touched by any of the domains in the MX
list for R with preference <= p, T is touched by X, and T accepts
mail from the network, then T is closer to U@R than H is.
Also, a host that does not accept mail from the network can relay
messages to a nearby hub.
A relay adds a new Received header field to the top of the output.
Other than this, the output header, body, and envelope are exactly
the same as the input header, body, and envelope. Exception: If the
input envelope recipient is U@R, R is an alias for X, and X is not
an alias, the output envelope recipient is U@X.
3.3. Bouncers
A bouncer is a pre-delivery automailer that lets the envelope sender
know what happened to a message. Most bouncers send failure notices.
Some bouncers, such as vacation servers and echo servers, send
success notices.
In a bouncer's output, the envelope sender is <>, and the envelope
recipient is the input envelope sender. A bouncer refuses to operate
if the input envelope sender is <> or <#@[]>.
Some mailers on the Internet do not understand the <> convention. In
fact, some mailers will rewrite <> as <@host>. So any message with an
envelope recipient of <> or <@host> is discarded upon local delivery.
Unlike a relay, a bouncer produces output with a new header, not
simply a copy of the input header. For example:
(envelope) from <> to <djb@silverton.berkeley.edu>
Date: 2 Jan 1996 03:38:25 GMT
From: DELIVERY NOTICE SYSTEM <MAILER-DAEMON@heaven.af.mil>
To: djb@silverton.berkeley.edu
Subject: failure notice
However, the body of the bounce indicates the relevant input envelope
recipient, as well as the Message-ID of the input, if the input had a
Message-ID. The body of a failure notice includes a copy of the
entire input message.
3.4. Double-bouncers
A double-bouncer is a pre-delivery automailer that informs a local
postmaster of permanent failures to deliver bounce messages. Such
failures are generally caused by poorly configured hosts that produce
normal messages with faulty envelope sender addresses.
A double-bouncer refuses to operate unless the input envelope sender
is <>. The output envelope sender from a double-bouncer is <#@[]>;
note that <#@[]> cannot be used as an SMTP envelope sender under
RFC 821. The output envelope recipient is predetermined.
Note that double bounces are not suggested by RFC 1123. However,
faulty envelope sender addresses are usually configuration errors
that can and should be fixed. Some postmasters, faced with mail
software that throws away double bounces, resort to keeping copies of
all bounces; but single bounces are rarely the postmaster's problem.
4. Post-delivery automailers
Conceptually: The input is a message that has reached its envelope
recipient address. It is fed to a post-delivery automailer at that
address.
The basic weapon against post-delivery loops is a new header field,
Delivered-To, tracing all the forwarders and mailing lists that a
message has been through. This field has the side benefit of making
it much easier for a user (or for a postmaster seeing a bounce) to
figure out the path that the message took. Delivered-To is similar to
RFC 1327's DL-Expansion-History, but (1) it omits the time stamp,
removing any need for parsing, and (2) it has a much better name.
4.1. Exploders and repliers
There are two basic types of post-delivery automailers: exploders,
where the output envelope recipients are predetermined; and repliers,
where there is just one output, with envelope recipient determined
from the input.
Repliers normally determine the output envelope recipient as either
the input Reply-To header field, if it exists; or else the input
From header field, if it exists; or else the envelope sender. A
replier never produces an output to <> or <#@[]>.
Exploders are classified into mailing lists, where the output
envelope senders are predetermined, and forwarders, where every
output has envelope sender equal to the original envelope sender.
Exception: if the input envelope sender is <> or <#@[]>, then the
output envelope senders are equal to the input envelope sender, even
for a mailing list.
Note that, if the envelope sender of a mailing list with M bad
addresses is another exploder with E bad addresses, the local
postmaster will receive EM double bounces for each message to the
mailing list.
4.2. Delivered-To
Every post-delivery automailer adds a new Delivered-To header field
to the top of each output.
The contents of the Delivered-To field are typically the address of
the automailer, i.e., the input envelope recipient, conventionally
without any quoting. The contents of the Delivered-To field are in
any case entirely predetermined. The automailer checks if exactly the
same Delivered-To field already appears in the header; if so, it
refuses to operate.
A post-delivery automailer preserves existing Delivered-To and
Received fields. In fact, a post-delivery automailer generally
preserves all header fields. The exceptions are limited to known
fields that are not used for loop detection and that must be removed
for correct operation. For example, a replier generally changes the
body of a message and thus should not preserve the SVR4
Content-Length field.
4.3. An example
Aliases and mailing lists are highly dangerous, because they can
generate several outputs for each input.
Here is an extreme example. A user has three accounts, and wants any
message to any of the accounts to be delivered to all three. So he
forwards luser@host1 to luser@host2 and luser@host3, forwards
luser@host2 to luser@host1 and luser@host3, and forwards luser@host3
to luser@host1 and luser@host2.
Without Delivered-To, someone who sends a message to luser@host1 will
receive a practically infinite series of bounces. For example, with a
hop count limit of 50, the sender will receive 1125899906842624
bounces.
If all the hosts, or two out of the three, support Delivered-To, the
message will bounce just a few times. If just one of the hosts
supports Delivered-To, it will be the unfortunate victim of a loop
between the other two hosts---although the total number of bounces
will drop from practically infinite down to a few hundred, with
typical hop count limits.
Appendix A. Proofs of correctness for MX handling
Section 3.2 states three facts about the notion of distance defined
in section 3.1. Here are mathematical proofs of those facts.
Symbols: D, E, R, and X are domains; H and T are hosts; p and q are
nonnegative integers. {} is the empty set.
Hypotheses: M(R), the ``MX list for R,'' is a set of pairs (p,D)
where p <= 65535. There is a set A of domains, called ``aliases.''
There is a relation D->H, called ``D touches H.'' There is a set N of
hosts, called ``hosts that accept mail from the network.''
Definitions: m(D,R) = min { p: p = 100000 or (p,D) in M(R) } when
M(R) is nonempty. When M(R) is empty, m(D,R) is 0 if D = R, 100000
otherwise. f(D,R) is defined as 500000 if R is in A, m(D,R)
otherwise; this is the ``distance from D to U@R,'' for any U. g(H,R)
is defined as min { f(D,R): D->H } if H is in N, 999999 otherwise;
this is the ``distance from H to U@R,'' for any U.
Fact 1 (generalized): If R is in A, X is not in A, D->T, and T is in
N, then g(T,X) < g(H,R). Proof: R is in A, so f(E,R) = 500000 for any
E; thus g(H,R) >= 500000. X is not in A, so f(D,X) = m(D,X) <=
100000; hence g(T,X) <= f(D,X) <= 100000 < g(H,R).
Fact 2: If R is not in A, M(R) = {}, R->T, T is in N, and not R->H,
then g(T,R) < g(H,R). Proof: f(R,R) = m(R,R) = 0 since R is not in A
and M(R) = {}. T is in N so g(T,R) <= f(R,R) = 0 so g(T,R) = 0.
Suppose that g(H,R) <= g(T,R). Then g(H,R) = 0, so f(D,R) = 0 for
some D with D->H, so m(D,R) = 0. But then D = R by definition of m,
so R->H. Contradiction. Thus g(T,R) < g(H,R).
Fact 3: If R is not in A, (p,X) is in M(R), X->T, T is in N, and
(q,D) is not in M(R) whenever D->H and q <= p, then g(T,R) < g(H,R).
Proof: First m(X,R) <= p. R is not in A, so f(X,R) = m(X,R). T is in
N, so g(T,R) <= f(X,R). Thus g(T,R) <= p. Suppose that g(H,R) <= p.
Then f(D,R) <= p for some D with D->H, so m(D,R) <= p. But then
(m(D,R),D) is in M(R). Contradiction. Thus g(T,R) <= p < g(H,R).

122
RFCMXPS
View file

@ -1,122 +0,0 @@
The Mail Exchanger Protocol Switch (MXPS)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
Mail messages today are transferred through the Simple Mail Transfer
Protocol (SMTP). One can imagine other protocols that achieve the
same results as SMTP but that, for example, use the network more
efficiently.
The Mail Exchanger Protocol Switch (MXPS) lets other protocols
compete with SMTP. A receiver can announce its support for another
protocol while operating properly with MXPS-ignorant senders. A
sender can check for support, with no overhead, while operating
properly with MXPS-ignorant receivers.
All receivers must support SMTP, i.e., must be able to receive
messages via SMTP. Similarly, all senders must be able to send
messages via SMTP.
2. The protocol switch
MXPS abuses the preference field of MX records. A protocol is
assigned to each possible preference.
SMTP is assigned to preferences 0 through 10000.
The initial MXPS experiment will involve preferences between 12800
and 13055 inclusive. These preferences are sliced into 16 portions:
12800, 12816, 12832, 12848, 12864, ..., 13040: slice #0 (SMTP)
12801, 12817, 12833, 12849, 12865, ..., 13041: slice #1 (QMTP)
12802, 12818, 12834, 12850, 12866, ..., 13042: slice #2
...
12815, 12831, 12847, 12863, 12879, ..., 13055: slice #15
Preferences in slice #0 are assigned SMTP. Preferences in slice #1
are assigned the Quick Mail Transfer Protocol (QMTP). Preferences in
the remaining slices may be assigned protocols in the future.
A receiver must support the protocol assigned to its preference. More
precisely, if an MX record points to domain D, and the MX preference
is assigned protocol P, then every host listed as an A record for D
must support protocol P.
When a sender, following the procedure outlined in RFC 974 (and
modified by RFC 1123), attempts to deliver a mail message as
specified by that MX record, it may use protocol P instead of SMTP.
If it does not support protocol P, it may treat the attempt as a
temporary failure and go on to the next MX record. However, the
sender must not skip every MX record.
MX records must never use unassigned preferences. A sender may treat
an unassigned preference as referring to SMTP.
Example:
A.EXAMPLE.ORG IN MX 12801 A.EXAMPLE.ORG
B.EXAMPLE.ORG IN MX 12801 A.EXAMPLE.ORG
IN MX 12816 C.EXAMPLE.ORG
A sender with a message for A.EXAMPLE.ORG will try A.EXAMPLE.ORG by
QMTP. If it does not support QMTP, it will try SMTP instead. Note
that A.EXAMPLE.ORG must support both QMTP and SMTP.
A sender with a message for B.EXAMPLE.ORG will try A.EXAMPLE.ORG by
QMTP, then C.EXAMPLE.ORG by SMTP. If it does not support QMTP, it may
try SMTP instead of QMTP, or it may skip A.EXAMPLE.ORG.
Some of the above requirements might be violated if current
MXPS-ignorant domains use any preferences above 10000. Mail could be
unnecessarily rejected if any existing MXPS-ignorant domains have a
best-preference MX above 10000. I do not know any examples of such
domains.
3. Protocol requirements
MXPS operates purely at the link level. It does not change the
fundamental nature of Internet mail.
The function of a mail transfer protocol is to transmit a message, as
described below, together with an envelope sender address and one or
more envelope recipient addresses.
A recipient address is a sequence of characters---i.e., nonnegative
integers---including an ASCII @ (64). It is parsed as box@dom, where
dom does not contain an @. The interpretation of box is up to the
hosts listed as MX records for dom. A sender address may contain an
@, in which case it is also of the form box@dom; or it may be a
special address, such as the empty string.
A mail message is structured as a sequence of lines. A line is a
sequence of characters. Every mail transfer protocol must be able to
transmit all sufficiently short boring mail messages. A boring mail
message is one where (1) no line has more than 80 characters and (2)
each character is either 9 or between 32 and 127 inclusive. Note that
RFC 1341 defines a mechanism for encoding a message with characters
between 0 and 255 inclusive as a boring mail message of similar
length.
The receiver must indicate, for each recipient address, either
acceptance, permanent rejection, or temporary rejection of the
message. Acceptance means that the receiver has taken responsibility,
in the sense of RFC 1123, section 5.3.3, for delivering the message
to that recipient. Rejection means that the receiver will not deliver
the message to that recipient.
Mail transfer protocols may vary in many details, such as line
encodings, the means of expressing acceptance or rejection, the
maximum number of allowable recipients per envelope, the encoding of
envelope addresses, the nature of optional protocol extensions, etc.
4. Security considerations
MXPS does not change the following facts: An attacker who can subvert
the Domain Name System can steal or forge mail. An attacker who can
subvert TCP/IP can also steal or forge mail.

View file

@ -1,88 +0,0 @@
Netstrings
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
A netstring is a self-delimiting encoding of a string. Netstrings are
very easy to generate and to parse. Any string may be encoded as a
netstring; there are no restrictions on length or on allowed bytes.
Another virtue of a netstring is that it declares the string size up
front. Thus an application can check in advance whether it has enough
space to store the entire string.
Netstrings may be used as a basic building block for reliable network
protocols. Most high-level protocols, in effect, transmit a sequence
of strings; those strings may be encoded as netstrings and then
concatenated into a sequence of characters, which in turn may be
transmitted over a reliable stream protocol such as TCP.
Note that netstrings can be used recursively. The result of encoding
a sequence of strings is a single string. A series of those encoded
strings may in turn be encoded into a single string. And so on.
In this document, a string of 8-bit bytes may be written in two
different forms: as a series of hexadecimal numbers between angle
brackets, or as a sequence of ASCII characters between double quotes.
For example, <68 65 6c 6c 6f 20 77 6f 72 6c 64 21> is a string of
length 12; it is the same as the string "hello world!".
Although this document restricts attention to strings of 8-bit bytes,
netstrings could be used with any 6-bit-or-larger character set.
2. Definition
Any string of 8-bit bytes may be encoded as [len]":"[string]",".
Here [string] is the string and [len] is a nonempty sequence of ASCII
digits giving the length of [string] in decimal. The ASCII digits are
<30> for 0, <31> for 1, and so on up through <39> for 9. Extra zeros
at the front of [len] are prohibited: [len] begins with <30> exactly
when [string] is empty.
For example, the string "hello world!" is encoded as <31 32 3a 68
65 6c 6c 6f 20 77 6f 72 6c 64 21 2c>, i.e., "12:hello world!,". The
empty string is encoded as "0:,".
[len]":"[string]"," is called a netstring. [string] is called the
interpretation of the netstring.
3. Sample code
The following C code starts with a buffer buf of length len and
prints it as a netstring.
if (printf("%lu:",len) < 0) barf();
if (fwrite(buf,1,len,stdout) < len) barf();
if (putchar(',') < 0) barf();
The following C code reads a netstring and decodes it into a
dynamically allocated buffer buf of length len.
if (scanf("%9lu",&len) < 1) barf(); /* >999999999 bytes is bad */
if (getchar() != ':') barf();
buf = malloc(len + 1); /* malloc(0) is not portable */
if (!buf) barf();
if (fread(buf,1,len,stdin) < len) barf();
if (getchar() != ',') barf();
Both of these code fragments assume that the local character set is
ASCII, and that the relevant stdio streams are in binary mode.
4. Security considerations
The famous Finger security hole may be blamed on Finger's use of the
CRLF encoding. In that encoding, each string is simply terminated by
CRLF. This encoding has several problems. Most importantly, it does
not declare the string size in advance. This means that a correct
CRLF parser must be prepared to ask for more and more memory as it is
reading the string. In the case of Finger, a lazy implementor found
this to be too much trouble; instead he simply declared a fixed-size
buffer and used C's gets() function. The rest is history.
In contrast, as the above sample code shows, it is very easy to
handle netstrings without risking buffer overflow. Thus widespread
use of netstrings may improve network security.

View file

@ -1,89 +0,0 @@
Notice-Requested-Upon-Delivery-To (NRUDT)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The UNIX sendmail program has for many years supported a
Return-Receipt-To (RRT) header field that requests a notice of
successful final delivery.
Notice-Requested-Upon-Delivery-To (NRUDT) has the same basic
function. The big difference is that RRT lists the sender's address,
while NRUDT lists the recipient's address.
This change is critical. RRT works poorly for messages to multiple
recipients, because it requests a notice from every recipient. RRT in
a message to a large mailing list produces a giant, usually
unintentional, flood of mail. This problem is so severe that RRT has
been disabled in recent versions of sendmail.
NRUDT is designed to be adopted immediately, with minimal disruption,
as a solution to the problems of RRT. Note that NRUDT is merely a
request for notification; unlike the link-level Delivery Status
Notification SMTP extension, NRUDT does not provide a guarantee of
notification.
NRUDT is supported by the qreceipt program in the qmail package.
2. Syntax
NRUDT is a field in the header of an RFC 822 mail message. It has the
following syntax:
"Notice-Requested-Upon-Delivery-To" ":" 1#address
See RFC 822 for more information about header fields and addresses.
NRUDT requests that, upon final delivery of the message to any of the
specified addresses, the sender be notified. Note that more than one
address can appear in a single NRUDT header field. Multiple NRUDT
header fields should not appear in a single message.
3. Response
Upon successful final delivery of a message to any address listed in
an NRUDT header field, the host performing delivery may, if desired,
generate a success notice.
The success notice is similar to a failure notice as described in RFC
1123. Its envelope sender is <>. Its envelope recipient is the
envelope sender of the original message; however, if the envelope
sender of the original message is <>, a success notice is not sent.
The body of the success notice does not contain a copy of the
original message, but it does indicate the Message-ID of the original
message, as well as the relevant recipient address.
A success notice may indicate delivery to several addresses. For
example, given the following message:
(envelope) from djb@silverton.berkeley.edu
(envelope) to god@heaven.af.mil, angels@heaven.af.mil
Date: 1 Jan 1996 21:43:34 GMT
From: "D. J. Bernstein" <djb@silverton.berkeley.edu>
Message-Id: <19960101214334.8529.qmail@silverton.berkeley.edu>
Notice-Requested-Upon-Delivery-To: God <god@heaven.af.mil>,
angels@heaven.af.mil (You Know Who You Are)
...
a host may respond as follows:
(envelope) from <> to djb@silverton.berkeley.edu
Date: 1 Jan 1996 21:43:37 GMT
From: DELIVERY NOTICE SYSTEM <MAILER-DAEMON@heaven.af.mil>
To: djb@silverton.berkeley.edu
Subject: success notice
I delivered <19960101214334.8529.qmail@silverton.berkeley.edu>
to the following local mailboxes:
god@heaven.af.mil
angels@heaven.af.mil
Thanks for asking.
However, a success notice is never merged with a failure notice.

229
RFCQMTP
View file

@ -1,229 +0,0 @@
Quick Mail Transfer Protocol (QMTP)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The Quick Mail Transfer Protocol (QMTP) is a replacement for the
Simple Mail Transfer Protocol (SMTP). QMTP eliminates any need for
end-of-line scanning between hosts with the same end-of-line
convention. It features automatic pipelining and chunking, 8-bit
transmission, prior declaration of the message size, and efficient
batching. It is designed to be very easy to implement.
QMTP is supported by the qmail-qmtpd and maildir2qmtp programs in the
qmail package.
In this document, a string of 8-bit bytes may be written in two
different forms: as a series of hexadecimal numbers between angle
brackets, or as a sequence of ASCII characters between double quotes.
For example, <68 65 6c 6c 6f 20 77 6f 72 6c 64 21> is a string of
length 12; it is the same as the string "hello world!". Note that
these notations are part of this document, not part of the protocol.
2. Protocol
A QMTP client connects to a QMTP server, as discussed in section 7,
over a reliable stream protocol allowing transmission of 8-bit bytes.
Protocol outline: the client sends one or more packages; after each
package, the server sends back some responses.
The client begins by sending a package. A package contains a mail
message, an envelope sender address, and one or more envelope
recipient addresses. See section 4 for the format of a package.
When the server sees the end of the package, it sends back a series
of responses, one response for each envelope recipient address, in
the same order as given by the client. The server is not permitted to
change the order under any circumstances, even if two addresses are
the same. See section 5 for the format of a response.
The server is not permitted to send any portion of its responses to a
package until the client has sent the final byte of the package. The
client is permitted to close the connection before sending the final
byte of the package; in this case, the server must throw away the
package without attempting to deliver the message. However, the
server must not throw away previously accepted messages.
The client does NOT need to wait for a server response before sending
another package. The server must NOT throw away incoming data when it
sends a response. It is the client's responsibility to avoid
deadlock: if it sends a package before receiving all expected server
responses, it must continuously watch for those responses. The server
is permitted to delay its responses if further data has already shown
up from the client; while it is delaying responses, it must not pause
to wait for further data for the client.
The server is permitted to close the connection at any time, although
high-quality servers will try to avoid doing so. Any response not
received by the client indicates a temporary failure.
A QMTP session should take at most 1 hour. Both sides are expected
to close the connection after this time.
3. Messages
In this document, an ``8-bit mail message'' means a sequence of
lines. Each line is a string of zero or more 8-bit bytes.
A message is called ``safe'' if none of its bytes are <0a>.
Implementation note: Here is the intended interpretation of text
files as messages under some current operating systems. Under DOS, a
message is stored on disk as
first line, <0d 0a>, second line, <0d 0a> ... <0d 0a>, last line.
Under UNIX, a message is stored on disk as
first line, <0a>, second line, <0a> ... <0a>, last line.
Notice that both of these encodings are reversible for safe messages.
In practice, it is very common for the last line to be empty. Many
existing utilities refer to the last line as a ``partial line'' and
ignore it whether or not it is empty.
4. Packages
A package is the concatenation of three strings:
first, an encoded 8-bit mail message;
second, an encoded envelope sender address;
third, an encoded series of encoded envelope recipient addresses.
Each envelope address is a string of 8-bit bytes. The interpretation
of addresses depends on the environment in which QMTP is used and is
outside the scope of this document. Each address is encoded as a
netstring, as discussed in section 6. The series of encoded recipient
addresses is in turn encoded as a netstring.
A message is encoded as a string of 8-bit bytes in one of two ways:
Encoding #1 is <0d>, the first line, <0d 0a>, the second line,
<0d 0a>, the third line, ..., <0d 0a>, the last line.
Encoding #2 is <0a>, the first line, <0a>, the second line, <0a>,
the third line, ..., <0a>, the last line.
This string of 8-bit bytes is in turn encoded as a netstring, as
discussed in section 6.
Every server must be prepared to handle encoding #1 and encoding #2.
A server must not reject a message merely because of its encoding.
Implementation note: The intent of encoding #1 and encoding #2 is to
allow very straightforward handling of text files under DOS and UNIX
respectively. The programmer can print <0d> or <0a> and then simply
copy the file.
5. Responses
Each response is a nonempty string of 8-bit bytes, encoded as a
netstring. The first byte of the string is one of the following:
"K" The message has been accepted for delivery to this envelope
recipient. This is morally equivalent to the 250 response to
DATA in SMTP; it is subject to the reliability requirements
of RFC 1123, section 5.3.3.
"Z" Temporary failure. The client should try again later.
"D" Permanent failure.
The remaining bytes are a description of what happened. It is
expected that the description, when interpreted as UTF-2 characters,
(1) will be human-readable, (2) will not repeat the envelope
recipient address, and (3) will not include formatting characters
other than <20>. However, these expectations are not requirements,
and the client should be ready for arbitrary bytes from the server.
Descriptions beginning with <20> are reserved for future extensions.
In descriptions not beginning with <20>, the character "#" must not
appear except in HCMSSC codes.
A server must NOT accept a safe message unless it can store the
message without corruption. More precisely: if the encoded message
sent by the client matches the encoding of some safe message M, then
acceptance means that the server is accepting responsibility to
deliver M to the envelope recipient. (There is at most one
possibility for M, since encodings are reversible on safe messages.)
Deletion of nulls is NOT permissible; a server that deletes nulls
must reject any message containing nulls. Folding of long lines and
high-bit stripping are also NOT permissible.
Servers are permitted to change unsafe messages.
6. Netstrings
Any string of 8-bit bytes may be encoded as [len]":"[string]",".
Here [string] is the string and [len] is a nonempty sequence of ASCII
digits giving the length of [string] in decimal. The ASCII digits are
<30> for 0, <31> for 1, and so on up through <39> for 9. Extra zeros
at the front of [len] are prohibited: [len] begins with <30> exactly
when [string] is empty.
For example, the string "hello world!" is encoded as <31 32 3a 68
65 6c 6c 6f 20 77 6f 72 6c 64 21 2c>, i.e., "12:hello world!,". The
empty string is encoded as "0:,".
[len]":"[string]"," is called a netstring. [string] is called the
interpretation of the netstring.
7. Encapsulation
QMTP may be used on top of TCP. A QMTP-over-TCP server listens for
TCP connections on port 209.
8. Examples
A client opens a connection and sends the concatenation of the
following strings:
"246:" <0a>
"Received: (qmail-queue invoked by uid 0);"
" 29 Jul 1996 09:36:40 -0000" <0a>
"Date: 29 Jul 1996 11:35:35 -0000" <0a>
"Message-ID: <19960729113535.375.qmail@heaven.af.mil>" <0a>
"From: God@heaven.af.mil" <0a>
"To: djb@silverton.berkeley.edu (D. J. Bernstein)" <0a>
<0a>
"This is a test." <0a> ","
"24:" "God-DSN-37@heaven.af.mil" ","
"30:" "26:djb@silverton.berkeley.edu," ","
"356:" <0d>
"From: MAILER-DAEMON@heaven.af.mil" <0d 0a>
"To:" <0d 0a>
" Hate." <22> "The Quoting" <22>
"@SILVERTON.berkeley.edu," <0d 0a>
" " <22> "\\Backslashes!" <22>
"@silverton.BERKELEY.edu" <0d 0a>
<0d 0a>
"The recipient addresses here could"
" have been encoded in SMTP as" <0d 0a>
"" <0d 0a>
" RCPT TO:<Hate.The\ Quoting@silverton.berkeley.EDU>" <0d 0a>
" RCPT TO:<\\Backslashes!@silverton.berkeley.edu>" <0d 0a>
<0d 0a>
"This ends with a partial last line, right here" ","
"0:" ","
"83:" "39:Hate.The Quoting@silverton.berkeley.edu,"
"36:\Backslashes!@silverton.berkeley.EDU," ","
The server sends the following response, indicating acceptance:
"21:Kok 838640135 qp 1390,"
"21:Kok 838640135 qp 1391,"
"21:Kok 838640135 qp 1391,"
The client closes the connection.

155
RFCQSBMF
View file

@ -1,155 +0,0 @@
The qmail-send Bounce Message Format (QSBMF)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
When a message transport agent (MTA) finds itself permanently unable
to deliver a mail message, it generates a new message, generally
known as a bounce message, back to the envelope sender.
Bounce messages produced by the qmail-send program display the list
of failed recipient addresses, an explanation for each address, and a
copy of the original message, in a format that is easy for both
humans and programs to read. For example:
Date: 17 Mar 1996 03:54:40 -0000
From: MAILER-DAEMON@silverton.berkeley.edu
To: djb@silverton.berkeley.edu
Subject: failure notice
Hi. This is the qmail-send program at silverton.berkeley.edu.
I'm afraid I wasn't able to deliver your message to the
following addresses. This is a permanent error; I've given up.
Sorry it didn't work out.
<god@heaven.af.mil>:
Sorry, I couldn't find any host by that name.
--- Below this line is a copy of the message.
Return-Path: <djb@silverton.berkeley.edu>
Received: (qmail 317 invoked by uid 7); 17 Mar 1996 03:54:38 -0000
Date: 17 Mar 1996 03:54:38 -0000
Message-ID: <19960317035438.316.qmail@silverton.berkeley.edu>
From: djb@silverton.berkeley.edu (D. J. Bernstein)
To: god@heaven.af.mil
Subject: are you there?
Just checking.
This document defines qmail-send's format for bounce messages.
In this document, a string of 8-bit bytes may be written in two
different forms: as a series of hexadecimal numbers between angle
brackets, or as a sequence of ASCII characters between double quotes.
For example, <68 65 6c 6c 6f 20 77 6f 72 6c 64 21> is a string of
length 12; it is the same as the string "hello world!".
2. Format
A bounce message may be recognized as QSBMF as follows: its body
begins with the characters "Hi. This is the" exactly as shown.
The body of the message has four pieces: an introductory paragraph,
zero or more recipient paragraphs, a break paragraph, and the
original message.
Each paragraph is a series of non-blank lines followed by a single
blank line. The break paragraph begins with the character "-". All
other paragraphs begin with characters other than "-". The break
paragraph is human-readable but provides no interesting information.
The introductory paragraph is human-readable. It gives the name and
human-comprehensible location of the MTA, but parsers should not
attempt to use this information.
The only type of recipient paragraph described here is a failure
paragraph, which begins with the character "<". Paragraphs beginning
with other characters are reserved for future extensions.
The first line of a failure paragraph ends with the characters ">:".
Everything between the leading "<" and the trailing ">:" is an
(unquoted) Internet mail address.
A failure paragraph asserts that the MTA was permanently unable to
deliver the message to the mail address shown on the first line; the
MTA will not attempt further deliveries to that address. The
remaining lines of the paragraph give a human-readable description of
the reason for failure. Descriptions beginning with <20>, and
descriptions containing "#", are reserved for future extensions.
The envelope sender might not have sent his message to the address
shown. There are two reasons for this. First, the MTA may freely
replace unprintable characters with "_". Second, the original
recipient address may have been an alias for the address shown.
The original message is an exact copy of the message received by the
MTA, including both header and body, preceded by a Return-Path field
showing the envelope sender.
3. Comparison with 1892/1894
RFC 1892 and RFC 1894 together describe a format for delivery status
notifications. I have decided not to use that format, because I
believe that its complexity will prevent wide implementation and
increase the burden on people who manage mailing lists.
QSBMF is dedicated to failure reports, whereas RFC 1894 allows
success reports and deferral reports. Although it would be possible
to add deferral paragraphs and success paragraphs to QSBMF, it would
be even easier to design separate formats for such notices. I have
trouble reading mixed failure/deferral reports.
QSBMF always returns the entire original message. RFC 1892 allows
the MTA to return nothing or to return just the headers; it states
``Return of content may be wasteful of network bandwidth.'' However,
failure notices are very rare, so the overall loss of bandwidth in
this case is insignificant. A much more important issue is storage
space: someone who manages a big mailing list does not want to have
to store several copies of each message in the form of bounces. The
best solution is to have each bounce automatically fed through a
program that stores only the critical information. I expect such
programs to spring up quickly for QSBMF.
RFC 1894 provides language-independent error messages, as described
by RFC 1893. One can achieve the same results more easily by adding
structure to the human-readable failure descriptions, for example
with HCMSSC.
RFC 1894 is able to communicate an ``envelope ID'' and the original
envelope recipient address specified by the sender. Unfortunately,
this information will almost never be available, since it requires
support by every intermediate MTA. All of the applications of this
information can be handled reliably, right now, with VERPs; this
requires support from the sender's MTA but not from other hosts.
RFC 1894 includes several pieces of information that might be of
human interest but can be seen just as easily from Received lines:
the name of the MTA where delivery failed, the name of the previous
MTA, timestamps, etc.
All of these RFC 1894 features have a cost: complexity. A program
cannot parse an 1894 report without parsing RFC 822 header fields
and understanding quite a bit of MIME. This will limit the
availability of parsing software. In the meantime, such reports are
annoying to mailing list maintainers, since they are full of
uninteresting information and are difficult to parse visually.
4. Security considerations
Bounce messages may be forged. Never remove someone from a mailing
list without sending him a message stating that you are doing so,
even if the reason for removal is a series of apparent bounce
messages from his address.
If you send a message along a secret path, you should change the
envelope sender address of the message to yourself, so that a bounce
will not reveal anything to the original sender. In other words: for
secret forwarding, use a mailing list, not a forwarder.
See RFC 1894 for further discussion of these points.

88
RFCVERP
View file

@ -1,88 +0,0 @@
Variable Envelope Return Paths
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The fundamental problem in managing a large mailing list is matching
bounce messages to subscription addresses.
Often a bounce message refers to a failing address that does not
appear on the mailing list. One of the mailing list subscribers is
forwarding messages to that address. Which subscriber? As the list
grows, this question becomes more and more difficult to answer.
Sometimes a bounce message doesn't identify the address that failed.
On occasion it doesn't even include a copy of the original message.
See RFC 1211 for an extensive collection of horror stories.
In theory, one could solve this problem with the DSN option and DSN
format described in RFC 1891, RFC 1892, and RFC 1894. Unfortunately,
the DSN option is useless unless it is supported by every
intermediate MTA. The complexity of RFC 1891 means that it will be
many years, perhaps infinitely many, before DSNs are universally
supported. Furthermore, the complexity of RFC 1894 means that parsing
the subscriber address is difficult even on the occasions that the
address is available.
Variable envelope return paths (VERPs) completely eliminate this
problem _right now_. They automatically and reliably identify the
subscription address relevant to each bounce message. They provide
the address in a form that is trivial for automated bounce handlers
to parse. They require support from the local mailer, but they do not
require support from any other hosts.
2. Variable envelope return paths
Here is how VERPs work: each recipient of the message sees a
different envelope sender address. When a message to the
djb-sos@silverton.berkeley.edu mailing list is sent to
God@heaven.af.mil, for example, it has the following envelope sender:
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu
If the message bounces, the bounce message will be sent back to
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu.
If God is forwarding His mail, the bounce message will still go to
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu. No matter how
uninformative the bounce message is, it will display God's
subscription address in its envelope.
Another benefit of VERPs is that God Himself can see what address He
used to subscribe.
Making VERPs work requires two pieces of local software support.
First: it must be easy to modify the outgoing sender address
separately for each envelope recipient. For example, with one mailer,
qmail, a user can simply touch ~/.qmail-list-owner and
~/.qmail-list-owner-default to apply VERPs to user-list.
Second, and more important: it must be easy to identify a collection
of addresses, such as djb-sos-owner-*, and send all mail for those
addresses to one place, while preserving the * information. Under
qmail, all user-list-owner-* mail will be sent to the user once he
touches ~/.qmail-list-owner-default. Sending the mail through an
automated bounce-handling program is just as easy.
With older mailers, applying VERPs would require setting up a new
user-list-owner-recipient alias for each new recipient. This
inconvenience has prevented VERPs from being widely exploited, even
though the idea is not new.
3. Per-message VERPs
VERPs are not restricted to distinguishing mailing list subscribers;
they can also be used to distinguish messages.
For example, a user can send one message with an envelope sender
address of user-dsn-1, the next message with user-dsn-2, and so on.
As long as the local mailer gives all user-dsn-* back to that user,
he can reliably match up incoming bounces with outgoing messages.
Per-message VERPs can be combined with per-recipient VERPs. Every
application of RFC 1891's ORCPT and ENVID can be handled with
VERPs---easily, reliably, and right now.

View file

@ -5,6 +5,11 @@ discovered; sendmail's design means that any minor bug in 46000 lines of
code is a major security risk. Other popular mailers, such as Smail, and code is a major security risk. Other popular mailers, such as Smail, and
even mailing-list managers, such as Majordomo, seem nearly as bad. even mailing-list managers, such as Majordomo, seem nearly as bad.
Note added in 1998: I wrote the above paragraph in December 1995, when
the latest version of sendmail was 8.6.12 (with 41000 lines of code).
Fourteen security holes were discovered from sendmail 8.6.12 through
8.8.5. See http://pobox.com/~djb/docs/maildisasters/sendmail.html.
I started working on qmail because I was sick of this cycle of doom. I started working on qmail because I was sick of this cycle of doom.
Here are some of the things I did to make sure that qmail will never let Here are some of the things I did to make sure that qmail will never let
an intruder into your machine. an intruder into your machine.
@ -38,7 +43,7 @@ timers, signals, and more. Even worse, the list of controlled items
varies from one vendor's UNIX to the next, so it is very difficult to varies from one vendor's UNIX to the next, so it is very difficult to
write portable code that cleans up everything. write portable code that cleans up everything.
Of the twelve most recent sendmail security holes, six worked only Of the twenty most recent sendmail security holes, eleven worked only
because the entire sendmail system is setuid. because the entire sendmail system is setuid.
Only one qmail program is setuid: qmail-queue. Its only purpose is to Only one qmail program is setuid: qmail-queue. Its only purpose is to
@ -95,7 +100,7 @@ and the quoter both misinterpret the interface in the same way.
When the original data is controlled by a malicious user, many of these When the original data is controlled by a malicious user, many of these
bugs translate into security holes. Some examples: the Linux login bugs translate into security holes. Some examples: the Linux login
-froot security hole; the classic find | xargs rm security hole; the -froot security hole; the classic find | xargs rm security hole; the
recent Majordomo security hole. Even a simple parser like getopt is Majordomo injection security hole. Even a simple parser like getopt is
complicated enough for people to screw up the quoting. complicated enough for people to screw up the quoting.
In qmail, all the internal file structures are incredibly simple: text0 In qmail, all the internal file structures are incredibly simple: text0
@ -107,11 +112,6 @@ All the complexity of parsing RFC 822 address lists and rewriting
headers is in the qmail-inject program, which runs without privileges headers is in the qmail-inject program, which runs without privileges
and is essentially part of the UA. and is essentially part of the UA.
The only nasty case is .qmail, qmail's answer to .forward. I tried to
make this as simple as possible, but unfortunately it still has to be
edited by users. As a result, the qlist mailing-list-management program
has to be careful to exclude subscriber addresses that contain newlines.
6. Keep it simple, stupid. 6. Keep it simple, stupid.
@ -127,5 +127,5 @@ I've mostly given up on the standard C library. Many of its facilities,
particularly stdio, seem designed to encourage bugs. A big chunk of particularly stdio, seem designed to encourage bugs. A big chunk of
qmail is stolen from a basic C library that I've been developing for qmail is stolen from a basic C library that I've been developing for
several years for a variety of applications. The stralloc concept and several years for a variety of applications. The stralloc concept and
getline2() make it very easy to avoid buffer overruns, memory leaks, getln() make it very easy to avoid buffer overruns, memory leaks, and
and artificial line length limits. artificial line length limits.

76
SENDMAIL Normal file
View file

@ -0,0 +1,76 @@
This document explains what you, as a user, will notice when the system
switches from sendmail to qmail.
This is a global document, part of the qmail package, not reflecting the
decisions made by your system administrator. For details on
* which local delivery agent qmail is configured to use,
* whether qmail is configured to use dot-forward,
* whether ezmlm is installed,
* whether fastforward is installed, and
* all other local configuration features,
see your local sendmail-qmail upgrade announcement (which your system
administrator may have placed into /var/qmail/doc/ANNOUNCE).
--- Mailbox location
If your system administrator has configured qmail to use binmail for
local deliveries, your mailbox will be in /var/spool/mail/you, just as
it was under sendmail.
If your system administrator has configured qmail to use qmail-local for
local deliveries, your mailbox will be moved to ~you/Mailbox. There is a
symbolic link from /var/spool/mail/you to ~you/Mailbox, so your mail
reader will find the mailbox at its new location.
--- Loop control
qmail-local automatically adds a Delivered-To field at the top of every
delivered message. It uses Delivered-To to prevent mail forwarding
loops, including cross-host mailing-list loops.
--- Outgoing messages
qmail lets you use environment variables to control the appearance of
your outgoing mail, supplementing the features offered by your MUA. For
example, qmail-inject will set up Mail-Followup-To for you automatically
if you tell it which mailing lists you are subscribed to. See
qmail-inject(8) for a complete list of features.
If you're at (say) sun.ee.movie.edu, qmail lets you type joe@mac for
joe@mac.ee.movie.edu, and joe@mac+ for joe@mac.movie.edu without the ee.
sendmail has a different interpretation of hostnames without dots.
--- Forwarding and mailing lists
qmail gives you the power to set up your own mailing lists without
pestering your system administrator.
Under qmail, you are in charge of all addresses of the form
you-anything. The delivery of you-anything is controlled by
~you/.qmail-anything, a file in your home directory.
For example, if you want to set up a bug-of-the-month-club mailing list,
you can put a list of addresses into ~you/.qmail-botmc. Any mail to
you-botmc will be forwarded to all of those addresses. Mail directly to
you is controlled by ~you/.qmail. You can even set up a catch-all,
~you/.qmail-default, to handle unknown you- addresses.
See dot-qmail(5) for the complete story. Beware that the syntax of
.qmail is different from the syntax of sendmail's .forward file.
If your system administrator has configured qmail to use the dot-forward
compatibility tool, you can put forwarding addresses (and programs) into
.forward the same way you did with sendmail.
If your system administrator has installed ezmlm, you can use ezmlm-make
to instantly set up a professional-quality mailing list, handling
subscriptions and archives automatically.
If your system administrator has installed fastforward, you can easily
manage a large database of forwarding addresses.

165
TARGETS
View file

@ -8,58 +8,13 @@ compile
fork.h fork.h
qmail-local.o qmail-local.o
qmail.o qmail.o
auto-str.o
make-makelib
makelib
substdio.o
substdi.o
substdo.o
subfderr.o
subfdout.o
subfdouts.o
subfdin.o
subfdins.o
substdio_copy.o
substdio.a
error.o
error_str.o
error_temp.o
error.a
str_len.o
str_diff.o
str_diffn.o
str_cpy.o
str_chr.o
str_rchr.o
str_start.o
byte_chr.o
byte_rchr.o
byte_diff.o
byte_copy.o
byte_cr.o
byte_zero.o
str.a
auto-str
auto_qmail.c
auto_qmail.o
auto-int8.o
fmt_str.o
fmt_strn.o
fmt_uint.o
fmt_uint0.o
fmt_ulong.o
scan_ulong.o
scan_8long.o
scan_nbblong.o
fs.a
auto-int8
auto_patrn.c
auto_patrn.o
quote.o quote.o
now.o now.o
gfrom.o gfrom.o
myctime.o myctime.o
slurpclose.o slurpclose.o
make-makelib
makelib
case_diffb.o case_diffb.o
case_diffs.o case_diffs.o
case_lowerb.o case_lowerb.o
@ -104,8 +59,8 @@ lock.a
fd_copy.o fd_copy.o
fd_move.o fd_move.o
fd.a fd.a
wait_pid.o
haswaitp.h haswaitp.h
wait_pid.o
wait_nohang.o wait_nohang.o
wait.a wait.a
env.o env.o
@ -124,24 +79,66 @@ stralloc.a
alloc.o alloc.o
alloc_re.o alloc_re.o
alloc.a alloc.a
strerr_sys.o
strerr_die.o
strerr.a
substdio.o
substdi.o
substdo.o
subfderr.o
subfdout.o
subfdouts.o
subfdin.o
subfdins.o
substdio_copy.o
substdio.a
error.o
error_str.o
error_temp.o
error.a
str_len.o
str_diff.o
str_diffn.o
str_cpy.o
str_chr.o
str_rchr.o
str_start.o
byte_chr.o
byte_rchr.o
byte_diff.o
byte_copy.o
byte_cr.o
byte_zero.o
str.a
fmt_str.o
fmt_strn.o
fmt_uint.o
fmt_uint0.o
fmt_ulong.o
scan_ulong.o
scan_8long.o
fs.a
datetime.o datetime.o
datetime_un.o datetime_un.o
datetime.a datetime.a
auto-str.o
auto-str
auto_qmail.c
auto_qmail.o
auto-int8.o
auto-int8
auto_patrn.c
auto_patrn.o
socket.lib
qmail-local qmail-local
uint32.h uint32.h
qmail-lspawn.o qmail-lspawn.o
auto-uid.o select.h
auto-uid chkspawn.o
auto-gid.o
auto-gid
auto_uids.c
auto_uids.o
auto-int.o auto-int.o
auto-int auto-int
auto_spawn.c auto_spawn.c
auto_spawn.o auto_spawn.o
select.h
chkspawn.o
chkspawn chkspawn
spawn.o spawn.o
chkshsgr.o chkshsgr.o
@ -153,6 +150,12 @@ cdb_hash.o
cdb_unpack.o cdb_unpack.o
cdb_seek.o cdb_seek.o
cdb.a cdb.a
auto-uid.o
auto-uid
auto-gid.o
auto-gid
auto_uids.c
auto_uids.o
qmail-lspawn qmail-lspawn
qmail-getpw.o qmail-getpw.o
auto_break.c auto_break.c
@ -175,7 +178,6 @@ ipme.o
ndelay.o ndelay.o
ndelay_off.o ndelay_off.o
ndelay.a ndelay.a
socket.lib
dns.lib dns.lib
qmail-remote qmail-remote
qmail-rspawn.o qmail-rspawn.o
@ -183,9 +185,9 @@ tcpto_clean.o
qmail-rspawn qmail-rspawn
direntry.h direntry.h
qmail-clean.o qmail-clean.o
fmtqfn.o
auto_split.c auto_split.c
auto_split.o auto_split.o
fmtqfn.o
qmail-clean qmail-clean
qmail-send.o qmail-send.o
qsutil.o qsutil.o
@ -216,7 +218,6 @@ predate
datemail datemail
mailsubj mailsubj
qmail-upq qmail-upq
qmail-config
qmail-showctl.o qmail-showctl.o
qmail-showctl qmail-showctl
qmail-newu.o qmail-newu.o
@ -233,12 +234,21 @@ qmail-qread
qmail-qstat qmail-qstat
qmail-tcpto.o qmail-tcpto.o
qmail-tcpto qmail-tcpto
qmail-tcpok.o
qmail-tcpok
qmail-pop3d.o qmail-pop3d.o
commands.o
maildir.o
qmail-pop3d qmail-pop3d
qmail-popup.o qmail-popup.o
qmail-popup qmail-popup
qmail-qmtpd.o qmail-qmqpc.o
qmail-qmqpc
qmail-qmqpd.o
received.o received.o
qmail-qmqpd
qmail-qmtpd.o
rcpthosts.o
qmail-qmtpd qmail-qmtpd
qmail-smtpd.o qmail-smtpd.o
qmail-smtpd qmail-smtpd
@ -247,6 +257,10 @@ sendmail
tcp-env.o tcp-env.o
remoteinfo.o remoteinfo.o
tcp-env tcp-env
qmail-newmrh.o
qmail-newmrh
config
config-fast
dnscname.o dnscname.o
dnsdoe.o dnsdoe.o
dnscname dnscname
@ -262,9 +276,6 @@ hostname.o
hostname hostname
ipmeprint.o ipmeprint.o
ipmeprint ipmeprint
qlist.o
qlist
qlist2
qreceipt.o qreceipt.o
qreceipt qreceipt
qsmhook.o qsmhook.o
@ -280,10 +291,6 @@ condredirect
maildirmake.o maildirmake.o
maildirmake maildirmake
maildir2mbox.o maildir2mbox.o
maildir.o
strerr_sys.o
strerr_die.o
strerr.a
maildir2mbox maildir2mbox
maildirwatch.o maildirwatch.o
maildirwatch maildirwatch
@ -296,6 +303,16 @@ install.o
install install
instcheck.o instcheck.o
instcheck instcheck
home
home+df
proc
proc+df
binm1
binm1+df
binm2
binm2+df
binm3
binm3+df
it it
qmail-local.0 qmail-local.0
qmail-lspawn.0 qmail-lspawn.0
@ -306,24 +323,30 @@ qmail-rspawn.0
qmail-clean.0 qmail-clean.0
qmail-send.8 qmail-send.8
qmail-send.0 qmail-send.0
qmail-start.8
qmail-start.0 qmail-start.0
splogger.0 splogger.0
qmail-queue.0 qmail-queue.0
qmail-inject.0 qmail-inject.0
mailsubj.0 mailsubj.0
qmail-showctl.0 qmail-showctl.0
qmail-newu.8
qmail-newu.0 qmail-newu.0
qmail-pw2u.8 qmail-pw2u.8
qmail-pw2u.0 qmail-pw2u.0
qmail-qread.0 qmail-qread.0
qmail-qstat.0 qmail-qstat.0
qmail-tcpto.0 qmail-tcpto.0
qmail-tcpok.0
qmail-pop3d.0 qmail-pop3d.0
qmail-popup.0 qmail-popup.0
qmail-qmqpc.0
qmail-qmqpd.0
qmail-qmtpd.0 qmail-qmtpd.0
qmail-smtpd.0 qmail-smtpd.0
tcp-env.0 tcp-env.0
qlist.0 qmail-newmrh.8
qmail-newmrh.0
qreceipt.0 qreceipt.0
qbiff.0 qbiff.0
forward.0 forward.0
@ -333,13 +356,13 @@ maildirmake.0
maildir2mbox.0 maildir2mbox.0
maildirwatch.0 maildirwatch.0
qmail.0 qmail.0
qmail-upgrade.7
qmail-upgrade.0
qmail-limits.7 qmail-limits.7
qmail-limits.0 qmail-limits.0
qmail-log.0 qmail-log.0
qmail-control.5
qmail-control.0 qmail-control.0
qmail-header.0 qmail-header.0
qmail-users.5
qmail-users.0 qmail-users.0
dot-qmail.5 dot-qmail.5
dot-qmail.0 dot-qmail.0
@ -351,3 +374,5 @@ addresses.0
envelopes.0 envelopes.0
forgeries.0 forgeries.0
man man
setup
check

522
THANKS
View file

@ -2,216 +2,334 @@ Thanks to lots of people for success and failure reports, code, ideas,
and documentation. See CHANGES for details of specific contributions. and documentation. See CHANGES for details of specific contributions.
Sorry if I left anyone out. Sorry if I left anyone out.
SA = Satoshi Adachi A2B = Are Bryne
A2L = Ali Lomonaco
A2P = Andrea Paolini
AAF = Adam A. Frey
AB = Alan Briggs
ABC = Alan B. Clegg
AC = Arne Coucheron
ACB = Andy C. Brandt
AF = Andreas Faerber
AG = Armin Gruner
AGB = Andre Grosse Bley
AH = Amos Hayes
AI = Akihiro Iijima
AJ = Alan Jaffray
AJK = Antti-Juhani Kaijanaho
AKB = Allen K. Briggs
AL = Andreas Lamprecht
ALB = Allan L. Bazinet
ANR = Adriano Nagelschmidt Rodrigues
AP = Andrew Pam
AS = Akos Szalkai
AV = Alex Vostrikov
AWB = Andy W. Barclay
AY = Araki Yasuhiro
B1F = Bo Fussing
B2F = Brad Forschinger
B2H = Buck Huppmann
B2L = Brent Laminack
B2W = Bil Wendling
B3W = Boris Wedl
BB = Bruce Bodger
BC = Bob Collie
BCK = Benjamin C. Kite
BCM = Bill C. Miller
BDB = Boris D. Beletsky
BDM = Byron D. Miller
BEO = Bruce E. O'Neel
BET = Bennett E. Todd
BG = Bert Gijsbers
BH = Brad Howes
BJ = Brian Jackson
BJM = Barry J. Miller
BL = Brian Litzinger
BMF = Brian M. Fisk
BN = Bill Nugent
BP = Bruce Perens
BR = Brian J. Reichert
BS = Bjoern Stabell
BT = Brad Templeton
BTW = Brian T. Wightman
BW = Bill Weinman
BZ = Blaz Zupan
C2F = Chuck Foster
C2H = Christoph Heidermanns
C2S = Craig Shrimpton
CEJ = Colin Eric Johnson
CF = C. Ferree
CG = Chris Garrigues
CH = Chael Hall
CHR = Craig H. Rowland
CK = Christoph Kaesling
CL = Carsten Leonhardt
CLS = Christopher L. Seawood
CM = Charles Mattair
CMP = Chase M. Phillips
CR = Christian Riede
CS = Cloyce Spradling
CSH = Clayton S. Haapala
D1H = Dieter Heidner
D2H = Dan Hollis
D2K = Dax Kelson
D2S = Dan Senie
D3S = Don Samek
DA = Dave Arcuri
DAR = Daniel A. Reish
DB = David Buscher
DBK = Douglas B. Kerry
DC = Dan Cross
DCC = Daniel C. Cotey
DE = Daniel Egnor
DEH = Daniel E. Harris
DF = Dale Farnsworth
DG = David Guntner
DK = Dave Kopper
DL = Daniel Lawrence
DM = David Mazieres
DML = David M. Lew
DP = Dave Platt
DS = Dave Sill
DST = Daniel S. Thibadeau
DWS = David Wayne Summers
EC = Evan Champion
ECG = Eric C. Garrison
EG = Eivind Gjelseth
EK = Eric Krohn
EP = Emanuele Pucciarelli
ERH = Eric R. Hankins
ES = Eric Smith
ESM = Edward S. Marshall
ET = Eivind Tagseth
ETT = Emmanuel T. Tardieu
F2T = Frank Thieme
FE = Frank Ederveen
FN = Faried Nawaz
FPL = Frederik P. Lindberg
FT = Frank Tegtmeyer
FW = Frank Wagner
G1A = Graham Adams G1A = Graham Adams
G2A = Greg Andrews
GAW = Greg A. Woods
GB = Glenn Barry
GH = Gene Hightower
GL = Giles Lean
GLM = Grant L. Miller
H2S = Harley Silver
HCJ = Helio Coelho Jr.
HDG = Hans de Graaff
HG = Howard Goldstein
HHO = Harald Hanche-Olsen
HJB = Herbert J. Bernstein
HM = Hirokazu Morikawa
HS = Harlan Stenn
HT = Henry Timmerman
HW = Hal Wine
HWM = Henry W. Miller
IH = Ingmar Hupp
IK = Ivan Kohler
IKW = Ian Keith Wynne
IS = Icarus Sparry
IW = Ian Westcott
J1B = John Banghart
J1K = Jost Krieger
J2B = Jos Backus
J2K = Johannes Kroeger
J2M = Joel Maslak
J2P = John Parker
J2W = Jim Whitby
JAB = Jeremy A. Bussard
JAK = Johan A. Kullstam
JB = Joshua Buysse
JBB = Jason B. Brown
JBF = John B. Fleming
JC = Jim Clausing
JCD = Jeffrey C. Dege
JD = Joe Doupnik
JDHB = Johannes D. H. Beekhuizen
JDJ = Joshua D. Juran
JF = Janos Farkas
JFK = James F. Kane III
JGM = John G. Myers
JJB = J. J. Bailey
JJMK = Jonathan J. M. Katz
JJR = Jaron J. Rubenstein
JK = Jari Kirma
JL = Jim Littlefield
JLB = Julie L. Baumler
JLH = Jason L. Haar
JLW = Jason L. Wright
JM = Jim Meehan
JMS = Jason M. Stokes
JMT = John M. Twilley
JP = John Palkovic
JPB = Joe Block
JPH = Justin P. Hannah
JPR = Jean-Pierre Radley
JRL = John R. Levine
JRY = Jamie R. Yukes
JS = Jesper Skriver
JTB = Jonathan T. Bowie
JW = John Whittaker
JWB = James W. Birdsall
K1J = Kyle Jones
K2J = Kevin Johnson
KA = Klaus Aigte
KB = Keith Burdis
KE = Kenny Elliott
KJJ = Kevin J. Johnson
KJS = Kevin J. Sawyer
KMD = Kevin M. Dulzo
KO = Keith Owens
KR = Kenji Rikitake
KT = Karsten Thygesen
KUT = Kai Uwe Tempel
KY = Kentaro Yoshitomi
L2L = Louis Larry
L3L = Luis Lopes
LB = Laurentiu Badea
LL = lilo
LW = Lionel Widdifield
M2C = Mark Crimmins
M2G = Michael R. Gile
M2H = Martin Hager
M2L = M. Lyons
M2R = Mark Riekenberg
M2S = Mikael Suokas
M3H = Michael Holzt
M3L = Michael Lazarou
M3S = Morten Skjelland
M4S = Michael Shields
MB = Martin Budsj?
MBS = Michael B. Scher
MC = Michael Cooley
MD = Mark Delany
MDI = Miguel de Icaza
ME = Marc Ewing
MEE = Mads E. Eilertsen
MF = Massimo Fusaro
MG = Michael Graff
MGM = Mitchell G. Morris
MH = Markus Hofmann
MJD = Mark-Jason Dominus
MJG = Manuel J. Galan
ML = Martin Lucina
MLH = May Liss Haarstad
MM = Martin Mersberger
MMM = Momchil M. Momchev
MMM2 = Marc M. Martinez
MP = Matt Paduano
MR = Mosfeq Rashid
MRG = Matthew R. Green
MS = Mark Spears
MSD = Mandell S. Degerness
MSS = Matthew S. Soffen
MT = Mark Thompson
MW = Mate Wierdl
MWE = Mark W. Eichin
NA = Norm Aleks NA = Norm Aleks
NAA = Nicholas A. Amato NAA = Nicholas A. Amato
G2A = Greg Andrews
TA = Tetsuo Aoki
DA = Dave Arcuri
JJB = J. J. Bailey
J2B = Jos Backus
SSB = Stik Bakken
J1B = John Banghart
GB = Glenn Barry
JLB = Julie L. Baumler
SLB = Steven L. Baur
ALB = Allan L. Bazinet
JDHB = Johannes D. H. Beekhuizen
BDB = Boris D. Beletsky
HJB = Herbert J. Bernstein
REB = Ronald E. Bickers
JPB = Joe Block
BB = Bruce Bodger
SB = Stephane Bortzmeyer
PB = Peter Bowyer
ACB = Andy C. Brandt
AB = Alan Briggs
AKB = Allen K. Briggs
JBB = Jason B. Brown
JAB = Jeremy A. Bussard
RJC = Robert J. Carter
EC = Evan Champion
JC = Jim Clausing
HCJ = Helio Coelho Jr.
BC = Bob Collie
SGC = Stephen G. Comings
MC = Michael Cooley
DCC = Daniel C. Cotey
AC = Arne Coucheron
M2C = Mark Crimmins
DC = Dan Cross
MD = Mark Delany
SVD = Stef Van Dessel
RD = Rahul Dhesi
MJD = Mark-Jason Dominus
JD = Joe Doupnik
FE = Frank Ederveen
DE = Daniel Egnor
MWE = Mark W. Eichin
MEE = Mads E. Eilertsen
KE = Kenny Elliott
TEE = Thomas E. Erskine
ME = Marc Ewing
JF = Janos Farkas
DF = Dale Farnsworth
YF = Yaroslav Faybishenko
CF = C. Ferree
JBF = John B. Fleming
C2F = Chuck Foster
RF = Rainer Fraedrich
MF = Massimo Fusaro
CG = Chris Garrigues
BG = Bert Gijsbers
M2G = Michael R. Gile
EG = Eivind Gjelseth
HG = Howard Goldstein
TG = Tim Goodwin
HDG = Hans de Graaff
MG = Michael Graff
PJG = Paul Graham
MRG = Matthew R. Green
SG = Steven Grimm
AG = Armin Gruner
CSH = Clayton S. Haapala
JLH = Jason L. Haar
MLH = May Liss Haarstad
CH = Chael Hall
JPH = Justin P. Hannah
RJH = Randy Harmon
PH = Paul Harrington
DEH = Daniel E. Harris
RFH = Robert F. Harrison
D1H = Dieter Heidner
GH = Gene Hightower
MH = Markus Hofmann
D2H = Dan Hollis
NH = Nick Holloway NH = Nick Holloway
TH = Ton Hospel NR = Norbert Roeding
BH = Brad Howes NW = Nicholas Waples
TJH = Timothy J. Hunt OK = Oezguer Kesim
B2H = Buck Huppmann
MDI = Miguel de Icaza
BJ = Brian Jackson
AJ = Alan Jaffray
CEJ = Colin Eric Johnson
K2J = Kevin Johnson
K1J = Kyle Jones
SJ = Sudish Joseph
W2K = Wolfram Kahl
JJMK = Jonathan J. M. Katz
CK = Christoph Kaesling
PK = Petri Kaukasoina
D2K = Dax Kelson
TK = Terry Kennedy
DBK = Douglas B. Kerry
WK = Werner Koch
DK = Dave Kopper
J1K = Jost Krieger
J2K = Johannes Kroeger
EK = Eric Krohn
B2L = Brent Laminack
AL = Andreas Lamprecht
L2L = Louis Larry
M3L = Michael Lazarou
CL = Carsten Leonhardt
JRL = John R. Levine
DML = David M. Lew
LL = lilo
FPL = Frederik P. Lindberg
JL = Jim Littlefield
BL = Brian Litzinger
RL = Robert Luce
ML = Martin Lucina
M2L = M. Lyons
TM = Toshinori Maeno
ESM = Edward S. Marshall
J2M = Joel Maslak
TLM = Timothy L. Mayo
DM = David Mazieres
RM = Rich McClellan
SM = Shawn McHorse
JM = Jim Meehan
HWM = Henry W. Miller
RDM = Raul Miller
MMM = Momchil M. Momchev
JGM = John G. Myers
FN = Faried Nawaz
RN = Russell Nelson
TN = Thomas Neumann
UO = Uwe Ohse
PCO = Peter C. Olsen
HHO = Harald Hanche-Olsen
BEO = Bruce E. O'Neel
JP = John Palkovic
AP = Andrew Pam
SP = Stephen Parker
TVP = Tom van Peer
BP = Bruce Perens
CMP = Chase M. Phillips
DP = Dave Platt
EP = Emanuele Pucciarelli
JPR = Jean-Pierre Radley
S1R = Satish Ramachandran
MR = Mosfeq Rashid
TRR = Tracy R. Reed
BR = Brian Reichert
S2R = Sean Reifschneider
DAR = Daniel A. Reish
CR = Christian Riede
KR = Kenji Rikitake
OR = Ollivier Robert OR = Ollivier Robert
ANR = Adriano Nagelschmidt Rodrigues
JJR = Jaron J. Rubenstein
RS = Robert Sanders
KJS = Kevin J. Sawyer
MBS = Mike Scher
SAS = Steven A. Schrader
CLS = Christopher L. Seawood
OS = Oliver Seiler OS = Oliver Seiler
D2S = Dan Senie PB = Peter Bowyer
SS = Simon Shapiro PCO = Peter C. Olsen
RGS = Richard G. Sharman PGF = Paul Fox
DS = Dave Sill PGR = Phil G. Rorex
H2S = Harley Silver PH = Paul Harrington
M3S = Morten Skjelland PJG = Paul Graham
JS = Jesper Skriver PJH = Peter J. Hunter
ES = Eric Smith PK = Petri Kaukasoina
IS = Icarus Sparry PMH = Peter M. Haworth
CS = Cloyce Spradling PO = Paul Overell
BS = Bjoern Stabell
HS = Harlan Stenn
JMS = Jason M. Stokes
DWS = David Wayne Summers
M2S = Mikael Suokas
PS = Paul Svensson PS = Paul Svensson
ET = Eivind Tagseth
PT = Paul Taylor PT = Paul Taylor
S2T = Steve Taylor PTW = P. T. Withington
BT = Brad Templeton
DST = Daniel S. Thibadeau
MT = Mark Thompson
S3T = Steffen Thorsen
KT = Karsten Thygesen
BET = Bennett E. Todd
JMT = John M. Twilley
ST = Steve Tylock
TU = Tetsu Ushijima
VV = Vince Vielhaber
TV = Tommi Virtanen
FW = Frank Wagner
BW = Bill Weinman
IW = Ian Westcott
SJW = Stephen J. White
JW = John Whittaker
LW = Lionel Widdifield
MW = Mate Wierdl
BTW = Brian T. Wightman
PW = Peter Wilkinson PW = Peter Wilkinson
HW = Hal Wine R2N = Rivo Nurges
GAW = Greg A. Woods RA = Russ Allbery
RAB = Randolph Allen Bentson
RAM = Robin A. McCollum
RB = Robert Bridgham
RC = Ryan Crum
RD = Rahul Dhesi
RDM = Raul D. Miller
REB = Ronald E. Bickers
RF = Rainer Fraedrich
RFH = Robert F. Harrison
RGS = Richard G. Sharman
RJC = Robert J. Carter
RJH = Randy Harmon
RJO = Richard J. Ohnemus
RK = Riho Kurg
RL = Robert Luce
RM = Rich McClellan
RN = Russell Nelson
RO = Roberto Oppedisano
RPS = Russell P. Sutherland
RS = Robert Sanders
RSK = Robert S. Krzaczek
S1R = Satish Ramachandran
S2P = Stefan Puscasu
S2R = Sean Reifschneider
S2S = Scott Schwartz
S2T = Steve Taylor
S3T = Steffen Thorsen
SA = Satoshi Adachi
SAE = Stefaan A. Eeckels
SAS = Steven A. Schrader
SB = Stephane Bortzmeyer
SC = Stefan Cars
SCW = Steven C. Work SCW = Steven C. Work
JLW = Jason L. Wright SG = Steven Grimm
SGC = Stephen G. Comings
SJ = Sudish Joseph
SJB = SJ Burns
SJW = Stephen J. White
SLB = Steven L. Baur
SM = Shawn McHorse
SP = Stephen Parker
SPM = Salvatore P. Miccicke
SS = Simon Shapiro
SSB = Stik Bakken
ST = Steve Tylock
SV = Sven Velt
SVD = Stef Van Dessel
T2K = Tomoya Konishi
T2M = Toni Mueller
T2U = Todd Underwood
TA = Tetsuo Aoki
TB = Tobias Brox
TD = Tom Demmer
TEE = Thomas E. Erskine
TG = Tim Goodwin
TH = Ton Hospel
TJH = Timothy J. Hunt
TK = Terry Kennedy
TL = Timothy Lorenc
TLF = Timo L. Felbinger
TLM = Timothy L. Mayo
TM = Toshinori Maeno
TN = Thomas Neumann
TRR = Tracy R. Reed
TT = Takaki Taniguchi
TU = Tetsu Ushijima
TV = Tommi Virtanen
TVP = Tom van Peer
UO = Uwe Ohse
VBM = Vladimir B. Machulsky
VR = Vincenzo Romano
VU = Viriya Upatising
VV = Vince Vielhaber
W2K = Wolfram Kahl
WEB = William E. Baxter
WK = Werner Koch
WS = Wilbur Sims
WW = Wei Wu WW = Wei Wu
IKW = Ian Keith Wynne YC = Yuji Chikahiro
BZ = Blaz Zupan YF = Yaroslav Faybishenko
ZU = Zin Uda

149
THOUGHTS
View file

@ -45,9 +45,8 @@ smaller resolver library into qmail. (Bonus: I'd avoid system-specific
problems with old resolvers.) The problem is that I'd then be writing a problems with old resolvers.) The problem is that I'd then be writing a
fundamentally insecure library. I'd no longer be able to blame the BIND fundamentally insecure library. I'd no longer be able to blame the BIND
authors and vendors for the fact that attackers can easily use DNS to authors and vendors for the fact that attackers can easily use DNS to
steal mail. Possible solution: replace dns.c with something that passes steal mail. Solution: insist that the resolver run on the same host; the
requests (reliably!) to a local daemon; call the original resolver kernel can guarantee the security of low-numbered 127.0.0.1 UDP ports.
library from that daemon.
NFS is the primary enemy of security partitioning under UNIX. Here's the NFS is the primary enemy of security partitioning under UNIX. Here's the
story. Sun knew from the start that NFS was completely insecure. It story. Sun knew from the start that NFS was completely insecure. It
@ -72,10 +71,10 @@ is, as far as I can tell, always wrong.
2. Injecting mail locally (qmail-inject, sendmail-clone) 2. Injecting mail locally (qmail-inject, sendmail-clone)
RFC 822 section 3.4.9 prohibits certain visual effects in headers. RFC 822 section 3.4.9 prohibits certain visual effects in headers, and
qmail-inject doesn't waste the time to enforce this absurd restriction. the 822bis draft prohibits even more. qmail-inject could enforce these
If you will suffer from someone sending you ``flash mail,'' go find a absurd restrictions, but why waste the time? If you will suffer from
better mail reader. someone sending you ``flash mail,'' go find a better mail reader.
qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops
sendmail from adding Apparently-To. Unfortunately, old versions of sendmail from adding Apparently-To. Unfortunately, old versions of
@ -101,44 +100,47 @@ It is possible to extract non-unique Message-IDs out of qmail-inject.
Here's how: stop qmail-inject before it gets to the third line of Here's how: stop qmail-inject before it gets to the third line of
main(), then wait until the pids wrap around, then restart qmail-inject main(), then wait until the pids wrap around, then restart qmail-inject
and blast the message through, then start another qmail-inject with the and blast the message through, then start another qmail-inject with the
same pid in the same second. I'm not sure how to fix this. (Of course, same pid in the same second. I'm not sure how to fix this without
the user could just type in his own non-unique Message-IDs.) system-supplied sequence numbers. (Of course, the user could just type
in his own non-unique Message-IDs.)
The bat book says: ``Rules that hide hosts in a domain should be applied The bat book says: ``Rules that hide hosts in a domain should be applied
only to sender addresses.'' Recipient masquerading works fine with only to sender addresses.'' Recipient masquerading works fine with
qmail. None of sendmail's pitfalls apply, basically because qmail has a qmail. None of sendmail's pitfalls apply, basically because qmail has a
straight paper path. straight paper path.
I expect to receive some pressure to make up for the failings of MUA I predicted that I would receive some pressure to make up for the
writers who don't understand the concept of reliability. (``Like, duh, failings of MUA writers who don't understand the concept of reliability.
you mean I was supposed to check the sendmail exit code?'') (``Like, duh, you mean I'm supposed to check the sendmail exit code?'')
I was right.
3. Receiving mail from the network (tcp-env, qmail-smtpd) 3. Receiving mail from the network (tcp-env, qmail-smtpd)
RFC 1123 requires VRFY support, but says that it's okay if an qmail-smtpd doesn't allow privacy-invading commands like VRFY and EXPN.
implementation can be configured to not allow VRFY. qmail-smtpd doesn't If you really want to publish such information, use a mechanism that
allow VRFY. If you desperately want your SMTP server (i.e., inetd) to legitimate users actually know about, such as fingerd or httpd.
provide useful information for VRFY, just compile and install sendmail.
Were the RFC 1123 writers aware of the as-if principle of interface RFC 1123 says that VRFY and EXPN are important to track down cross-host
specification? ... They say that VRFY and EXPN are important for mailing list loops. With Delivered-To, mailing list loops do no damage,
tracking down cross-host mailing list loops. Catch up to the 1990s, _and_ one of the list administrators gets a bounce message that shows
guys: with Delivered-To, mailing list loops do absolutely no damage, exactly how the loop occurred. Solve the problem, not the symptom.
_and_ one of the list administrators gets a bounce that shows exactly
how the loop occurred. Solve the problem, not the symptom. ... There's a
vastly superior alternative to EXPN. Hint: finger postmaster@ai.mit.edu.
Should dns.c make special allowances for 127.0.0.1/localhost? Should dns.c make special allowances for 127.0.0.1/localhost?
badmailfrom (like 8BITMIME) is a waste of code space. badmailfrom (like 8BITMIME) is a waste of code space.
In theory a MAIL or RCPT argument can contain unquoted LFs. In practice
there are a huge number of clients that terminate commands with just LF,
even if they use CR properly inside DATA.
4. Adding messages to the queue (qmail-queue) 4. Adding messages to the queue (qmail-queue)
Should qmail-queue try to make sure enough disk space is free in Should qmail-queue try to make sure enough disk space is free in
advance? When qmail-queue is invoked by qmail-local or (with ESMTP) advance? When qmail-queue is invoked by qmail-local or (with ESMTP)
qmail-smtpd or qmail-qmtpd, it could be told a size in advance. I wish qmail-smtpd or qmail-qmtpd or qmail-qmqpd, it could be told a size in
UNIX had an atomic allocate-disk-space routine... advance. I wish UNIX had an atomic allocate-disk-space routine...
The qmail.h interface (reflecting the qmail-queue interface, which in The qmail.h interface (reflecting the qmail-queue interface, which in
turn reflects the current queue file structure) is constitutionally turn reflects the current queue file structure) is constitutionally
@ -152,11 +154,7 @@ Should qmail-queue not bother queueing a message with no recipients?
The queue directory must be local. Mounting it over NFS is extremely The queue directory must be local. Mounting it over NFS is extremely
dangerous---not that this stops people from running sendmail that way! dangerous---not that this stops people from running sendmail that way!
Perhaps it is worth putting together a diskless-host qmail package with Diskless hosts should use mini-qmail instead.
just qmail-inject and an SMTP client in place of qmail-queue. Sending
mail to the server via SMTP is of course vastly better than trying to do
anything over NFS. If the NFS server is up but the mail server is down,
users will just have to wait.
Queue reliability demands that single-byte writes be atomic. This is Queue reliability demands that single-byte writes be atomic. This is
true for a fixed-block filesystem such as UFS, and for a logging true for a fixed-block filesystem such as UFS, and for a logging
@ -190,11 +188,8 @@ than one deliverable message at any given moment.
Exception: Even with all the concurrency tricks, qmail-send can end up Exception: Even with all the concurrency tricks, qmail-send can end up
spending a few minutes on a mailing list with thousands of remote spending a few minutes on a mailing list with thousands of remote
entries. A user might send a new message to a remote address in the entries. A user might send a new message to a remote address in the
meantime. Perhaps qmail-send should limit its time per message to, meantime. The simplest way to handle this would be to put big messages
say, thirty recipients. This will require some way to mark recipients on a separate channel.
who were already done on this pass. Possible approach: Maintain two todo
lists (for both L and R). Always work on the earlier todo list. Move
deferrals to the other todo list.
qmail-send will never start a pass for a job that it already has. This qmail-send will never start a pass for a job that it already has. This
means that, if one delivery takes longer than the retry interval, the means that, if one delivery takes longer than the retry interval, the
@ -207,40 +202,26 @@ Some things that qmail-send does synchronously: queueing a bounce
message; doing a cleanup via qmail-clean; classifying and rewriting all message; doing a cleanup via qmail-clean; classifying and rewriting all
the addresses in a new message. As usual, making these asynchronous the addresses in a new message. As usual, making these asynchronous
would require some housekeeping, but could speed things up a bit. would require some housekeeping, but could speed things up a bit.
(Making bounces asynchronous, without POSIX waitpid(), means that (I'm willing to assume POSIX waitpid() for asynchronous bounces; putting
wait_pid() has to keep a buffer of previous wait()s. Ugh.) an unbounded buffer into wait_pid() for the sake of NeXTSTEP 3 is not
worthwhile.)
fsync() is a bottleneck. To make this asynchronous would require gobs of Disk I/O is a bottleneck; UFS is reliable but it isn't fast. A good
dedicated output processes whose only purpose in life is to watch data logging filesystem offers much better performance, but logging
get written to the disk. Inconceivable! (``You keep using that word. I filesystems aren't widely available. Solution: Keep a journal, separate
do not think that word means what you think it means.'') from the queue, adequate to rebuild the queue (with at worst some
duplicate deliveries). Compress the journal. This would dramatically
reduce total disk I/O.
On the other hand, I could survive without fsync()ing the local and Bounce aggregation is a dubious feature. Bounce records aren't
remote and info files as long as I don't unlink todo. This would require crashproof; there can be a huge delay between a failure and a bounce;
redefining the queue states. I need to see how much speed can be gained. the resulting bounce format is unnecessarily complicated. I'm tempted to
scrap the bounce directory and send one bounce for each failing
recipient, with appropriate modifications in the accompanying text.
Currently qmail-send sends at most one bounce message for each incoming qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Or run
message. This means that the sender doesn't get flooded with copies of qmail-start under an external service controller, such as supervise;
his own message. On the other hand, a single slow address can hold up that's why it runs in the foreground.
bounces for a bunch of fast addresses. It would be easy to call
injectbounce() more often. What is the best strategy? This feels like
the TCP-buffering issue... don't want to pepper the other guy with
little packets, but do want to get the data across.
qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Given how
simple this is, I'm not inclined to set up some tricky locking solution
where qmail-send records its pid etc. But I just know that, if I provide
this qmail-stop program, someone will screw himself by making another
uid the same as UID_SEND, or making UID_SEND be root, or whatever.
Aargh. Maybe use another named pipe... New solution: Run qmail-start
under an external service controller---it runs in the foreground now.
Bounce messages could include more statistical information in the first
paragraph: when I received the message, how many recipients I was
supposed to handle, how many I successfully dealt with, how many I
already told you about, how many are still in the queue. Have to
emphasize that the number of recipients _here_ is perhaps less than the
number of recipients on the original message.
The readdir() interface hides I/O errors. Lower-level interfaces would The readdir() interface hides I/O errors. Lower-level interfaces would
lead me into a thicket of portability problems. I'm really not sure what lead me into a thicket of portability problems. I'm really not sure what
@ -270,22 +251,17 @@ messages to multiple recipients at a single host? For typical hosts,
multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a
_slowness_ feature. Separate SMTP transactions have much lower latency. _slowness_ feature. Separate SMTP transactions have much lower latency.
The multiple-RCPT bandwidth gain _might_ be noticeable for a machine I've heard three complaints about bandwidth use from masochists sending
that sends most messages to a smarthost. It would be easy to have messages through a modem through a smarthost to thousands of users---
qmail-rspawn supply qmail-remote with all the addresses at once, as long without sublists! They can get much better performance with QMQP.
as qmail-send says when it's about to block... Putting recipients into
the right order is clearly the UA's job. One multiple-RCPT pitfall is
that a remote host might not be able to deal with (say) 10 recipients,
even though RFC 821 says everyone has to be able to handle 100;
qmail-rspawn would have to notice this and back off. (Not that other
mailers do. Sometimes I'm amazed Internet mail works at all.)
In the opposite direction: It's tempting to remove the @host part of the In the opposite direction: It's tempting to remove the @host part of the
qmail-remote recip argument. Or at least avoid double-dns_cname. qmail-remote recip argument. Or at least avoid double-dns_cname.
There are lots of reasons that qmail-rspawn should take a more active There are lots of reasons that qmail-rspawn should take a more active
role in qmail-remote's activities. It should call separate programs to role in qmail-remote's activities. It should call separate programs to
do (1) MX lookups, (2) SMTP connections, (3) QMTP connections. do (1) MX lookups, (2) SMTP connections, (3) QMTP connections. (But this
wouldn't be so important if the DNS library didn't burn so much memory.)
I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX
record sending me mail for a host that I don't recognize as local.) record sending me mail for a host that I don't recognize as local.)
@ -310,6 +286,15 @@ time across any number of MXs; this idea is due to Mark Delany.
RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that
the server's reply text doesn't contain bare LFs. the server's reply text doesn't contain bare LFs.
RFC 821 and RFC 1123 prohibit host names in MAIL FROM and RCPT TO from
being aliases. qmail-remote, like sendmail, rewrites aliases in RCPT;
people who don't list aliases in control/locals or sendmail's Cw are
implicitly relying on this conversion. It is course quite silly for an
internal DNS detail to have such an effect on mail delivery, but that's
how the Internet works. On the other hand, the compatibility arguments
do not apply to MAIL FROM. qmail-remote no longer bothers with CNAME
lookups for the envelope sender host.
7. Delivering mail locally (qmail-lspawn, qmail-local) 7. Delivering mail locally (qmail-lspawn, qmail-local)
@ -347,7 +332,7 @@ Configurable user database: Yup.
BIND support: Yup. BIND support: Yup.
Keyed files: Yes, in qmsmac. Keyed files: Yes, in fastforward.
931/1413/Ident/TAP: Yup. 931/1413/Ident/TAP: Yup.
@ -358,7 +343,9 @@ List-owner handling: Yup.
Dynamic header allocation: Yup. Dynamic header allocation: Yup.
Minimum number of disk blocks: Yes, via tunefs -m. Minimum number of disk blocks: Yes, via tunefs -m. (Or quotas; the right
setup has qmailq with a small quota, qmails with a larger quota, so that
qmail-send always has room to work.)
Checkpointing: Yes, but not configurable---qmail always checkpoints. Checkpointing: Yes, but not configurable---qmail always checkpoints.
@ -420,12 +407,6 @@ I should clean up the bput/put choices.
Some of the stralloc_0()s indicate that certain lower-level routines Some of the stralloc_0()s indicate that certain lower-level routines
should grok stralloc. should grok stralloc.
RN suggests having qlist smash the case of the incoming host name.
K1J suggests that mailing list subscription managers should have
a three-way handshake, to prevent person A from subscribing person B to
a mailing list. qlist doesn't do this, but ezmlm does.
qmail assumes that all times are positive; that pid_t, time_t and ino_t qmail assumes that all times are positive; that pid_t, time_t and ino_t
fit into unsigned long; that gid_t fits into int; that the character set fit into unsigned long; that gid_t fits into int; that the character set
is ASCII; and that all pointers are interchangeable. Do I care? is ASCII; and that all pointers are interchangeable. Do I care?

29
TODO
View file

@ -1,9 +1,26 @@
do some serious coverage testing, preferably under Purify, without alloc slop test qmail-qmqpc
replace INTERNALS and THOUGHTS with a real paper describing qmail test qmail-qmqpd
reorganize qmail-inject to do most rw on characters, not tokens
allow concurrency over 255 consider stripping vdoms for VERPs; tnx PJH
handle IPv6 consider ~ in qmail-local for doing defaultdelivery (not recursively)
consider POP bulletins
turn qmail-upq into a more serious queue-moving utility turn qmail-upq into a more serious queue-moving utility
consider fast-greeting option in qmail-smtpd
build a returnmail package
expand strerr coverage expand strerr coverage
redo control interface
allow concurrency over 255
allow more channels at compile time
test for linux fifo close bug at compile time
eliminate qsmhook
finish OTBS conversion
use lib822 in qmail-inject
use lib822 in qreceipt
use lib822 in qbiff
use lib822 in maildirwatch
eliminate token822, headerbody, hfield
replace INTERNALS and THOUGHTS with a real paper describing qmail
handle IPv6
rewrite everything from scratch rewrite everything from scratch
maybe allow root to clear tcpto on the fly

93
UPGRADE
View file

@ -3,12 +3,20 @@ information generally), the qmail system comes with NO WARRANTY. It's
much more secure and reliable than sendmail, but that's not saying much. much more secure and reliable than sendmail, but that's not saying much.
Here's how to upgrade from qmail 1.00 to qmail 1.01. This procedure will Here's how to upgrade from qmail 1.01 to qmail 1.02. This procedure will
overwrite the old qmail binaries. Furthermore, it may begin delivering overwrite the old qmail binaries. Furthermore, it may begin delivering
messages from the queue before you have had a chance to test it. messages from the queue before you have had a chance to test it.
WARNING: The qmail-start command line has changed. WARNING: qlist has been split into a separate package. You can obtain it
from http://pobox.com/~djb/qlist.html if you have any users who need it.
WARNING: recipientmap is gone. The virtualdomains mechanism has been
expanded to support virtual users.
WARNING: qmail-start is now normally started from /var/qmail/rc. If you
have changed your qmail-start line, you will have to change
/var/qmail/rc accordingly in step 8.
Before starting, compare conf* to your old conf*, and make any necessary Before starting, compare conf* to your old conf*, and make any necessary
@ -17,27 +25,37 @@ changes. Do not copy your old conf*; the baseline has changed.
How to install: How to install:
1. Compile the programs: 1. Compile the programs and create the formatted man pages:
# make # make it man
2. Create the formatted man pages, *.0:
# make man 2. Inform your users that mail will not be accepted for a few minutes.
3. Inform your users that mail will not be accepted for a few minutes.
4. Disable deliveries by killing your old qmail-send. Wait for it to 3. Disable deliveries by killing your old qmail-send. Wait for it to
print ``exiting'' in the log. print ``exiting'' in the log.
5. Disable SMTP service by commenting out the smtp line in inetd.conf;
4. Disable SMTP service by commenting out the smtp line in inetd.conf;
kill -HUP your inetd. (If you are using tcpserver, simply kill -STOP kill -HUP your inetd. (If you are using tcpserver, simply kill -STOP
your tcpserver.) Wait for current qmail-smtpd processes to die. (If your tcpserver. If you are running a QMTP server, disable that too.)
you are running a QMTP server, disable that too.) Wait for current qmail-smtpd processes to die.
6. Install the new binaries and man pages:
# rm /var/qmail/bin/* /var/qmail/man/*/* 5. Install the new binaries and man pages:
# make setup # make setup
7. Run instcheck to make sure it doesn't print any warnings:
6. Run instcheck to make sure it doesn't print any warnings:
# make check # make check
8. Reenable deliveries:
# env - PATH="/var/qmail/bin:$PATH" \ 7. Copy /var/qmail/boot/home to /var/qmail/rc. (Use home+df instead if
qmail-start ./Mailbox splogger qmail & you have installed dot-forward; use proc or proc+df if you are using
Make sure to include the ./ in ./Mailbox. procmail by default for local deliveries.)
9. Insert ./Mailbox into the qmail-start line in your boot scripts.
8. Compare /var/qmail/rc to the qmail-start boot line in your boot
scripts. Edit /var/qmail/rc if necessary. Replace the qmail-start
boot line in your boot scripts with
csh -cf '/var/qmail/rc &'
9. Reenable deliveries:
# csh -cf '/var/qmail/rc &'
10. Reenable SMTP service by restoring the smtp line in inetd.conf; kill 10. Reenable SMTP service by restoring the smtp line in inetd.conf; kill
-HUP your inetd. (If you are using tcpserver, simply kill -CONT your -HUP your inetd. (If you are using tcpserver, simply kill -CONT your
tcpserver. If you are running a QMTP server, reenable that too.) tcpserver. If you are running a QMTP server, reenable that too.)
@ -46,48 +64,61 @@ How to install:
How to test (steps 11-17 can be done before step 10): How to test (steps 11-17 can be done before step 10):
11. Look for a 11. Look for a
qmail: running qmail: status: local 0/10 remote 0/20
line in syslog. (The big number is a splogger timestamp.) line in syslog. (The big number is a splogger timestamp.)
12. Local-local test: Send yourself an empty message. (Replace ``me'' 12. Local-local test: Send yourself an empty message. (Replace ``me''
with your username. Make sure to include the ``to:'' colon.) with your username. Make sure to include the ``to:'' colon.)
% echo to: me | /var/qmail/bin/qmail-inject % echo to: me | /var/qmail/bin/qmail-inject
The message will show up immediately in ~/Mailbox, and syslog will The message will show up immediately in your mailbox, and syslog
show something like this: will show something like this:
qmail: new msg 53 qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666 qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
qmail: starting delivery 1: msg 53 to local me@domain qmail: starting delivery 1: msg 53 to local me@domain
qmail: status: local 1/10 remote 0/20
qmail: delivery 1: success: did_1+0+0/ qmail: delivery 1: success: did_1+0+0/
qmail: status: local 0/10 remote 0/20
qmail: end msg 53 qmail: end msg 53
(53 is an inode number; 20345 is a process ID; your numbers will (53 is an inode number; 20345 is a process ID; your numbers will
probably be different.) probably be different.)
13. Local-error test: Send a message to a nonexistent local address. 13. Local-error test: Send a message to a nonexistent local address.
% echo to: nonexistent | /var/qmail/bin/qmail-inject % echo to: nonexistent | /var/qmail/bin/qmail-inject
qmail: new msg 53 qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666 qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
qmail: starting delivery 2: msg 53 to local nonexistent@domain qmail: starting delivery 2: msg 53 to local nonexistent@domain
qmail: status: local 1/10 remote 0/20
qmail: delivery 2: failure: No_such_address.__#5.1.1_/ qmail: delivery 2: failure: No_such_address.__#5.1.1_/
qmail: status: local 0/10 remote 0/20
qmail: bounce msg 53 qp 20357 qmail: bounce msg 53 qp 20357
qmail: end msg 53 qmail: end msg 53
qmail: new msg 54 qmail: new msg 54
qmail: info msg 54: bytes 743 from <> qp 20357 uid 666 qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
qmail: starting delivery 3: msg 54 to local me@domain qmail: starting delivery 3: msg 54 to local me@domain
qmail: status: local 1/10 remote 0/20
qmail: delivery 3: success: did_1+0+0/ qmail: delivery 3: success: did_1+0+0/
qmail: status: local 0/10 remote 0/20
qmail: end msg 54 qmail: end msg 54
You will now have a bounce message in ~/Mailbox. You will now have a bounce message in your mailbox.
14. Local-remote test: Send an empty message to your account on another 14. Local-remote test: Send an empty message to your account on another
machine. machine.
% echo to: me@wherever | /var/qmail/bin/qmail-inject % echo to: me@wherever | /var/qmail/bin/qmail-inject
qmail: new msg 53 qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666 qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
qmail: starting delivery 4: msg 53 to remote me@wherever qmail: starting delivery 4: msg 53 to remote me@wherever
qmail: status: local 0/10 remote 1/20
qmail: delivery 4: success: 1.2.3.4_accepted_message./... qmail: delivery 4: success: 1.2.3.4_accepted_message./...
qmail: status: local 0/10 remote 0/20
qmail: end msg 53 qmail: end msg 53
There will be a pause between ``starting delivery'' and ``success''; There will be a pause between ``starting delivery'' and ``success'';
SMTP is slow. Check that the message is in your mailbox on the other SMTP is slow. Check that the message is in your mailbox on the other
machine. machine.
15. Local-postmaster test: Send mail to postmaster, any capitalization. 15. Local-postmaster test: Send mail to postmaster, any capitalization.
% echo to: POSTmaster | /var/qmail/bin/qmail-inject % echo to: POSTmaster | /var/qmail/bin/qmail-inject
Look for the message in ~alias/Mailbox. Look for the message in the alias mailbox.
16. Double-bounce test: Send a message with a completely bad envelope. 16. Double-bounce test: Send a message with a completely bad envelope.
% /var/qmail/bin/qmail-inject -f nonexistent % /var/qmail/bin/qmail-inject -f nonexistent
To: unknownuser To: unknownuser
@ -96,7 +127,8 @@ How to test (steps 11-17 can be done before step 10):
This is a test. This is only a test. This is a test. This is only a test.
% %
(Use end-of-file, not dot, to end the message.) Look for the double (Use end-of-file, not dot, to end the message.) Look for the double
bounce in ~alias/Mailbox. bounce in the alias mailbox.
17. Group membership test: 17. Group membership test:
% cat > ~me/.qmail-groups % cat > ~me/.qmail-groups
|groups >> MYGROUPS; exit 0 |groups >> MYGROUPS; exit 0
@ -104,6 +136,7 @@ How to test (steps 11-17 can be done before step 10):
% cat ~me/MYGROUPS % cat ~me/MYGROUPS
MYGROUPS will show your normal gid and nothing else. (Under Solaris, MYGROUPS will show your normal gid and nothing else. (Under Solaris,
make sure to use /usr/ucb/groups; /usr/bin/groups is broken.) make sure to use /usr/ucb/groups; /usr/bin/groups is broken.)
18. SMTP server test: Forge some mail locally via SMTP. 18. SMTP server test: Forge some mail locally via SMTP.
% telnet 127.0.0.1 25 % telnet 127.0.0.1 25
Trying 127.0.0.1... Trying 127.0.0.1...
@ -130,16 +163,20 @@ How to test (steps 11-17 can be done before step 10):
Connection closed by foreign host. Connection closed by foreign host.
% %
Look for the message in your mailbox. Look for the message in your mailbox.
19. Remote-local test: Send yourself some mail from another machine. 19. Remote-local test: Send yourself some mail from another machine.
20. Remote-error test: I think you can figure this one out. 20. Remote-error test: I think you can figure this one out.
21. UA test: Try sending mail, first to a local account, then to a 21. UA test: Try sending mail, first to a local account, then to a
remote account, with your normal user agent. remote account, with your normal user agent.
22. Remote-postmaster test: Send mail from another machine to 22. Remote-postmaster test: Send mail from another machine to
PoStMaStEr@domain. Look for the message in ~alias/Mailbox. PoStMaStEr@domain. Look for the message in the alias mailbox.
That's it! To report success: That's it! To report success:
% ( echo 'First M. Last'; cat `cat SYSDEPS` ) \ % ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
| mail djb-qst@koobera.math.uic.edu | mail djb-qst@cr.yp.to
Replace First M. Last with your name. If you have questions about qmail, Replace First M. Last with your name. If you have questions about qmail,
contact qmail@pobox.com. join the qmail mailing list; see http://pobox.com/~djb/qmail.html.

View file

@ -1 +1 @@
qmail 1.01 qmail 1.02

11
binm1+df.sh Normal file
View file

@ -0,0 +1,11 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using dot-forward to support sendmail-style ~/.forward files.
# Using binmail to deliver messages to /var/spool/mail/$USER by default.
# Using BSD 4.4 binmail interface: /usr/libexec/mail.local -r
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start '|dot-forward .forward
|preline -f /usr/libexec/mail.local -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
splogger qmail

10
binm1.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using binmail to deliver messages to /var/spool/mail/$USER by default.
# Using BSD 4.4 binmail interface: /usr/libexec/mail.local -r
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start \
'|preline -f /usr/libexec/mail.local -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
splogger qmail

11
binm2+df.sh Normal file
View file

@ -0,0 +1,11 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using dot-forward to support sendmail-style ~/.forward files.
# Using binmail to deliver messages to /var/spool/mail/$USER by default.
# Using SVR4 binmail interface: /bin/mail -r
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start '|dot-forward .forward
|preline -f /bin/mail -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
splogger qmail

10
binm2.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using binmail to deliver messages to /var/spool/mail/$USER by default.
# Using SVR4 binmail interface: /bin/mail -r
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start \
'|preline -f /bin/mail -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
splogger qmail

11
binm3+df.sh Normal file
View file

@ -0,0 +1,11 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using dot-forward to support sendmail-style ~/.forward files.
# Using binmail to deliver messages to /var/spool/mail/$USER by default.
# Using V7 binmail interface: /bin/mail -f
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start '|dot-forward .forward
|preline -f /bin/mail -f "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
splogger qmail

10
binm3.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using binmail to deliver messages to /var/spool/mail/$USER by default.
# Using V7 binmail interface: /bin/mail -f
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start \
'|preline -f /bin/mail -f "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
splogger qmail

40
commands.c Normal file
View file

@ -0,0 +1,40 @@
#include "commands.h"
#include "substdio.h"
#include "stralloc.h"
#include "str.h"
#include "case.h"
static stralloc cmd = {0};
int commands(ss,c)
substdio *ss;
struct commands *c;
{
int i;
char *arg;
for (;;) {
if (!stralloc_copys(&cmd,"")) return -1;
for (;;) {
if (!stralloc_readyplus(&cmd,1)) return -1;
i = substdio_get(ss,cmd.s + cmd.len,1);
if (i != 1) return i;
if (cmd.s[cmd.len] == '\n') break;
++cmd.len;
}
if (cmd.len > 0) if (cmd.s[cmd.len - 1] == '\r') --cmd.len;
cmd.s[cmd.len] = 0;
i = str_chr(cmd.s,' ');
arg = cmd.s + i;
while (*arg == ' ') ++arg;
cmd.s[i] = 0;
for (i = 0;c[i].text;++i) if (case_equals(c[i].text,cmd.s)) break;
c[i].fun(arg);
if (c[i].flush) c[i].flush();
}
}

12
commands.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef COMMANDS_H
#define COMMANDS_H
struct commands {
char *text;
void (*fun)();
void (*flush)();
} ;
extern int commands();
#endif

View file

@ -48,6 +48,13 @@ it is not safe for
.I program .I program
to fork a child that to fork a child that
reads the message in the background. reads the message in the background.
.B WARNING:
If you create a
.B .qmail
file to enable
.BR condredirect ,
make sure to also add a line specifying delivery to your normal mailbox.
.SH "SEE ALSO" .SH "SEE ALSO"
dot-qmail(5), dot-qmail(5),
qmail-command(8), qmail-command(8),

View file

@ -7,15 +7,11 @@
#include "wait.h" #include "wait.h"
#include "seek.h" #include "seek.h"
#include "qmail.h" #include "qmail.h"
#include "stralloc.h" #include "strerr.h"
#include "subfd.h"
#include "substdio.h" #include "substdio.h"
#include "fmt.h"
void die_success() { _exit(0); } #define FATAL "condredirect: fatal: "
void die_99() { _exit(99); }
void die_perm(s) char *s; { substdio_putsflush(subfderr,s); _exit(100); }
void die_temp(s) char *s; { substdio_putsflush(subfderr,s); _exit(111); }
void die_nomem() { die_temp("condredirect: fatal: out of memory\n"); }
struct qmail qqt; struct qmail qqt;
@ -25,10 +21,12 @@ int mywrite(fd,buf,len) int fd; char *buf; int len;
return len; return len;
} }
substdio ssin;
substdio ssout;
char inbuf[SUBSTDIO_INSIZE]; char inbuf[SUBSTDIO_INSIZE];
char outbuf[16]; char outbuf[1];
substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf);
char num[FMT_ULONG];
void main(argc,argv) void main(argc,argv)
int argc; int argc;
@ -38,50 +36,50 @@ char **argv;
char *dtline; char *dtline;
int pid; int pid;
int wstat; int wstat;
char *qqx;
if (!argv[1] || !argv[2]) if (!argv[1] || !argv[2])
die_perm("condredirect: usage: condredirect newaddress program arg ...\n"); strerr_die1x(100,"condredirect: usage: condredirect newaddress program [ arg ... ]");
switch(pid = fork()) pid = fork();
{ if (pid == -1)
case -1: die_temp("condredirect: fatal: unable to fork\n"); strerr_die2sys(111,FATAL,"unable to fork: ");
case 0: if (pid == 0) {
execvp(argv[2],argv + 2); execvp(argv[2],argv + 2);
if (error_temp(errno)) _exit(111); if (error_temp(errno)) _exit(111);
_exit(100); _exit(100);
} }
if (wait_pid(&wstat,pid) != pid) if (wait_pid(&wstat,pid) == -1)
die_perm("condredirect: fatal: internal bug\n"); strerr_die2x(111,FATAL,"wait failed");
if (wait_crashed(wstat)) die_temp("condredirect: fatal: child crashed\n"); if (wait_crashed(wstat))
switch(wait_exitcode(wstat)) strerr_die2x(111,FATAL,"child crashed");
{ switch(wait_exitcode(wstat)) {
case 0: break; case 0: break;
case 111: die_temp("condredirect: fatal: temporary child error\n"); case 111: strerr_die2x(111,FATAL,"temporary child error");
default: die_success(); default: _exit(0);
} }
if (seek_begin(0) == -1) die_temp("condredirect: fatal: unable to rewind\n"); if (seek_begin(0) == -1)
strerr_die2sys(111,FATAL,"unable to rewind: ");
sig_pipeignore(); sig_pipeignore();
sender = env_get("SENDER"); sender = env_get("SENDER");
if (!sender) die_perm("condredirect: fatal: SENDER not set\n"); if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
dtline = env_get("DTLINE"); dtline = env_get("DTLINE");
if (!dtline) die_perm("condredirect: fatal: DTLINE not set\n"); if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set");
if (qmail_open(&qqt) == -1) die_temp("condredirect: fatal: unable to fork\n"); if (qmail_open(&qqt) == -1)
strerr_die2sys(111,FATAL,"unable to fork: ");
qmail_puts(&qqt,dtline); qmail_puts(&qqt,dtline);
substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf));
if (substdio_copy(&ssout,&ssin) != 0) if (substdio_copy(&ssout,&ssin) != 0)
die_temp("condredirect: fatal: error while reading message\n"); strerr_die2sys(111,FATAL,"unable to read message: ");
substdio_flush(&ssout); substdio_flush(&ssout);
num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
qmail_from(&qqt,sender); qmail_from(&qqt,sender);
qmail_to(&qqt,argv[1]); qmail_to(&qqt,argv[1]);
switch(qmail_close(&qqt)) qqx = qmail_close(&qqt);
{ if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
case 0: die_99(); strerr_die2x(99,"condredirect: qp ",num);
case QMAIL_TOOLONG: die_perm("condredirect: fatal: permanent qmail-queue error\n");
default: die_temp("condredirect: fatal: temporary qmail-queue error\n");
}
} }

View file

@ -2,3 +2,10 @@
This is the qmail home directory. It must be a local directory, not This is the qmail home directory. It must be a local directory, not
shared among machines. This is where qmail queues all mail messages. shared among machines. This is where qmail queues all mail messages.
The queue (except for bounce message contents) is crashproof, if the
filesystem guarantees that single-byte writes are atomic and that
directory operations are synchronous. These guarantees are provided by
fixed-block filesystems such as UFS and by journaling filesystems. Under
Linux, make sure that all mail-handling filesystems are mounted with
synchronous metadata.

30
config-fast.sh Normal file
View file

@ -0,0 +1,30 @@
fqdn="$1"
echo Your fully qualified host name is "$fqdn".
echo Putting "$fqdn" into control/me...
echo "$fqdn" > QMAIL/control/me
chmod 644 QMAIL/control/me
( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | (
read ddom
echo Putting "$ddom" into control/defaultdomain...
echo "$ddom" > QMAIL/control/defaultdomain
chmod 644 QMAIL/control/defaultdomain
) )
( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | (
read pdom
echo Putting "$pdom" into control/plusdomain...
echo "$pdom" > QMAIL/control/plusdomain
chmod 644 QMAIL/control/plusdomain
) )
echo Putting "$fqdn" into control/locals...
echo "$fqdn" >> QMAIL/control/locals
chmod 644 QMAIL/control/locals
echo Putting "$fqdn" into control/rcpthosts...
echo "$fqdn" >> QMAIL/control/rcpthosts
chmod 644 QMAIL/control/rcpthosts
echo "Now qmail will refuse to accept SMTP messages except to $fqdn."
echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!'

View file

@ -9,8 +9,7 @@ int len;
unsigned char ch; unsigned char ch;
constmap_hash h; constmap_hash h;
h = 5381; h = 5381;
while (len > 0) while (len > 0) {
{
ch = *s++ - 'A'; ch = *s++ - 'A';
if (ch <= 'Z' - 'A') ch += 'a' - 'A'; if (ch <= 'Z' - 'A') ch += 'a' - 'A';
h = ((h << 5) + h) ^ ch; h = ((h << 5) + h) ^ ch;
@ -28,8 +27,7 @@ int len;
int pos; int pos;
h = hash(s,len); h = hash(s,len);
pos = cm->first[h & cm->mask]; pos = cm->first[h & cm->mask];
while (pos != -1) while (pos != -1) {
{
if (h == cm->hash[pos]) if (h == cm->hash[pos])
if (len == cm->inputlen[pos]) if (len == cm->inputlen[pos])
if (!case_diffb(cm->input[pos],len,s)) if (!case_diffb(cm->input[pos],len,s))
@ -59,30 +57,23 @@ int flagcolon;
cm->mask = h - 1; cm->mask = h - 1;
cm->first = (int *) alloc(sizeof(int) * h); cm->first = (int *) alloc(sizeof(int) * h);
if (cm->first) if (cm->first) {
{
cm->input = (char **) alloc(sizeof(char *) * cm->num); cm->input = (char **) alloc(sizeof(char *) * cm->num);
if (cm->input) if (cm->input) {
{
cm->inputlen = (int *) alloc(sizeof(int) * cm->num); cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
if (cm->inputlen) if (cm->inputlen) {
{
cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num); cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
if (cm->hash) if (cm->hash) {
{
cm->next = (int *) alloc(sizeof(int) * cm->num); cm->next = (int *) alloc(sizeof(int) * cm->num);
if (cm->next) if (cm->next) {
{
for (h = 0;h <= cm->mask;++h) for (h = 0;h <= cm->mask;++h)
cm->first[h] = -1; cm->first[h] = -1;
pos = 0; pos = 0;
i = 0; i = 0;
for (j = 0;j < len;++j) for (j = 0;j < len;++j)
if (!s[j]) if (!s[j]) {
{
k = j - i; k = j - i;
if (flagcolon) if (flagcolon) {
{
for (k = i;k < j;++k) for (k = i;k < j;++k)
if (s[k] == ':') if (s[k] == ':')
break; break;

View file

@ -3,8 +3,7 @@
typedef unsigned long constmap_hash; typedef unsigned long constmap_hash;
struct constmap struct constmap {
{
int num; int num;
constmap_hash mask; constmap_hash mask;
constmap_hash *hash; constmap_hash *hash;
@ -12,8 +11,7 @@ struct constmap
int *next; int *next;
char **input; char **input;
int *inputlen; int *inputlen;
} } ;
;
extern int constmap_init(); extern int constmap_init();
extern void constmap_free(); extern void constmap_free();

View file

@ -79,7 +79,8 @@ char *fn;
case 0: return 0; case 0: return 0;
case -1: return -1; case -1: return -1;
} }
if (scan_nbblong(line.s,line.len,10,0,&u) == 0) return 0; if (!stralloc_0(&line)) return -1;
if (!scan_ulong(line.s,&u)) return 0;
*i = u; *i = u;
return 1; return 1;
} }

View file

@ -45,7 +45,7 @@ ignores the line.
A program line begins with a vertical bar: A program line begins with a vertical bar:
.EX .EX
|/usr/ucb/vacation djb |preline /usr/ucb/vacation djb
.EE .EE
.B qmail-local .B qmail-local
@ -173,10 +173,10 @@ If
is completely empty (0 bytes long), or does not exist, is completely empty (0 bytes long), or does not exist,
.B qmail-local .B qmail-local
follows the follows the
.I aliasempty .I defaultdelivery
instructions set by your system administrator; instructions set by your system administrator;
normally normally
.I aliasempty .I defaultdelivery
is is
.BR ./Mailbox , .BR ./Mailbox ,
so so
@ -224,7 +224,7 @@ It's a good idea to test your new
file as follows: file as follows:
.EX .EX
qmail-local -n $USER $HOME $USER '' '' '' '' qmail-local -n $USER ~ $USER '' '' '' '' ./Mailbox
.EE .EE
.SH "EXTENSION ADDRESSES" .SH "EXTENSION ADDRESSES"
In the In the
@ -274,7 +274,7 @@ If
is completely empty, is completely empty,
.B qmail-local .B qmail-local
follows the follows the
.I aliasempty .I defaultdelivery
instructions set by your system administrator. instructions set by your system administrator.
If If

View file

@ -3,14 +3,13 @@
#include "exit.h" #include "exit.h"
#include "env.h" #include "env.h"
#include "qmail.h" #include "qmail.h"
#include "stralloc.h" #include "strerr.h"
#include "subfd.h"
#include "substdio.h" #include "substdio.h"
#include "fmt.h"
void die_success() { _exit(0); } #define FATAL "forward: fatal: "
void die_perm(s) char *s; { substdio_putsflush(subfderr,s); _exit(100); }
void die_temp(s) char *s; { substdio_putsflush(subfderr,s); _exit(111); } void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
void die_nomem() { die_temp("forward: fatal: out of memory\n"); }
struct qmail qqt; struct qmail qqt;
@ -20,10 +19,12 @@ int mywrite(fd,buf,len) int fd; char *buf; int len;
return len; return len;
} }
substdio ssin;
substdio ssout;
char inbuf[SUBSTDIO_INSIZE]; char inbuf[SUBSTDIO_INSIZE];
char outbuf[16]; char outbuf[1];
substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf);
char num[FMT_ULONG];
void main(argc,argv) void main(argc,argv)
int argc; int argc;
@ -31,28 +32,29 @@ char **argv;
{ {
char *sender; char *sender;
char *dtline; char *dtline;
char *qqx;
sig_pipeignore(); sig_pipeignore();
sender = env_get("NEWSENDER"); sender = env_get("NEWSENDER");
if (!sender) die_perm("forward: fatal: NEWSENDER not set\n"); if (!sender)
strerr_die2x(100,FATAL,"NEWSENDER not set");
dtline = env_get("DTLINE"); dtline = env_get("DTLINE");
if (!dtline) die_perm("forward: fatal: DTLINE not set\n"); if (!dtline)
strerr_die2x(100,FATAL,"DTLINE not set");
if (qmail_open(&qqt) == -1) die_temp("forward: fatal: unable to fork\n"); if (qmail_open(&qqt) == -1)
strerr_die2sys(111,FATAL,"unable to fork: ");
qmail_puts(&qqt,dtline); qmail_puts(&qqt,dtline);
substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf));
if (substdio_copy(&ssout,&ssin) != 0) if (substdio_copy(&ssout,&ssin) != 0)
die_temp("forward: fatal: error while reading message\n"); strerr_die2sys(111,FATAL,"unable to read message: ");
substdio_flush(&ssout); substdio_flush(&ssout);
num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
qmail_from(&qqt,sender); qmail_from(&qqt,sender);
while (*++argv) qmail_to(&qqt,*argv); while (*++argv) qmail_to(&qqt,*argv);
switch(qmail_close(&qqt)) qqx = qmail_close(&qqt);
{ if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
case 0: die_success(); strerr_die2x(0,"forward: qp ",num);
case QMAIL_TOOLONG: die_perm("forward: fatal: permanent qmail-queue error\n");
default: die_temp("forward: fatal: temporary qmail-queue error\n");
}
} }

View file

@ -29,6 +29,7 @@ static char *(hname[]) = {
, "content-type" , "content-type"
, "content-transfer-encoding" , "content-transfer-encoding"
, "notice-requested-upon-delivery-to" , "notice-requested-upon-delivery-to"
, "mail-followup-to"
, 0 , 0
}; };

View file

@ -32,6 +32,7 @@ extern int hfield_valid();
#define H_CONTENTTYPE 25 #define H_CONTENTTYPE 25
#define H_CONTENTTRANSFERENCODING 26 #define H_CONTENTTRANSFERENCODING 26
#define H_NOTICEREQUESTEDUPONDELIVERYTO 27 #define H_NOTICEREQUESTEDUPONDELIVERYTO 27
#define H_NUM 28 #define H_MAILFOLLOWUPTO 28
#define H_NUM 29
#endif #endif

9
home+df.sh Normal file
View file

@ -0,0 +1,9 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using dot-forward to support sendmail-style ~/.forward files.
# Using qmail-local to deliver messages to ~/Mailbox by default.
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start '|dot-forward .forward
./Mailbox' splogger qmail

7
home.sh Normal file
View file

@ -0,0 +1,7 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using qmail-local to deliver messages to ~/Mailbox by default.
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start ./Mailbox splogger qmail

View file

@ -209,7 +209,7 @@ or rename
as as
.BR cur/\fIunique:info . .BR cur/\fIunique:info .
See See
.B http://pobox.com/~djb/maildir.html .B http://pobox.com/~djb/proto/maildir.html
for the meaning of for the meaning of
.IR info . .IR info .

View file

@ -1,22 +1,24 @@
#include "subfd.h" #include "strerr.h"
#include "substdio.h"
#include "error.h"
#include "exit.h" #include "exit.h"
void die(s) char *s; { substdio_putsflush(subfderr,s); _exit(111); } #define FATAL "maildirmake: fatal: "
void main(argc,argv) void main(argc,argv)
int argc; int argc;
char **argv; char **argv;
{ {
umask(077); umask(077);
if (!argv[1]) die("usage: maildirmake name\n"); if (!argv[1])
if (mkdir(argv[1],0700)) strerr_die1x(100,"maildirmake: usage: maildirmake name");
if (errno == error_exist) die("fatal: directory already exists\n"); if (mkdir(argv[1],0700) == -1)
else die("fatal: unable to mkdir\n"); strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],": ");
if (chdir(argv[1])) die("fatal: unable to chdir\n"); if (chdir(argv[1]) == -1)
if (mkdir("tmp",0700)) die("fatal: unable to make tmp/ subdir\n"); strerr_die4sys(111,FATAL,"unable to chdir to ",argv[1],": ");
if (mkdir("new",0700)) die("fatal: unable to make new/ subdir\n"); if (mkdir("tmp",0700) == -1)
if (mkdir("cur",0700)) die("fatal: unable to make cur/ subdir\n"); strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/tmp: ");
if (mkdir("new",0700) == -1)
strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/new: ");
if (mkdir("cur",0700) == -1)
strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/cur: ");
_exit(0); _exit(0);
} }

View file

@ -2,8 +2,12 @@
#include <fcntl.h> #include <fcntl.h>
#include "ndelay.h" #include "ndelay.h"
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
int ndelay_on(fd) int ndelay_on(fd)
int fd; int fd;
{ {
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NDELAY); return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
} }

View file

@ -2,8 +2,12 @@
#include <fcntl.h> #include <fcntl.h>
#include "ndelay.h" #include "ndelay.h"
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
int ndelay_off(fd) int ndelay_off(fd)
int fd; int fd;
{ {
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NDELAY); return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
} }

View file

@ -5,11 +5,14 @@
#include "wait.h" #include "wait.h"
#include "fd.h" #include "fd.h"
#include "fmt.h" #include "fmt.h"
#include "strerr.h"
#include "substdio.h" #include "substdio.h"
#include "subfd.h" #include "subfd.h"
#include "readwrite.h" #include "readwrite.h"
#include "exit.h" #include "exit.h"
#define FATAL "predate: fatal: "
static char *montab[12] = { static char *montab[12] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
}; };
@ -34,30 +37,22 @@ char **argv;
sig_pipeignore(); sig_pipeignore();
if (!argv[1]) { if (!argv[1])
substdio_putsflush(subfderr,"predate: usage: predate child\n"); strerr_die1x(100,"predate: usage: predate child");
_exit(100);
}
if (pipe(pi) == -1) { if (pipe(pi) == -1)
substdio_putsflush(subfderr,"predate: fatal: unable to create pipe\n"); strerr_die2sys(111,FATAL,"unable to create pipe: ");
_exit(111);
}
switch(pid = fork()) { switch(pid = fork()) {
case -1: case -1:
substdio_putsflush(subfderr,"predate: fatal: unable to fork\n"); strerr_die2sys(111,FATAL,"unable to fork: ");
_exit(111);
case 0: case 0:
close(pi[1]); close(pi[1]);
if (fd_move(0,pi[0]) == -1) { if (fd_move(0,pi[0]) == -1)
substdio_putsflush(subfderr,"predate: fatal: unable to set up fds\n"); strerr_die2sys(111,FATAL,"unable to set up fds: ");
_exit(111);
}
sig_pipedefault(); sig_pipedefault();
execvp(argv[1],argv + 1); execvp(argv[1],argv + 1);
substdio_putsflush(subfderr,"predate: fatal: unable to exec\n"); strerr_die4sys(111,FATAL,"unable to run ",argv[1],": ");
_exit(111);
} }
close(pi[0]); close(pi[0]);
substdio_fdbuf(&ss,write,pi[1],outbuf,sizeof(outbuf)); substdio_fdbuf(&ss,write,pi[1],outbuf,sizeof(outbuf));
@ -113,13 +108,9 @@ char **argv;
substdio_flush(&ss); substdio_flush(&ss);
close(pi[1]); close(pi[1]);
if (wait_pid(&wstat,pid) == -1) { if (wait_pid(&wstat,pid) == -1)
substdio_putsflush(subfderr,"predate: fatal: wait failed\n"); strerr_die2sys(111,FATAL,"wait failed: ");
_exit(111); if (wait_crashed(wstat))
} strerr_die2x(111,FATAL,"child crashed");
if (wait_crashed(wstat)) {
substdio_putsflush(subfderr,"predate: fatal: child crashed\n");
_exit(111);
}
_exit(wait_exitcode(wstat)); _exit(wait_exitcode(wstat));
} }

View file

@ -1,7 +1,7 @@
#include "fd.h" #include "fd.h"
#include "sgetopt.h" #include "sgetopt.h"
#include "readwrite.h" #include "readwrite.h"
#include "subfd.h" #include "strerr.h"
#include "substdio.h" #include "substdio.h"
#include "exit.h" #include "exit.h"
#include "fork.h" #include "fork.h"
@ -10,20 +10,21 @@
#include "sig.h" #include "sig.h"
#include "error.h" #include "error.h"
void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); } #define FATAL "preline: fatal: "
void die_usage() { die(100,"preline: fatal: incorrect usage\n"); }
void die_temp() { die(111,"preline: fatal: temporary problem\n"); } void die_usage()
void die_read() { die(111,"preline: fatal: unable to read message\n"); } {
void die_badcmd() { die(100,"preline: fatal: command not found\n"); } strerr_die1x(100,"preline: usage: preline cmd [ arg ... ]");
}
int flagufline = 1; char *ufline; int flagufline = 1; char *ufline;
int flagrpline = 1; char *rpline; int flagrpline = 1; char *rpline;
int flagdtline = 1; char *dtline; int flagdtline = 1; char *dtline;
substdio ssout;
char outbuf[SUBSTDIO_OUTSIZE]; char outbuf[SUBSTDIO_OUTSIZE];
substdio ssin;
char inbuf[SUBSTDIO_INSIZE]; char inbuf[SUBSTDIO_INSIZE];
substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof outbuf);
substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
void main(argc,argv) void main(argc,argv)
int argc; int argc;
@ -41,47 +42,49 @@ char **argv;
if (!(dtline = env_get("DTLINE"))) die_usage(); if (!(dtline = env_get("DTLINE"))) die_usage();
while ((opt = getopt(argc,argv,"frdFRD")) != opteof) while ((opt = getopt(argc,argv,"frdFRD")) != opteof)
switch(opt) switch(opt) {
{
case 'f': flagufline = 0; break; case 'f': flagufline = 0; break;
case 'r': flagrpline = 0; break; case 'r': flagrpline = 0; break;
case 'd': flagdtline = 0; break; case 'd': flagdtline = 0; break;
case 'F': flagufline = 1; break; case 'F': flagufline = 1; break;
case 'R': flagrpline = 1; break; case 'R': flagrpline = 1; break;
case 'D': flagdtline = 1; break; case 'D': flagdtline = 1; break;
default: default: die_usage();
_exit(100);
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (!*argv) die_usage(); if (!*argv) die_usage();
if (pipe(pi) == -1) die_temp(); if (pipe(pi) == -1)
strerr_die2sys(111,FATAL,"unable to create pipe: ");
switch(pid = fork()) pid = fork();
{ if (pid == -1)
case -1: strerr_die2sys(111,FATAL,"unable to fork: ");
die_temp();
case 0: if (pid == 0) {
close(pi[1]); close(pi[1]);
if (fd_move(0,pi[0])) die_temp(); if (fd_move(0,pi[0]) == -1)
strerr_die2sys(111,FATAL,"unable to set up fds: ");
sig_pipedefault(); sig_pipedefault();
execvp(*argv,argv); execvp(*argv,argv);
if (error_temp(errno)) die_temp(); strerr_die4sys(error_temp(errno) ? 111 : 100,FATAL,"unable to run ",*argv,": ");
die_badcmd();
} }
close(pi[0]); close(pi[0]);
if (fd_move(1,pi[1]) == -1)
strerr_die2sys(111,FATAL,"unable to set up fds: ");
substdio_fdbuf(&ssout,write,pi[1],outbuf,sizeof(outbuf));
substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
if (flagufline) substdio_bputs(&ssout,ufline); if (flagufline) substdio_bputs(&ssout,ufline);
if (flagrpline) substdio_bputs(&ssout,rpline); if (flagrpline) substdio_bputs(&ssout,rpline);
if (flagdtline) substdio_bputs(&ssout,dtline); if (flagdtline) substdio_bputs(&ssout,dtline);
if (substdio_copy(&ssout,&ssin) == -2) die_read(); if (substdio_copy(&ssout,&ssin) != 0)
strerr_die2sys(111,FATAL,"unable to copy input: ");
substdio_flush(&ssout); substdio_flush(&ssout);
close(pi[1]); close(1);
if (wait_pid(&wstat,pid) == -1) die_temp(); if (wait_pid(&wstat,pid) == -1)
if (wait_crashed(wstat)) die_temp(); strerr_die2sys(111,FATAL,"wait failed: ");
if (wait_crashed(wstat))
strerr_die2x(111,FATAL,"child crashed");
_exit(wait_exitcode(wstat)); _exit(wait_exitcode(wstat));
} }

9
proc+df.sh Normal file
View file

@ -0,0 +1,9 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using dot-forward to support sendmail-style ~/.forward files.
# Using procmail to deliver messages to /var/spool/mail/$USER by default.
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start '|dot-forward .forward
|preline procmail' splogger qmail

7
proc.sh Normal file
View file

@ -0,0 +1,7 @@
#!/bin/sh
# Using splogger to send the log through syslog.
# Using procmail to deliver messages to /var/spool/mail/$USER by default.
exec env - PATH="QMAIL/bin:$PATH" \
qmail-start '|preline procmail' splogger qmail

112
qlist.1
View file

@ -1,112 +0,0 @@
.TH qlist 1
.SH NAME
qlist \- handle mailing list subscription requests
.SH SYNOPSIS
in
.BR .qmail-\fIlist-request :
.br
.B |qlist
.I user-list@host
.I user-list-request@host
.br
.B .qmail-\fIlist
.B .qmail-\fIlist-request
.B .qtemp-\fIlist
.br
.I owner
[
.I moreinfo
]
.br
(all on one line)
.SH DESCRIPTION
.B qlist
manages a
.B qmail
mailing list.
When
.B qlist
receives a message,
it looks through the body of the message for commands.
.B WARNING:
.B qlist
looks for a command only at the beginning of a line.
Exception:
.B qlist
also looks at
.B Subject
lines.
.B qlist
supports two commands.
.B SUBSCRIBE
adds a new subscription to the mailing list;
.B UNSUBSCRIBE
removes a subscription.
.B qlist
looks for the subscription address in the
.BR Reply-To ,
.BR From ,
or
.BR Return-Path
fields in the message.
.B qlist
inserts an acknowledgment of each action it took,
along with general help instructions, into the message.
It then forwards the message to the subscription address,
with a copy to
.IR owner .
.BR qlist 's
general help instructions identify
.I user-list@host
as the address of the mailing list,
.I user-list-request@host
as the address of
.B qlist
itself, and
.I owner
as the address
of the mailing list owner.
If
.I moreinfo
is supplied,
it is inserted into the middle of the instructions,
surrounded by blank lines.
.B qlist
maintains its address list in
.BR .qmail-\fIlist ,
so mail to
.I user-list
will be forwarded to each subscription address.
While
.B qlist
is editing
.BR .qmail-\fIlist ,
it locks
.BR .qmail-\fIlist-request .
It uses
.B .qtemp-\fIlist
as a temporary file.
Note that
.B qlist
only manipulates lines beginning with an ampersand;
if you manually add an address without an ampersand,
it cannot be removed by
.BR qlist .
.B qlist
automatically sets the execute bit on
.BR qmail-\fIlist ,
so
.B qmail-local
will ignore any program or file instructions in
.BR qmail-\fIlist .
.SH "SEE ALSO"
dot-qmail(5),
envelopes(5),
qmail-queue(8)

333
qlist.c
View file

@ -1,333 +0,0 @@
#include "sig.h"
#include "readwrite.h"
#include "substdio.h"
#include "stralloc.h"
#include "subfd.h"
#include "getln.h"
#include "alloc.h"
#include "str.h"
#include "env.h"
#include "hfield.h"
#include "case.h"
#include "token822.h"
#include "error.h"
#include "gen_alloc.h"
#include "gen_allocdefs.h"
#include "headerbody.h"
#include "exit.h"
#include "open.h"
#include "lock.h"
#include "qmail.h"
#define ADDRLIMIT 100
void die() { _exit(100); }
void die_temp() { _exit(111); }
void die_nomem() {
substdio_putsflush(subfderr,"qlist: fatal: out of memory\n"); die_temp(); }
void die_fork() {
substdio_putsflush(subfderr,"qlist: fatal: unable to fork\n"); die_temp(); }
void die_nolock() {
substdio_putsflush(subfderr,"qlist: fatal: unable to open lock file\n"); die_temp(); }
void die_boing() {
substdio_putsflush(subfderr,"qlist: fatal: I don't reply to bounces\n"); die(); }
void die_badaddr() {
substdio_putsflush(subfderr,"qlist: fatal: sorry, I'm not allowed to use that address\n"); die(); }
void die_qqperm() {
substdio_putsflush(subfderr,"qlist: fatal: permanent qmail-queue error\n"); die(); }
void die_qqtemp() {
substdio_putsflush(subfderr,"qlist: fatal: temporary qmail-queue error\n"); die_temp(); }
void die_usage() {
substdio_putsflush(subfderr,
"qlist: usage: qlist user-list@host user-list-request@host .qmail-list .qmail-list-request .qtemp-list owner [moreinfo]\n"); die(); }
void die_read() {
if (errno == error_nomem) die_nomem();
substdio_putsflush(subfderr,"qlist: fatal: read error\n"); die_temp(); }
void doordie(sa,r) stralloc *sa; int r; {
if (r == 1) return; if (r == -1) die_nomem();
substdio_putsflush(subfderr,"qlist: fatal: unable to parse this: ");
substdio_putflush(subfderr,sa->s,sa->len); die(); }
int subjectaction = 0;
int numcommands;
int fdlock;
struct qmail qqt;
char *target;
char *listathost;
char *requestathost;
char *qmaillist;
char *qmailrequest;
char *qtemplist;
char *owner;
char *moreinfo;
char *dtline;
char *returnpath;
stralloc safrom = {0};
stralloc sart = {0};
int rwfrom(addr) token822_alloc *addr; { token822_reverse(addr);
if (token822_unquote(&safrom,addr) != 1) die_nomem();
token822_reverse(addr); return 1; }
int rwrt(addr) token822_alloc *addr; { token822_reverse(addr);
if (token822_unquote(&sart,addr) != 1) die_nomem();
token822_reverse(addr); return 1; }
GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
static stralloc sauninit = {0}; saa savedh = {0};
void savedh_append(h) stralloc *h; {
if (!saa_readyplus(&savedh,1)) die_nomem(); savedh.sa[savedh.len] = sauninit;
if (!stralloc_copy(savedh.sa + savedh.len,h)) die_nomem(); ++savedh.len; }
void savedh_print() { int i; for (i = 0;i < savedh.len;++i)
qmail_put(&qqt,savedh.sa[i].s,savedh.sa[i].len); }
void finishheader()
{
int i;
if (sart.s)
{ if (!stralloc_0(&sart)) die_nomem(); target = sart.s; }
else if (safrom.s)
{ if (!stralloc_0(&safrom)) die_nomem(); target = safrom.s; }
else
target = returnpath;
for (i = 0;target[i];++i)
if (target[i] == '\n')
die_badaddr();
if (i > ADDRLIMIT) die_badaddr();
if (str_equal(target,"")) die_boing();
if (str_equal(target,"#@[]")) die_boing();
if (qmail_open(&qqt) == -1) die_fork();
qmail_puts(&qqt,dtline);
savedh_print();
qmail_puts(&qqt,"\n***** Text inserted by ");
qmail_puts(&qqt,requestathost);
qmail_puts(&qqt,"\n\
*\n\
* Hi! This is the qlist program. I'm handling subscriptions for the\n\
* ");
qmail_puts(&qqt,listathost);
qmail_puts(&qqt," mailing list.\n\
*\n");
if (moreinfo)
{
qmail_puts(&qqt,"* ");
qmail_puts(&qqt,moreinfo);
qmail_puts(&qqt,"\n*\n");
}
qmail_puts(&qqt,"* My human owner is ");
qmail_puts(&qqt,owner);
qmail_puts(&qqt,".\n\
*\n\
* To the recipient: This message was sent to me on your behalf. (Your\n\
* address was listed in the Reply-To or From field.) For security,\n\
* I'm forwarding this message to you, along with my notes.\n\
*\n\
* Anyway, to subscribe, send me an empty message. To unsubscribe, send me\n\
* a message with the word UNSUBSCRIBE at the beginning of a line. Remember,\n\
* my address is ");
qmail_puts(&qqt,requestathost);
qmail_puts(&qqt,".\n\
*\n\
* Now I'll look for requests inside this message...\n\
*\n\
*****\n");
}
substdio subin; char subinbuf[SUBSTDIO_INSIZE];
substdio subout; char suboutbuf[SUBSTDIO_OUTSIZE];
stralloc subline = {0};
void subscribe(flagadd)
int flagadd;
{
int fdin;
int fdout;
int match;
int flagwasthere;
++numcommands;
if (lock_ex(fdlock) == -1) goto bad;
fdin = open_read(qmaillist);
if (fdin == -1) goto badlock;
fdout = open_trunc(qtemplist);
if (fdout == -1) goto badinlock;
if (chmod(qtemplist,0700) == -1) goto badoutinlock;
flagwasthere = 0;
substdio_fdbuf(&subin,read,fdin,subinbuf,sizeof(subinbuf));
substdio_fdbuf(&subout,write,fdout,suboutbuf,sizeof(suboutbuf));
for (;;)
{
if (getln(&subin,&subline,&match,'\n') == -1) goto badoutinlock;
if (!match) break; /* goodbye partial lines */
if (subline.len == str_len(target) + 2)
if (!str_diffn(subline.s + 1,target,subline.len - 2))
if (subline.s[0] == '&')
{
flagwasthere = 1;
if (!flagadd)
continue;
}
if (substdio_put(&subout,subline.s,subline.len) == -1) goto badoutinlock;
}
if (flagadd && !flagwasthere)
{
if (substdio_puts(&subout,"&") == -1) goto badoutinlock;
if (substdio_puts(&subout,target) == -1) goto badoutinlock;
if (substdio_puts(&subout,"\n") == -1) goto badoutinlock;
}
if (substdio_flush(&subout) == -1) goto badoutinlock;
close(fdout);
close(fdin);
if (rename(qtemplist,qmaillist) == -1) goto badlock;
if (chmod(qmaillist,0500) == -1) goto badlock;
lock_un(fdlock);
qmail_puts(&qqt,"***** Text inserted by ");
qmail_puts(&qqt,requestathost);
qmail_puts(&qqt,"\n*\n* ");
if (flagadd)
if (flagwasthere)
{
qmail_puts(&qqt,"Acknowledgment: ");
qmail_puts(&qqt,target);
qmail_puts(&qqt," was already a subscriber.\n");
}
else
{
qmail_puts(&qqt,"Acknowledgment: ");
qmail_puts(&qqt,target);
qmail_puts(&qqt," is now a subscriber.\n");
}
else
if (flagwasthere)
{
qmail_puts(&qqt,"Acknowledgment: ");
qmail_puts(&qqt,target);
qmail_puts(&qqt," is no longer a subscriber.\n");
}
else
{
qmail_puts(&qqt,"Hmmm, I don't see ");
qmail_puts(&qqt,target);
qmail_puts(&qqt," on the subscription list.\n* I'll let my owner know.\n");
}
qmail_puts(&qqt,"*\n*****\n");
return;
badoutinlock: close(fdout);
badinlock: close(fdin);
badlock: lock_un(fdlock);
bad:
qmail_puts(&qqt,"***** Text inserted by ");
qmail_puts(&qqt,requestathost);
qmail_puts(&qqt,"\n*\n\
* Oh no! Trouble making the new list. I'll let my owner know.\n\
*\n\
*****\n");
}
void dobody(h) stralloc *h;
{
qmail_put(&qqt,h->s,h->len);
if (case_starts(h->s,"subs")) subscribe(1);
if (case_starts(h->s,"unsu")) subscribe(0);
}
stralloc hfbuf = {0};
token822_alloc hfin = {0};
token822_alloc hfrewrite = {0};
token822_alloc hfaddr = {0};
void doheaderfield(h)
stralloc *h;
{
char *x;
switch(hfield_known(h->s,h->len))
{
case H_CONTENTLENGTH: /* SVR4 silliness */
case H_CONTENTTYPE:
case H_CONTENTTRANSFERENCODING: /* A-bombs 4.2.1.5.2 is idiotic */
return;
case H_FROM:
doordie(h,token822_parse(&hfin,h,&hfbuf));
doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwfrom));
break;
case H_REPLYTO:
doordie(h,token822_parse(&hfin,h,&hfbuf));
doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwrt));
break;
case H_SUBJECT:
x = h->s + hfield_skipname(h->s,h->len);
if (!case_diffb(x,4,"subs")) subjectaction = 1;
if (!case_diffb(x,4,"unsu")) subjectaction = 2;
break;
}
savedh_append(h);
}
void main(argc,argv)
int argc;
char **argv;
{
sig_pipeignore();
if (!(listathost = argv[1])) die_usage();
if (!(requestathost = argv[2])) die_usage();
if (!(qmaillist = argv[3])) die_usage();
if (!(qmailrequest = argv[4])) die_usage();
if (!(qtemplist = argv[5])) die_usage();
if (!(owner = argv[6])) die_usage();
moreinfo = argv[7];
if (!(returnpath = env_get("NEWSENDER"))) die_usage();
if (!(dtline = env_get("DTLINE"))) die_usage();
fdlock = open_append(qmailrequest);
if (fdlock == -1) die_nolock();
numcommands = 0;
if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1) die_read();
if (!numcommands)
{
qmail_puts(&qqt,"***** Text inserted by ");
qmail_puts(&qqt,requestathost);
qmail_puts(&qqt,"\n*\n* ");
if (subjectaction)
{
qmail_puts(&qqt,"\
Hmmm, no commands? Let me check the Subject line...\n*\n*****\n");
subscribe(subjectaction == 1);
}
else
{
qmail_puts(&qqt,"\
I didn't see any commands. I presume this is a subscription request.\n\
*\n*****\n");
subscribe(1);
}
}
qmail_from(&qqt,returnpath);
qmail_to(&qqt,owner);
qmail_to(&qqt,target);
switch(qmail_close(&qqt))
{
case 0: _exit(0);
case QMAIL_TOOLONG: die_qqperm();
default: die_qqtemp();
}
}

View file

@ -1 +0,0 @@
exec QMAIL/bin/qlist "$USERBREAK$1@$2" "$USERBREAK$1-request@$2" .qmail-"$1" .qmail-"$1"-request .qtemp-"$1" "$USERBREAK$1-owner" ${3+"$3"}

View file

@ -37,7 +37,7 @@ file, so it is not safe for
.I command .I command
to fork a child that to fork a child that
reads the message in the background while the parent exits. reads the message in the background while the parent exits.
.SH "EXIT CODES"
.IR command 's .IR command 's
exit codes are interpreted as follows: exit codes are interpreted as follows:
0 means that the delivery was successful; 0 means that the delivery was successful;
@ -48,15 +48,21 @@ should ignore all further delivery instructions;
100 means that the delivery failed permanently (hard error); 100 means that the delivery failed permanently (hard error);
111 means that the delivery failed but should be tried again 111 means that the delivery failed but should be tried again
in a little while (soft error). in a little while (soft error).
Currently 64, 65, 70, 76, 77, 78, and 112 are considered hard errors, Currently 64, 65, 70, 76, 77, 78, and 112 are considered hard errors,
and all other codes are considered soft errors, and all other codes are considered soft errors,
but but
.I command .I command
should avoid relying on this. should avoid relying on this.
.SH "ENVIRONMENT VARIABLES"
.B qmail-local .B qmail-local
supplies several useful environment variables to supplies several useful environment variables to
.IR command . .IR command .
.B WARNING:
These environment variables are not quoted.
They may contain special characters.
They are under the control of a possibly malicious remote user.
.B SENDER .B SENDER
is the envelope sender address. is the envelope sender address.
.B NEWSENDER .B NEWSENDER
@ -82,9 +88,22 @@ is the
part. part.
.B EXT .B EXT
is the is the
.B .qmail address extension,
extension,
.IR ext . .IR ext .
.B HOST2
is the portion of
.B HOST
preceding the last dot;
.B HOST3
is the portion of
.B HOST
preceding the second-to-last dot;
.B HOST4
is the portion of
.B HOST
preceding the third-to-last dot.
.B EXT2 .B EXT2
is the portion of is the portion of
.B EXT .B EXT
@ -95,6 +114,18 @@ following the second dash;
.B EXT4 .B EXT4
is the portion is the portion
following the third dash. following the third dash.
.B DEFAULT
is the portion
corresponding to the
.B default
part of the
.BR .qmail\- ...
file name;
.B DEFAULT
is not set if
the file name does not end with
.BR default .
.B DTLINE .B DTLINE
and and
.B RPLINE .B RPLINE
@ -112,11 +143,6 @@ line that
adds to adds to
.IR mbox -format .IR mbox -format
files. files.
.B WARNING:
These environment variables are not quoted.
They may contain special characters.
They are under the control of a possibly malicious remote user.
.SH "SEE ALSO" .SH "SEE ALSO"
dot-qmail(5), dot-qmail(5),
envelopes(5), envelopes(5),

View file

@ -23,6 +23,7 @@ in
.IR badmailfrom , .IR badmailfrom ,
.IR locals , .IR locals ,
.IR percenthack , .IR percenthack ,
.IR qmqpservers ,
.IR rcpthosts , .IR rcpthosts ,
.IR smtproutes , .IR smtproutes ,
and and
@ -46,6 +47,7 @@ control default used by
.I concurrencyremote \fR20 \fRqmail-send .I concurrencyremote \fR20 \fRqmail-send
.I defaultdomain \fIme \fRqmail-inject .I defaultdomain \fIme \fRqmail-inject
.I defaulthost \fIme \fRqmail-inject .I defaulthost \fIme \fRqmail-inject
.I databytes \fR0 \fRqmail-smtpd
.I doublebouncehost \fIme \fRqmail-send .I doublebouncehost \fIme \fRqmail-send
.I doublebounceto \fRpostmaster \fRqmail-send .I doublebounceto \fRpostmaster \fRqmail-send
.I envnoathost \fIme \fRqmail-send .I envnoathost \fIme \fRqmail-send
@ -53,11 +55,12 @@ control default used by
.I idhost \fIme \fRqmail-inject .I idhost \fIme \fRqmail-inject
.I localiphost \fIme \fRqmail-smtpd .I localiphost \fIme \fRqmail-smtpd
.I locals \fIme \fRqmail-send .I locals \fIme \fRqmail-send
.I morercpthosts \fR(none) \fRqmail-smtpd
.I percenthack \fR(none) \fRqmail-send .I percenthack \fR(none) \fRqmail-send
.I plusdomain \fIme \fRqmail-inject .I plusdomain \fIme \fRqmail-inject
.I qmqpservers \fR(none) \fRqmail-qmqpc
.I queuelifetime \fR604800 \fRqmail-send .I queuelifetime \fR604800 \fRqmail-send
.I rcpthosts \fR(none) \fRqmail-smtpd .I rcpthosts \fR(none) \fRqmail-smtpd
.I recipientmap \fR(none) \fRqmail-send
.I smtpgreeting \fIme \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd
.I smtproutes \fR(none) \fRqmail-remote .I smtproutes \fR(none) \fRqmail-remote
.I timeoutconnect \fR60 \fRqmail-remote .I timeoutconnect \fR60 \fRqmail-remote
@ -68,6 +71,7 @@ control default used by
.RE .RE
.SH "SEE ALSO" .SH "SEE ALSO"
qmail-inject(8), qmail-inject(8),
qmail-qmqpc(8),
qmail-remote(8), qmail-remote(8),
qmail-send(8), qmail-send(8),
qmail-showctl(8), qmail-showctl(8),

View file

@ -42,7 +42,12 @@ The operating system's
.B getpwnam .B getpwnam
function, which is at the heart of function, which is at the heart of
.BR qmail-getpw , .BR qmail-getpw ,
is inherently unreliable. is inherently unreliable:
it fails to distinguish between temporary errors and nonexistent users.
Future versions of
.B getpwnam
should return ETXTBSY to indicate temporary errors
and ESRCH to indicate nonexistent users.
.SH "RULES" .SH "RULES"
.B qmail-getpw .B qmail-getpw
considers an account in considers an account in

View file

@ -33,7 +33,9 @@ int userext()
byte_copy(username,extension - local,local); byte_copy(username,extension - local,local);
username[extension - local] = 0; username[extension - local] = 0;
case_lowers(username); case_lowers(username);
errno = 0;
pw = getpwnam(username); pw = getpwnam(username);
if (errno == error_txtbsy) _exit(QLX_SYS);
if (pw) if (pw)
if (pw->pw_uid) if (pw->pw_uid)
if (stat(pw->pw_dir,&st) == 0) { if (stat(pw->pw_dir,&st) == 0) {

View file

@ -239,7 +239,7 @@ does not create any
fields. fields.
.SH "RESENT MESSAGES" .SH "RESENT MESSAGES"
A message is A message is
.I forwarded .I resent
if it contains any of the following fields: if it contains any of the following fields:
.BR Resent-Sender , .BR Resent-Sender ,
.BR Resent-From , .BR Resent-From ,
@ -250,7 +250,7 @@ if it contains any of the following fields:
.BR Resent-Date , .BR Resent-Date ,
.BR Resent-Message-ID . .BR Resent-Message-ID .
If a message is forwarded, If a message is resent,
.B qmail-inject .B qmail-inject
changes its behavior as follows. changes its behavior as follows.

View file

@ -78,8 +78,42 @@ void main()
dir(auto_uido,"755","/man/man5"); dir(auto_uido,"755","/man/man5");
dir(auto_uido,"755","/man/man7"); dir(auto_uido,"755","/man/man7");
dir(auto_uido,"755","/man/man8"); dir(auto_uido,"755","/man/man8");
dir(auto_uido,"755","/doc");
dir(auto_uido,"755","/boot");
dir(auto_uida,"755","/alias"); copy(auto_uido,"755","/boot/","home");
copy(auto_uido,"755","/boot/","home+df");
copy(auto_uido,"755","/boot/","proc");
copy(auto_uido,"755","/boot/","proc+df");
copy(auto_uido,"755","/boot/","binm1");
copy(auto_uido,"755","/boot/","binm1+df");
copy(auto_uido,"755","/boot/","binm2");
copy(auto_uido,"755","/boot/","binm2+df");
copy(auto_uido,"755","/boot/","binm3");
copy(auto_uido,"755","/boot/","binm3+df");
copy(auto_uido,"644","/doc/","FAQ");
copy(auto_uido,"644","/doc/","UPGRADE");
copy(auto_uido,"644","/doc/","SENDMAIL");
copy(auto_uido,"644","/doc/","INSTALL");
copy(auto_uido,"644","/doc/","INSTALL.alias");
copy(auto_uido,"644","/doc/","INSTALL.boot");
copy(auto_uido,"644","/doc/","INSTALL.ctl");
copy(auto_uido,"644","/doc/","INSTALL.ids");
copy(auto_uido,"644","/doc/","INSTALL.maildir");
copy(auto_uido,"644","/doc/","INSTALL.mbox");
copy(auto_uido,"644","/doc/","INSTALL.vsm");
copy(auto_uido,"644","/doc/","PIC.local2alias");
copy(auto_uido,"644","/doc/","PIC.local2ext");
copy(auto_uido,"644","/doc/","PIC.local2local");
copy(auto_uido,"644","/doc/","PIC.local2rem");
copy(auto_uido,"644","/doc/","PIC.local2virt");
copy(auto_uido,"644","/doc/","PIC.nullclient");
copy(auto_uido,"644","/doc/","PIC.relaybad");
copy(auto_uido,"644","/doc/","PIC.relaygood");
copy(auto_uido,"644","/doc/","PIC.rem2local");
dir(auto_uida,"2755","/alias");
dir(auto_uidq,"750","/queue"); dir(auto_uidq,"750","/queue");
dir(auto_uidq,"700","/queue/pid"); dir(auto_uidq,"700","/queue/pid");
dir(auto_uidq,"700","/queue/intd"); dir(auto_uidq,"700","/queue/intd");
@ -115,6 +149,7 @@ void main()
copy(auto_uido,"711","/bin/","qmail-send"); copy(auto_uido,"711","/bin/","qmail-send");
copy(auto_uido,"711","/bin/","splogger"); copy(auto_uido,"711","/bin/","splogger");
copy(auto_uido,"700","/bin/","qmail-newu"); copy(auto_uido,"700","/bin/","qmail-newu");
copy(auto_uido,"700","/bin/","qmail-newmrh");
copy(auto_uido,"711","/bin/","qmail-pw2u"); copy(auto_uido,"711","/bin/","qmail-pw2u");
copy(auto_uido,"755","/bin/","qmail-inject"); copy(auto_uido,"755","/bin/","qmail-inject");
copy(auto_uido,"755","/bin/","predate"); copy(auto_uido,"755","/bin/","predate");
@ -124,14 +159,15 @@ void main()
copy(auto_uido,"755","/bin/","qmail-qread"); copy(auto_uido,"755","/bin/","qmail-qread");
copy(auto_uido,"755","/bin/","qmail-qstat"); copy(auto_uido,"755","/bin/","qmail-qstat");
copy(auto_uido,"755","/bin/","qmail-tcpto"); copy(auto_uido,"755","/bin/","qmail-tcpto");
copy(auto_uido,"755","/bin/","qmail-tcpok");
copy(auto_uido,"755","/bin/","qmail-pop3d"); copy(auto_uido,"755","/bin/","qmail-pop3d");
copy(auto_uido,"700","/bin/","qmail-popup"); copy(auto_uido,"711","/bin/","qmail-popup");
copy(auto_uido,"755","/bin/","qmail-qmqpc");
copy(auto_uido,"755","/bin/","qmail-qmqpd");
copy(auto_uido,"755","/bin/","qmail-qmtpd"); copy(auto_uido,"755","/bin/","qmail-qmtpd");
copy(auto_uido,"755","/bin/","qmail-smtpd"); copy(auto_uido,"755","/bin/","qmail-smtpd");
copy(auto_uido,"755","/bin/","sendmail"); copy(auto_uido,"755","/bin/","sendmail");
copy(auto_uido,"755","/bin/","tcp-env"); copy(auto_uido,"755","/bin/","tcp-env");
copy(auto_uido,"755","/bin/","qlist");
copy(auto_uido,"755","/bin/","qlist2");
copy(auto_uido,"755","/bin/","qreceipt"); copy(auto_uido,"755","/bin/","qreceipt");
copy(auto_uido,"755","/bin/","qsmhook"); copy(auto_uido,"755","/bin/","qsmhook");
copy(auto_uido,"755","/bin/","qbiff"); copy(auto_uido,"755","/bin/","qbiff");
@ -170,8 +206,6 @@ void main()
copy(auto_uido,"644","/man/cat7/","forgeries.0"); copy(auto_uido,"644","/man/cat7/","forgeries.0");
copy(auto_uido,"644","/man/man7/","qmail-limits.7"); copy(auto_uido,"644","/man/man7/","qmail-limits.7");
copy(auto_uido,"644","/man/cat7/","qmail-limits.0"); copy(auto_uido,"644","/man/cat7/","qmail-limits.0");
copy(auto_uido,"644","/man/man7/","qmail-upgrade.7");
copy(auto_uido,"644","/man/cat7/","qmail-upgrade.0");
copy(auto_uido,"644","/man/man7/","qmail.7"); copy(auto_uido,"644","/man/man7/","qmail.7");
copy(auto_uido,"644","/man/cat7/","qmail.0"); copy(auto_uido,"644","/man/cat7/","qmail.0");
@ -187,8 +221,6 @@ void main()
copy(auto_uido,"644","/man/cat1/","maildirwatch.0"); copy(auto_uido,"644","/man/cat1/","maildirwatch.0");
copy(auto_uido,"644","/man/man1/","mailsubj.1"); copy(auto_uido,"644","/man/man1/","mailsubj.1");
copy(auto_uido,"644","/man/cat1/","mailsubj.0"); copy(auto_uido,"644","/man/cat1/","mailsubj.0");
copy(auto_uido,"644","/man/man1/","qlist.1");
copy(auto_uido,"644","/man/cat1/","qlist.0");
copy(auto_uido,"644","/man/man1/","qreceipt.1"); copy(auto_uido,"644","/man/man1/","qreceipt.1");
copy(auto_uido,"644","/man/cat1/","qreceipt.0"); copy(auto_uido,"644","/man/cat1/","qreceipt.0");
copy(auto_uido,"644","/man/man1/","qbiff.1"); copy(auto_uido,"644","/man/man1/","qbiff.1");
@ -222,6 +254,8 @@ void main()
copy(auto_uido,"644","/man/cat8/","qmail-inject.0"); copy(auto_uido,"644","/man/cat8/","qmail-inject.0");
copy(auto_uido,"644","/man/man8/","qmail-showctl.8"); copy(auto_uido,"644","/man/man8/","qmail-showctl.8");
copy(auto_uido,"644","/man/cat8/","qmail-showctl.0"); copy(auto_uido,"644","/man/cat8/","qmail-showctl.0");
copy(auto_uido,"644","/man/man8/","qmail-newmrh.8");
copy(auto_uido,"644","/man/cat8/","qmail-newmrh.0");
copy(auto_uido,"644","/man/man8/","qmail-newu.8"); copy(auto_uido,"644","/man/man8/","qmail-newu.8");
copy(auto_uido,"644","/man/cat8/","qmail-newu.0"); copy(auto_uido,"644","/man/cat8/","qmail-newu.0");
copy(auto_uido,"644","/man/man8/","qmail-pw2u.8"); copy(auto_uido,"644","/man/man8/","qmail-pw2u.8");
@ -230,12 +264,18 @@ void main()
copy(auto_uido,"644","/man/cat8/","qmail-qread.0"); copy(auto_uido,"644","/man/cat8/","qmail-qread.0");
copy(auto_uido,"644","/man/man8/","qmail-qstat.8"); copy(auto_uido,"644","/man/man8/","qmail-qstat.8");
copy(auto_uido,"644","/man/cat8/","qmail-qstat.0"); copy(auto_uido,"644","/man/cat8/","qmail-qstat.0");
copy(auto_uido,"644","/man/man8/","qmail-tcpok.8");
copy(auto_uido,"644","/man/cat8/","qmail-tcpok.0");
copy(auto_uido,"644","/man/man8/","qmail-tcpto.8"); copy(auto_uido,"644","/man/man8/","qmail-tcpto.8");
copy(auto_uido,"644","/man/cat8/","qmail-tcpto.0"); copy(auto_uido,"644","/man/cat8/","qmail-tcpto.0");
copy(auto_uido,"644","/man/man8/","qmail-pop3d.8"); copy(auto_uido,"644","/man/man8/","qmail-pop3d.8");
copy(auto_uido,"644","/man/cat8/","qmail-pop3d.0"); copy(auto_uido,"644","/man/cat8/","qmail-pop3d.0");
copy(auto_uido,"644","/man/man8/","qmail-popup.8"); copy(auto_uido,"644","/man/man8/","qmail-popup.8");
copy(auto_uido,"644","/man/cat8/","qmail-popup.0"); copy(auto_uido,"644","/man/cat8/","qmail-popup.0");
copy(auto_uido,"644","/man/man8/","qmail-qmqpc.8");
copy(auto_uido,"644","/man/cat8/","qmail-qmqpc.0");
copy(auto_uido,"644","/man/man8/","qmail-qmqpd.8");
copy(auto_uido,"644","/man/cat8/","qmail-qmqpd.0");
copy(auto_uido,"644","/man/man8/","qmail-qmtpd.8"); copy(auto_uido,"644","/man/man8/","qmail-qmtpd.8");
copy(auto_uido,"644","/man/cat8/","qmail-qmtpd.0"); copy(auto_uido,"644","/man/cat8/","qmail-qmtpd.0");
copy(auto_uido,"644","/man/man8/","qmail-smtpd.8"); copy(auto_uido,"644","/man/man8/","qmail-smtpd.8");

View file

@ -76,6 +76,21 @@ and
letters described below. letters described below.
Bounces will be sent to this address. Bounces will be sent to this address.
If
.B QMAILMFTFILE
is set,
.B qmail-inject
reads a list of mailing list addresses,
one per line,
from that file.
If To+Cc includes one of those addresses (without regard to case),
.B qmail-inject
adds a Mail-Followup-To field
with all the To+Cc addresses.
.B qmail-inject
does not add Mail-Followup-To
to a message that already has one.
The The
.B QMAILINJECT .B QMAILINJECT
environment variable environment variable

View file

@ -21,6 +21,7 @@
#include "headerbody.h" #include "headerbody.h"
#include "auto_qmail.h" #include "auto_qmail.h"
#include "newfield.h" #include "newfield.h"
#include "constmap.h"
#define LINELEN 80 #define LINELEN 80
@ -63,14 +64,10 @@ void die_nomem() {
void die_invalid(sa) stralloc *sa; { void die_invalid(sa) stralloc *sa; {
substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: "); substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: ");
substdio_putflush(subfderr,sa->s,sa->len); perm(); } substdio_putflush(subfderr,sa->s,sa->len); perm(); }
void die_exec() {
substdio_putsflush(subfderr,"qmail-inject: fatal: unable to exec qmail-queue\n"); temp(); }
void die_qqt() { void die_qqt() {
substdio_putsflush(subfderr,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); } substdio_putsflush(subfderr,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); }
void die_chdir() { void die_chdir() {
substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); } substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); }
void die_bug() {
substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); }
void die_read() { void die_read() {
if (errno == error_nomem) die_nomem(); if (errno == error_nomem) die_nomem();
substdio_putsflush(subfderr,"qmail-inject: fatal: read error\n"); temp(); } substdio_putsflush(subfderr,"qmail-inject: fatal: read error\n"); temp(); }
@ -79,19 +76,6 @@ void doordie(sa,r) stralloc *sa; int r; {
substdio_putsflush(subfderr,"qmail-inject: fatal: unable to parse this line:\n"); substdio_putsflush(subfderr,"qmail-inject: fatal: unable to parse this line:\n");
substdio_putflush(subfderr,sa->s,sa->len); perm(); } substdio_putflush(subfderr,sa->s,sa->len); perm(); }
void die_comm() {
substdio_putsflush(subfderr,"qmail-inject: fatal: qmail-queue lost communications link\n"); temp(); }
void die_qq() {
substdio_putsflush(subfderr,"qmail-inject: fatal: qmail-queue died\n"); temp(); }
void die_qqwrite() {
substdio_putsflush(subfderr,"qmail-inject: fatal: qmail-queue unable to write message to disk; disk full?\n"); temp(); }
void die_qqsig() {
substdio_putsflush(subfderr,"qmail-inject: fatal: qmail-queue was killed\n"); temp(); }
void die_qqtimeout() {
substdio_putsflush(subfderr,"qmail-inject: fatal: qmail-queue timed out\n"); temp(); }
void die_qqtoolong() {
substdio_putsflush(subfderr,"qmail-inject: fatal: qmail-queue unhappy with long addresses\n"); perm(); }
GEN_ALLOC_typedef(saa,stralloc,sa,len,a) GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
@ -99,12 +83,15 @@ static stralloc sauninit = {0};
saa savedh = {0}; saa savedh = {0};
saa hrlist = {0}; saa hrlist = {0};
saa tocclist = {0};
saa hrrlist = {0}; saa hrrlist = {0};
saa reciplist = {0}; saa reciplist = {0};
int flagresent; int flagresent;
void exitnicely() void exitnicely()
{ {
char *qqx;
if (!flagqueue) substdio_flush(subfdout); if (!flagqueue) substdio_flush(subfdout);
if (flagqueue) if (flagqueue)
@ -133,18 +120,21 @@ void exitnicely()
qmail_to(&qqt,hrlist.sa[i].s); qmail_to(&qqt,hrlist.sa[i].s);
} }
switch(qmail_close(&qqt)) qqx = qmail_close(&qqt);
{ if (*qqx)
case 0: break; if (*qqx == 'D') {
case QMAIL_CRASHED: die_qqsig(); substdio_puts(subfderr,"qmail-inject: fatal: ");
case QMAIL_USAGE: case QMAIL_BUG: die_bug(); substdio_puts(subfderr,qqx + 1);
case QMAIL_EXECSOFT: die_exec(); substdio_puts(subfderr,"\n");
case QMAIL_NOMEM: die_nomem(); substdio_flush(subfderr);
case QMAIL_READ: die_comm(); perm();
case QMAIL_WRITE: die_qqwrite(); }
case QMAIL_TOOLONG: die_qqtoolong(); else {
case QMAIL_TIMEOUT: die_qqtimeout(); substdio_puts(subfderr,"qmail-inject: fatal: ");
default: die_qq(); substdio_puts(subfderr,qqx + 1);
substdio_puts(subfderr,"\n");
substdio_flush(subfderr);
temp();
} }
} }
@ -324,11 +314,10 @@ token822_alloc *addr;
return 1; return 1;
} }
void rwrecip(addr,xl) void rwappend(addr,xl)
token822_alloc *addr; token822_alloc *addr;
saa *xl; saa *xl;
{ {
rwgeneric(addr);
token822_reverse(addr); token822_reverse(addr);
if (!saa_readyplus(xl,1)) die_nomem(); if (!saa_readyplus(xl,1)) die_nomem();
xl->sa[xl->len] = sauninit; xl->sa[xl->len] = sauninit;
@ -338,9 +327,11 @@ saa *xl;
} }
int rwhrr(addr) token822_alloc *addr; int rwhrr(addr) token822_alloc *addr;
{ rwrecip(addr,&hrrlist); return 1; } { rwgeneric(addr); rwappend(addr,&hrrlist); return 1; }
int rwhr(addr) token822_alloc *addr; int rwhr(addr) token822_alloc *addr;
{ rwrecip(addr,&hrlist); return 1; } { rwgeneric(addr); rwappend(addr,&hrlist); return 1; }
int rwtocc(addr) token822_alloc *addr;
{ rwgeneric(addr); rwappend(addr,&hrlist); rwappend(addr,&tocclist); return 1; }
int htypeseen[H_NUM]; int htypeseen[H_NUM];
stralloc hfbuf = {0}; stralloc hfbuf = {0};
@ -352,9 +343,7 @@ void doheaderfield(h)
stralloc *h; stralloc *h;
{ {
int htype; int htype;
int flagrewrite; int (*rw)() = 0;
int flagrecip;
int flagrr;
htype = hfield_known(h->s,h->len); htype = hfield_known(h->s,h->len);
if (flagdeletefrom) if (htype == H_FROM) return; if (flagdeletefrom) if (htype == H_FROM) return;
@ -367,26 +356,24 @@ stralloc *h;
if (!hfield_valid(h->s,h->len)) if (!hfield_valid(h->s,h->len))
die_invalid(h); die_invalid(h);
flagrewrite = 0; switch(htype) {
flagrecip = 0; case H_TO: case H_CC:
flagrr = 0; rw = rwtocc; break;
switch(htype) case H_BCC: case H_APPARENTLYTO:
{ rw = rwhr; break;
case H_R_TO: case H_R_CC: case H_R_BCC: case H_R_TO: case H_R_CC: case H_R_BCC:
flagrr = 1; rw = rwhrr; break;
case H_TO: case H_CC: case H_BCC: case H_APPARENTLYTO: case H_RETURNPATH:
flagrecip = 1; rw = rwreturn; break;
case H_SENDER: case H_FROM: case H_REPLYTO: case H_SENDER: case H_FROM: case H_REPLYTO:
case H_RETURNRECEIPTTO: case H_ERRORSTO: case H_RETURNPATH: case H_RETURNRECEIPTTO: case H_ERRORSTO:
case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO: case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO:
flagrewrite = 1; rw = rwsender; break;
break;
} }
if (flagrewrite) if (rw) {
{
doordie(h,token822_parse(&hfin,h,&hfbuf)); doordie(h,token822_parse(&hfin,h,&hfbuf));
doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,(htype == H_RETURNPATH) ? rwreturn : (flagrecip ? (flagrr ? rwhrr : rwhr) : rwsender))); doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rw));
if (token822_unparse(h,&hfrewrite,LINELEN) != 1) if (token822_unparse(h,&hfrewrite,LINELEN) != 1)
die_nomem(); die_nomem();
} }
@ -421,7 +408,8 @@ char *s;
perm(); perm();
} }
token822_reverse(&tr); token822_reverse(&tr);
rwrecip(&tr,&reciplist); rwgeneric(&tr);
rwappend(&tr,&reciplist);
} }
stralloc defaultfrom = {0}; stralloc defaultfrom = {0};
@ -526,6 +514,53 @@ void dodefaultreturnpath()
if (token822_unparse(&defaultreturnpath,&hfrewrite,LINELEN) != 1) die_nomem(); if (token822_unparse(&defaultreturnpath,&hfrewrite,LINELEN) != 1) die_nomem();
} }
int flagmft = 0;
stralloc mft = {0};
struct constmap mapmft;
void mft_init()
{
char *x;
int r;
x = env_get("QMAILMFTFILE");
if (!x) return;
r = control_readfile(&mft,x,0);
if (r == -1) die_read(); /*XXX*/
if (!r) return;
if (!constmap_init(&mapmft,mft.s,mft.len,0)) die_nomem();
flagmft = 1;
}
void finishmft()
{
int i;
static stralloc sa = {0};
static stralloc sa2 = {0};
if (!flagmft) return;
if (htypeseen[H_MAILFOLLOWUPTO]) return;
for (i = 0;i < tocclist.len;++i)
if (constmap(&mapmft,tocclist.sa[i].s,tocclist.sa[i].len))
break;
if (i == tocclist.len) return;
puts("Mail-Followup-To: ");
i = tocclist.len;
while (i--) {
if (!stralloc_copy(&sa,&tocclist.sa[i])) die_nomem();
if (!stralloc_0(&sa)) die_nomem();
if (!quote2(&sa2,sa.s)) die_nomem();
put(sa2.s,sa2.len);
if (i) puts(",\n ");
}
puts("\n");
}
void finishheader() void finishheader()
{ {
flagresent = flagresent =
@ -596,6 +631,7 @@ void finishheader()
} }
if (!htypeseen[H_TO] && !htypeseen[H_CC]) if (!htypeseen[H_TO] && !htypeseen[H_CC])
puts("Cc: recipient list not shown: ;\n"); puts("Cc: recipient list not shown: ;\n");
finishmft();
} }
savedh_print(); savedh_print();
@ -606,6 +642,9 @@ void getcontrols()
static stralloc sa = {0}; static stralloc sa = {0};
char *x; char *x;
mft_init();
if (chdir(auto_qmail) == -1) die_chdir();
if (control_init() == -1) die_read(); if (control_init() == -1) die_read();
if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1) if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1)
@ -688,11 +727,10 @@ char **argv;
recipstrategy = RECIP_DEFAULT; recipstrategy = RECIP_DEFAULT;
flagqueue = 1; flagqueue = 1;
if (chdir(auto_qmail) == -1)
die_chdir();
getcontrols(); getcontrols();
if (!saa_readyplus(&hrlist,1)) die_nomem(); if (!saa_readyplus(&hrlist,1)) die_nomem();
if (!saa_readyplus(&tocclist,1)) die_nomem();
if (!saa_readyplus(&hrrlist,1)) die_nomem(); if (!saa_readyplus(&hrrlist,1)) die_nomem();
if (!saa_readyplus(&reciplist,1)) die_nomem(); if (!saa_readyplus(&reciplist,1)) die_nomem();

View file

@ -13,7 +13,7 @@ qmail-local \- deliver or forward a mail message
.I ext .I ext
.I domain .I domain
.I sender .I sender
.I aliasempty .I defaultdelivery
.SH DESCRIPTION .SH DESCRIPTION
.B qmail-local .B qmail-local
reads a mail message reads a mail message
@ -69,7 +69,7 @@ treats a nonexistent
the same way as an empty the same way as an empty
.BR .qmail\fIext : .BR .qmail\fIext :
namely, following the delivery instructions in namely, following the delivery instructions in
.IR aliasempty . .IR defaultdelivery .
The standard input for The standard input for
.B qmail-local .B qmail-local

View file

@ -12,6 +12,7 @@
#include "seek.h" #include "seek.h"
#include "substdio.h" #include "substdio.h"
#include "getln.h" #include "getln.h"
#include "strerr.h"
#include "subfd.h" #include "subfd.h"
#include "sgetopt.h" #include "sgetopt.h"
#include "alloc.h" #include "alloc.h"
@ -28,40 +29,17 @@
#include "gfrom.h" #include "gfrom.h"
#include "auto_patrn.h" #include "auto_patrn.h"
void err(s) char *s; { substdio_putsflush(subfderr,s); } void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
void soft() { _exit(111); }
void hard() { _exit(100); }
void temp_childcrashed() { err("Aack, child crashed. (#4.3.0)\n"); soft(); } void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); }
void temp_rewind() { err("Unable to rewind message. (#4.3.0)\n"); soft(); } void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
void temp_fork() { err("Unable to fork. (#4.3.0)\n"); soft(); } void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
void temp_read() { err("Error while reading message. (#4.3.0)\n"); soft(); } void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); }
void temp_write() { err("Error while writing message. (#4.3.0)\n"); soft(); } void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno),". (#4.3.0)"); }
void temp_child() { err("Temporary error in forwarding message. (#4.3.0)\n"); soft(); } void temp_slowlock()
void temp_maildirtimeout() { err("Timeout on maildir delivery. (#4.3.0)\n"); soft(); } { strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
void temp_maildir() { err("Temporary error on maildir delivery. (#4.3.0)\n"); soft(); } void temp_qmail(fn) char *fn;
void temp_nomaildir() { err("Unable to chdir to maildir. (#4.2.1)\n"); soft(); } { strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
void temp_open(fn) char *fn; { err("Unable to open "); err(fn); err(". (#4.2.1)\n"); soft(); }
void temp_blankline() { err("Uh-oh: first line of .qmail file is blank. (#4.2.1)\n"); soft(); }
void temp_fofile() { err("Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)\n"); soft(); }
void temp_foprog() { err("Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)\n"); soft(); }
void temp_nomem() { err("Out of memory. (#4.3.0)\n"); soft(); }
void temp_chdir() { err("Unable to switch to home directory. (#4.3.0)\n"); soft(); }
void temp_homestat() { err("Unable to stat home directory. (#4.3.0)\n"); soft(); }
void temp_homesticky() { err("Home directory is sticky: user is editing his .qmail file. (#4.2.1)\n"); soft(); }
void temp_homewritable() { err("Uh-oh: home directory is writable. (#4.7.0)\n"); soft(); }
void temp_qmwritable() { err("Uh-oh: .qmail file is writable. (#4.7.0)\n"); soft(); }
void temp_nfsqmail() { err("Temporary error trying to open .qmail file. (#4.3.0)\n"); soft(); }
void temp_denyqmail() { err("Permission error trying to open .qmail file. (#4.3.0)\n"); soft(); }
void temp_slowlock() { err("File has been locked for 30 seconds straight. (#4.3.0)\n"); soft(); }
void bounce_childperm() { err("Permanent error in forwarding message. (#5.2.4)\n"); hard(); }
void bounce_loop() { err("This message is looping: it already has my Delivered-To line. (#5.4.6)\n"); hard(); }
void bounce_ext() { err("Sorry, no mailbox here by that name. (#5.1.1)\n"); hard(); }
void usage() { err("qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty\n"); hard(); }
void warn_homesticky() { err("Warning: home directory is sticky.\n"); }
int flagdoit; int flagdoit;
int flag99; int flag99;
@ -75,7 +53,7 @@ char *host;
char *sender; char *sender;
char *aliasempty; char *aliasempty;
stralloc dashext = {0}; stralloc safeext = {0};
stralloc ufline = {0}; stralloc ufline = {0};
stralloc rpline = {0}; stralloc rpline = {0};
stralloc envrecip = {0}; stralloc envrecip = {0};
@ -172,7 +150,7 @@ char *fn;
temp_fork(); temp_fork();
case 0: case 0:
maildir_child(fn); maildir_child(fn);
soft(); _exit(111);
} }
wait_pid(&wstat,child); wait_pid(&wstat,child);
@ -181,15 +159,13 @@ char *fn;
switch(wait_exitcode(wstat)) switch(wait_exitcode(wstat))
{ {
case 0: break; case 0: break;
case 2: temp_nomaildir(); case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
case 3: temp_maildirtimeout(); case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
case 4: temp_read(); case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
default: temp_maildir(); default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
} }
} }
void slowlock() { temp_slowlock(); }
void mailfile(fn) void mailfile(fn)
char *fn; char *fn;
{ {
@ -203,9 +179,10 @@ char *fn;
if (seek_begin(0) == -1) temp_rewind(); if (seek_begin(0) == -1) temp_rewind();
fd = open_append(fn); fd = open_append(fn);
if (fd == -1) temp_open(fn); if (fd == -1)
strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)");
sig_alarmcatch(slowlock); sig_alarmcatch(temp_slowlock);
alarm(30); alarm(30);
flaglocked = (lock_ex(fd) != -1); flaglocked = (lock_ex(fd) != -1);
alarm(0); alarm(0);
@ -222,7 +199,11 @@ char *fn;
for (;;) for (;;)
{ {
if (getln(&ss,&messline,&match,'\n') != 0) if (getln(&ss,&messline,&match,'\n') != 0)
{ if (flaglocked) seek_trunc(fd,pos); close(fd); temp_read(); } {
strerr_warn3("Unable to read message: ",error_str(errno),". (#4.3.0)",0);
if (flaglocked) seek_trunc(fd,pos); close(fd);
_exit(111);
}
if (!match && !messline.len) break; if (!match && !messline.len) break;
if (gfrom(messline.s,messline.len)) if (gfrom(messline.s,messline.len))
if (substdio_bput(&ssout,">",1)) goto writeerrs; if (substdio_bput(&ssout,">",1)) goto writeerrs;
@ -240,9 +221,10 @@ char *fn;
return; return;
writeerrs: writeerrs:
strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
if (flaglocked) seek_trunc(fd,pos); if (flaglocked) seek_trunc(fd,pos);
close(fd); close(fd);
temp_write(); _exit(111);
} }
void mailprogram(prog) void mailprogram(prog)
@ -259,15 +241,10 @@ char *prog;
case -1: case -1:
temp_fork(); temp_fork();
case 0: case 0:
args[0] = "sh"; args[1] = "-c"; args[2] = prog; args[3] = 0; args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
sig_pipedefault(); sig_pipedefault();
execvp(*args,args); execv(*args,args);
if (errno == error_txtbsy) { err("Text busy. (#4.3.0)\n"); soft(); } strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)");
if (errno == error_nomem) { err("Out of memory. (#4.3.0)\n"); soft(); }
if (errno == error_io) { err("I/O error. (#4.3.0)\n"); soft(); }
if (error_temp(errno)) { err("Temporary error. (#4.3.0)\n"); soft(); }
err("Unable to execute "); err(*args); err(" (#5.2.4)\n");
hard();
} }
wait_pid(&wstat,child); wait_pid(&wstat,child);
@ -276,10 +253,10 @@ char *prog;
switch(wait_exitcode(wstat)) switch(wait_exitcode(wstat))
{ {
case 100: case 100:
case 64: case 65: case 70: case 76: case 77: case 78: case 112: hard(); case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
case 0: break; case 0: break;
case 99: flag99 = 1; break; case 99: flag99 = 1; break;
default: soft(); default: _exit(111);
} }
} }
@ -289,6 +266,7 @@ void mailforward(recips)
char **recips; char **recips;
{ {
struct qmail qqt; struct qmail qqt;
char *qqx;
substdio ss; substdio ss;
int match; int match;
@ -306,13 +284,9 @@ char **recips;
while (match); while (match);
qmail_from(&qqt,ueo.s); qmail_from(&qqt,ueo.s);
while (*recips) qmail_to(&qqt,*recips++); while (*recips) qmail_to(&qqt,*recips++);
switch(qmail_close(&qqt)) qqx = qmail_close(&qqt);
{ if (!*qqx) return;
case QMAIL_TOOLONG: bounce_childperm(); strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,".");
case QMAIL_READ: temp_read();
case 0: return;
default: temp_child();
}
} }
void bouncexf() void bouncexf()
@ -330,7 +304,7 @@ void bouncexf()
break; break;
if (messline.len == dtline.len) if (messline.len == dtline.len)
if (!str_diffn(messline.s,dtline.s,dtline.len)) if (!str_diffn(messline.s,dtline.s,dtline.len))
bounce_loop(); strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)");
} }
} }
@ -338,10 +312,15 @@ void checkhome()
{ {
struct stat st; struct stat st;
if (stat(".",&st) == -1) temp_homestat(); if (stat(".",&st) == -1)
if (st.st_mode & auto_patrn) temp_homewritable(); strerr_die3x(111,"Unable to stat home directory: ",error_str(errno),". (#4.3.0)");
if (st.st_mode & auto_patrn)
strerr_die1x(111,"Uh-oh: home directory is writable. (#4.7.0)");
if (st.st_mode & 01000) if (st.st_mode & 01000)
if (flagdoit) temp_homesticky(); else warn_homesticky(); if (flagdoit)
strerr_die1x(111,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)");
else
strerr_warn1("Warning: home directory is sticky.",0);
} }
int qmeox(dashowner) int qmeox(dashowner)
@ -350,54 +329,86 @@ char *dashowner;
struct stat st; struct stat st;
if (!stralloc_copys(&qme,".qmail")) temp_nomem(); if (!stralloc_copys(&qme,".qmail")) temp_nomem();
if (!stralloc_cat(&qme,&dashext)) temp_nomem(); if (!stralloc_cats(&qme,dash)) temp_nomem();
if (!stralloc_cat(&qme,&safeext)) temp_nomem();
if (!stralloc_cats(&qme,dashowner)) temp_nomem(); if (!stralloc_cats(&qme,dashowner)) temp_nomem();
if (!stralloc_0(&qme)) temp_nomem(); if (!stralloc_0(&qme)) temp_nomem();
if (stat(qme.s,&st) == -1) if (stat(qme.s,&st) == -1)
{ {
if (error_temp(errno)) temp_nfsqmail(); if (error_temp(errno)) temp_qmail(qme.s);
return -1; return -1;
} }
return 0; return 0;
} }
int qmeopen(cutable) int qmeexists(fd,cutable)
int *fd;
int *cutable; int *cutable;
{ {
int fd;
struct stat st; struct stat st;
if (!stralloc_0(&qme)) temp_nomem();
*fd = open_read(qme.s);
if (*fd == -1) {
if (error_temp(errno)) temp_qmail(qme.s);
if (errno == error_perm) temp_qmail(qme.s);
if (errno == error_acces) temp_qmail(qme.s);
return 0;
}
if (fstat(*fd,&st) == -1) temp_qmail(qme.s);
if ((st.st_mode & S_IFMT) == S_IFREG) {
if (st.st_mode & auto_patrn)
strerr_die1x(111,"Uh-oh: .qmail file is writable. (#4.7.0)");
*cutable = !!(st.st_mode & 0100);
return 1;
}
close(*fd);
return 0;
}
/* "" "": "" */
/* "-/" "": "-/" "-/default" */
/* "-/" "a": "-/a" "-/default" */
/* "-/" "a-": "-/a-" "-/a-default" "-/default" */
/* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */
/* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */
/* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */
void qmesearch(fd,cutable)
int *fd;
int *cutable;
{
int i; int i;
i = dashext.len;
for (;;)
{
if (!stralloc_copys(&qme,".qmail")) temp_nomem(); if (!stralloc_copys(&qme,".qmail")) temp_nomem();
if (!stralloc_catb(&qme,dashext.s,i)) temp_nomem(); if (!stralloc_cats(&qme,dash)) temp_nomem();
if (i < dashext.len) if (!stralloc_cats(&qme,"-default")) temp_nomem(); if (!stralloc_cat(&qme,&safeext)) temp_nomem();
if (!stralloc_0(&qme)) temp_nomem(); if (qmeexists(fd,cutable)) {
fd = open_read(qme.s); if (safeext.len >= 7) {
if (fd == -1) i = safeext.len - 7;
{ if (!byte_diff("default",7,safeext.s + i))
if (error_temp(errno)) temp_nfsqmail(); if (i <= str_len(ext)) /* paranoia */
if (errno == error_perm) temp_denyqmail(); if (!env_put2("DEFAULT",ext + i)) temp_nomem();
if (errno == error_acces) temp_denyqmail();
} }
else return;
{
if (fstat(fd,&st) == -1) temp_nfsqmail();
if ((st.st_mode & S_IFMT) == S_IFREG)
{
if (st.st_mode & auto_patrn) temp_qmwritable();
*cutable = !!(st.st_mode & 0100);
return fd;
} }
close(fd);
for (i = safeext.len;i >= 0;--i)
if (!i || (safeext.s[i - 1] == '-')) {
if (!stralloc_copys(&qme,".qmail")) temp_nomem();
if (!stralloc_cats(&qme,dash)) temp_nomem();
if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem();
if (!stralloc_cats(&qme,"default")) temp_nomem();
if (qmeexists(fd,cutable)) {
if (i <= str_len(ext)) /* paranoia */
if (!env_put2("DEFAULT",ext + i)) temp_nomem();
return;
} }
if (!i) return -1;
do
if (dashext.s[--i] == '-') break;
while (i);
} }
*fd = -1;
} }
unsigned long count_file = 0; unsigned long count_file = 0;
@ -446,7 +457,7 @@ char **argv;
char **recips; char **recips;
datetime_sec starttime; datetime_sec starttime;
int flagforwardonly; int flagforwardonly;
char *extx; char *x;
umask(077); umask(077);
sig_pipeignore(); sig_pipeignore();
@ -459,9 +470,8 @@ char **argv;
{ {
case 'n': flagdoit = 0; break; case 'n': flagdoit = 0; break;
case 'N': flagdoit = 1; break; case 'N': flagdoit = 1; break;
case '?':
default: default:
hard(); usage();
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
@ -477,7 +487,8 @@ char **argv;
if (*argv) usage(); if (*argv) usage();
if (homedir[0] != '/') usage(); if (homedir[0] != '/') usage();
if (chdir(homedir) == -1) temp_chdir(); if (chdir(homedir) == -1)
strerr_die5x(111,"Unable to switch to ",homedir,": ",error_str(errno),". (#4.3.0)");
checkhome(); checkhome();
if (!env_put2("HOST",host)) temp_nomem(); if (!env_put2("HOST",host)) temp_nomem();
@ -542,25 +553,40 @@ char **argv;
if (!stralloc_0(&foo)) temp_nomem(); if (!stralloc_0(&foo)) temp_nomem();
if (!env_put2("UFLINE",foo.s)) temp_nomem(); if (!env_put2("UFLINE",foo.s)) temp_nomem();
if (!stralloc_copys(&dashext,dash)) temp_nomem(); x = ext;
if (!stralloc_cats(&dashext,ext)) temp_nomem(); if (!env_put2("EXT",x)) temp_nomem();
for (i = 0;i < dashext.len;++i) x += str_chr(x,'-'); if (*x) ++x;
if (dashext.s[i] == '.') if (!env_put2("EXT2",x)) temp_nomem();
dashext.s[i] = ':'; x += str_chr(x,'-'); if (*x) ++x;
case_lowerb(dashext.s,dashext.len); if (!env_put2("EXT3",x)) temp_nomem();
x += str_chr(x,'-'); if (*x) ++x;
if (!env_put2("EXT4",x)) temp_nomem();
extx = ext; if (!stralloc_copys(&safeext,ext)) temp_nomem();
if (!env_put2("EXT",extx)) temp_nomem(); case_lowerb(safeext.s,safeext.len);
extx += str_chr(extx,'-'); if (*extx) ++extx; for (i = 0;i < safeext.len;++i)
if (!env_put2("EXT2",extx)) temp_nomem(); if (safeext.s[i] == '.')
extx += str_chr(extx,'-'); if (*extx) ++extx; safeext.s[i] = ':';
if (!env_put2("EXT3",extx)) temp_nomem();
extx += str_chr(extx,'-'); if (*extx) ++extx; i = str_len(host);
if (!env_put2("EXT4",extx)) temp_nomem(); i = byte_rchr(host,i,'.');
if (!stralloc_copyb(&foo,host,i)) temp_nomem();
if (!stralloc_0(&foo)) temp_nomem();
if (!env_put2("HOST2",foo.s)) temp_nomem();
i = byte_rchr(host,i,'.');
if (!stralloc_copyb(&foo,host,i)) temp_nomem();
if (!stralloc_0(&foo)) temp_nomem();
if (!env_put2("HOST3",foo.s)) temp_nomem();
i = byte_rchr(host,i,'.');
if (!stralloc_copyb(&foo,host,i)) temp_nomem();
if (!stralloc_0(&foo)) temp_nomem();
if (!env_put2("HOST4",foo.s)) temp_nomem();
flagforwardonly = 0; flagforwardonly = 0;
fd = qmeopen(&flagforwardonly); qmesearch(&fd,&flagforwardonly);
if (fd == -1) if (*dash) bounce_ext(); if (fd == -1)
if (*dash)
strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
if (!stralloc_copys(&ueo,sender)) temp_nomem(); if (!stralloc_copys(&ueo,sender)) temp_nomem();
if (str_diff(sender,"")) if (str_diff(sender,""))
@ -625,13 +651,13 @@ char **argv;
{ {
case 0: /* k == i */ case 0: /* k == i */
if (i) break; if (i) break;
temp_blankline(); strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
case '#': case '#':
break; break;
case '.': case '.':
case '/': case '/':
++count_file; ++count_file;
if (flagforwardonly) temp_fofile(); if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)");
if (cmds.s[k - 1] == '/') if (cmds.s[k - 1] == '/')
if (flagdoit) maildir(cmds.s + i); if (flagdoit) maildir(cmds.s + i);
else sayit("maildir ",cmds.s + i,k - i); else sayit("maildir ",cmds.s + i,k - i);
@ -641,7 +667,7 @@ char **argv;
break; break;
case '|': case '|':
++count_program; ++count_program;
if (flagforwardonly) temp_foprog(); if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)");
if (flagdoit) mailprogram(cmds.s + i + 1); if (flagdoit) mailprogram(cmds.s + i + 1);
else sayit("program ",cmds.s + i + 1,k - i - 1); else sayit("program ",cmds.s + i + 1,k - i - 1);
break; break;

View file

@ -5,31 +5,22 @@ qmail-log \- the qmail activity record
.B qmail-send .B qmail-send
prints a series of lines describing its activities. prints a series of lines describing its activities.
Each possible line is described below. Each possible line is described below.
.SH "STARTING AND STOPPING" .SH "STATUS"
.TP .TP
.B running .B status: local \fIl\fB/\fIL\fB remote \fIr\fB/\fIR\fB ...
.B qmail-send .B qmail-send
is ready to deliver messages. is waiting for
.I l
local deliveries
and
.I r
remote deliveries.
The concurrency limits are
.I L
and
.IR R .
.TP .TP
.B local deliveries will be put on hold .B status: exiting
The local concurrency limit is 0, so
.B qmail-send
will not perform any local deliveries.
.TP
.B remote deliveries will be put on hold
The remote concurrency limit is 0, so
.B qmail-send
will not perform any remote deliveries.
.TP
.B number of deliveries left before exiting: ...
.B qmail-send
wants to exit as soon as possible,
usually because it was sent a
TERM signal,
but it has to wait for some deliveries to finish.
It will not start any new deliveries.
.TP
.B exiting
.B qmail-send .B qmail-send
is done. is done.
.SH "FATAL PROBLEMS" .SH "FATAL PROBLEMS"

View file

@ -3,7 +3,7 @@
qmail-lspawn \- schedule local deliveries qmail-lspawn \- schedule local deliveries
.SH SYNOPSIS .SH SYNOPSIS
.B qmail-lspawn .B qmail-lspawn
.I aliasempty .I defaultdelivery
.SH DESCRIPTION .SH DESCRIPTION
.B qmail-lspawn .B qmail-lspawn
reads a series of local delivery commands from descriptor 0, reads a series of local delivery commands from descriptor 0,
@ -12,7 +12,7 @@ invokes
to perform the deliveries, to perform the deliveries,
and prints the results to descriptor 1. and prints the results to descriptor 1.
It passes It passes
.I aliasempty .I defaultdelivery
to to
.B qmail-local .B qmail-local
as the default delivery instruction. as the default delivery instruction.

41
qmail-newmrh.9 Normal file
View file

@ -0,0 +1,41 @@
.TH qmail-newmrh 8
.SH NAME
qmail-newmrh \- prepare morercpthosts for qmail-smtpd
.SH SYNOPSIS
.B qmail-newmrh
.SH DESCRIPTION
.B qmail-newmrh
reads the instructions in
.B QMAILHOME/control/morercpthosts
and writes them into
.B QMAILHOME/control/morercpthosts.cdb
in a binary format suited
for quick access by
.BR qmail-smtpd .
If there is a problem with
.BR control/morercpthosts ,
.B qmail-newmrh
complains and leaves
.B control/morercpthosts.cdb
alone.
.B qmail-newmrh
ensures that
.B control/morercpthosts.cdb
is updated atomically,
so
.B qmail-smtpd
never has to wait for
.B qmail-newmrh
to finish.
However,
.B qmail-newmrh
makes no attempt to protect against two simultaneous updates of
.BR control/morercpthosts.cdb .
The binary
.B control/morercpthosts.cdb
format is portable across machines.
.SH "SEE ALSO"
qmail-smtpd(8)

70
qmail-newmrh.c Normal file
View file

@ -0,0 +1,70 @@
#include "strerr.h"
#include "stralloc.h"
#include "substdio.h"
#include "getln.h"
#include "exit.h"
#include "readwrite.h"
#include "open.h"
#include "auto_qmail.h"
#include "cdbmss.h"
#define FATAL "qmail-newmrh: fatal: "
void die_read()
{
strerr_die2sys(111,FATAL,"unable to read control/morercpthosts: ");
}
void die_write()
{
strerr_die2sys(111,FATAL,"unable to write to control/morercpthosts.tmp: ");
}
char inbuf[1024];
substdio ssin;
int fd;
int fdtemp;
struct cdbmss cdbmss;
stralloc line = {0};
int match;
void main()
{
umask(033);
if (chdir(auto_qmail) == -1)
strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
fd = open_read("control/morercpthosts");
if (fd == -1) die_read();
substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
fdtemp = open_trunc("control/morercpthosts.tmp");
if (fdtemp == -1) die_write();
if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write();
for (;;) {
if (getln(&ssin,&line,&match,'\n') != 0) die_read();
case_lowerb(line.s,line.len);
while (line.len) {
if (line.s[line.len - 1] == ' ') { --line.len; continue; }
if (line.s[line.len - 1] == '\n') { --line.len; continue; }
if (line.s[line.len - 1] == '\t') { --line.len; continue; }
if (line.s[0] != '#')
if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1)
die_write();
break;
}
if (!match) break;
}
if (cdbmss_finish(&cdbmss) == -1) die_write();
if (fsync(fdtemp) == -1) die_write();
if (close(fdtemp) == -1) die_write(); /* NFS stupidity */
if (rename("control/morercpthosts.tmp","control/morercpthosts.cdb") == -1)
strerr_die2sys(111,FATAL,"unable to move control/morercpthosts.tmp to control/morercpthosts.cdb");
_exit(0);
}

View file

@ -29,7 +29,7 @@ which checks the password and sets up environment variables.
has a 20-minute idle timeout. has a 20-minute idle timeout.
.B qmail-pop3d .B qmail-pop3d
supports UIDL and TOP. supports UIDL, TOP, and LAST.
.B qmail-pop3d .B qmail-pop3d
appends an extra blank line to every message appends an extra blank line to every message

View file

@ -1,60 +1,69 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "direntry.h" #include "commands.h"
#include "sig.h" #include "sig.h"
#include "getln.h" #include "getln.h"
#include "stralloc.h" #include "stralloc.h"
#include "substdio.h" #include "substdio.h"
#include "alloc.h" #include "alloc.h"
#include "datetime.h"
#include "prot.h"
#include "open.h" #include "open.h"
#include "prioq.h" #include "prioq.h"
#include "scan.h" #include "scan.h"
#include "fmt.h" #include "fmt.h"
#include "error.h"
#include "str.h" #include "str.h"
#include "exit.h" #include "exit.h"
#include "now.h" #include "maildir.h"
#include "readwrite.h" #include "readwrite.h"
#include "timeoutread.h"
int timeout = 1200; #include "timeoutwrite.h"
char ssoutbuf[1024];
substdio ssout = SUBSTDIO_FDBUF(write,1,ssoutbuf,sizeof(ssoutbuf));
int timeoutread(fd,buf,n) int fd; char *buf; int n;
{
int r; int saveerrno;
alarm(timeout);
r = read(fd,buf,n); saveerrno = errno;
alarm(0);
errno = saveerrno; return r;
}
char ssinbuf[128];
substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf));
void die() { _exit(0); } void die() { _exit(0); }
int saferead(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = timeoutread(1200,fd,buf,len);
if (r <= 0) die();
return r;
}
int safewrite(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = timeoutwrite(1200,fd,buf,len);
if (r <= 0) die();
return r;
}
char ssoutbuf[1024];
substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
char ssinbuf[128];
substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
void put(buf,len) char *buf; int len;
{
substdio_put(&ssout,buf,len);
}
void puts(s) char *s; void puts(s) char *s;
{ {
if (substdio_puts(&ssout,s) == -1) die(); substdio_puts(&ssout,s);
} }
void flush() void flush()
{ {
if (substdio_flush(&ssout) == -1) die(); substdio_flush(&ssout);
} }
void err(s) char *s; void err(s) char *s;
{ {
puts("-ERR "); puts("-ERR ");
puts(s); puts(s);
puts("\r\n"); puts("\r\n");
if (substdio_flush(&ssout) == -1) die(); flush();
} }
void die_nomem() { err("out of memory"); die(); } void die_nomem() { err("out of memory"); die(); }
void die_prot() { err("protection problem"); die(); }
void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); } void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
void err_syntax() { err("syntax error"); } void err_syntax() { err("syntax error"); }
void err_unimpl() { err("unimplemented"); } void err_unimpl() { err("unimplemented"); }
@ -65,26 +74,15 @@ void err_nosuch() { err("unable to open that message"); }
void err_nounlink() { err("unable to unlink all deleted messages"); } void err_nounlink() { err("unable to unlink all deleted messages"); }
void okay() { puts("+OK \r\n"); flush(); } void okay() { puts("+OK \r\n"); flush(); }
void pop3_last() { puts("+OK 0\r\n"); flush(); }
void printfn(fn) char *fn;
stralloc dataline = {0};
stralloc filenames = {0};
prioq pq = {0};
stralloc newname = {0};
struct message
{ {
int flagdeleted; fn += 4;
unsigned long size; put(fn,str_chr(fn,':'));
char *fn;
} }
*m;
int numm;
substdio ssmsg; char ssmsgbuf[1024];
char strnum[FMT_ULONG];
stralloc line = {0};
void blast(ssfrom,limit) void blast(ssfrom,limit)
substdio *ssfrom; substdio *ssfrom;
@ -93,104 +91,50 @@ unsigned long limit;
int match; int match;
int inheaders = 1; int inheaders = 1;
for (;;) for (;;) {
{ if (getln(ssfrom,&line,&match,'\n') != 0) die();
if (getln(ssfrom,&dataline,&match,'\n') != 0) die(); if (!match && !line.len) break;
if (!match && !dataline.len) break; if (match) --line.len; /* no way to pass this info over POP */
if (match) --dataline.len; /* no way to pass this info over POP */
if (limit) if (!inheaders) if (!--limit) break; if (limit) if (!inheaders) if (!--limit) break;
if (!dataline.len) if (!line.len)
inheaders = 0; inheaders = 0;
else else
if (dataline.s[0] == '.') if (line.s[0] == '.')
substdio_put(&ssout,".",1); put(".",1);
if (substdio_put(&ssout,dataline.s,dataline.len) == -1) die(); put(line.s,line.len);
if (substdio_put(&ssout,"\r\n",2) == -1) die(); put("\r\n",2);
if (!match) break; if (!match) break;
} }
if (substdio_put(&ssout,"\r\n.\r\n",5) == -1) die(); put("\r\n.\r\n",5);
if (substdio_flush(&ssout) == -1) die(); flush();
} }
stralloc filenames = {0};
prioq pq = {0};
struct message {
int flagdeleted;
unsigned long size;
char *fn;
} *m;
int numm;
int last = 0;
void getlist() void getlist()
{ {
unsigned long pos;
datetime_sec time;
DIR *dir;
direntry *d;
struct prioq_elt pe; struct prioq_elt pe;
struct stat st; struct stat st;
int i; int i;
numm = 0; maildir_clean(&line);
if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan();
time = now();
if (dir = opendir("tmp"))
{
while (d = readdir(dir))
{
if (str_equal(d->d_name,".")) continue;
if (str_equal(d->d_name,"..")) continue;
if (!stralloc_copys(&newname,"tmp/")) die_nomem();
if (!stralloc_cats(&newname,d->d_name)) die_nomem();
if (!stralloc_0(&newname)) die_nomem();
if (stat(newname.s,&st) == 0)
if (time > st.st_atime + 129600)
unlink(newname.s);
}
closedir(dir);
}
if (!stralloc_copys(&filenames,"")) die_nomem();
if (dir = opendir("new"))
{
while (d = readdir(dir))
{
if (str_equal(d->d_name,".")) continue;
if (str_equal(d->d_name,"..")) continue;
pos = filenames.len;
if (!stralloc_cats(&filenames,"new/")) die_nomem();
if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
if (!stralloc_0(&filenames)) die_nomem();
if (stat(filenames.s + pos,&st) == 0)
{
pe.dt = st.st_mtime;
pe.id = pos;
if (!prioq_insert(&pq,&pe)) die_nomem();
++numm;
}
}
closedir(dir);
}
if (dir = opendir("cur"))
{
while (d = readdir(dir))
{
if (str_equal(d->d_name,".")) continue;
if (str_equal(d->d_name,"..")) continue;
pos = filenames.len;
if (!stralloc_cats(&filenames,"cur/")) die_nomem();
if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
if (!stralloc_0(&filenames)) die_nomem();
if (stat(filenames.s + pos,&st) == 0)
{
pe.dt = st.st_mtime;
pe.id = pos;
if (!prioq_insert(&pq,&pe)) die_nomem();
++numm;
}
}
closedir(dir);
}
numm = pq.p ? pq.len : 0;
m = (struct message *) alloc(numm * sizeof(struct message)); m = (struct message *) alloc(numm * sizeof(struct message));
if (!m) die_nomem(); if (!m) die_nomem();
for (i = 0;i < numm;++i) for (i = 0;i < numm;++i) {
{
if (!prioq_min(&pq,&pe)) { numm = i; break; } if (!prioq_min(&pq,&pe)) { numm = i; break; }
prioq_delmin(&pq); prioq_delmin(&pq);
m[i].fn = filenames.s + pe.id; m[i].fn = filenames.s + pe.id;
@ -202,28 +146,6 @@ void getlist()
} }
} }
char foo[FMT_ULONG];
void printint(u) unsigned int u;
{
foo[fmt_uint(foo,u)] = 0;
puts(foo);
puts(" ");
}
void printlong(u) unsigned long u;
{
foo[fmt_uint(foo,u)] = 0;
puts(foo);
puts("\r\n");
}
void printfn(fn) char *fn;
{
puts(fn + 4);
puts("\r\n");
}
void pop3_stat() void pop3_stat()
{ {
int i; int i;
@ -232,8 +154,10 @@ void pop3_stat()
total = 0; total = 0;
for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
puts("+OK "); puts("+OK ");
printint(numm); put(strnum,fmt_uint(strnum,numm));
printlong(total); puts(" ");
put(strnum,fmt_ulong(strnum,total));
puts("\r\n");
flush(); flush();
} }
@ -241,15 +165,33 @@ void pop3_rset()
{ {
int i; int i;
for (i = 0;i < numm;++i) m[i].flagdeleted = 0; for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
last = 0;
okay(); okay();
} }
void pop3_last()
{
puts("+OK ");
put(strnum,fmt_uint(strnum,last));
puts("\r\n");
flush();
}
void pop3_quit() void pop3_quit()
{ {
int i; int i;
for (i = 0;i < numm;++i) for (i = 0;i < numm;++i)
if (m[i].flagdeleted) if (m[i].flagdeleted) {
if (unlink(m[i].fn) == -1) err_nounlink(); if (unlink(m[i].fn) == -1) err_nounlink();
}
else
if (str_start(m[i].fn,"new/")) {
if (!stralloc_copys(&line,"cur/")) die_nomem();
if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem();
if (!stralloc_cats(&line,":2,")) die_nomem();
if (!stralloc_0(&line)) die_nomem();
rename(m[i].fn,line.s); /* if it fails, bummer */
}
okay(); okay();
die(); die();
} }
@ -257,7 +199,6 @@ void pop3_quit()
int msgno(arg) char *arg; int msgno(arg) char *arg;
{ {
unsigned long u; unsigned long u;
if (!arg) { err_syntax(); return -1; }
if (!scan_ulong(arg,&u)) { err_syntax(); return -1; } if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
if (!u) { err_nozero(); return -1; } if (!u) { err_nozero(); return -1; }
--u; --u;
@ -269,35 +210,38 @@ int msgno(arg) char *arg;
void pop3_dele(arg) char *arg; void pop3_dele(arg) char *arg;
{ {
int i; int i;
i = msgno(arg); i = msgno(arg);
if (i == -1) return; if (i == -1) return;
m[i].flagdeleted = 1; m[i].flagdeleted = 1;
if (i + 1 > last) last = i + 1;
okay(); okay();
} }
void list(i,flaguidl)
int i;
int flaguidl;
{
put(strnum,fmt_uint(strnum,i + 1));
puts(" ");
if (flaguidl) printfn(m[i].fn);
else put(strnum,fmt_ulong(strnum,m[i].size));
puts("\r\n");
}
void dolisting(arg,flaguidl) char *arg; int flaguidl; void dolisting(arg,flaguidl) char *arg; int flaguidl;
{ {
unsigned int i; unsigned int i;
if (*arg) {
if (arg)
{
i = msgno(arg); i = msgno(arg);
if (i == -1) return; if (i == -1) return;
puts("+OK "); puts("+OK ");
printint(i + 1); list(i,flaguidl);
if (flaguidl) printfn(m[i].fn); else printlong(m[i].size);
} }
else else {
{
okay(); okay();
for (i = 0;i < numm;++i) for (i = 0;i < numm;++i)
if (!m[i].flagdeleted) if (!m[i].flagdeleted)
{ list(i,flaguidl);
printint(i + 1);
if (flaguidl) printfn(m[i].fn); else printlong(m[i].size);
}
puts(".\r\n"); puts(".\r\n");
} }
flush(); flush();
@ -306,6 +250,8 @@ void dolisting(arg,flaguidl) char *arg; int flaguidl;
void pop3_uidl(arg) char *arg; { dolisting(arg,1); } void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
void pop3_list(arg) char *arg; { dolisting(arg,0); } void pop3_list(arg) char *arg; { dolisting(arg,0); }
substdio ssmsg; char ssmsgbuf[1024];
void pop3_top(arg) char *arg; void pop3_top(arg) char *arg;
{ {
int i; int i;
@ -327,53 +273,24 @@ void pop3_top(arg) char *arg;
close(fd); close(fd);
} }
static struct { void (*fun)(); char *text; } pop3cmd[] = { struct commands pop3commands[] = {
{ pop3_quit, "quit" } { "quit", pop3_quit, 0 }
, { pop3_stat, "stat" } , { "stat", pop3_stat, 0 }
, { pop3_list, "list" } , { "list", pop3_list, 0 }
, { pop3_uidl, "uidl" } , { "uidl", pop3_uidl, 0 }
, { pop3_dele, "dele" } , { "dele", pop3_dele, 0 }
, { pop3_top, "retr" } , { "retr", pop3_top, 0 }
, { pop3_rset, "rset" } , { "rset", pop3_rset, 0 }
, { pop3_last, "last" } , { "last", pop3_last, 0 }
, { pop3_top, "top" } , { "top", pop3_top, 0 }
, { okay, "noop" } , { "noop", okay, 0 }
, { 0, 0 } , { 0, err_unimpl, 0 }
} ; } ;
void doit(cmd)
char *cmd;
{
int i;
int j;
char ch;
for (i = 0;pop3cmd[i].fun;++i)
{
for (j = 0;ch = pop3cmd[i].text[j];++j)
if ((cmd[j] != ch) && (cmd[j] != ch - 32))
break;
if (!ch)
if (!cmd[j] || (cmd[j] == ' '))
{
while (cmd[j] == ' ') ++j;
if (!cmd[j])
pop3cmd[i].fun((char *) 0);
else
pop3cmd[i].fun(cmd + j);
return;
}
}
err_unimpl();
}
void main(argc,argv) void main(argc,argv)
int argc; int argc;
char **argv; char **argv;
{ {
static stralloc cmd = {0};
int match;
sig_alarmcatch(die); sig_alarmcatch(die);
sig_pipeignore(); sig_pipeignore();
@ -383,15 +300,6 @@ char **argv;
getlist(); getlist();
okay(); okay();
commands(&ssin,pop3commands);
for (;;) die();
{
if (getln(&ssin,&cmd,&match,'\n') == -1) die();
if (!match) die();
if (cmd.len == 0) die();
if (cmd.s[--cmd.len] != '\n') die();
if ((cmd.len > 0) && (cmd.s[cmd.len - 1] == '\r')) --cmd.len;
cmd.s[cmd.len++] = 0;
doit(cmd.s);
}
} }

View file

@ -1,53 +1,59 @@
#include <sys/types.h> #include "commands.h"
#include <sys/stat.h>
#include "fd.h" #include "fd.h"
#include "sig.h" #include "sig.h"
#include "getln.h"
#include "stralloc.h" #include "stralloc.h"
#include "substdio.h" #include "substdio.h"
#include "subfd.h"
#include "alloc.h" #include "alloc.h"
#include "datetime.h"
#include "error.h"
#include "wait.h" #include "wait.h"
#include "str.h" #include "str.h"
#include "byte.h"
#include "now.h" #include "now.h"
#include "fmt.h" #include "fmt.h"
#include "exit.h" #include "exit.h"
#include "readwrite.h" #include "readwrite.h"
#include "timeoutread.h"
int timeout = 1200; #include "timeoutwrite.h"
int timeoutread(fd,buf,n) int fd; char *buf; int n;
{
int r; int saveerrno;
alarm(timeout);
r = read(fd,buf,n); saveerrno = errno;
alarm(0);
errno = saveerrno; return r;
}
char ssinbuf[128];
substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf));
void die() { _exit(1); } void die() { _exit(1); }
void out(s) char *s;
int saferead(fd,buf,len) int fd; char *buf; int len;
{ {
if (substdio_puts(subfdoutsmall,s) == -1) die(); int r;
r = timeoutread(1200,fd,buf,len);
if (r <= 0) die();
return r;
} }
void outflush(s) char *s;
int safewrite(fd,buf,len) int fd; char *buf; int len;
{ {
out(s); int r;
if (substdio_flush(subfdoutsmall) == -1) die(); r = timeoutwrite(1200,fd,buf,len);
if (r <= 0) die();
return r;
}
char ssoutbuf[128];
substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
char ssinbuf[128];
substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
void puts(s) char *s;
{
substdio_puts(&ssout,s);
}
void flush()
{
substdio_flush(&ssout);
} }
void err(s) char *s; void err(s) char *s;
{ {
if (substdio_puts(subfdoutsmall,"-ERR ") == -1) die(); puts("-ERR ");
if (substdio_puts(subfdoutsmall,s) == -1) die(); puts(s);
if (substdio_puts(subfdoutsmall,"\r\n") == -1) die(); puts("\r\n");
if (substdio_flush(subfdoutsmall) == -1) die(); flush();
} }
void die_usage() { err("usage: popup hostname subprogram"); die(); } void die_usage() { err("usage: popup hostname subprogram"); die(); }
void die_nomem() { err("out of memory"); die(); } void die_nomem() { err("out of memory"); die(); }
void die_pipe() { err("unable to open pipe"); die(); } void die_pipe() { err("unable to open pipe"); die(); }
@ -60,7 +66,8 @@ void err_syntax() { err("syntax error"); }
void err_wantuser() { err("USER first"); } void err_wantuser() { err("USER first"); }
void err_authoriz() { err("authorization first"); } void err_authoriz() { err("authorization first"); }
void okay() { outflush("+OK \r\n"); } void okay() { puts("+OK \r\n"); flush(); }
void pop3_quit() { okay(); die(); }
char unique[FMT_ULONG + FMT_ULONG + 3]; char unique[FMT_ULONG + FMT_ULONG + 3];
@ -80,14 +87,12 @@ char *pass;
int child; int child;
int wstat; int wstat;
int pi[2]; int pi[2];
int i;
if (fd_copy(2,1) == -1) die_pipe(); if (fd_copy(2,1) == -1) die_pipe();
close(3); close(3);
if (pipe(pi) == -1) die_pipe(); if (pipe(pi) == -1) die_pipe();
if (pi[0] != 3) die_pipe(); if (pi[0] != 3) die_pipe();
switch(child = fork()) switch(child = fork()) {
{
case -1: case -1:
die_fork(); die_fork();
case 0: case 0:
@ -97,7 +102,7 @@ char *pass;
_exit(1); _exit(1);
} }
close(pi[0]); close(pi[0]);
substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof(upbuf)); substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
if (substdio_put(&ssup,user,userlen) == -1) die_write(); if (substdio_put(&ssup,user,userlen) == -1) die_write();
if (substdio_put(&ssup,pass,str_len(pass) + 1) == -1) die_write(); if (substdio_put(&ssup,pass,str_len(pass) + 1) == -1) die_write();
if (substdio_puts(&ssup,"<") == -1) die_write(); if (substdio_puts(&ssup,"<") == -1) die_write();
@ -106,8 +111,8 @@ char *pass;
if (substdio_put(&ssup,">",2) == -1) die_write(); if (substdio_put(&ssup,">",2) == -1) die_write();
if (substdio_flush(&ssup) == -1) die_write(); if (substdio_flush(&ssup) == -1) die_write();
close(pi[1]); close(pi[1]);
for (i = 0;pass[i];++i) pass[i] = 0; byte_zero(pass,str_len(pass));
for (i = 0;i < sizeof(upbuf);++i) upbuf[i] = 0; byte_zero(upbuf,sizeof upbuf);
if (wait_pid(&wstat,child) == -1) die(); if (wait_pid(&wstat,child) == -1) die();
if (wait_crashed(wstat)) die_childcrashed(); if (wait_crashed(wstat)) die_childcrashed();
if (wait_exitcode(wstat)) die_badauth(); if (wait_exitcode(wstat)) die_badauth();
@ -122,15 +127,15 @@ void pop3_greet()
s += fmt_ulong(s,(unsigned long) now()); s += fmt_ulong(s,(unsigned long) now());
*s++ = '@'; *s++ = '@';
*s++ = 0; *s++ = 0;
puts("+OK <");
out("+OK <"); puts(unique);
out(unique); puts(hostname);
out(hostname); puts(">\r\n");
outflush(">\r\n"); flush();
} }
void pop3_user(arg) char *arg; void pop3_user(arg) char *arg;
{ {
if (!arg) { err_syntax(); return; } if (!*arg) { err_syntax(); return; }
okay(); okay();
seenuser = 1; seenuser = 1;
if (!stralloc_copys(&username,arg)) die_nomem(); if (!stralloc_copys(&username,arg)) die_nomem();
@ -139,63 +144,31 @@ void pop3_user(arg) char *arg;
void pop3_pass(arg) char *arg; void pop3_pass(arg) char *arg;
{ {
if (!seenuser) { err_wantuser(); return; } if (!seenuser) { err_wantuser(); return; }
if (!arg) { err_syntax(); return; } if (!*arg) { err_syntax(); return; }
doanddie(username.s,username.len,arg); doanddie(username.s,username.len,arg);
} }
void pop3_apop(arg) char *arg; void pop3_apop(arg) char *arg;
{ {
char *space; char *space;
if (!arg) { err_syntax(); return; }
space = arg + str_chr(arg,' '); space = arg + str_chr(arg,' ');
if (!*space) { err_syntax(); return; } if (!*space) { err_syntax(); return; }
*space++ = 0; *space++ = 0;
doanddie(arg,space - arg,space); doanddie(arg,space - arg,space);
} }
void pop3_quit() { okay(); die(); } struct commands pop3commands[] = {
{ "user", pop3_user, 0 }
static struct { void (*fun)(); char *text; } pop3cmd[] = { , { "pass", pop3_pass, 0 }
{ pop3_user, "user" } , { "apop", pop3_apop, 0 }
, { pop3_pass, "pass" } , { "quit", pop3_quit, 0 }
, { pop3_apop, "apop" } , { "noop", okay, 0 }
, { pop3_quit, "quit" } , { 0, err_authoriz, 0 }
, { okay, "noop" }
, { 0, 0 }
} ; } ;
void doit(cmd)
char *cmd;
{
int i;
int j;
char ch;
for (i = 0;pop3cmd[i].fun;++i)
{
for (j = 0;ch = pop3cmd[i].text[j];++j)
if ((cmd[j] != ch) && (cmd[j] != ch - 32))
break;
if (!ch)
if (!cmd[j] || (cmd[j] == ' '))
{
while (cmd[j] == ' ') ++j;
if (!cmd[j])
pop3cmd[i].fun((char *) 0);
else
pop3cmd[i].fun(cmd + j);
return;
}
}
err_authoriz();
}
void main(argc,argv) void main(argc,argv)
int argc; int argc;
char **argv; char **argv;
{ {
static stralloc cmd = {0};
int match;
sig_alarmcatch(die); sig_alarmcatch(die);
sig_pipeignore(); sig_pipeignore();
@ -205,15 +178,6 @@ char **argv;
if (!*childargs) die_usage(); if (!*childargs) die_usage();
pop3_greet(); pop3_greet();
commands(&ssin,pop3commands);
for (;;) die();
{
if (getln(&ssin,&cmd,&match,'\n') == -1) die();
if (!match) die();
if (cmd.len == 0) die();
if (cmd.s[--cmd.len] != '\n') die();
if ((cmd.len > 0) && (cmd.s[cmd.len - 1] == '\r')) --cmd.len;
cmd.s[cmd.len++] = 0;
doit(cmd.s);
}
} }

View file

@ -4,7 +4,7 @@ qmail-pw2u \- build address assignments from a passwd file
.SH SYNOPSIS .SH SYNOPSIS
.B qmail-pw2u .B qmail-pw2u
[ [
.B \-ohHuUC .B \-/ohHuUC
] ]
[ [
.B \-c\fIchar .B \-c\fIchar
@ -160,7 +160,7 @@ Each line has the form
.I sub .I sub
will be handled by will be handled by
.IR home\fB/.qmail-\fIpre , .IR home\fB/.qmail\-\fIpre ,
where where
.I home .I home
is is
@ -168,7 +168,7 @@ is
home directory; home directory;
.I sub\fBBREAK\fIext .I sub\fBBREAK\fIext
will be handled by will be handled by
.IR home\fB/.qmail-\fIpre\fB-\fIext . .IR home\fB/.qmail\-\fIpre\fB\-\fIext .
.TP .TP
.B append .B append
Extra assignments, Extra assignments,
@ -228,6 +228,12 @@ in place of
.TP .TP
.B \-C .B \-C
Disable the user-extension mechanism. Disable the user-extension mechanism.
.TP
.B \-/
Use
.IR home\fB/.qmail\-/ ...
instead of
.IR home\fB/.qmail\- ...
.SH "SEE ALSO" .SH "SEE ALSO"
qmail-users(5), qmail-users(5),
qmail-lspawn(8), qmail-lspawn(8),

View file

@ -67,6 +67,7 @@ void die_user(s,len) char *s; unsigned int len;
_exit(111); _exit(111);
} }
char *dashcolon = "-:";
int flagalias = 0; int flagalias = 0;
int flagnoupper = 1; int flagnoupper = 1;
int homestrategy = 2; int homestrategy = 2;
@ -154,7 +155,8 @@ void doaccount()
if (str_equal(user.s,auto_usera)) { if (str_equal(user.s,auto_usera)) {
if (substdio_puts(subfdout,"+") == -1) die_write(); if (substdio_puts(subfdout,"+") == -1) die_write();
if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write(); if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
if (substdio_puts(subfdout,"-::\n") == -1) die_write(); if (substdio_puts(subfdout,dashcolon) == -1) die_write();
if (substdio_puts(subfdout,":\n") == -1) die_write();
flagalias = 1; flagalias = 1;
} }
@ -180,7 +182,8 @@ void doaccount()
if (substdio_put(subfdout,mailnames,i) == -1) die_write(); if (substdio_put(subfdout,mailnames,i) == -1) die_write();
if (substdio_put(subfdout,auto_break,1) == -1) die_write(); if (substdio_put(subfdout,auto_break,1) == -1) die_write();
if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write(); if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
if (substdio_puts(subfdout,"-::\n") == -1) die_write(); if (substdio_puts(subfdout,dashcolon) == -1) die_write();
if (substdio_puts(subfdout,":\n") == -1) die_write();
} }
mailnames += i; mailnames += i;
@ -206,7 +209,7 @@ void dosubuser()
if (substdio_puts(subfdout,"=") == -1) die_write(); if (substdio_puts(subfdout,"=") == -1) die_write();
if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write(); if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write();
if (substdio_puts(subfdout,uugh) == -1) die_write(); if (substdio_puts(subfdout,uugh) == -1) die_write();
if (substdio_puts(subfdout,"-:") == -1) die_write(); if (substdio_puts(subfdout,dashcolon) == -1) die_write();
if (substdio_put(subfdout,x,i) == -1) die_write(); if (substdio_put(subfdout,x,i) == -1) die_write();
if (substdio_puts(subfdout,":\n") == -1) die_write(); if (substdio_puts(subfdout,":\n") == -1) die_write();
@ -215,7 +218,7 @@ void dosubuser()
if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write(); if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write();
if (substdio_put(subfdout,auto_break,1) == -1) die_write(); if (substdio_put(subfdout,auto_break,1) == -1) die_write();
if (substdio_puts(subfdout,uugh) == -1) die_write(); if (substdio_puts(subfdout,uugh) == -1) die_write();
if (substdio_puts(subfdout,"-:") == -1) die_write(); if (substdio_puts(subfdout,dashcolon) == -1) die_write();
if (substdio_put(subfdout,x,i) == -1) die_write(); if (substdio_put(subfdout,x,i) == -1) die_write();
if (substdio_puts(subfdout,"-:\n") == -1) die_write(); if (substdio_puts(subfdout,"-:\n") == -1) die_write();
} }
@ -232,8 +235,9 @@ char **argv;
int opt; int opt;
int match; int match;
while ((opt = getopt(argc,argv,"ohHuUc:C")) != opteof) while ((opt = getopt(argc,argv,"/ohHuUc:C")) != opteof)
switch(opt) { switch(opt) {
case '/': dashcolon = "-/:"; break;
case 'o': homestrategy = 2; break; case 'o': homestrategy = 2; break;
case 'h': homestrategy = 1; break; case 'h': homestrategy = 1; break;
case 'H': homestrategy = 0; break; case 'H': homestrategy = 0; break;

35
qmail-qmqpc.8 Normal file
View file

@ -0,0 +1,35 @@
.TH qmail-qmqpc 8
.SH NAME
qmail-qmqpc \- queue a mail message via QMQP
.SH SYNOPSIS
.B qmail-qmqpc
.SH DESCRIPTION
.B qmail-qmqpc
offers the same interface as
.BR qmail-queue ,
but it gives the message to a QMQP server
instead of storing it locally.
In a
.B mini-qmail
installation,
.B qmail-queue
is replaced with a symbolic link to
.BR qmail-qmqpc .
.B qmail-qmqpc
is currently experimental;
use it at your own risk.
It will be fully supported in the next release of
.BR qmail .
.SH "CONTROL FILES"
.TP 5
.I qmqpservers
IP addresses of QMQP servers, one address per line.
.B qmail-qmqpc
will try each address in turn until it establishes a QMQP connection
or runs out of addresses.
.SH "SEE ALSO"
qmail-control(5),
qmail-queue(8),
qmail-qmqpd(8)

161
qmail-qmqpc.c Normal file
View file

@ -0,0 +1,161 @@
/* EXPERIMENTAL! Use at your own risk. */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "substdio.h"
#include "getln.h"
#include "readwrite.h"
#include "exit.h"
#include "stralloc.h"
#include "slurpclose.h"
#include "error.h"
#include "sig.h"
#include "ip.h"
#include "timeoutconn.h"
#include "timeoutread.h"
#include "timeoutwrite.h"
#include "auto_qmail.h"
#include "control.h"
#include "fmt.h"
#define PORT_QMQP 628
void die_success() { _exit(0); }
void die_perm() { _exit(31); }
void nomem() { _exit(51); }
void die_read() { if (errno == error_nomem) nomem(); _exit(54); }
void die_control() { _exit(55); }
void die_socket() { _exit(56); }
void die_home() { _exit(61); }
void die_temp() { _exit(71); }
void die_conn() { _exit(74); }
void die_format() { _exit(91); }
int lasterror = 55;
int qmqpfd;
int saferead(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = timeoutread(60,qmqpfd,buf,len);
if (r <= 0) die_conn();
return r;
}
int safewrite(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = timeoutwrite(60,qmqpfd,buf,len);
if (r <= 0) die_conn();
return r;
}
char buf[1024];
substdio to = SUBSTDIO_FDBUF(safewrite,-1,buf,sizeof buf);
substdio from = SUBSTDIO_FDBUF(saferead,-1,buf,sizeof buf);
substdio envelope = SUBSTDIO_FDBUF(read,1,buf,sizeof buf);
/* WARNING: can use only one of these at a time! */
stralloc beforemessage = {0};
stralloc message = {0};
stralloc aftermessage = {0};
char strnum[FMT_ULONG];
stralloc line = {0};
void getmess()
{
int match;
if (slurpclose(0,&message,1024) == -1) die_read();
strnum[fmt_ulong(strnum,(unsigned long) message.len)] = 0;
if (!stralloc_copys(&beforemessage,strnum)) nomem();
if (!stralloc_cats(&beforemessage,":")) nomem();
if (!stralloc_copys(&aftermessage,",")) nomem();
if (getln(&envelope,&line,&match,'\0') == -1) die_read();
if (!match) die_format();
if (line.len < 2) die_format();
if (line.s[0] != 'F') die_format();
strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0;
if (!stralloc_cats(&aftermessage,strnum)) nomem();
if (!stralloc_cats(&aftermessage,":")) nomem();
if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem();
if (!stralloc_cats(&aftermessage,",")) nomem();
for (;;) {
if (getln(&envelope,&line,&match,'\0') == -1) die_read();
if (!match) die_format();
if (line.len < 2) break;
if (line.s[0] != 'T') die_format();
strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0;
if (!stralloc_cats(&aftermessage,strnum)) nomem();
if (!stralloc_cats(&aftermessage,":")) nomem();
if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem();
if (!stralloc_cats(&aftermessage,",")) nomem();
}
}
void doit(server)
char *server;
{
struct ip_address ip;
char ch;
if (!ip_scan(server,&ip)) return;
qmqpfd = socket(AF_INET,SOCK_STREAM,0);
if (qmqpfd == -1) die_socket();
if (timeoutconn(qmqpfd,&ip,PORT_QMQP,10) != 0) {
lasterror = 73;
if (errno == error_timeout) lasterror = 72;
close(qmqpfd);
return;
}
strnum[fmt_ulong(strnum,(unsigned long) (beforemessage.len + message.len + aftermessage.len))] = 0;
substdio_puts(&to,strnum);
substdio_puts(&to,":");
substdio_put(&to,beforemessage.s,beforemessage.len);
substdio_put(&to,message.s,message.len);
substdio_put(&to,aftermessage.s,aftermessage.len);
substdio_puts(&to,",");
substdio_flush(&to);
for (;;) {
substdio_get(&from,&ch,1);
if (ch == 'K') die_success();
if (ch == 'Z') die_temp();
if (ch == 'D') die_perm();
}
}
stralloc servers = {0};
main()
{
int i;
int j;
sig_pipeignore();
if (chdir(auto_qmail) == -1) die_home();
if (control_init() == -1) die_control();
if (control_readfile(&servers,"control/qmqpservers",0) != 1) die_control();
getmess();
i = 0;
for (j = 0;j < servers.len;++j)
if (!servers.s[j]) {
doit(servers.s + i);
i = j + 1;
}
_exit(lasterror);
}

31
qmail-qmqpd.8 Normal file
View file

@ -0,0 +1,31 @@
.TH qmail-qmqpd 8
.SH NAME
qmail-qmqpd \- receive mail via QMQP
.SH SYNOPSIS
.B qmail-qmqpd
.SH DESCRIPTION
.B qmail-qmqpd
receives mail messages via the Quick Mail Queueing Protocol (QMQP)
and invokes
.B qmail-queue
to deposit them into the outgoing queue.
.B qmail-qmqpd
must be supplied several environment variables;
see
.BR tcp-environ(5) .
.B qmail-qmqpd
will relay messages to any destination.
It should be invoked only for connections from preauthorized users.
.B qmail-qmqpd
is currently experimental;
use it at your own risk.
It will be fully supported in the next release of
.BR qmail .
.SH "SEE ALSO"
tcp-env(1),
tcpserver(1),
tcp-environ(5),
qmail-qmqpc(8),
qmail-queue(8)

178
qmail-qmqpd.c Normal file
View file

@ -0,0 +1,178 @@
/* EXPERIMENTAL! Use at your own risk. */
#include "auto_qmail.h"
#include "qmail.h"
#include "received.h"
#include "sig.h"
#include "substdio.h"
#include "readwrite.h"
#include "exit.h"
#include "now.h"
#include "fmt.h"
#include "env.h"
void resources() { _exit(111); }
int safewrite(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = write(fd,buf,len);
if (r <= 0) _exit(0);
return r;
}
int saferead(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = read(fd,buf,len);
if (r <= 0) _exit(0);
return r;
}
char ssinbuf[512];
substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
char ssoutbuf[256];
substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
unsigned long bytesleft = 100;
void getbyte(ch)
char *ch;
{
if (!bytesleft--) _exit(100);
substdio_get(&ssin,ch,1);
}
unsigned long getlen()
{
unsigned long len = 0;
char ch;
for (;;) {
getbyte(&ch);
if (ch == ':') return len;
if (len > 200000000) resources();
len = 10 * len + (ch - '0');
}
}
void getcomma()
{
char ch;
getbyte(&ch);
if (ch != ',') _exit(100);
}
struct qmail qq;
void identify()
{
char *remotehost;
char *remoteinfo;
char *remoteip;
char *local;
remotehost = env_get("TCPREMOTEHOST");
if (!remotehost) remotehost = "unknown";
remoteinfo = env_get("TCPREMOTEINFO");
remoteip = env_get("TCPREMOTEIP");
if (!remoteip) remoteip = "unknown";
local = env_get("TCPLOCALHOST");
if (!local) local = env_get("TCPLOCALIP");
if (!local) local = "unknown";
received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0);
}
char buf[1000];
char strnum[FMT_ULONG];
int getbuf()
{
unsigned long len;
int i;
len = getlen();
if (len >= 1000) {
for (i = 0;i < len;++i) getbyte(buf);
getcomma();
buf[0] = 0;
return 0;
}
for (i = 0;i < len;++i) getbyte(buf + i);
getcomma();
buf[len] = 0;
return byte_chr(buf,len,'\0') == len;
}
int flagok = 1;
main()
{
char *result;
unsigned long qp;
unsigned long len;
char ch;
sig_pipeignore();
sig_alarmcatch(resources);
alarm(3600);
bytesleft = getlen();
len = getlen();
if (chdir(auto_qmail) == -1) resources();
if (qmail_open(&qq) == -1) resources();
qp = qmail_qp(&qq);
identify();
while (len > 0) { /* XXX: could speed this up */
getbyte(&ch);
--len;
qmail_put(&qq,&ch,1);
}
getcomma();
if (getbuf())
qmail_from(&qq,buf);
else {
qmail_from(&qq,"");
qmail_fail(&qq);
flagok = 0;
}
while (bytesleft)
if (getbuf())
qmail_to(&qq,buf);
else {
qmail_fail(&qq);
flagok = 0;
}
bytesleft = 1;
getcomma();
result = qmail_close(&qq);
if (!*result) {
len = fmt_str(buf,"Kok ");
len += fmt_ulong(buf + len,(unsigned long) now());
len += fmt_str(buf + len," qp ");
len += fmt_ulong(buf + len,qp);
buf[len] = 0;
result = buf;
}
if (!flagok)
result = "Dsorry, I can't accept addresses like that (#5.1.3)";
fmt_ulong(strnum,str_len(result));
substdio_puts(&ssout,strnum);
substdio_puts(&ssout,":");
substdio_puts(&ssout,result);
substdio_puts(&ssout,",");
substdio_flush(&ssout);
_exit(0);
}

View file

@ -16,9 +16,12 @@ see
.B qmail-qmtpd .B qmail-qmtpd
supports the supports the
.I rcpthosts .IR rcpthosts ,
.IR morercpthosts ,
.BR RELAYCLIENT ,
.IR databytes ,
and and
.B RELAYCLIENT .B DATABYTES
mechanisms described in mechanisms described in
.BR qmail-smtpd(8) . .BR qmail-smtpd(8) .
.SH "SEE ALSO" .SH "SEE ALSO"

Some files were not shown because too many files have changed in this diff Show more