summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BIN.Makefile24
-rw-r--r--BIN.README19
-rw-r--r--BLURB43
-rw-r--r--BLURB226
-rw-r--r--BLURB393
-rw-r--r--BLURB444
-rw-r--r--CHANGES1484
-rw-r--r--COPYRIGHT33
-rw-r--r--FAQ709
-rw-r--r--FILES434
-rw-r--r--INSTALL1
-rw-r--r--INSTALL.alias40
-rw-r--r--INSTALL.ctl38
-rw-r--r--INSTALL.ids72
-rw-r--r--INSTALL.maildir59
-rw-r--r--INSTALL.mbox53
-rw-r--r--INSTALL.vsm50
-rw-r--r--INTERNALS156
-rw-r--r--Makefile2141
-rw-r--r--PIC.local2alias37
-rw-r--r--PIC.local2ext41
-rw-r--r--PIC.local2local40
-rw-r--r--PIC.local2rem38
-rw-r--r--PIC.local2virt44
-rw-r--r--PIC.nullclient38
-rw-r--r--PIC.relaybad8
-rw-r--r--PIC.relaygood33
-rw-r--r--PIC.rem2local36
-rw-r--r--README295
-rw-r--r--REMOVE.binmail16
-rw-r--r--REMOVE.sendmail28
-rw-r--r--SECURITY131
-rw-r--r--SENDMAIL76
-rw-r--r--SYSDEPS17
-rw-r--r--TARGETS387
-rw-r--r--TEST.deliver82
-rw-r--r--TEST.receive41
-rw-r--r--THANKS338
-rw-r--r--THOUGHTS418
-rw-r--r--TODO23
-rw-r--r--UPGRADE66
-rw-r--r--VERSION1
-rw-r--r--addresses.5260
-rw-r--r--alloc.362
-rw-r--r--alloc.c32
-rw-r--r--alloc.h8
-rw-r--r--alloc_re.c17
-rw-r--r--auto-gid.c51
-rw-r--r--auto-int.c40
-rw-r--r--auto-int8.c40
-rw-r--r--auto-str.c44
-rw-r--r--auto-uid.c51
-rw-r--r--auto_break.h6
-rw-r--r--auto_patrn.h6
-rw-r--r--auto_qmail.h6
-rw-r--r--auto_spawn.h6
-rw-r--r--auto_split.h6
-rw-r--r--auto_uids.h16
-rw-r--r--auto_usera.h6
-rw-r--r--binm1+df.sh11
-rw-r--r--binm1.sh10
-rw-r--r--binm2+df.sh11
-rw-r--r--binm2.sh10
-rw-r--r--binm3+df.sh11
-rw-r--r--binm3.sh10
-rw-r--r--bouncesaying.171
-rw-r--r--bouncesaying.c41
-rw-r--r--byte.h13
-rw-r--r--byte_chr.c20
-rw-r--r--byte_copy.c14
-rw-r--r--byte_cr.c16
-rw-r--r--byte_diff.c16
-rw-r--r--byte_rchr.c23
-rw-r--r--byte_zero.c13
-rw-r--r--case.3100
-rw-r--r--case.h13
-rw-r--r--case_diffb.c21
-rw-r--r--case_diffs.c19
-rw-r--r--case_lowerb.c14
-rw-r--r--case_lowers.c12
-rw-r--r--case_starts.c18
-rw-r--r--cdb.362
-rw-r--r--cdb.h12
-rw-r--r--cdb_hash.c16
-rw-r--r--cdb_seek.c94
-rw-r--r--cdb_unpack.c12
-rw-r--r--cdbmake.h35
-rw-r--r--cdbmake_add.c118
-rw-r--r--cdbmake_hash.c10
-rw-r--r--cdbmake_pack.c11
-rw-r--r--cdbmss.c65
-rw-r--r--cdbmss.h16
-rw-r--r--chkshsgr.c9
-rw-r--r--chkspawn.c48
-rw-r--r--coe.325
-rw-r--r--coe.c8
-rw-r--r--coe.h6
-rw-r--r--commands.c40
-rw-r--r--commands.h12
-rw-r--r--condredirect.163
-rw-r--r--condredirect.c85
-rw-r--r--conf-break9
-rw-r--r--conf-cc3
-rw-r--r--conf-groups6
-rw-r--r--conf-ld3
-rw-r--r--conf-patrn6
-rw-r--r--conf-qmail11
-rw-r--r--conf-spawn5
-rw-r--r--conf-split3
-rw-r--r--conf-users15
-rw-r--r--config-fast.sh30
-rw-r--r--config.sh64
-rw-r--r--constmap.c114
-rw-r--r--constmap.h20
-rw-r--r--control.c130
-rw-r--r--control.h10
-rw-r--r--date822fmt.c29
-rw-r--r--date822fmt.h7
-rw-r--r--datemail.sh1
-rw-r--r--datetime.373
-rw-r--r--datetime.c55
-rw-r--r--datetime.h20
-rw-r--r--datetime_un.c35
-rw-r--r--direntry.336
-rw-r--r--direntry.h18
-rw-r--r--direntry.h28
-rw-r--r--dns.c398
-rw-r--r--dns.h14
-rw-r--r--dnscname.c25
-rw-r--r--dnsdoe.c16
-rw-r--r--dnsdoe.h6
-rw-r--r--dnsfq.c32
-rw-r--r--dnsip.c34
-rw-r--r--dnsmxip.c40
-rw-r--r--dnsptr.c27
-rw-r--r--dot-qmail.9394
-rw-r--r--elq.sh1
-rw-r--r--env.331
-rw-r--r--env.c113
-rw-r--r--env.h17
-rw-r--r--envelopes.5231
-rw-r--r--envread.c30
-rw-r--r--error.345
-rw-r--r--error.c95
-rw-r--r--error.h23
-rw-r--r--error_str.319
-rw-r--r--error_str.c276
-rw-r--r--error_temp.327
-rw-r--r--error_temp.c80
-rw-r--r--except.133
-rw-r--r--except.c37
-rw-r--r--exit.h6
-rw-r--r--extra.h7
-rw-r--r--fd.h7
-rw-r--r--fd_copy.344
-rw-r--r--fd_copy.c13
-rw-r--r--fd_move.341
-rw-r--r--fd_move.c11
-rw-r--r--fifo.c10
-rw-r--r--fifo.h6
-rw-r--r--fifo_make.324
-rw-r--r--find-systype.sh144
-rw-r--r--fmt.h25
-rw-r--r--fmt_str.c12
-rw-r--r--fmt_strn.c12
-rw-r--r--fmt_uint.c6
-rw-r--r--fmt_uint0.c10
-rw-r--r--fmt_ulong.c13
-rw-r--r--fmtqfn.c24
-rw-r--r--fmtqfn.h8
-rw-r--r--forgeries.7104
-rw-r--r--fork.h17
-rw-r--r--fork.h27
-rw-r--r--forward.124
-rw-r--r--forward.c60
-rw-r--r--gen_alloc.h7
-rw-r--r--gen_allocdefs.h34
-rw-r--r--getln.351
-rw-r--r--getln.c20
-rw-r--r--getln.h7
-rw-r--r--getln2.364
-rw-r--r--getln2.c31
-rw-r--r--gfrom.c10
-rw-r--r--gfrom.h6
-rw-r--r--headerbody.c87
-rw-r--r--headerbody.h6
-rw-r--r--hfield.c125
-rw-r--r--hfield.h38
-rw-r--r--hier.c252
-rw-r--r--home+df.sh9
-rw-r--r--home.sh7
-rw-r--r--hostname.c17
-rw-r--r--idedit.c147
-rw-r--r--install-big.c285
-rw-r--r--install.c164
-rw-r--r--instcheck.c108
-rw-r--r--ip.c53
-rw-r--r--ip.h11
-rw-r--r--ipalloc.c7
-rw-r--r--ipalloc.h14
-rw-r--r--ipme.c100
-rw-r--r--ipme.h12
-rw-r--r--ipmeprint.c24
-rw-r--r--lock.h8
-rw-r--r--lock_ex.c11
-rw-r--r--lock_exnb.c11
-rw-r--r--lock_un.c11
-rw-r--r--maildir.5239
-rw-r--r--maildir.c108
-rw-r--r--maildir.h12
-rw-r--r--maildir2mbox.153
-rw-r--r--maildir2mbox.c162
-rw-r--r--maildirmake.115
-rw-r--r--maildirmake.c24
-rw-r--r--maildirwatch.123
-rw-r--r--maildirwatch.c125
-rw-r--r--mailsubj.138
-rw-r--r--mailsubj.sh7
-rw-r--r--make-compile.sh1
-rw-r--r--make-load.sh2
-rw-r--r--make-makelib.sh16
-rw-r--r--mbox.5235
-rw-r--r--myctime.c37
-rw-r--r--myctime.h6
-rw-r--r--ndelay.c13
-rw-r--r--ndelay.h7
-rw-r--r--ndelay_off.c13
-rw-r--r--newfield.c68
-rw-r--r--newfield.h12
-rw-r--r--now.314
-rw-r--r--now.c8
-rw-r--r--now.h8
-rw-r--r--old-patches/README3
-rw-r--r--old-patches/netqmail-1.04.patch414
-rw-r--r--old-patches/qmail-isoc.patch362
-rw-r--r--open.h10
-rw-r--r--open_append.c6
-rw-r--r--open_excl.c6
-rw-r--r--open_read.c6
-rw-r--r--open_trunc.c6
-rw-r--r--open_write.c6
-rw-r--r--other-patches/README51
-rw-r--r--other-patches/checkpassword-0.90.errno.patch12
-rw-r--r--other-patches/daemontools-0.76.errno.patch12
-rw-r--r--other-patches/djbdns-1.05.errno.patch12
-rw-r--r--other-patches/dot-forward-0.71.errno.patch12
-rw-r--r--other-patches/fastforward-0.51.errno.patch22
-rw-r--r--other-patches/mess822-0.58.errno.patch33
-rw-r--r--other-patches/qmailanalog-0.70.errno.patch87
-rw-r--r--other-patches/serialmail-0.75.errno.patch23
-rw-r--r--other-patches/ucspi-tcp-0.88.a_record.patch64
-rw-r--r--other-patches/ucspi-tcp-0.88.errno.patch12
-rw-r--r--other-patches/ucspi-tcp-0.88.nodefaultrbl.patch28
-rw-r--r--pinq.sh1
-rw-r--r--predate.c116
-rw-r--r--preline.157
-rw-r--r--preline.c90
-rw-r--r--prioq.c58
-rw-r--r--prioq.h15
-rw-r--r--proc+df.sh9
-rw-r--r--proc.sh7
-rw-r--r--prot.c21
-rw-r--r--prot.h7
-rw-r--r--qail.sh1
-rw-r--r--qbiff.131
-rw-r--r--qbiff.c113
-rw-r--r--qlx.h18
-rw-r--r--qmail-clean.813
-rw-r--r--qmail-clean.c98
-rw-r--r--qmail-command.8149
-rw-r--r--qmail-control.978
-rw-r--r--qmail-getpw.9114
-rw-r--r--qmail-getpw.c88
-rw-r--r--qmail-header.5332
-rw-r--r--qmail-inject.8309
-rw-r--r--qmail-inject.c773
-rw-r--r--qmail-limits.930
-rw-r--r--qmail-local.899
-rw-r--r--qmail-local.c698
-rw-r--r--qmail-log.5266
-rw-r--r--qmail-lspawn.846
-rw-r--r--qmail-lspawn.c234
-rw-r--r--qmail-newmrh.941
-rw-r--r--qmail-newmrh.c70
-rw-r--r--qmail-newu.943
-rw-r--r--qmail-newu.c137
-rw-r--r--qmail-pop3d.843
-rw-r--r--qmail-pop3d.c305
-rw-r--r--qmail-popup.865
-rw-r--r--qmail-popup.c183
-rw-r--r--qmail-pw2u.9241
-rw-r--r--qmail-pw2u.c312
-rw-r--r--qmail-qmqpc.829
-rw-r--r--qmail-qmqpc.c159
-rw-r--r--qmail-qmqpd.825
-rw-r--r--qmail-qmqpd.c174
-rw-r--r--qmail-qmtpd.832
-rw-r--r--qmail-qmtpd.c268
-rw-r--r--qmail-qread.824
-rw-r--r--qmail-qread.c175
-rw-r--r--qmail-qstat.818
-rw-r--r--qmail-qstat.sh7
-rw-r--r--qmail-queue.8161
-rw-r--r--qmail-queue.c254
-rw-r--r--qmail-remote.8205
-rw-r--r--qmail-remote.c427
-rw-r--r--qmail-rspawn.821
-rw-r--r--qmail-rspawn.c103
-rw-r--r--qmail-send.9246
-rw-r--r--qmail-send.c1612
-rw-r--r--qmail-showctl.812
-rw-r--r--qmail-showctl.c306
-rw-r--r--qmail-smtpd.8179
-rw-r--r--qmail-smtpd.c421
-rw-r--r--qmail-start.994
-rw-r--r--qmail-start.c120
-rw-r--r--qmail-tcpok.824
-rw-r--r--qmail-tcpok.c35
-rw-r--r--qmail-tcpto.830
-rw-r--r--qmail-tcpto.c85
-rw-r--r--qmail-upq.sh14
-rw-r--r--qmail-users.9113
-rw-r--r--qmail.768
-rw-r--r--qmail.c136
-rw-r--r--qmail.h24
-rw-r--r--qreceipt.133
-rw-r--r--qreceipt.c131
-rw-r--r--qsmhook.c137
-rw-r--r--qsutil.c46
-rw-r--r--qsutil.h12
-rw-r--r--quote.c83
-rw-r--r--quote.h8
-rw-r--r--rcpthosts.c60
-rw-r--r--rcpthosts.h7
-rw-r--r--readsubdir.c49
-rw-r--r--readsubdir.h20
-rw-r--r--readwrite.h7
-rw-r--r--received.c71
-rw-r--r--received.h6
-rw-r--r--remoteinfo.c77
-rw-r--r--remoteinfo.h6
-rw-r--r--scan.h27
-rw-r--r--scan_8long.c11
-rw-r--r--scan_ulong.c11
-rw-r--r--seek.h15
-rw-r--r--seek_cur.c7
-rw-r--r--seek_end.c7
-rw-r--r--seek_set.c7
-rw-r--r--seek_trunc.c5
-rw-r--r--select.h18
-rw-r--r--select.h29
-rw-r--r--sendmail.c162
-rw-r--r--sgetopt.328
-rw-r--r--sgetopt.c54
-rw-r--r--sgetopt.h21
-rw-r--r--sig.h43
-rw-r--r--sig_alarm.c7
-rw-r--r--sig_block.c40
-rw-r--r--sig_bug.c17
-rw-r--r--sig_catch.c18
-rw-r--r--sig_child.c7
-rw-r--r--sig_hup.c7
-rw-r--r--sig_misc.c17
-rw-r--r--sig_pause.c14
-rw-r--r--sig_pipe.c5
-rw-r--r--sig_term.c7
-rw-r--r--slurpclose.c19
-rw-r--r--slurpclose.h6
-rw-r--r--spawn.c260
-rw-r--r--splogger.860
-rw-r--r--splogger.c72
-rw-r--r--str.h14
-rw-r--r--str_chr.c19
-rw-r--r--str_cpy.c16
-rw-r--r--str_diff.c17
-rw-r--r--str_diffn.c18
-rw-r--r--str_len.c15
-rw-r--r--str_rchr.c22
-rw-r--r--str_start.c15
-rw-r--r--stralloc.3160
-rw-r--r--stralloc.h21
-rw-r--r--stralloc_arts.c12
-rw-r--r--stralloc_cat.c9
-rw-r--r--stralloc_catb.c15
-rw-r--r--stralloc_cats.c10
-rw-r--r--stralloc_copy.c9
-rw-r--r--stralloc_eady.c6
-rw-r--r--stralloc_opyb.c14
-rw-r--r--stralloc_opys.c10
-rw-r--r--stralloc_pend.c5
-rw-r--r--strerr.h80
-rw-r--r--strerr_die.c37
-rw-r--r--strerr_sys.c12
-rw-r--r--subfd.h15
-rw-r--r--subfderr.c7
-rw-r--r--subfdin.c13
-rw-r--r--subfdins.c13
-rw-r--r--subfdout.c7
-rw-r--r--subfdouts.c7
-rw-r--r--subgetopt.3357
-rw-r--r--subgetopt.c79
-rw-r--r--subgetopt.h24
-rw-r--r--substdi.c91
-rw-r--r--substdio.c15
-rw-r--r--substdio.h47
-rw-r--r--substdio_copy.c18
-rw-r--r--substdo.c108
-rw-r--r--tcp-env.167
-rw-r--r--tcp-env.c129
-rw-r--r--tcp-environ.562
-rw-r--r--tcpto.c165
-rw-r--r--tcpto.h8
-rw-r--r--tcpto_clean.c20
-rw-r--r--timeoutconn.c59
-rw-r--r--timeoutconn.h6
-rw-r--r--timeoutread.c22
-rw-r--r--timeoutread.h6
-rw-r--r--timeoutwrite.c22
-rw-r--r--timeoutwrite.h6
-rw-r--r--token822.c513
-rw-r--r--token822.h37
-rw-r--r--trigger.c41
-rw-r--r--trigger.h8
-rw-r--r--triggerpull.c16
-rw-r--r--triggerpull.h6
-rw-r--r--trycpp.c7
-rw-r--r--trydrent.c8
-rw-r--r--tryflock.c8
-rw-r--r--trylsock.c4
-rw-r--r--trymkffo.c7
-rw-r--r--trynpbg1.c26
-rw-r--r--tryrsolv.c4
-rw-r--r--trysalen.c11
-rw-r--r--trysgact.c10
-rw-r--r--trysgprm.c10
-rw-r--r--tryshsgr.c14
-rw-r--r--trysysel.c8
-rw-r--r--trysyslog.c9
-rw-r--r--tryulong32.c11
-rw-r--r--tryvfork.c4
-rw-r--r--trywaitp.c7
-rw-r--r--uint32.h16
-rw-r--r--uint32.h26
-rw-r--r--wait.393
-rw-r--r--wait.h14
-rw-r--r--wait_nohang.c12
-rw-r--r--wait_pid.c39
-rw-r--r--warn-auto.sh2
-rw-r--r--warn-shsgr3
449 files changed, 32673 insertions, 0 deletions
diff --git a/BIN.Makefile b/BIN.Makefile
new file mode 100644
index 0000000..f46c537
--- /dev/null
+++ b/BIN.Makefile
@@ -0,0 +1,24 @@
+SHELL=/bin/sh
+
+# Files are edited in the installation directory, then copied.
+# There are 40 arguments to idedit after the filename,
+# showing the positions of each byte in the following ten ints:
+# uida, uidd, uidl, uido, uidp, uidq, uidr, uids, gidq, gidn.
+# Normal little-endian positions are n n+1 n+2 ... n+39 for some n.
+# Normal big-endian positions are n+3 n+2 n+1 n n+7 ... n+36 for some n.
+
+setup:
+ mkdir /var/qmail
+ ./idedit install-big XXX
+ ./idedit qmail-lspawn XXX
+ ./idedit qmail-queue XXX
+ ./idedit qmail-rspawn XXX
+ ./idedit qmail-showctl XXX
+ ./idedit qmail-start XXX
+ ./install-big
+ cp /var/qmail/boot/binm1+df /var/qmail/rc
+ chmod 755 /var/qmail/rc
+ echo '|fastforward -d /etc/aliases.cdb' > /var/qmail/alias/.qmail-default
+ chmod 644 /var/qmail/alias/.qmail-default
+ hostname | grep -q '\.'
+ ./config-fast `hostname`
diff --git a/BIN.README b/BIN.README
new file mode 100644
index 0000000..6beeee1
--- /dev/null
+++ b/BIN.README
@@ -0,0 +1,19 @@
+Like any other piece of software (and information generally), qmail
+comes with NO WARRANTY.
+
+Configuration: The qmail home directory is /var/qmail. (This must be a
+local directory, not shared among machines. Under Linux, make sure that
+all mail-handling filesystems are mounted with synchronous metadata.)
+The user-ext delimiter is -. The silent concurrency limit is 120. The
+queue subdirectory split is 23.
+
+To install:
+ # make setup
+
+To set up qmail to receive and deliver mail, follow the instructions in
+/var/qmail/doc/fastforward/ALIASES, and then start at step 9 of
+/var/qmail/doc/INSTALL.
+
+Compilation environment: Oops, the package creator forgot to edit this!
+He's supposed to list his OS version, compiler version, hardware, and
+name.
diff --git a/BLURB b/BLURB
new file mode 100644
index 0000000..7bcfb3f
--- /dev/null
+++ b/BLURB
@@ -0,0 +1,43 @@
+qmail is a secure, reliable, efficient, simple message transfer agent.
+It is meant as a replacement for the entire sendmail-binmail system on
+typical Internet-connected UNIX hosts.
+
+Secure: Security isn't just a goal, but an absolute requirement. Mail
+delivery is critical for users; it cannot be turned off, so it must be
+completely secure. (This is why I started writing qmail: I was sick of
+the security holes in sendmail and other MTAs.)
+
+Reliable: qmail's straight-paper-path philosophy guarantees that a
+message, once accepted into the system, will never be lost. qmail also
+supports maildir, a new, super-reliable user mailbox format. Maildirs,
+unlike mbox files and mh folders, won't be corrupted if the system
+crashes during delivery. Even better, not only can a user safely read
+his mail over NFS, but any number of NFS clients can deliver mail to him
+at the same time.
+
+Efficient: On a Pentium under BSD/OS, qmail can easily sustain 200000
+local messages per day---that's separate messages injected and delivered
+to mailboxes in a real test! Although remote deliveries are inherently
+limited by the slowness of DNS and SMTP, qmail overlaps 20 simultaneous
+deliveries by default, so it zooms quickly through mailing lists. (This
+is why I finished qmail: I had to get a big mailing list set up.)
+
+Simple: qmail is vastly smaller than any other Internet MTA. Some
+reasons why: (1) Other MTAs have separate forwarding, aliasing, and
+mailing list mechanisms. qmail has one simple forwarding mechanism that
+lets users handle their own mailing lists. (2) Other MTAs offer a
+spectrum of delivery modes, from fast+unsafe to slow+queued. qmail-send
+is instantly triggered by new items in the queue, so the qmail system
+has just one delivery mode: fast+queued. (3) Other MTAs include, in
+effect, a specialized version of inetd that watches the load average.
+qmail's design inherently limits the machine load, so qmail-smtpd can
+safely run from your system's inetd.
+
+Replacement for sendmail: qmail supports host and user masquerading,
+full host hiding, virtual domains, null clients, list-owner rewriting,
+relay control, double-bounce recording, arbitrary RFC 822 address lists,
+cross-host mailing list loop detection, per-recipient checkpointing,
+downed host backoffs, independent message retry schedules, etc. In
+short, it's up to speed on modern MTA features. qmail also includes a
+drop-in ``sendmail'' wrapper so that it will be used transparently by
+your current UAs.
diff --git a/BLURB2 b/BLURB2
new file mode 100644
index 0000000..1a98d0e
--- /dev/null
+++ b/BLURB2
@@ -0,0 +1,26 @@
+Mailing list management is one of qmail's strengths. Notable features:
+
+* qmail lets each user handle his own mailing lists. The delivery
+instructions for user-whatever go into ~user/.qmail-whatever.
+
+* qmail makes it really easy to set up mailing list owners. If the user
+touches ~user/.qmail-whatever-owner, all bounces will come back to him.
+
+* qmail supports VERPs, which permit completely reliable automated
+bounce handling for mailing lists of any size.
+
+* SPEED---qmail blasts through mailing lists an order of magnitude
+faster than sendmail. For example, one message was successfully
+delivered to 150 hosts around the world in just 70 seconds, with qmail's
+out-of-the-box configuration.
+
+* qmail automatically prevents mailing list loops, even across hosts.
+
+* qmail allows inconceivably gigantic mailing lists. No random limits.
+
+* qmail handles aliasing and forwarding with the same simple mechanism.
+For example, Postmaster is controlled by ~alias/.qmail-postmaster. This
+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.
diff --git a/BLURB3 b/BLURB3
new file mode 100644
index 0000000..5f8af6d
--- /dev/null
+++ b/BLURB3
@@ -0,0 +1,93 @@
+Here are some of qmail's features.
+
+Setup:
+* automatic adaptation to your UNIX variant---no configuration needed
+* AIX, BSD/OS, FreeBSD, HP/UX, Irix, Linux, OSF/1, SunOS, Solaris, and more
+* automatic per-host configuration (config, config-fast)
+* quick installation---no big list of decisions to make
+
+Security:
+* clear separation between addresses, files, and programs
+* minimization of setuid code (qmail-queue)
+* minimization of root code (qmail-start, qmail-lspawn)
+* five-way trust partitioning---security in depth
+* optional logging of one-way hashes, entire contents, etc. (QUEUE_EXTRA)
+
+Message construction (qmail-inject):
+* RFC 822, RFC 1123
+* full support for address groups
+* 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
+* host masquerading (control/defaulthost)
+* user masquerading ($MAILUSER, $MAILHOST)
+* automatic Mail-Followup-To creation ($QMAILMFTFILE)
+
+SMTP service (qmail-smtpd):
+* RFC 821, RFC 1123, RFC 1651, RFC 1652, RFC 1854
+* 8-bit clean
+* 931/1413/ident/TAP callback (tcp-env)
+* relay control---stop unauthorized relaying by outsiders (control/rcpthosts)
+* no interference between relay control and forwarding
+* tcpd hook---reject SMTP connections from known abusers
+* automatic recognition of local IP addresses
+* per-buffer timeouts
+* hop counting
+
+Queue management (qmail-send):
+* instant handling of messages added to queue
+* parallelism limit (control/concurrencyremote, control/concurrencylocal)
+* split queue directory---no slowdown when queue gets big
+* quadratic retry schedule---old messages tried less often
+* independent message retry schedules
+* automatic safe queueing---no loss of mail if system crashes
+* automatic per-recipient checkpointing
+* automatic queue cleanups (qmail-clean)
+* queue viewing (qmail-qread)
+* detailed delivery statistics (qmailanalog, available separately)
+
+Bounces (qmail-send):
+* QSBMF bounce messages---both machine-readable and human-readable
+* HCMSSC support---language-independent RFC 1893 error codes
+* double bounces sent to postmaster
+
+Routing by domain (qmail-send):
+* any number of names for local host (control/locals)
+* any number of virtual domains (control/virtualdomains)
+* domain wildcards (control/virtualdomains)
+* configurable percent hack support (control/percenthack)
+* UUCP hook
+
+SMTP delivery (qmail-remote):
+* RFC 821, RFC 974, RFC 1123
+* 8-bit clean
+* automatic downed host backoffs
+* artificial routing---smarthost, localnet, mailertable (control/smtproutes)
+* per-buffer timeouts
+* passive SMTP queue---perfect for SLIP/PPP (serialmail, available separately)
+
+Forwarding and mailing lists (qmail-local):
+* address wildcards (.qmail-default, .qmail-foo-default, etc.)
+* 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
+* VERPs---automatic recipient identification for mailing list bounces
+* Delivered-To---automatic loop prevention, even across hosts
+* automatic mailing list management (ezmlm, available separately)
+
+Local delivery (qmail-local):
+* user-controlled address hierarchy---fred controls fred-anything
+* mbox delivery
+* reliable NFS delivery (maildir)
+* user-controlled program delivery: procmail etc. (qmail-command)
+* optional new-mail notification (qbiff)
+* optional NRUDT return receipts (qreceipt)
+* conditional filtering (condredirect, bouncesaying)
+
+POP3 service (qmail-popup, qmail-pop3d):
+* RFC 1939
+* UIDL support
+* TOP support
+* APOP hook
+* modular password checking (checkpassword, available separately)
diff --git a/BLURB4 b/BLURB4
new file mode 100644
index 0000000..6c4eaac
--- /dev/null
+++ b/BLURB4
@@ -0,0 +1,44 @@
+qmail's modular, lightweight design and sensible queue management make
+it the fastest available message transfer agent. Here's how it stacks up
+against the competition in five different speed measurements.
+
+* 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
+over 9 million deliveries a day! Compare this to the speed advertised
+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,
+with the default qmail configuration. qmail's logs were piped through
+accustamp and written to disk as usual.)
+
+* Local mailing lists: When qmail is delivering a message to a mailbox,
+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
+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
+deliveries a day! Sending 1024 copies to a _single_ mailbox was just as
+fast. Compare these figures to Zmailer's advertised rate for throwing
+recipients away without even delivering the message---only 0.48 million
+per day on the SparcStation.
+
+* Mailing lists with remote recipients: qmail uses the same delivery
+strategy that makes LSOFT's LSMTP so fast for outgoing mailing lists---
+you choose how many parallel SMTP connections you want to run, and qmail
+runs exactly that many. Of course, performance varies depending on how
+far away your recipients are. The advantage of qmail over other packages
+is its smallness: for example, one Linux user is running 60 simultaneous
+connections, without swapping, on a machine with just 16MB of memory!
+
+* Separate local messages: What LSOFT doesn't tell you about LSMTP is
+how many _separate_ messages it can handle in a day. Does it get bogged
+down as the queue fills up? On my home machine, I disabled qmail's
+deliveries and then sent 5000 separate messages to one recipient. The
+messages were all safely written to the queue disk in 23 minutes, with
+no slowdown as the queue filled up. After I reenabled deliveries, all
+the messages were delivered to the recipient's mailbox in under 12
+minutes. End-to-end rate: more than 200000 individual messages a day!
+
+* Overall performance: What really matters is how well qmail performs
+with your mail load. Red Hat Software found one day that their mail hub,
+a 48MB Pentium running sendmail 8.7, was running out of steam at 70000
+messages a day. They shifted the load to qmail---on a _smaller_ machine,
+a 16MB 486/66---and now they're doing fine.
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..bb20670
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,1484 @@
+20071130 version: netqmail 1.06
+20071130 legal: qmail-1.03 is now in the public domain
+20051103 doc: dot-qmail.9 updated for changed (19980613) conf-patrn default, tnx ADM
+20040121 version: netqmail 1.05
+20040121 code: qmail-smtpd is protected from exceedingly long (eg 2GB)
+ header lines
+20040121 code: qmail_lspawn, qmail-newmrh, qmail-newu, and qmail-rspawn
+ are protected from misbehaving on hosts where the size of an
+ integer is not the same as the size of a character pointer
+ (eg 64 bit hosts with 32 bit ints)
+20031027 version: netqmail 1.04.
+20031027 doc: INSTALL points to http://lifewithqmail.org/lwq.html
+20031027 doc: qmail.7 identifies installation as netqmail and points to
+ http://qmail.org/
+20031027 doc: qmail-queue.8 adds explanation of $QMAILQUEUE
+20031027 doc: qmail-log.5 adds reference to errors from $QMAILQUEUE script
+20031027 doc: FAQ also points to http://cr.yp.to/qmail/faq.html and
+ http://qmail.org/
+20031027 code: qmail-smtpd identifies itself as netqmail
+20031027 code: if $QMAILQUEUE is set, it's invoked instead of qmail-queue
+20031024 code: changed errno from int to #include.
+20031024 code: fixed .qmail parsing bug.
+20031024 code: recognize 0.0.0.0 as a local address.
+20031024 code: sendmail's -f flag now overrides environment variables.
+19980615 version: qmail 1.03.
+19980614 doc: eliminated BIN.setup in favor of a web page.
+19980614 code: added other auto* to qmail-showctl output.
+19980614 doc: added pointer to immhf.html in qmail-header.5.
+19980614 doc: added note to TEST.receive about SMTP command format.
+19980614 doc: added FAQ 5.6 on qmail-qmqpd.
+19980614 code: removed unused variables in idedit.c.
+19980613 code: changed conf-patrn to 002.
+19980613 doc: moved SENDMAIL lower in INSTALL.
+19980612 code: added install-big.
+19980612 code: added BIN.Makefile.
+19980612 doc: added BIN.README, BIN.setup.
+19980612 code: switched to new install.
+19980611 code: added idedit.
+19980611 doc: added FAQ 1.3 on $QMAILMFTFILE.
+19980611 doc: used bouncesaying in FAQ 5.5.
+19980611 code: added except.
+19980611 code: added bouncesaying.
+19980611 code: allowed unbracketed IP addresses in dns_ipplus() and
+ dns_mxip().
+19980611 code: allowed spaces after colon in non-bracketed addresses in
+ qmail-smtpd.
+19980611 doc: cleaned up UPGRADE.
+19980528 bug: qmail-smtpd skips first character in rcpthosts() call.
+ tnx NND. impact: qmail-smtpd crashes on empty address; and it
+ allows relaying to ""@any.host. fix: use addr.s.
+19980515 doc: expanded flock discussion in INSTALL.mbox.
+19980515 doc: eliminated flock warning from INSTALL.maildir.
+19980515 doc: split REMOVE.binmail out of INSTALL.
+19980515 doc: split REMOVE.sendmail out of INSTALL.
+19980515 doc: split TEST.deliver and TEST.receive out of INSTALL and
+ UPGRADE.
+19980515 doc: integrated INSTALL.boot into INSTALL.
+19980515 code: cleaned up final output in qmail-qmqpd.c.
+19980514 doc: updated procmail notes in INSTALL.mbox. tnx JRM.
+19980514 doc: changed FAQ 4.4 to point to INSTALL.mbox for procmail.
+ tnx JRM.
+19980514 code: separated HELO and EHLO; single-line response for HELO.
+ tnx to various people.
+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.
+19970414 doc: tightened up qmail-upgrade.7.
+19970414 code: rewrote rewrite().
+19970414 code: implemented recipientmap. suggested by RDM.
+19970414 doc: auto-configured qmail home directory in qmail-control.5,
+ qmail-newu.8, qmail-pw2u.8, qmail-start.8, qmail-users.5.
+19970414 port: Solaris needs socket libs for gethostname. impact: can't
+ compile under Solaris. fix: use socket.lib for qmail-local.
+19970412 code: introduced stralloc_starts.
+19970412 code: introduced str_equal.
+19970412 code: introduced str_start.
+19970412 code: introduced byte_equal.
+19970412 code: made an optional aliasempty arg for qmail-start.
+19970412 code: made an aliasempty arg for qmail-lspawn.
+19970412 code: changed ALIAS_EMPTY to an arg for qmail-local.
+19970412 port: UnixWare returns >0 for SIOCGIFCONF. impact: ipme fails
+ under UnixWare. fix: check for >=0, not =0. tnx JD.
+19970412 port: DGUX does not have ranlib. impact: can't compile under
+ DGUX. fix: added dgux line to make-makelib. tnx HWM.
+19970412 code: changed maildir library to skip any filename beginning
+ with dot. tnx SP.
+19970412 doc: added FAQ entry about aliases with dots.
+19970412 doc: clarified in qmail-inject.8 that default envelope sender
+ is the same as _default_ From address.
+19970411 code: renamed qmail-makectl as qmail-config.
+19970411 code: renamed qmail-alias as qmail-local.
+19970411 code: switched from signal library to sig library.
+19970411 code: switched from qqtalk library to qmail library.
+19970411 code: switched from getline library to getln library.
+19970411 code: massive library cleanups.
+19970411 code: revamped autoconfiguration system.
+19970411 code: revamped configuration interface.
+19970411 code: eliminated qmail-home.
+19970411 code: eliminated tokenize.
+19970220 qmail 1.00.
+19970219 change: various documentation tweaks.
+19970218 change: updated THOUGHTS.
+19970218 change: talked about SPAWN_NUMD in FAQ. tnx EC.
+19970210 change: noted in maildir.5 that readers should skip any name
+ starting with a dot. tnx SP.
+19970209 change: added note to splogger.8 about reliability. tnx BT.
+19970209 change: added section to FAQ on slow sendmail switch. tnx BT.
+19970206 change: added section to FAQ about dtcm. tnx PJG.
+19970206 change: tweaked maildir.5.
+19970201 change: added MH spost note to FAQ. tnx TU.
+19970131 change: reorganized FAQ.
+19970131 change: added web references to FAQ.
+19970124 change: tweaked qmail-upgrade man page.
+19970120 qmail 0.96, gamma.
+19970120 change: removed various try* in auto-configuration.
+19970120 bug: qmail-inject fails to quote argument addresses. impact:
+ addresses containing special characters could be misinterpreted
+ or rejected. tnx C2F. fix: use quote2().
+19970120 portability problem: ESIX puts syslog() and openlog() into
+ -lgen. impact: can't compile under ESIX. fix: put -lgen into
+ LIBS for unix_sv. tnx RN.
+19961221 qmail 0.95, gamma.
+19961218 change: added various try* to TARGETS. tnx SA.
+19961216 change: clarified in qmail-send.8 that virtualdomains does not
+ apply to domains listed in locals.
+19961216 change: slurpclose() now closes fd on out-of-memory. makes it
+ more widely applicable.
+19961215 change: replaced elm instructions in INSTALL.mbox with an
+ explanation of what source change to make. tnx AB.
+19961212 portability problem: under NEWS-OS, time_t needs sys/types.h.
+ impact: couldn't compile under NEWS-OS. fix: include
+ sys/types.h in predate.c. tnx TU.
+19961211 change: used timeoutread, timeoutwrite in remoteinfo(). tnx
+ REB.
+19961210 portability problem: apparently some SGIs produce a systype of
+ irix64. impact: couldn't compile on those systems. fix: handle
+ irix64 in make-cmds. tnx M3S.
+19961208 change: added note to maildir2mbox.1 about mbox locking.
+19961208 qmail 0.94, gamma.
+19961207 change: added QMAILDEFAULTDOMAIN, QMAILDEFAULTHOST,
+ QMAILIDHOST, QMAILPLUSDOMAIN. tnx BTW.
+19961206 cleanup: readsubdir() protects itself against name overflow,
+ rather than depending on caller.
+19961204 change: changed FAQ 7.3 to prohibit fixup relaying.
+19961203 change: added note to FAQ about possibly having to put a space
+ before "$SENDER" for uux. tnx FW.
+19961202 change: added FAQ entry on QUEUE_EXTRA.
+19961202 change: added FAQ entry on backups. tnx DP.
+19961202 change: added note to INSTALL.mbox about qpopper. tnx VV.
+19961201 change: replaced logger with splogger in FAQ 5.1. tnx FPL.
+19961201 change: used netmask example for tcpcontrol in FAQ. tnx FPL.
+19961201 change: added note to README about the mailing list. tnx FPL.
+19961201 change: documented rcpthosts wildcards. tnx RN.
+19961201 change: added note to FAQ about making mailx use datemail.
+19961201 change: added datemail. function requested by several people;
+ approach suggested by TG.
+19961201 change: added predate.
+19961129 change: added QUEUE_EXTRA, QUEUE_EXTRALEN.
+19961129 change: qmail-remote bounces messages with partial final lines.
+19961129 change: added atomcheck() to quote crappy atoms.
+19961129 change: revised atomok() to let atoms deal with more crap.
+19961127 change: qmail-send adds note to deferral if flagdying. tnx TG.
+19961127 change: split off maildirbounce, maildir2qmtp, and maildir2smtp
+ into a separate serialmail package.
+19961126 change: eliminated beta success reports from README.
+19961124 change: forced res_query() return value to fit inside incoming
+ buffer size. allegedly there are buggy versions of res_query()
+ that don't guarantee this.
+19961122 qmail 0.93, gamma.
+19961122 change: allowed empty arg list in forward.
+19961121 change: qmail-smtpd now uses unknown (like qmail-qmtpd) rather
+ than dying if environment variables are not set.
+19961121 cleanup: reorganized helo handling in qmail-smtpd.c.
+19961121 cleanup: eliminated newfield_rec.
+19961121 cleanup: introduced DATE822FMT. used it in received.c.
+19961121 cleanup: introduced received.c. used it in qmail-qmtpd.c,
+ qmail-smtpd.c.
+19961121 change: qmail-smtpd now generates a new timestamp for each
+ message. tnx PJG.
+19961121 cleanup: used stralloc in newfield.
+19961121 cleanup: eliminated newfield_cc.
+19961121 change: eliminated hfield_mort().
+19961119 change: added 2-minute damper on tcpto.
+19961118 change: wrote defaults for readfile controls in showctl.
+19961117 change: control_readfile() now allows comments and blank lines.
+ tnx LW.
+19961117 change: qmail-start sets logger gid to GID_NOFILES.
+19961117 bug: ipme_init() uses a fixed-length buffer for SIOCGIFCONF.
+ impact: qmail-smtpd and qmail-remote will die if there are too
+ many local IP addresses. tnx MD. fix: ipme_init() now
+ dynamically allocates space, up to 200000 bytes, as long as
+ SIOCGIFCONF keeps failing. note that this is a very widespread
+ bug; it's in amd, exim, mrouted, named, nntpd, rarpd, sendmail,
+ tcpdump, timed, xntpd, and probably dozens more programs.
+19961117 portability problem: on BSD 4.4 and various other systems,
+ SIOCGIFCONF will truncate long lists and return success.
+ impact: on those systems, qmail-smtpd and qmail-remote will
+ miss some local IP addresses if there are too many. fix:
+ ipme_init() now checks whether there is enough space left in
+ the buffer for another ifreq, plus 64 bytes JIC. yuck.
+19961117 change: ipmeprint now flushes only at end.
+19961117 cleanup: introduced subfdinsmall. used it in qmail-clean.c,
+ qmail-qmtpd.c.
+19961117 cleanup: introduced subfdoutsmall. used it in hostname.c,
+ printbreak.c, printnumd.c, printsplit.c, qmail-alias.c,
+ qmail-clean.c, qmail-getpw.c, qmail-qmtpd.c, maildir2smtp.c,
+ maildir2qmtp.c.
+19961117 change: moved subfderr buf up to 256 characters.
+19961117 change: added maildirbounce. tnx MD, TG.
+19961116 change: maildir2smtp and maildir2qmtp now print filenames for
+ permanent bounces. tnx MD.
+19961116 change: in SECURITY, ``eleven most recent sendmail security
+ holes, five'' -> ``twelve most recent sendmail security holes,
+ six.''
+19961116 change: rewrote qmail-showctl more professionally.
+19961115 change: added several tests to find-systype.sh. this will
+ affect many systypes.
+19961115 change: qmail-alias now treats most exit codes as soft errors.
+19961115 change: revamped exit codes everywhere for 0, 100, 111.
+19961114 change: added splogger.
+19961114 portability problem: Sun's cc recognizes sqrt() as a builtin,
+ even if math.h is not included and sqrt is defined statically.
+ yuck. impact: when qmail is compiled with Sun's cc, next-retry
+ times are all screwed up. tnx PJG. fix: my sqrt() is now called
+ squareroot().
+19961114 change: dns_ip() now recognizes [1.2.3.4]. tnx DS.
+19961112 change: enabled x option in sendmail. tnx DS.
+19961111 change: added SIGHUP handling to qmail-send. suggested by many
+ people.
+19961111 bug: control routines returned incorrect codes for some
+ out-of-memory conditions. impact: none; conditions cannot
+ happen with sane control files. fix: return -1.
+19961111 change: added SIGALRM handling to qmail-send. suggested by many
+ people.
+19961111 change: eliminated flagnobreak (-b/-B) from qmail-pw2u.
+19961111 change: qmail-getpw now allows hyphens inside usernames.
+19961111 change: added users/append to qmail-pw2u. tnx G2A.
+19961111 change: added badmailfrom to qmail-smtpd. requested by several
+ people.
+19961110 change: replaced elm instructions in INSTALL.mbox with a simple
+ note to set incomingfolders in elm.rc. tnx AB.
+19961110 change: replaced ``owner hack'' with ``variable envelope
+ return paths'' throughout the documentation. tnx DS.
+19961110 change: qmail-setup installs man pages as well as cat pages.
+19961110 change: renamed qmail-newuser as qmail-newu. tnx G2A.
+19961110 change: renamed qmail-pw2user as qmail-pw2u. tnx G2A.
+19961105 change: set path in INSTALL.boot. tnx TJH.
+19961105 change: noted in qmail-smtpd.8 that addresses without @ are
+ always allowed through.
+19961105 change: indicated at various spots in FAQ that rcpthosts has to
+ be updated. suggested by various people.
+19961105 change: indicated at various spots in FAQ that qmail has to be
+ restarted. suggested by various people.
+19961029 change: fixed typo in maildir2qmtp.1. tnx BG.
+19961026 qmail 0.92, gamma.
+19961026 bug: qmail-getpw did not 0-terminate usernames. tnx CF. impact:
+ qmail-getpw would crash on some systems, deferring local
+ deliveries. fix: 0-terminate.
+19961025 cleanup: renamed auto-hassgprm.h to hassgprm.h.
+19961025 cleanup: renamed auto-hassgact.h to hassgact.h.
+19961024 change: replaced qmail-alias.0 with dot-qmail.0 in
+ INSTALL.alias. tnx MW.
+19961022 change: switched uids as early as possible in qmail-start.c.
+19961022 change: in SECURITY, ``ten most recent sendmail security
+ holes, five'' -> ``eleven most recent sendmail security holes,
+ five.''
+19961022 change: quote_need() now treats non-ASCII characters the same
+ way as control characters.
+19961022 change: added version and home page to qmail.7.
+19961022 cleanup: introduced slurpclose.c. used it in qmail-alias.c,
+ qmail-lspawn.c.
+19961021 portability problem: AT&T NCR boxes need stdio.h before
+ arpa/nameser.h. impact: dns.c would not compile. fix: include
+ stdio.h. tnx HS.
+19961021 change: added AIX section to INSTALL.ids. tnx SSB.
+19961021 change: added qmail-pw2user.
+19961020 change: added qmail-pw2user.8.
+19961020 change: qmail-alias now dies soft on EACCES/EPERM for .qmail.
+19961020 change: eliminated root comments from INSTALL.qsmhook.
+19961020 change: various improvements in FAQ.
+19961017 change: added QLX_ROOT.
+19961017 change: renamed hosts in FAQ. tnx SLB.
+19961017 change: in dot-qmail.5, documented envnoathost effects. tnx RN.
+19961017 change: revamped addresses.5.
+19961017 change: added stripvdomprepend() for better bounces. tnx PT.
+19961012 portability problem: under HP-UX 9, can't setgroups() to 65537.
+ impact: couldn't compile under HP-UX 9. fix: use 0 instead of
+ 65537 in chkshsgr.c. tnx HHO.
+19961008 change: added several qlx codes.
+19961008 cleanup: eliminated qlx from qmail-alias.
+19961008 change: qmail-lspawn runs qmail-getpw as UID_PW.
+19961008 change: added qmail-newuser.
+19961008 change: added cdb support to qmail-lspawn.
+19961008 change: integrated cdb.
+19961007 change: added qmail-users.5.
+19961007 change: eliminated usermap.
+19961007 cleanup: switched execvp to execv in sendmail, qmail-lspawn.
+19961007 change: used qmail-getpw in qmail-lspawn.
+19961007 change: renamed LSPAWN_USERLEN as GETPW_USERLEN.
+19961007 change: added qmail-getpw.
+19961007 change: created users subdirectory of CONF_HOME.
+19961007 change: fixed typo in FAQ. tnx J1B.
+19961006 change: replaced subfdout with a small ss in qmail-alias.
+19961006 change: reduced qmail-alias buffer sizes to 1024.
+19961003 change: added note to maildir2smtp.0 about maildirmake. tnx SM.
+19961003 bug: if ipme_init() returned -1, qmail-remote would continue,
+ blindly assuming that all addresses are local. impact: on
+ systems with too many aliases, all remote deliveries fail. tnx
+ MD. fix: qmail-remote now dies with temp_oserr() on any result
+ other than 1.
+19961003 portability problem: all pre-4.9.4 versions of bind barf,
+ badly, on CNAME queries to lame servers. what a crappy system.
+ even if the resolver doesn't barf, the next name server down
+ the line may barf. impact: qmail can't get mail through to
+ domains that are (1) lame and (2) running old versions of bind.
+ fix: never, ever, do a CNAME query. dns_cname() now does an ANY
+ query instead. this, like sendmail's analogous procedure, is
+ unreliable when a CNAME is mixed with other records.
+19961001 cleanup: switched to libfd in qmail-start.c.
+19960929 cleanup: renamed auto-hasmkffo.h to hasmkffo.h.
+19960928 cleanup: reorganized qmail-start.c.
+19960928 cleanup: used libfd in preline.c, qmail-lspawn.c,
+ qmail-popup.c, qmail-rspawn.c, qmail-start.c, qqtalk.c,
+ qsmhook.c.
+19960928 cleanup: added libfd.
+19960927 change: in SECURITY, ``nine most recent sendmail security
+ holes, four'' -> ``ten most recent sendmail security holes,
+ five.''
+19960926 change: added tcpcontrol notes to FAQ.
+19960926 change: qmail-smtpd now immediately closes connection, with a
+ warning message dedicated to Solaris, if stray newlines show up
+ in the incoming data.
+19960926 change: added INSTALL.boot.
+19960926 portability problem: on systems that can handle IP interface
+ aliases (i.e., on sa_len systems), SIOCGIFADDR returns the
+ primary address for an alias. impact: ipme_init() did not
+ include alias addresses. fix: ipme_init() avoids SIOCGIFADDR on
+ sa_len systems; on these systems, the address we want is
+ already in ifr. tnx DM.
+19960926 change: qmail-alias kills itself if locking takes longer than
+ 30 seconds.
+19960926 change: qmail-pop3d no longer moves messages. tnx RS.
+19960924 change: added note to FAQ about descriptors limit. tnx RD.
+19960922 change: open_trunc() now uses 644.
+19960922 change: qmail-setup now does umask(077).
+19960922 change: maildir2mbox now does umask(077).
+19960922 change: moved subfderr buf up to 64 characters.
+19960920 change: in SECURITY, ``eight most recent sendmail security
+ holes, three'' -> ``nine most recent sendmail security holes,
+ four.''
+19960920 portability problem: init run commands are subject to job
+ control signals under more systems than HP-UX. impact: on some
+ systems (e.g., Solaris), qmail daemons would be killed. fix:
+ INSTALL now tells everybody to use csh -cf.
+19960920 change: added queue-run section to FAQ.
+19960920 change: in pine-crashing question in FAQ, added -oem and -oi,
+ so that change will work with the real sendmail too.
+19960919 change: added CNAME section to FAQ. tnx to various people.
+19960919 change: eliminated QQX_EXECHARD and QQT_EXECHARD. this means
+ that all qmail-queue invocation failures are now soft, even
+ things like EPERM.
+19960919 change: replaced ``No such address'' with ``Sorry, no mailbox
+ here by that name.'' tnx G2A.
+19960919 change: qmail-remote now includes host name in no-such-host
+ messages. tnx G2A.
+19960919 change: replaced ``Temporarily unable to canonicalize address''
+ with ``CNAME lookup failed temporarily.''
+19960918 change: improved an error message in qmail-alias.c. tnx TG.
+19960918 change: added SHELL=/bin/sh to Makefile. tnx JL.
+19960916 change: reorganized INSTALL.ids a bit.
+19960916 change: ``from smtpd'' is now ``from network''.
+19960916 change: SMTPD is now DAEMON.
+19960916 change: qmail-start sets logger uid to UID_LOG. tnx JLH.
+19960916 change: added CONF_USERL.
+19960916 change: iaafmt() now puts a dot on in-addr.arpa.
+19960915 change: added UPGRADE. suggested by several people.
+19960915 change: added qsutil error messages to qmail-log.5.
+19960915 change: qsutil error messages are now alerts.
+19960915 portability problem: on some systems, logger appears to use
+ syslog(pri,buf) instead of syslog(pri,"%s",buf). tnx JC.
+ impact: logger could barf or crash if fed messages containing
+ %. an attacker could easily cause a crash, eliminating qmail's
+ logs. fix: % is no longer considered safe for logs.
+19960912 cleanup: split seek.c into seek_*.c.
+19960912 cleanup: replaced seek_to() with seek_set().
+19960912 cleanup: introduced libseek.a.
+19960907 cleanup: split case.c into case_*.c.
+19960907 cleanup: introduced libcase.a.
+19960907 cleanup: split wait.c into wait_*.c.
+19960907 cleanup: introduced libwait.a.
+19960907 cleanup: renamed auto-haswaitp.h to haswaitp.h.
+19960907 cleanup: split open.c into open_*.c.
+19960907 cleanup: introduced libopen.a.
+19960904 change: added generic pointer to qmail-control.5. tnx HW.
+19960904 change: rewrote rcpthosts section in FAQ. tnx HW.
+19960904 change: added organization section to FAQ. tnx HW.
+19960902 qmail 0.91, gamma.
+19960902 change: control_readfile() can now handle partial lines. tnx
+ JDHB.
+19960902 change: eliminated non-fqdn note from FAQ. next version of
+ tcpserver will use DNS directly.
+19960902 change: qlist now uses NEWSENDER, not SENDER.
+19960902 change: qmail-pop3d no longer obtains a lock. tnx RS.
+19960902 change: put }g on all seds in Makefile.
+19960831 change: noted in qmail-control.5 that comments are not allowed
+ in control files. tnx J2B.
+19960829 change: used double union in alloc.c. tnx ME.
+19960829 change: replaced semicolon with colon for smtproutes port.
+19960829 change: in INSTALL, put make man just before make setup.
+19960829 change: changed a few qmail messages into alerts.
+19960829 cleanup: renamed datetime_gmt as datetime_tai.
+19960829 change: added note to UUCP question that some UUCP software
+ doesn't want preline -f. tnx SB.
+19960829 change: added question 2.4 to FAQ on SLIP/PPP.
+19960828 change: replaced owner- with owner-@host-@[] in qmail-inject.
+19690828 change: replaced owner- with owner-@host-@[] in qmail-alias.
+19960828 change: replaced owner- with owner-@host-@[] in injectbounce().
+19960828 change: replaced owner- with owner-@host-@[] in senderadd() for
+ owner hack.
+19960828 change: qmail-inject -n now prints Return-Path.
+19960825 cleanup: revised ending code in token_addrlist().
+19960825 change: tokenize now uses linelen 0 for unparse.
+19960825 change: if linelen is 0 in token822_unparse, no length limit.
+19960825 change: added LINELEN macro to qmail-inject for unparse.
+19960825 change: token822_unparse now takes linelen argument. (leaving
+ two spaces on the right before linelen.)
+19960824 cleanup: renamed token as token822.
+19960822 portability problem: under NEWS-OS, /bin/mail and /usr/ucb/mail
+ invoke sendmail with -E and -J options. tnx TU. impact:
+ couldn't send mail with those programs. fix: accept opts,
+ including _optional_ args. ugh.
+19960821 change: sendmail now quits if invoked as newaliases. tnx TU.
+19960821 portability problem: under NEWS-OS, dirent.h needs sys/types.h.
+ tnx TU. this POSIX violation also appears in some versions of
+ FreeBSD. impact: couldn't compile under NEWS-OS. fix: include
+ sys/types.h in direntry.h* and trydrent.c. [sigh]
+19960821 change: added concurrencyremote question to FAQ.
+19960821 change: added chkspawn.
+19960821 change: moved default SPAWN_NUMD up to 120.
+19960818 change: allowed ;port in smtproutes. tnx AL.
+19960818 change: introduced port in qmail-remote.c.
+19960818 change: qmail-queue records qp in Received lines. 2 lines of
+ code. tnx ME.
+19960818 change: in SECURITY, ``seven most recent sendmail security
+ holes'' -> ``eight most recent sendmail security holes.''
+19960818 change: qmail-pop3d now appends an extra blank line to every
+ message, for compatibility with popper. some clients can't
+ deal with the right thing, unfortunately. tnx FPL.
+19960818 change: added qmail-tcpto.
+19960818 change: eliminated cc -posix for NeXTs. tnx SA.
+19960818 change: eliminated loadfifo. tnx SA.
+19960818 change: integrated auto-configured fifo.c code from SA.
+19960817 change: put SYSDEPS into a more reasonable order.
+19960813 change: indicated possibility of duplication when qmail-remote
+ gets a dead connection after DATA. tnx ME.
+19960813 change: documented qmail-inject environment variables.
+19960813 change: supported per-recipient owner hack in qmail-inject.
+19960813 change: supported per-message owner hack in qmail-inject.
+19960813 change: introduced hackedruser into qmail-inject.
+19960813 change: introduced QMAILRUSER, QMAILRHOST.
+19960812 change: added QMAILINJECT option to allow address-comment form.
+19960812 change: made name-address form the default in qmail-inject.
+19960812 change: added QMAILINJECT options f and m to delete From and
+ Message-ID on input. tnx LL.
+19960812 change: added QMAILINJECT environment variable.
+19960812 change: added QMAILHOST, QMAILUSER, QMAILNAME to override
+ MAILHOST, MAILUSER, MAILNAME. tnx MG.
+19960812 change: added qmail-showctl.
+19960812 portability problem: under Solaris 2.4 and possibly other
+ systems, the linker does not give generic alignment to an array
+ of 4096 chars. tnx JP. impact: some subset of the programs
+ would (reliably) die with a bus error; in the Solaris case,
+ maildir2mbox. fix: redefine space in alloc.c to be aligned.
+19960812 change: qmail-remote no longer does CNAME lookups if there's an
+ artificial SMTP route. tnx ME.
+19960812 change: added flagcname arg to addrmangle() in qmail-remote.
+19960812 cleanup: moved host/relayhost processing earlier in
+ qmail-remote.
+19960812 change: qmail-remote stops before DATA if no RCPTs were
+ successful. tnx JLH.
+19960812 change: rewrote rcpthosts explanation in FAQ.
+19960811 change: added qmail-log.5.
+19960811 change: introduced ALIAS_PATERNALISM. configurability requested
+ by several people.
+19960811 change: eliminated go-writability test for qmeox(). the alleged
+ value of paternalism is nonexistent if nobody even notices
+ you're doing it.
+19960811 change: in qbiff, changed no-/-allowed to no-/-at-beginning,
+ no-dots-allowed, must-be-nonempty. tnx MD.
+19960811 change: in mbox.5, discouraged mail readers from looking for
+ From_ lines only after blank lines. too much crap in the world.
+19960811 change: added subject line to qreceipt success notices.
+19960811 change: added subject line to qmail-send bounce messages.
+19960811 change: qmail-alias now expects dash arg. this finally gives
+ lspawn complete control over the local -> ~user/.qmail-ext map.
+19960811 change: qmail-lspawn passes dash arg to qmail-alias.
+19960811 change: reorganized qlist acknowledgment format. again.
+19960811 change: documented EXT, EXT2, EXT3, EXT4. tnx BB.
+19960810 change: qmail-makectl now copies locals to rcpthosts. should be
+ a better default. suggested by TK.
+19960805 portability problem: new makefile generator put in tabs again.
+ sigh. impact: couldn't compile under some systems. fix: same as
+ before. tnx TG.
+19960804 change: added tcpserver instructions to FAQ.
+19960804 change: reorganized FAQ server instructions into a new section.
+19960801 qmail 0.90, gamma.
+19960801 change: qmail-qmtpd now supports rcpthosts, RELAYCLIENT.
+19960731 change: default NUMD is now 29. this prepares for weird systems
+ where getpwnam() needs more than one descriptor (but the
+ descriptor limit is still 64! ... you never know), and for
+ possible future getpwnam() replacements.
+19960731 change: popped subfderr buffer up to 32 characters. made sure
+ that everybody flushed subfderr as necessary.
+19960731 change: maildir2qmtp now prints filenames and responses.
+19960731 change: maildir2smtp now prints filenames it's trying and
+ relevant portion of SMTP responses.
+19960731 change: used succwrite() in maildir2smtp, maildir2qmtp.
+ simplifies code quite a bit.
+19960731 change: qmail-remote's blast() checks sooner for write errors.
+19960731 change: added better -e option to sendmail. tnx TG.
+19960731 change: added maildir2qmtp.
+19960730 cleanup: eliminated die_nomem() in maildir2smtp.c.
+19960730 change: dns_cname now pretends that "foo." is a CNAME for "foo"
+ to give the desired behavior for people who misuse DNS and
+ violate RFC 822. tnx RN.
+19960730 change: dns_cname now tests for empty names and ] on every
+ loop.
+19960730 change: used LSPAWN_BREAK in qmail-send.c for usermap.
+19960730 change: updated header example in qmail-header.5.
+19960730 change: added printbreak. auto-configured BREAK in dot-qmail.5,
+ qmail-lspawn.7, qmail-send.8, qmail-upgrade.7, qlist2.
+19960730 change: added printnumd. auto-configured NUMD in qmail-send.8,
+ qmail-limits.8.
+19960730 change: added printsplit. auto-configured split in qmail-upq.
+19960730 change: added dot-qmail.5.
+19960730 change: qmail-smtpd now treats HELO as including RSET.
+19960730 change: added moreinfo to qlist usage message.
+19960729 change: improved an error message in qmail-alias.
+19960729 cleanup: merged qmeox(), qmeodx().
+19960729 bug: failure to stat .qmail-owner was not an error. impact: if
+ stat failed temporarily (e.g., because of NFS), .qmail-owner
+ would be incorrectly ignored, so outgoing message would have
+ wrong envelope sender. fix: qmail-alias does temp_nfsqmail() if
+ stat() returns a temporary error.
+19960729 change: added RFCOWNER.
+19960729 change: added qmtpd setup question to FAQ.
+19960729 change: added qmail-qmtpd.
+19960728 change: revamped maildir2smtp error messages.
+19960728 change: revamped maildirwatch error messages.
+19960728 change: revamped maildir2mbox error messages.
+19960728 change: used strerr in maildir_scan().
+19960728 change: used strerr in maildir_chdir().
+19960728 change: introduced strerr.
+19960728 bug: the new tcp-env tried to read from an ndelay socket.
+ impact: TCPREMOTEINFO would always be empty. fix: unset ndelay
+ in remoteinfo.c.
+19960728 bug: if maildir2smtp saw a permanent failure after MAIL, it
+ failed to do RSET. impact: all further messages would be
+ rejected at the MAIL stage. fix: maildir2smtp now always does
+ RSET. tnx JW.
+19960728 cleanup: qmail-alias now applies lowercase and dot-to-colon
+ conversion directly to dashext, leaving everything else alone.
+ this works since all .qmail access is factored through dashext.
+19960727 portability problem: under NeXTStep, -posix is almost entirely
+ broken. impact: qmail daemons would dump core under NeXTStep.
+ fix: turn off -posix, except for loading qmail-setup, which
+ needs mkfifo(); NeXT, bless them, didn't put mkfifo() into the
+ C library where it belongs. this requires a new make command,
+ namely loadfifo.
+19960727 change: all characters 33-126 are now considered safe for logs.
+ tnx MD.
+19960727 cleanup: eliminated qp variable from mailforward().
+19960727 cleanup: maildirwatch.c includes headerbody.h.
+19960727 cleanup: eliminated match from maildirwatch.c.
+19960727 cleanup: eliminated code variable from maildir2smtp.c:doit().
+19960727 cleanup: maildir2smtp.c includes scan.h.
+19960727 cleanup: maildir.c includes str.h.
+19960727 cleanup: qmail-popup.c now includes exit.h.
+19960727 cleanup: qmail-pop3d.c now includes exit.h.
+19960727 cleanup: eliminated path from qmail-start.c.
+19960727 cleanup: eliminated birthplusnn from nextretry().
+19960727 cleanup: eliminated r from timeoutconn().
+19960727 cleanup: tcpto.c now includes byte.h.
+19960727 cleanup: spawn.c now declares initialize().
+19960727 cleanup: qmail-lspawn.c now includes str.h, byte.h.
+19960727 cleanup: qmail-inject.c now includes quote.h.
+19960727 change: qmail-check now checks separately for group
+ readability and other readability.
+19960727 bug: maildir2smtp didn't check flagehlo in PIPELINING parsing.
+ impact: a server that said PIPELINING at any point, not just
+ EHLO, would receive pipelined data. fix: check flagehlo.
+19960727 bug: readsubdir was calling pause(). impact: if a subdirectory
+ was removed, qmail-send would hang. fix: use rs->pause().
+19960727 change: used error_str in qmail-qread.
+19960727 change: qmail-qread now looks for local/remote open errors.
+19960727 cleanup: added warn() in qmail-qread.c.
+19960727 change: qmail-qread now exits 111 for temporary errors.
+19960727 change: used error_str in qmail-setup.
+19960727 change: introduced error_str.
+19960727 change: replaced qmail-check with make check in INSTALL.
+19960727 change: added check target to Makefile.
+19960727 change: replaced qmail-setup with make setup in INSTALL.
+19960727 change: indirected fake targets through do- targets.
+19960727 change: added setup target to Makefile.
+19960727 change: qmail-makectl now makes sure that defaultdomain has
+ at least one dot. e.g., enteract.com -> enteract.com, not com.
+19960726 bug: quote() failed to quote commas. impact: addresses
+ containing commas would not have been quoted correctly for
+ Return-Path or for SMTP MAIL FROM. fix: quote commas.
+19960726 change: sendmail now mentions qmail-qread, not qmail-mailq.
+19960726 change: qmail-alias now expects ext arg. this eliminates
+ LSPAWN_BREAK from qmail-alias and gives lspawn almost complete
+ control over the local -> ~user/.qmail-ext transformation. the
+ exception is that qmail-alias always uses ~user/.qmail,
+ ignoring ext, if local is the same as user.
+19960726 change: qmail-lspawn passes ext to qmail-alias.
+19960726 change: alloc() now uses up a 4K space before calling malloc().
+19960726 change: ipalloc allocation base is now 10. 100 was silly.
+19960726 change: stralloc allocation base is now 30.
+19960726 change: injectbounce() now supports the owner hack.
+19960726 change: qmail-smtpd no longer requires HELO. tnx K1J.
+19960726 cleanup: replaced makereceived() with dohelo().
+19960726 change: qmail-smtpd is back to 555 for syntax errors.
+19960725 change: qmail-alias now supports the owner hack. tnx to RN for
+ prodding me to look at this problem.
+19960725 change: senderadd() now supports the owner hack.
+19960725 cleanup: split off senderadd().
+19960725 change: added pine-crashing note to FAQ.
+19960725 change: added procmail config.h note to INSTALL.mbox.
+19960725 change: added elm TMPDIR note to INSTALL.mbox.
+19960725 change: added pine.conf note to INSTALL.mbox.
+19960724 change: added fixup note to FAQ.
+19960724 change: qmail-inject now exits 111 for temporary errors.
+19960724 change: qmail-smtpd now appends RELAYCLIENT to incoming
+ recipient domain names.
+19960724 cleanup: moved relayclient out of qmail-smtpd's addrallowed()
+ into caller.
+19960724 change: added rcpthosts wildcards.
+19960724 change: added clean target to Makefile.
+19960723 change: added virtualdomains exceptions.
+19960722 change: added BLURB4.
+19960722 change: added BLURB3.
+19960722 change: eliminated smarthost and localnet.
+19960722 change: incorporated relaymap, contributed by LW. renamed it
+ as smtproutes.
+19960722 change: qmail-popup now supports APOP. suggested by BG, who
+ distributed similar changes.
+19960722 change: qmail-popup now sends APOP timestamp to checkpassword.
+19960722 cleanup: in qmail-popup, split off doanddie().
+19960722 change: qmail-popup now prints APOP timestamp in banner.
+19960722 change: added hostname argument to qmail-popup.
+19960722 cleanup: in qmail-popup, split out() into out(), outflush().
+19960722 cleanup: in qmail-popup, introduced pop3_greet().
+19960721 portability problem: under Unisys SVR4, hostname is not in the
+ usual path. impact: qmail-makectl fails. fix: added hostname
+ command here, used it in qmail-makectl.
+19960721 portability problem: on some sysctl-based systems, apparently
+ gethostname() doesn't write anything if the output buffer is
+ too small. it should write a truncated name. impact: if anyone
+ has a hostname longer than 64 characters, maildirs could get up
+ to 64 characters of garbage, rather than a truncated hostname.
+ fix: qmail-alias now does *host = 0 before calling gethostname.
+19960721 change: updated FAQ examples from qsmhook to preline.
+19960721 change: added preline.
+19960721 change: qsmhook now uses signal_init, signal_uninit.
+19960721 change: qsmhook now checks specifically for empty args.
+19960721 change: documented mbox.
+19960721 change: added EXT, EXT2, EXT3, EXT4.
+19960721 change: added LAST response to qmail-pop3d, always returning
+ OK 0. tnx RN.
+19960721 change: added qmail home page to README.
+19960721 change: added HELP response to qmail-smtpd. tnx RN.
+19960720 change: expanded, vertically, the qmail-inject error message
+ for unparseable header fields.
+19960720 change: logo is now dolphin. tnx CEJ.
+19960719 qmail 0.76, beta.
+19960719 change: used LSPAWN_BREAK in qmail-alias for deciding how to
+ handle extensions. this should produce better behavior in the
+ (unsupported) case that LSPAWN_BREAK is not a hyphen.
+19960719 bug: qmail-smtpd didn't check for null arg on MAIL, RCPT.
+ impact: qmail-smtpd would deref 0 and crash. fix: qmail-smtpd
+ now gives syntax error on null arg.
+19960719 change: documented UFLINE in qmail-command.8. tnx TG.
+19960718 change: added maildir2smtp.
+19960718 cleanup: introduced maildir.c. used it in maildir2mbox.c,
+ maildirwatch.c.
+19960718 change: added maildirwatch.
+19960718 cleanup: maildir2mbox now sets up pq2 as it is deleting from
+ pq, rather than simultaneously with pq.
+19960718 change: added H_DELIVEREDTO.
+19960718 portability problem: Unisys requires -lsocket -lnsl. impact:
+ couldn't compile under Unisys. fix: added unix_sv section to
+ make-cmds.sh. tnx TVP.
+19960718 change: added unix_sv section in find-systype. tnx TVP.
+19960717 change: qmail-alias now appends newline if .qmail does not end
+ with a newline. tnx MC.
+19960717 change: qmail-alias now defers delivery for a blank line only
+ if it is the first line of the file. handles user behavior
+ described by MC of putting many newlines at end of file.
+19960717 bug: qmail-inject looked for dots in user part, not just host
+ part, when deciding whether to use defaultdomain. impact: the
+ address joe.bloggs@here didn't have defaultdomain added. fix:
+ qmail-inject now stops at the @.
+19960717 change: updated INSTALL.alias to mention qmsmac.
+19960717 change: syntax error code for SMTP is now 501.
+19960717 change: added -e option to sendmail. tnx TG.
+19960716 change: changed ~alias files to .qmail-local, not .qmaillocal.
+ suggested by many people.
+19960716 change: redid qmail-alias/qmail-lspawn interface.
+19960716 change: replaced EXTENSION, USEREXT with LOCAL.
+19960716 change: qmail-queue now removes intd, mess upon error, as long
+ as it doesn't time out. suggested by BB et al.
+19960716 change: added flagmademess, flagmadeintd to qmail-queue.c.
+19960716 cleanup: changed todofd to intdfd in qmail-queue.c.
+19960716 cleanup: added cleanup() to qmail-queue.c.
+19960716 change: added timeout to tcp-env.c, default 30 seconds.
+19960716 change: remoteinfo_get() now uses timeoutconn().
+19960715 change: added procmail config.h note to FAQ.
+19960704 change: qmail-upgrade.7 now warns administrators that ~alias
+ generally doesn't apply to addresses starting with a user name.
+19960703 change: added echo \c note to FAQ. tnx PJG.
+19960702 change: qmail-smtpd now accepts HELO without an argument.
+ tnx K1J, J1B.
+19960627 change: qmail-lspawn.8 now mentions that qmail-lspawn doesn't
+ set up supplementary groups. tnx TG.
+19960625 portability problem: under Linux, read(,,0) doesn't do proper
+ error slippage. impact: timeoutconn() would always report
+ success; if a connection failed, qmail-remote would report a
+ greeting failure and skip all further MX records. tnx ME. fix:
+ timeoutconn() now uses getpeername() to check for success.
+19960625 change: qmail-smtpd now mentions disk full for QQT_WRITE.
+19960625 change: qmail-inject now mentions disk full for QQT_WRITE.
+19960622 change: if RELAYCLIENT is set, qmail-smtpd skips rcpthosts.
+19960609 change: updated INSTALL for current SMTP responses.
+19960607 change: clarified INSTALL.qsmhook examples. tnx S1R.
+19960607 change: added subject parsing to qlist.c. tnx RN.
+19960607 cleanup: used case_diffb in qlist.c.
+19960607 change: added extra log information to INSTALL examples.
+19960606 change: added -Pn to uucp line in FAQ. tnx DWS.
+19960605 portability problem: under Solaris, /usr/bin/groups incorrectly
+ reports your groups in /etc/group, rather than the results of
+ getgroups(). tnx MD, PJG. impact: test #19 in INSTALL fails.
+ fix: added special note to test #19 (sigh) about Solaris.
+19960605 change: improved maildir setup commands in INSTALL.mbox.
+19960605 change: on success, qmail-alias logs forwarding qp. 9 lines
+ extra code.
+19960605 change: qmail-send logs qp for bounce. 6 lines extra code.
+19960605 change: qmail-smtpd includes qp in its response when it accepts
+ a message. 7 lines extra code. requested by MD and others.
+19960605 change: added qqtalk_qp.
+19960605 change: qmail-send now logs uid and qp from todo file. 14 lines
+ extra code.
+19960605 change: qmail-queue now records uid and qp in u and p lines
+ in todo file. 7 lines extra code.
+19960605 change: improved qmail-alias x-bit error messages.
+19960605 change: newline in log is now converted to /, not underscore.
+19960604 change: when it accepts a message, qmail-smtpd includes the
+ local time in its 250 response.
+19960604 change: on success, qmail-alias prints delivery counts,
+ file+forward+program.
+19960603 change: qmail-remote now reports IP address on success. tnx MD.
+19960603 change: qmail-send now logs success and failure reports, not
+ just deferral reports.
+19960603 change: added netbsd section in find-systype, same as bsd.os
+ section. this will affect netbsd-* systypes. tnx MBS.
+19960530 qmail 0.75, beta.
+19960528 change: added qmail.7. tnx MD.
+19960525 change: added qmail-pop3d. tnx RN.
+19960525 change: added qmail-popup. tnx RN.
+19960525 change: added elm filter section to FAQ. tnx GB.
+19960502 portability problem: on many systems, select() on an
+ almost-full pipe incorrectly says writable. tnx ME for running
+ into this and helping track it down. impact: if qmail-send
+ writes a pipeful to qmail-lspawn or qmail-rspawn before they
+ can react (because of high concurrency, high load, or long
+ addresses), it will receive an incorrect -1/EAGAIN, and will
+ conclude that spawn died. sysadmin will have to restart qmail,
+ and messages will be duplicated. fix: in qmail-send.c,
+ busy-loop if write() to spawn returns any error other than
+ EPIPE.
+19960501 bug: qmail-alias treated NAMETOOLONG and NOTDIR as temporary
+ errors. impact: qmail-alias never looked for -default; even if
+ mail was destined to bounce, it would have to time out first.
+ fix: qmail-alias now uses error_temp().
+19960430 bug: qmail-smtpd treated qq crash as permanent error. impact:
+ if somebody actively kills qq, mail will be incorrectly
+ bounced. tnx SS. fix: qmail-smtpd now treats only TOOLONG and
+ EXECHARD as permanent errors.
+19960430 cleanup: eliminated QQT_TTY from qqtalk.h.
+19960428 change: added ``warning: '' before trouble-marking message.
+19960428 change: added percenthack. requested by GB.
+19960428 cleanup: switched to auto-generated Makefile.
+19960428 cleanup: switched to auto-generated .o dependencies.
+19960428 cleanup: eliminated fmt.o, scan.o from Makefile.
+19960428 portability problem: under HP-UX 10, the rc pgrp is sent HUP
+ when rc finishes. tnx BG. impact: the qmail daemons are killed
+ when rc finishes. fix: added special note in INSTALL (sigh) to
+ use csh -cf.
+19960427 cleanup: added PORT_SMTP in qmail-remote.c.
+19960427 cleanup: introduced timeoutwrite.c. used it in qmail-remote.c.
+19960427 cleanup: introduced timeoutread.c. used it in qmail-remote.c.
+19960427 cleanup: introduced timeoutconn.c. used it in qmail-remote.c.
+19960427 change: added timeoutconnect. default: 60 seconds.
+19960427 change: added pop3d instructions to FAQ. tnx RN.
+19960427 change: eliminated env manipulation from qmail-start. tnx BG.
+19960427 change: headerbody now ends header, inserting blank line, if
+ first line of a header field doesn't pass hfield_valid. tnx TG.
+19960427 change: headerbody now prepends MBOX-Line: to any header line
+ starting From_. this lets qmail-inject work with elm's bounce.
+ tnx OR, K1J, et al.
+19960426 change: added moreinfo arg to qlist and qlist2.
+19960426 change: added signal_uncatchchild() to qmail-send.c. tnx BG.
+ now, if sysadmin sets SIGCHLD to SIG_IGN before invoking
+ qmail-send [sigh], qmail-send won't screw up bounce messages.
+19960426 change: dns_cname now checks whether last character is ],
+ rather than whether first character is [, for quick return.
+19960426 cleanup: glue is now global in dns.c.
+19960426 cleanup: qmail-remote no longer does stralloc_0 for host and
+ canonhost.
+19960426 change: dns_mxip no longer rejects [foo].bar.
+19960426 change: dns_mxip no longer requires for bracket that input
+ be 0-terminated.
+19960426 change: qmail-start can now take logger as an argument.
+19960426 change: qmail-start now invokes qmail-send in foreground (as
+ parent of other processes).
+19960426 change: added mailsubj. tnx GAW.
+19960426 portability problem: under some systems, can't lock read-only
+ file. impact: maildir2mbox would always fail on those systems.
+ fix: maildir2mbox now opens a separate lock fd. tnx BG.
+19960426 cleanup: removed unnecessary #!/bin/sh and # AUTO from mctl.sh.
+19960426 change: added qmail-qstat.
+19960426 change: added qmail-qread.8.
+19960426 change: renamed qmail-mailq as qmail-qread.
+19960419 change: qmail-alias now defers delivery rather than skipping
+ blank lines in .qmail.
+19960419 change: in qmail-lspawn.c, lowercased name before getpwnam().
+ really getpwnam() should do this, but oh well.
+19960419 change: added username to qmail-lspawn.c, with LSPAWN_USERLEN
+ in conf-unusual.h. names longer than LSPAWN_USERLEN will skip
+ getpwnam().
+19960419 change: if qlist doesn't see any cmds, it presumes that you
+ meant to subscribe.
+19960419 change: reorganized qlist acknowledgment format.
+19960415 change: reorganized and rewrote FAQ.
+19960415 change: renamed HOWTO as FAQ.
+19960414 change: in qmail-alias, converted extension to lowercase just
+ before qmeopen(), qmeox() calls. thus EXTENSION and USEREXT and
+ RECIPIENT will preserve case passed by qmail-lspawn, while
+ .qmailext lookups will not.
+19960414 change: removed case_lowers(r) from qmail-lspawn.c. tnx JLH.
+19960414 change: moved extension . -> : conversion to just before
+ qmeopen(), qmeox() calls in qmail-alias.c. thus EXTENSION and
+ USEREXT and RECIPIENT will preserve dots.
+19960414 change: qsmhook -x now does case-independent comparison.
+19960413 change: added procmail instructions to HOWTO.
+19960409 bug: qmail-alias does not check for newlines when it generates
+ Return-Path. impact: resulting Return-Path header field will be
+ illegal, if sender address contains newline followed by
+ something other than whitespace. fix: qmail-alias now replaces
+ newline with underscore in rpline.
+19960409 change: added leaf UUCP description to HOWTO. tnx J2K.
+19960409 change: added -B option to sendmail. tnx OR.
+19960409 change: qlist now makes lists unwritable (after renaming from
+ .qtemp to .qmail). tnx MLH.
+19960409 change: added flagdtline to qsmhook.c, based on -l option.
+19960409 change: added PIPELINING declaration to qmail-smtpd. tnx JGM.
+19960409 change: qmail-smtpd now flushes output instantly after DATA,
+ QUIT, HELO, EHLO, NOOP, VRFY, or any 502.
+19960409 change: qmail-smtpd now flushes output upon read() and death.
+19960409 change: qmail-smtpd no longer flushes output in out().
+19960409 change: increased qmail-smtpd outbuf size from 128 to 512.
+19960409 cleanup: in qmail-smtpd, eliminated ssinit() in favor of FDBUF.
+19960409 bug: qmail-alias produced aliasfoo-owner rather than foo-owner
+ as envelope sender for ~alias/.qmailfoo. tnx DS. impact: wrong
+ envelope sender whenever ~alias/.qmailfoo-owner existed. fix:
+ qmail-alias now checks for hyphen at beginning of extension.
+19960409 change: added _ESMTP to end of 220. tnx JLH.
+19960409 change: moved out("\r\n") out of smtp_greet() into callers.
+ this improves the flushing behavior on 221.
+19960328 qmail 0.74, beta.
+19960326 change: changed subdirectory split from 32 to 23.
+19960326 portability problem: some versions of make don't understand
+ that a line with just a tab is blank. impact: couldn't compile
+ under those systems. fix: eliminated extra tab from Makefile.
+ tnx TG.
+19960325 change: added qmail-mailq.
+19960325 change: introduced readsubdir.
+19960325 change: qmail-setup makes split; qmail-check checks split.
+19960325 change: used split in qmail-send, qmail-clean, qmail-queue
+ for mess, info, local, remote.
+19960325 change: fmtqfn now supports split queue subdirectories.
+19960325 cleanup: eliminated cat2s().
+19960325 cleanup: introduced fmtqfn.c. used it in qmail-queue.c,
+ qmail-send.c, qmail-clean.c.
+19960325 change: in protocol between qmail-clean and qmail-send, now
+ using intd/ instead of mess/.
+19960325 change: qmail-queue.c and triggerpull.c now work inside queue
+ subdirectory.
+19960325 change: spawn.c now checks whether message is a regular file.
+19960325 change: spawn.c now allows slashes in messid except at
+ beginning.
+19960325 cleanup: introduced fnmake_split in qmail-send.c.
+19960325 cleanup: eliminated strnum in qmail-send.c in favor of
+ fnmake_{info,todo,mess,chanaddr} and fnmake2_bounce.
+19960325 cleanup: introduced strnum3 in qmail-send.c for the logging
+ uses of strnum.
+19960325 cleanup: in qmail-send.c, getinfo() now takes id argument.
+19960325 cleanup: qmail-send.c now preallocates space for fn, fn2.
+19960325 change: time zone is now -0000 instead of +0000. encouraging
+ DRUMS to use this as an i-don't-know-the-local-time indicator.
+19960324 change: qmail-rspawn.c now calls tcpto_clean().
+19960324 cleanup: spawn.c now calls initialize().
+19960324 change: qmail-setup makes lock/tcpto; qmail-check checks it.
+19960324 change: qmail-remote now quickly skips connect() to a host that
+ seems to be down. tnx BP for pressuring me to get this done.
+19960323 change: in qmail-alias.8, renamed mboxg as mboxrd. tnx RD.
+ idea was popularized by RD in June 1995.
+19960322 cleanup: eliminated subfd_init().
+19960322 change: qbiff now removes the word Subject.
+19960322 change: now /bin/true instead of /dev/null in the generic
+ INSTALL.ids instructions. tnx JPR.
+19960322 change: added hfield_skipname(). tnx RN.
+19960322 bug: qmail-inject did not check whether USER needed quoting.
+ impact: if USER had weird characters, the From address would
+ generally be wrong, unless the user manually set up MAILUSER
+ with proper quoting. fix: qmail-inject sets up a quoted-string
+ if necessary.
+19960322 cleanup: separated out quote_need() in quote.c.
+19960322 cleanup: added stralloc_catb.c. used it in qmail-alias.c,
+ qmail-send.c.
+19960322 change: qmail-send now uses a quadratic retry schedule from
+ birth of each message. this also eliminates clustering.
+19960322 cleanup: separated out nextretry() in qmail-send.c.
+19960322 change: qmail-remote now passes all non-@ addresses through
+ without comment, not just <> and <#>.
+19960322 change: replaced # test with anything@[] test in qmail-inject.
+19960322 change: replaced # with #@[] in qlist.c, qmail-alias.c,
+ qmail-send.c, qreceipt.c.
+19960322 change: qmail-lspawn no longer discards messages to <#>.
+19960322 cleanup: in qlist, used str_diff for <> and <#> tests.
+19960322 change: qmail-alias is now back to testing envelope sender for
+ <> and <#>, rather than things without an @.
+19960321 change: added 8BITMIME support to qmail-smtpd.
+19960321 change: added ESMTP support to qmail-smtpd.
+19960318 change: used NEWSENDER in place of SENDER for |forward.
+19960318 change: added NEWSENDER.
+19960318 change: added HCMSSC support to qmail-alias.c.
+19960318 change: added HCMSSC support to spawn.c.
+19960318 change: added HCMSSC support to qmail-remote.c.
+19960318 change: added HCMSSC support to qmail-smtpd.c.
+19960317 portability problem: SCO requires -lsocket -lnsl. impact:
+ couldn't compile under SCO. fix: added SCO section in
+ make-cmds.sh. tnx JPR. note that this is for OSR 5; 3.2v4.2
+ will need more fixes, and old 3.2 is basically hopeless.
+19960317 bug: newfield_datemake would leave newfield_date alone if it
+ was already initialized, even though qmail-send calls
+ newfield_datemake anew for each bounce. impact: bounce messages
+ would usually have an incorrect Date field. fix: redid
+ newfield_datemake to update newfield_date each time.
+19960317 change: allowed . and @ in 822 phrases; 822 doesn't allow them,
+ but they do show up. tnx to the DRUMS group.
+19960317 change: replaced GMT with +0000 in date822fmt.c. this confuses
+ a few versions of getdate(), but the DRUMS group is going to
+ outlaw GMT, not just recommend against it as in 1123.
+19960317 change: redefined ALIAS_EMPTY to take advantage of . for file
+ deliveries. tnx RN.
+19960317 change: qmail-alias now allows . as well as / to start file
+ deliveries. tnx RN.
+19960317 change: qmail-alias now dies (soft) if .qmail is writable to
+ others, rather than silently ignoring it.
+19960317 change: qmail-alias now dies (soft) if flagforwardonly is
+ violated, rather than silently ignoring the bad instructions.
+19960317 change: qmail-alias now ignores x bit on empty .qmail files.
+19960317 bug: if RCPT gave 4xx and DATA gave 5xx, qmail-rspawn would
+ incorrectly assign a permanent failure to that recipient.
+ impact: in that case, mail would be incorrectly bounced. fix:
+ remove orr > 0 test from qmail-rspawn.c.
+19960310 change: tcp-env now uses signal_uninit(). [sigh]
+19960310 change: tcp-env now specifically unsets HOST and INFO if they
+ are not applicable. just trying to make it more widely usable.
+19960310 cleanup: used byte_* in remoteinfo.c, ipme.c, tcp-env.c.
+19960310 cleanup: added readwrite.h, eliminated sys.h.
+19960310 cleanup: included byte.h in qmail-send.c.
+19960310 cleanup: eliminated i and j from forward.c's main().
+19960310 cleanup: eliminated wstat from qlist.c.
+19960310 cleanup: eliminated die_nomem() parameter in qmail-setup.c.
+19960310 cleanup: eliminated i from qmail-remote's addrmangle().
+19960310 cleanup: added exit.h.
+19960310 cleanup: split ipalloc.c off of ip.c.
+19960310 cleanup: added fmt_strn.c, eliminated fmt_strncpy.c.
+19960310 change: reorganized INSTALL to do some pre-upgrade tests.
+ tnx RN.
+19960310 change: reordered steps in upgrade procedure in INSTALL.
+19960308 change: eliminated ownership test in qmail-alias.c. tnx DS.
+19960304 change: in SECURITY, ``six most recent sendmail security
+ holes'' -> ``seven most recent sendmail security holes.''
+19960303 qmail 0.73, beta.
+19960303 change: added SYSDEPS.
+19960303 cleanup: revamped select.h autoconfiguration.
+19960303 cleanup: revamped fork.h autoconfiguration.
+19960303 cleanup: revamped direntry.h autoconfiguration. target is now
+ direntry.h; auto-hasdrent.h is gone.
+19960303 change: tryflock.c now includes <sys/types.h>, for consistency
+ with lock.c. may affect portability.
+19960302 portability problem: under BSDI, can't set sticky on normal
+ files. dorks. impact: the new qlist doesn't work under BSDI;
+ be glad I test things before release. fix: qmail-alias and
+ qlist now use executable instead of sticky.
+19960302 change: gfrom now quotes >From and >>From etc. as well as From;
+ in other words, I'm switching from mbox format to mboxg format.
+19960302 cleanup: added gfrom.c. used it in qmail-alias.c, maildir2mbox.c.
+19960302 change: addbounce() now substitutes \n\n -> \n/ in reports,
+ and \n -> _ in recips. thus bounces can now be reliably parsed.
+19960302 change: if qmail-send had trouble reading the original message
+ or the list of addresses for a bounce, it used to give up and
+ send a bounce with "Oh no! I had trouble reading the rest of
+ your message" or some such. now it aborts the bounce attempt
+ and tries again later.
+19960302 cleanup: added qqtalk_fail(). used it in qmail-alias.c,
+ qmail-smtpd.c.
+19960302 bug: if mailforward() had trouble reading message (e.g.,
+ because of an I/O error), it marked an error but kept reading.
+ impact: could loop forever. fix: upon error, break.
+19960302 change: maildir2mbox now scans (restrictively) for return-path.
+19960302 change: qbiff now prints subject and body, up to 74 chars.
+19960302 change: added H_SUBJECT to hfield.
+19960302 change: qbiff now puts TO before FROM.
+19960301 cleanup: added fmt_str.c. used it in many places.
+19960301 change: qmail-send now says something if you've told it to exit
+ but it's waiting for some deliveries. tnx RN.
+19960301 change: qmail-alias -n now continues (with warning) if home
+ directory is sticky. tnx RN.
+19960301 change: improved usage messages in qmail-alias.c. tnx RN.
+19960301 change: put limit on length of addresses in qlist.
+19960301 change: added exit 99 support to qmail-alias. tnx RN.
+19960301 change: qmail-alias now exits immediately on temporary or
+ permanent error. rewrote section in qmail-alias.8 accordingly.
+19960301 cleanup: eliminated flagsuccesses from qmail-alias.c.
+19960301 change: added usermap.
+19960301 bug: failure to append to mbox was a permanent error. impact:
+ if mbox was temporarily unopenable (e.g., because fds were
+ low), mail would be incorrectly bounced. fix: failure is now
+ temporary. tnx DS.
+19960229 change: qmail-alias now preserves any envelope sender that
+ doesn't contain an @, not just <> and <#>.
+19960229 cleanup: revamped byte_* interface.
+19960229 cleanup: renamed str_cpy as str_copy.
+19960229 cleanup: added str_chr.c. used it in qbiff.c, qmail-smtpd.c.
+19960229 cleanup: added str_rchr.c. used it in qmail-send.c, quote.c,
+ qmail-remote.c.
+19960229 cleanup: added byte_rchr.c. used it in qmail-smtpd.c, spawn.c.
+19960229 cleanup: used USEREXT instead of RECIPIENT in qsmhook.c.
+19960229 cleanup: used USEREXT instead of RECIPIENT in qbiff.c.
+19960229 cleanup: removed j and k from rewrite() in qmail-send.c.
+19960229 portability problem: under HP-UX 10 and Solaris 2.5, can't
+ setgroups()/setgid() to the system's nogroup/nobody gid. dorks.
+ impact: inetd chokes, so all SMTP connections are rejected; and
+ ``alias'' mail, including postmaster, bounces. fix: in
+ INSTALL.ids, set up a separate powerless gid (tentatively
+ ``nofiles'') for qmaild and alias. tnx DS and PJG.
+19960229 change: qreceipt now uses qqtalk rather than qmail-inject.
+19960229 change: qlist now uses qqtalk rather than qmail-inject.
+19960229 change: incorporated qmail-setup patch from RN for better
+ error messages.
+19960228 change: added LSPAWN_BREAK in conf-unusual.h; used it in
+ lspawn.c. configurability requested by PJG.
+19960228 portability problem: on several systems, including everything
+ from DEC, select() on a pipe reader returns 1 if there aren't
+ any writers yet. pointed out by DS. impact: qmail-send chewed
+ up lots of CPU time. fix: trigger_set() now opens the pipe for
+ writing after opening it for reading. also added trynpbg1; on
+ working systems, no point in wasting the extra fd.
+19960228 change: qmail-alias uses .qmail sticky bit for forwardonly.
+19960228 change: qlist now sets sticky bit on .qmail file.
+19960228 change: un-documented +list.
+19960228 portability problem: on HP-UX and possibly other systems, the
+ supplementary group list does not include the gid. pointed out
+ by DS. impact: on those systems, tryshsgr could incorrectly set
+ hasshsgr; this would prevent qmail-send from running. fix: if
+ tryshsgr sees that getgroups() returns 0, now it actively sets
+ up a supplementary group list. added chkshsgr to make sure the
+ setgroups() will work.
+19960227 cleanup: eliminated GETSHORT in dns.c in favor of getshort().
+19960227 cleanup: deleted h->len < 3 test from qlist.c:dobody. tnx RN.
+19960227 change: replaced ~ with $HOME in INSTALL.mbox.
+19960227 change: added note about setgid-mail bits to INSTALL.mbox.
+19960227 change: added forward.1.
+19960227 change: modified forward to allow multiple addresses.
+19960227 change: modified forward to take an entire address, not just a
+ hostname.
+19960227 change: renamed qrelay as forward.
+19960227 change: added USEREXT support to qmail-alias.
+19960227 change: added -F to sendmail. the need for this was pointed
+ out by RN.
+19960227 change: added 2 bytes of slop in alloc().
+19960227 bug: received_setup() was not allowing space for the final \0.
+ impact: none; the line length is always between 65 and 75
+ characters, which gives at least 45 characters of slop with
+ existing malloc() implementations. fix: leave space. tnx NH.
+ note that the bug here is really in fmt_strncpy, which was
+ written before i was truly free of the curse of libc.a.
+19960227 change: added ALIAS_EMPTY in conf-unusual.h; used it in
+ qmail-alias.c. tnx PJG.
+19960227 change: added SPAWN_NUMD in conf-unusual.h; used it in spawn.c.
+19960227 change: added conf-unusual.h.
+19960227 cleanup: replaced sizeof(short) with 2 in dns.c.
+19960227 portability problem: on an Alpha, long is 64 bits. pointed out
+ by DS. impact: address lookups produced incorrect results on an
+ Alpha; qmail-makectl and qmail-remote failed. fix: replaced
+ sizeof(long) with 4 in dns.c.
+19960227 portability problem: on an Alpha, bzero() is declared properly
+ via sys/time.h. impact: couldn't compile on an Alpha. fix:
+ removed bzero() declaration from select.h. tnx DS.
+19960227 portability problem: under SCO, sys/file.h is not protected.
+ impact: couldn't compile under SCO. fix: include sys/types.h in
+ lock.c. tnx RN.
+19960219 change: added some .qmail-list hints to qlist.1.
+19960219 change: added +list support to qmail-alias.
+19960215 change: added THANKS.
+19960212 bug: foo was not initialized in qrelay.c. impact: depends on
+ the machine; on some machines, no effect; on other machines,
+ guaranteed core dump. fix: initialized foo. tnx DS.
+19960209 qmail 0.72, beta.
+19960209 change: qmail-alias now replaces dot, not slash, with colon.
+ also, qmeopen() makes sure that .qmail file is S_IFREG; I hope
+ this doesn't cause portability problems.
+19960209 change: added success-reporting procedure to INSTALL.
+19960208 change: added VERSION.
+19960208 change: added qlist2.
+19960208 change: revamped qlist interface. tnx RN.
+19960208 change: improved an error message in qlist.c.
+19960208 change: added qrelay. added relay section to HOWTO. tnx DS.
+19960208 cleanup: included substdio.h in qqtalk.h.
+19960207 bug: prioq_delmin() wasn't guaranteeing heap structure on the
+ last element. impact: scheduled passes could have been delayed,
+ conceivably as long as half an hour. fix: prioq_delmin() now
+ checks when it can safely move the last element.
+19960207 change: added maildirmake.1, maildir2mbox.1.
+19960206 change: revised logo paragraph in THOUGHTS.
+19960206 change: replaced nowhere.org with nowhere.mil in examples.
+ nowhere.org is a real domain... [sigh]
+19960206 change: added qreceipt.1.
+19960206 portability problem: IRIX doesn't have vfork. pointed out by
+ DS. impact: couldn't compile under IRIX. fix: added fork.h,
+ tryvfork.c.
+19960206 portability problem: IRIX doesn't have ranlib. pointed out by
+ DS. impact: couldn't compile under IRIX. fix: added IRIX
+ section in make-cmds.sh.
+19960205 cleanup: removed warning from substdio_copy() documentation; in
+ fact, substdio_copy() can be used safely on a fed substdio.
+19960205 change: added qbiff.1.
+19960204 change: implemented localnet. removed relevant paragraph from
+ THOUGHTS. tnx IS.
+19960204 change: in qmail-remote.8, explained the dangers of smarthost.
+ tnx IS.
+19960204 change: implemented virtualdomains wildcards. tnx JLH.
+19960203 change: qmail-send now handles virtualdomains _after_ locals.
+ updated INSTALL.qsmhook appropriately.
+19960203 change: added note to INSTALL.alias about ~ftp, ~www, ~uucp
+ being owned by root.
+19960130 cleanup: in qlist.c, renamed flagremoved as flagwasthere.
+19960130 bug: qmail-send did not pay attention to flagexitasap in
+ pass_dochan(). impact: qmail-send would happily start new
+ deliveries even if it wanted to exit. fix: qmail-send now
+ returns immediately in pass_selprep() and pass_dochan() if
+ flagexitasap.
+19960130 change: in qlist.c and qlist.1, renamed ext as list.
+19960130 change: in qlist.c and qlist.1, renamed manager as owner.
+19960129 qmail 0.71, beta.
+19960129 change: mentioned djb-qmailbeta in README. tnx MWE.
+19960129 change: added a note to INSTALL.mbox making clear that
+ Mailbox is in mbox format. tnx MWE.
+19960129 change: qlist now warns you if it didn't see any cmds. tnx RN.
+19960129 change: incorporated qlist patch from RN to refuse double subs.
+19960129 change: added qlist.1, contributed by RN. mangled it a bit.
+19960129 bug: comment was not allowed in ``phrase (comment) <route>'';
+ pointed out by RN. impact: some correct address lists could be
+ mis-parsed by qmail-inject or qlist. fix: token.c now allows
+ TOKEN_COMMENT in the appropriate scan.
+19960128 change: added a logo paragraph to THOUGHTS.
+19960127 change: implemented rcpthosts.
+19960127 change: split up some uses of putflush in qmail-remote,
+ qmail-smtpd, spawn.c. eliminated NODELAY and corresponding
+ paragraph in THOUGHTS.
+19960127 change: added quote2(). used it in qmail-alias, qmail-send,
+ qreceipt. now addresses are properly quoted in the From, To,
+ and internal Return-Path of bounces; the From and To of
+ receipts; and the Return-Path/RPLINE of delivered messages.
+ removed relevant paragraph from THOUGHTS.
+19960127 change: in RFCLOOPS, documented fact that Delivered-To address
+ is conventionally not quoted.
+19960127 change: knocked default SMTP timeouts down to 20 minutes.
+19960127 change: added INSTALL.ids. tnx RN.
+19960127 change: in INSTALL, noted that nogroup should already exist.
+19960127 bug: pass_selprep checked pqchan[c] even if pass[c].id. impact:
+ qmail-send wasted CPU time whenever more than one message was
+ waiting on a blocked channel. fix: pass_selprep now checks
+ !pass[c].id.
+19960127 bug: programs invoked from qmail-alias were immune to SIGPIPE.
+ impact: a delivery pipeline such as |yes|head -1000 would loop
+ forever, since yes does not check for write errors. fix: added
+ signal_uninit(). used it before execvp in qmail-alias. [sigh]
+19960127 cleanup: added date822fmt.c. used it in newfield.c, qmail-queue.
+19960127 cleanup: added fmt_uint0.c. used it in myctime.c, newfield.c,
+ qmail-queue.
+19960127 cleanup: added dnsdoe.c. used it in dnscname, dnsfq, dnsip,
+ dnsmxip, dnsptr.
+19960127 cleanup: eliminated temp from dnsfq.c.
+19960127 bug: gen_allocdefs was making assumptions incompatible with the
+ alloc_re interface. impact: qmail-send would dump core if you
+ ran out of memory. fix: changed alloc_re interface.
+19960126 portability problem: some versions of Linux don't have
+ net/route.h. pointed out by RN. impact: couldn't compile under
+ those versions. fix: ipme.c no longer includes net/route.h;
+ hopefully this won't cause new portability problems.
+19960126 change: added chmod instructions to INSTALL and INSTALL.alias.
+ tnx RN.
+19960126 change: INSTALL now refers to the traditional sendmail spot
+ (/usr/lib), not the BSD 4.4 spot (/usr/sbin). tnx RN.
+19960126 change: make auto-uids.h now creates auto-uids.h.tmp first.
+ thus, if someone disobeys the installation instructions, and
+ his make fails to remove targets upon error, he'll still be
+ okay. tnx RN.
+19960126 change: added forgeries.7.
+19960125 cleanup: eliminated flagverbose, flagmetoo in sendmail.
+19960125 cleanup: added substdio_copy.c. used it at several spots.
+19960125 cleanup: added constmap.c. qmail-send now uses constmap for
+ locals and virtualdomains. this will speed things up: no
+ problem now to have thousands of virtual domains. removed
+ relevant paragraph from THOUGHTS.
+19960125 change: added linux section in find-systype. this will affect
+ linux-* systypes. tnx RN for relevant info.
+19960124 change: added -od, -oe, -p, -f to sendmail. the need for
+ these was pointed out by TN.
+19960124 bug: qmail-smtpd was reading from descriptor 1. impact: none;
+ in normal use, both 0 and 1 point to the network. fix: changed
+ 1 to 0.
+19960124 bug: qmail-alias treated any .qmail open failure as permanent.
+ impact: if a .qmail file was temporarily unopenable (e.g.,
+ because of NFS), it was incorrectly ignored. fix: qmail-alias
+ now dies QLX_SOFT on any open failure other than ENOENT.
+19960124 change: added freebsd section in find-systype, same as bsd.os
+ section. this will affect freebsd-* systypes.
+19960124 cleanup: find-systype now immediately converts sys to lowercase.
+19960124 change: qmail-setup now copies man pages into /var/qmail/man;
+ qmail-check checks /var/qmail/man. using .0 style, which might
+ cause trouble on various machines, but better than not trying.
+19960124 change: in qmail-remote.c, changed perm_control to temp_control
+ (and D to Z, thanks); thus failure to read control files (e.g.,
+ because of permissions) is now a temporary error.
+19960124 bug: in qmail-remote.c, temp_chdir() used D, not Z. impact: if
+ chdir() to CONF_HOME failed (e.g., because of NFS), message
+ would be bounced. fix: changed D to Z.
+19960124 change: reorganized README.
+19960124 portability problem: Linux has the fifo kernel bug that I had
+ hoped I'd never run into. impact: messages under Linux (and any
+ other systems with this bug) were picked up only in sweeps, not
+ instantly. fix: triggerpull.c now writes a byte (non-blocking)
+ to the fifo. updated INTERNALS accordingly.
+19960124 bug: in qmail-remote.c, if quit() saw a remote write error, it
+ would call writeerr() even though a message report had already
+ been produced. impact: the mess report would include an extra
+ ``ZConnected but communications failed,'' which was confusing
+ to humans. fix: quit() now simply skips the wait-for-QUIT
+ smtpcode() upon write error.
+19960124 portability problem: Linux does not have SIGSYS or SIGEMT.
+ impact: couldn't compile under Linux. fix: added appropriate
+ ifdefs in signal.c.
+19960124 qmail 0.70, beta.
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..3f36a91
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,33 @@
+netqmail-1.04
+-------------
+No copyright is claimed by the distributors of netqmail for changes from
+qmail 1.03 to netqmail 1.04.
+NOTE: netqmail 1.04 is a community-assembled distribution of qmail from
+the official qmail-1.03.tar.gz and patches approved by the community.
+D. J. Bernstein did not participate in, nor has he been asked to approve
+of this distribution.
+
+netqmail-1.05
+-------------
+James Craig Burley claims copyright on the qmail-isoc patch. See the file
+
+ old-patches/qmail-isoc.patch
+
+for details on James' copyright claim and distribution license.
+
+James' patch has been combined with the original netqmail-1.04 patch
+and the result incorporated into a unified netqmail-1.05 patch.
+
+Apart from James' copyrights, no other copyright is claimed by the
+distributors of netqmail for changes from qmail 1.03 to netqmail 1.05.
+
+NOTE: netqmail 1.05 is a community-assembled distribution of qmail from
+the official qmail-1.03.tar.gz and patches approved by the community.
+D. J. Bernstein did not participate in, nor has he been asked to approve
+of this distribution.
+
+netqmail-1.06
+-------------
+The same copyright information as netqmail-1.05 applies to netqmail-1.06
+with the addition of D. J. Bernstein's dedication of qmail to the public
+domain.
diff --git a/FAQ b/FAQ
new file mode 100644
index 0000000..ab24b64
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,709 @@
+See http://cr.yp.to/qmail/faq.html for newer FAQs not included in this
+document, and http://qmail.org/ for qmail community contributions.
+
+1. Controlling the appearance of outgoing messages
+1.1. How do I set up host masquerading?
+1.2. How do I set up user masquerading?
+1.3. How do I set up Mail-Followup-To automatically?
+
+2. Routing outgoing messages
+2.1. How do I send local messages to another host?
+2.2. How do I set up a null client?
+2.3. How do I send outgoing mail through UUCP?
+2.4. How do I set up a separate queue for a SLIP/PPP link?
+2.5. How do I deal with ``CNAME lookup failed temporarily''?
+
+3. Routing incoming messages by host
+3.1. How do I receive mail for another host name?
+3.2. How do I set up a virtual domain?
+3.3. How do I set up several virtual domains for one user?
+
+4. Routing incoming messages by user
+4.1. How do I forward unrecognized usernames to another host?
+4.2. How do I set up a mailing list?
+4.3. How do I use majordomo with qmail?
+4.4. How do I use procmail with qmail?
+4.5. How do I use elm's filter with qmail?
+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.1. How do I run qmail-smtpd under tcpserver?
+5.2. How do I set up qmail-qmtpd?
+5.3. How do I set up qmail-pop3d?
+5.4. How do I allow selected clients to use this host as a relay?
+5.5. How do I fix up messages from broken SMTP clients?
+5.6. How do I set up qmail-qmqpd?
+
+6. Configuring MUAs to work with qmail
+6.1. How do I make BSD mail generate a Date with the local time zone?
+6.2. How do I make pine work with qmail?
+6.3. How do I make MH work with qmail?
+6.4. How do I stop Sun's dtcm from hanging?
+
+7. Managing the mail system
+7.1. How do I safely stop qmail-send?
+7.2. How do I manually run the queue?
+7.3. How do I rejuvenate a message?
+7.4. How do I organize a big network?
+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.1. How do I tell qmail to do more deliveries at once?
+8.2. How do I keep a copy of all incoming and outgoing mail messages?
+8.3. How do I switch slowly from sendmail to qmail?
+
+
+
+1. Controlling the appearance of outgoing messages
+
+
+1.1. How do I set up host masquerading? All the users on this host,
+zippy.af.mil, are users on af.mil. When joe sends a message to fred, the
+message should say ``From: joe@af.mil'' and ``To: fred@af.mil'', without
+``zippy'' anywhere.
+
+Answer: echo af.mil > /var/qmail/control/defaulthost; chmod 644
+/var/qmail/control/defaulthost.
+
+
+1.2. How do I set up user masquerading? I'd like my own From lines to
+show boss@af.mil rather than god@heaven.af.mil.
+
+Answer: Add MAILHOST=af.mil and MAILUSER=boss to your environment. To
+override From lines supplied by your MUA, add QMAILINJECT=f to your
+environment.
+
+
+1.3. How do I set up Mail-Followup-To automatically? When I send a
+message to the sos@heaven.af.mil mailing list, I'd like to include
+``Mail-Followup-To: sos@heaven.af.mil''.
+
+Answer: Add QMAILMFTFILE=$HOME/.lists to your environment, and put
+sos@heaven.af.mil into ~/.lists.
+
+
+
+2. Routing outgoing messages
+
+
+2.1. How do I send local messages to another host? All the mail for
+af.mil should be delivered to our disk server, pokey.af.mil. I've set up
+an MX from af.mil to pokey.af.mil, but when a user on the af.mil host
+sends a message to boss@af.mil, af.mil tries to deliver it locally. How
+do I stop that?
+
+Answer: Remove af.mil from /var/qmail/control/locals. If qmail-send is
+running, give it a HUP. Make sure the MX is set up properly before you
+do this. Also make sure that pokey can receive mail for af.mil---see
+question 3.1.
+
+
+2.2. How do I set up a null client? I'd like zippy.af.mil to
+send all mail to bigbang.af.mil.
+
+Answer: echo :bigbang.af.mil > /var/qmail/control/smtproutes;
+chmod 644 /var/qmail/control/smtproutes. Disable local delivery as in
+question 2.1. Turn off qmail-smtpd in /etc/inetd.conf.
+
+
+2.3. How do I send outgoing mail through UUCP? I need qmail to send all
+outgoing mail via UUCP to my upstream UUCP site, gonzo.
+
+Answer: Put
+
+ :alias-uucp
+
+into control/virtualdomains and
+
+ |preline -df /usr/bin/uux - -r -gC
+ -a"${SENDER:-MAILER-DAEMON}" gonzo!rmail "($DEFAULT@$HOST)"
+
+(all on one line) into ~alias/.qmail-uucp-default. (For some UUCP
+software you will need to use -d instead of -df.) If qmail-send is
+running, give it a HUP.
+
+
+2.4. How do I set up a separate queue for a SLIP/PPP link?
+
+Answer: Use serialmail (http://pobox.com/~djb/serialmail.html).
+
+
+2.5. How do I deal with ``CNAME lookup failed temporarily''? The log
+showed that a message was deferred for this reason. Why is qmail doing
+CNAME lookups, anyway?
+
+Answer: The SMTP standard does not permit aliased hostnames, so qmail
+has to do a CNAME lookup in DNS for every recipient host. If the
+relevant DNS server is down, qmail defers the message. It will try again
+soon.
+
+
+
+3. Routing incoming messages by host
+
+
+3.1. How do I receive mail for another host name? I'd like our disk
+server, pokey.af.mil, to receive mail addressed to af.mil. I've set up
+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?
+
+Answer: Add af.mil to /var/qmail/control/locals and to
+/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
+nowhere.mil, including root@nowhere.mil and postmaster@nowhere.mil and
+so on, to be delivered to Bob. I've set up the MX already.
+
+Answer: Put
+
+ nowhere.mil:bob
+
+into control/virtualdomains. Add nowhere.mil to control/rcpthosts. If
+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
+bob-whatever. Bob can set up ~bob/.qmail-default to catch all the
+possible addresses, ~bob/.qmail-info to catch info@nowhere.mil, etc.
+
+
+3.3. How do I set up several virtual domains for one user? Bob wants
+another virtual domain, everywhere.org, but he wants to handle
+nowhere.mil users and everywhere.org users differently. How can we do
+that without setting up a second account?
+
+Answer: Put two lines into control/virtualdomains:
+
+ nowhere.mil:bob-nowhere
+ everywhere.org:bob-everywhere
+
+Add nowhere.mil and everywhere.org to control/rcpthosts. If qmail-send
+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
+can even set up .qmail-nowhere-default and .qmail-everywhere-default.
+
+
+
+4. Routing incoming messages by user
+
+
+4.1. How do I forward unrecognized usernames to another host? I'd like
+to set up a LUSER_RELAY pointing at bigbang.af.mil.
+
+Answer: Put
+
+ | forward "$LOCAL"@bigbang.af.mil
+
+into ~alias/.qmail-default.
+
+
+4.2. How do I set up a mailing list? I'd like me-sos@my.host.name to be
+forwarded to a bunch of people.
+
+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.
+You should also touch ~me/.qmail-sos-owner so that bounces come back to
+you rather than the original sender.
+
+Alternative: ezmlm (http://pobox.com/~djb/ezmlm.html) is a modern
+mailing list manager, supporting automatic subscriptions, confirmations,
+archives, fully automatic bounce handling (including warnings to
+subscribers saying which messages they've missed), and more.
+
+
+4.3. How do I use majordomo with qmail?
+
+Answer: See ftp://ftp.eyrie.org/pub/software/majordomo/mjqmail and
+http://www.qmail.org for various methods. majordomo 2.0 is expected to
+support qmail directly.
+
+Beware that majordomo's lists are not crashproof.
+
+
+
+4.4. How do I use procmail with qmail?
+
+Answer: Put
+
+ | preline procmail
+
+into ~/.qmail. You'll have to use a full path for procmail unless
+procmail is in the system's startup PATH. Note that procmail will try to
+deliver to /var/spool/mail/$USER by default; to change this, see
+INSTALL.mbox.
+
+
+4.5. How do I use elm's filter with qmail?
+
+Answer: Put
+
+ | preline filter
+
+into ~/.qmail. You'll have to use a full path for filter unless filter
+is in the system's startup PATH.
+
+
+4.6. How do I create aliases with dots? I tried setting up
+~alias/.qmail-P.D.Q.Bach, but it doesn't do anything.
+
+Answer: Use .qmail-p:d:q:bach. Dots are converted to colons, and
+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.1. How do I run qmail-smtpd under tcpserver? inetd is barfing at high
+loads, cutting off service for ten-minute stretches. I'd also like
+better connection logging.
+
+Answer: First, install the tcpserver program, part of the ucspi-tcp
+package (http://pobox.com/~djb/ucspi-tcp.html). Second, remove the smtp
+line from /etc/inetd.conf, and put the line
+
+ tcpserver -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd &
+
+into your system startup files. Replace 7770 with your qmaild uid, and
+replace 2108 with your nofiles gid. Don't forget the &. The change will
+take effect at your next reboot.
+
+By default, tcpserver allows at most 40 simultaneous qmail-smtpd
+processes. To raise this limit to 400, use tcpserver -c 400. To keep
+track of who's connecting and for how long, run (on two lines)
+
+ tcpserver -v -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd \
+ 2>&1 | /var/qmail/bin/splogger smtpd 3 &
+
+
+5.2. How do I set up qmail-qmtpd?
+
+Answer: Two steps. First, put a
+
+ qmtp 209/tcp
+
+line into /etc/services. Second, put (all on one line)
+
+ qmtp stream tcp nowait qmaild
+ /var/qmail/bin/tcp-env tcp-env /var/qmail/bin/qmail-qmtpd
+
+into /etc/inetd.conf, and give inetd a HUP.
+
+If you have tcpserver installed, skip the inetd step, and set up
+
+ tcpserver -u 7770 -g 2108 0 qmtp /var/qmail/bin/qmail-qmtpd &
+
+replacing 7770 and 2108 with the qmaild uid and nofiles gid. See
+question 5.1 for more details on tcpserver.
+
+
+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
+(http://pobox.com/~djb/checkpwd.html). Second, make sure you have a
+
+ pop3 110/tcp
+
+line in /etc/services. Third, put (all on one line, including
+qmail-popup twice)
+
+ pop3 stream tcp nowait root
+ /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
+host's fully qualified domain name. Fourth, set up Maildir delivery for
+any user who wants to read mail via POP.
+
+If you have tcpserver installed, skip the inetd step, and set up (on two
+lines)
+
+ tcpserver 0 pop3 /var/qmail/bin/qmail-popup YOURHOST \
+ /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir &
+
+replacing YOURHOST with your host's fully qualified domain name. See
+question 5.1 for more details on tcpserver.
+
+Security note: pop3d should be used only within a secure network;
+otherwise an eavesdropper can steal passwords.
+
+
+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
+control/rcpthosts.
+
+Answer: Three steps. First, install tcp-wrappers, available separately,
+including hosts_options. Second, change your qmail-smtpd line in
+inetd.conf to
+
+ smtp stream tcp nowait qmaild /usr/local/bin/tcpd
+ /var/qmail/bin/tcp-env /var/qmail/bin/qmail-smtpd
+
+(all on one line) and give inetd a HUP. Third, in tcpd's hosts.allow,
+make a line setting the environment variable RELAYCLIENT to the empty
+string for the selected clients:
+
+ tcp-env: 1.2.3.4, 1.2.3.5: setenv = RELAYCLIENT
+
+Here 1.2.3.4 and 1.2.3.5 are the clients' IP addresses. qmail-smtpd
+ignores control/rcpthosts when RELAYCLIENT is set. (It also appends
+RELAYCLIENT to each envelope recipient address. See question 5.5 for an
+application.)
+
+Alternative procedure, if you are using tcpserver 0.80 or above: Create
+/etc/tcp.smtp containing
+
+ 1.2.3.6:allow,RELAYCLIENT=""
+ 127.:allow,RELAYCLIENT=""
+
+to allow clients with IP addresses 1.2.3.6 and 127.*. Run
+
+ tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
+
+Finally, insert
+
+ -x /etc/tcp.smtp.cdb
+
+after tcpserver in your qmail-smtpd invocation.
+
+
+5.5. How do I fix up messages from broken SMTP clients?
+
+Answer: Three steps. First, put
+
+ | bouncesaying 'Permission denied' [ "@$HOST" != "@fixme" ]
+ | qmail-inject -f "$SENDER" -- "$DEFAULT"
+
+into ~alias/.qmail-fixup-default. Second, put
+
+ fixme:fixup
+
+into /var/qmail/control/virtualdomains, and give qmail-send a HUP.
+Third, follow the procedure in question 5.4, but set RELAYCLIENT to the
+string ``@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
+tcpserver instead of inetd and tcpd, put
+
+ 1.2.3.6:allow,RELAYCLIENT="@fixme"
+ 1.2.3.7:allow,RELAYCLIENT="@fixme"
+
+into /etc/tcp.smtp, and run tcprules as in question 5.4.
+
+
+5.6. How do I set up qmail-qmqpd? I'd like to allow fast queueing of
+outgoing mail from authorized clients.
+
+Answer: Make sure you have installed tcpserver 0.80 or above. Create
+/etc/qmqp.tcp in tcprules format to allow connections from authorized
+hosts. For example, if queueing is allowed from 1.2.3.*:
+
+ 1.2.3.:allow
+ :deny
+
+Convert /etc/qmqp.tcp to /etc/qmqp.cdb:
+
+ tcprules /etc/qmqp.cdb /etc/qmqp.tmp < /etc/qmqp.tcp
+
+Finally, set up
+
+ tcpserver -x /etc/qmqp.cdb -u 7770 -g 2108 0 628 /var/qmail/bin/qmail-qmqpd &
+
+replacing 7770 and 2108 with the qmaild uid and nofiles gid. See
+question 5.1 for more details on tcpserver.
+
+
+
+6. Configuring MUAs to work with qmail
+
+
+6.1. How do I make BSD mail generate a Date with the local time zone?
+When I send mail, I'd rather use the local time zone than GMT, since
+some MUAs don't know how to display Date in the receiver's time zone.
+
+Answer: Put
+
+ set sendmail=/var/qmail/bin/datemail
+
+into your .mailrc or your system-wide Mail.rc. Beware that BSD mail is
+neither secure nor reliable.
+
+
+6.2. How do I make pine work with qmail?
+
+Answer: Put
+
+ sendmail-path=/usr/lib/sendmail -oem -oi -t
+
+into /usr/local/lib/pine.conf. (This will work with sendmail too.)
+Beware that pine is neither secure nor reliable.
+
+
+6.3. How do I make MH work with qmail?
+
+Answer: Put
+
+ postproc: /usr/mh/lib/spost
+
+into each user's .mh_profile. (This will work with sendmail too.) Beware
+that MH is neither secure nor reliable.
+
+
+6.4. How do I stop Sun's dtcm from hanging?
+
+Answer: There is a novice programming error in dtcm, known as ``failure
+to close the output side of the pipe in the child.'' Sun has, at the
+time of this writing, not yet provided a patch. Sorry.
+
+
+
+7. Managing the mail system
+
+
+7.1. How do I safely stop qmail-send? Back when we were running
+sendmail, it was always tricky to kill sendmail without risking the loss
+of current deliveries; what should I do with qmail-send?
+
+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 qmail,
+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
+all the remote messages right now.
+
+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
+again; it's going to be down for at least another two days. I know Eric
+has been expecting an important message---in fact, I see it sitting here
+in /var/qmail/queue/mess/15/26902. It's been in the queue for six days;
+how can I make sure it isn't bounced tomorrow?
+
+Answer: Just touch /var/qmail/queue/info/15/26902. (This is the only
+form of queue modification that's safe while qmail is running.)
+
+
+7.4. How do I organize a big network? I have a lot of machines, and I
+don't know where to start.
+
+Answer: First, choose the domain name where your users will receive
+mail. This is normally the shortest domain name you control. If you are
+in charge of *.movie.edu, you can use addresses like joe@movie.edu.
+
+Second, choose the machine that will know what to do with different
+users at movie.edu. Set up a host name in DNS for this machine:
+
+ mailhost.movie.edu IN A 1.2.3.4
+ 4.3.2.1.in-addr.arpa IN PTR mailhost.movie.edu
+
+Here 1.2.3.4 is the IP address of that machine.
+
+Third, make a list of machines where mail should end up. For example, if
+mail for Bob should end up on Bob's workstation, put Bob's workstation
+onto the list. For each of these machines, set up a host name in DNS:
+
+ bobshost.movie.edu IN A 1.2.3.7
+ 7.3.2.1.in-addr.arpa IN PTR bobshost.movie.edu
+
+Fourth, install qmail on bobshost.movie.edu. qmail will automatically
+configure itself to accept messages for bob@bobshost.movie.edu and
+deliver them to ~bob/Mailbox on bobshost. Do the same for the other
+machines where mail should end up.
+
+Fifth, install qmail on mailhost.movie.edu. Put
+
+ movie.edu:alias-movie
+
+into control/virtualdomains on mailhost. Then forward bob@movie.edu to
+bob@bobshost.movie.edu, by putting
+
+ bob@bobshost.movie.edu
+
+into ~alias/.qmail-movie-bob. Do the same for other users.
+
+Sixth, put movie.edu into control/rcpthosts on mailhost.movie.edu, so
+that mailhost.movie.edu will accept messages for users at movie.edu.
+
+Seventh, set up an MX record in DNS to deliver movie.edu messages to
+mailhost:
+
+ movie.edu IN MX 10 mailhost.movie.edu
+
+Eighth, on all your machines, put movie.edu into control/defaulthost.
+
+
+7.5. How do I back up and restore the queue disk?
+
+Answer: You can't.
+
+One difficulty is that you can't get a consistent snapshot of the queue
+while qmail-send is running. Another difficulty is that messages in the
+queue must have filenames that match their inode numbers.
+
+However, the big problem is that backups---even twice-daily backups---
+are far too unreliable for mail. If your disk dies, there will be very
+little overlap between the messages saved in the last backup and the
+messages that were lost.
+
+There are several ways to add real reliability to a mail server. Battery
+backups will keep your server alive, letting you park the disk to avoid
+a head crash, when the power goes out. Solid-state disks have their own
+battery backups. RAID boxes let you replace dead disks without losing
+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.1. How do I tell qmail to do more deliveries at once? It's running
+only 20 parallel qmail-remote processes.
+
+Answer: Decide how many deliveries you want to allow at once. Put that
+number into control/concurrencyremote. Restart qmail-send as in question
+7.1. If your system has resource limits, make sure you set the
+descriptors limit to at least double the concurrency plus 5; otherwise
+you'll get lots of unnecessary deferrals whenever a big burst of mail
+shows up. Note that qmail also imposes a compile-time concurrency limit,
+120 by default; this is set in conf-spawn.
+
+
+8.2. How do I keep a copy of all incoming and outgoing mail messages?
+
+Answer: Set QUEUE_EXTRA to "Tlog\0" and QUEUE_EXTRALEN to 5 in extra.h.
+Recompile qmail. Put ./msg-log into ~alias/.qmail-log.
+
+You can also use QUEUE_EXTRA to, e.g., record the Message-ID of every
+message: run
+
+ | awk '/^$/ { exit } /^[mM][eE][sS][sS][aA][gG][eE]-/ { print }'
+
+from ~alias/.qmail-log.
+
+
+8.3. How do I switch slowly from sendmail to qmail? I'm thinking of
+moving the heaven.af.mil network over to qmail, but first I'd like to
+give my users a chance to try out qmail without affecting current
+sendmail deliveries. We're using NFS.
+
+Answer: Find a host in your network, say pc.heaven.af.mil, that isn't
+running an SMTP server. (If addresses at pc.heaven.af.mil are used, you
+should already have an MX pointing pc.heaven.af.mil to your mail hub.)
+
+Set up a new MX record pointing lists.heaven.af.mil to pc.heaven.af.mil.
+Install qmail on pc.heaven.af.mil. Replace pc with lists in the control
+files. Make the qmail man pages available on all your machines.
+
+Now tell your users about qmail. A user can forward joe@heaven.af.mil to
+joe@lists.heaven.af.mil to get ~/Mailbox delivery; he can set up .qmail
+files; he can start running his own mailing lists @lists.heaven.af.mil.
+
+When you're ready to turn sendmail off, you can set up pc.heaven.af.mil
+as your new mail hub. Add heaven.af.mil to control/locals, and change
+the heaven.af.mil MX to point to pc.heaven.af.mil. Make sure you leave
+lists.heaven.af.mil in control/locals so that transition addresses will
+continue to work.
diff --git a/FILES b/FILES
new file mode 100644
index 0000000..0f70971
--- /dev/null
+++ b/FILES
@@ -0,0 +1,434 @@
+BLURB
+BLURB2
+BLURB3
+BLURB4
+README
+FAQ
+INSTALL
+INSTALL.alias
+INSTALL.ctl
+INSTALL.ids
+INSTALL.maildir
+INSTALL.mbox
+INSTALL-1.03
+INSTALL.vsm
+REMOVE.sendmail
+REMOVE.binmail
+TEST.deliver
+TEST.receive
+UPGRADE
+THOUGHTS
+TODO
+THANKS
+CHANGES
+SECURITY
+INTERNALS
+SENDMAIL
+PIC.local2alias
+PIC.local2ext
+PIC.local2local
+PIC.local2rem
+PIC.local2virt
+PIC.nullclient
+PIC.relaybad
+PIC.relaygood
+PIC.rem2local
+FILES
+VERSION
+SYSDEPS
+TARGETS
+Makefile
+BIN.README
+BIN.Makefile
+idedit.c
+conf-break
+auto_break.h
+conf-spawn
+auto_spawn.h
+chkspawn.c
+conf-split
+auto_split.h
+conf-patrn
+auto_patrn.h
+conf-users
+conf-groups
+auto_uids.h
+auto_usera.h
+extra.h
+addresses.5
+except.1
+bouncesaying.1
+condredirect.1
+dot-qmail.9
+envelopes.5
+forgeries.7
+forward.1
+maildir2mbox.1
+maildirmake.1
+maildirwatch.1
+mailsubj.1
+mbox.5
+preline.1
+qbiff.1
+qmail-clean.8
+qmail-command.8
+qmail-control.9
+qmail-getpw.9
+qmail-header.5
+qmail-inject.8
+qmail-limits.9
+qmail-local.8
+qmail-log.5
+qmail-lspawn.8
+qmail-newmrh.9
+qmail-newu.9
+qmail-pop3d.8
+qmail-popup.8
+qmail-pw2u.9
+qmail-qmqpc.8
+qmail-qmqpd.8
+qmail-qmtpd.8
+qmail-qread.8
+qmail-qstat.8
+qmail-queue.8
+qmail-remote.8
+qmail-rspawn.8
+qmail-send.9
+qmail-showctl.8
+qmail-smtpd.8
+qmail-start.9
+qmail-tcpok.8
+qmail-tcpto.8
+qmail-users.9
+qmail.7
+qreceipt.1
+splogger.8
+tcp-env.1
+config.sh
+config-fast.sh
+qmail-clean.c
+qmail-getpw.c
+qmail-inject.c
+qmail-local.c
+qmail-lspawn.c
+qmail-newmrh.c
+qmail-newu.c
+qmail-pop3d.c
+qmail-popup.c
+qmail-pw2u.c
+qmail-qmqpc.c
+qmail-qmqpd.c
+qmail-qmtpd.c
+qmail-qread.c
+qmail-qstat.sh
+qmail-queue.c
+qmail-remote.c
+qmail-rspawn.c
+qmail-send.c
+qmail-showctl.c
+qmail-smtpd.c
+qmail-start.c
+qmail-tcpok.c
+qmail-tcpto.c
+spawn.c
+dnscname.c
+dnsfq.c
+dnsip.c
+dnsmxip.c
+dnsptr.c
+hostname.c
+ipmeprint.c
+tcp-env.c
+sendmail.c
+qreceipt.c
+qsmhook.c
+qbiff.c
+forward.c
+preline.c
+predate.c
+except.c
+bouncesaying.c
+condredirect.c
+maildirmake.c
+maildir2mbox.c
+maildirwatch.c
+splogger.c
+qail.sh
+elq.sh
+pinq.sh
+qmail-upq.sh
+datemail.sh
+mailsubj.sh
+qlx.h
+rcpthosts.h
+rcpthosts.c
+commands.h
+commands.c
+dnsdoe.h
+dnsdoe.c
+fmtqfn.h
+fmtqfn.c
+gfrom.h
+gfrom.c
+myctime.h
+myctime.c
+newfield.h
+newfield.c
+qsutil.h
+qsutil.c
+readsubdir.h
+readsubdir.c
+received.h
+received.c
+tcpto.h
+tcpto.c
+tcpto_clean.c
+trigger.h
+trigger.c
+triggerpull.h
+triggerpull.c
+trynpbg1.c
+trysyslog.c
+conf-cc
+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
+make-compile.sh
+make-load.sh
+make-makelib.sh
+trycpp.c
+warn-auto.sh
+auto-str.c
+auto-int.c
+auto-int8.c
+auto-gid.c
+auto-uid.c
+hier.c
+install.c
+instcheck.c
+install-big.c
+alloc.3
+alloc.h
+alloc.c
+alloc_re.c
+case.3
+case.h
+case_diffb.c
+case_diffs.c
+case_lowerb.c
+case_lowers.c
+case_starts.c
+cdb.3
+cdb.h
+cdb_hash.c
+cdb_seek.c
+cdb_unpack.c
+cdbmake.h
+cdbmake_add.c
+cdbmake_hash.c
+cdbmake_pack.c
+cdbmss.h
+cdbmss.c
+coe.3
+coe.h
+coe.c
+fd.h
+fd_copy.3
+fd_copy.c
+fd_move.3
+fd_move.c
+fifo_make.3
+fifo.h
+fifo.c
+trymkffo.c
+fork.h1
+fork.h2
+tryvfork.c
+now.3
+now.h
+now.c
+open.h
+open_append.c
+open_excl.c
+open_read.c
+open_trunc.c
+open_write.c
+seek.h
+seek_cur.c
+seek_end.c
+seek_set.c
+seek_trunc.c
+conf-qmail
+auto_qmail.h
+qmail.h
+qmail.c
+gen_alloc.h
+gen_allocdefs.h
+stralloc.3
+stralloc.h
+stralloc_eady.c
+stralloc_pend.c
+stralloc_copy.c
+stralloc_opyb.c
+stralloc_opys.c
+stralloc_cat.c
+stralloc_catb.c
+stralloc_cats.c
+stralloc_arts.c
+strerr.h
+strerr_sys.c
+strerr_die.c
+substdio.h
+substdio.c
+substdi.c
+substdo.c
+substdio_copy.c
+subfd.h
+subfderr.c
+subfdouts.c
+subfdout.c
+subfdins.c
+subfdin.c
+readwrite.h
+exit.h
+timeoutconn.h
+timeoutconn.c
+timeoutread.h
+timeoutread.c
+timeoutwrite.h
+timeoutwrite.c
+remoteinfo.h
+remoteinfo.c
+uint32.h1
+uint32.h2
+tryulong32.c
+wait.3
+wait.h
+wait_pid.c
+wait_nohang.c
+trywaitp.c
+sig.h
+sig_alarm.c
+sig_block.c
+sig_catch.c
+sig_pause.c
+sig_pipe.c
+sig_child.c
+sig_term.c
+sig_hup.c
+sig_misc.c
+sig_bug.c
+trysgact.c
+trysgprm.c
+env.3
+env.h
+env.c
+envread.c
+byte.h
+byte_chr.c
+byte_copy.c
+byte_cr.c
+byte_diff.c
+byte_rchr.c
+byte_zero.c
+str.h
+str_chr.c
+str_cpy.c
+str_diff.c
+str_diffn.c
+str_len.c
+str_rchr.c
+str_start.c
+lock.h
+lock_ex.c
+lock_exnb.c
+lock_un.c
+tryflock.c
+getln.3
+getln.h
+getln.c
+getln2.3
+getln2.c
+sgetopt.3
+sgetopt.h
+sgetopt.c
+subgetopt.3
+subgetopt.h
+subgetopt.c
+error.3
+error_str.3
+error_temp.3
+error.h
+error.c
+error_str.c
+error_temp.c
+fmt.h
+fmt_str.c
+fmt_strn.c
+fmt_uint.c
+fmt_uint0.c
+fmt_ulong.c
+scan.h
+scan_ulong.c
+scan_8long.c
+slurpclose.h
+slurpclose.c
+quote.h
+quote.c
+hfield.h
+hfield.c
+headerbody.h
+headerbody.c
+token822.h
+token822.c
+control.h
+control.c
+datetime.3
+datetime.h
+datetime.c
+datetime_un.c
+prioq.h
+prioq.c
+date822fmt.h
+date822fmt.c
+dns.h
+dns.c
+trylsock.c
+tryrsolv.c
+ip.h
+ip.c
+ipalloc.h
+ipalloc.c
+select.h1
+select.h2
+trysysel.c
+ndelay.h
+ndelay.c
+ndelay_off.c
+direntry.3
+direntry.h1
+direntry.h2
+trydrent.c
+prot.h
+prot.c
+chkshsgr.c
+warn-shsgr
+tryshsgr.c
+ipme.h
+ipme.c
+trysalen.c
+maildir.5
+maildir.h
+maildir.c
+tcp-environ.5
+constmap.h
+constmap.c
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..b2da6b1
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1 @@
+See http://lifewithqmail.org/lwq.html
diff --git a/INSTALL.alias b/INSTALL.alias
new file mode 100644
index 0000000..672365a
--- /dev/null
+++ b/INSTALL.alias
@@ -0,0 +1,40 @@
+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
+user, alias. Delivery instructions for foo go into ~alias/.qmail-foo;
+delivery instructions for user-foo go into ~user/.qmail-foo. See
+dot-qmail.0 for the full story.
+
+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 fastforward
+package, available separately. /etc/aliases should already include the
+aliases discussed below---Postmaster, MAILER-DAEMON, and root.
+
+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
+set up right now.
+
+* Postmaster. You're not an Internet citizen if this address doesn't
+work. Simply touch (and chmod 644) ~alias/.qmail-postmaster; any mail
+for Postmaster will be delivered to ~alias/Mailbox.
+
+* MAILER-DAEMON. Not required, but users sometimes respond to bounce
+messages. Touch (and chmod 644) ~alias/.qmail-mailer-daemon.
+
+* root. Under qmail, root never receives mail. Your system may generate
+mail messages to root every night; if you don't have an alias for root,
+those messages will bounce. (They'll end up double-bouncing to the
+postmaster.) Set up an alias for root in ~alias/.qmail-root. .qmail
+files are similar to .forward files, but beware that they are strictly
+line-oriented---see dot-qmail.0 for details.
+
+* Other non-user accounts. Under qmail, non-user accounts don't get
+mail; ``user'' means a non-root account that owns ~account. Set up
+aliases for any non-user accounts that normally receive mail.
+
+Note that special accounts such as ftp, www, and uucp should always have
+home directories owned by root.
+
+* Default. If you want, you can touch ~alias/.qmail-default to catch
+everything else. Beware: this will also catch typos and other addresses
+that should probably be bounced instead. It won't catch addresses that
+start with a user name---the user can set up his own ~/.qmail-default.
diff --git a/INSTALL.ctl b/INSTALL.ctl
new file mode 100644
index 0000000..00ce689
--- /dev/null
+++ b/INSTALL.ctl
@@ -0,0 +1,38 @@
+As you've seen, qmail has essentially no pre-compilation configuration.
+You should never have to recompile it unless you want to change the
+qmail home directory, usernames, or uids.
+
+qmail does allow quite a bit of easy post-installation configuration. If
+you care how your machine greets other machines via SMTP, for example,
+you can put an appropriate line into /var/qmail/control/smtpgreeting.
+
+But this is all optional---if control/smtpgreeting doesn't exist, qmail
+will do something reasonable by default. You shouldn't worry much about
+configuration right now. You can always come back and tune things later.
+
+There's one big exception. You MUST tell qmail your hostname. Just run
+the config-fast script:
+
+ # ./config-fast your.full.host.name
+
+config-fast puts your.full.host.name into control/me. It also puts it
+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
+design decision. qmail does all its local functions---header rewriting,
+checking if a recipient is local, etc.---without talking to the network.
+The point is that qmail can continue accepting and delivering local mail
+even if your network connection goes down.)
+
+Next, read through FAQ for information on setting up optional features
+like masquerading. If you really want to learn right now what all the
+configuration possibilities are, see qmail-control.0.
diff --git a/INSTALL.ids b/INSTALL.ids
new file mode 100644
index 0000000..a50e10d
--- /dev/null
+++ b/INSTALL.ids
@@ -0,0 +1,72 @@
+Here's how to set up the qmail groups and the qmail users.
+
+On some systems there are commands that make this easy. Solaris and
+Linux:
+
+ # groupadd nofiles
+ # useradd -g nofiles -d /var/qmail/alias alias
+ # useradd -g nofiles -d /var/qmail qmaild
+ # useradd -g nofiles -d /var/qmail qmaill
+ # useradd -g nofiles -d /var/qmail qmailp
+ # groupadd qmail
+ # useradd -g qmail -d /var/qmail qmailq
+ # useradd -g qmail -d /var/qmail qmailr
+ # 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:
+
+ # addgroup nofiles
+ # adduser -g nofiles -H/var/qmail/alias -G,,, -s/dev/null -P'*' alias
+ # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaild
+ # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaill
+ # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmailp
+ # addgroup qmail
+ # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailq
+ # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailr
+ # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmails
+
+AIX:
+
+ # mkgroup -A nofiles
+ # mkuser pgrp=nofiles home=/var/qmail/alias shell=/bin/true alias
+ # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaild
+ # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaill
+ # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmailp
+ # mkgroup -A qmail
+ # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailq
+ # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailr
+ # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmails
+
+On other systems, you will have to edit /etc/group and /etc/passwd
+manually. First add two new lines to /etc/group, something like
+
+ qmail:*:2107:
+ nofiles:*:2108:
+
+where 2107 and 2108 are different from the other gids in /etc/group.
+Next (using vipw) add six new lines to /etc/passwd, something like
+
+ alias:*:7790:2108::/var/qmail/alias:/bin/true
+ qmaild:*:7791:2108::/var/qmail:/bin/true
+ qmaill:*:7792:2108::/var/qmail:/bin/true
+ qmailp:*:7793:2108::/var/qmail:/bin/true
+ qmailq:*:7794:2107::/var/qmail:/bin/true
+ qmailr:*:7795:2107::/var/qmail:/bin/true
+ qmails:*:7796:2107::/var/qmail:/bin/true
+
+where 7790 through 7796 are _new_ uids, 2107 is the qmail gid, and 2108
+is the nofiles gid. Make sure you use the nofiles gid for qmaild,
+qmaill, qmailp, and alias, and the qmail gid for qmailq, qmailr, and
+qmails.
diff --git a/INSTALL.maildir b/INSTALL.maildir
new file mode 100644
index 0000000..72373aa
--- /dev/null
+++ b/INSTALL.maildir
@@ -0,0 +1,59 @@
+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.
+
+
+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.
diff --git a/INSTALL.mbox b/INSTALL.mbox
new file mode 100644
index 0000000..93ca16c
--- /dev/null
+++ b/INSTALL.mbox
@@ -0,0 +1,53 @@
+The qmail package includes a local delivery agent, qmail-local, which
+provides user-controlled mailing lists, cross-host alias loop detection,
+and many other important qmail features.
+
+There's one important difference between qmail-local and binmail:
+qmail-local delivers mail by default into ~user/Mailbox, rather than
+/var/spool/mail/user. It uses mbox format, with lockf locking on systems
+that don't have flock (HP/UX, Solaris), and flock locking otherwise.
+
+This file explains how to switch your system to ~user/Mailbox. You
+aren't required to do this; for further discussion of /var/spool/mail,
+and an explanation of how to continue using binmail for local
+deliveries, see INSTALL.vsm.
+
+The basic procedure for switching to ~user/Mailbox is simple:
+
+ * Move each /var/spool/mail/user to ~user/Mailbox. For safety, do
+ this in single-user mode.
+
+ * As root, set up a symbolic link from /var/spool/mail/user to
+ ~user/Mailbox for each user. /var/spool/mail should be mode 1777,
+ so users will not be able to accidentally remove these links.
+
+A few mail programs are unable to handle symbolic links, so you will
+have to configure them to look at ~user/Mailbox directly:
+
+ * procmail: Change SYSTEM_MBOX in config.h and recompile; or, with
+ recent versions, define MAILSPOOLHOME in src/authenticate.c.
+
+An alternative to symbolic links is hlfsd. Consult the documentation for
+hlfsd if it is included in your operating system.
+
+If /var/spool/mail is large, you can gain extra speed by configuring
+all your mail software to look at ~user/Mailbox directly:
+
+ * Most MUAs: Put ``setenv MAIL $HOME/Mailbox'' in your system-wide
+ .cshrc and ``MAIL=$HOME/Mailbox; export MAIL'' in your system-wide
+ .profile.
+
+ * elm: Change "mailbox" to "Mailbox" around line 388 of newmbox.c and
+ recompile. (elm looks at $MAIL, but without this change elm will
+ fail if two users try to read mail simultaneously.)
+
+ * pine: Put ``inbox-path=Mailbox'' in your system-wide pine.conf.
+ (For pine versions more recent than 3.91, see also FAQ 6.2.)
+
+ * 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
+/var/spool/mail, have made all their mail software setgid mail. After
+you move the mailboxes, you can---and, for security, should---remove
+those setgid-mail bits.
diff --git a/INSTALL.vsm b/INSTALL.vsm
new file mode 100644
index 0000000..cf6a6cc
--- /dev/null
+++ b/INSTALL.vsm
@@ -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.
diff --git a/INTERNALS b/INTERNALS
new file mode 100644
index 0000000..e668ae8
--- /dev/null
+++ b/INTERNALS
@@ -0,0 +1,156 @@
+1. Overview
+
+Here's the data flow in the qmail suite:
+
+ qmail-smtpd --- qmail-queue --- qmail-send --- qmail-rspawn --- qmail-remote
+ / | \
+qmail-inject _/ qmail-clean \_ qmail-lspawn --- qmail-local
+
+Every message is added to a central queue directory by qmail-queue.
+qmail-queue is invoked as needed, usually by qmail-inject for locally
+generated messages, qmail-smtpd for messages received through SMTP,
+qmail-local for forwarded messages, or qmail-send for bounce messages.
+
+Every message is then delivered by qmail-send, in cooperation with
+qmail-lspawn and qmail-rspawn, and cleaned up by qmail-clean. These four
+programs are long-running daemons.
+
+The queue is designed to be crashproof, provided that the underlying
+filesystem is crashproof. All cleanups are handled by qmail-send and
+qmail-clean without human intervention. See section 6 for more details.
+
+
+2. Queue structure
+
+Each message in the queue is identified by a unique number, let's say
+457. The queue is organized into several directories, each of which may
+contain files related to message 457:
+
+ mess/457: the message
+ todo/457: the envelope: where the message came from, where it's going
+ intd/457: the envelope, under construction by qmail-queue
+ info/457: the envelope sender address, after preprocessing
+ local/457: local envelope recipient addresses, after preprocessing
+ remote/457: remote envelope recipient addresses, after preprocessing
+ bounce/457: permanent delivery errors
+
+Here are all possible states for a message. + means a file exists; -
+means it does not exist; ? means it may or may not exist.
+
+ S1. -mess -intd -todo -info -local -remote -bounce
+ S2. +mess -intd -todo -info -local -remote -bounce
+ S3. +mess +intd -todo -info -local -remote -bounce
+ S4. +mess ?intd +todo ?info ?local ?remote -bounce (queued)
+ S5. +mess -intd -todo +info ?local ?remote ?bounce (preprocessed)
+
+Guarantee: If mess/457 exists, it has inode number 457.
+
+
+3. How messages enter the queue
+
+To add a message to the queue, qmail-queue first creates a file in a
+separate directory, pid/, with a unique name. The filesystem assigns
+that file a unique inode number. qmail-queue looks at that number, say
+457. By the guarantee above, message 457 must be in state S1.
+
+qmail-queue renames pid/whatever as mess/457, moving to S2. It writes
+the message to mess/457. It then creates intd/457, moving to S3, and
+writes the envelope information to intd/457.
+
+Finally qmail-queue creates a new link, todo/457, for intd/457, moving
+to S4. At that instant the message has been successfully queued, and
+qmail-queue leaves it for further handling by qmail-send.
+
+qmail-queue starts a 24-hour timer before touching any files, and
+commits suicide if the timer expires.
+
+
+4. How queued messages are preprocessed
+
+Once a message has been queued, qmail-send must decide which recipients
+are local and which recipients are remote. It may also rewrite some
+recipient addresses.
+
+When qmail-send notices todo/457, it knows that message 457 is in S4. It
+removes info/457, local/457, and remote/457 if they exist. Then it reads
+through todo/457. It creates info/457, possibly local/457, and possibly
+remote/457. When it is done, it removes intd/457. The message is still
+in S4 at this point. Finally qmail-send removes todo/457, moving to S5.
+At that instant the message has been successfully preprocessed.
+
+
+5. How preprocessed messages are delivered
+
+Messages at S5 are handled as follows. Each address in local/457 and
+remote/457 is marked either NOT DONE or DONE.
+
+ DONE: The message was successfully delivered, or the last delivery
+ attempt met with permanent failure. Either way, qmail-send
+ should not attempt further delivery to this address.
+
+ NOT DONE: If there have been any delivery attempts, they have all
+ met with temporary failure. Either way, qmail-send should
+ try delivery in the future.
+
+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 as DONE. If the delivery attempt meets with permanent failure,
+qmail-send first appends a note to bounce/457, creating bounce/457 if
+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
+a new bounce message, created from bounce/457 and mess/457; (2) deletes
+bounce/457.
+
+When all addresses in local/457 are DONE, qmail-send deletes local/457.
+Same for remote/457.
+
+When local/457 and remote/457 are gone, qmail-send eliminates the
+message, as follows. First, if bounce/457 exists, qmail-send handles it
+as described above. Once bounce/457 is definitely gone, qmail-send
+deletes info/457, moving to S2, and finally mess/457, moving to S1.
+
+
+6. Cleanups
+
+If the computer crashes while qmail-queue is trying to queue a message,
+or while qmail-send is eliminating a message, the message may be left in
+state S2 or S3.
+
+When qmail-send sees a message in state S2 or S3---other than one
+it is currently eliminating!---where mess/457 is more than 36 hours old,
+it deletes intd/457 if that exists, then deletes mess/457. Note that any
+qmail-queue handling the message must be dead.
+
+Similarly, when qmail-send sees a file in the pid/ directory that is
+more than 36 hours old, it deletes it.
+
+Cleanups are not necessary if the computer crashes while qmail-send is
+delivering a message. At worst a message may be delivered twice. (There
+is no way for a distributed mail system to eliminate the possibility of
+duplication. What if an SMTP connection is broken just before the server
+acknowledges successful receipt of the message? The client must assume
+the worst and send the message again. Similarly, if the computer crashes
+just before qmail-send marks a message as DONE, the new qmail-send must
+assume the worst and send the message again. The usual solutions in the
+database literature---e.g., keeping log files---amount to saying that
+it's the recipient's computer's job to discard duplicate messages.)
+
+
+7. Further notes
+
+Currently info/457 serves two purposes: first, it records the envelope
+sender; second, its modification time is used to decide when a message
+has been in the queue too long. In the future info/457 may store more
+information. Any non-backwards-compatible changes will be identified by
+version numbers.
+
+When qmail-queue has successfully placed a message into the queue, it
+pulls a trigger offered by qmail-send. Here is the current triggering
+mechanism: lock/trigger is a named pipe. Before scanning todo/,
+qmail-send opens lock/trigger O_NDELAY for reading. It then selects for
+readability on lock/trigger. qmail-queue pulls the trigger by writing a
+byte O_NDELAY to lock/trigger. This makes lock/trigger readable and
+wakes up qmail-send. Before scanning todo/ again, qmail-send closes and
+reopens lock/trigger.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0f0e31a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2141 @@
+# Don't edit Makefile! Use conf-* for configuration.
+
+SHELL=/bin/sh
+
+default: it
+
+addresses.0: \
+addresses.5
+ nroff -man addresses.5 > addresses.0
+
+alloc.a: \
+makelib alloc.o alloc_re.o
+ ./makelib alloc.a alloc.o alloc_re.o
+
+alloc.o: \
+compile alloc.c alloc.h error.h
+ ./compile alloc.c
+
+alloc_re.o: \
+compile alloc_re.c alloc.h byte.h
+ ./compile alloc_re.c
+
+auto-ccld.sh: \
+conf-cc conf-ld warn-auto.sh
+ ( cat warn-auto.sh; \
+ echo CC=\'`head -1 conf-cc`\'; \
+ echo LD=\'`head -1 conf-ld`\' \
+ ) > auto-ccld.sh
+
+auto-gid: \
+load auto-gid.o substdio.a error.a str.a fs.a
+ ./load auto-gid substdio.a error.a str.a fs.a
+
+auto-gid.o: \
+compile auto-gid.c subfd.h substdio.h substdio.h readwrite.h exit.h \
+scan.h fmt.h
+ ./compile auto-gid.c
+
+auto-int: \
+load auto-int.o substdio.a error.a str.a fs.a
+ ./load auto-int substdio.a error.a str.a fs.a
+
+auto-int.o: \
+compile auto-int.c substdio.h readwrite.h exit.h scan.h fmt.h
+ ./compile auto-int.c
+
+auto-int8: \
+load auto-int8.o substdio.a error.a str.a fs.a
+ ./load auto-int8 substdio.a error.a str.a fs.a
+
+auto-int8.o: \
+compile auto-int8.c substdio.h readwrite.h exit.h scan.h fmt.h
+ ./compile auto-int8.c
+
+auto-str: \
+load auto-str.o substdio.a error.a str.a
+ ./load auto-str substdio.a error.a str.a
+
+auto-str.o: \
+compile auto-str.c substdio.h readwrite.h exit.h
+ ./compile auto-str.c
+
+auto-uid: \
+load auto-uid.o substdio.a error.a str.a fs.a
+ ./load auto-uid substdio.a error.a str.a fs.a
+
+auto-uid.o: \
+compile auto-uid.c subfd.h substdio.h substdio.h readwrite.h exit.h \
+scan.h fmt.h
+ ./compile auto-uid.c
+
+auto_break.c: \
+auto-str conf-break
+ ./auto-str auto_break \
+ "`head -1 conf-break`" > auto_break.c
+
+auto_break.o: \
+compile auto_break.c
+ ./compile auto_break.c
+
+auto_patrn.c: \
+auto-int8 conf-patrn
+ ./auto-int8 auto_patrn `head -1 conf-patrn` > auto_patrn.c
+
+auto_patrn.o: \
+compile auto_patrn.c
+ ./compile auto_patrn.c
+
+auto_qmail.c: \
+auto-str conf-qmail
+ ./auto-str auto_qmail `head -1 conf-qmail` > auto_qmail.c
+
+auto_qmail.o: \
+compile auto_qmail.c
+ ./compile auto_qmail.c
+
+auto_spawn.c: \
+auto-int conf-spawn
+ ./auto-int auto_spawn `head -1 conf-spawn` > auto_spawn.c
+
+auto_spawn.o: \
+compile auto_spawn.c
+ ./compile auto_spawn.c
+
+auto_split.c: \
+auto-int conf-split
+ ./auto-int auto_split `head -1 conf-split` > auto_split.c
+
+auto_split.o: \
+compile auto_split.c
+ ./compile auto_split.c
+
+auto_uids.c: \
+auto-uid auto-gid conf-users conf-groups
+ ( ./auto-uid auto_uida `head -1 conf-users` \
+ &&./auto-uid auto_uidd `head -2 conf-users | tail -1` \
+ &&./auto-uid auto_uidl `head -3 conf-users | tail -1` \
+ &&./auto-uid auto_uido `head -4 conf-users | tail -1` \
+ &&./auto-uid auto_uidp `head -5 conf-users | tail -1` \
+ &&./auto-uid auto_uidq `head -6 conf-users | tail -1` \
+ &&./auto-uid auto_uidr `head -7 conf-users | tail -1` \
+ &&./auto-uid auto_uids `head -8 conf-users | tail -1` \
+ &&./auto-gid auto_gidq `head -1 conf-groups` \
+ &&./auto-gid auto_gidn `head -2 conf-groups | tail -1` \
+ ) > auto_uids.c.tmp && mv auto_uids.c.tmp auto_uids.c
+
+auto_uids.o: \
+compile auto_uids.c
+ ./compile auto_uids.c
+
+auto_usera.c: \
+auto-str conf-users
+ ./auto-str auto_usera `head -1 conf-users` > auto_usera.c
+
+auto_usera.o: \
+compile auto_usera.c
+ ./compile auto_usera.c
+
+binm1: \
+binm1.sh conf-qmail
+ cat binm1.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > binm1
+ chmod 755 binm1
+
+binm1+df: \
+binm1+df.sh conf-qmail
+ cat binm1+df.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > binm1+df
+ chmod 755 binm1+df
+
+binm2: \
+binm2.sh conf-qmail
+ cat binm2.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > binm2
+ chmod 755 binm2
+
+binm2+df: \
+binm2+df.sh conf-qmail
+ cat binm2+df.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > binm2+df
+ chmod 755 binm2+df
+
+binm3: \
+binm3.sh conf-qmail
+ cat binm3.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > binm3
+ chmod 755 binm3
+
+binm3+df: \
+binm3+df.sh conf-qmail
+ cat binm3+df.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > binm3+df
+ chmod 755 binm3+df
+
+bouncesaying: \
+load bouncesaying.o strerr.a error.a substdio.a str.a wait.a
+ ./load bouncesaying strerr.a error.a substdio.a str.a \
+ wait.a
+
+bouncesaying.0: \
+bouncesaying.1
+ nroff -man bouncesaying.1 > bouncesaying.0
+
+bouncesaying.o: \
+compile bouncesaying.c fork.h strerr.h error.h wait.h sig.h exit.h
+ ./compile bouncesaying.c
+
+byte_chr.o: \
+compile byte_chr.c byte.h
+ ./compile byte_chr.c
+
+byte_copy.o: \
+compile byte_copy.c byte.h
+ ./compile byte_copy.c
+
+byte_cr.o: \
+compile byte_cr.c byte.h
+ ./compile byte_cr.c
+
+byte_diff.o: \
+compile byte_diff.c byte.h
+ ./compile byte_diff.c
+
+byte_rchr.o: \
+compile byte_rchr.c byte.h
+ ./compile byte_rchr.c
+
+byte_zero.o: \
+compile byte_zero.c byte.h
+ ./compile byte_zero.c
+
+case.a: \
+makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \
+case_starts.o
+ ./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \
+ case_lowers.o case_starts.o
+
+case_diffb.o: \
+compile case_diffb.c case.h
+ ./compile case_diffb.c
+
+case_diffs.o: \
+compile case_diffs.c case.h
+ ./compile case_diffs.c
+
+case_lowerb.o: \
+compile case_lowerb.c case.h
+ ./compile case_lowerb.c
+
+case_lowers.o: \
+compile case_lowers.c case.h
+ ./compile case_lowers.c
+
+case_starts.o: \
+compile case_starts.c case.h
+ ./compile case_starts.c
+
+cdb.a: \
+makelib cdb_hash.o cdb_unpack.o cdb_seek.o
+ ./makelib cdb.a cdb_hash.o cdb_unpack.o cdb_seek.o
+
+cdb_hash.o: \
+compile cdb_hash.c cdb.h uint32.h
+ ./compile cdb_hash.c
+
+cdb_seek.o: \
+compile cdb_seek.c cdb.h uint32.h
+ ./compile cdb_seek.c
+
+cdb_unpack.o: \
+compile cdb_unpack.c cdb.h uint32.h
+ ./compile cdb_unpack.c
+
+cdbmake.a: \
+makelib cdbmake_pack.o cdbmake_hash.o cdbmake_add.o
+ ./makelib cdbmake.a cdbmake_pack.o cdbmake_hash.o \
+ cdbmake_add.o
+
+cdbmake_add.o: \
+compile cdbmake_add.c cdbmake.h alloc.h uint32.h
+ ./compile cdbmake_add.c
+
+cdbmake_hash.o: \
+compile cdbmake_hash.c cdbmake.h uint32.h
+ ./compile cdbmake_hash.c
+
+cdbmake_pack.o: \
+compile cdbmake_pack.c cdbmake.h uint32.h
+ ./compile cdbmake_pack.c
+
+cdbmss.o: \
+compile cdbmss.c readwrite.h seek.h alloc.h cdbmss.h cdbmake.h \
+uint32.h substdio.h
+ ./compile cdbmss.c
+
+check: \
+it man
+ ./instcheck
+
+chkshsgr: \
+load chkshsgr.o
+ ./load chkshsgr
+
+chkshsgr.o: \
+compile chkshsgr.c exit.h
+ ./compile chkshsgr.c
+
+chkspawn: \
+load chkspawn.o substdio.a error.a str.a fs.a auto_spawn.o
+ ./load chkspawn substdio.a error.a str.a fs.a auto_spawn.o
+
+chkspawn.o: \
+compile chkspawn.c substdio.h subfd.h substdio.h fmt.h select.h \
+exit.h auto_spawn.h
+ ./compile chkspawn.c
+
+clean: \
+TARGETS
+ rm -f `cat TARGETS`
+
+coe.o: \
+compile coe.c coe.h
+ ./compile coe.c
+
+commands.o: \
+compile commands.c commands.h substdio.h stralloc.h gen_alloc.h str.h \
+case.h
+ ./compile commands.c
+
+compile: \
+make-compile warn-auto.sh systype
+ ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \
+ compile
+ chmod 755 compile
+
+condredirect: \
+load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \
+substdio.a error.a str.a fs.a auto_qmail.o
+ ./load condredirect qmail.o strerr.a fd.a sig.a wait.a \
+ seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o
+
+condredirect.0: \
+condredirect.1
+ nroff -man condredirect.1 > condredirect.0
+
+condredirect.o: \
+compile condredirect.c sig.h readwrite.h exit.h env.h error.h fork.h \
+wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h
+ ./compile condredirect.c
+
+config: \
+warn-auto.sh config.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh config.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > config
+ chmod 755 config
+
+config-fast: \
+warn-auto.sh config-fast.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh config-fast.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > config-fast
+ chmod 755 config-fast
+
+constmap.o: \
+compile constmap.c constmap.h alloc.h case.h
+ ./compile constmap.c
+
+control.o: \
+compile control.c readwrite.h open.h getln.h stralloc.h gen_alloc.h \
+substdio.h error.h control.h alloc.h scan.h
+ ./compile control.c
+
+date822fmt.o: \
+compile date822fmt.c datetime.h fmt.h date822fmt.h
+ ./compile date822fmt.c
+
+datemail: \
+warn-auto.sh datemail.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh datemail.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > datemail
+ chmod 755 datemail
+
+datetime.a: \
+makelib datetime.o datetime_un.o
+ ./makelib datetime.a datetime.o datetime_un.o
+
+datetime.o: \
+compile datetime.c datetime.h
+ ./compile datetime.c
+
+datetime_un.o: \
+compile datetime_un.c datetime.h
+ ./compile datetime_un.c
+
+direntry.h: \
+compile trydrent.c direntry.h1 direntry.h2
+ ( ./compile trydrent.c >/dev/null 2>&1 \
+ && cat direntry.h2 || cat direntry.h1 ) > direntry.h
+ rm -f trydrent.o
+
+dns.lib: \
+tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \
+alloc.a error.a fs.a str.a
+ ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \
+ ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
+ -lresolv `cat socket.lib` ) >/dev/null 2>&1 \
+ && echo -lresolv || exit 0 ) > dns.lib
+ rm -f tryrsolv.o tryrsolv
+
+dns.o: \
+compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \
+stralloc.h gen_alloc.h dns.h case.h
+ ./compile dns.c
+
+dnscname: \
+load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a dns.lib socket.lib
+ ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
+ socket.lib`
+
+dnscname.o: \
+compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \
+gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h
+ ./compile dnscname.c
+
+dnsdoe.o: \
+compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h
+ ./compile dnsdoe.c
+
+dnsfq: \
+load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a dns.lib socket.lib
+ ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
+ socket.lib`
+
+dnsfq.o: \
+compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
+dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
+ ./compile dnsfq.c
+
+dnsip: \
+load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a dns.lib socket.lib
+ ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
+ socket.lib`
+
+dnsip.o: \
+compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
+dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
+ ./compile dnsip.c
+
+dnsmxip: \
+load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a dns.lib socket.lib
+ ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \
+ stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \
+ dns.lib` `cat socket.lib`
+
+dnsmxip.o: \
+compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \
+gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \
+now.h datetime.h exit.h
+ ./compile dnsmxip.c
+
+dnsptr: \
+load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a dns.lib socket.lib
+ ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
+ socket.lib`
+
+dnsptr.o: \
+compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
+str.h scan.h dns.h dnsdoe.h ip.h exit.h
+ ./compile dnsptr.c
+
+dot-qmail.0: \
+dot-qmail.5
+ nroff -man dot-qmail.5 > dot-qmail.0
+
+dot-qmail.5: \
+dot-qmail.9 conf-break conf-spawn
+ cat dot-qmail.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > dot-qmail.5
+
+elq: \
+warn-auto.sh elq.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh elq.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > elq
+ chmod 755 elq
+
+env.a: \
+makelib env.o envread.o
+ ./makelib env.a env.o envread.o
+
+env.o: \
+compile env.c str.h alloc.h env.h
+ ./compile env.c
+
+envelopes.0: \
+envelopes.5
+ nroff -man envelopes.5 > envelopes.0
+
+envread.o: \
+compile envread.c env.h str.h
+ ./compile envread.c
+
+error.a: \
+makelib error.o error_str.o error_temp.o
+ ./makelib error.a error.o error_str.o error_temp.o
+
+error.o: \
+compile error.c error.h
+ ./compile error.c
+
+error_str.o: \
+compile error_str.c error.h
+ ./compile error_str.c
+
+error_temp.o: \
+compile error_temp.c error.h
+ ./compile error_temp.c
+
+except: \
+load except.o strerr.a error.a substdio.a str.a wait.a
+ ./load except strerr.a error.a substdio.a str.a wait.a
+
+except.0: \
+except.1
+ nroff -man except.1 > except.0
+
+except.o: \
+compile except.c fork.h strerr.h wait.h error.h exit.h
+ ./compile except.c
+
+fd.a: \
+makelib fd_copy.o fd_move.o
+ ./makelib fd.a fd_copy.o fd_move.o
+
+fd_copy.o: \
+compile fd_copy.c fd.h
+ ./compile fd_copy.c
+
+fd_move.o: \
+compile fd_move.c fd.h
+ ./compile fd_move.c
+
+fifo.o: \
+compile fifo.c hasmkffo.h fifo.h
+ ./compile fifo.c
+
+find-systype: \
+find-systype.sh auto-ccld.sh
+ cat auto-ccld.sh find-systype.sh > find-systype
+ chmod 755 find-systype
+
+fmt_str.o: \
+compile fmt_str.c fmt.h
+ ./compile fmt_str.c
+
+fmt_strn.o: \
+compile fmt_strn.c fmt.h
+ ./compile fmt_strn.c
+
+fmt_uint.o: \
+compile fmt_uint.c fmt.h
+ ./compile fmt_uint.c
+
+fmt_uint0.o: \
+compile fmt_uint0.c fmt.h
+ ./compile fmt_uint0.c
+
+fmt_ulong.o: \
+compile fmt_ulong.c fmt.h
+ ./compile fmt_ulong.c
+
+fmtqfn.o: \
+compile fmtqfn.c fmtqfn.h fmt.h auto_split.h
+ ./compile fmtqfn.c
+
+forgeries.0: \
+forgeries.7
+ nroff -man forgeries.7 > forgeries.0
+
+fork.h: \
+compile load tryvfork.c fork.h1 fork.h2
+ ( ( ./compile tryvfork.c && ./load tryvfork ) >/dev/null \
+ 2>&1 \
+ && cat fork.h2 || cat fork.h1 ) > fork.h
+ rm -f tryvfork.o tryvfork
+
+forward: \
+load forward.o qmail.o strerr.a alloc.a fd.a wait.a sig.a env.a \
+substdio.a error.a str.a fs.a auto_qmail.o
+ ./load forward qmail.o strerr.a alloc.a fd.a wait.a sig.a \
+ env.a substdio.a error.a str.a fs.a auto_qmail.o
+
+forward.0: \
+forward.1
+ nroff -man forward.1 > forward.0
+
+forward.o: \
+compile forward.c sig.h readwrite.h exit.h env.h qmail.h substdio.h \
+strerr.h substdio.h fmt.h
+ ./compile forward.c
+
+fs.a: \
+makelib fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o fmt_ulong.o \
+scan_ulong.o scan_8long.o
+ ./makelib fs.a fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o \
+ fmt_ulong.o scan_ulong.o scan_8long.o
+
+getln.a: \
+makelib getln.o getln2.o
+ ./makelib getln.a getln.o getln2.o
+
+getln.o: \
+compile getln.c substdio.h byte.h stralloc.h gen_alloc.h getln.h
+ ./compile getln.c
+
+getln2.o: \
+compile getln2.c substdio.h stralloc.h gen_alloc.h byte.h getln.h
+ ./compile getln2.c
+
+getopt.a: \
+makelib subgetopt.o sgetopt.o
+ ./makelib getopt.a subgetopt.o sgetopt.o
+
+gfrom.o: \
+compile gfrom.c str.h gfrom.h
+ ./compile gfrom.c
+
+hasflock.h: \
+tryflock.c compile load
+ ( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \
+ 2>&1 \
+ && echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h
+ rm -f tryflock.o tryflock
+
+hasmkffo.h: \
+trymkffo.c compile load
+ ( ( ./compile trymkffo.c && ./load trymkffo ) >/dev/null \
+ 2>&1 \
+ && echo \#define HASMKFIFO 1 || exit 0 ) > hasmkffo.h
+ rm -f trymkffo.o trymkffo
+
+hasnpbg1.h: \
+trynpbg1.c compile load open.h open.a fifo.h fifo.o select.h
+ ( ( ./compile trynpbg1.c \
+ && ./load trynpbg1 fifo.o open.a && ./trynpbg1 ) \
+ >/dev/null 2>&1 \
+ && echo \#define HASNAMEDPIPEBUG1 1 || exit 0 ) > \
+ hasnpbg1.h
+ rm -f trynpbg1.o trynpbg1
+
+hassalen.h: \
+trysalen.c compile
+ ( ./compile trysalen.c >/dev/null 2>&1 \
+ && echo \#define HASSALEN 1 || exit 0 ) > hassalen.h
+ rm -f trysalen.o
+
+hassgact.h: \
+trysgact.c compile load
+ ( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \
+ 2>&1 \
+ && echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h
+ rm -f trysgact.o trysgact
+
+hassgprm.h: \
+trysgprm.c compile load
+ ( ( ./compile trysgprm.c && ./load trysgprm ) >/dev/null \
+ 2>&1 \
+ && echo \#define HASSIGPROCMASK 1 || exit 0 ) > hassgprm.h
+ rm -f trysgprm.o trysgprm
+
+hasshsgr.h: \
+chkshsgr warn-shsgr tryshsgr.c compile load
+ ./chkshsgr || ( cat warn-shsgr; exit 1 )
+ ( ( ./compile tryshsgr.c \
+ && ./load tryshsgr && ./tryshsgr ) >/dev/null 2>&1 \
+ && echo \#define HASSHORTSETGROUPS 1 || exit 0 ) > \
+ hasshsgr.h
+ rm -f tryshsgr.o tryshsgr
+
+haswaitp.h: \
+trywaitp.c compile load
+ ( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \
+ 2>&1 \
+ && echo \#define HASWAITPID 1 || exit 0 ) > haswaitp.h
+ rm -f trywaitp.o trywaitp
+
+headerbody.o: \
+compile headerbody.c stralloc.h gen_alloc.h substdio.h getln.h \
+hfield.h headerbody.h
+ ./compile headerbody.c
+
+hfield.o: \
+compile hfield.c hfield.h
+ ./compile hfield.c
+
+hier.o: \
+compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
+ ./compile hier.c
+
+home: \
+home.sh conf-qmail
+ cat home.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > home
+ chmod 755 home
+
+home+df: \
+home+df.sh conf-qmail
+ cat home+df.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > home+df
+ chmod 755 home+df
+
+hostname: \
+load hostname.o substdio.a error.a str.a dns.lib socket.lib
+ ./load hostname substdio.a error.a str.a `cat dns.lib` \
+ `cat socket.lib`
+
+hostname.o: \
+compile hostname.c substdio.h subfd.h substdio.h readwrite.h exit.h
+ ./compile hostname.c
+
+idedit: \
+load idedit.o strerr.a substdio.a error.a str.a fs.a wait.a open.a \
+seek.a
+ ./load idedit strerr.a substdio.a error.a str.a fs.a \
+ wait.a open.a seek.a
+
+idedit.o: \
+compile idedit.c readwrite.h exit.h scan.h fmt.h strerr.h open.h \
+seek.h fork.h
+ ./compile idedit.c
+
+install: \
+load install.o fifo.o hier.o auto_qmail.o auto_split.o auto_uids.o \
+strerr.a substdio.a open.a error.a str.a fs.a
+ ./load install fifo.o hier.o auto_qmail.o auto_split.o \
+ auto_uids.o strerr.a substdio.a open.a error.a str.a fs.a
+
+install-big: \
+load install-big.o fifo.o install.o auto_qmail.o auto_split.o \
+auto_uids.o strerr.a substdio.a open.a error.a str.a fs.a
+ ./load install-big fifo.o install.o auto_qmail.o \
+ auto_split.o auto_uids.o strerr.a substdio.a open.a error.a \
+ str.a fs.a
+
+install-big.o: \
+compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \
+fifo.h
+ ./compile install-big.c
+
+install.o: \
+compile install.c substdio.h strerr.h error.h open.h readwrite.h \
+exit.h
+ ./compile install.c
+
+instcheck: \
+load instcheck.o fifo.o hier.o auto_qmail.o auto_split.o auto_uids.o \
+strerr.a substdio.a error.a str.a fs.a
+ ./load instcheck fifo.o hier.o auto_qmail.o auto_split.o \
+ auto_uids.o strerr.a substdio.a error.a str.a fs.a
+
+instcheck.o: \
+compile instcheck.c strerr.h error.h readwrite.h exit.h
+ ./compile instcheck.c
+
+ip.o: \
+compile ip.c fmt.h scan.h ip.h
+ ./compile ip.c
+
+ipalloc.o: \
+compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \
+gen_alloc.h
+ ./compile ipalloc.c
+
+ipme.o: \
+compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \
+stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h
+ ./compile ipme.c
+
+ipmeprint: \
+load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \
+error.a str.a fs.a socket.lib
+ ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \
+ substdio.a error.a str.a fs.a `cat socket.lib`
+
+ipmeprint.o: \
+compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
+ipalloc.h ip.h gen_alloc.h exit.h
+ ./compile ipmeprint.c
+
+it: \
+qmail-local qmail-lspawn qmail-getpw qmail-remote qmail-rspawn \
+qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \
+predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
+qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
+qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
+qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
+dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
+forward preline condredirect bouncesaying except maildirmake \
+maildir2mbox maildirwatch qail elq pinq idedit install-big install \
+instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
+binm3 binm3+df
+
+load: \
+make-load warn-auto.sh systype
+ ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load
+ chmod 755 load
+
+lock.a: \
+makelib lock_ex.o lock_exnb.o lock_un.o
+ ./makelib lock.a lock_ex.o lock_exnb.o lock_un.o
+
+lock_ex.o: \
+compile lock_ex.c hasflock.h lock.h
+ ./compile lock_ex.c
+
+lock_exnb.o: \
+compile lock_exnb.c hasflock.h lock.h
+ ./compile lock_exnb.c
+
+lock_un.o: \
+compile lock_un.c hasflock.h lock.h
+ ./compile lock_un.c
+
+maildir.0: \
+maildir.5
+ nroff -man maildir.5 > maildir.0
+
+maildir.o: \
+compile maildir.c prioq.h datetime.h gen_alloc.h env.h stralloc.h \
+gen_alloc.h direntry.h datetime.h now.h datetime.h str.h maildir.h \
+strerr.h
+ ./compile maildir.c
+
+maildir2mbox: \
+load maildir2mbox.o maildir.o prioq.o now.o myctime.o gfrom.o lock.a \
+getln.a env.a open.a strerr.a stralloc.a alloc.a substdio.a error.a \
+str.a fs.a datetime.a
+ ./load maildir2mbox maildir.o prioq.o now.o myctime.o \
+ gfrom.o lock.a getln.a env.a open.a strerr.a stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a datetime.a
+
+maildir2mbox.0: \
+maildir2mbox.1
+ nroff -man maildir2mbox.1 > maildir2mbox.0
+
+maildir2mbox.o: \
+compile maildir2mbox.c readwrite.h prioq.h datetime.h gen_alloc.h \
+env.h stralloc.h gen_alloc.h subfd.h substdio.h substdio.h getln.h \
+error.h open.h lock.h gfrom.h str.h exit.h myctime.h maildir.h \
+strerr.h
+ ./compile maildir2mbox.c
+
+maildirmake: \
+load maildirmake.o strerr.a substdio.a error.a str.a
+ ./load maildirmake strerr.a substdio.a error.a str.a
+
+maildirmake.0: \
+maildirmake.1
+ nroff -man maildirmake.1 > maildirmake.0
+
+maildirmake.o: \
+compile maildirmake.c strerr.h exit.h
+ ./compile maildirmake.c
+
+maildirwatch: \
+load maildirwatch.o hfield.o headerbody.o maildir.o prioq.o now.o \
+getln.a env.a open.a strerr.a stralloc.a alloc.a substdio.a error.a \
+str.a
+ ./load maildirwatch hfield.o headerbody.o maildir.o \
+ prioq.o now.o getln.a env.a open.a strerr.a stralloc.a \
+ alloc.a substdio.a error.a str.a
+
+maildirwatch.0: \
+maildirwatch.1
+ nroff -man maildirwatch.1 > maildirwatch.0
+
+maildirwatch.o: \
+compile maildirwatch.c getln.h substdio.h subfd.h substdio.h prioq.h \
+datetime.h gen_alloc.h stralloc.h gen_alloc.h str.h exit.h hfield.h \
+readwrite.h open.h headerbody.h maildir.h strerr.h
+ ./compile maildirwatch.c
+
+mailsubj: \
+warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh mailsubj.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > mailsubj
+ chmod 755 mailsubj
+
+mailsubj.0: \
+mailsubj.1
+ nroff -man mailsubj.1 > mailsubj.0
+
+make-compile: \
+make-compile.sh auto-ccld.sh
+ cat auto-ccld.sh make-compile.sh > make-compile
+ chmod 755 make-compile
+
+make-load: \
+make-load.sh auto-ccld.sh
+ cat auto-ccld.sh make-load.sh > make-load
+ chmod 755 make-load
+
+make-makelib: \
+make-makelib.sh auto-ccld.sh
+ cat auto-ccld.sh make-makelib.sh > make-makelib
+ chmod 755 make-makelib
+
+makelib: \
+make-makelib warn-auto.sh systype
+ ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \
+ makelib
+ chmod 755 makelib
+
+man: \
+qmail-local.0 qmail-lspawn.0 qmail-getpw.0 qmail-remote.0 \
+qmail-rspawn.0 qmail-clean.0 qmail-send.0 qmail-start.0 splogger.0 \
+qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 \
+qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \
+qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \
+qmail-smtpd.0 tcp-env.0 qmail-newmrh.0 qreceipt.0 qbiff.0 forward.0 \
+preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \
+maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
+qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
+qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
+envelopes.0 forgeries.0
+
+mbox.0: \
+mbox.5
+ nroff -man mbox.5 > mbox.0
+
+myctime.o: \
+compile myctime.c datetime.h fmt.h myctime.h
+ ./compile myctime.c
+
+ndelay.a: \
+makelib ndelay.o ndelay_off.o
+ ./makelib ndelay.a ndelay.o ndelay_off.o
+
+ndelay.o: \
+compile ndelay.c ndelay.h
+ ./compile ndelay.c
+
+ndelay_off.o: \
+compile ndelay_off.c ndelay.h
+ ./compile ndelay_off.c
+
+newfield.o: \
+compile newfield.c fmt.h datetime.h stralloc.h gen_alloc.h \
+date822fmt.h newfield.h stralloc.h
+ ./compile newfield.c
+
+now.o: \
+compile now.c datetime.h now.h datetime.h
+ ./compile now.c
+
+open.a: \
+makelib open_append.o open_excl.o open_read.o open_trunc.o \
+open_write.o
+ ./makelib open.a open_append.o open_excl.o open_read.o \
+ open_trunc.o open_write.o
+
+open_append.o: \
+compile open_append.c open.h
+ ./compile open_append.c
+
+open_excl.o: \
+compile open_excl.c open.h
+ ./compile open_excl.c
+
+open_read.o: \
+compile open_read.c open.h
+ ./compile open_read.c
+
+open_trunc.o: \
+compile open_trunc.c open.h
+ ./compile open_trunc.c
+
+open_write.o: \
+compile open_write.c open.h
+ ./compile open_write.c
+
+pinq: \
+warn-auto.sh pinq.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh pinq.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > pinq
+ chmod 755 pinq
+
+predate: \
+load predate.o datetime.a strerr.a sig.a fd.a wait.a substdio.a \
+error.a str.a fs.a
+ ./load predate datetime.a strerr.a sig.a fd.a wait.a \
+ substdio.a error.a str.a fs.a
+
+predate.o: \
+compile predate.c datetime.h fork.h wait.h fd.h fmt.h strerr.h \
+substdio.h subfd.h substdio.h readwrite.h exit.h
+ ./compile predate.c
+
+preline: \
+load preline.o strerr.a fd.a wait.a sig.a env.a getopt.a substdio.a \
+error.a str.a
+ ./load preline strerr.a fd.a wait.a sig.a env.a getopt.a \
+ substdio.a error.a str.a
+
+preline.0: \
+preline.1
+ nroff -man preline.1 > preline.0
+
+preline.o: \
+compile preline.c fd.h sgetopt.h subgetopt.h readwrite.h strerr.h \
+substdio.h exit.h fork.h wait.h env.h sig.h error.h
+ ./compile preline.c
+
+prioq.o: \
+compile prioq.c alloc.h gen_allocdefs.h prioq.h datetime.h \
+gen_alloc.h
+ ./compile prioq.c
+
+proc: \
+proc.sh conf-qmail
+ cat proc.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > proc
+ chmod 755 proc
+
+proc+df: \
+proc+df.sh conf-qmail
+ cat proc+df.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ > proc+df
+ chmod 755 proc+df
+
+prot.o: \
+compile prot.c hasshsgr.h prot.h
+ ./compile prot.c
+
+qail: \
+warn-auto.sh qail.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh qail.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > qail
+ chmod 755 qail
+
+qbiff: \
+load qbiff.o headerbody.o hfield.o getln.a env.a open.a stralloc.a \
+alloc.a substdio.a error.a str.a
+ ./load qbiff headerbody.o hfield.o getln.a env.a open.a \
+ stralloc.a alloc.a substdio.a error.a str.a
+
+qbiff.0: \
+qbiff.1
+ nroff -man qbiff.1 > qbiff.0
+
+qbiff.o: \
+compile qbiff.c readwrite.h stralloc.h gen_alloc.h substdio.h subfd.h \
+substdio.h open.h byte.h str.h headerbody.h hfield.h env.h exit.h
+ ./compile qbiff.c
+
+qmail-clean: \
+load qmail-clean.o fmtqfn.o now.o getln.a sig.a stralloc.a alloc.a \
+substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
+ ./load qmail-clean fmtqfn.o now.o getln.a sig.a stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
+ auto_split.o
+
+qmail-clean.0: \
+qmail-clean.8
+ nroff -man qmail-clean.8 > qmail-clean.0
+
+qmail-clean.o: \
+compile qmail-clean.c readwrite.h sig.h now.h datetime.h str.h \
+direntry.h getln.h stralloc.h gen_alloc.h substdio.h subfd.h \
+substdio.h byte.h scan.h fmt.h error.h exit.h fmtqfn.h auto_qmail.h
+ ./compile qmail-clean.c
+
+qmail-command.0: \
+qmail-command.8
+ nroff -man qmail-command.8 > qmail-command.0
+
+qmail-control.0: \
+qmail-control.5
+ nroff -man qmail-control.5 > qmail-control.0
+
+qmail-control.5: \
+qmail-control.9 conf-break conf-spawn
+ cat qmail-control.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-control.5
+
+qmail-getpw: \
+load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \
+auto_usera.o
+ ./load qmail-getpw case.a substdio.a error.a str.a fs.a \
+ auto_break.o auto_usera.o
+
+qmail-getpw.0: \
+qmail-getpw.8
+ nroff -man qmail-getpw.8 > qmail-getpw.0
+
+qmail-getpw.8: \
+qmail-getpw.9 conf-break conf-spawn
+ cat qmail-getpw.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-getpw.8
+
+qmail-getpw.o: \
+compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \
+error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \
+qlx.h
+ ./compile qmail-getpw.c
+
+qmail-header.0: \
+qmail-header.5
+ nroff -man qmail-header.5 > qmail-header.0
+
+qmail-inject: \
+load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \
+control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \
+getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \
+substdio.a error.a str.a fs.a auto_qmail.o
+ ./load qmail-inject headerbody.o hfield.o newfield.o \
+ quote.o now.o control.o date822fmt.o constmap.o qmail.o \
+ case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \
+ token822.o env.a stralloc.a alloc.a substdio.a error.a \
+ str.a fs.a auto_qmail.o
+
+qmail-inject.0: \
+qmail-inject.8
+ nroff -man qmail-inject.8 > qmail-inject.0
+
+qmail-inject.o: \
+compile qmail-inject.c sig.h substdio.h stralloc.h gen_alloc.h \
+subfd.h substdio.h sgetopt.h subgetopt.h getln.h alloc.h str.h fmt.h \
+hfield.h token822.h gen_alloc.h control.h env.h gen_alloc.h \
+gen_allocdefs.h error.h qmail.h substdio.h now.h datetime.h exit.h \
+quote.h headerbody.h auto_qmail.h newfield.h stralloc.h constmap.h
+ ./compile qmail-inject.c
+
+qmail-limits.0: \
+qmail-limits.7
+ nroff -man qmail-limits.7 > qmail-limits.0
+
+qmail-limits.7: \
+qmail-limits.9 conf-break conf-spawn
+ cat qmail-limits.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-limits.7
+
+qmail-local: \
+load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \
+slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \
+wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
+fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib
+ ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \
+ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \
+ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \
+ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \
+ auto_patrn.o `cat socket.lib`
+
+qmail-local.0: \
+qmail-local.8
+ nroff -man qmail-local.8 > qmail-local.0
+
+qmail-local.o: \
+compile qmail-local.c readwrite.h sig.h env.h byte.h exit.h fork.h \
+open.h wait.h lock.h seek.h substdio.h getln.h strerr.h subfd.h \
+substdio.h sgetopt.h subgetopt.h alloc.h error.h stralloc.h \
+gen_alloc.h fmt.h str.h now.h datetime.h case.h quote.h qmail.h \
+substdio.h slurpclose.h myctime.h gfrom.h auto_patrn.h
+ ./compile qmail-local.c
+
+qmail-log.0: \
+qmail-log.5
+ nroff -man qmail-log.5 > qmail-log.0
+
+qmail-lspawn: \
+load qmail-lspawn.o spawn.o prot.o slurpclose.o coe.o sig.a wait.a \
+case.a cdb.a fd.a open.a stralloc.a alloc.a substdio.a error.a str.a \
+fs.a auto_qmail.o auto_uids.o auto_spawn.o
+ ./load qmail-lspawn spawn.o prot.o slurpclose.o coe.o \
+ sig.a wait.a case.a cdb.a fd.a open.a stralloc.a alloc.a \
+ substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \
+ auto_spawn.o
+
+qmail-lspawn.0: \
+qmail-lspawn.8
+ nroff -man qmail-lspawn.8 > qmail-lspawn.0
+
+qmail-lspawn.o: \
+compile qmail-lspawn.c fd.h wait.h prot.h substdio.h stralloc.h \
+gen_alloc.h scan.h exit.h fork.h error.h cdb.h uint32.h case.h \
+slurpclose.h auto_qmail.h auto_uids.h qlx.h
+ ./compile qmail-lspawn.c
+
+qmail-newmrh: \
+load qmail-newmrh.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
+stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
+ ./load qmail-newmrh cdbmss.o getln.a open.a cdbmake.a \
+ seek.a case.a stralloc.a alloc.a strerr.a substdio.a \
+ error.a str.a auto_qmail.o
+
+qmail-newmrh.0: \
+qmail-newmrh.8
+ nroff -man qmail-newmrh.8 > qmail-newmrh.0
+
+qmail-newmrh.8: \
+qmail-newmrh.9 conf-break conf-spawn
+ cat qmail-newmrh.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-newmrh.8
+
+qmail-newmrh.o: \
+compile qmail-newmrh.c strerr.h stralloc.h gen_alloc.h substdio.h \
+getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \
+uint32.h substdio.h
+ ./compile qmail-newmrh.c
+
+qmail-newu: \
+load qmail-newu.o cdbmss.o getln.a open.a seek.a cdbmake.a case.a \
+stralloc.a alloc.a substdio.a error.a str.a auto_qmail.o
+ ./load qmail-newu cdbmss.o getln.a open.a seek.a cdbmake.a \
+ case.a stralloc.a alloc.a substdio.a error.a str.a \
+ auto_qmail.o
+
+qmail-newu.0: \
+qmail-newu.8
+ nroff -man qmail-newu.8 > qmail-newu.0
+
+qmail-newu.8: \
+qmail-newu.9 conf-break conf-spawn
+ cat qmail-newu.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-newu.8
+
+qmail-newu.o: \
+compile qmail-newu.c stralloc.h gen_alloc.h subfd.h substdio.h \
+getln.h substdio.h cdbmss.h cdbmake.h uint32.h substdio.h exit.h \
+readwrite.h open.h error.h case.h auto_qmail.h
+ ./compile qmail-newu.c
+
+qmail-pop3d: \
+load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \
+maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \
+stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
+ ./load qmail-pop3d commands.o case.a timeoutread.o \
+ timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \
+ open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \
+ fs.a `cat socket.lib`
+
+qmail-pop3d.0: \
+qmail-pop3d.8
+ nroff -man qmail-pop3d.8 > qmail-pop3d.0
+
+qmail-pop3d.o: \
+compile qmail-pop3d.c commands.h sig.h getln.h stralloc.h gen_alloc.h \
+substdio.h alloc.h open.h prioq.h datetime.h gen_alloc.h scan.h fmt.h \
+str.h exit.h maildir.h strerr.h readwrite.h timeoutread.h \
+timeoutwrite.h
+ ./compile qmail-pop3d.c
+
+qmail-popup: \
+load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \
+case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \
+fs.a socket.lib
+ ./load qmail-popup commands.o timeoutread.o timeoutwrite.o \
+ now.o case.a fd.a sig.a wait.a stralloc.a alloc.a \
+ substdio.a error.a str.a fs.a `cat socket.lib`
+
+qmail-popup.0: \
+qmail-popup.8
+ nroff -man qmail-popup.8 > qmail-popup.0
+
+qmail-popup.o: \
+compile qmail-popup.c commands.h fd.h sig.h stralloc.h gen_alloc.h \
+substdio.h alloc.h wait.h str.h byte.h now.h datetime.h fmt.h exit.h \
+readwrite.h timeoutread.h timeoutwrite.h
+ ./compile qmail-popup.c
+
+qmail-pw2u: \
+load qmail-pw2u.o constmap.o control.o open.a getln.a case.a getopt.a \
+stralloc.a alloc.a substdio.a error.a str.a fs.a auto_usera.o \
+auto_break.o auto_qmail.o
+ ./load qmail-pw2u constmap.o control.o open.a getln.a \
+ case.a getopt.a stralloc.a alloc.a substdio.a error.a str.a \
+ fs.a auto_usera.o auto_break.o auto_qmail.o
+
+qmail-pw2u.0: \
+qmail-pw2u.8
+ nroff -man qmail-pw2u.8 > qmail-pw2u.0
+
+qmail-pw2u.8: \
+qmail-pw2u.9 conf-break conf-spawn
+ cat qmail-pw2u.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-pw2u.8
+
+qmail-pw2u.o: \
+compile qmail-pw2u.c substdio.h readwrite.h subfd.h substdio.h \
+sgetopt.h subgetopt.h control.h constmap.h stralloc.h gen_alloc.h \
+fmt.h str.h scan.h open.h error.h getln.h auto_break.h auto_qmail.h \
+auto_usera.h
+ ./compile qmail-pw2u.c
+
+qmail-qmqpc: \
+load qmail-qmqpc.o slurpclose.o timeoutread.o timeoutwrite.o \
+timeoutconn.o ip.o control.o auto_qmail.o sig.a ndelay.a open.a \
+getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a socket.lib
+ ./load qmail-qmqpc slurpclose.o timeoutread.o \
+ timeoutwrite.o timeoutconn.o ip.o control.o auto_qmail.o \
+ sig.a ndelay.a open.a getln.a substdio.a stralloc.a alloc.a \
+ error.a str.a fs.a `cat socket.lib`
+
+qmail-qmqpc.0: \
+qmail-qmqpc.8
+ nroff -man qmail-qmqpc.8 > qmail-qmqpc.0
+
+qmail-qmqpc.o: \
+compile qmail-qmqpc.c substdio.h getln.h readwrite.h exit.h \
+stralloc.h gen_alloc.h slurpclose.h error.h sig.h ip.h timeoutconn.h \
+timeoutread.h timeoutwrite.h auto_qmail.h control.h fmt.h
+ ./compile qmail-qmqpc.c
+
+qmail-qmqpd: \
+load qmail-qmqpd.o received.o now.o date822fmt.o qmail.o auto_qmail.o \
+env.a substdio.a sig.a error.a wait.a fd.a str.a datetime.a fs.a
+ ./load qmail-qmqpd received.o now.o date822fmt.o qmail.o \
+ auto_qmail.o env.a substdio.a sig.a error.a wait.a fd.a \
+ str.a datetime.a fs.a
+
+qmail-qmqpd.0: \
+qmail-qmqpd.8
+ nroff -man qmail-qmqpd.8 > qmail-qmqpd.0
+
+qmail-qmqpd.o: \
+compile qmail-qmqpd.c auto_qmail.h qmail.h substdio.h received.h \
+sig.h substdio.h readwrite.h exit.h now.h datetime.h fmt.h env.h
+ ./compile qmail-qmqpd.c
+
+qmail-qmtpd: \
+load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \
+date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a open.a \
+getln.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a \
+str.a fs.a auto_qmail.o
+ ./load qmail-qmtpd rcpthosts.o control.o constmap.o \
+ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
+ datetime.a open.a getln.a sig.a case.a env.a stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a auto_qmail.o
+
+qmail-qmtpd.0: \
+qmail-qmtpd.8
+ nroff -man qmail-qmtpd.8 > qmail-qmtpd.0
+
+qmail-qmtpd.o: \
+compile qmail-qmtpd.c stralloc.h gen_alloc.h substdio.h qmail.h \
+substdio.h now.h datetime.h str.h fmt.h env.h sig.h rcpthosts.h \
+auto_qmail.h readwrite.h control.h received.h
+ ./compile qmail-qmtpd.c
+
+qmail-qread: \
+load qmail-qread.o fmtqfn.o readsubdir.o date822fmt.o datetime.a \
+open.a getln.a stralloc.a alloc.a substdio.a error.a str.a fs.a \
+auto_qmail.o auto_split.o
+ ./load qmail-qread fmtqfn.o readsubdir.o date822fmt.o \
+ datetime.a open.a getln.a stralloc.a alloc.a substdio.a \
+ error.a str.a fs.a auto_qmail.o auto_split.o
+
+qmail-qread.0: \
+qmail-qread.8
+ nroff -man qmail-qread.8 > qmail-qread.0
+
+qmail-qread.o: \
+compile qmail-qread.c stralloc.h gen_alloc.h substdio.h subfd.h \
+substdio.h fmt.h str.h getln.h fmtqfn.h readsubdir.h direntry.h \
+auto_qmail.h open.h datetime.h date822fmt.h readwrite.h error.h \
+exit.h
+ ./compile qmail-qread.c
+
+qmail-qstat: \
+warn-auto.sh qmail-qstat.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh qmail-qstat.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > qmail-qstat
+ chmod 755 qmail-qstat
+
+qmail-qstat.0: \
+qmail-qstat.8
+ nroff -man qmail-qstat.8 > qmail-qstat.0
+
+qmail-queue: \
+load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \
+datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
+str.a fs.a auto_qmail.o auto_split.o auto_uids.o
+ ./load qmail-queue triggerpull.o fmtqfn.o now.o \
+ date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
+ alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
+ auto_split.o auto_uids.o
+
+qmail-queue.0: \
+qmail-queue.8
+ nroff -man qmail-queue.8 > qmail-queue.0
+
+qmail-queue.o: \
+compile qmail-queue.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
+alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
+ ./compile qmail-queue.c
+
+qmail-remote: \
+load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
+timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \
+ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
+substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
+ ./load qmail-remote control.o constmap.o timeoutread.o \
+ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
+ ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
+ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
+ str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
+
+qmail-remote.0: \
+qmail-remote.8
+ nroff -man qmail-remote.8 > qmail-remote.0
+
+qmail-remote.o: \
+compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \
+subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \
+alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \
+gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \
+tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h
+ ./compile qmail-remote.c
+
+qmail-rspawn: \
+load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \
+seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \
+auto_qmail.o auto_uids.o auto_spawn.o
+ ./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \
+ sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \
+ substdio.a error.a str.a auto_qmail.o auto_uids.o \
+ auto_spawn.o
+
+qmail-rspawn.0: \
+qmail-rspawn.8
+ nroff -man qmail-rspawn.8 > qmail-rspawn.0
+
+qmail-rspawn.o: \
+compile qmail-rspawn.c fd.h wait.h substdio.h exit.h fork.h error.h \
+tcpto.h
+ ./compile qmail-rspawn.c
+
+qmail-send: \
+load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \
+trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
+datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
+lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
+auto_split.o env.a
+ ./load qmail-send qsutil.o control.o constmap.o newfield.o \
+ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
+ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
+ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
+ substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a
+
+qmail-send.0: \
+qmail-send.8
+ nroff -man qmail-send.8 > qmail-send.0
+
+qmail-send.8: \
+qmail-send.9 conf-break conf-spawn
+ cat qmail-send.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-send.8
+
+qmail-send.o: \
+compile qmail-send.c readwrite.h sig.h direntry.h control.h select.h \
+open.h seek.h exit.h lock.h ndelay.h now.h datetime.h getln.h \
+substdio.h alloc.h error.h stralloc.h gen_alloc.h str.h byte.h fmt.h \
+scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \
+qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \
+fmtqfn.h readsubdir.h direntry.h
+ ./compile qmail-send.c
+
+qmail-showctl: \
+load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \
+alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_break.o \
+auto_patrn.o auto_spawn.o auto_split.o
+ ./load qmail-showctl auto_uids.o control.o open.a getln.a \
+ stralloc.a alloc.a substdio.a error.a str.a fs.a \
+ auto_qmail.o auto_break.o auto_patrn.o auto_spawn.o \
+ auto_split.o
+
+qmail-showctl.0: \
+qmail-showctl.8
+ nroff -man qmail-showctl.8 > qmail-showctl.0
+
+qmail-showctl.o: \
+compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \
+str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \
+auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \
+auto_split.h
+ ./compile qmail-showctl.c
+
+qmail-smtpd: \
+load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
+timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
+date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
+open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
+fs.a auto_qmail.o socket.lib
+ ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
+ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
+ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
+ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
+ socket.lib`
+
+qmail-smtpd.0: \
+qmail-smtpd.8
+ nroff -man qmail-smtpd.8 > qmail-smtpd.0
+
+qmail-smtpd.o: \
+compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
+substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
+error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
+substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
+ ./compile qmail-smtpd.c
+
+qmail-start: \
+load qmail-start.o prot.o fd.a auto_uids.o
+ ./load qmail-start prot.o fd.a auto_uids.o
+
+qmail-start.0: \
+qmail-start.8
+ nroff -man qmail-start.8 > qmail-start.0
+
+qmail-start.8: \
+qmail-start.9 conf-break conf-spawn
+ cat qmail-start.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-start.8
+
+qmail-start.o: \
+compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h
+ ./compile qmail-start.c
+
+qmail-tcpok: \
+load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \
+auto_qmail.o
+ ./load qmail-tcpok open.a lock.a strerr.a substdio.a \
+ error.a str.a auto_qmail.o
+
+qmail-tcpok.0: \
+qmail-tcpok.8
+ nroff -man qmail-tcpok.8 > qmail-tcpok.0
+
+qmail-tcpok.o: \
+compile qmail-tcpok.c strerr.h substdio.h lock.h open.h readwrite.h \
+auto_qmail.h exit.h
+ ./compile qmail-tcpok.c
+
+qmail-tcpto: \
+load qmail-tcpto.o ip.o now.o open.a lock.a substdio.a error.a str.a \
+fs.a auto_qmail.o
+ ./load qmail-tcpto ip.o now.o open.a lock.a substdio.a \
+ error.a str.a fs.a auto_qmail.o
+
+qmail-tcpto.0: \
+qmail-tcpto.8
+ nroff -man qmail-tcpto.8 > qmail-tcpto.0
+
+qmail-tcpto.o: \
+compile qmail-tcpto.c substdio.h subfd.h substdio.h auto_qmail.h \
+fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h
+ ./compile qmail-tcpto.c
+
+qmail-upq: \
+warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split
+ cat warn-auto.sh qmail-upq.sh \
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPLIT}"`head -1 conf-split`"}g \
+ > qmail-upq
+ chmod 755 qmail-upq
+
+qmail-users.0: \
+qmail-users.5
+ nroff -man qmail-users.5 > qmail-users.0
+
+qmail-users.5: \
+qmail-users.9 conf-break conf-spawn
+ cat qmail-users.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ | sed s}BREAK}"`head -1 conf-break`"}g \
+ | sed s}SPAWN}"`head -1 conf-spawn`"}g \
+ > qmail-users.5
+
+qmail.0: \
+qmail.7
+ nroff -man qmail.7 > qmail.0
+
+qmail.o: \
+compile qmail.c substdio.h readwrite.h wait.h exit.h fork.h fd.h \
+qmail.h substdio.h auto_qmail.h
+ ./compile qmail.c
+
+qreceipt: \
+load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \
+getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \
+str.a auto_qmail.o
+ ./load qreceipt headerbody.o hfield.o quote.o token822.o \
+ qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \
+ substdio.a error.a str.a auto_qmail.o
+
+qreceipt.0: \
+qreceipt.1
+ nroff -man qreceipt.1 > qreceipt.0
+
+qreceipt.o: \
+compile qreceipt.c sig.h env.h substdio.h stralloc.h gen_alloc.h \
+subfd.h substdio.h getln.h alloc.h str.h hfield.h token822.h \
+gen_alloc.h error.h gen_alloc.h gen_allocdefs.h headerbody.h exit.h \
+open.h quote.h qmail.h substdio.h
+ ./compile qreceipt.c
+
+qsmhook: \
+load qsmhook.o sig.a case.a fd.a wait.a getopt.a env.a stralloc.a \
+alloc.a substdio.a error.a str.a
+ ./load qsmhook sig.a case.a fd.a wait.a getopt.a env.a \
+ stralloc.a alloc.a substdio.a error.a str.a
+
+qsmhook.o: \
+compile qsmhook.c fd.h stralloc.h gen_alloc.h readwrite.h sgetopt.h \
+subgetopt.h wait.h env.h byte.h str.h alloc.h exit.h fork.h case.h \
+subfd.h substdio.h error.h substdio.h sig.h
+ ./compile qsmhook.c
+
+qsutil.o: \
+compile qsutil.c stralloc.h gen_alloc.h readwrite.h substdio.h \
+qsutil.h
+ ./compile qsutil.c
+
+quote.o: \
+compile quote.c stralloc.h gen_alloc.h str.h quote.h
+ ./compile quote.c
+
+rcpthosts.o: \
+compile rcpthosts.c cdb.h uint32.h byte.h open.h error.h control.h \
+constmap.h stralloc.h gen_alloc.h rcpthosts.h
+ ./compile rcpthosts.c
+
+readsubdir.o: \
+compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \
+auto_split.h
+ ./compile readsubdir.c
+
+received.o: \
+compile received.c fmt.h qmail.h substdio.h now.h datetime.h \
+datetime.h date822fmt.h received.h
+ ./compile received.c
+
+remoteinfo.o: \
+compile remoteinfo.c byte.h substdio.h ip.h fmt.h timeoutconn.h \
+timeoutread.h timeoutwrite.h remoteinfo.h
+ ./compile remoteinfo.c
+
+scan_8long.o: \
+compile scan_8long.c scan.h
+ ./compile scan_8long.c
+
+scan_ulong.o: \
+compile scan_ulong.c scan.h
+ ./compile scan_ulong.c
+
+seek.a: \
+makelib seek_cur.o seek_end.o seek_set.o seek_trunc.o
+ ./makelib seek.a seek_cur.o seek_end.o seek_set.o \
+ seek_trunc.o
+
+seek_cur.o: \
+compile seek_cur.c seek.h
+ ./compile seek_cur.c
+
+seek_end.o: \
+compile seek_end.c seek.h
+ ./compile seek_end.c
+
+seek_set.o: \
+compile seek_set.c seek.h
+ ./compile seek_set.c
+
+seek_trunc.o: \
+compile seek_trunc.c seek.h
+ ./compile seek_trunc.c
+
+select.h: \
+compile trysysel.c select.h1 select.h2
+ ( ./compile trysysel.c >/dev/null 2>&1 \
+ && cat select.h2 || cat select.h1 ) > select.h
+ rm -f trysysel.o trysysel
+
+sendmail: \
+load sendmail.o env.a getopt.a alloc.a substdio.a error.a str.a \
+auto_qmail.o
+ ./load sendmail env.a getopt.a alloc.a substdio.a error.a \
+ str.a auto_qmail.o
+
+sendmail.o: \
+compile sendmail.c sgetopt.h subgetopt.h substdio.h subfd.h \
+substdio.h alloc.h auto_qmail.h exit.h env.h str.h
+ ./compile sendmail.c
+
+setup: \
+it man
+ ./install
+
+sgetopt.o: \
+compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \
+subgetopt.h
+ ./compile sgetopt.c
+
+shar: \
+FILES BLURB BLURB2 BLURB3 BLURB4 README FAQ INSTALL INSTALL.alias \
+INSTALL.ctl INSTALL.ids INSTALL.maildir INSTALL.mbox INSTALL.vsm \
+REMOVE.sendmail REMOVE.binmail TEST.deliver TEST.receive UPGRADE \
+THOUGHTS TODO THANKS CHANGES SECURITY INTERNALS SENDMAIL \
+PIC.local2alias PIC.local2ext PIC.local2local PIC.local2rem \
+PIC.local2virt PIC.nullclient PIC.relaybad PIC.relaygood \
+PIC.rem2local FILES VERSION SYSDEPS TARGETS Makefile BIN.README \
+BIN.Makefile BIN.setup idedit.c conf-break auto_break.h conf-spawn \
+auto_spawn.h chkspawn.c conf-split auto_split.h conf-patrn \
+auto_patrn.h conf-users conf-groups auto_uids.h auto_usera.h extra.h \
+addresses.5 except.1 bouncesaying.1 condredirect.1 dot-qmail.9 \
+envelopes.5 forgeries.7 forward.1 maildir2mbox.1 maildirmake.1 \
+maildirwatch.1 mailsubj.1 mbox.5 preline.1 qbiff.1 qmail-clean.8 \
+qmail-command.8 qmail-control.9 qmail-getpw.9 qmail-header.5 \
+qmail-inject.8 qmail-limits.9 qmail-local.8 qmail-log.5 \
+qmail-lspawn.8 qmail-newmrh.9 qmail-newu.9 qmail-pop3d.8 \
+qmail-popup.8 qmail-pw2u.9 qmail-qmqpc.8 qmail-qmqpd.8 qmail-qmtpd.8 \
+qmail-qread.8 qmail-qstat.8 qmail-queue.8 qmail-remote.8 \
+qmail-rspawn.8 qmail-send.9 qmail-showctl.8 qmail-smtpd.8 \
+qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-users.9 qmail.7 \
+qreceipt.1 splogger.8 tcp-env.1 config.sh config-fast.sh \
+qmail-clean.c qmail-getpw.c qmail-inject.c qmail-local.c \
+qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \
+qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
+qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
+qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
+qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
+dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
+sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
+except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \
+maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \
+datemail.sh mailsubj.sh qlx.h rcpthosts.h rcpthosts.c commands.h \
+commands.c dnsdoe.h dnsdoe.c fmtqfn.h fmtqfn.c gfrom.h gfrom.c \
+myctime.h myctime.c newfield.h newfield.c qsutil.h qsutil.c \
+readsubdir.h readsubdir.c received.h received.c tcpto.h tcpto.c \
+tcpto_clean.c trigger.h trigger.c triggerpull.h triggerpull.c \
+trynpbg1.c trysyslog.c conf-cc 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 make-compile.sh make-load.sh \
+make-makelib.sh trycpp.c warn-auto.sh auto-str.c auto-int.c \
+auto-int8.c auto-gid.c auto-uid.c hier.c install.c instcheck.c \
+install-big.c alloc.3 alloc.h alloc.c alloc_re.c case.3 case.h \
+case_diffb.c case_diffs.c case_lowerb.c case_lowers.c case_starts.c \
+cdb.3 cdb.h cdb_hash.c cdb_seek.c cdb_unpack.c cdbmake.h \
+cdbmake_add.c cdbmake_hash.c cdbmake_pack.c cdbmss.h cdbmss.c coe.3 \
+coe.h coe.c fd.h fd_copy.3 fd_copy.c fd_move.3 fd_move.c fifo_make.3 \
+fifo.h fifo.c trymkffo.c fork.h1 fork.h2 tryvfork.c now.3 now.h now.c \
+open.h open_append.c open_excl.c open_read.c open_trunc.c \
+open_write.c seek.h seek_cur.c seek_end.c seek_set.c seek_trunc.c \
+conf-qmail auto_qmail.h qmail.h qmail.c gen_alloc.h gen_allocdefs.h \
+stralloc.3 stralloc.h stralloc_eady.c stralloc_pend.c stralloc_copy.c \
+stralloc_opyb.c stralloc_opys.c stralloc_cat.c stralloc_catb.c \
+stralloc_cats.c stralloc_arts.c strerr.h strerr_sys.c strerr_die.c \
+substdio.h substdio.c substdi.c substdo.c substdio_copy.c subfd.h \
+subfderr.c subfdouts.c subfdout.c subfdins.c subfdin.c readwrite.h \
+exit.h timeoutconn.h timeoutconn.c timeoutread.h timeoutread.c \
+timeoutwrite.h timeoutwrite.c remoteinfo.h remoteinfo.c uint32.h1 \
+uint32.h2 tryulong32.c wait.3 wait.h wait_pid.c wait_nohang.c \
+trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \
+sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \
+trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \
+byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \
+str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \
+str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \
+getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \
+subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \
+error.h error.c error_str.c error_temp.c fmt.h fmt_str.c fmt_strn.c \
+fmt_uint.c fmt_uint0.c fmt_ulong.c scan.h scan_ulong.c scan_8long.c \
+slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \
+headerbody.h headerbody.c token822.h token822.c control.h control.c \
+datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
+date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
+ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
+ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
+prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
+maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
+ shar -m `cat FILES` > shar
+ chmod 400 shar
+
+sig.a: \
+makelib sig_alarm.o sig_block.o sig_catch.o sig_pause.o sig_pipe.o \
+sig_child.o sig_hup.o sig_term.o sig_bug.o sig_misc.o
+ ./makelib sig.a sig_alarm.o sig_block.o sig_catch.o \
+ sig_pause.o sig_pipe.o sig_child.o sig_hup.o sig_term.o \
+ sig_bug.o sig_misc.o
+
+sig_alarm.o: \
+compile sig_alarm.c sig.h
+ ./compile sig_alarm.c
+
+sig_block.o: \
+compile sig_block.c sig.h hassgprm.h
+ ./compile sig_block.c
+
+sig_bug.o: \
+compile sig_bug.c sig.h
+ ./compile sig_bug.c
+
+sig_catch.o: \
+compile sig_catch.c sig.h hassgact.h
+ ./compile sig_catch.c
+
+sig_child.o: \
+compile sig_child.c sig.h
+ ./compile sig_child.c
+
+sig_hup.o: \
+compile sig_hup.c sig.h
+ ./compile sig_hup.c
+
+sig_misc.o: \
+compile sig_misc.c sig.h
+ ./compile sig_misc.c
+
+sig_pause.o: \
+compile sig_pause.c sig.h hassgprm.h
+ ./compile sig_pause.c
+
+sig_pipe.o: \
+compile sig_pipe.c sig.h
+ ./compile sig_pipe.c
+
+sig_term.o: \
+compile sig_term.c sig.h
+ ./compile sig_term.c
+
+slurpclose.o: \
+compile slurpclose.c stralloc.h gen_alloc.h readwrite.h slurpclose.h \
+error.h
+ ./compile slurpclose.c
+
+socket.lib: \
+trylsock.c compile load
+ ( ( ./compile trylsock.c && \
+ ./load trylsock -lsocket -lnsl ) >/dev/null 2>&1 \
+ && echo -lsocket -lnsl || exit 0 ) > socket.lib
+ rm -f trylsock.o trylsock
+
+spawn.o: \
+compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \
+stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \
+auto_qmail.h auto_uids.h auto_spawn.h
+ ./chkspawn
+ ./compile spawn.c
+
+splogger: \
+load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib
+ ./load splogger substdio.a error.a str.a fs.a `cat \
+ syslog.lib` `cat socket.lib`
+
+splogger.0: \
+splogger.8
+ nroff -man splogger.8 > splogger.0
+
+splogger.o: \
+compile splogger.c error.h substdio.h subfd.h substdio.h exit.h str.h \
+scan.h fmt.h
+ ./compile splogger.c
+
+str.a: \
+makelib 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
+ ./makelib str.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_chr.o: \
+compile str_chr.c str.h
+ ./compile str_chr.c
+
+str_cpy.o: \
+compile str_cpy.c str.h
+ ./compile str_cpy.c
+
+str_diff.o: \
+compile str_diff.c str.h
+ ./compile str_diff.c
+
+str_diffn.o: \
+compile str_diffn.c str.h
+ ./compile str_diffn.c
+
+str_len.o: \
+compile str_len.c str.h
+ ./compile str_len.c
+
+str_rchr.o: \
+compile str_rchr.c str.h
+ ./compile str_rchr.c
+
+str_start.o: \
+compile str_start.c str.h
+ ./compile str_start.c
+
+stralloc.a: \
+makelib stralloc_eady.o stralloc_pend.o stralloc_copy.o \
+stralloc_opys.o stralloc_opyb.o stralloc_cat.o stralloc_cats.o \
+stralloc_catb.o stralloc_arts.o
+ ./makelib stralloc.a stralloc_eady.o stralloc_pend.o \
+ stralloc_copy.o stralloc_opys.o stralloc_opyb.o \
+ stralloc_cat.o stralloc_cats.o stralloc_catb.o \
+ stralloc_arts.o
+
+stralloc_arts.o: \
+compile stralloc_arts.c byte.h str.h stralloc.h gen_alloc.h
+ ./compile stralloc_arts.c
+
+stralloc_cat.o: \
+compile stralloc_cat.c byte.h stralloc.h gen_alloc.h
+ ./compile stralloc_cat.c
+
+stralloc_catb.o: \
+compile stralloc_catb.c stralloc.h gen_alloc.h byte.h
+ ./compile stralloc_catb.c
+
+stralloc_cats.o: \
+compile stralloc_cats.c byte.h str.h stralloc.h gen_alloc.h
+ ./compile stralloc_cats.c
+
+stralloc_copy.o: \
+compile stralloc_copy.c byte.h stralloc.h gen_alloc.h
+ ./compile stralloc_copy.c
+
+stralloc_eady.o: \
+compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \
+gen_allocdefs.h
+ ./compile stralloc_eady.c
+
+stralloc_opyb.o: \
+compile stralloc_opyb.c stralloc.h gen_alloc.h byte.h
+ ./compile stralloc_opyb.c
+
+stralloc_opys.o: \
+compile stralloc_opys.c byte.h str.h stralloc.h gen_alloc.h
+ ./compile stralloc_opys.c
+
+stralloc_pend.o: \
+compile stralloc_pend.c alloc.h stralloc.h gen_alloc.h \
+gen_allocdefs.h
+ ./compile stralloc_pend.c
+
+strerr.a: \
+makelib strerr_sys.o strerr_die.o
+ ./makelib strerr.a strerr_sys.o strerr_die.o
+
+strerr_die.o: \
+compile strerr_die.c substdio.h subfd.h substdio.h exit.h strerr.h
+ ./compile strerr_die.c
+
+strerr_sys.o: \
+compile strerr_sys.c error.h strerr.h
+ ./compile strerr_sys.c
+
+subfderr.o: \
+compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
+ ./compile subfderr.c
+
+subfdin.o: \
+compile subfdin.c readwrite.h substdio.h subfd.h substdio.h
+ ./compile subfdin.c
+
+subfdins.o: \
+compile subfdins.c readwrite.h substdio.h subfd.h substdio.h
+ ./compile subfdins.c
+
+subfdout.o: \
+compile subfdout.c readwrite.h substdio.h subfd.h substdio.h
+ ./compile subfdout.c
+
+subfdouts.o: \
+compile subfdouts.c readwrite.h substdio.h subfd.h substdio.h
+ ./compile subfdouts.c
+
+subgetopt.o: \
+compile subgetopt.c subgetopt.h
+ ./compile subgetopt.c
+
+substdi.o: \
+compile substdi.c substdio.h byte.h error.h
+ ./compile substdi.c
+
+substdio.a: \
+makelib substdio.o substdi.o substdo.o subfderr.o subfdout.o \
+subfdouts.o subfdin.o subfdins.o substdio_copy.o
+ ./makelib substdio.a substdio.o substdi.o substdo.o \
+ subfderr.o subfdout.o subfdouts.o subfdin.o subfdins.o \
+ substdio_copy.o
+
+substdio.o: \
+compile substdio.c substdio.h
+ ./compile substdio.c
+
+substdio_copy.o: \
+compile substdio_copy.c substdio.h
+ ./compile substdio_copy.c
+
+substdo.o: \
+compile substdo.c substdio.h str.h byte.h error.h
+ ./compile substdo.c
+
+syslog.lib: \
+trysyslog.c compile load
+ ( ( ./compile trysyslog.c && \
+ ./load trysyslog -lgen ) >/dev/null 2>&1 \
+ && echo -lgen || exit 0 ) > syslog.lib
+ rm -f trysyslog.o trysyslog
+
+systype: \
+find-systype trycpp.c
+ ./find-systype > systype
+
+tcp-env: \
+load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
+timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \
+stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
+ ./load tcp-env dns.o remoteinfo.o timeoutread.o \
+ timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \
+ sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
+ str.a fs.a `cat dns.lib` `cat socket.lib`
+
+tcp-env.0: \
+tcp-env.1
+ nroff -man tcp-env.1 > tcp-env.0
+
+tcp-env.o: \
+compile tcp-env.c sig.h stralloc.h gen_alloc.h str.h env.h fmt.h \
+scan.h subgetopt.h ip.h dns.h byte.h remoteinfo.h exit.h case.h
+ ./compile tcp-env.c
+
+tcp-environ.0: \
+tcp-environ.5
+ nroff -man tcp-environ.5 > tcp-environ.0
+
+tcpto.o: \
+compile tcpto.c tcpto.h open.h lock.h seek.h now.h datetime.h ip.h \
+byte.h datetime.h readwrite.h
+ ./compile tcpto.c
+
+tcpto_clean.o: \
+compile tcpto_clean.c tcpto.h open.h substdio.h readwrite.h
+ ./compile tcpto_clean.c
+
+timeoutconn.o: \
+compile timeoutconn.c ndelay.h select.h error.h readwrite.h ip.h \
+byte.h timeoutconn.h
+ ./compile timeoutconn.c
+
+timeoutread.o: \
+compile timeoutread.c timeoutread.h select.h error.h readwrite.h
+ ./compile timeoutread.c
+
+timeoutwrite.o: \
+compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
+ ./compile timeoutwrite.c
+
+token822.o: \
+compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
+gen_alloc.h gen_allocdefs.h
+ ./compile token822.c
+
+trigger.o: \
+compile trigger.c select.h open.h trigger.h hasnpbg1.h
+ ./compile trigger.c
+
+triggerpull.o: \
+compile triggerpull.c ndelay.h open.h triggerpull.h
+ ./compile triggerpull.c
+
+uint32.h: \
+tryulong32.c compile load uint32.h1 uint32.h2
+ ( ( ./compile tryulong32.c && ./load tryulong32 && \
+ ./tryulong32 ) >/dev/null 2>&1 \
+ && cat uint32.h2 || cat uint32.h1 ) > uint32.h
+ rm -f tryulong32.o tryulong32
+
+wait.a: \
+makelib wait_pid.o wait_nohang.o
+ ./makelib wait.a wait_pid.o wait_nohang.o
+
+wait_nohang.o: \
+compile wait_nohang.c haswaitp.h
+ ./compile wait_nohang.c
+
+wait_pid.o: \
+compile wait_pid.c error.h haswaitp.h
+ ./compile wait_pid.c
diff --git a/PIC.local2alias b/PIC.local2alias
new file mode 100644
index 0000000..75cff56
--- /dev/null
+++ b/PIC.local2alias
@@ -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.
diff --git a/PIC.local2ext b/PIC.local2ext
new file mode 100644
index 0000000..a8bf644
--- /dev/null
+++ b/PIC.local2ext
@@ -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.
diff --git a/PIC.local2local b/PIC.local2local
new file mode 100644
index 0000000..3a067e0
--- /dev/null
+++ b/PIC.local2local
@@ -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.
diff --git a/PIC.local2rem b/PIC.local2rem
new file mode 100644
index 0000000..6857af5
--- /dev/null
+++ b/PIC.local2rem
@@ -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>
diff --git a/PIC.local2virt b/PIC.local2virt
new file mode 100644
index 0000000..60f80c8
--- /dev/null
+++ b/PIC.local2virt
@@ -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.
diff --git a/PIC.nullclient b/PIC.nullclient
new file mode 100644
index 0000000..a90d7cb
--- /dev/null
+++ b/PIC.nullclient
@@ -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>
diff --git a/PIC.relaybad b/PIC.relaybad
new file mode 100644
index 0000000..513f74f
--- /dev/null
+++ b/PIC.relaybad
@@ -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.
diff --git a/PIC.relaygood b/PIC.relaygood
new file mode 100644
index 0000000..0d62fa9
--- /dev/null
+++ b/PIC.relaygood
@@ -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>
diff --git a/PIC.rem2local b/PIC.rem2local
new file mode 100644
index 0000000..62fe61a
--- /dev/null
+++ b/PIC.rem2local
@@ -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.
diff --git a/README b/README
new file mode 100644
index 0000000..e282597
--- /dev/null
+++ b/README
@@ -0,0 +1,295 @@
+netqmail 1.06
+20071130
+James Craig Burley claims copyright on the qmail-isoc patch. For
+more details, see COPYRIGHT.
+
+Apart from James' copyrights, no other copyright is claimed by the
+distributors of netqmail for changes from qmail 1.03 to netqmail 1.05.
+Daniel J. Bernstein has abandoned copyright for qmail 1.03.
+D. J. Bernstein did not participate in, nor has he been asked to
+approve of this distribution.
+
+With this distribution, we thank Daniel Bernstein for his dedication
+of qmail to the public domain. We have taken advantage of this event
+to remove the need to apply a patch. We will distribute a Netqmail 1.07
+shortly which includes enhancements.
+
+Netqmail 1.06 is produced by this motley krewe:
+
+Russ Nelson <nelson@qmail.org>
+Charles Cazabon <charlesc-software-netqmail@pyropus.ca>
+Dave Sill <de5@sws5.ornl.gov>
+Peter Samuel <Peter.Samuel@gormand.com.au>
+Henning Brauer <henning@bsws.de>
+Andrew Richards <ar-nq@acrconsulting.co.uk>
+Richard Lyons <rick-netqmail@frob.com.au>
+John Levine <netqmail@johnlevine.com>
+Scott Gifford <sgifford@suspectclass.com>
+Kyle Wheeler <kyle-netqmail@memoryhole.net>
+Wayne Marshall
+
+qmail is a secure, reliable, efficient, simple message transfer agent.
+It is meant as a replacement for the entire sendmail-binmail system on
+typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and
+BLURB4 for more detailed advertisements.
+
+INSTALL says how to set up and test qmail. If you're upgrading from a
+previous version, 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
+and a pointer to the qmail mailing list.
+
+Other documentation: http://pobox.com/~djb/proto.html shows solutions to
+several Internet mail problems; many of these solutions are implemented
+in qmail. CHANGES and THANKS show how qmail has changed since it was
+first released. SECURITY, INTERNALS, THOUGHTS, and TODO record many of
+the qmail design decisions.
+
+The rest of this file is a list of systypes where various versions of
+qmail have been reported to work. 0.96 was the final gamma version; 1.00
+had exactly the same code as 0.96. To see your systype, make systype;
+cat systype.
+
+1.00: a.ux-3.0-svr2-:-:-:mc68030-:- (tnx RF)
+1.01: aix-3-2-:-:-:000000406300-:- (tnx DG)
+1.01: aix-3-2-:-:-:000011216700-:- (tnx JLB)
+1.01: aix-4-1-:-:-:000041574c00-:- (tnx M2H)
+1.01: aix-4-1-:-:-:000088581000-:- (tnx HJB)
+1.01: aix-4-1-:-:-:002b51134c00-:- (tnx MP)
+1.00: aix-4-1-:-:-:00910033a000-:- (tnx KJJ)
+1.01: aix-4-2-:-:-:000055247900-:- (tnx JLB)
+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)
+1.00: bsd.os-2.1-:i386-:-:i486-:- (tnx RJC)
+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)
+1.01: freebsd-2.1.0-release-:i386-:-:i486-dx-:- (tnx VV)
+1.01: freebsd-2.1.0-release-:i386-:-:i486.dx2-:- (tnx JLB)
+1.00: freebsd-2.1.0-release-:i386-:-:i486dx-:- (tnx chrisj=???)
+1.01: freebsd-2.1.0-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MBS)
+1.01: freebsd-2.1.5-release-:i386-:-:i486-dx-:- (tnx B1F)
+0.96: freebsd-2.1.5-release-:i386-:-:i486dx-:- (tnx FN)
+1.01: freebsd-2.1.5-release-:i386-:-:unknown.-:- (tnx BMF)
+1.00: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM)
+0.96: freebsd-2.1.6-release-:i386-:-:Pentium-Pro.150-:- (tnx CH)
+1.01: freebsd-2.1.6-release-:i386-:-:cy486dlc-:- (tnx M3H)
+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.01: freebsd-2.1.7-release-:i386-:-:pentium.815\100-:- (tnx B1F)
+1.01: freebsd-2.2-970422-releng-:i386-:-:-:- (tnx TM)
+1.00: freebsd-2.2-release-:i386-:-:-:- (tnx MT)
+1.01: freebsd-2.2-stable-:i386-:-:cyrix.5x86-:- (tnx A2B)
+1.01: freebsd-2.2-stable-:i386-:-:pentium-:- (tnx gary@systemics=???)
+1.01: freebsd-2.2.1-release-:i386-:-:-:- (tnx M2R)
+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.01: hp-ux-b.10.20-a-:-:-:9000.755-:- (tnx BCK)
+1.01: irix-5.3-11091812-:-:-:ip22-:- (tnx JL)
+1.01: irix-6.2-03131015-:-:-:ip22-:- (tnx DS)
+1.01: irix64-6.2-03131016-:-:-:ip19-:- (tnx AH)
+1.01: irix64-6.2-06101031-:-:-:ip28-:- (tnx DB)
+1.01: linux-1.2.13-:i386-:-:i486-:- (tnx RF)
+1.01: linux-1.2.13-:i386-:-:pentium-:- (tnx MEE)
+1.01: linux-1.99.4-:i386-:-:pentium-:- (tnx C2H)
+1.01: linux-2.0.0-:i386-:-:i486-:- (tnx kragen@gentle=???)
+1.01: linux-2.0.0-:i386-:-:pentium-:- (tnx MJD)
+1.01: linux-2.0.6-:i386-:-:pentium-:-
+1.00: linux-2.0.6-:i386-:-:ppro-:- (tnx MR)
+1.01: linux-2.0.7-:i386-:-:i486-:- (tnx TLM)
+1.01: linux-2.0.9-:i386-:-:i486-:- (tnx VBM)
+0.96: linux-2.0.13-:i386-:-:pentium-:- (tnx BW)
+1.01: linux-2.0.15-:i386-:-:i486-:- (tnx JCD)
+1.01: linux-2.0.18-:i386-:-:i486-:- (tnx tk@avalon=???)
+1.01: linux-2.0.18-:i386-:-:pentium-:- (tnx root@webtvchat=???)
+1.00: linux-2.0.22-:i386-:-:pentium-:- (tnx MDI)
+1.00: linux-2.0.23-:i386-:-:i486-:- (tnx B2L)
+1.01: linux-2.0.24-:i386-:-:i486-:- (tnx GLM)
+1.00: linux-2.0.24-:i386-:-:pentium-:- (tnx VV)
+0.96: linux-2.0.25-:i386-:-:i486-:- (tnx BDB)
+1.01: linux-2.0.25-:i386-:-:pentium-:- (tnx KA)
+0.93: linux-2.0.26-:i386-:-:i486-:- (tnx blynch@texas=???)
+1.01: linux-2.0.26-:i386-:-:pentium-:- (tnx robbie@opus=???)
+1.00: linux-2.0.27-:-:-:sparc-:- (tnx SVD)
+1.00: linux-2.0.27-:i386-:-:i386-:- (tnx ECG)
+1.01: linux-2.0.27-:i386-:-:i486-:- (tnx BN)
+1.01: linux-2.0.27-:i386-:-:pentium-:- (tnx EK)
+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.01: linux-2.0.28-:i386-:-:ppro-:- (tnx S3T)
+1.01: linux-2.0.28-osfmach3-:-:-:ppc-:- (tnx CG)
+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.01: linux-2.0.30-:-:-:sparc-:- (tnx J2P)
+1.01: linux-2.0.30-:alpha-:-:alpha-:- (tnx WS)
+1.01: linux-2.0.30-:i386-:-:i386-:- (tnx OK)
+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.14-:i386-:-:pentium-:- (tnx SCW)
+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-:-:pentium-:- (tnx UO)
+1.00: linux-2.1.26-:i386-:-:i486-:- (tnx DK)
+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.29-:i386-:-:i486-:- (tnx SJW)
+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)
+1.01: netbsd-1.2.1-:mac68k-:-:apple.macintosh.se/30..(68030)-:- (tnx HM)
+1.01: netbsd-1.2.1-:sparc-:-:fmi,mb86904.@.110.mhz,.on-chip.fpu-:- (tnx ZU)
+0.96: netbsd-1.2c-:pmax-:-:-:- (tnx JLW)
+1.01: netbsd-1.3-:hp300-:-:hp.9000/433.(33mhz.mc68040.cpu+mmu+fpu,.4k.on-chip.physical.i/d.caches)-:- (tnx TB)
+1.01: netbsd-1.3.1-:sun3-:-:sun.3/60-:- (tnx MBS)
+1.01: netbsd-1.3_alpha-:i386-:-:intel.pentium.(p54c).(586-class)-:- (tnx GL)
+1.01: nextstep-3.1-:mc680x0-:-:68040-:- (tnx JRY)
+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-mr_potatoe_head#2-:openbsd.i386-:-:i386-:- (tnx JJMK)
+0.96: openbsd-2.0-puma#1-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
+1.01: openbsd-2.1-asgard#1-:openbsd.i386-:-:i386-:- (tnx ETT)
+1.01: openbsd-2.1-generic#71-:openbsd.sparc-:-:sparc-:- (tnx MMM2)
+1.01: openbsd-2.1-katana#2-:openbsd.i386-:-:i386-:- (tnx CHR)
+1.01: openbsd-2.1-puma#0-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
+1.01: openbsd-2.2-ele#2-:openbsd.i386-:-:i386-:- (tnx RC)
+1.01: openbsd-2.2-generic#424-:openbsd.i386-:-:i386-:- (tnx ETT)
+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.01: sunos-4.1.3_u1-6-:sparc-:sun4-:sun4m-:sun4m- (tnx RD)
+1.01: sunos-4.1.4-1-:unknown-:sun4-:sun4m-:sun4m- (tnx M3S)
+1.01: sunos-4.1.4-2-:sparc-:sun4-:sun4m-:sun4m-
+1.01: sunos-5.3-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx JDJ)
+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)
+1.00: sunos-5.4-generic_101945-34-:sparc-:sun4-:sun4m-:sun4m- (tnx ACB)
+0.96: sunos-5.4-generic_101946-35-:i386-:i86pc-:i86pc-:i86pc- (tnx CK)
+1.01: sunos-5.5-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx seong=???)
+1.01: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx SPM)
+1.01: sunos-5.5-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM)
+1.01: sunos-5.5-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx YC)
+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)
+1.01: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (tnx ERH)
+1.01: sunos-5.5-generic_103093-10-:sparc-:sun4-:sun4d-:sun4d- (tnx KT)
+1.01: sunos-5.5-generic_103094-05-:i386-:i86pc-:i86pc-:i86pc- (tnx M2G)
+1.01: sunos-5.5.1-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx cro=???)
+1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CG)
+1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx MBS)
+1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4u-:sun4u-
+0.96: sunos-5.5.1-generic_103640-02-:sparc-:sun4-:sun4m-:sun4m- (tnx SGC)
+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.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)
+1.01: ultrix-4.4-0-:-:-:risc-:- (tnx RSK)
+1.01: unix_sv-4.2mp-2.1.2-:i386-:-:i386-:- (tnx J2W)
+
diff --git a/REMOVE.binmail b/REMOVE.binmail
new file mode 100644
index 0000000..9532ac9
--- /dev/null
+++ b/REMOVE.binmail
@@ -0,0 +1,16 @@
+Here's how to remove binmail from your system. Don't do this if you have
+configured qmail to use binmail for local delivery.
+
+
+1. Find the binmail binary on your system: /usr/libexec/mail.local if
+ that exists, otherwise /bin/mail.
+
+2. Remove permissions from the binmail binary:
+ # chmod 0 /usr/libexec/mail.local
+
+3. If the binmail binary was /bin/mail, make sure that ``mail'' still
+ invokes a usable mailer. Under SVR4 you may want to link mail to
+ mailx.
+
+4. Comment out the comsat line in /etc/inetd.conf, and kill -HUP your
+ inetd.
diff --git a/REMOVE.sendmail b/REMOVE.sendmail
new file mode 100644
index 0000000..5be6e78
--- /dev/null
+++ b/REMOVE.sendmail
@@ -0,0 +1,28 @@
+Here's how to remove sendmail from your system.
+
+1. Find sendmail in your boot scripts. It's usually in either /etc/rc or
+ /etc/init.d/sendmail. It looks like
+ sendmail -bd -q15m
+ -q15m means that it should run the queue every 15 minutes; you may
+ see a different number. Comment out this line.
+
+2. Kill the sendmail daemon. You should first kill -STOP the daemon; if
+ any children are running, you should kill -CONT, wait, kill -STOP
+ again, and repeat ad nauseam. If there aren't any children, kill
+ -TERM and then kill -CONT.
+
+3. Check whether you have any messages in the sendmail queue,
+ /var/spool/mqueue. If you do, you will have to try flushing them with
+ sendmail.bak -q. If necessary, wait a while and run sendmail.bak -q
+ again. Repeat until the queue is empty. This may take several days.
+
+4. Remove the setuid bit on the sendmail binary, to prevent local users
+ from gaining extra privileges through sendmail's security holes. The
+ binary may be at several different locations:
+ # chmod 0 /usr/lib/sendmail
+ # chmod 0 /usr/sbin/sendmail
+ # chmod 0 /usr/lib/sendmail.mx
+
+5. Move the sendmail binary out of the way:
+ # mv /usr/lib/sendmail /usr/lib/sendmail.bak
+ # mv /usr/sbin/sendmail /usr/sbin/sendmail.bak
diff --git a/SECURITY b/SECURITY
new file mode 100644
index 0000000..098f124
--- /dev/null
+++ b/SECURITY
@@ -0,0 +1,131 @@
+Background: Every few months CERT announces Yet Another Security Hole In
+Sendmail---something that lets local or even remote users take complete
+control of the machine. I'm sure there are many more holes waiting to be
+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
+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.
+Here are some of the things I did to make sure that qmail will never let
+an intruder into your machine.
+
+
+1. Programs and files are not addresses. Don't treat them as addresses.
+
+sendmail treats programs and files as addresses. Obviously random people
+can't be allowed to execute arbitrary programs or write to arbitrary
+files, so sendmail goes through horrendous contortions trying to keep
+track of whether a local user was ``responsible'' for an address. This
+has proven to be an unmitigated disaster.
+
+In qmail, programs and files are not addresses. The local delivery
+agent, qmail-local, can run programs or write to files as directed by
+~user/.qmail, but it's always running as that user. (The notion of
+``user'' is configurable, but root is never a user. To prevent silly
+mistakes, qmail-local makes sure that neither ~user nor ~user/.qmail is
+group-writable or world-writable.)
+
+Security impact: .qmail, like .cshrc and .exrc and various other files,
+means that anyone who can write arbitrary files as a user can execute
+arbitrary programs as that user. That's it.
+
+
+2. Do as little as possible in setuid programs.
+
+A setuid program must operate in a very dangerous environment: a user is
+under complete control of its fds, args, environ, cwd, tty, rlimits,
+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
+write portable code that cleans up everything.
+
+Of the twenty most recent sendmail security holes, eleven worked only
+because the entire sendmail system is setuid.
+
+Only one qmail program is setuid: qmail-queue. Its only purpose is to
+add a new mail message to the outgoing queue.
+
+
+3. Do as little as possible as root.
+
+The entire sendmail system runs as root, so there's no way that its
+mistakes can be caught by the operating system's built-in protections.
+In contrast, only two qmail programs, qmail-start and qmail-lspawn,
+run as root.
+
+
+4. Move separate functions into mutually untrusting programs.
+
+Five of the qmail programs---qmail-smtpd, qmail-send, qmail-rspawn,
+qmail-remote, and tcp-env---are not security-critical. Even if all of
+these programs are completely compromised, so that an intruder has
+control over the qmaild, qmails, and qmailr accounts and the mail queue,
+he still can't take over your system. None of the other programs trust
+the results from these five.
+
+In fact, these programs don't even trust each other. They are in three
+groups: tcp-env and qmail-smtpd, which run as qmaild; qmail-rspawn and
+qmail-remote, which run as qmailr; and qmail-send, the queue manager,
+which runs as qmails. Each group is immune from attacks by the others.
+
+(From root's point of view, as long as root doesn't send any mail, only
+qmail-start and qmail-lspawn are security-critical. They don't write any
+files or start any other programs as root.)
+
+
+5. Don't parse.
+
+I have discovered that there are two types of command interfaces in the
+world of computing: good interfaces and user interfaces.
+
+The essence of user interfaces is _parsing_---converting an unstructured
+sequence of commands, in a format usually determined more by psychology
+than by solid engineering, into structured data.
+
+When another programmer wants to talk to a user interface, he has to
+_quote_: convert his structured data into an unstructured sequence of
+commands that the parser will, he hopes, convert back into the original
+structured data.
+
+This situation is a recipe for disaster. The parser often has bugs: it
+fails to handle some inputs according to the documented interface. The
+quoter often has bugs: it produces outputs that do not have the right
+meaning. Only on rare joyous occasions does it happen that the parser
+and the quoter both misinterpret the interface in the same way.
+
+When the original data is controlled by a malicious user, many of these
+bugs translate into security holes. Some examples: the Linux login
+-froot security hole; the classic find | xargs rm security hole; the
+Majordomo injection security hole. Even a simple parser like getopt is
+complicated enough for people to screw up the quoting.
+
+In qmail, all the internal file structures are incredibly simple: text0
+lines beginning with single-character commands. (text0 format means that
+lines are separated by a 0 byte instead of line feed.) The program-level
+interfaces don't take options.
+
+All the complexity of parsing RFC 822 address lists and rewriting
+headers is in the qmail-inject program, which runs without privileges
+and is essentially part of the UA.
+
+
+6. Keep it simple, stupid.
+
+See BLURB for some of the reasons that qmail is so much smaller than
+sendmail. There's nothing inherently complicated about writing a mailer.
+(Except RFC 822 support; but that's only in qmail-inject.) Security
+holes can't show up in features that don't exist.
+
+
+7. Write bug-free code.
+
+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
+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
+getln() make it very easy to avoid buffer overruns, memory leaks, and
+artificial line length limits.
diff --git a/SENDMAIL b/SENDMAIL
new file mode 100644
index 0000000..9280c24
--- /dev/null
+++ b/SENDMAIL
@@ -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.
diff --git a/SYSDEPS b/SYSDEPS
new file mode 100644
index 0000000..0bb01ec
--- /dev/null
+++ b/SYSDEPS
@@ -0,0 +1,17 @@
+VERSION
+systype
+hasshsgr.h
+hasnpbg1.h
+select.h
+hasflock.h
+hassalen.h
+fork.h
+hassgact.h
+direntry.h
+hassgprm.h
+haswaitp.h
+hasmkffo.h
+uint32.h
+dns.lib
+socket.lib
+syslog.lib
diff --git a/TARGETS b/TARGETS
new file mode 100644
index 0000000..facdad7
--- /dev/null
+++ b/TARGETS
@@ -0,0 +1,387 @@
+auto-ccld.sh
+make-load
+find-systype
+systype
+load
+make-compile
+compile
+fork.h
+qmail-local.o
+qmail.o
+quote.o
+now.o
+gfrom.o
+myctime.o
+slurpclose.o
+make-makelib
+makelib
+case_diffb.o
+case_diffs.o
+case_lowerb.o
+case_lowers.o
+case_starts.o
+case.a
+getln.o
+getln2.o
+getln.a
+subgetopt.o
+sgetopt.o
+getopt.a
+sig_alarm.o
+hassgprm.h
+sig_block.o
+hassgact.h
+sig_catch.o
+sig_pause.o
+sig_pipe.o
+sig_child.o
+sig_hup.o
+sig_term.o
+sig_bug.o
+sig_misc.o
+sig.a
+open_append.o
+open_excl.o
+open_read.o
+open_trunc.o
+open_write.o
+open.a
+seek_cur.o
+seek_end.o
+seek_set.o
+seek_trunc.o
+seek.a
+hasflock.h
+lock_ex.o
+lock_exnb.o
+lock_un.o
+lock.a
+fd_copy.o
+fd_move.o
+fd.a
+haswaitp.h
+wait_pid.o
+wait_nohang.o
+wait.a
+env.o
+envread.o
+env.a
+stralloc_eady.o
+stralloc_pend.o
+stralloc_copy.o
+stralloc_opys.o
+stralloc_opyb.o
+stralloc_cat.o
+stralloc_cats.o
+stralloc_catb.o
+stralloc_arts.o
+stralloc.a
+alloc.o
+alloc_re.o
+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_un.o
+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
+uint32.h
+qmail-lspawn.o
+select.h
+chkspawn.o
+auto-int.o
+auto-int
+auto_spawn.c
+auto_spawn.o
+chkspawn
+spawn.o
+chkshsgr.o
+chkshsgr
+hasshsgr.h
+prot.o
+coe.o
+cdb_hash.o
+cdb_unpack.o
+cdb_seek.o
+cdb.a
+auto-uid.o
+auto-uid
+auto-gid.o
+auto-gid
+auto_uids.c
+auto_uids.o
+qmail-lspawn
+qmail-getpw.o
+auto_break.c
+auto_break.o
+auto_usera.c
+auto_usera.o
+qmail-getpw
+qmail-remote.o
+control.o
+constmap.o
+timeoutread.o
+timeoutwrite.o
+timeoutconn.o
+tcpto.o
+dns.o
+ip.o
+ipalloc.o
+hassalen.h
+ipme.o
+ndelay.o
+ndelay_off.o
+ndelay.a
+dns.lib
+qmail-remote
+qmail-rspawn.o
+tcpto_clean.o
+qmail-rspawn
+direntry.h
+qmail-clean.o
+fmtqfn.o
+auto_split.c
+auto_split.o
+qmail-clean
+qmail-send.o
+qsutil.o
+newfield.o
+prioq.o
+hasmkffo.h
+fifo.o
+hasnpbg1.h
+trigger.o
+readsubdir.o
+date822fmt.o
+qmail-send
+qmail-start.o
+qmail-start
+splogger.o
+syslog.lib
+splogger
+qmail-queue.o
+triggerpull.o
+qmail-queue
+qmail-inject.o
+headerbody.o
+hfield.o
+token822.o
+qmail-inject
+predate.o
+predate
+datemail
+mailsubj
+qmail-upq
+qmail-showctl.o
+qmail-showctl
+qmail-newu.o
+cdbmss.o
+cdbmake_pack.o
+cdbmake_hash.o
+cdbmake_add.o
+cdbmake.a
+qmail-newu
+qmail-pw2u.o
+qmail-pw2u
+qmail-qread.o
+qmail-qread
+qmail-qstat
+qmail-tcpto.o
+qmail-tcpto
+qmail-tcpok.o
+qmail-tcpok
+qmail-pop3d.o
+commands.o
+maildir.o
+qmail-pop3d
+qmail-popup.o
+qmail-popup
+qmail-qmqpc.o
+qmail-qmqpc
+qmail-qmqpd.o
+received.o
+qmail-qmqpd
+qmail-qmtpd.o
+rcpthosts.o
+qmail-qmtpd
+qmail-smtpd.o
+qmail-smtpd
+sendmail.o
+sendmail
+tcp-env.o
+remoteinfo.o
+tcp-env
+qmail-newmrh.o
+qmail-newmrh
+config
+config-fast
+dnscname.o
+dnsdoe.o
+dnscname
+dnsptr.o
+dnsptr
+dnsip.o
+dnsip
+dnsmxip.o
+dnsmxip
+dnsfq.o
+dnsfq
+hostname.o
+hostname
+ipmeprint.o
+ipmeprint
+qreceipt.o
+qreceipt
+qsmhook.o
+qsmhook
+qbiff.o
+qbiff
+forward.o
+forward
+preline.o
+preline
+condredirect.o
+condredirect
+bouncesaying.o
+bouncesaying
+except.o
+except
+maildirmake.o
+maildirmake
+maildir2mbox.o
+maildir2mbox
+maildirwatch.o
+maildirwatch
+qail
+elq
+pinq
+idedit.o
+idedit
+install-big.o
+install.o
+install-big
+hier.o
+install
+instcheck.o
+instcheck
+home
+home+df
+proc
+proc+df
+binm1
+binm1+df
+binm2
+binm2+df
+binm3
+binm3+df
+it
+qmail-local.0
+qmail-lspawn.0
+qmail-getpw.8
+qmail-getpw.0
+qmail-remote.0
+qmail-rspawn.0
+qmail-clean.0
+qmail-send.8
+qmail-send.0
+qmail-start.8
+qmail-start.0
+splogger.0
+qmail-queue.0
+qmail-inject.0
+mailsubj.0
+qmail-showctl.0
+qmail-newu.8
+qmail-newu.0
+qmail-pw2u.8
+qmail-pw2u.0
+qmail-qread.0
+qmail-qstat.0
+qmail-tcpto.0
+qmail-tcpok.0
+qmail-pop3d.0
+qmail-popup.0
+qmail-qmqpc.0
+qmail-qmqpd.0
+qmail-qmtpd.0
+qmail-smtpd.0
+tcp-env.0
+qmail-newmrh.8
+qmail-newmrh.0
+qreceipt.0
+qbiff.0
+forward.0
+preline.0
+condredirect.0
+bouncesaying.0
+except.0
+maildirmake.0
+maildir2mbox.0
+maildirwatch.0
+qmail.0
+qmail-limits.7
+qmail-limits.0
+qmail-log.0
+qmail-control.5
+qmail-control.0
+qmail-header.0
+qmail-users.5
+qmail-users.0
+dot-qmail.5
+dot-qmail.0
+qmail-command.0
+tcp-environ.0
+maildir.0
+mbox.0
+addresses.0
+envelopes.0
+forgeries.0
+man
+setup
+check
diff --git a/TEST.deliver b/TEST.deliver
new file mode 100644
index 0000000..4fc4c32
--- /dev/null
+++ b/TEST.deliver
@@ -0,0 +1,82 @@
+You can do several tests of qmail delivery without setting up qmail to
+accept messages through SMTP or through /usr/lib/sendmail:
+
+1. After you start qmail, look for a
+ qmail: status: local 0/10 remote 0/20
+ line in syslog. qmail-send always prints either ``cannot start'' or
+ ``status''. (The big number is a splogger timestamp.)
+
+2. 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
+ as root; qmail-rspawn, running as qmailr; and qmail-clean, running
+ as qmailq. You will also see splogger, running as qmaill.
+
+3. Local-local test: Send yourself an empty message. (Replace ``me''
+ with your username. Make sure to include the ``to:'' colon.)
+ % echo to: me | /var/qmail/bin/qmail-inject
+ The message will show up immediately in your mailbox, and syslog
+ will show something like this:
+ qmail: new msg 53
+ qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
+ 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: status: local 0/10 remote 0/20
+ qmail: end msg 53
+ (53 is an inode number; 20345 is a process ID; your numbers will
+ probably be different.)
+
+4. Local-error test: Send a message to a nonexistent local address.
+ % echo to: nonexistent | /var/qmail/bin/qmail-inject
+ qmail: new msg 53
+ qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
+ 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: status: local 0/10 remote 0/20
+ qmail: bounce msg 53 qp 20357
+ qmail: end msg 53
+ qmail: new msg 54
+ qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
+ 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: status: local 0/10 remote 0/20
+ qmail: end msg 54
+ You will now have a bounce message in your mailbox.
+
+5. Local-remote test: Send an empty message to your account on another
+ machine.
+ % echo to: me@wherever | /var/qmail/bin/qmail-inject
+ qmail: new msg 53
+ qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
+ 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: status: local 0/10 remote 0/20
+ qmail: end msg 53
+ There will be a pause between ``starting delivery'' and ``success'';
+ SMTP is slow. Check that the message is in your mailbox on the other
+ machine.
+
+6. Local-postmaster test: Send mail to postmaster, any capitalization.
+ % echo to: POSTmaster | /var/qmail/bin/qmail-inject
+ Look for the message in the alias mailbox, normally ~alias/Mailbox.
+
+7. Double-bounce test: Send a message with a completely bad envelope.
+ % /var/qmail/bin/qmail-inject -f nonexistent
+ To: unknownuser
+ Subject: testing
+
+ This is a test. This is only a test.
+ %
+ (Use end-of-file, not dot, to end the message.) Look for the double
+ bounce in the alias mailbox.
+
+8. Group membership test:
+ % cat > ~me/.qmail-groups
+ |groups >> MYGROUPS; exit 0
+ % /var/qmail/bin/qmail-inject me-groups < /dev/null
+ % cat ~me/MYGROUPS
+ MYGROUPS will show your normal gid and nothing else. (Under Solaris,
+ make sure to use /usr/ucb/groups; /usr/bin/groups is broken.)
diff --git a/TEST.receive b/TEST.receive
new file mode 100644
index 0000000..7644845
--- /dev/null
+++ b/TEST.receive
@@ -0,0 +1,41 @@
+You can do several tests of messages entering the qmail system:
+
+1. SMTP server test: Forge some mail locally via SMTP. Replace ``me''
+ with your username and ``domain'' with your host's name.
+ % telnet 127.0.0.1 25
+ Trying 127.0.0.1...
+ Connected to 127.0.0.1.
+ Escape character is '^]'.
+ 220 domain ESMTP
+ helo dude
+ 250 domain
+ mail <me@domain>
+ 250 ok
+ rcpt <me@domain>
+ 250 ok
+ data
+ 354 go ahead
+ Subject: testing
+
+ This is a test.
+ .
+ 250 ok 812345679 qp 12345
+ quit
+ 221 domain
+ Connection closed by foreign host.
+ %
+ Look for the message in your mailbox. (Note for programmers: Most
+ SMTP servers need more text after MAIL and RCPT. See RFC 821.)
+
+2. Remote-local test: Send yourself some mail from another machine.
+ Look for the message in your mailbox.
+
+3. Remote-error test: Send some mail from another machine to
+ nonexistent@domain. Look for a bounce message in the remote mailbox.
+
+4. UA test: Try sending mail, first to a local account, then to a
+ remote account, with your normal user agent.
+
+5. Remote-postmaster test: Send mail from another machine to
+ PoStMaStEr@domain. Look for the message in the alias mailbox,
+ normally ~alias/Mailbox.
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..76996f8
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,338 @@
+Thanks to lots of people for success and failure reports, code, ideas,
+and documentation. See CHANGES for details of specific contributions.
+Sorry if I left anyone out.
+
+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
+ADM = Adam D. Morley
+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
+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
+JRM = Jason R. Mastaler
+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
+NAA = Nicholas A. Amato
+NH = Nick Holloway
+NND = N. Dudorov
+NR = Norbert Roeding
+NW = Nicholas Waples
+OK = Oezguer Kesim
+OR = Ollivier Robert
+OS = Oliver Seiler
+PB = Peter Bowyer
+PCO = Peter C. Olsen
+PGF = Paul Fox
+PGR = Phil G. Rorex
+PH = Paul Harrington
+PJG = Paul Graham
+PJH = Peter J. Hunter
+PK = Petri Kaukasoina
+PMH = Peter M. Haworth
+PO = Paul Overell
+PS = Paul Svensson
+PT = Paul Taylor
+PTW = P. T. Withington
+PW = Peter Wilkinson
+R2N = Rivo Nurges
+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
+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
+YC = Yuji Chikahiro
+YF = Yaroslav Faybishenko
+ZU = Zin Uda
diff --git a/THOUGHTS b/THOUGHTS
new file mode 100644
index 0000000..d6910da
--- /dev/null
+++ b/THOUGHTS
@@ -0,0 +1,418 @@
+Please note that this file is not called ``Internet Mail For Dummies.''
+It _records_ my thoughts on various issues. It does not _explain_ them.
+Paragraphs are not organized except by section. The required background
+varies wildly from one paragraph to the next.
+
+In this file, ``sendmail'' means Allman's creation; ``sendmail-clone''
+means the program in this package.
+
+
+1. Security
+
+There are lots of interesting remote denial-of-service attacks on any
+mail system. A long-term solution is to insist on prepayment for
+unauthorized resource use. The tricky technical problem is to make the
+prepayment enforcement mechanism cheaper than the expected cost of the
+attacks. (For local denial-of-service attacks it's enough to be able to
+figure out which user is responsible.)
+
+qmail-send's log was originally designed for profiling. It subsequently
+sprouted some tracing features. However, there's no way to verify
+securely that a particular message came from a particular local user;
+how do you know the recipient is telling you the truth about the
+contents of the message? With QUEUE_EXTRA it'd be possible to record a
+one-way hash of each outgoing message, but a user who wants to send
+``bad'' mail can avoid qmail entirely.
+
+I originally decided on security grounds not to put qmail advertisements
+into SMTP responses: advertisements often act as version identifiers.
+But this problem went away when I found a stable qmail URL.
+
+As qmail grows in popularity, the mere knowledge that rcpthosts is so
+easily available will deter people from setting up unauthorized MXs.
+(I've never seen an unauthorized MX, but I can imagine that it would be
+rather annoying.) Note that, unlike the bat book checkcompat() kludge,
+rcpthosts doesn't interfere with mailing lists.
+
+qmail-start doesn't bother with tty dissociation. On some old machines
+this means that random people can send tty signals to the qmail daemons.
+That's a security flaw in the job control subsystem, not in qmail.
+
+The resolver library isn't too bloated (before 4.9.4, at least), but it
+uses stdio, which _is_ bloated. Reading /etc/resolv.conf costs lots of
+memory in each qmail-remote process. So it's tempting to incorporate a
+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
+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
+steal mail. Solution: insist that the resolver run on the same host; the
+kernel can guarantee the security of low-numbered 127.0.0.1 UDP ports.
+
+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
+tried to hide that fact by disallowing root access over NFS. Intruders
+nevertheless broke into system after system, first obtaining bin access
+and then obtaining root access. Various people thus decided to compound
+Sun's error and build a wall between root and all other users: if all
+system files are owned by root, and if there are no security holes other
+than NFS, someone who breaks in via NFS won't be able to wipe out the
+operating system---he'll merely be able to wipe out all user files. This
+clueless policy means that, for example, all the qmail users have to be
+replaced by root. See what I mean by ``enemy''? ... Basic NFS comments:
+Aside from the cryptographic problem of having hosts communicate
+securely, it's obvious that there's an administrative problem of mapping
+client uids to server uids. If a host is secure and under your control,
+you shouldn't have to map anything. If a host is under someone else's
+control, you'll want to map his uids to one local account; it's his
+client's job to decide which of his users get to talk NFS in the first
+place. Sun's original map---root to nobody, everyone else left alone---
+is, as far as I can tell, always wrong.
+
+
+2. Injecting mail locally (qmail-inject, sendmail-clone)
+
+RFC 822 section 3.4.9 prohibits certain visual effects in headers, and
+the 822bis draft prohibits even more. qmail-inject could enforce these
+absurd restrictions, but why waste the time? If you will suffer from
+someone sending you ``flash mail,'' go find a better mail reader.
+
+qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops
+sendmail from adding Apparently-To. Unfortunately, old versions of
+sendmail will append a host name. This wasn't fixed until sendmail 8.7.
+How many years has it been since RFC 822 came out?
+
+sendmail discards duplicate addresses. This has probably resulted in
+more lost and stolen mail over the years than the entire Chicago branch
+of the United States Postal Service. The qmail system delivers messages
+exactly as it's told to do. Along the same lines: qmail-inject is both
+unable and unwilling to support anything like sendmail's (default)
+nometoo option. Of course, a list manager could support nometoo.
+
+There should be a mechanism in qmail-inject that does for envelope
+recipients what Return-Path does for the envelope sender. Then
+qmail-inject -n could print the recipients.
+
+Should qmail-inject bounce messages with no recipients? Should there be
+an option for this? If it stays as is (accept the message), qmail-inject
+could at least avoid invoking qmail-queue.
+
+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
+main(), then wait until the pids wrap around, then restart qmail-inject
+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 without
+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
+only to sender addresses.'' Recipient masquerading works fine with
+qmail. None of sendmail's pitfalls apply, basically because qmail has a
+straight paper path.
+
+I predicted that I would receive some pressure to make up for the
+failings of MUA writers who don't understand the concept of reliability.
+(``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)
+
+qmail-smtpd doesn't allow privacy-invading commands like VRFY and EXPN.
+If you really want to publish such information, use a mechanism that
+legitimate users actually know about, such as fingerd or httpd.
+
+RFC 1123 says that VRFY and EXPN are important to track down cross-host
+mailing list loops. With Delivered-To, mailing list loops do no damage,
+_and_ one of the list administrators gets a bounce message that shows
+exactly how the loop occurred. Solve the problem, not the symptom.
+
+Should dns.c make special allowances for 127.0.0.1/localhost?
+
+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)
+
+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)
+qmail-smtpd or qmail-qmtpd or qmail-qmqpd, it could be told a size in
+advance. I wish UNIX had an atomic allocate-disk-space routine...
+
+The qmail.h interface (reflecting the qmail-queue interface, which in
+turn reflects the current queue file structure) is constitutionally
+incapable of handling an address that contains a 0 byte. I can't imagine
+that this will be a problem.
+
+Should qmail-queue not bother queueing a message with no recipients?
+
+
+5. Handling queued mail (qmail-send, qmail-clean)
+
+The queue directory must be local. Mounting it over NFS is extremely
+dangerous---not that this stops people from running sendmail that way!
+Diskless hosts should use mini-qmail instead.
+
+Queue reliability demands that single-byte writes be atomic. This is
+true for a fixed-block filesystem such as UFS, and for a logging
+filesystem such as LFS.
+
+qmail-send uses 8 bytes of memory per queued message. Double that for
+reallocation. (Fix: use a small forest of heaps; i.e., keep several
+prioqs.) Double again for buddy malloc()s. (Fix: be clever about the
+heap sizes.) 32 bytes is worrisome, but not devastating. Even on my
+disk-heavy memory-light machine, I'd run out of inodes long before
+running out of memory.
+
+Some mail systems organize the queue by host. This is pointless as a
+means of splitting up the queue directory. The real issue is what to do
+when you suddenly find out that a host is up. For local SLIP/PPP links
+you know in advance which hosts need this treatment, so you can handle
+them with virtualdomains and serialmail.
+
+For the old queue structure I implemented recipient list compression:
+if mail goes out to a giant mailing list, and most of the recipients are
+delivered, make a new, compressed, todo list. But this really isn't
+worth the effort: it saves only a tiny bit of CPU time.
+
+qmail-send doesn't have any notions of precedence, priority, fairness,
+importance, etc. It handles the queue in first-seen-first-served order.
+One could put a lot of work into doing something different, but that
+work would be a waste: given the triggering mechanism and qmail's
+deferral strategy, it is exceedingly rare for the queue to contain more
+than one deliverable message at any given moment.
+
+Exception: Even with all the concurrency tricks, qmail-send can end up
+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
+meantime. The simplest way to handle this would be to put big messages
+on a separate channel.
+
+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
+next pass will be delayed. I implemented the opposite strategy for the
+old queue structure. Some hassles: mark() had to understand how job
+input was buffered; every new delivery had to check whether the same
+mpos in the same message was already being done.
+
+Some things that qmail-send does synchronously: queueing a bounce
+message; doing a cleanup via qmail-clean; classifying and rewriting all
+the addresses in a new message. As usual, making these asynchronous
+would require some housekeeping, but could speed things up a bit.
+(I'm willing to assume POSIX waitpid() for asynchronous bounces; putting
+an unbounded buffer into wait_pid() for the sake of NeXTSTEP 3 is not
+worthwhile.)
+
+Disk I/O is a bottleneck; UFS is reliable but it isn't fast. A good
+logging filesystem offers much better performance, but logging
+filesystems aren't widely available. Solution: Keep a journal, separate
+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.
+
+Bounce aggregation is a dubious feature. Bounce records aren't
+crashproof; there can be a huge delay between a failure and a bounce;
+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.
+
+qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Or run
+qmail-start under an external service controller, such as supervise;
+that's why it runs in the foreground.
+
+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
+to do about this. Of course, a hard I/O error means that mail is toast,
+but a soft I/O error shouldn't cause any trouble.
+
+job_open() or pass_dochan() could be paranoid about the same id,channel
+already being open; but, since messdone() is so paranoid, the worst
+possible effect of a bug along these lines would be double delivery.
+
+Mathematical amusement: The optimal retry schedule is essentially,
+though not exactly, independent of the actual distribution of message
+delay times. What really matters is how much cost you assign to retries
+and to particular increases in latency. qmail's current quadratic retry
+schedule says that an hour-long delay in a day-old message is worth the
+same as a ten-minute delay in an hour-old message; this doesn't seem so
+unreasonable.
+
+Insider information: AOL retries their messages every five minutes for
+three days straight. Hmmm.
+
+
+6. Sending mail through the network (qmail-rspawn, qmail-remote)
+
+Are there any hosts, anywhere, whose mailers are bogged down by huge
+messages to multiple recipients at a single host? For typical hosts,
+multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a
+_slowness_ feature. Separate SMTP transactions have much lower latency.
+
+I've heard three complaints about bandwidth use from masochists sending
+messages through a modem through a smarthost to thousands of users---
+without sublists! They can get much better performance with QMQP.
+
+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.
+
+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
+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
+record sending me mail for a host that I don't recognize as local.)
+Automatically treating ambiguous MXs as local is incompatible with my
+design decision to keep local delivery working when the network goes
+down. It puts more faith in DNS than DNS deserves. Much better: Have
+your MX records generated automatically from control/locals.
+
+If I successfully connect to an MX host but it temporarily refuses to
+accept the message, I give up and put the message back into the queue.
+But several documents seem to suggest that I should try further MX
+records. What are they thinking? My approach deals properly with downed
+hosts, hosts that are unreachable through a firewall, and load
+balancing; what else do people use multiple MX records for?
+
+Currently qmail-remote sends data in 1024-byte buffers. Perhaps it
+should try to take account of the MTU.
+
+Perhaps qmail-remote should allocate a fixed amount of DNS/connect()
+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
+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)
+
+qmail-local doesn't support comsat. comsat is a pointless abomination.
+Use qbiff if you want that kind of notification.
+
+The getpwnam() interface hides I/O errors. Solution: qmail-pw2u.
+
+
+8. sendmail V8's new features
+
+sendmail-8.8.0/doc/op/op.me includes a list of big improvements of
+sendmail 8.8.0 over sendmail 5.67. Here's how qmail stacks up against
+each of those improvements. (Of course, qmail has its own improvements,
+but that's not the point of this list.)
+
+Connection caching, MX piggybacking: Nope. (Profile. Don't speculate.)
+
+Response to RCPT command is fast: Yup.
+
+IP addresses show up in Received lines: Yup.
+
+Self domain literal is properly handled: Yup.
+
+Different timeouts for QUIT, RCPT, etc.: No, just a single timeout.
+
+Proper <> handling, route-address pruning: Yes, but not configurable.
+
+ESMTP support: Yup. (Server-side, including PIPELINING.)
+
+8-bit clean: Yup. (Including server-side 8BITMIME support; same as
+sendmail with the 8 option.)
+
+Configurable user database: Yup.
+
+BIND support: Yup.
+
+Keyed files: Yes, in fastforward.
+
+931/1413/Ident/TAP: Yup.
+
+Correct 822 address list parsing: Yup. (Note that sendmail still has
+some major problems with quoting.)
+
+List-owner handling: Yup.
+
+Dynamic header allocation: Yup.
+
+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.
+
+Error message configuration: Nope.
+
+GECOS matching: Not directly, but easy to hook in.
+
+Hop limit configuration: No. (qmail's limit is 100 hops. qmail offers
+automatic loop protection much more advanced than hop counting.)
+
+MIME error messages: No. (qmail uses QSBMF error messages, which are
+much easier to parse.)
+
+Forward file path: Yes, via /etc/passwd.
+
+Incoming SMTP configuration: Yes, via inetd or tcpserver.
+
+Privacy options: Yes, but they're not options.
+
+Best-MX mangling: Nope. See section 6 for further discussion.
+
+7-bit mangling: Nope. qmail always uses 8 bits.
+
+Support for up to 20 MX records: Yes, and more. qmail has no limits
+other than memory.
+
+Correct quoting of name-and-address headers: Yup.
+
+VRFY and EXPN now different: Nope. qmail always hides this information.
+
+Multi-word classes, deferred macro expansion, separate envelope/header
+$g processing, separate per-mailer envelope and header processing, new
+command line flags, new configuration lines, new mailer flags, new
+macros: These are sendmail-specific; they wouldn't even make sense for
+qmail. For example, _of course_ qmail handles envelopes and headers
+separately; they're almost entirely different objects!
+
+
+9. Miscellany
+
+sendmail-clone and qsmhook are too bletcherous to be documented. (The
+official replacement for qsmhook is preline, together with the
+qmail-command environment variables.)
+
+I've considered making install atomic, but this is very difficult to do
+right, and pointless if it isn't done right.
+
+RN suggests automatically putting together a reasonable set of lines for
+/etc/passwd. I perceive this as getting into the adduser business, which
+is worrisome: I'll be lynched the first time I screw up somebody's
+passwd file. This should be left to OS-specific installation scripts.
+
+The BSD 4.2 inetd didn't allow a username. I think I can safely forget
+about this. (DS notes that the username works under Ultrix even though
+it's undocumented.)
+
+I should clean up the bput/put choices.
+
+Some of the stralloc_0()s indicate that certain lower-level routines
+should grok stralloc.
+
+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
+is ASCII; and that all pointers are interchangeable. Do I care?
+
+The bat book justifies sendmail's insane line-splitting mechanism by
+pointing out that it might be useful for ``a 40-character braille
+print-driving program.'' C'mon, guys, is that your best excuse?
+
+qmail's mascot is a dolphin.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..5841c0a
--- /dev/null
+++ b/TODO
@@ -0,0 +1,23 @@
+consider stripping vdoms for VERPs; tnx PJH
+consider ~ in qmail-local for doing defaultdelivery (not recursively)
+consider POP bulletins
+turn qmail-upq into a more serious queue-moving utility
+consider fast-greeting option in qmail-smtpd
+build a returnmail package
+
+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 mess822 in qmail-inject
+use mess822 in qreceipt
+use mess822 in qbiff
+use mess822 in maildirwatch
+eliminate token822, headerbody, hfield
+replace INTERNALS and THOUGHTS with a real paper describing qmail
+handle IPv6
+rewrite everything from scratch
diff --git a/UPGRADE b/UPGRADE
new file mode 100644
index 0000000..959120d
--- /dev/null
+++ b/UPGRADE
@@ -0,0 +1,66 @@
+SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and
+information generally), the qmail system comes with NO WARRANTY. It's
+much more secure and reliable than sendmail, but that's not saying much.
+
+
+Here's how to upgrade to netqmail 1.05. This procedure will overwrite the
+old qmail binaries. Furthermore, it may begin delivering messages from
+the queue before you have had a chance to test it.
+
+
+WARNING for upgrades from 1.00 or 1.01: 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 for upgrades from 1.01: recipientmap is gone. The virtualdomains
+mechanism has been expanded to support virtual users.
+
+
+Before starting, compare conf* to your old conf*, and make any necessary
+changes. You can copy conf* from 1.02 or 1.03.
+
+
+How to install:
+
+ 1. Compile the programs and create the formatted man pages:
+ # make it man
+
+ 2. Inform your users that mail will not be accepted for a few minutes.
+
+ 3. Disable deliveries by killing your old qmail-send. Wait for it to
+ print ``exiting'' in the log.
+
+ 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
+ your tcpserver. If you are running a QMTP server, disable that too.)
+ Wait for current qmail-smtpd processes to die.
+
+ 5. Install the new binaries and man pages:
+ # make setup check
+
+ 6. If your boot scripts are using qmail-start instead of /var/qmail/rc:
+ Copy /var/qmail/boot/home to /var/qmail/rc. (Use home+df instead if
+ you have installed dot-forward; use proc or proc+df if you are using
+ procmail by default for local deliveries.) Compare /var/qmail/rc to
+ your qmail-start boot line, and edit /var/qmail/rc if necessary.
+ Replace your qmail-start boot line with
+ csh -cf '/var/qmail/rc &'
+
+ 7. Reenable deliveries:
+ # csh -cf '/var/qmail/rc &'
+
+ 8. Read TEST.deliver.
+
+ 9. Reenable SMTP service by restoring the smtp line in inetd.conf; kill
+ -HUP your inetd. (If you are using tcpserver, simply kill -CONT your
+ tcpserver. If you are running a QMTP server, reenable that too.)
+
+10. Read TEST.receive.
+
+
+That's it! To report success:
+ % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to
+Replace First M. Last with your name.
+
+If you have questions about qmail, join the qmail mailing list; see
+http://pobox.com/~djb/qmail.html.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..5ac3967
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+netqmail 1.06
diff --git a/addresses.5 b/addresses.5
new file mode 100644
index 0000000..1fd4c5b
--- /dev/null
+++ b/addresses.5
@@ -0,0 +1,260 @@
+.TH addresses 5
+.SH "NAME"
+addresses \- formats for Internet mail addresses
+.SH "INTRODUCTION"
+A
+.B mail address
+is a string of characters containing @.
+
+Every mail address has a
+.B local part
+and a
+.B domain part\fR.
+The domain part is everything after the final @.
+The local part is everything before.
+
+For example, the mail addresses
+
+.EX
+ God@heaven.af.mil
+ @heaven.af.mil
+ @at@@heaven.af.mil
+.EE
+
+all have domain part
+.BR heaven.af.mil .
+The local parts are
+.BR God ,
+empty,
+and
+.BR @at@ .
+
+Some domains have owners.
+It is up to the owner of
+.B heaven.af.mil
+to say how mail messages will be delivered to addresses with domain part
+.BR heaven.af.mil .
+
+The domain part of an address is interpreted without regard to case, so
+
+.EX
+ God@heaven.af.mil
+.br
+ God@HEAVEN.AF.MIL
+.br
+ God@Heaven.AF.Mil
+.EE
+
+all refer to the same domain.
+
+There is one exceptional address that does not contain an @:
+namely, the empty string.
+The empty string cannot be used as a recipient address.
+It can be used as a sender address so that
+the real sender doesn't receive bounces.
+.SH "QMAIL EXTENSIONS"
+The
+.B qmail
+system allows several further types of addresses in mail envelopes.
+
+First, an envelope recipient address without an @ is interpreted as being at
+.IR envnoathost .
+For example, if
+.I envnoathost
+is
+.BR heaven.af.mil ,
+the address
+.B God
+will be rewritten as
+.BR God@heaven.af.mil .
+
+Second, the address
+.B #@[]
+is used as an envelope sender address for double bounces.
+
+Third, envelope sender addresses of the form
+.I pre\fB@\fIhost\fB-@[]
+are used to support variable envelope return paths (VERPs).
+.B qmail-send
+will rewrite
+.I pre\fB@\fIhost\fB-@[]
+as
+.I prerecip\fB=\fIdomain\fB@\fIhost
+for deliveries to
+.IR recip\fB@\fIdomain .
+Bounces directly from
+.B qmail-send
+will come back to
+.IR pre\fB@\fIhost .
+.SH "CHOOSING MAIL ADDRESSES"
+Here are some suggestions on choosing mail addresses for the Internet.
+
+Do not use non-ASCII characters.
+Under RFC 822 and RFC 821,
+these characters cannot be used in mail headers or in SMTP commands.
+In practice, they are regularly corrupted.
+
+Do not use ASCII control characters.
+NUL is regularly corrupted.
+CR and LF cannot be used in some combinations
+and are corrupted in all.
+None of these characters are usable on business cards.
+
+Avoid spaces and the characters
+
+.EX
+ \\"<>()[],;:
+.EE
+
+These all require quoting in mail headers and in SMTP.
+Many existing mail programs do not handle quoting properly.
+
+Do not use @ in a local part.
+@ requires quoting in mail headers and in SMTP.
+Many programs incorrectly look for the first @,
+rather than the last @,
+to find the domain part of an address.
+
+In a local part,
+do not use two consecutive dots, a dot at the beginning, or a dot at the end.
+Any of these would require quoting in mail headers.
+
+Do not use an empty local part; it cannot appear in SMTP commands.
+
+Avoid local parts longer than 64 characters.
+
+Be wary of uppercase letters in local parts.
+Some mail programs (and users!) will incorrectly convert
+.B God@heaven.af.mil
+to
+.BR god@heaven.af.mil .
+
+Be wary of the following characters:
+
+.EX
+ $&!#~`'^*|{}
+.EE
+
+Some users will not know
+how to feed these characters safely to their mail programs.
+
+In domain names, stick to letters, digits, dash, and dot.
+One popular DNS resolver has,
+under the banner of security,
+recently begun destroying domain names
+that contain certain other characters,
+including underscore.
+Exception: A dotted-decimal IP address in brackets,
+such as
+.BR [127.0.0.1] ,
+identifies a domain owned by whoever owns the host at that IP address,
+and can be used safely.
+
+In a domain name,
+do not use two consecutive dots,
+a dot at the beginning,
+or a dot at the end.
+This means that,
+when a domain name is broken down into components separated by dots,
+there are no empty components.
+
+Always use at least one dot in a domain name.
+If you own the
+.B mil
+domain,
+don't bother using the address
+.BR root@mil ;
+most users will be unable to send messages to that address.
+Same for the root domain.
+
+Avoid domain names longer than 64 characters.
+.SH "ENCODED ADDRESSES IN SMTP COMMANDS"
+RFC 821 defines an encoding of mail addresses in SMTP.
+For example, the addresses
+
+.EX
+ God@heaven.af.mil
+.br
+ a"quote@heaven.af.mil
+.br
+ The Almighty.One@heaven.af.mil
+.EE
+
+could be encoded in RCPT commands as
+
+.EX
+ RCPT TO:<God@heaven.af.mil>
+.br
+ RCPT TO:<a\\"quote@heaven.af.mil>
+.br
+ RCPT TO:<The\\ Almighty.One@heaven.af.mil>
+.EE
+
+There are several restrictions in RFC 821
+on the mail addresses that can be used over SMTP.
+Non-ASCII characters are prohibited.
+The local part must not be empty.
+The domain part must be a sequence of elements separated by dots,
+where each element is either a component,
+a sequence of digits preceded by #,
+or a dotted-decimal IP address surrounded by brackets.
+The only allowable characters in components are
+letters, digits, and dashes.
+Every component must (believe it or not)
+have at least three characters;
+the first character must be a letter;
+the last character must not be a hyphen.
+.SH "ENCODED ADDRESSES IN MAIL HEADERS"
+RFC 822 defines an encoding of mail addresses
+in certain header fields in a mail message.
+For example, the addresses
+
+.EX
+ God@heaven.af.mil
+.br
+ a"quote@heaven.af.mil
+.br
+ The Almighty.One@heaven.af.mil
+.EE
+
+could be encoded in a
+.B To
+field as
+
+.EX
+ To: God@heaven.af.mil,
+.br
+ <@brl.mil:"a\\"quote"@heaven.af.mil>,
+.br
+ "The Almighty".One@heaven.af.mil
+.EE
+
+or perhaps
+
+.EX
+ To: < "God"@heaven .af.mil>,
+.br
+ "a\\"quote" (Who?) @ heaven . af. mil
+.br
+ , God<"The Almighty.One"@heaven.af.mil>
+.EE
+
+There are several restrictions on the mail addresses that can
+be used in these header fields.
+Non-ASCII characters are prohibited.
+The domain part must be a sequence of elements separated by dots,
+where each element either (1) begins with [ and ends with ]
+or (2) is a nonempty string of printable ASCII characters
+not including any of
+
+.EX
+ \\".<>()[],;:
+.EE
+
+and not including space.
+.SH "SEE ALSO"
+envelopes(5),
+qmail-header(5),
+qmail-inject(8),
+qmail-remote(8),
+qmail-smtpd(8)
diff --git a/alloc.3 b/alloc.3
new file mode 100644
index 0000000..58b4432
--- /dev/null
+++ b/alloc.3
@@ -0,0 +1,62 @@
+.TH alloc 3
+.SH NAME
+alloc \- allocate memory
+.SH SYNTAX
+.B #include <alloc.h>
+
+char *\fBalloc\fP(\fInew\fR);
+
+void \fBalloc_free\fP(\fIx\fR);
+
+void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR);
+
+char *\fIx\fR;
+.br
+unsigned int \fIold\fR;
+.br
+unsigned int \fInew\fR;
+.SH DESCRIPTION
+.B alloc
+allocates enough space from the heap for
+.I new
+bytes of data,
+adequately aligned for any data type.
+.I new
+may be 0.
+.B alloc
+returns a pointer to the space.
+If space is not available,
+.B alloc
+returns 0,
+setting
+.B errno
+appropriately.
+
+.B alloc_free
+returns space to the heap.
+
+.B alloc_re
+expands the space allocated to
+.I x
+from
+.I old
+bytes to
+.I new
+bytes.
+It allocates new space,
+copies
+.I old
+bytes from the old space to the new space,
+returns the old space to the heap,
+and changes
+.I x
+to point to the new space.
+It then returns 1.
+If space is not available,
+.B alloc_re
+returns 0,
+leaving the old space alone.
+.SH "SEE ALSO"
+sbrk(2),
+malloc(3),
+error(3)
diff --git a/alloc.c b/alloc.c
new file mode 100644
index 0000000..c661453
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,32 @@
+#include "alloc.h"
+#include "error.h"
+extern char *malloc();
+extern void free();
+
+#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
+#define SPACE 4096 /* must be multiple of ALIGNMENT */
+
+typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
+static aligned realspace[SPACE / ALIGNMENT];
+#define space ((char *) realspace)
+static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
+
+/*@null@*//*@out@*/char *alloc(n)
+unsigned int n;
+{
+ char *x;
+ n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
+ if (n <= avail) { avail -= n; return space + avail; }
+ x = malloc(n);
+ if (!x) errno = error_nomem;
+ return x;
+}
+
+void alloc_free(x)
+char *x;
+{
+ if (x >= space)
+ if (x < space + SPACE)
+ return; /* XXX: assuming that pointers are flat */
+ free(x);
+}
diff --git a/alloc.h b/alloc.h
new file mode 100644
index 0000000..1b1d893
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,8 @@
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+#endif
diff --git a/alloc_re.c b/alloc_re.c
new file mode 100644
index 0000000..feb8b49
--- /dev/null
+++ b/alloc_re.c
@@ -0,0 +1,17 @@
+#include "alloc.h"
+#include "byte.h"
+
+int alloc_re(x,m,n)
+char **x;
+unsigned int m;
+unsigned int n;
+{
+ char *y;
+
+ y = alloc(n);
+ if (!y) return 0;
+ byte_copy(y,m,*x);
+ alloc_free(*x);
+ *x = y;
+ return 1;
+}
diff --git a/auto-gid.c b/auto-gid.c
new file mode 100644
index 0000000..774a8c1
--- /dev/null
+++ b/auto-gid.c
@@ -0,0 +1,51 @@
+#include <sys/types.h>
+#include <grp.h>
+#include "subfd.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "scan.h"
+#include "fmt.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void outs(s)
+char *s;
+{
+ if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *name;
+ char *value;
+ struct group *gr;
+ char strnum[FMT_ULONG];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ gr = getgrnam(value);
+ if (!gr) {
+ substdio_puts(subfderr,"fatal: unable to find group ");
+ substdio_puts(subfderr,value);
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+ _exit(111);
+ }
+
+ strnum[fmt_ulong(strnum,(unsigned long) gr->gr_gid)] = 0;
+
+ outs("int ");
+ outs(name);
+ outs(" = ");
+ outs(strnum);
+ outs(";\n");
+ if (substdio_flush(&ss1) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/auto-int.c b/auto-int.c
new file mode 100644
index 0000000..c138869
--- /dev/null
+++ b/auto-int.c
@@ -0,0 +1,40 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "scan.h"
+#include "fmt.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void puts(s)
+char *s;
+{
+ if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *name;
+ char *value;
+ unsigned long num;
+ char strnum[FMT_ULONG];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ scan_ulong(value,&num);
+ strnum[fmt_ulong(strnum,num)] = 0;
+
+ puts("int ");
+ puts(name);
+ puts(" = ");
+ puts(strnum);
+ puts(";\n");
+ if (substdio_flush(&ss1) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/auto-int8.c b/auto-int8.c
new file mode 100644
index 0000000..091978f
--- /dev/null
+++ b/auto-int8.c
@@ -0,0 +1,40 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "scan.h"
+#include "fmt.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void puts(s)
+char *s;
+{
+ if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *name;
+ char *value;
+ unsigned long num;
+ char strnum[FMT_ULONG];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ scan_8long(value,&num);
+ strnum[fmt_ulong(strnum,num)] = 0;
+
+ puts("int ");
+ puts(name);
+ puts(" = ");
+ puts(strnum);
+ puts(";\n");
+ if (substdio_flush(&ss1) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/auto-str.c b/auto-str.c
new file mode 100644
index 0000000..acc3d60
--- /dev/null
+++ b/auto-str.c
@@ -0,0 +1,44 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void puts(s)
+char *s;
+{
+ if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *name;
+ char *value;
+ unsigned char ch;
+ char octal[4];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ puts("char ");
+ puts(name);
+ puts("[] = \"\\\n");
+
+ while (ch = *value++) {
+ puts("\\");
+ octal[3] = 0;
+ octal[2] = '0' + (ch & 7); ch >>= 3;
+ octal[1] = '0' + (ch & 7); ch >>= 3;
+ octal[0] = '0' + (ch & 7);
+ puts(octal);
+ }
+
+ puts("\\\n\";\n");
+ if (substdio_flush(&ss1) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/auto-uid.c b/auto-uid.c
new file mode 100644
index 0000000..326051c
--- /dev/null
+++ b/auto-uid.c
@@ -0,0 +1,51 @@
+#include <sys/types.h>
+#include <pwd.h>
+#include "subfd.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "scan.h"
+#include "fmt.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void outs(s) /* was named puts, but Solaris pwd.h includes stdio.h. dorks. */
+char *s;
+{
+ if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *name;
+ char *value;
+ struct passwd *pw;
+ char strnum[FMT_ULONG];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ pw = getpwnam(value);
+ if (!pw) {
+ substdio_puts(subfderr,"fatal: unable to find user ");
+ substdio_puts(subfderr,value);
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+ _exit(111);
+ }
+
+ strnum[fmt_ulong(strnum,(unsigned long) pw->pw_uid)] = 0;
+
+ outs("int ");
+ outs(name);
+ outs(" = ");
+ outs(strnum);
+ outs(";\n");
+ if (substdio_flush(&ss1) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/auto_break.h b/auto_break.h
new file mode 100644
index 0000000..b7f3a63
--- /dev/null
+++ b/auto_break.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_BREAK_H
+#define AUTO_BREAK_H
+
+extern char auto_break[];
+
+#endif
diff --git a/auto_patrn.h b/auto_patrn.h
new file mode 100644
index 0000000..77cdf1f
--- /dev/null
+++ b/auto_patrn.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_PATRN_H
+#define AUTO_PATRN_H
+
+extern int auto_patrn;
+
+#endif
diff --git a/auto_qmail.h b/auto_qmail.h
new file mode 100644
index 0000000..0c56001
--- /dev/null
+++ b/auto_qmail.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_QMAIL_H
+#define AUTO_QMAIL_H
+
+extern char auto_qmail[];
+
+#endif
diff --git a/auto_spawn.h b/auto_spawn.h
new file mode 100644
index 0000000..165d988
--- /dev/null
+++ b/auto_spawn.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_SPAWN_H
+#define AUTO_SPAWN_H
+
+extern int auto_spawn;
+
+#endif
diff --git a/auto_split.h b/auto_split.h
new file mode 100644
index 0000000..3754129
--- /dev/null
+++ b/auto_split.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_SPLIT_H
+#define AUTO_SPLIT_H
+
+extern int auto_split;
+
+#endif
diff --git a/auto_uids.h b/auto_uids.h
new file mode 100644
index 0000000..1252ecb
--- /dev/null
+++ b/auto_uids.h
@@ -0,0 +1,16 @@
+#ifndef AUTO_UIDS_H
+#define AUTO_UIDS_H
+
+extern int auto_uida;
+extern int auto_uidd;
+extern int auto_uidl;
+extern int auto_uido;
+extern int auto_uidp;
+extern int auto_uidq;
+extern int auto_uidr;
+extern int auto_uids;
+
+extern int auto_gidn;
+extern int auto_gidq;
+
+#endif
diff --git a/auto_usera.h b/auto_usera.h
new file mode 100644
index 0000000..49d7755
--- /dev/null
+++ b/auto_usera.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_USERA_H
+#define AUTO_USERA_H
+
+extern char auto_usera[];
+
+#endif
diff --git a/binm1+df.sh b/binm1+df.sh
new file mode 100644
index 0000000..0ff1a68
--- /dev/null
+++ b/binm1+df.sh
@@ -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
diff --git a/binm1.sh b/binm1.sh
new file mode 100644
index 0000000..db79cbd
--- /dev/null
+++ b/binm1.sh
@@ -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
diff --git a/binm2+df.sh b/binm2+df.sh
new file mode 100644
index 0000000..4f78101
--- /dev/null
+++ b/binm2+df.sh
@@ -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
diff --git a/binm2.sh b/binm2.sh
new file mode 100644
index 0000000..7905308
--- /dev/null
+++ b/binm2.sh
@@ -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
diff --git a/binm3+df.sh b/binm3+df.sh
new file mode 100644
index 0000000..3d69401
--- /dev/null
+++ b/binm3+df.sh
@@ -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
diff --git a/binm3.sh b/binm3.sh
new file mode 100644
index 0000000..eb139e6
--- /dev/null
+++ b/binm3.sh
@@ -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
diff --git a/bouncesaying.1 b/bouncesaying.1
new file mode 100644
index 0000000..d99a460
--- /dev/null
+++ b/bouncesaying.1
@@ -0,0 +1,71 @@
+.TH bouncesaying 1
+.SH NAME
+bouncesaying \- perhaps bounce each incoming message
+.SH SYNOPSIS
+in
+.BR .qmail :
+.B |bouncesaying
+.I error
+[
+.I program
+[
+.I arg ...
+]
+]
+.SH DESCRIPTION
+.B bouncesaying
+feeds each new mail message to
+.I program
+with the given arguments.
+If
+.I program
+exits 0,
+.B bouncesaying
+prints
+.I error
+and bounces the message.
+
+If
+.I program
+exits 111,
+.B bouncesaying
+exits 111,
+so delivery will be retried later.
+
+If
+.I program
+exits anything else
+(or does not exist),
+.B bouncesaying
+exits 0,
+so the rest of
+.B .qmail
+will be processed as usual.
+
+Note that
+it is not safe for
+.I program
+to fork a child that
+reads the message in the background.
+
+If
+.I program
+is not supplied,
+.B bouncesaying
+always bounces the message:
+
+.EX
+ |bouncesaying 'This address no longer accepts mail.'
+.EE
+
+.B WARNING:
+If you create a
+.B .qmail
+file to enable
+.BR bouncesaying ,
+make sure to also add a line specifying delivery to your normal mailbox.
+.SH "SEE ALSO"
+condredirect(1),
+except(1),
+dot-qmail(5),
+qmail-command(8)
diff --git a/bouncesaying.c b/bouncesaying.c
new file mode 100644
index 0000000..c7d0026
--- /dev/null
+++ b/bouncesaying.c
@@ -0,0 +1,41 @@
+#include "fork.h"
+#include "strerr.h"
+#include "error.h"
+#include "wait.h"
+#include "sig.h"
+#include "exit.h"
+
+#define FATAL "bouncesaying: fatal: "
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int pid;
+ int wstat;
+
+ if (!argv[1])
+ strerr_die1x(100,"bouncesaying: usage: bouncesaying error [ program [ arg ... ] ]");
+
+ if (argv[2]) {
+ pid = fork();
+ if (pid == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+ if (pid == 0) {
+ execvp(argv[2],argv + 2);
+ if (error_temp(errno)) _exit(111);
+ _exit(100);
+ }
+ if (wait_pid(&wstat,pid) == -1)
+ strerr_die2x(111,FATAL,"wait failed");
+ if (wait_crashed(wstat))
+ strerr_die2x(111,FATAL,"child crashed");
+ switch(wait_exitcode(wstat)) {
+ case 0: break;
+ case 111: strerr_die2x(111,FATAL,"temporary child error");
+ default: _exit(0);
+ }
+ }
+
+ strerr_die1x(100,argv[1]);
+}
diff --git a/byte.h b/byte.h
new file mode 100644
index 0000000..de06c69
--- /dev/null
+++ b/byte.h
@@ -0,0 +1,13 @@
+#ifndef BYTE_H
+#define BYTE_H
+
+extern unsigned int byte_chr();
+extern unsigned int byte_rchr();
+extern void byte_copy();
+extern void byte_copyr();
+extern int byte_diff();
+extern void byte_zero();
+
+#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
+
+#endif
diff --git a/byte_chr.c b/byte_chr.c
new file mode 100644
index 0000000..f81dde8
--- /dev/null
+++ b/byte_chr.c
@@ -0,0 +1,20 @@
+#include "byte.h"
+
+unsigned int byte_chr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ }
+ return t - s;
+}
diff --git a/byte_copy.c b/byte_copy.c
new file mode 100644
index 0000000..eaad11b
--- /dev/null
+++ b/byte_copy.c
@@ -0,0 +1,14 @@
+#include "byte.h"
+
+void byte_copy(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+ for (;;) {
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ }
+}
diff --git a/byte_cr.c b/byte_cr.c
new file mode 100644
index 0000000..3e7a1d5
--- /dev/null
+++ b/byte_cr.c
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+void byte_copyr(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+ to += n;
+ from += n;
+ for (;;) {
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ }
+}
diff --git a/byte_diff.c b/byte_diff.c
new file mode 100644
index 0000000..cdbd760
--- /dev/null
+++ b/byte_diff.c
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+int byte_diff(s,n,t)
+register char *s;
+register unsigned int n;
+register char *t;
+{
+ for (;;) {
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ }
+ return ((int)(unsigned int)(unsigned char) *s)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/byte_rchr.c b/byte_rchr.c
new file mode 100644
index 0000000..476bc22
--- /dev/null
+++ b/byte_rchr.c
@@ -0,0 +1,23 @@
+#include "byte.h"
+
+unsigned int byte_rchr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+ register char ch;
+ register char *t;
+ register char *u;
+
+ ch = c;
+ t = s;
+ u = 0;
+ for (;;) {
+ if (!n) break; if (*t == ch) u = t; ++t; --n;
+ if (!n) break; if (*t == ch) u = t; ++t; --n;
+ if (!n) break; if (*t == ch) u = t; ++t; --n;
+ if (!n) break; if (*t == ch) u = t; ++t; --n;
+ }
+ if (!u) u = t;
+ return u - s;
+}
diff --git a/byte_zero.c b/byte_zero.c
new file mode 100644
index 0000000..92009ba
--- /dev/null
+++ b/byte_zero.c
@@ -0,0 +1,13 @@
+#include "byte.h"
+
+void byte_zero(s,n)
+char *s;
+register unsigned int n;
+{
+ for (;;) {
+ if (!n) break; *s++ = 0; --n;
+ if (!n) break; *s++ = 0; --n;
+ if (!n) break; *s++ = 0; --n;
+ if (!n) break; *s++ = 0; --n;
+ }
+}
diff --git a/case.3 b/case.3
new file mode 100644
index 0000000..58bd724
--- /dev/null
+++ b/case.3
@@ -0,0 +1,100 @@
+.TH case 3
+.SH NAME
+case \- convert ASCII uppercase bytes to lowercase
+.SH SYNTAX
+.B #include <case.h>
+
+void \fBcase_lowers\fP(\fIs\fR);
+.br
+void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR);
+
+int \fBcase_diffs\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_equals\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_starts\fP(\fIs\fR,\fIt\fR);
+
+int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
+.br
+int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
+
+char *\fIs\fR;
+.br
+char *\fIt\fR;
+.br
+unsigned int \fIlen\fR;
+.SH DESCRIPTION
+.B case_lowers
+converts each uppercase byte in the string
+.I s
+to lowercase.
+.I s
+must be 0-terminated.
+
+.B case_lowerb
+converts each uppercase byte in the buffer
+.IR s ,
+of length
+.IR len ,
+to lowercase.
+
+.B case_diffs
+lexicographically compares lowercase versions of the strings
+.I s
+and
+.IR t .
+It returns something positive, negative, or zero
+when the first is larger than, smaller than, or equal to the second.
+.I s
+and
+.I t
+must be 0-terminated.
+
+.B case_equals
+means
+.BR !case_diffs .
+
+.B case_starts
+returns 1 if a lowercase version of
+.I s
+starts with a lowercase version of
+.IR t .
+.I s
+and
+.I t
+must be 0-terminated.
+
+.B case_diffb
+lexicographically compares lowercase versions of the buffers
+.I s
+and
+.IR t ,
+each of length
+.IR len .
+It returns something positive, negative, or zero
+when the first is larger than, smaller than, or equal to the second.
+
+.B case_startb
+returns 1 if a lowercase version of the buffer
+.IR s ,
+of length
+.IR len ,
+starts with a lowercase version of the string
+.IR t .
+.I t
+must be 0-terminated.
+
+The
+.B case
+routines
+are ASCII-specific.
+They are suitable for programs that handle
+case-independent networking protocols.
+
+All comparisons are performed on unsigned bytes.
+.SH "SEE ALSO"
+byte_diff(3),
+byte_equal(3),
+str_diff(3),
+str_equal(3),
+str_start(3)
diff --git a/case.h b/case.h
new file mode 100644
index 0000000..35a2434
--- /dev/null
+++ b/case.h
@@ -0,0 +1,13 @@
+#ifndef CASE_H
+#define CASE_H
+
+extern void case_lowers();
+extern void case_lowerb();
+extern int case_diffs();
+extern int case_diffb();
+extern int case_starts();
+extern int case_startb();
+
+#define case_equals(s,t) (!case_diffs((s),(t)))
+
+#endif
diff --git a/case_diffb.c b/case_diffb.c
new file mode 100644
index 0000000..9064b8a
--- /dev/null
+++ b/case_diffb.c
@@ -0,0 +1,21 @@
+#include "case.h"
+
+int case_diffb(s,len,t)
+register char *s;
+unsigned int len;
+register char *t;
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ while (len > 0) {
+ --len;
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y)
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+ }
+ return 0;
+}
diff --git a/case_diffs.c b/case_diffs.c
new file mode 100644
index 0000000..212c645
--- /dev/null
+++ b/case_diffs.c
@@ -0,0 +1,19 @@
+#include "case.h"
+
+int case_diffs(s,t)
+register char *s;
+register char *t;
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y) break;
+ if (!x) break;
+ }
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+}
diff --git a/case_lowerb.c b/case_lowerb.c
new file mode 100644
index 0000000..4034c14
--- /dev/null
+++ b/case_lowerb.c
@@ -0,0 +1,14 @@
+#include "case.h"
+
+void case_lowerb(s,len)
+char *s;
+unsigned int len;
+{
+ unsigned char x;
+ while (len > 0) {
+ --len;
+ x = *s - 'A';
+ if (x <= 'Z' - 'A') *s = x + 'a';
+ ++s;
+ }
+}
diff --git a/case_lowers.c b/case_lowers.c
new file mode 100644
index 0000000..208b3f5
--- /dev/null
+++ b/case_lowers.c
@@ -0,0 +1,12 @@
+#include "case.h"
+
+void case_lowers(s)
+char *s;
+{
+ unsigned char x;
+ while (x = *s) {
+ x -= 'A';
+ if (x <= 'Z' - 'A') *s = x + 'a';
+ ++s;
+ }
+}
diff --git a/case_starts.c b/case_starts.c
new file mode 100644
index 0000000..2278a0a
--- /dev/null
+++ b/case_starts.c
@@ -0,0 +1,18 @@
+#include "case.h"
+
+int case_starts(s,t)
+register char *s;
+register char *t;
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (!y) return 1;
+ if (x != y) return 0;
+ }
+}
diff --git a/cdb.3 b/cdb.3
new file mode 100644
index 0000000..a85b34c
--- /dev/null
+++ b/cdb.3
@@ -0,0 +1,62 @@
+.TH cdb 3
+.SH NAME
+cdb \- read from a constant database
+.SH SYNTAX
+.B #include <cdb.h>
+
+int \fBcdb_seek(\fP\fIfd,key,len,dlen\fR\fB)\fP;
+
+int \fIfd\fR;
+.br
+char *\fIkey\fR;
+.br
+unsigned int \fIlen\fR;
+.br
+uint32 *\fIdlen\fR;
+.SH DESCRIPTION
+.B cdb_seek
+looks up
+.I key
+in a constant database.
+It returns 1 if
+.I key
+is present,
+0 if
+.I key
+is not present,
+or \-1 if there was a read error.
+.I key
+is an array of
+.I len
+characters.
+
+.B cdb_seek
+needs an open file descriptor,
+.IR fd ,
+pointing to the database.
+If
+.B cdb_seek
+returns 1,
+it points
+.I fd
+at the beginning of the data portion of the first record
+indexed by
+.IR key ,
+and it stores the data length in
+.IR dlen.
+.B cdb_seek
+does not provide a way to read subsequent records with the same key.
+
+It's fine to do several
+.B cdb_seek
+lookups with the same open file descriptor.
+Beware, however, that two simultaneous
+.B cdb_seek
+lookups can fail horribly;
+separate processes should not share the same database descriptor.
+Furthermore, any updates after the database was opened
+will be invisible.
+It's rarely a good idea for a long-running program
+to hold a database open.
+.SH "SEE ALSO"
+cdbget(1)
diff --git a/cdb.h b/cdb.h
new file mode 100644
index 0000000..571e5d6
--- /dev/null
+++ b/cdb.h
@@ -0,0 +1,12 @@
+#ifndef CDB_H
+#define CDB_H
+
+#include "uint32.h"
+
+extern uint32 cdb_hash();
+extern uint32 cdb_unpack();
+
+extern int cdb_bread();
+extern int cdb_seek();
+
+#endif
diff --git a/cdb_hash.c b/cdb_hash.c
new file mode 100644
index 0000000..8238020
--- /dev/null
+++ b/cdb_hash.c
@@ -0,0 +1,16 @@
+#include "cdb.h"
+
+uint32 cdb_hash(buf,len)
+unsigned char *buf;
+unsigned int len;
+{
+ uint32 h;
+
+ h = 5381;
+ while (len) {
+ --len;
+ h += (h << 5);
+ h ^= (uint32) *buf++;
+ }
+ return h;
+}
diff --git a/cdb_seek.c b/cdb_seek.c
new file mode 100644
index 0000000..f31b87d
--- /dev/null
+++ b/cdb_seek.c
@@ -0,0 +1,94 @@
+#include <sys/types.h>
+#include <errno.h>
+#include "cdb.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+int cdb_bread(fd,buf,len)
+int fd;
+char *buf;
+int len;
+{
+ int r;
+ while (len > 0) {
+ do
+ r = read(fd,buf,len);
+ while ((r == -1) && (errno == EINTR));
+ if (r == -1) return -1;
+ if (r == 0) { errno = EIO; return -1; }
+ buf += r;
+ len -= r;
+ }
+ return 0;
+}
+
+static int match(fd,key,len)
+int fd;
+char *key;
+unsigned int len;
+{
+ char buf[32];
+ int n;
+ int i;
+
+ while (len > 0) {
+ n = sizeof(buf);
+ if (n > len) n = len;
+ if (cdb_bread(fd,buf,n) == -1) return -1;
+ for (i = 0;i < n;++i) if (buf[i] != key[i]) return 0;
+ key += n;
+ len -= n;
+ }
+ return 1;
+}
+
+int cdb_seek(fd,key,len,dlen)
+int fd;
+char *key;
+unsigned int len;
+uint32 *dlen;
+{
+ char packbuf[8];
+ uint32 pos;
+ uint32 h;
+ uint32 lenhash;
+ uint32 h2;
+ uint32 loop;
+ uint32 poskd;
+
+ h = cdb_hash(key,len);
+
+ pos = 8 * (h & 255);
+ if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1;
+
+ if (cdb_bread(fd,packbuf,8) == -1) return -1;
+
+ pos = cdb_unpack(packbuf);
+ lenhash = cdb_unpack(packbuf + 4);
+
+ if (!lenhash) return 0;
+ h2 = (h >> 8) % lenhash;
+
+ for (loop = 0;loop < lenhash;++loop) {
+ if (lseek(fd,(off_t) (pos + 8 * h2),SEEK_SET) == -1) return -1;
+ if (cdb_bread(fd,packbuf,8) == -1) return -1;
+ poskd = cdb_unpack(packbuf + 4);
+ if (!poskd) return 0;
+ if (cdb_unpack(packbuf) == h) {
+ if (lseek(fd,(off_t) poskd,SEEK_SET) == -1) return -1;
+ if (cdb_bread(fd,packbuf,8) == -1) return -1;
+ if (cdb_unpack(packbuf) == len)
+ switch(match(fd,key,len)) {
+ case -1:
+ return -1;
+ case 1:
+ *dlen = cdb_unpack(packbuf + 4);
+ return 1;
+ }
+ }
+ if (++h2 == lenhash) h2 = 0;
+ }
+ return 0;
+}
diff --git a/cdb_unpack.c b/cdb_unpack.c
new file mode 100644
index 0000000..c882202
--- /dev/null
+++ b/cdb_unpack.c
@@ -0,0 +1,12 @@
+#include "cdb.h"
+
+uint32 cdb_unpack(buf)
+unsigned char *buf;
+{
+ uint32 num;
+ num = buf[3]; num <<= 8;
+ num += buf[2]; num <<= 8;
+ num += buf[1]; num <<= 8;
+ num += buf[0];
+ return num;
+}
diff --git a/cdbmake.h b/cdbmake.h
new file mode 100644
index 0000000..883a231
--- /dev/null
+++ b/cdbmake.h
@@ -0,0 +1,35 @@
+#ifndef CDBMAKE_H
+#define CDBMAKE_H
+
+#include "uint32.h"
+
+#define CDBMAKE_HPLIST 1000
+
+struct cdbmake_hp { uint32 h; uint32 p; } ;
+
+struct cdbmake_hplist {
+ struct cdbmake_hp hp[CDBMAKE_HPLIST];
+ struct cdbmake_hplist *next;
+ int num;
+} ;
+
+struct cdbmake {
+ char final[2048];
+ uint32 count[256];
+ uint32 start[256];
+ struct cdbmake_hplist *head;
+ struct cdbmake_hp *split; /* includes space for hash */
+ struct cdbmake_hp *hash;
+ uint32 numentries;
+} ;
+
+extern void cdbmake_pack();
+#define CDBMAKE_HASHSTART ((uint32) 5381)
+extern uint32 cdbmake_hashadd();
+
+extern void cdbmake_init();
+extern int cdbmake_add();
+extern int cdbmake_split();
+extern uint32 cdbmake_throw();
+
+#endif
diff --git a/cdbmake_add.c b/cdbmake_add.c
new file mode 100644
index 0000000..83a03ff
--- /dev/null
+++ b/cdbmake_add.c
@@ -0,0 +1,118 @@
+#include "alloc.h"
+#include "cdbmake.h"
+
+void cdbmake_init(cdbm)
+struct cdbmake *cdbm;
+{
+ cdbm->head = 0;
+ cdbm->split = 0;
+ cdbm->hash = 0;
+ cdbm->numentries = 0;
+}
+
+int cdbmake_add(cdbm,h,p,alloc)
+struct cdbmake *cdbm;
+uint32 h;
+uint32 p;
+char *(*alloc)();
+{
+ struct cdbmake_hplist *head;
+
+ head = cdbm->head;
+ if (!head || (head->num >= CDBMAKE_HPLIST)) {
+ head = (struct cdbmake_hplist *) alloc(sizeof(struct cdbmake_hplist));
+ if (!head) return 0;
+ head->num = 0;
+ head->next = cdbm->head;
+ cdbm->head = head;
+ }
+ head->hp[head->num].h = h;
+ head->hp[head->num].p = p;
+ ++head->num;
+ ++cdbm->numentries;
+ return 1;
+}
+
+int cdbmake_split(cdbm,alloc)
+struct cdbmake *cdbm;
+char *(*alloc)();
+{
+ int i;
+ uint32 u;
+ uint32 memsize;
+ struct cdbmake_hplist *x;
+
+ for (i = 0;i < 256;++i)
+ cdbm->count[i] = 0;
+
+ for (x = cdbm->head;x;x = x->next) {
+ i = x->num;
+ while (i--)
+ ++cdbm->count[255 & x->hp[i].h];
+ }
+
+ memsize = 1;
+ for (i = 0;i < 256;++i) {
+ u = cdbm->count[i] * 2;
+ if (u > memsize)
+ memsize = u;
+ }
+
+ memsize += cdbm->numentries; /* no overflow possible up to now */
+ u = (uint32) 0 - (uint32) 1;
+ u /= sizeof(struct cdbmake_hp);
+ if (memsize > u) return 0;
+
+ cdbm->split = (struct cdbmake_hp *) alloc(memsize * sizeof(struct cdbmake_hp));
+ if (!cdbm->split) return 0;
+
+ cdbm->hash = cdbm->split + cdbm->numentries;
+
+ u = 0;
+ for (i = 0;i < 256;++i) {
+ u += cdbm->count[i]; /* bounded by numentries, so no overflow */
+ cdbm->start[i] = u;
+ }
+
+ for (x = cdbm->head;x;x = x->next) {
+ i = x->num;
+ while (i--)
+ cdbm->split[--cdbm->start[255 & x->hp[i].h]] = x->hp[i];
+ }
+
+ return 1;
+}
+
+uint32 cdbmake_throw(cdbm,pos,b)
+struct cdbmake *cdbm;
+uint32 pos;
+int b;
+{
+ uint32 len;
+ uint32 j;
+ uint32 count;
+ struct cdbmake_hp *hp;
+ uint32 where;
+
+ count = cdbm->count[b];
+
+ len = count + count; /* no overflow possible */
+ cdbmake_pack(cdbm->final + 8 * b,pos);
+ cdbmake_pack(cdbm->final + 8 * b + 4,len);
+
+ if (len) {
+ for (j = 0;j < len;++j)
+ cdbm->hash[j].h = cdbm->hash[j].p = 0;
+
+ hp = cdbm->split + cdbm->start[b];
+ for (j = 0;j < count;++j) {
+ where = (hp->h >> 8) % len;
+ while (cdbm->hash[where].p)
+ if (++where == len)
+ where = 0;
+ cdbm->hash[where] = *hp++;
+ }
+ }
+
+ return len;
+}
diff --git a/cdbmake_hash.c b/cdbmake_hash.c
new file mode 100644
index 0000000..f9dc3e5
--- /dev/null
+++ b/cdbmake_hash.c
@@ -0,0 +1,10 @@
+#include "cdbmake.h"
+
+uint32 cdbmake_hashadd(h,c)
+uint32 h;
+unsigned int c;
+{
+ h += (h << 5);
+ h ^= (uint32) (unsigned char) c;
+ return h;
+}
diff --git a/cdbmake_pack.c b/cdbmake_pack.c
new file mode 100644
index 0000000..04b5f5b
--- /dev/null
+++ b/cdbmake_pack.c
@@ -0,0 +1,11 @@
+#include "cdbmake.h"
+
+void cdbmake_pack(buf,num)
+unsigned char *buf;
+uint32 num;
+{
+ *buf++ = num; num >>= 8;
+ *buf++ = num; num >>= 8;
+ *buf++ = num; num >>= 8;
+ *buf = num;
+}
diff --git a/cdbmss.c b/cdbmss.c
new file mode 100644
index 0000000..2d8f367
--- /dev/null
+++ b/cdbmss.c
@@ -0,0 +1,65 @@
+#include "readwrite.h"
+#include "seek.h"
+#include "alloc.h"
+#include "cdbmss.h"
+
+int cdbmss_start(c,fd)
+struct cdbmss *c;
+int fd;
+{
+ cdbmake_init(&c->cdbm);
+ c->fd = fd;
+ c->pos = sizeof(c->cdbm.final);
+ substdio_fdbuf(&c->ss,write,fd,c->ssbuf,sizeof(c->ssbuf));
+ return seek_set(fd,(seek_pos) c->pos);
+}
+
+int cdbmss_add(c,key,keylen,data,datalen)
+struct cdbmss *c;
+unsigned char *key;
+unsigned int keylen;
+unsigned char *data;
+unsigned int datalen;
+{
+ uint32 h;
+ int i;
+
+ cdbmake_pack(c->packbuf,(uint32) keylen);
+ cdbmake_pack(c->packbuf + 4,(uint32) datalen);
+ if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
+ if (substdio_put(&c->ss,key,keylen) == -1) return -1;
+ if (substdio_put(&c->ss,data,datalen) == -1) return -1;
+
+ h = CDBMAKE_HASHSTART;
+ for (i = 0;i < keylen;++i)
+ h = cdbmake_hashadd(h,(unsigned int) key[i]);
+
+ if (!cdbmake_add(&c->cdbm,h,c->pos,alloc)) return -1;
+
+ c->pos += 8 + keylen + datalen; /* XXX: overflow? */
+ return 0;
+}
+
+int cdbmss_finish(c)
+struct cdbmss *c;
+{
+ int i;
+ uint32 len;
+ uint32 u;
+
+ if (!cdbmake_split(&c->cdbm,alloc)) return -1;
+
+ for (i = 0;i < 256;++i) {
+ len = cdbmake_throw(&c->cdbm,c->pos,i);
+ for (u = 0;u < len;++u) {
+ cdbmake_pack(c->packbuf,c->cdbm.hash[u].h);
+ cdbmake_pack(c->packbuf + 4,c->cdbm.hash[u].p);
+ if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
+ c->pos += 8; /* XXX: overflow? */
+ }
+ }
+
+ if (substdio_flush(&c->ss) == -1) return -1;
+ if (seek_begin(c->fd) == -1) return -1;
+ return substdio_putflush(&c->ss,c->cdbm.final,sizeof(c->cdbm.final));
+}
diff --git a/cdbmss.h b/cdbmss.h
new file mode 100644
index 0000000..5e6bdf4
--- /dev/null
+++ b/cdbmss.h
@@ -0,0 +1,16 @@
+#ifndef CDBMSS_H
+#define CDBMSS_H
+
+#include "cdbmake.h"
+#include "substdio.h"
+
+struct cdbmss {
+ char ssbuf[1024];
+ struct cdbmake cdbm;
+ substdio ss;
+ char packbuf[8];
+ uint32 pos;
+ int fd;
+} ;
+
+#endif
diff --git a/chkshsgr.c b/chkshsgr.c
new file mode 100644
index 0000000..2bcdade
--- /dev/null
+++ b/chkshsgr.c
@@ -0,0 +1,9 @@
+#include "exit.h"
+void main()
+{
+ short x[4];
+
+ x[0] = x[1] = 0;
+ if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
+ _exit(0);
+}
diff --git a/chkspawn.c b/chkspawn.c
new file mode 100644
index 0000000..d19259e
--- /dev/null
+++ b/chkspawn.c
@@ -0,0 +1,48 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "fmt.h"
+#include "select.h"
+#include "exit.h"
+#include "auto_spawn.h"
+
+char num[FMT_ULONG];
+fd_set fds;
+
+void main()
+{
+ unsigned long hiddenlimit;
+ unsigned long maxnumd;
+
+ hiddenlimit = sizeof(fds) * 8;
+ maxnumd = (hiddenlimit - 5) / 2;
+
+ if (auto_spawn < 1) {
+ substdio_puts(subfderr,"Oops. You have set conf-spawn lower than 1.\n");
+ substdio_flush(subfderr);
+ _exit(1);
+ }
+
+ if (auto_spawn > 255) {
+ substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n");
+ substdio_flush(subfderr);
+ _exit(1);
+ }
+
+ if (auto_spawn > maxnumd) {
+ substdio_puts(subfderr,"Oops. Your system's FD_SET() has a hidden limit of ");
+ substdio_put(subfderr,num,fmt_ulong(num,hiddenlimit));
+ substdio_puts(subfderr," descriptors.\n\
+This means that the qmail daemons could crash if you set the run-time\n\
+concurrency higher than ");
+ substdio_put(subfderr,num,fmt_ulong(num,maxnumd));
+ substdio_puts(subfderr,". So I'm going to insist that the concurrency\n\
+limit in conf-spawn be at most ");
+ substdio_put(subfderr,num,fmt_ulong(num,maxnumd));
+ substdio_puts(subfderr,". Right now it's ");
+ substdio_put(subfderr,num,fmt_ulong(num,(unsigned long) auto_spawn));
+ substdio_puts(subfderr,".\n");
+ substdio_flush(subfderr);
+ _exit(1);
+ }
+ _exit(0);
+}
diff --git a/coe.3 b/coe.3
new file mode 100644
index 0000000..06591b6
--- /dev/null
+++ b/coe.3
@@ -0,0 +1,25 @@
+.TH coe 3
+.SH NAME
+coe \- set close-on-exec flag for a descriptor
+.SH SYNTAX
+.B #include <coe.h>
+
+int \fBcoe\fP(\fIfd\fR);
+
+int \fIfd\fR;
+.SH DESCRIPTION
+.B coe
+sets the close-on-exec flag for
+file descriptor
+.IR fd ,
+returning 0 if it was successful
+or -1 on error.
+If
+.B coe
+is successful,
+.I fd
+will be closed when the process calls
+.BR execve .
+.SH "SEE ALSO"
+execve(2),
+fcntl(2)
diff --git a/coe.c b/coe.c
new file mode 100644
index 0000000..d855158
--- /dev/null
+++ b/coe.c
@@ -0,0 +1,8 @@
+#include <fcntl.h>
+#include "coe.h"
+
+int coe(fd)
+int fd;
+{
+ return fcntl(fd,F_SETFD,1);
+}
diff --git a/coe.h b/coe.h
new file mode 100644
index 0000000..1559bc1
--- /dev/null
+++ b/coe.h
@@ -0,0 +1,6 @@
+#ifndef COE_H
+#define COE_H
+
+extern int coe();
+
+#endif
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..b0d3f61
--- /dev/null
+++ b/commands.c
@@ -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();
+ }
+}
diff --git a/commands.h b/commands.h
new file mode 100644
index 0000000..da05a8d
--- /dev/null
+++ b/commands.h
@@ -0,0 +1,12 @@
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+struct commands {
+ char *text;
+ void (*fun)();
+ void (*flush)();
+} ;
+
+extern int commands();
+
+#endif
diff --git a/condredirect.1 b/condredirect.1
new file mode 100644
index 0000000..9f9d3c4
--- /dev/null
+++ b/condredirect.1
@@ -0,0 +1,63 @@
+.TH condredirect 1
+.SH NAME
+condredirect \- perhaps redirect mail to another address
+.SH SYNOPSIS
+in
+.BR .qmail :
+.B |condredirect
+.I newaddress
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B condredirect
+feeds each new mail message to
+.I program
+with the given arguments.
+If
+.I program
+exits 0,
+.B condredirect
+forwards the mail message to
+.IR newaddress ,
+and then exits 99,
+so further commands in
+.B .qmail
+are ignored.
+
+If
+.I program
+exits 111,
+.B condredirect
+exits 111,
+so delivery will be retried later.
+
+If
+.I program
+exits anything else
+(or does not exist),
+.B condredirect
+exits 0,
+so the rest of
+.B .qmail
+will be processed as usual.
+
+Note that
+it is not safe for
+.I program
+to fork a child that
+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"
+bouncesaying(1),
+except(1),
+dot-qmail(5),
+qmail-command(8),
+qmail-queue(8)
diff --git a/condredirect.c b/condredirect.c
new file mode 100644
index 0000000..593d2f5
--- /dev/null
+++ b/condredirect.c
@@ -0,0 +1,85 @@
+#include "sig.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "env.h"
+#include "error.h"
+#include "fork.h"
+#include "wait.h"
+#include "seek.h"
+#include "qmail.h"
+#include "strerr.h"
+#include "substdio.h"
+#include "fmt.h"
+
+#define FATAL "condredirect: fatal: "
+
+struct qmail qqt;
+
+int mywrite(fd,buf,len) int fd; char *buf; int len;
+{
+ qmail_put(&qqt,buf,len);
+ return len;
+}
+
+char inbuf[SUBSTDIO_INSIZE];
+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)
+int argc;
+char **argv;
+{
+ char *sender;
+ char *dtline;
+ int pid;
+ int wstat;
+ char *qqx;
+
+ if (!argv[1] || !argv[2])
+ strerr_die1x(100,"condredirect: usage: condredirect newaddress program [ arg ... ]");
+
+ pid = fork();
+ if (pid == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+ if (pid == 0) {
+ execvp(argv[2],argv + 2);
+ if (error_temp(errno)) _exit(111);
+ _exit(100);
+ }
+ if (wait_pid(&wstat,pid) == -1)
+ strerr_die2x(111,FATAL,"wait failed");
+ if (wait_crashed(wstat))
+ strerr_die2x(111,FATAL,"child crashed");
+ switch(wait_exitcode(wstat)) {
+ case 0: break;
+ case 111: strerr_die2x(111,FATAL,"temporary child error");
+ default: _exit(0);
+ }
+
+ if (seek_begin(0) == -1)
+ strerr_die2sys(111,FATAL,"unable to rewind: ");
+ sig_pipeignore();
+
+ sender = env_get("SENDER");
+ if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
+ dtline = env_get("DTLINE");
+ if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set");
+
+ if (qmail_open(&qqt) == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+ qmail_puts(&qqt,dtline);
+ if (substdio_copy(&ssout,&ssin) != 0)
+ strerr_die2sys(111,FATAL,"unable to read message: ");
+ substdio_flush(&ssout);
+
+ num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
+
+ qmail_from(&qqt,sender);
+ qmail_to(&qqt,argv[1]);
+ qqx = qmail_close(&qqt);
+ if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
+ strerr_die2x(99,"condredirect: qp ",num);
+}
diff --git a/conf-break b/conf-break
new file mode 100644
index 0000000..28fd977
--- /dev/null
+++ b/conf-break
@@ -0,0 +1,9 @@
+-
+
+This character is the user-ext delimiter. The default delimiter is -,
+meaning that user joe controls joe-anything. Some system administrators
+prefer + or =.
+
+You can override this choice at run time with the qmail-users mechanism.
+
+Multicharacter delimiters are not permitted.
diff --git a/conf-cc b/conf-cc
new file mode 100644
index 0000000..e58fb9b
--- /dev/null
+++ b/conf-cc
@@ -0,0 +1,3 @@
+cc -O2
+
+This will be used to compile .c files.
diff --git a/conf-groups b/conf-groups
new file mode 100644
index 0000000..cec0845
--- /dev/null
+++ b/conf-groups
@@ -0,0 +1,6 @@
+qmail
+nofiles
+
+These are the qmail groups. The second group should not have access to
+any files, but it must be usable for processes; this requirement
+excludes the ``nogroup'' and ``nobody'' groups on many systems.
diff --git a/conf-ld b/conf-ld
new file mode 100644
index 0000000..a9e796a
--- /dev/null
+++ b/conf-ld
@@ -0,0 +1,3 @@
+cc -s
+
+This will be used to link .o files into an executable.
diff --git a/conf-patrn b/conf-patrn
new file mode 100644
index 0000000..70c72af
--- /dev/null
+++ b/conf-patrn
@@ -0,0 +1,6 @@
+002
+
+These stat bits are not allowed in ~ and ~/.qmail. On most systems, the
+default umask is 022 or 077, so 022 will work here.
+
+Note that ~ftp, ~www, ~uucp, etc. should be owned by root.
diff --git a/conf-qmail b/conf-qmail
new file mode 100644
index 0000000..0267f30
--- /dev/null
+++ b/conf-qmail
@@ -0,0 +1,11 @@
+/var/qmail
+
+This is the qmail home directory. It must be a local directory, not
+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.
diff --git a/conf-spawn b/conf-spawn
new file mode 100644
index 0000000..d6a4256
--- /dev/null
+++ b/conf-spawn
@@ -0,0 +1,5 @@
+120
+
+This is a silent concurrency limit. You can't set it above 255. On some
+systems you can't set it above 125. qmail will refuse to compile if the
+limit is too high.
diff --git a/conf-split b/conf-split
new file mode 100644
index 0000000..dc6aaf1
--- /dev/null
+++ b/conf-split
@@ -0,0 +1,3 @@
+23
+
+This is the queue subdirectory split.
diff --git a/conf-users b/conf-users
new file mode 100644
index 0000000..9bb35df
--- /dev/null
+++ b/conf-users
@@ -0,0 +1,15 @@
+alias
+qmaild
+qmaill
+root
+qmailp
+qmailq
+qmailr
+qmails
+
+The qmail system is heavily partitioned for security; it does almost
+nothing as root.
+
+The first eight lines of this file are the alias user, the daemon user,
+the log user, the owner of miscellaneous files such as binaries, the
+passwd user, the queue user, the remote user, and the send user.
diff --git a/config-fast.sh b/config-fast.sh
new file mode 100644
index 0000000..d05cda9
--- /dev/null
+++ b/config-fast.sh
@@ -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!'
diff --git a/config.sh b/config.sh
new file mode 100644
index 0000000..8450070
--- /dev/null
+++ b/config.sh
@@ -0,0 +1,64 @@
+./hostname | tr '[A-Z]' '[a-z]' | (
+ if read host
+ then
+ echo Your hostname is "$host".
+ ./dnsfq "$host" | tr '[A-Z]' '[a-z]' | (
+ if read fqdn
+ then
+ echo Your host\'s fully qualified name in DNS 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 ' '
+ echo Checking local IP addresses:
+ : > QMAIL/control/locals
+ chmod 644 QMAIL/control/locals
+ ( ./dnsip "$fqdn"
+ ./ipmeprint ) | sort -u | \
+ (
+ while read localip
+ do
+ echo "$localip: " | tr -d '\012'
+ ./dnsptr "$localip" 2>/dev/null | (
+ if read local
+ then
+ echo Adding "$local" to control/locals...
+ echo "$local" >> QMAIL/control/locals
+ else
+ echo PTR lookup failed. I assume this address has no DNS name.
+ fi
+ )
+ done
+ )
+ echo ' '
+ echo If there are any other domain names that point to you,
+ echo you will have to add them to QMAIL/control/locals.
+ echo You don\'t have to worry about aliases, i.e., domains with CNAME records.
+ echo ' '
+ echo Copying QMAIL/control/locals to QMAIL/control/rcpthosts...
+ cp QMAIL/control/locals QMAIL/control/rcpthosts
+ chmod 644 QMAIL/control/rcpthosts
+ echo 'Now qmail will refuse to accept SMTP messages except to those hosts.'
+ echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!'
+ else
+ echo Sorry, I couldn\'t find your host\'s canonical name in DNS.
+ echo You will have to set up control/me yourself.
+ fi
+ )
+ else
+ echo Sorry, I couldn\'t find your hostname.
+ echo You will have to set up control/me yourself.
+ fi
+)
diff --git a/constmap.c b/constmap.c
new file mode 100644
index 0000000..722e3b8
--- /dev/null
+++ b/constmap.c
@@ -0,0 +1,114 @@
+#include "constmap.h"
+#include "alloc.h"
+#include "case.h"
+
+static constmap_hash hash(s,len)
+char *s;
+int len;
+{
+ unsigned char ch;
+ constmap_hash h;
+ h = 5381;
+ while (len > 0) {
+ ch = *s++ - 'A';
+ if (ch <= 'Z' - 'A') ch += 'a' - 'A';
+ h = ((h << 5) + h) ^ ch;
+ --len;
+ }
+ return h;
+}
+
+char *constmap(cm,s,len)
+struct constmap *cm;
+char *s;
+int len;
+{
+ constmap_hash h;
+ int pos;
+ h = hash(s,len);
+ pos = cm->first[h & cm->mask];
+ while (pos != -1) {
+ if (h == cm->hash[pos])
+ if (len == cm->inputlen[pos])
+ if (!case_diffb(cm->input[pos],len,s))
+ return cm->input[pos] + cm->inputlen[pos] + 1;
+ pos = cm->next[pos];
+ }
+ return 0;
+}
+
+int constmap_init(cm,s,len,flagcolon)
+struct constmap *cm;
+char *s;
+int len;
+int flagcolon;
+{
+ int i;
+ int j;
+ int k;
+ int pos;
+ constmap_hash h;
+
+ cm->num = 0;
+ for (j = 0;j < len;++j) if (!s[j]) ++cm->num;
+
+ h = 64;
+ while (h && (h < cm->num)) h += h;
+ cm->mask = h - 1;
+
+ cm->first = (int *) alloc(sizeof(int) * h);
+ if (cm->first) {
+ cm->input = (char **) alloc(sizeof(char *) * cm->num);
+ if (cm->input) {
+ cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
+ if (cm->inputlen) {
+ cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
+ if (cm->hash) {
+ cm->next = (int *) alloc(sizeof(int) * cm->num);
+ if (cm->next) {
+ for (h = 0;h <= cm->mask;++h)
+ cm->first[h] = -1;
+ pos = 0;
+ i = 0;
+ for (j = 0;j < len;++j)
+ if (!s[j]) {
+ k = j - i;
+ if (flagcolon) {
+ for (k = i;k < j;++k)
+ if (s[k] == ':')
+ break;
+ if (k >= j) { i = j + 1; continue; }
+ k -= i;
+ }
+ cm->input[pos] = s + i;
+ cm->inputlen[pos] = k;
+ h = hash(s + i,k);
+ cm->hash[pos] = h;
+ h &= cm->mask;
+ cm->next[pos] = cm->first[h];
+ cm->first[h] = pos;
+ ++pos;
+ i = j + 1;
+ }
+ return 1;
+ }
+ alloc_free(cm->hash);
+ }
+ alloc_free(cm->inputlen);
+ }
+ alloc_free(cm->input);
+ }
+ alloc_free(cm->first);
+ }
+ return 0;
+}
+
+void constmap_free(cm)
+struct constmap *cm;
+{
+ alloc_free(cm->next);
+ alloc_free(cm->hash);
+ alloc_free(cm->inputlen);
+ alloc_free(cm->input);
+ alloc_free(cm->first);
+}
diff --git a/constmap.h b/constmap.h
new file mode 100644
index 0000000..3f29179
--- /dev/null
+++ b/constmap.h
@@ -0,0 +1,20 @@
+#ifndef CONSTMAP_H
+#define CONSTMAP_H
+
+typedef unsigned long constmap_hash;
+
+struct constmap {
+ int num;
+ constmap_hash mask;
+ constmap_hash *hash;
+ int *first;
+ int *next;
+ char **input;
+ int *inputlen;
+} ;
+
+extern int constmap_init();
+extern void constmap_free();
+extern char *constmap();
+
+#endif
diff --git a/control.c b/control.c
new file mode 100644
index 0000000..b655352
--- /dev/null
+++ b/control.c
@@ -0,0 +1,130 @@
+#include "readwrite.h"
+#include "open.h"
+#include "getln.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "error.h"
+#include "control.h"
+#include "alloc.h"
+#include "scan.h"
+
+static char inbuf[64];
+static stralloc line = {0};
+static stralloc me = {0};
+static int meok = 0;
+
+static void striptrailingwhitespace(sa)
+stralloc *sa;
+{
+ while (sa->len > 0)
+ switch(sa->s[sa->len - 1])
+ {
+ case '\n': case ' ': case '\t':
+ --sa->len;
+ break;
+ default:
+ return;
+ }
+}
+
+int control_init()
+{
+ int r;
+ r = control_readline(&me,"control/me");
+ if (r == 1) meok = 1;
+ return r;
+}
+
+int control_rldef(sa,fn,flagme,def)
+stralloc *sa;
+char *fn;
+int flagme;
+char *def;
+{
+ int r;
+ r = control_readline(sa,fn);
+ if (r) return r;
+ if (flagme) if (meok) return stralloc_copy(sa,&me) ? 1 : -1;
+ if (def) return stralloc_copys(sa,def) ? 1 : -1;
+ return r;
+}
+
+int control_readline(sa,fn)
+stralloc *sa;
+char *fn;
+{
+ substdio ss;
+ int fd;
+ int match;
+
+ fd = open_read(fn);
+ if (fd == -1) { if (errno == error_noent) return 0; return -1; }
+
+ substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
+
+ if (getln(&ss,sa,&match,'\n') == -1) { close(fd); return -1; }
+
+ striptrailingwhitespace(sa);
+ close(fd);
+ return 1;
+}
+
+int control_readint(i,fn)
+int *i;
+char *fn;
+{
+ unsigned long u;
+ switch(control_readline(&line,fn))
+ {
+ case 0: return 0;
+ case -1: return -1;
+ }
+ if (!stralloc_0(&line)) return -1;
+ if (!scan_ulong(line.s,&u)) return 0;
+ *i = u;
+ return 1;
+}
+
+int control_readfile(sa,fn,flagme)
+stralloc *sa;
+char *fn;
+int flagme;
+{
+ substdio ss;
+ int fd;
+ int match;
+
+ if (!stralloc_copys(sa,"")) return -1;
+
+ fd = open_read(fn);
+ if (fd == -1)
+ {
+ if (errno == error_noent)
+ {
+ if (flagme && meok)
+ {
+ if (!stralloc_copy(sa,&me)) return -1;
+ if (!stralloc_0(sa)) return -1;
+ return 1;
+ }
+ return 0;
+ }
+ return -1;
+ }
+
+ substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
+
+ for (;;)
+ {
+ if (getln(&ss,&line,&match,'\n') == -1) break;
+ if (!match && !line.len) { close(fd); return 1; }
+ striptrailingwhitespace(&line);
+ if (!stralloc_0(&line)) break;
+ if (line.s[0])
+ if (line.s[0] != '#')
+ if (!stralloc_cat(sa,&line)) break;
+ if (!match) { close(fd); return 1; }
+ }
+ close(fd);
+ return -1;
+}
diff --git a/control.h b/control.h
new file mode 100644
index 0000000..7cba89b
--- /dev/null
+++ b/control.h
@@ -0,0 +1,10 @@
+#ifndef CONTROL_H
+#define CONTROL_H
+
+extern int control_init();
+extern int control_readline();
+extern int control_rldef();
+extern int control_readint();
+extern int control_readfile();
+
+#endif
diff --git a/date822fmt.c b/date822fmt.c
new file mode 100644
index 0000000..7674bd1
--- /dev/null
+++ b/date822fmt.c
@@ -0,0 +1,29 @@
+#include "datetime.h"
+#include "fmt.h"
+#include "date822fmt.h"
+
+static char *montab[12] = {
+"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+unsigned int date822fmt(s,dt)
+char *s;
+struct datetime *dt;
+{
+ unsigned int i;
+ unsigned int len;
+ len = 0;
+ i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
+ i = fmt_str(s," "); len += i; if (s) s += i;
+ i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
+ i = fmt_str(s," "); len += i; if (s) s += i;
+ i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
+ i = fmt_str(s," "); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
+ i = fmt_str(s,":"); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
+ i = fmt_str(s,":"); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
+ i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
+ return len;
+}
diff --git a/date822fmt.h b/date822fmt.h
new file mode 100644
index 0000000..1848e5a
--- /dev/null
+++ b/date822fmt.h
@@ -0,0 +1,7 @@
+#ifndef DATE822FMT_H
+#define DATE822FMT_H
+
+extern unsigned int date822fmt();
+#define DATE822FMT 60
+
+#endif
diff --git a/datemail.sh b/datemail.sh
new file mode 100644
index 0000000..fd28f46
--- /dev/null
+++ b/datemail.sh
@@ -0,0 +1 @@
+exec QMAIL/bin/predate QMAIL/bin/sendmail ${1+"$@"}
diff --git a/datetime.3 b/datetime.3
new file mode 100644
index 0000000..33a623f
--- /dev/null
+++ b/datetime.3
@@ -0,0 +1,73 @@
+.TH datetime 3
+.SH NAME
+datetime \- convert between TAI labels and seconds
+.SH SYNTAX
+.B #include <datetime.h>
+
+void \fBdatetime_tai\fP(&\fIdt\fR,\fIt\fR);
+
+datetime_sec \fBdatetime_untai\fP(&\fIdt\fR);
+
+struct datetime \fIdt\fR;
+.br
+datetime_sec \fIt\fR;
+.SH DESCRIPTION
+International Atomic Time, TAI,
+is the fundamental unit for time measurements.
+TAI has one label for every second of real time,
+without complications such as leap seconds.
+
+A
+struct datetime
+variable,
+such as
+.IR dt ,
+stores a TAI label.
+.I dt\fB.year
+is the year number minus 1900;
+.I dt\fB.mon
+is the month number, from 0 (January) through 11 (December);
+.I dt\fB.mday
+is the day of the month, from 1 through 31;
+.I dt\fB.hour
+is the hour, from 0 through 23;
+.I dt\fB.min
+is the minute, from 0 through 59;
+.I dt\fB.sec
+is the second, from 0 through 59;
+.I dt\fB.wday
+is the day of the week, from 0 (Sunday) through 6 (Saturday);
+.I dt\fB.yday
+is the day of the year, from 0 through 365.
+
+The
+.B datetime
+library supports more convenient TAI manipulation with
+the datetime_sec type.
+A datetime_sec value, such as
+.IR t ,
+is an integer referring to the
+.IR t th
+second after the beginning of 1970 TAI.
+The first second of 1970 TAI was 0;
+the next second was 1;
+the last second of 1969 TAI was -1.
+The difference between two datetime_sec values is a number
+of real-time seconds.
+
+.B datetime_tai
+converts a datetime_sec to a TAI label.
+
+.B datetime_untai
+reads a TAI label
+(specifically
+.IR dt\fB.year ,
+.IR dt\fB.mon ,
+.IR dt\fB.mday ,
+.IR dt\fB.hour ,
+.IR dt\fB.min ,
+and
+.IR dt\fB.sec )
+and returns a datetime_sec.
+.SH "SEE ALSO"
+now(3)
diff --git a/datetime.c b/datetime.c
new file mode 100644
index 0000000..7b8a803
--- /dev/null
+++ b/datetime.c
@@ -0,0 +1,55 @@
+/* 19950925 */
+#include "datetime.h"
+
+void datetime_tai(dt,t)
+struct datetime *dt;
+datetime_sec t;
+{
+ int day;
+ int tod;
+ int year;
+ int yday;
+ int wday;
+ int mon;
+
+ tod = t % 86400;
+ day = t / 86400;
+ if (tod < 0) { tod += 86400; --day; }
+
+ dt->hour = tod / 3600;
+ tod %= 3600;
+ dt->min = tod / 60;
+ dt->sec = tod % 60;
+
+ wday = (day + 4) % 7; if (wday < 0) wday += 7;
+ dt->wday = wday;
+
+ day -= 11017;
+ /* day 0 is march 1, 2000 */
+ year = 5 + day / 146097;
+ day = day % 146097; if (day < 0) { day += 146097; --year; }
+ /* from now on, day is nonnegative */
+ year *= 4;
+ if (day == 146096) { year += 3; day = 36524; }
+ else { year += day / 36524; day %= 36524; }
+ year *= 25;
+ year += day / 1461;
+ day %= 1461;
+ year *= 4;
+ yday = (day < 306);
+ if (day == 1460) { year += 3; day = 365; }
+ else { year += day / 365; day %= 365; }
+ yday += day;
+
+ day *= 10;
+ mon = (day + 5) / 306;
+ day = day + 5 - 306 * mon;
+ day /= 10;
+ if (mon >= 10) { yday -= 306; ++year; mon -= 10; }
+ else { yday += 59; mon += 2; }
+
+ dt->yday = yday;
+ dt->year = year - 1900;
+ dt->mon = mon;
+ dt->mday = day + 1;
+}
diff --git a/datetime.h b/datetime.h
new file mode 100644
index 0000000..cde2a9b
--- /dev/null
+++ b/datetime.h
@@ -0,0 +1,20 @@
+#ifndef DATETIME_H
+#define DATETIME_H
+
+struct datetime {
+ int hour;
+ int min;
+ int sec;
+ int wday;
+ int mday;
+ int yday;
+ int mon;
+ int year;
+} ;
+
+typedef long datetime_sec;
+
+extern void datetime_tai();
+extern datetime_sec datetime_untai();
+
+#endif
diff --git a/datetime_un.c b/datetime_un.c
new file mode 100644
index 0000000..b5048f8
--- /dev/null
+++ b/datetime_un.c
@@ -0,0 +1,35 @@
+#include "datetime.h"
+
+/* roughly 100x faster than mktime() */
+datetime_sec datetime_untai(dt)
+struct datetime *dt;
+{
+ int year;
+ int day;
+ int mon;
+
+ year = dt->year + 1900;
+
+ mon = dt->mon;
+ if (mon >= 2) { mon -= 2; }
+ else { mon += 10; --year; }
+
+ day = (dt->mday - 1) * 10 + 5 + 306 * mon;
+ day /= 10;
+
+ if (day == 365) { year -= 3; day = 1460; }
+ else { day += 365 * (year % 4); }
+ year /= 4;
+
+ day += 1461 * (year % 25);
+ year /= 25;
+
+ if (day == 36524) { year -= 3; day = 146096; }
+ else { day += 36524 * (year % 4); }
+ year /= 4;
+
+ day += 146097 * (year - 5);
+ day += 11017;
+
+ return ((day * 24 + dt->hour) * 60 + dt->min) * 60 + dt->sec;
+}
diff --git a/direntry.3 b/direntry.3
new file mode 100644
index 0000000..8928fbb
--- /dev/null
+++ b/direntry.3
@@ -0,0 +1,36 @@
+.TH direntry 3
+.SH NAME
+direntry \- read directory entries
+.SH SYNTAX
+.B #include <direntry.h>
+
+DIR *\fBopendir\fP(\fIfn\fR);
+
+struct direntry *\fBreaddir\fP(\fIdir\fP);
+
+void \fBclosedir\fP(\fIdir\fP);
+
+DIR *\fIdir\fR;
+.br
+char *\fIfn\fR;
+.SH DESCRIPTION
+The point of
+.B direntry.h
+is to provide a uniform interface to BSD's
+.B sys/dir.h
+and POSIX's
+.BR dirent.h .
+
+The
+.B readdir
+interface is highly unsatisfactory.
+It does not distinguish between I/O errors and end-of-directory.
+It uses
+.BR malloc .
+The return type for
+.B closedir
+varies: some implementations return the
+.B close
+return value.
+.SH "SEE ALSO"
+readdir(3)
diff --git a/direntry.h1 b/direntry.h1
new file mode 100644
index 0000000..f737676
--- /dev/null
+++ b/direntry.h1
@@ -0,0 +1,8 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#define direntry struct direct
+
+#endif
diff --git a/direntry.h2 b/direntry.h2
new file mode 100644
index 0000000..0302ebe
--- /dev/null
+++ b/direntry.h2
@@ -0,0 +1,8 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+#include <sys/types.h>
+#include <dirent.h>
+#define direntry struct dirent
+
+#endif
diff --git a/dns.c b/dns.c
new file mode 100644
index 0000000..e9faad7
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,398 @@
+#include <stdio.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <errno.h>
+extern int res_query();
+extern int res_search();
+#include "ip.h"
+#include "ipalloc.h"
+#include "fmt.h"
+#include "alloc.h"
+#include "str.h"
+#include "stralloc.h"
+#include "dns.h"
+#include "case.h"
+
+static unsigned short getshort(c) unsigned char *c;
+{ unsigned short u; u = c[0]; return (u << 8) + c[1]; }
+
+static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
+static int responselen;
+static unsigned char *responseend;
+static unsigned char *responsepos;
+
+static int numanswers;
+static char name[MAXDNAME];
+static struct ip_address ip;
+unsigned short pref;
+
+static stralloc glue = {0};
+
+static int (*lookup)() = res_query;
+
+static int resolve(domain,type)
+stralloc *domain;
+int type;
+{
+ int n;
+ int i;
+
+ errno = 0;
+ if (!stralloc_copy(&glue,domain)) return DNS_MEM;
+ if (!stralloc_0(&glue)) return DNS_MEM;
+ responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
+ if (responselen <= 0)
+ {
+ if (errno == ECONNREFUSED) return DNS_SOFT;
+ if (h_errno == TRY_AGAIN) return DNS_SOFT;
+ return DNS_HARD;
+ }
+ if (responselen >= sizeof(response))
+ responselen = sizeof(response);
+ responseend = response.buf + responselen;
+ responsepos = response.buf + sizeof(HEADER);
+ n = ntohs(response.hdr.qdcount);
+ while (n-- > 0)
+ {
+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
+ if (i < 0) return DNS_SOFT;
+ responsepos += i;
+ i = responseend - responsepos;
+ if (i < QFIXEDSZ) return DNS_SOFT;
+ responsepos += QFIXEDSZ;
+ }
+ numanswers = ntohs(response.hdr.ancount);
+ return 0;
+}
+
+static int findname(wanttype)
+int wanttype;
+{
+ unsigned short rrtype;
+ unsigned short rrdlen;
+ int i;
+
+ if (numanswers <= 0) return 2;
+ --numanswers;
+ if (responsepos == responseend) return DNS_SOFT;
+
+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
+ if (i < 0) return DNS_SOFT;
+ responsepos += i;
+
+ i = responseend - responsepos;
+ if (i < 4 + 3 * 2) return DNS_SOFT;
+
+ rrtype = getshort(responsepos);
+ rrdlen = getshort(responsepos + 8);
+ responsepos += 10;
+
+ if (rrtype == wanttype)
+ {
+ if (dn_expand(response.buf,responseend,responsepos,name,MAXDNAME) < 0)
+ return DNS_SOFT;
+ responsepos += rrdlen;
+ return 1;
+ }
+
+ responsepos += rrdlen;
+ return 0;
+}
+
+static int findip(wanttype)
+int wanttype;
+{
+ unsigned short rrtype;
+ unsigned short rrdlen;
+ int i;
+
+ if (numanswers <= 0) return 2;
+ --numanswers;
+ if (responsepos == responseend) return DNS_SOFT;
+
+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
+ if (i < 0) return DNS_SOFT;
+ responsepos += i;
+
+ i = responseend - responsepos;
+ if (i < 4 + 3 * 2) return DNS_SOFT;
+
+ rrtype = getshort(responsepos);
+ rrdlen = getshort(responsepos + 8);
+ responsepos += 10;
+
+ if (rrtype == wanttype)
+ {
+ if (rrdlen < 4)
+ return DNS_SOFT;
+ ip.d[0] = responsepos[0];
+ ip.d[1] = responsepos[1];
+ ip.d[2] = responsepos[2];
+ ip.d[3] = responsepos[3];
+ responsepos += rrdlen;
+ return 1;
+ }
+
+ responsepos += rrdlen;
+ return 0;
+}
+
+static int findmx(wanttype)
+int wanttype;
+{
+ unsigned short rrtype;
+ unsigned short rrdlen;
+ int i;
+
+ if (numanswers <= 0) return 2;
+ --numanswers;
+ if (responsepos == responseend) return DNS_SOFT;
+
+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
+ if (i < 0) return DNS_SOFT;
+ responsepos += i;
+
+ i = responseend - responsepos;
+ if (i < 4 + 3 * 2) return DNS_SOFT;
+
+ rrtype = getshort(responsepos);
+ rrdlen = getshort(responsepos + 8);
+ responsepos += 10;
+
+ if (rrtype == wanttype)
+ {
+ if (rrdlen < 3)
+ return DNS_SOFT;
+ pref = (responsepos[0] << 8) + responsepos[1];
+ if (dn_expand(response.buf,responseend,responsepos + 2,name,MAXDNAME) < 0)
+ return DNS_SOFT;
+ responsepos += rrdlen;
+ return 1;
+ }
+
+ responsepos += rrdlen;
+ return 0;
+}
+
+void dns_init(flagsearch)
+int flagsearch;
+{
+ res_init();
+ if (flagsearch) lookup = res_search;
+}
+
+int dns_cname(sa)
+stralloc *sa;
+{
+ int r;
+ int loop;
+ for (loop = 0;loop < 10;++loop)
+ {
+ if (!sa->len) return loop;
+ if (sa->s[sa->len - 1] == ']') return loop;
+ if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
+ switch(resolve(sa,T_ANY))
+ {
+ case DNS_MEM: return DNS_MEM;
+ case DNS_SOFT: return DNS_SOFT;
+ case DNS_HARD: return loop;
+ default:
+ while ((r = findname(T_CNAME)) != 2)
+ {
+ if (r == DNS_SOFT) return DNS_SOFT;
+ if (r == 1)
+ {
+ if (!stralloc_copys(sa,name)) return DNS_MEM;
+ break;
+ }
+ }
+ if (r == 2) return loop;
+ }
+ }
+ return DNS_HARD; /* alias loop */
+}
+
+#define FMT_IAA 40
+
+static int iaafmt(s,ip)
+char *s;
+struct ip_address *ip;
+{
+ unsigned int i;
+ unsigned int len;
+ len = 0;
+ i = fmt_ulong(s,(unsigned long) ip->d[3]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip->d[2]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i;
+ i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i;
+ return len;
+}
+
+int dns_ptr(sa,ip)
+stralloc *sa;
+struct ip_address *ip;
+{
+ int r;
+
+ if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM;
+ sa->len = iaafmt(sa->s,ip);
+ switch(resolve(sa,T_PTR))
+ {
+ case DNS_MEM: return DNS_MEM;
+ case DNS_SOFT: return DNS_SOFT;
+ case DNS_HARD: return DNS_HARD;
+ }
+ while ((r = findname(T_PTR)) != 2)
+ {
+ if (r == DNS_SOFT) return DNS_SOFT;
+ if (r == 1)
+ {
+ if (!stralloc_copys(sa,name)) return DNS_MEM;
+ return 0;
+ }
+ }
+ return DNS_HARD;
+}
+
+static int dns_ipplus(ia,sa,pref)
+ipalloc *ia;
+stralloc *sa;
+int pref;
+{
+ int r;
+ struct ip_mx ix;
+
+ if (!stralloc_copy(&glue,sa)) return DNS_MEM;
+ if (!stralloc_0(&glue)) return DNS_MEM;
+ if (glue.s[0]) {
+ ix.pref = 0;
+ if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
+ {
+ if (!ipalloc_append(ia,&ix)) return DNS_MEM;
+ return 0;
+ }
+ }
+
+ switch(resolve(sa,T_A))
+ {
+ case DNS_MEM: return DNS_MEM;
+ case DNS_SOFT: return DNS_SOFT;
+ case DNS_HARD: return DNS_HARD;
+ }
+ while ((r = findip(T_A)) != 2)
+ {
+ ix.ip = ip;
+ ix.pref = pref;
+ if (r == DNS_SOFT) return DNS_SOFT;
+ if (r == 1)
+ if (!ipalloc_append(ia,&ix)) return DNS_MEM;
+ }
+ return 0;
+}
+
+int dns_ip(ia,sa)
+ipalloc *ia;
+stralloc *sa;
+{
+ if (!ipalloc_readyplus(ia,0)) return DNS_MEM;
+ ia->len = 0;
+ return dns_ipplus(ia,sa,0);
+}
+
+int dns_mxip(ia,sa,random)
+ipalloc *ia;
+stralloc *sa;
+unsigned long random;
+{
+ int r;
+ struct mx { stralloc sa; unsigned short p; } *mx;
+ struct ip_mx ix;
+ int nummx;
+ int i;
+ int j;
+ int flagsoft;
+
+ if (!ipalloc_readyplus(ia,0)) return DNS_MEM;
+ ia->len = 0;
+
+ if (!stralloc_copy(&glue,sa)) return DNS_MEM;
+ if (!stralloc_0(&glue)) return DNS_MEM;
+ if (glue.s[0]) {
+ ix.pref = 0;
+ if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
+ {
+ if (!ipalloc_append(ia,&ix)) return DNS_MEM;
+ return 0;
+ }
+ }
+
+ switch(resolve(sa,T_MX))
+ {
+ case DNS_MEM: return DNS_MEM;
+ case DNS_SOFT: return DNS_SOFT;
+ case DNS_HARD: return dns_ip(ia,sa);
+ }
+
+ mx = (struct mx *) alloc(numanswers * sizeof(struct mx));
+ if (!mx) return DNS_MEM;
+ nummx = 0;
+
+ while ((r = findmx(T_MX)) != 2)
+ {
+ if (r == DNS_SOFT) { alloc_free(mx); return DNS_SOFT; }
+ if (r == 1)
+ {
+ mx[nummx].p = pref;
+ mx[nummx].sa.s = 0;
+ if (!stralloc_copys(&mx[nummx].sa,name))
+ {
+ while (nummx > 0) alloc_free(mx[--nummx].sa.s);
+ alloc_free(mx); return DNS_MEM;
+ }
+ ++nummx;
+ }
+ }
+
+ if (!nummx) return dns_ip(ia,sa); /* e.g., CNAME -> A */
+
+ flagsoft = 0;
+ while (nummx > 0)
+ {
+ unsigned long numsame;
+
+ i = 0;
+ numsame = 1;
+ for (j = 1;j < nummx;++j)
+ if (mx[j].p < mx[i].p)
+ {
+ i = j;
+ numsame = 1;
+ }
+ else if (mx[j].p == mx[i].p)
+ {
+ ++numsame;
+ random = random * 69069 + 1;
+ if ((random / 2) < (2147483647 / numsame))
+ i = j;
+ }
+
+ switch(dns_ipplus(ia,&mx[i].sa,mx[i].p))
+ {
+ case DNS_MEM: case DNS_SOFT:
+ flagsoft = 1; break;
+ }
+
+ alloc_free(mx[i].sa.s);
+ mx[i] = mx[--nummx];
+ }
+
+ alloc_free(mx);
+ return flagsoft;
+}
diff --git a/dns.h b/dns.h
new file mode 100644
index 0000000..bca9490
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,14 @@
+#ifndef DNS_H
+#define DNS_H
+
+#define DNS_SOFT -1
+#define DNS_HARD -2
+#define DNS_MEM -3
+
+void dns_init();
+int dns_cname();
+int dns_mxip();
+int dns_ip();
+int dns_ptr();
+
+#endif
diff --git a/dnscname.c b/dnscname.c
new file mode 100644
index 0000000..b3cd6f1
--- /dev/null
+++ b/dnscname.c
@@ -0,0 +1,25 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "dns.h"
+#include "dnsdoe.h"
+#include "readwrite.h"
+#include "exit.h"
+
+stralloc sa = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ if (!argv[1]) _exit(100);
+
+ if (!stralloc_copys(&sa,argv[1]))
+ { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
+
+ dns_init(0);
+ dnsdoe(dns_cname(&sa));
+ substdio_putflush(subfdout,sa.s,sa.len);
+ substdio_putsflush(subfdout,"\n");
+ _exit(0);
+}
diff --git a/dnsdoe.c b/dnsdoe.c
new file mode 100644
index 0000000..806ca53
--- /dev/null
+++ b/dnsdoe.c
@@ -0,0 +1,16 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "dns.h"
+#include "dnsdoe.h"
+
+void dnsdoe(r)
+int r;
+{
+ switch (r)
+ {
+ case DNS_HARD: substdio_putsflush(subfderr,"hard error\n"); _exit(100);
+ case DNS_SOFT: substdio_putsflush(subfderr,"soft error\n"); _exit(111);
+ case DNS_MEM: substdio_putsflush(subfderr,"out of memory\n"); _exit(111);
+ }
+}
diff --git a/dnsdoe.h b/dnsdoe.h
new file mode 100644
index 0000000..0e47bf6
--- /dev/null
+++ b/dnsdoe.h
@@ -0,0 +1,6 @@
+#ifndef DNSDOE_H
+#define DNSDOE_H
+
+extern void dnsdoe();
+
+#endif
diff --git a/dnsfq.c b/dnsfq.c
new file mode 100644
index 0000000..b7619b9
--- /dev/null
+++ b/dnsfq.c
@@ -0,0 +1,32 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "dns.h"
+#include "dnsdoe.h"
+#include "ip.h"
+#include "ipalloc.h"
+#include "exit.h"
+
+stralloc sa = {0};
+ipalloc ia = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ if (!argv[1]) _exit(100);
+
+ if (!stralloc_copys(&sa,argv[1]))
+ { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
+
+ dns_init(1);
+ dnsdoe(dns_ip(&ia,&sa));
+ if (ia.len <= 0)
+ {
+ substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100);
+ }
+ dnsdoe(dns_ptr(&sa,&ia.ix[0].ip));
+ substdio_putflush(subfdout,sa.s,sa.len);
+ substdio_putsflush(subfdout,"\n");
+ _exit(0);
+}
diff --git a/dnsip.c b/dnsip.c
new file mode 100644
index 0000000..e7b671c
--- /dev/null
+++ b/dnsip.c
@@ -0,0 +1,34 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "dns.h"
+#include "dnsdoe.h"
+#include "ip.h"
+#include "ipalloc.h"
+#include "exit.h"
+
+char temp[IPFMT];
+
+stralloc sa = {0};
+ipalloc ia = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int j;
+
+ if (!argv[1]) _exit(100);
+
+ if (!stralloc_copys(&sa,argv[1]))
+ { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
+
+ dns_init(0);
+ dnsdoe(dns_ip(&ia,&sa));
+ for (j = 0;j < ia.len;++j)
+ {
+ substdio_put(subfdout,temp,ip_fmt(temp,&ia.ix[j].ip));
+ substdio_putsflush(subfdout,"\n");
+ }
+ _exit(0);
+}
diff --git a/dnsmxip.c b/dnsmxip.c
new file mode 100644
index 0000000..6d8e137
--- /dev/null
+++ b/dnsmxip.c
@@ -0,0 +1,40 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "fmt.h"
+#include "dns.h"
+#include "dnsdoe.h"
+#include "ip.h"
+#include "ipalloc.h"
+#include "now.h"
+#include "exit.h"
+
+char temp[IPFMT + FMT_ULONG];
+
+stralloc sa = {0};
+ipalloc ia = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int j;
+ unsigned long r;
+
+ if (!argv[1]) _exit(100);
+
+ if (!stralloc_copys(&sa,argv[1]))
+ { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
+
+ r = now() + getpid();
+ dns_init(0);
+ dnsdoe(dns_mxip(&ia,&sa,r));
+ for (j = 0;j < ia.len;++j)
+ {
+ substdio_put(subfdout,temp,ip_fmt(temp,&ia.ix[j].ip));
+ substdio_puts(subfdout," ");
+ substdio_put(subfdout,temp,fmt_ulong(temp,(unsigned long) ia.ix[j].pref));
+ substdio_putsflush(subfdout,"\n");
+ }
+ _exit(0);
+}
diff --git a/dnsptr.c b/dnsptr.c
new file mode 100644
index 0000000..6a92fe0
--- /dev/null
+++ b/dnsptr.c
@@ -0,0 +1,27 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "str.h"
+#include "scan.h"
+#include "dns.h"
+#include "dnsdoe.h"
+#include "ip.h"
+#include "exit.h"
+
+stralloc sa = {0};
+struct ip_address ip;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ if (!argv[1]) _exit(100);
+
+ ip_scan(argv[1],&ip);
+
+ dns_init(0);
+ dnsdoe(dns_ptr(&sa,&ip));
+ substdio_putflush(subfdout,sa.s,sa.len);
+ substdio_putsflush(subfdout,"\n");
+ _exit(0);
+}
diff --git a/dot-qmail.9 b/dot-qmail.9
new file mode 100644
index 0000000..52d4626
--- /dev/null
+++ b/dot-qmail.9
@@ -0,0 +1,394 @@
+.TH dot-qmail 5
+.SH NAME
+dot-qmail \- control the delivery of mail messages
+.SH DESCRIPTION
+Normally the
+.B qmail-local
+program delivers each incoming message to your system mailbox,
+.IR homedir\fB/Mailbox ,
+where
+.I homedir
+is your home directory.
+
+It can instead
+write the mail to a different file or directory,
+forward it to another address,
+distribute it to a mailing list,
+or even execute programs,
+all under your control.
+.SH "THE QMAIL FILE"
+To change
+.BR qmail-local 's
+behavior, set up a
+.B .qmail
+file in your home directory.
+
+.B .qmail
+contains one or more lines.
+Each line is a delivery instruction.
+.B qmail-local
+follows each instruction in turn.
+There are five types of delivery instructions:
+(1) comment; (2) program; (3) forward; (4) mbox; (5) maildir.
+.TP 5
+(1)
+A comment line begins with a number sign:
+
+.EX
+ # this is a comment
+.EE
+
+.B qmail-local
+ignores the line.
+.TP 5
+(2)
+A program line begins with a vertical bar:
+
+.EX
+ |preline /usr/ucb/vacation djb
+.EE
+
+.B qmail-local
+takes the rest of the line as a command to supply to
+.BR sh .
+See
+.B qmail-command(8)
+for further information.
+.TP 5
+(3)
+A forward line begins with an ampersand:
+
+.EX
+ &me@new.job.com
+.EE
+
+.B qmail-local
+takes the rest of the line as a mail address;
+it uses
+.B qmail-queue
+to forward the message to that address.
+The address must contain a fully qualified domain name;
+it must not contain extra spaces, angle brackets, or comments:
+
+.EX
+ # the following examples are WRONG
+.br
+ &me@new
+.br
+ &<me@new.job.com>
+.br
+ & me@new.job.com
+.br
+ &me@new.job.com (New Address)
+.EE
+
+If the address begins with a letter or number,
+you may leave out the ampersand:
+
+.EX
+ me@new.job.com
+.EE
+
+Note that
+.B qmail-local
+omits its new
+.B Return-Path
+line when forwarding messages.
+.TP 5
+(4)
+An
+.I mbox
+line begins with a slash or dot,
+and does not end with a slash:
+
+.EX
+ /home/djb/Mailbox.sos
+.EE
+
+.B qmail-local
+takes the entire line as a filename.
+It appends the mail message to that file,
+using
+.BR flock -style
+file locking if possible.
+.B qmail-local
+stores the mail message in
+.I mbox
+format, as described in
+.BR mbox(5) .
+
+.B WARNING:
+On many systems,
+anyone who can read a file can
+.B flock
+it, and thus hold up
+.BR qmail-local 's
+delivery forever.
+Do not deliver mail to a publicly accessible file!
+
+If
+.B qmail-local
+is able to lock the file, but has trouble writing to it
+(because, for example, the disk is full),
+it will truncate the file back to its original length.
+However, it cannot prevent mailbox corruption if the system
+crashes during delivery.
+.TP 5
+(5)
+A
+.I maildir
+line begins with a slash or dot,
+and ends with a slash:
+
+.EX
+ /home/djb/Maildir/
+.EE
+
+.B qmail-local
+takes the entire line as the name of a directory in
+.I maildir
+format.
+It reliably stores the incoming message in that directory.
+See
+.B maildir(5)
+for more details.
+.PP
+If
+.B .qmail
+has the execute bit set,
+it must not contain any
+program lines,
+.I mbox
+lines,
+or
+.I maildir
+lines.
+If
+.B qmail-local
+sees any such lines,
+it will stop and indicate a temporary failure.
+
+If
+.B .qmail
+is completely empty (0 bytes long), or does not exist,
+.B qmail-local
+follows the
+.I defaultdelivery
+instructions set by your system administrator;
+normally
+.I defaultdelivery
+is
+.BR ./Mailbox ,
+so
+.B qmail-local
+appends the mail message to
+.B Mailbox
+in
+.I mbox
+format.
+
+.B .qmail
+may contain extra spaces and tabs at the end of a line.
+Blank lines are allowed, but not for the first line of
+.BR .qmail .
+
+If
+.B .qmail
+is world-writable,
+.B qmail-local
+stops and indicates a temporary failure.
+.SH "SAFE QMAIL EDITING"
+Incoming messages can arrive at any moment.
+If you want to safely edit your
+.B .qmail
+file, first set the sticky bit on your home directory:
+
+.EX
+ chmod +t $HOME
+.EE
+
+.B qmail-local
+will temporarily defer delivery of any message to you
+if your home directory is sticky
+(or group-writable or other-writable,
+which should never happen).
+Make sure to
+
+.EX
+ chmod -t $HOME
+.EE
+
+when you are done!
+It's a good idea to test your new
+.B .qmail
+file as follows:
+
+.EX
+ qmail-local -n $USER ~ $USER '' '' '' '' ./Mailbox
+.EE
+.SH "EXTENSION ADDRESSES"
+In the
+.B qmail
+system,
+you control all local addresses of the form
+.IR user\fBBREAK\fIanything ,
+as well as the address
+.I user
+itself,
+where
+.I user
+is your account name.
+Delivery to
+.I user\fBBREAK\fIanything
+is controlled by the file
+.IR homedir/\fB.qmail\-\fIanything .
+(These rules may be changed by the system administrator;
+see
+.BR qmail-users (5).)
+
+The
+.B alias
+user controls all other addresses.
+Delivery to
+.I local
+is controlled by the file
+.IR homedir/\fB.qmail\-\fIlocal ,
+where
+.I homedir
+is
+.BR alias 's
+home directory.
+
+In the following description,
+.B qmail-local
+is handling a message addressed to
+.IR local@domain ,
+where
+.I local
+is controlled by
+.BR .qmail\-\fIext .
+Here is what it does.
+
+If
+.B .qmail\-\fIext
+is completely empty,
+.B qmail-local
+follows the
+.I defaultdelivery
+instructions set by your system administrator.
+
+If
+.B .qmail\-\fIext
+doesn't exist,
+.B qmail-local
+will try some default
+.B .qmail
+files.
+For example,
+if
+.I ext
+is
+.BR foo-bar ,
+.B qmail-local
+will try first
+.BR .qmail-foo-bar ,
+then
+.BR .qmail-foo-default ,
+and finally
+.BR .qmail-default .
+If none of these exist,
+.B qmail-local
+will bounce the message.
+(Exception: for the basic
+.I user
+address,
+.B qmail-local
+treats a nonexistent
+.B .qmail
+the same as an empty
+.BR .qmail .)
+
+.B WARNING:
+For security,
+.B qmail-local
+replaces any dots in
+.I ext
+with colons before checking
+.BR .qmail\-\fIext .
+For convenience,
+.B qmail-local
+converts any uppercase letters in
+.I ext
+to lowercase.
+
+When
+.B qmail-local
+forwards a message as instructed in
+.B .qmail\-\fIext
+(or
+.BR .qmail-default ),
+it checks whether
+.B .qmail\-\fIext\fB-owner\fP
+exists.
+If so,
+it uses
+.I local\fB-owner@\fIdomain
+as the envelope sender for the forwarded message.
+Otherwise it retains the envelope sender of the original message.
+Exception:
+.B qmail-local
+always retains the original envelope sender
+if it is the empty address or
+.BR #@[] ,
+i.e., if this is a bounce message.
+
+.B qmail-local
+also supports
+.B variable envelope return paths
+(VERPs):
+if
+.B .qmail\-\fIext\fB-owner\fP
+and
+.B .qmail\-\fIext\fB-owner-default\fP
+both exist, it uses
+.I local\fB\-owner\-@\fIdomain\fB-@[]
+as the envelope sender.
+This will cause a recipient
+.I recip\fB@\fIreciphost
+to see an envelope sender of
+.IR local\fB\-owner\-\fIrecip\fB=\fIreciphost\fB@\fIdomain .
+.SH "ERROR HANDLING"
+If a delivery instruction fails,
+.B qmail-local
+stops immediately and reports failure.
+.B qmail-local
+handles forwarding after all other instructions,
+so any error in another type of delivery will prevent all forwarding.
+
+If a program returns exit code 99,
+.B qmail-local
+ignores all succeeding lines in
+.BR .qmail ,
+but it still pays attention to previous forward lines.
+
+To set up independent instructions,
+where a temporary or permanent failure in one instruction
+does not affect the others,
+move each instruction into a separate
+.B .qmail\-\fIext
+file, and set up a central
+.B .qmail
+file that forwards to all of the
+.BR .qmail\-\fIext s.
+Note that
+.B qmail-local
+can handle any number of forward lines simultaneously.
+.SH "SEE ALSO"
+envelopes(5),
+maildir(5),
+mbox(5),
+qmail-users(5),
+qmail-local(8),
+qmail-command(8),
+qmail-queue(8),
+qmail-lspawn(8)
diff --git a/elq.sh b/elq.sh
new file mode 100644
index 0000000..7e20262
--- /dev/null
+++ b/elq.sh
@@ -0,0 +1 @@
+QMAIL/bin/maildir2mbox && exec elm ${1+"$@"}
diff --git a/env.3 b/env.3
new file mode 100644
index 0000000..53a5f89
--- /dev/null
+++ b/env.3
@@ -0,0 +1,31 @@
+.TH env 3
+.SH NAME
+env \- manage the environment
+.SH SYNTAX
+.B #include <env.h>
+
+char **\fBenviron\fP;
+
+char *\fBenv_get\fP(\fIname\fR);
+.br
+char *\fBenv_pick\fP();
+
+char *\fIname\fR;
+.SH DESCRIPTION
+The environment,
+.BR environ ,
+is a 0-terminated array of 0-terminated strings,
+called environment variables.
+Each environment variable is of the form
+.IR name\fB=\fIvalue .
+
+.B env_get
+returns the value of the first variable whose name is
+.IR name ,
+or 0 if there is no such variable.
+
+.B env_pick
+returns any variable in the environment,
+or 0 if the environment is empty.
+.SH "SEE ALSO"
+environ(7)
diff --git a/env.c b/env.c
new file mode 100644
index 0000000..05d527b
--- /dev/null
+++ b/env.c
@@ -0,0 +1,113 @@
+/* env.c, envread.c, env.h: environ library
+Daniel J. Bernstein, djb@silverton.berkeley.edu.
+Depends on str.h, alloc.h.
+Requires environ.
+19960113: rewrite. warning: interface is different.
+No known patent problems.
+*/
+
+#include "str.h"
+#include "alloc.h"
+#include "env.h"
+
+int env_isinit = 0; /* if env_isinit: */
+static int ea; /* environ is a pointer to ea+1 char*'s. */
+static int en; /* the first en of those are ALLOCATED. environ[en] is 0. */
+
+static void env_goodbye(i) int i;
+{
+ alloc_free(environ[i]);
+ environ[i] = environ[--en];
+ environ[en] = 0;
+}
+
+static char *null = 0;
+
+void env_clear()
+{
+ if (env_isinit) while (en) env_goodbye(0);
+ else environ = &null;
+}
+
+static void env_unsetlen(s,len) char *s; int len;
+{
+ int i;
+ for (i = en - 1;i >= 0;--i)
+ if (!str_diffn(s,environ[i],len))
+ if (environ[i][len] == '=')
+ env_goodbye(i);
+}
+
+int env_unset(s) char *s;
+{
+ if (!env_isinit) if (!env_init()) return 0;
+ env_unsetlen(s,str_len(s));
+ return 1;
+}
+
+static int env_add(s) char *s;
+{
+ char *t;
+ t = env_findeq(s);
+ if (t) env_unsetlen(s,t - s);
+ if (en == ea)
+ {
+ ea += 30;
+ if (!alloc_re(&environ,(en + 1) * sizeof(char *),(ea + 1) * sizeof(char *)))
+ { ea = en; return 0; }
+ }
+ environ[en++] = s;
+ environ[en] = 0;
+ return 1;
+}
+
+int env_put(s) char *s;
+{
+ char *u;
+ if (!env_isinit) if (!env_init()) return 0;
+ u = alloc(str_len(s) + 1);
+ if (!u) return 0;
+ str_copy(u,s);
+ if (!env_add(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_put2(s,t) char *s; char *t;
+{
+ char *u;
+ int slen;
+ if (!env_isinit) if (!env_init()) return 0;
+ slen = str_len(s);
+ u = alloc(slen + str_len(t) + 2);
+ if (!u) return 0;
+ str_copy(u,s);
+ u[slen] = '=';
+ str_copy(u + slen + 1,t);
+ if (!env_add(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_init()
+{
+ char **newenviron;
+ int i;
+ for (en = 0;environ[en];++en) ;
+ ea = en + 10;
+ newenviron = (char **) alloc((ea + 1) * sizeof(char *));
+ if (!newenviron) return 0;
+ for (en = 0;environ[en];++en)
+ {
+ newenviron[en] = alloc(str_len(environ[en]) + 1);
+ if (!newenviron[en])
+ {
+ for (i = 0;i < en;++i) alloc_free(newenviron[i]);
+ alloc_free(newenviron);
+ return 0;
+ }
+ str_copy(newenviron[en],environ[en]);
+ }
+ newenviron[en] = 0;
+ environ = newenviron;
+ env_isinit = 1;
+ return 1;
+}
diff --git a/env.h b/env.h
new file mode 100644
index 0000000..9befc79
--- /dev/null
+++ b/env.h
@@ -0,0 +1,17 @@
+#ifndef ENV_H
+#define ENV_H
+
+extern int env_isinit;
+
+extern int env_init();
+extern int env_put();
+extern int env_put2();
+extern int env_unset();
+extern /*@null@*/char *env_get();
+extern char *env_pick();
+extern void env_clear();
+extern char *env_findeq();
+
+extern char **environ;
+
+#endif
diff --git a/envelopes.5 b/envelopes.5
new file mode 100644
index 0000000..5f7084a
--- /dev/null
+++ b/envelopes.5
@@ -0,0 +1,231 @@
+.TH envelopes 5
+.SH "NAME"
+envelopes \- sender/recipient lists attached to messages
+.SH "INTRODUCTION"
+Electronic mail messages are delivered in
+.IR envelopes .
+
+An envelope lists a
+.I sender
+and one or more
+.IR recipients .
+Usually these
+envelope addresses are the same
+as the addresses listed in the message header:
+
+.EX
+ (envelope) from djb to root
+.br
+ From: djb
+.br
+ To: root
+.EE
+
+In more complicated situations, though,
+the envelope addresses may differ from the header addresses.
+.SH "ENVELOPE EXAMPLES"
+When a message is delivered to
+several people at different locations,
+it is first photocopied
+and placed into several envelopes:
+
+.EX
+ (envelope) from djb to root
+.br
+ From: djb Copy #1 of message
+.br
+ To: root, god@brl.mil
+.EE
+
+.EX
+ (envelope) from djb to god@brl.mil
+.br
+ From: djb Copy #2 of message
+.br
+ To: root, god@brl.mil
+.EE
+
+When a message is delivered
+to several people at the same location,
+the sender doesn't have to photocopy it.
+He can instead stuff it into
+one envelope with several addresses;
+the recipients will make the photocopy:
+
+.EX
+ (envelope) from djb to god@brl.mil, angel@brl.mil
+.br
+ From: djb
+.br
+ To: god@brl.mil, angel@brl.mil, joe, frde
+.EE
+
+Bounced mail is sent back to the envelope sender address.
+The bounced mail doesn't list an envelope sender,
+so bounce loops are impossible:
+
+.EX
+ (envelope) from <> to djb
+.br
+ From: MAILER-DAEMON
+.br
+ To: djb
+.br
+ Subject: unknown user frde
+.EE
+
+The recipient of a message may make another copy
+and forward it in a new envelope:
+
+.EX
+ (envelope) from djb to joe
+.br
+ From: djb Original message
+.br
+ To: joe
+.EE
+
+.EX
+ (envelope) from joe to fred
+.br
+ From: djb Forwarded message
+.br
+ To: joe
+.EE
+
+A mailing list works almost the same way:
+
+.EX
+ (envelope) from djb to sos-list
+.br
+ From: djb Original message
+.br
+ To: sos-list
+.EE
+
+.EX
+ (envelope) from sos-owner to god@brl.mil
+.br
+ From: djb Forwarded message
+.br
+ To: sos-list to recipient #1
+.EE
+
+.EX
+ (envelope) from sos-owner to frde
+.br
+ From: djb Forwarded message
+.br
+ To: sos-list to recipient #2
+.EE
+
+Notice that the mailing list is set up
+to replace the envelope sender with something new,
+.BR sos-owner .
+So bounces will come back to
+.BR sos-owner :
+
+.EX
+ (envelope) from <> to sos-owner
+.br
+ From: MAILER-DAEMON
+.br
+ To: sos-owner
+.br
+ Subject: unknown user frde
+.EE
+
+It's a good idea to set up an extra address,
+.BR sos-owner ,
+like this:
+the original envelope sender (\fBdjb\fP)
+has no way to fix bad
+.B sos-list
+addresses,
+and of course bounces must not be sent to
+.B sos-list
+itself.
+.SH "HOW ENVELOPE ADDRESSES ARE STORED"
+Envelope sender and envelope recipient addresses
+are transmitted and recorded in several ways.
+
+When a user injects mail through
+.BR qmail-inject ,
+he can supply a
+.B Return-Path
+line or a
+.B \-f
+option for the envelope sender;
+by default the envelope sender is his login name.
+The envelope recipient addresses can be taken
+from the command line or from various header fields,
+depending on the options to
+.BR qmail-inject .
+Similar comments apply to
+.BR sendmail .
+
+When a message is transferred from one machine to another through SMTP,
+the envelope sender is given in a
+.B MAIL FROM
+command,
+the envelope recipients are given in
+.B RCPT TO
+commands,
+and the message is supplied separately by a
+.B DATA
+command.
+
+When a message is delivered by
+.B qmail
+to a single local recipient,
+.B qmail-local
+records the recipient in
+.B Delivered-To
+and the envelope sender in
+.BR Return-Path .
+It uses
+.B Delivered-To
+to detect mail forwarding loops.
+
+.B sendmail
+normally records the envelope sender in
+.BR Return-Path .
+It does not record envelope recipient addresses,
+on the theory that they are redundant:
+you received the mail,
+so you must have been one of the envelope recipients.
+
+Note that,
+if the header doesn't have any recipient addresses,
+.B sendmail
+will move envelope recipient addresses back into the header.
+This situation occurs if all addresses were originally listed as
+.BR Bcc ,
+since
+.B Bcc
+is automatically removed.
+When
+.B sendmail
+sees this, it creates a new
+.B Apparently-To
+header field with the envelope recipient addresses.
+This has the strange effect that each blind-carbon-copy recipient will see
+a list of all recipients on the same machine.
+
+When a message is stored in
+.B mbox
+format,
+the envelope sender is recorded at the top of the message
+as a UUCP-style
+.B From
+(no colon) line.
+Note that this line is less reliable than the
+.B Return-Path
+line added by
+.B qmail-local
+or
+.B sendmail\fP.
+.SH "SEE ALSO"
+qmail-header(5),
+qmail-local(8),
+qmail-inject(8)
diff --git a/envread.c b/envread.c
new file mode 100644
index 0000000..80185de
--- /dev/null
+++ b/envread.c
@@ -0,0 +1,30 @@
+#include "env.h"
+#include "str.h"
+
+extern /*@null@*/char *env_get(s)
+char *s;
+{
+ int i;
+ unsigned int slen;
+ char *envi;
+
+ slen = str_len(s);
+ for (i = 0;envi = environ[i];++i)
+ if ((!str_diffn(s,envi,slen)) && (envi[slen] == '='))
+ return envi + slen + 1;
+ return 0;
+}
+
+extern char *env_pick()
+{
+ return environ[0];
+}
+
+extern char *env_findeq(s)
+char *s;
+{
+ for (;*s;++s)
+ if (*s == '=')
+ return s;
+ return 0;
+}
diff --git a/error.3 b/error.3
new file mode 100644
index 0000000..308e43c
--- /dev/null
+++ b/error.3
@@ -0,0 +1,45 @@
+.TH error 3
+.SH NAME
+error \- syscall error codes
+.SH SYNTAX
+.B #include <error.h>
+.br
+.B #include <errno.h>
+
+extern int \fBerror_intr\fP;
+.br
+extern int \fBerror_nomem\fP;
+.br
+extern int \fBerror_noent\fP;
+.br
+extern int \fBerror_txtbsy\fP;
+.br
+extern int \fBerror_io\fP;
+.br
+extern int \fBerror_exist\fP;
+.br
+extern int \fBerror_timeout\fP;
+.br
+extern int \fBerror_inprogress\fP;
+.br
+extern int \fBerror_wouldblock\fP;
+.br
+extern int \fBerror_again\fP;
+.br
+extern int \fBerror_pipe\fP;
+.br
+extern int \fBerror_perm\fP;
+.br
+extern int \fBerror_acces\fP;
+.SH DESCRIPTION
+UNIX syscalls provide detailed error codes in the
+.B errno
+variable.
+The
+.B error
+library provides portable names for a variety of possible
+.B errno
+values.
+.SH "SEE ALSO"
+error_str(3),
+error_temp(3)
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..d51304f
--- /dev/null
+++ b/error.c
@@ -0,0 +1,95 @@
+#include <errno.h>
+#include "error.h"
+
+/* warning: as coverage improves here, should update error_{str,temp} */
+
+int error_intr =
+#ifdef EINTR
+EINTR;
+#else
+-1;
+#endif
+
+int error_nomem =
+#ifdef ENOMEM
+ENOMEM;
+#else
+-2;
+#endif
+
+int error_noent =
+#ifdef ENOENT
+ENOENT;
+#else
+-3;
+#endif
+
+int error_txtbsy =
+#ifdef ETXTBSY
+ETXTBSY;
+#else
+-4;
+#endif
+
+int error_io =
+#ifdef EIO
+EIO;
+#else
+-5;
+#endif
+
+int error_exist =
+#ifdef EEXIST
+EEXIST;
+#else
+-6;
+#endif
+
+int error_timeout =
+#ifdef ETIMEDOUT
+ETIMEDOUT;
+#else
+-7;
+#endif
+
+int error_inprogress =
+#ifdef EINPROGRESS
+EINPROGRESS;
+#else
+-8;
+#endif
+
+int error_wouldblock =
+#ifdef EWOULDBLOCK
+EWOULDBLOCK;
+#else
+-9;
+#endif
+
+int error_again =
+#ifdef EAGAIN
+EAGAIN;
+#else
+-10;
+#endif
+
+int error_pipe =
+#ifdef EPIPE
+EPIPE;
+#else
+-11;
+#endif
+
+int error_perm =
+#ifdef EPERM
+EPERM;
+#else
+-12;
+#endif
+
+int error_acces =
+#ifdef EACCES
+EACCES;
+#else
+-13;
+#endif
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..5d98c6b
--- /dev/null
+++ b/error.h
@@ -0,0 +1,23 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <errno.h>
+
+extern int error_intr;
+extern int error_nomem;
+extern int error_noent;
+extern int error_txtbsy;
+extern int error_io;
+extern int error_exist;
+extern int error_timeout;
+extern int error_inprogress;
+extern int error_wouldblock;
+extern int error_again;
+extern int error_pipe;
+extern int error_perm;
+extern int error_acces;
+
+extern char *error_str();
+extern int error_temp();
+
+#endif
diff --git a/error_str.3 b/error_str.3
new file mode 100644
index 0000000..62043c4
--- /dev/null
+++ b/error_str.3
@@ -0,0 +1,19 @@
+.TH error_str 3
+.SH NAME
+error_str \- names for syscall error codes
+.SH SYNTAX
+.B #include <error.h>
+
+char *\fBerror_str\fP(\fIe\fR);
+
+int \fIe\fR;
+.SH DESCRIPTION
+.B error_str
+returns a printable string describing syscall error code
+.IR e .
+Normally
+.I e
+is
+.BR errno .
+.SH "SEE ALSO"
+error(3)
diff --git a/error_str.c b/error_str.c
new file mode 100644
index 0000000..804d1fa
--- /dev/null
+++ b/error_str.c
@@ -0,0 +1,276 @@
+#include <errno.h>
+#include "error.h"
+
+#define X(e,s) if (i == e) return s;
+
+char *error_str(i)
+int i;
+{
+ X(0,"no error")
+ X(error_intr,"interrupted system call")
+ X(error_nomem,"out of memory")
+ X(error_noent,"file does not exist")
+ X(error_txtbsy,"text busy")
+ X(error_io,"input/output error")
+ X(error_exist,"file already exists")
+ X(error_timeout,"timed out")
+ X(error_inprogress,"operation in progress")
+ X(error_again,"temporary failure")
+ X(error_wouldblock,"input/output would block")
+ X(error_pipe,"broken pipe")
+ X(error_perm,"permission denied")
+ X(error_acces,"access denied")
+#ifdef ESRCH
+ X(ESRCH,"no such process")
+#endif
+#ifdef ENXIO
+ X(ENXIO,"device not configured")
+#endif
+#ifdef E2BIG
+ X(E2BIG,"argument list too long")
+#endif
+#ifdef ENOEXEC
+ X(ENOEXEC,"exec format error")
+#endif
+#ifdef EBADF
+ X(EBADF,"file descriptor not open")
+#endif
+#ifdef ECHILD
+ X(ECHILD,"no child processes")
+#endif
+#ifdef EDEADLK
+ X(EDEADLK,"operation would cause deadlock")
+#endif
+#ifdef EFAULT
+ X(EFAULT,"bad address")
+#endif
+#ifdef ENOTBLK
+ X(ENOTBLK,"not a block device")
+#endif
+#ifdef EBUSY
+ X(EBUSY,"device busy")
+#endif
+#ifdef EXDEV
+ X(EXDEV,"cross-device link")
+#endif
+#ifdef ENODEV
+ X(ENODEV,"device does not support operation")
+#endif
+#ifdef ENOTDIR
+ X(ENOTDIR,"not a directory")
+#endif
+#ifdef EISDIR
+ X(EISDIR,"is a directory")
+#endif
+#ifdef EINVAL
+ X(EINVAL,"invalid argument")
+#endif
+#ifdef ENFILE
+ X(ENFILE,"system cannot open more files")
+#endif
+#ifdef EMFILE
+ X(EMFILE,"process cannot open more files")
+#endif
+#ifdef ENOTTY
+ X(ENOTTY,"not a tty")
+#endif
+#ifdef EFBIG
+ X(EFBIG,"file too big")
+#endif
+#ifdef ENOSPC
+ X(ENOSPC,"out of disk space")
+#endif
+#ifdef ESPIPE
+ X(ESPIPE,"unseekable descriptor")
+#endif
+#ifdef EROFS
+ X(EROFS,"read-only file system")
+#endif
+#ifdef EMLINK
+ X(EMLINK,"too many links")
+#endif
+#ifdef EDOM
+ X(EDOM,"input out of range")
+#endif
+#ifdef ERANGE
+ X(ERANGE,"output out of range")
+#endif
+#ifdef EALREADY
+ X(EALREADY,"operation already in progress")
+#endif
+#ifdef ENOTSOCK
+ X(ENOTSOCK,"not a socket")
+#endif
+#ifdef EDESTADDRREQ
+ X(EDESTADDRREQ,"destination address required")
+#endif
+#ifdef EMSGSIZE
+ X(EMSGSIZE,"message too long")
+#endif
+#ifdef EPROTOTYPE
+ X(EPROTOTYPE,"incorrect protocol type")
+#endif
+#ifdef ENOPROTOOPT
+ X(ENOPROTOOPT,"protocol not available")
+#endif
+#ifdef EPROTONOSUPPORT
+ X(EPROTONOSUPPORT,"protocol not supported")
+#endif
+#ifdef ESOCKTNOSUPPORT
+ X(ESOCKTNOSUPPORT,"socket type not supported")
+#endif
+#ifdef EOPNOTSUPP
+ X(EOPNOTSUPP,"operation not supported")
+#endif
+#ifdef EPFNOSUPPORT
+ X(EPFNOSUPPORT,"protocol family not supported")
+#endif
+#ifdef EAFNOSUPPORT
+ X(EAFNOSUPPORT,"address family not supported")
+#endif
+#ifdef EADDRINUSE
+ X(EADDRINUSE,"address already used")
+#endif
+#ifdef EADDRNOTAVAIL
+ X(EADDRNOTAVAIL,"address not available")
+#endif
+#ifdef ENETDOWN
+ X(ENETDOWN,"network down")
+#endif
+#ifdef ENETUNREACH
+ X(ENETUNREACH,"network unreachable")
+#endif
+#ifdef ENETRESET
+ X(ENETRESET,"network reset")
+#endif
+#ifdef ECONNABORTED
+ X(ECONNABORTED,"connection aborted")
+#endif
+#ifdef ECONNRESET
+ X(ECONNRESET,"connection reset")
+#endif
+#ifdef ENOBUFS
+ X(ENOBUFS,"out of buffer space")
+#endif
+#ifdef EISCONN
+ X(EISCONN,"already connected")
+#endif
+#ifdef ENOTCONN
+ X(ENOTCONN,"not connected")
+#endif
+#ifdef ESHUTDOWN
+ X(ESHUTDOWN,"socket shut down")
+#endif
+#ifdef ETOOMANYREFS
+ X(ETOOMANYREFS,"too many references")
+#endif
+#ifdef ECONNREFUSED
+ X(ECONNREFUSED,"connection refused")
+#endif
+#ifdef ELOOP
+ X(ELOOP,"symbolic link loop")
+#endif
+#ifdef ENAMETOOLONG
+ X(ENAMETOOLONG,"file name too long")
+#endif
+#ifdef EHOSTDOWN
+ X(EHOSTDOWN,"host down")
+#endif
+#ifdef EHOSTUNREACH
+ X(EHOSTUNREACH,"host unreachable")
+#endif
+#ifdef ENOTEMPTY
+ X(ENOTEMPTY,"directory not empty")
+#endif
+#ifdef EPROCLIM
+ X(EPROCLIM,"too many processes")
+#endif
+#ifdef EUSERS
+ X(EUSERS,"too many users")
+#endif
+#ifdef EDQUOT
+ X(EDQUOT,"disk quota exceeded")
+#endif
+#ifdef ESTALE
+ X(ESTALE,"stale NFS file handle")
+#endif
+#ifdef EREMOTE
+ X(EREMOTE,"too many levels of remote in path")
+#endif
+#ifdef EBADRPC
+ X(EBADRPC,"RPC structure is bad")
+#endif
+#ifdef ERPCMISMATCH
+ X(ERPCMISMATCH,"RPC version mismatch")
+#endif
+#ifdef EPROGUNAVAIL
+ X(EPROGUNAVAIL,"RPC program unavailable")
+#endif
+#ifdef EPROGMISMATCH
+ X(EPROGMISMATCH,"program version mismatch")
+#endif
+#ifdef EPROCUNAVAIL
+ X(EPROCUNAVAIL,"bad procedure for program")
+#endif
+#ifdef ENOLCK
+ X(ENOLCK,"no locks available")
+#endif
+#ifdef ENOSYS
+ X(ENOSYS,"system call not available")
+#endif
+#ifdef EFTYPE
+ X(EFTYPE,"bad file type")
+#endif
+#ifdef EAUTH
+ X(EAUTH,"authentication error")
+#endif
+#ifdef ENEEDAUTH
+ X(ENEEDAUTH,"not authenticated")
+#endif
+#ifdef ENOSTR
+ X(ENOSTR,"not a stream device")
+#endif
+#ifdef ETIME
+ X(ETIME,"timer expired")
+#endif
+#ifdef ENOSR
+ X(ENOSR,"out of stream resources")
+#endif
+#ifdef ENOMSG
+ X(ENOMSG,"no message of desired type")
+#endif
+#ifdef EBADMSG
+ X(EBADMSG,"bad message type")
+#endif
+#ifdef EIDRM
+ X(EIDRM,"identifier removed")
+#endif
+#ifdef ENONET
+ X(ENONET,"machine not on network")
+#endif
+#ifdef ERREMOTE
+ X(ERREMOTE,"object not local")
+#endif
+#ifdef ENOLINK
+ X(ENOLINK,"link severed")
+#endif
+#ifdef EADV
+ X(EADV,"advertise error")
+#endif
+#ifdef ESRMNT
+ X(ESRMNT,"srmount error")
+#endif
+#ifdef ECOMM
+ X(ECOMM,"communication error")
+#endif
+#ifdef EPROTO
+ X(EPROTO,"protocol error")
+#endif
+#ifdef EMULTIHOP
+ X(EMULTIHOP,"multihop attempted")
+#endif
+#ifdef EREMCHG
+ X(EREMCHG,"remote address changed")
+#endif
+ return "unknown error";
+}
diff --git a/error_temp.3 b/error_temp.3
new file mode 100644
index 0000000..2f8229d
--- /dev/null
+++ b/error_temp.3
@@ -0,0 +1,27 @@
+.TH error_temp 3
+.SH NAME
+error_temp \- identify soft syscall error codes
+.SH SYNTAX
+.B #include <error.h>
+
+int \fBerror_temp\fP(\fIe\fR);
+
+int \fIe\fR;
+.SH DESCRIPTION
+.B error_temp
+returns 1 if syscall error code
+.I e
+is a soft error, 0 if it is a hard error.
+Normally
+.I e
+is
+.BR errno .
+
+A hard error is persistent:
+file not found, read-only file system, symbolic link loop, etc.
+
+A soft error is usually transient:
+out of memory, out of disk space, I/O error, disk quota exceeded,
+connection refused, host unreachable, etc.
+.SH "SEE ALSO"
+error(3)
diff --git a/error_temp.c b/error_temp.c
new file mode 100644
index 0000000..6782cef
--- /dev/null
+++ b/error_temp.c
@@ -0,0 +1,80 @@
+#include <errno.h>
+#include "error.h"
+
+#define X(n) if (e == n) return 1;
+
+int error_temp(e)
+int e;
+{
+ X(error_intr)
+ X(error_nomem)
+ X(error_txtbsy)
+ X(error_io)
+ X(error_timeout)
+ X(error_wouldblock)
+ X(error_again)
+#ifdef EDEADLK
+ X(EDEADLK)
+#endif
+#ifdef EBUSY
+ X(EBUSY)
+#endif
+#ifdef ENFILE
+ X(ENFILE)
+#endif
+#ifdef EMFILE
+ X(EMFILE)
+#endif
+#ifdef EFBIG
+ X(EFBIG)
+#endif
+#ifdef ENOSPC
+ X(ENOSPC)
+#endif
+#ifdef ENETDOWN
+ X(ENETDOWN)
+#endif
+#ifdef ENETUNREACH
+ X(ENETUNREACH)
+#endif
+#ifdef ENETRESET
+ X(ENETRESET)
+#endif
+#ifdef ECONNABORTED
+ X(ECONNABORTED)
+#endif
+#ifdef ECONNRESET
+ X(ECONNRESET)
+#endif
+#ifdef ENOBUFS
+ X(ENOBUFS)
+#endif
+#ifdef ETOOMANYREFS
+ X(ETOOMANYREFS)
+#endif
+#ifdef ECONNREFUSED
+ X(ECONNREFUSED)
+#endif
+#ifdef EHOSTDOWN
+ X(EHOSTDOWN)
+#endif
+#ifdef EHOSTUNREACH
+ X(EHOSTUNREACH)
+#endif
+#ifdef EPROCLIM
+ X(EPROCLIM)
+#endif
+#ifdef EUSERS
+ X(EUSERS)
+#endif
+#ifdef EDQUOT
+ X(EDQUOT)
+#endif
+#ifdef ESTALE
+ X(ESTALE)
+#endif
+#ifdef ENOLCK
+ X(ENOLCK)
+#endif
+ return 0;
+}
diff --git a/except.1 b/except.1
new file mode 100644
index 0000000..4198cc2
--- /dev/null
+++ b/except.1
@@ -0,0 +1,33 @@
+.TH except 1
+.SH NAME
+except \- reverse the exit code of a program
+.SH SYNOPSIS
+.B except
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B except
+runs
+.I program
+with the given arguments.
+
+If
+.I program
+exits 0,
+.B except
+exits 100.
+If
+.I program
+exits 111,
+.B except
+exits 111.
+If
+.I program
+exits anything else,
+.B except
+exits 0.
+.SH "SEE ALSO"
+bouncesaying(1),
+condredirect(1)
diff --git a/except.c b/except.c
new file mode 100644
index 0000000..c553b3b
--- /dev/null
+++ b/except.c
@@ -0,0 +1,37 @@
+#include "fork.h"
+#include "strerr.h"
+#include "wait.h"
+#include "error.h"
+#include "exit.h"
+
+#define FATAL "except: fatal: "
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int pid;
+ int wstat;
+
+ if (!argv[1])
+ strerr_die1x(100,"except: usage: except program [ arg ... ]");
+
+ pid = fork();
+ if (pid == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+ if (pid == 0) {
+ execvp(argv[1],argv + 1);
+ if (error_temp(errno)) _exit(111);
+ _exit(100);
+ }
+
+ if (wait_pid(&wstat,pid) == -1)
+ strerr_die2x(111,FATAL,"wait failed");
+ if (wait_crashed(wstat))
+ strerr_die2x(111,FATAL,"child crashed");
+ switch(wait_exitcode(wstat)) {
+ case 0: _exit(100);
+ case 111: strerr_die2x(111,FATAL,"temporary child error");
+ default: _exit(0);
+ }
+}
diff --git a/exit.h b/exit.h
new file mode 100644
index 0000000..39011c8
--- /dev/null
+++ b/exit.h
@@ -0,0 +1,6 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
diff --git a/extra.h b/extra.h
new file mode 100644
index 0000000..c598175
--- /dev/null
+++ b/extra.h
@@ -0,0 +1,7 @@
+#ifndef EXTRA_H
+#define EXTRA_H
+
+#define QUEUE_EXTRA ""
+#define QUEUE_EXTRALEN 0
+
+#endif
diff --git a/fd.h b/fd.h
new file mode 100644
index 0000000..c3d6e3e
--- /dev/null
+++ b/fd.h
@@ -0,0 +1,7 @@
+#ifndef FD_H
+#define FD_H
+
+extern int fd_copy();
+extern int fd_move();
+
+#endif
diff --git a/fd_copy.3 b/fd_copy.3
new file mode 100644
index 0000000..758a7e7
--- /dev/null
+++ b/fd_copy.3
@@ -0,0 +1,44 @@
+.TH fd_copy 3
+.SH NAME
+fd_copy \- duplicate a descriptor
+.SH SYNTAX
+.B #include <fd.h>
+
+int \fBfd_copy\fP(\fIto\fR,\fIfrom\fR);
+
+int \fIto\fR;
+.br
+int \fIfrom\fR;
+.SH DESCRIPTION
+.B fd_copy
+copies
+descriptor
+.I from
+to descriptor
+.IR to .
+If
+.I to
+was already open,
+.B fd_copy
+closes it.
+.B fd_copy
+always leaves
+.I from
+intact;
+if
+.I to
+and
+.I from
+are the same number,
+.B fd_copy
+does nothing.
+
+.B fd_copy
+returns 0 on success, -1 on error.
+.B fd_copy
+does not guarantee that
+.I to
+will remain open, if it was open, in case of error.
+.SH "SEE ALSO"
+dup(2),
+fd_move(3)
diff --git a/fd_copy.c b/fd_copy.c
new file mode 100644
index 0000000..b9f7167
--- /dev/null
+++ b/fd_copy.c
@@ -0,0 +1,13 @@
+#include <fcntl.h>
+#include "fd.h"
+
+int fd_copy(to,from)
+int to;
+int from;
+{
+ if (to == from) return 0;
+ if (fcntl(from,F_GETFL,0) == -1) return -1;
+ close(to);
+ if (fcntl(from,F_DUPFD,to) == -1) return -1;
+ return 0;
+}
diff --git a/fd_move.3 b/fd_move.3
new file mode 100644
index 0000000..94aa1b7
--- /dev/null
+++ b/fd_move.3
@@ -0,0 +1,41 @@
+.TH fd_move 3
+.SH NAME
+fd_move \- renumber a descriptor
+.SH SYNTAX
+.B #include <fd.h>
+
+int \fBfd_move\fP(\fIto\fR,\fIfrom\fR);
+
+int \fIto\fR;
+.br
+int \fIfrom\fR;
+.SH DESCRIPTION
+.B fd_move
+moves
+descriptor
+.I from
+to descriptor
+.IR to .
+If
+.I to
+was already open,
+.B fd_move
+closes it.
+If the move is successful,
+.B fd_move
+closes
+.IR from .
+Exception:
+if
+.I to
+and
+.I from
+are the same number,
+.B fd_move
+does nothing.
+
+.B fd_move
+returns 0 on success, -1 on error.
+.SH "SEE ALSO"
+dup(2),
+fd_copy(3)
diff --git a/fd_move.c b/fd_move.c
new file mode 100644
index 0000000..1aa557f
--- /dev/null
+++ b/fd_move.c
@@ -0,0 +1,11 @@
+#include "fd.h"
+
+int fd_move(to,from)
+int to;
+int from;
+{
+ if (to == from) return 0;
+ if (fd_copy(to,from) == -1) return -1;
+ close(from);
+ return 0;
+}
diff --git a/fifo.c b/fifo.c
new file mode 100644
index 0000000..1f3d150
--- /dev/null
+++ b/fifo.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "hasmkffo.h"
+#include "fifo.h"
+
+#ifdef HASMKFIFO
+int fifo_make(fn,mode) char *fn; int mode; { return mkfifo(fn,mode); }
+#else
+int fifo_make(fn,mode) char *fn; int mode; { return mknod(fn,S_IFIFO | mode,0); }
+#endif
diff --git a/fifo.h b/fifo.h
new file mode 100644
index 0000000..2c5469b
--- /dev/null
+++ b/fifo.h
@@ -0,0 +1,6 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+extern int fifo_make();
+
+#endif
diff --git a/fifo_make.3 b/fifo_make.3
new file mode 100644
index 0000000..489233d
--- /dev/null
+++ b/fifo_make.3
@@ -0,0 +1,24 @@
+.TH fifo_make 3
+.SH NAME
+fifo_make \- create a named pipe
+.SH SYNTAX
+.B #include <fifo.h>
+
+int \fBfifo_make\fP(\fIfn\fR,\fImode\fR);
+
+char *\fIfn\fR;
+.br
+int \fImode\fR;
+.SH DESCRIPTION
+.B fifo_make
+creates a new named pipe
+with name
+.I fn
+and mode
+.I mode
+(modified by the process umask).
+
+.B fifo_make
+returns 0 on success, -1 on error.
+.SH "SEE ALSO"
+mkfifo(2)
diff --git a/find-systype.sh b/find-systype.sh
new file mode 100644
index 0000000..16266d3
--- /dev/null
+++ b/find-systype.sh
@@ -0,0 +1,144 @@
+# oper-:arch-:syst-:chip-:kern-
+# oper = operating system type; e.g., sunos-4.1.4
+# arch = machine language; e.g., sparc
+# syst = which binaries can run; e.g., sun4
+# chip = chip model; e.g., micro-2-80
+# kern = kernel version; e.g., sun4m
+# dependence: arch --- chip
+# \ \
+# oper --- syst --- kern
+# so, for example, syst is interpreted in light of oper, but chip is not.
+# anyway, no slashes, no extra colons, no uppercase letters.
+# the point of the extra -'s is to ease parsing: can add hierarchies later.
+# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
+# and i386-486 (486s do have more instructions, you know) as well as i386.
+# the idea here is to include ALL useful available information.
+
+exec 2>/dev/null
+sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`"
+if [ x"$sys" != x ]
+then
+ unamer="`uname -r | tr /: ..`"
+ unamem="`uname -m | tr /: ..`"
+ unamev="`uname -v | tr /: ..`"
+
+ case "$sys" in
+ bsd.os)
+ # in bsd 4.4, uname -v does not have useful info.
+ # in bsd 4.4, uname -m is arch, not chip.
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`"
+ kern=""
+ ;;
+ freebsd)
+ # see above about bsd 4.4
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`" # hopefully
+ kern=""
+ ;;
+ netbsd)
+ # see above about bsd 4.4
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`" # hopefully
+ kern=""
+ ;;
+ linux)
+ # as in bsd 4.4, uname -v does not have useful info.
+ oper="$sys-$unamer"
+ syst=""
+ chip="$unamem"
+ kern=""
+ case "$chip" in
+ i386|i486|i586|i686)
+ arch="i386"
+ ;;
+ alpha)
+ arch="alpha"
+ ;;
+ esac
+ ;;
+ aix)
+ # naturally IBM has to get uname -r and uname -v backwards. dorks.
+ oper="$sys-$unamev-$unamer"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ sunos)
+ oper="$sys-$unamer-$unamev"
+ arch="`(uname -p || mach) | tr /: ..`"
+ syst="`arch | tr /: ..`"
+ chip="$unamem" # this is wrong; is there any way to get the real info?
+ kern="`arch -k | tr /: ..`"
+ ;;
+ unix_sv)
+ oper="$sys-$unamer-$unamev"
+ arch="`uname -m`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ *)
+ oper="$sys-$unamer-$unamev"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ esac
+else
+ $CC -c trycpp.c
+ $LD -o trycpp trycpp.o
+ case `./trycpp` in
+ nextstep)
+ oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
+ arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
+ syst=""
+ chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
+ kern=""
+ ;;
+ *)
+ oper="unknown"
+ arch=""
+ syst=""
+ chip=""
+ kern=""
+ ;;
+ esac
+ rm -f trycpp.o trycpp
+fi
+
+case "$chip" in
+80486)
+ # let's try to be consistent here. (BSD/OS)
+ chip=i486
+ ;;
+i486DX)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx
+ ;;
+i486.DX2)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx2
+ ;;
+Intel.586)
+ # no, you nitwits, there is no such chip. (NeXTStep)
+ chip=pentium
+ ;;
+i586)
+ # no, you nitwits, there is no such chip. (Linux)
+ chip=pentium
+ ;;
+i686)
+ # STOP SAYING THAT! (Linux)
+ chip=ppro
+esac
+
+echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]'
diff --git a/fmt.h b/fmt.h
new file mode 100644
index 0000000..ba2fe16
--- /dev/null
+++ b/fmt.h
@@ -0,0 +1,25 @@
+#ifndef FMT_H
+#define FMT_H
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned int fmt_uint();
+extern unsigned int fmt_uint0();
+extern unsigned int fmt_xint();
+extern unsigned int fmt_nbbint();
+extern unsigned int fmt_ushort();
+extern unsigned int fmt_xshort();
+extern unsigned int fmt_nbbshort();
+extern unsigned int fmt_ulong();
+extern unsigned int fmt_xlong();
+extern unsigned int fmt_nbblong();
+
+extern unsigned int fmt_plusminus();
+extern unsigned int fmt_minus();
+extern unsigned int fmt_0x();
+
+extern unsigned int fmt_str();
+extern unsigned int fmt_strn();
+
+#endif
diff --git a/fmt_str.c b/fmt_str.c
new file mode 100644
index 0000000..48930cf
--- /dev/null
+++ b/fmt_str.c
@@ -0,0 +1,12 @@
+#include "fmt.h"
+
+unsigned int fmt_str(s,t)
+register char *s; register char *t;
+{
+ register unsigned int len;
+ char ch;
+ len = 0;
+ if (s) { while (ch = t[len]) s[len++] = ch; }
+ else while (t[len]) len++;
+ return len;
+}
diff --git a/fmt_strn.c b/fmt_strn.c
new file mode 100644
index 0000000..3ef58a6
--- /dev/null
+++ b/fmt_strn.c
@@ -0,0 +1,12 @@
+#include "fmt.h"
+
+unsigned int fmt_strn(s,t,n)
+register char *s; register char *t; register unsigned int n;
+{
+ register unsigned int len;
+ char ch;
+ len = 0;
+ if (s) { while (n-- && (ch = t[len])) s[len++] = ch; }
+ else while (n-- && t[len]) len++;
+ return len;
+}
diff --git a/fmt_uint.c b/fmt_uint.c
new file mode 100644
index 0000000..8595be8
--- /dev/null
+++ b/fmt_uint.c
@@ -0,0 +1,6 @@
+#include "fmt.h"
+
+unsigned int fmt_uint(s,u) register char *s; register unsigned int u;
+{
+ register unsigned long l; l = u; return fmt_ulong(s,l);
+}
diff --git a/fmt_uint0.c b/fmt_uint0.c
new file mode 100644
index 0000000..81705f6
--- /dev/null
+++ b/fmt_uint0.c
@@ -0,0 +1,10 @@
+#include "fmt.h"
+
+unsigned int fmt_uint0(s,u,n) char *s; unsigned int u; unsigned int n;
+{
+ unsigned int len;
+ len = fmt_uint(FMT_LEN,u);
+ while (len < n) { if (s) *s++ = '0'; ++len; }
+ if (s) fmt_uint(s,u);
+ return len;
+}
diff --git a/fmt_ulong.c b/fmt_ulong.c
new file mode 100644
index 0000000..ab12e8c
--- /dev/null
+++ b/fmt_ulong.c
@@ -0,0 +1,13 @@
+#include "fmt.h"
+
+unsigned int fmt_ulong(s,u) register char *s; register unsigned long u;
+{
+ register unsigned int len; register unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
diff --git a/fmtqfn.c b/fmtqfn.c
new file mode 100644
index 0000000..6229b4a
--- /dev/null
+++ b/fmtqfn.c
@@ -0,0 +1,24 @@
+#include "fmtqfn.h"
+#include "fmt.h"
+#include "auto_split.h"
+
+unsigned int fmtqfn(s,dirslash,id,flagsplit)
+char *s;
+char *dirslash;
+unsigned long id;
+int flagsplit;
+{
+ unsigned int len;
+ unsigned int i;
+
+ len = 0;
+ i = fmt_str(s,dirslash); len += i; if (s) s += i;
+ if (flagsplit)
+ {
+ i = fmt_ulong(s,id % auto_split); len += i; if (s) s += i;
+ i = fmt_str(s,"/"); len += i; if (s) s += i;
+ }
+ i = fmt_ulong(s,id); len += i; if (s) s += i;
+ if (s) *s++ = 0; ++len;
+ return len;
+}
diff --git a/fmtqfn.h b/fmtqfn.h
new file mode 100644
index 0000000..a449aa1
--- /dev/null
+++ b/fmtqfn.h
@@ -0,0 +1,8 @@
+#ifndef FMTQFN_H
+#define FMTQFN_H
+
+extern unsigned int fmtqfn();
+
+#define FMTQFN 40 /* maximum space needed, if len(dirslash) <= 10 */
+
+#endif
diff --git a/forgeries.7 b/forgeries.7
new file mode 100644
index 0000000..cb99fa7
--- /dev/null
+++ b/forgeries.7
@@ -0,0 +1,104 @@
+.TH forgeries 7
+.SH "NAME"
+forgeries \- how easy it is to forge mail
+.SH "SUMMARY"
+An electronic mail message can easily be forged.
+Almost everything in it,
+including the return address,
+is completely under the control of the sender.
+
+An electronic mail message can be manually traced to its origin
+if (1) all system administrators of intermediate machines
+are both cooperative and competent,
+(2) the sender did not break low-level TCP/IP security,
+and
+(3) all intermediate machines are secure.
+
+Users of
+.I cryptography
+can automatically ensure the integrity and secrecy
+of their mail messages, as long as
+the sending and receiving machines are secure.
+.SH "FORGERIES"
+Like postal mail,
+electronic mail can be created entirely at the whim of the sender.
+.BR From ,
+.BR Sender ,
+.BR Return-Path ,
+and
+.BR Message-ID
+can all contain whatever information the sender wants.
+
+For example, if you inject a message through
+.B sendmail
+or
+.B qmail-inject
+or
+.BR SMTP ,
+you can simply type in a
+.B From
+field.
+In fact,
+.B qmail-inject
+lets you set up
+.BR MAILUSER ,
+.BR MAILHOST ,
+and
+.B MAILNAME
+environment variables
+to produce your desired
+.B From
+field on every message.
+.SH "TRACING FORGERIES"
+Like postal mail,
+electronic mail is postmarked when it is sent.
+Each machine that receives an electronic mail message
+adds a
+.B Received
+line to the top.
+
+A modern
+.B Received
+line contains quite a bit of information.
+In conjunction with the machine's logs,
+it lets a competent system administrator
+determine where the machine received the message from,
+as long as the sender did not break low-level TCP/IP security
+or security on that machine.
+
+Large multi-user machines often come with inadequate logging software.
+Fortunately, a system administrator can easily obtain a copy of a
+931/1413/Ident/TAP server, such as
+.BR pidentd .
+Unfortunately,
+some system administrators fail to do this,
+and are thus unable to figure out which local user
+was responsible for generating a message.
+
+If all intermediate system administrators are competent,
+and the sender did not break machine security or low-level TCP/IP security,
+it is possible to trace a message backwards.
+Unfortunately, some traces are stymied by intermediate system
+administrators who are uncooperative or untrustworthy.
+.SH "CRYPTOGRAPHY"
+The sender of a mail message may place his message into a
+.I cryptographic
+envelope stamped with his seal.
+Strong cryptography guarantees that any two messages with the same seal
+were sent by the same cryptographic entity:
+perhaps a single person, perhaps a group of cooperating people,
+but in any case somebody who knows a secret originally held
+only by the creator of the seal.
+The seal is called a
+.I public key\fR.
+
+Unfortunately, the creator of the seal is often an insecure machine,
+or an untrustworthy central agency,
+but most of the time seals are kept secure.
+
+One popular cryptographic program is
+.BR pgp .
+.SH "SEE ALSO"
+pgp(1),
+identd(8),
+qmail-header(8)
diff --git a/fork.h1 b/fork.h1
new file mode 100644
index 0000000..b786255
--- /dev/null
+++ b/fork.h1
@@ -0,0 +1,7 @@
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+#define vfork fork
+
+#endif
diff --git a/fork.h2 b/fork.h2
new file mode 100644
index 0000000..41773b6
--- /dev/null
+++ b/fork.h2
@@ -0,0 +1,7 @@
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+extern int vfork();
+
+#endif
diff --git a/forward.1 b/forward.1
new file mode 100644
index 0000000..d0a7f95
--- /dev/null
+++ b/forward.1
@@ -0,0 +1,24 @@
+.TH forward 1
+.SH NAME
+forward \- forward new mail to one or more addresses
+.SH SYNOPSIS
+in
+.BR .qmail :
+.B |forward
+.I address ...
+.SH DESCRIPTION
+.B forward
+forwards each new mail message to the specified list of addresses.
+It is a simple wrapper around
+.BR qmail-queue .
+It achieves the same results as listing each
+.I address
+separately in
+.BR .qmail ,
+but it is more programmable since
+.I address
+can be constructed on the fly.
+.SH "SEE ALSO"
+dot-qmail(5),
+qmail-command(8),
+qmail-queue(8)
diff --git a/forward.c b/forward.c
new file mode 100644
index 0000000..e7390aa
--- /dev/null
+++ b/forward.c
@@ -0,0 +1,60 @@
+#include "sig.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "env.h"
+#include "qmail.h"
+#include "strerr.h"
+#include "substdio.h"
+#include "fmt.h"
+
+#define FATAL "forward: fatal: "
+
+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+
+struct qmail qqt;
+
+int mywrite(fd,buf,len) int fd; char *buf; int len;
+{
+ qmail_put(&qqt,buf,len);
+ return len;
+}
+
+char inbuf[SUBSTDIO_INSIZE];
+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)
+int argc;
+char **argv;
+{
+ char *sender;
+ char *dtline;
+ char *qqx;
+
+ sig_pipeignore();
+
+ sender = env_get("NEWSENDER");
+ if (!sender)
+ strerr_die2x(100,FATAL,"NEWSENDER not set");
+ dtline = env_get("DTLINE");
+ if (!dtline)
+ strerr_die2x(100,FATAL,"DTLINE not set");
+
+ if (qmail_open(&qqt) == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+ qmail_puts(&qqt,dtline);
+ if (substdio_copy(&ssout,&ssin) != 0)
+ strerr_die2sys(111,FATAL,"unable to read message: ");
+ substdio_flush(&ssout);
+
+ num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
+
+ qmail_from(&qqt,sender);
+ while (*++argv) qmail_to(&qqt,*argv);
+ qqx = qmail_close(&qqt);
+ if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
+ strerr_die2x(0,"forward: qp ",num);
+}
diff --git a/gen_alloc.h b/gen_alloc.h
new file mode 100644
index 0000000..b94a956
--- /dev/null
+++ b/gen_alloc.h
@@ -0,0 +1,7 @@
+#ifndef GEN_ALLOC_H
+#define GEN_ALLOC_H
+
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+ typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
+
+#endif
diff --git a/gen_allocdefs.h b/gen_allocdefs.h
new file mode 100644
index 0000000..783a9b1
--- /dev/null
+++ b/gen_allocdefs.h
@@ -0,0 +1,34 @@
+#ifndef GEN_ALLOC_DEFS_H
+#define GEN_ALLOC_DEFS_H
+
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; n += x->len; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(x,i) register ta *x; register type *i; \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+#endif
diff --git a/getln.3 b/getln.3
new file mode 100644
index 0000000..ffe1953
--- /dev/null
+++ b/getln.3
@@ -0,0 +1,51 @@
+.TH getln 3
+.SH NAME
+getln \- read one line of data
+.SH SYNTAX
+.B #include <getln.h>
+
+int \fBgetln\fP(&\fIss\fR,&\fIsa\fR,&\fImatch\fR,\fIsep\fR);
+
+substdio \fIss\fR;
+.br
+stralloc \fIsa\fR;
+.br
+int \fImatch\fR;
+.br
+int \fIsep\fR;
+.SH DESCRIPTION
+.B getln
+reads a line of characters, terminated by a
+.I sep
+character,
+from
+.IR ss .
+It returns the line in
+.I sa
+and sets
+.I match
+to 1.
+
+If
+.B getln
+sees end-of-input before it sees
+.IR sep ,
+it returns the partial line in
+.I sa
+and sets
+.I match
+to 0.
+
+.B getln
+normally returns 0.
+If it runs out of memory,
+or encounters an error from
+.IR ss ,
+it returns -1,
+setting
+.B errno
+appropriately.
+.SH "SEE ALSO"
+stralloc(3),
+substdio(3),
+getln2(3)
diff --git a/getln.c b/getln.c
new file mode 100644
index 0000000..c5cb097
--- /dev/null
+++ b/getln.c
@@ -0,0 +1,20 @@
+#include "substdio.h"
+#include "byte.h"
+#include "stralloc.h"
+#include "getln.h"
+
+int getln(ss,sa,match,sep)
+register substdio *ss;
+register stralloc *sa;
+int *match;
+int sep;
+{
+ char *cont;
+ unsigned int clen;
+
+ if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1;
+ if (!clen) { *match = 0; return 0; }
+ if (!stralloc_catb(sa,cont,clen)) return -1;
+ *match = 1;
+ return 0;
+}
diff --git a/getln.h b/getln.h
new file mode 100644
index 0000000..cf4f934
--- /dev/null
+++ b/getln.h
@@ -0,0 +1,7 @@
+#ifndef GETLN_H
+#define GETLN_H
+
+extern int getln();
+extern int getln2();
+
+#endif
diff --git a/getln2.3 b/getln2.3
new file mode 100644
index 0000000..f95105a
--- /dev/null
+++ b/getln2.3
@@ -0,0 +1,64 @@
+.TH getln2 3
+.SH NAME
+getln2 \- read one line of data
+.SH SYNTAX
+.B #include <getln.h>
+
+int \fBgetln2\fP(&\fIss\fR,&\fIsa\fR,&\fIcont\fR,&\fIclen\fR,\fIsep\fR);
+
+substdio \fIss\fR;
+.br
+stralloc \fIsa\fR;
+.br
+char *\fIcont\fR;
+.br
+unsigned int \fIclen\fR;
+.br
+int \fIsep\fR;
+.SH DESCRIPTION
+.B getln2
+reads a line of characters, terminated by a
+.I sep
+character,
+from
+.IR ss .
+
+The line is returned in two pieces.
+The first piece is stored in
+.IR sa .
+The second piece is
+.IR cont ,
+a pointer to
+.I clen
+characters inside the
+.I ss
+buffer.
+The second piece must be copied somewhere else
+before
+.I ss
+is used again.
+
+If
+.B getln2
+sees end-of-input before it sees
+.IR sep ,
+it sets
+.I clen
+to 0 and does not set
+.IR cont .
+It puts the partial line into
+.IR sa .
+
+.B getln2
+normally returns 0.
+If it runs out of memory,
+or encounters an error from
+.IR ss ,
+it returns -1,
+setting
+.B errno
+appropriately.
+.SH "SEE ALSO"
+stralloc(3),
+substdio(3),
+getln(3)
diff --git a/getln2.c b/getln2.c
new file mode 100644
index 0000000..9e576e9
--- /dev/null
+++ b/getln2.c
@@ -0,0 +1,31 @@
+#include "substdio.h"
+#include "stralloc.h"
+#include "byte.h"
+#include "getln.h"
+
+int getln2(ss,sa,cont,clen,sep)
+register substdio *ss;
+register stralloc *sa;
+/*@out@*/char **cont;
+/*@out@*/unsigned int *clen;
+int sep;
+{
+ register char *x;
+ register unsigned int i;
+ int n;
+
+ if (!stralloc_ready(sa,0)) return -1;
+ sa->len = 0;
+
+ for (;;) {
+ n = substdio_feed(ss);
+ if (n < 0) return -1;
+ if (n == 0) { *clen = 0; return 0; }
+ x = substdio_PEEK(ss);
+ i = byte_chr(x,n,sep);
+ if (i < n) { substdio_SEEK(ss,*clen = i + 1); *cont = x; return 0; }
+ if (!stralloc_readyplus(sa,n)) return -1;
+ i = sa->len;
+ sa->len = i + substdio_get(ss,sa->s + i,n);
+ }
+}
diff --git a/gfrom.c b/gfrom.c
new file mode 100644
index 0000000..87f9ad4
--- /dev/null
+++ b/gfrom.c
@@ -0,0 +1,10 @@
+#include "str.h"
+#include "gfrom.h"
+
+int gfrom(s,len)
+char *s;
+int len;
+{
+ while ((len > 0) && (*s == '>')) { ++s; --len; }
+ return (len >= 5) && !str_diffn(s,"From ",5);
+}
diff --git a/gfrom.h b/gfrom.h
new file mode 100644
index 0000000..488193c
--- /dev/null
+++ b/gfrom.h
@@ -0,0 +1,6 @@
+#ifndef GFROM_H
+#define GFROM_H
+
+extern int gfrom();
+
+#endif
diff --git a/headerbody.c b/headerbody.c
new file mode 100644
index 0000000..91965c0
--- /dev/null
+++ b/headerbody.c
@@ -0,0 +1,87 @@
+#include "stralloc.h"
+#include "substdio.h"
+#include "getln.h"
+#include "hfield.h"
+#include "headerbody.h"
+
+static int getsa(ss,sa,match)
+substdio *ss;
+stralloc *sa;
+int *match;
+{
+ if (!*match) return 0;
+ if (getln(ss,sa,match,'\n') == -1) return -1;
+ if (*match) return 1;
+ if (!sa->len) return 0;
+ if (!stralloc_append(sa,"\n")) return -1;
+ return 1;
+}
+
+static stralloc line = {0};
+static stralloc nextline = {0};
+
+int headerbody(ss,dohf,hdone,dobl)
+substdio *ss;
+void (*dohf)();
+void (*hdone)();
+void (*dobl)();
+{
+ int match;
+ int flaglineok;
+ match = 1;
+ flaglineok = 0;
+ for (;;)
+ {
+ switch(getsa(ss,&nextline,&match))
+ {
+ case -1:
+ return -1;
+ case 0:
+ if (flaglineok) dohf(&line);
+ hdone();
+ /* no message body; could insert blank line here */
+ return 0;
+ }
+ if (flaglineok)
+ {
+ if ((nextline.s[0] == ' ') || (nextline.s[0] == '\t'))
+ {
+ if (!stralloc_cat(&line,&nextline)) return -1;
+ continue;
+ }
+ dohf(&line);
+ }
+ if (nextline.len == 1)
+ {
+ hdone();
+ dobl(&nextline);
+ break;
+ }
+ if (stralloc_starts(&nextline,"From "))
+ {
+ if (!stralloc_copys(&line,"MBOX-Line: ")) return -1;
+ if (!stralloc_cat(&line,&nextline)) return -1;
+ }
+ else
+ if (hfield_valid(nextline.s,nextline.len))
+ {
+ if (!stralloc_copy(&line,&nextline)) return -1;
+ }
+ else
+ {
+ hdone();
+ if (!stralloc_copys(&line,"\n")) return -1;
+ dobl(&line);
+ dobl(&nextline);
+ break;
+ }
+ flaglineok = 1;
+ }
+ for (;;)
+ switch(getsa(ss,&nextline,&match))
+ {
+ case -1: return -1;
+ case 0: return 0;
+ case 1: dobl(&nextline);
+ }
+}
diff --git a/headerbody.h b/headerbody.h
new file mode 100644
index 0000000..3bb2e2f
--- /dev/null
+++ b/headerbody.h
@@ -0,0 +1,6 @@
+#ifndef HEADERBODY_H
+#define HEADERBODY_H
+
+extern int headerbody();
+
+#endif
diff --git a/hfield.c b/hfield.c
new file mode 100644
index 0000000..06de0be
--- /dev/null
+++ b/hfield.c
@@ -0,0 +1,125 @@
+#include "hfield.h"
+
+static char *(hname[]) = {
+ "unknown-header"
+, "sender"
+, "from"
+, "reply-to"
+, "to"
+, "cc"
+, "bcc"
+, "date"
+, "message-id"
+, "subject"
+, "resent-sender"
+, "resent-from"
+, "resent-reply-to"
+, "resent-to"
+, "resent-cc"
+, "resent-bcc"
+, "resent-date"
+, "resent-message-id"
+, "return-receipt-to"
+, "errors-to"
+, "apparently-to"
+, "received"
+, "return-path"
+, "delivered-to"
+, "content-length"
+, "content-type"
+, "content-transfer-encoding"
+, "notice-requested-upon-delivery-to"
+, "mail-followup-to"
+, 0
+};
+
+static int hmatch(s,len,t)
+char *s;
+int len;
+char *t;
+{
+ int i;
+ char ch;
+
+ for (i = 0;ch = t[i];++i)
+ {
+ if (i >= len) return 0;
+ if (ch != s[i])
+ {
+ if (ch == '-') return 0;
+ if (ch - 32 != s[i]) return 0;
+ }
+ }
+ for (;;)
+ {
+ if (i >= len) return 0;
+ ch = s[i];
+ if (ch == ':') return 1;
+ if ((ch != ' ') && (ch != '\t')) return 0;
+ ++i;
+ }
+}
+
+int hfield_known(s,len)
+char *s;
+int len;
+{
+ int i;
+ char *t;
+
+ for (i = 1;t = hname[i];++i)
+ if (hmatch(s,len,t))
+ return i;
+ return 0;
+}
+
+int hfield_valid(s,len)
+char *s;
+int len;
+{
+ int i;
+ int j;
+ char ch;
+
+ for (j = 0;j < len;++j)
+ if (s[j] == ':')
+ break;
+ if (j >= len) return 0;
+ while (j)
+ {
+ ch = s[j - 1];
+ if ((ch != ' ') && (ch != '\t'))
+ break;
+ --j;
+ }
+ if (!j) return 0;
+
+ for (i = 0;i < j;++i)
+ {
+ ch = s[i];
+ if (ch <= 32) return 0;
+ if (ch >= 127) return 0;
+ }
+ return 1;
+}
+
+unsigned int hfield_skipname(s,len)
+char *s;
+int len;
+{
+ int i;
+ char ch;
+
+ for (i = 0;i < len;++i)
+ if (s[i] == ':')
+ break;
+ if (i < len) ++i;
+ while (i < len)
+ {
+ ch = s[i];
+ if ((ch != '\t') && (ch != '\n') && (ch != '\r') && (ch != ' '))
+ break;
+ ++i;
+ }
+ return i;
+}
diff --git a/hfield.h b/hfield.h
new file mode 100644
index 0000000..20b30a5
--- /dev/null
+++ b/hfield.h
@@ -0,0 +1,38 @@
+#ifndef HFIELD_H
+#define HFIELD_H
+
+extern unsigned int hfield_skipname();
+extern int hfield_known();
+extern int hfield_valid();
+
+#define H_SENDER 1
+#define H_FROM 2
+#define H_REPLYTO 3
+#define H_TO 4
+#define H_CC 5
+#define H_BCC 6
+#define H_DATE 7
+#define H_MESSAGEID 8
+#define H_SUBJECT 9
+#define H_R_SENDER 10
+#define H_R_FROM 11
+#define H_R_REPLYTO 12
+#define H_R_TO 13
+#define H_R_CC 14
+#define H_R_BCC 15
+#define H_R_DATE 16
+#define H_R_MESSAGEID 17
+#define H_RETURNRECEIPTTO 18
+#define H_ERRORSTO 19
+#define H_APPARENTLYTO 20
+#define H_RECEIVED 21
+#define H_RETURNPATH 22
+#define H_DELIVEREDTO 23
+#define H_CONTENTLENGTH 24
+#define H_CONTENTTYPE 25
+#define H_CONTENTTRANSFERENCODING 26
+#define H_NOTICEREQUESTEDUPONDELIVERYTO 27
+#define H_MAILFOLLOWUPTO 28
+#define H_NUM 29
+
+#endif
diff --git a/hier.c b/hier.c
new file mode 100644
index 0000000..28e568d
--- /dev/null
+++ b/hier.c
@@ -0,0 +1,252 @@
+#include "auto_qmail.h"
+#include "auto_split.h"
+#include "auto_uids.h"
+#include "fmt.h"
+#include "fifo.h"
+
+char buf[100 + FMT_ULONG];
+
+void dsplit(base,uid,mode)
+char *base; /* must be under 100 bytes */
+int uid;
+int mode;
+{
+ char *x;
+ unsigned long i;
+
+ d(auto_qmail,base,uid,auto_gidq,mode);
+
+ for (i = 0;i < auto_split;++i) {
+ x = buf;
+ x += fmt_str(x,base);
+ x += fmt_str(x,"/");
+ x += fmt_ulong(x,i);
+ *x = 0;
+
+ d(auto_qmail,buf,uid,auto_gidq,mode);
+ }
+}
+
+void hier()
+{
+ h(auto_qmail,auto_uido,auto_gidq,0755);
+
+ d(auto_qmail,"control",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"users",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"bin",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"boot",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"doc",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat1",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat5",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat7",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat8",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man1",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man5",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man7",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755);
+
+ d(auto_qmail,"alias",auto_uida,auto_gidq,02755);
+
+ d(auto_qmail,"queue",auto_uidq,auto_gidq,0750);
+ d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700);
+ d(auto_qmail,"queue/intd",auto_uidq,auto_gidq,0700);
+ d(auto_qmail,"queue/todo",auto_uidq,auto_gidq,0750);
+ d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700);
+
+ dsplit("queue/mess",auto_uidq,0750);
+ dsplit("queue/info",auto_uids,0700);
+ dsplit("queue/local",auto_uids,0700);
+ dsplit("queue/remote",auto_uids,0700);
+
+ d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750);
+ z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644);
+ z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600);
+ p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622);
+
+ c(auto_qmail,"boot","home",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","home+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","proc",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","proc+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm1",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm1+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm2",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm2+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm3",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.alias",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.ctl",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.ids",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.maildir",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.mbox",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.vsm",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","TEST.deliver",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2rem",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2virt",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.nullclient",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.relaybad",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.relaygood",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.rem2local",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711);
+ c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","envelopes.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","envelopes.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","maildir.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","maildir.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","mbox.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","mbox.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","dot-qmail.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","dot-qmail.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-control.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-control.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-header.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-header.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-log.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-log.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-users.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man7","qmail-limits.7",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat7","qmail-limits.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man7","qmail.7",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat7","qmail.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man1","forward.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","forward.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","condredirect.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","condredirect.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","bouncesaying.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","bouncesaying.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","except.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","except.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","maildirmake.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","maildirmake.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","maildir2mbox.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","maildir2mbox.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","maildirwatch.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","maildirwatch.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","mailsubj.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","mailsubj.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","qreceipt.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","qreceipt.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","qbiff.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","qbiff.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","preline.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","preline.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","tcp-env.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-getpw.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-getpw.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-remote.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-remote.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-rspawn.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-rspawn.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-clean.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-clean.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-send.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-send.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-start.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-start.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","splogger.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","splogger.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-queue.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-queue.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-inject.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-newu.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-pw2u.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-pw2u.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qread.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qread.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qstat.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qstat.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-tcpok.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-popup.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qmqpc.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qmqpc.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qmqpd.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qmqpd.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qmtpd.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644);
+}
diff --git a/home+df.sh b/home+df.sh
new file mode 100644
index 0000000..7885cdf
--- /dev/null
+++ b/home+df.sh
@@ -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
diff --git a/home.sh b/home.sh
new file mode 100644
index 0000000..c96c02b
--- /dev/null
+++ b/home.sh
@@ -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
diff --git a/hostname.c b/hostname.c
new file mode 100644
index 0000000..39c7f87
--- /dev/null
+++ b/hostname.c
@@ -0,0 +1,17 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "readwrite.h"
+#include "exit.h"
+
+char host[256];
+
+void main()
+{
+ host[0] = 0; /* sigh */
+ gethostname(host,sizeof(host));
+ host[sizeof(host) - 1] = 0;
+ substdio_puts(subfdoutsmall,host);
+ substdio_puts(subfdoutsmall,"\n");
+ substdio_flush(subfdoutsmall);
+ _exit(0);
+}
diff --git a/idedit.c b/idedit.c
new file mode 100644
index 0000000..e6747b5
--- /dev/null
+++ b/idedit.c
@@ -0,0 +1,147 @@
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include "readwrite.h"
+#include "exit.h"
+#include "scan.h"
+#include "fmt.h"
+#include "strerr.h"
+#include "open.h"
+#include "seek.h"
+#include "fork.h"
+
+#define FATAL "idedit: fatal: "
+#define WARNING "idedit: warning: "
+
+int fd;
+
+void byte(pos,value)
+char *pos;
+unsigned int value;
+{
+ unsigned long u;
+ unsigned char ch;
+
+ if (pos[scan_ulong(pos,&u)]) return;
+
+ if (seek_set(fd,(seek_pos) u) == -1)
+ strerr_die2sys(111,FATAL,"unable to seek: ");
+
+ ch = value;
+ if (write(fd,&ch,1) != 1)
+ strerr_die2sys(111,FATAL,"unable to write: ");
+}
+
+char *args[10];
+
+void run()
+{
+ int pid;
+ int wstat;
+
+ pid = fork();
+ if (pid == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+
+ if (pid == 0) {
+ execv(*args,args);
+ strerr_die4sys(111,WARNING,"unable to run ",*args,": ");
+ }
+
+ if (wait_pid(&wstat,pid) != pid)
+ strerr_die2sys(111,FATAL,"waitpid surprise");
+}
+
+void u(account,group,home,pos0,pos1,pos2,pos3)
+char *account;
+char *group;
+char *home;
+char *pos0;
+char *pos1;
+char *pos2;
+char *pos3;
+{
+ struct passwd *pw;
+ unsigned int value;
+
+ pw = getpwnam(account);
+
+ if (!pw && group) {
+ args[0] = "add-account";
+ args[1] = account;
+ args[2] = group;
+ args[3] = home;
+ args[4] = 0;
+ run();
+ pw = getpwnam(account);
+ }
+
+ if (!pw)
+ strerr_die3x(111,FATAL,"unable to find uid for ",account);
+
+ value = pw->pw_uid;
+ byte(pos0,value); value >>= 8;
+ byte(pos1,value); value >>= 8;
+ byte(pos2,value); value >>= 8;
+ byte(pos3,value); value >>= 8;
+ if (value)
+ strerr_die3x(111,FATAL,"excessively large uid for ",account);
+}
+
+void g(group,pos0,pos1,pos2,pos3)
+char *group;
+char *pos0;
+char *pos1;
+char *pos2;
+char *pos3;
+{
+ struct group *gr;
+ unsigned int value;
+
+ gr = getgrnam(group);
+
+ if (!gr) {
+ args[0] = "add-group";
+ args[1] = group;
+ args[2] = 0;
+ run();
+ gr = getgrnam(group);
+ }
+
+ if (!gr)
+ strerr_die3x(111,FATAL,"unable to find gid for ",group);
+
+ value = gr->gr_gid;
+ byte(pos0,value); value >>= 8;
+ byte(pos1,value); value >>= 8;
+ byte(pos2,value); value >>= 8;
+ byte(pos3,value); value >>= 8;
+ if (value)
+ strerr_die3x(111,FATAL,"excessively large gid for ",group);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ if (argc < 42) _exit(100);
+
+ fd = open_write(argv[1]);
+ if (fd == -1) strerr_die4sys(111,FATAL,"unable to open ",argv[1],": ");
+
+ g("qmail",argv[34],argv[35],argv[36],argv[37]);
+ g("nofiles",argv[38],argv[39],argv[40],argv[41]);
+
+ u("root",(char *) 0,"/",argv[14],argv[15],argv[16],argv[17]);
+
+ u("qmaild","nofiles","/var/qmail",argv[6],argv[7],argv[8],argv[9]);
+ u("qmaill","nofiles","/var/qmail",argv[10],argv[11],argv[12],argv[13]);
+ u("qmailp","nofiles","/var/qmail",argv[18],argv[19],argv[20],argv[21]);
+ u("alias","nofiles","/var/qmail/alias",argv[2],argv[3],argv[4],argv[5]);
+
+ u("qmailq","qmail","/var/qmail",argv[22],argv[23],argv[24],argv[25]);
+ u("qmailr","qmail","/var/qmail",argv[26],argv[27],argv[28],argv[29]);
+ u("qmails","qmail","/var/qmail",argv[30],argv[31],argv[32],argv[33]);
+
+ _exit(0);
+}
diff --git a/install-big.c b/install-big.c
new file mode 100644
index 0000000..df813df
--- /dev/null
+++ b/install-big.c
@@ -0,0 +1,285 @@
+#include "auto_qmail.h"
+#include "auto_split.h"
+#include "auto_uids.h"
+#include "fmt.h"
+#include "fifo.h"
+
+char buf[100 + FMT_ULONG];
+
+void dsplit(base,uid,mode)
+char *base; /* must be under 100 bytes */
+int uid;
+int mode;
+{
+ char *x;
+ unsigned long i;
+
+ d(auto_qmail,base,uid,auto_gidq,mode);
+
+ for (i = 0;i < auto_split;++i) {
+ x = buf;
+ x += fmt_str(x,base);
+ x += fmt_str(x,"/");
+ x += fmt_ulong(x,i);
+ *x = 0;
+
+ d(auto_qmail,buf,uid,auto_gidq,mode);
+ }
+}
+
+void hier()
+{
+ h(auto_qmail,auto_uido,auto_gidq,0755);
+
+ d(auto_qmail,"control",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"users",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"bin",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"boot",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"doc",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat1",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat5",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat7",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/cat8",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man1",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man5",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man7",auto_uido,auto_gidq,0755);
+ d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755);
+
+ d(auto_qmail,"alias",auto_uida,auto_gidq,02755);
+
+ d(auto_qmail,"queue",auto_uidq,auto_gidq,0750);
+ d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700);
+ d(auto_qmail,"queue/intd",auto_uidq,auto_gidq,0700);
+ d(auto_qmail,"queue/todo",auto_uidq,auto_gidq,0750);
+ d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700);
+
+ dsplit("queue/mess",auto_uidq,0750);
+ dsplit("queue/info",auto_uids,0700);
+ dsplit("queue/local",auto_uids,0700);
+ dsplit("queue/remote",auto_uids,0700);
+
+ d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750);
+ z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644);
+ z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600);
+ p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622);
+
+ c(auto_qmail,"boot","home",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","home+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","proc",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","proc+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm1",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm1+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm2",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm2+df",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm3",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.alias",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.ctl",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.ids",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.maildir",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.mbox",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","INSTALL.vsm",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","TEST.deliver",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2rem",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.local2virt",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.nullclient",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.relaybad",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.relaygood",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"doc","PIC.rem2local",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711);
+ c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
+ c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","envelopes.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","envelopes.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","maildir.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","maildir.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","mbox.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","mbox.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","dot-qmail.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","dot-qmail.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-control.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-control.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-header.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-header.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-log.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-log.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","qmail-users.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man7","qmail-limits.7",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat7","qmail-limits.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man7","qmail.7",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat7","qmail.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man1","forward.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","forward.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","condredirect.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","condredirect.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","bouncesaying.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","bouncesaying.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","except.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","except.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","maildirmake.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","maildirmake.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","maildir2mbox.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","maildir2mbox.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","maildirwatch.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","maildirwatch.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","mailsubj.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","mailsubj.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","qreceipt.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","qreceipt.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","qbiff.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","qbiff.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","preline.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","preline.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","tcp-env.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-getpw.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-getpw.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-remote.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-remote.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-rspawn.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-rspawn.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-clean.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-clean.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-send.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-send.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-start.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-start.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","splogger.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","splogger.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-queue.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-queue.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-inject.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-newu.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-pw2u.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-pw2u.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qread.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qread.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qstat.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qstat.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-tcpok.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-popup.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qmqpc.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qmqpc.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qmqpd.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qmqpd.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-qmtpd.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"bin","dot-forward",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"man/man1","dot-forward.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","dot-forward.0",auto_uido,auto_gidq,0644);
+
+ d(auto_qmail,"doc/fastforward",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"bin","fastforward",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","printforward",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","setforward",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","newaliases",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","printmaillist",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","setmaillist",auto_uido,auto_gidq,0755);
+ c(auto_qmail,"bin","newinclude",auto_uido,auto_gidq,0755);
+
+ c(auto_qmail,"doc/fastforward","ALIASES",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/man1","fastforward.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","printforward.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","setforward.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","newaliases.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","printmaillist.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","setmaillist.1",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man1","newinclude.1",auto_uido,auto_gidq,0644);
+
+ c(auto_qmail,"man/cat1","fastforward.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","printforward.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","setforward.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","newaliases.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","printmaillist.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","setmaillist.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat1","newinclude.0",auto_uido,auto_gidq,0644);
+}
diff --git a/install.c b/install.c
new file mode 100644
index 0000000..95034f2
--- /dev/null
+++ b/install.c
@@ -0,0 +1,164 @@
+#include "substdio.h"
+#include "strerr.h"
+#include "error.h"
+#include "open.h"
+#include "readwrite.h"
+#include "exit.h"
+
+extern void hier();
+
+#define FATAL "install: fatal: "
+
+int fdsourcedir = -1;
+
+void h(home,uid,gid,mode)
+char *home;
+int uid;
+int gid;
+int mode;
+{
+ if (mkdir(home,0700) == -1)
+ if (errno != error_exist)
+ strerr_die4sys(111,FATAL,"unable to mkdir ",home,": ");
+ if (chown(home,uid,gid) == -1)
+ strerr_die4sys(111,FATAL,"unable to chown ",home,": ");
+ if (chmod(home,mode) == -1)
+ strerr_die4sys(111,FATAL,"unable to chmod ",home,": ");
+}
+
+void d(home,subdir,uid,gid,mode)
+char *home;
+char *subdir;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (mkdir(subdir,0700) == -1)
+ if (errno != error_exist)
+ strerr_die6sys(111,FATAL,"unable to mkdir ",home,"/",subdir,": ");
+ if (chown(subdir,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown ",home,"/",subdir,": ");
+ if (chmod(subdir,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",subdir,": ");
+}
+
+void p(home,fifo,uid,gid,mode)
+char *home;
+char *fifo;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (fifo_make(fifo,0700) == -1)
+ if (errno != error_exist)
+ strerr_die6sys(111,FATAL,"unable to mkfifo ",home,"/",fifo,": ");
+ if (chown(fifo,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown ",home,"/",fifo,": ");
+ if (chmod(fifo,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",fifo,": ");
+}
+
+char inbuf[SUBSTDIO_INSIZE];
+char outbuf[SUBSTDIO_OUTSIZE];
+substdio ssin;
+substdio ssout;
+
+void c(home,subdir,file,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int uid;
+int gid;
+int mode;
+{
+ int fdin;
+ int fdout;
+
+ if (fchdir(fdsourcedir) == -1)
+ strerr_die2sys(111,FATAL,"unable to switch back to source directory: ");
+
+ fdin = open_read(file);
+ if (fdin == -1)
+ strerr_die4sys(111,FATAL,"unable to read ",file,": ");
+ substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof inbuf);
+
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (chdir(subdir) == -1)
+ strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+
+ fdout = open_trunc(file);
+ if (fdout == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf);
+
+ switch(substdio_copy(&ssout,&ssin)) {
+ case -2:
+ strerr_die4sys(111,FATAL,"unable to read ",file,": ");
+ case -3:
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ }
+
+ close(fdin);
+ if (substdio_flush(&ssout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ if (fsync(fdout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ if (close(fdout) == -1) /* NFS silliness */
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+
+ if (chown(file,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown .../",subdir,"/",file,": ");
+ if (chmod(file,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod .../",subdir,"/",file,": ");
+}
+
+void z(home,file,len,uid,gid,mode)
+char *home;
+char *file;
+int len;
+int uid;
+int gid;
+int mode;
+{
+ int fdout;
+
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+
+ fdout = open_trunc(file);
+ if (fdout == -1)
+ strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
+ substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf);
+
+ while (len-- > 0)
+ if (substdio_put(&ssout,"",1) == -1)
+ strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
+
+ if (substdio_flush(&ssout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
+ if (fsync(fdout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
+ if (close(fdout) == -1) /* NFS silliness */
+ strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
+
+ if (chown(file,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown ",home,"/",file,": ");
+ if (chmod(file,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",file,": ");
+}
+
+void main()
+{
+ fdsourcedir = open_read(".");
+ if (fdsourcedir == -1)
+ strerr_die2sys(111,FATAL,"unable to open current directory: ");
+
+ umask(077);
+ hier();
+ _exit(0);
+}
diff --git a/instcheck.c b/instcheck.c
new file mode 100644
index 0000000..d41efda
--- /dev/null
+++ b/instcheck.c
@@ -0,0 +1,108 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "strerr.h"
+#include "error.h"
+#include "readwrite.h"
+#include "exit.h"
+
+extern void hier();
+
+#define FATAL "instcheck: fatal: "
+#define WARNING "instcheck: warning: "
+
+void perm(prefix1,prefix2,prefix3,file,type,uid,gid,mode)
+char *prefix1;
+char *prefix2;
+char *prefix3;
+char *file;
+int type;
+int uid;
+int gid;
+int mode;
+{
+ struct stat st;
+
+ if (stat(file,&st) == -1) {
+ if (errno == error_noent)
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," does not exist",0);
+ else
+ strerr_warn4(WARNING,"unable to stat .../",file,": ",&strerr_sys);
+ return;
+ }
+
+ if ((uid != -1) && (st.st_uid != uid))
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong owner",0);
+ if ((gid != -1) && (st.st_gid != gid))
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong group",0);
+ if ((st.st_mode & 07777) != mode)
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong permissions",0);
+ if ((st.st_mode & S_IFMT) != type)
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong type",0);
+}
+
+void h(home,uid,gid,mode)
+char *home;
+int uid;
+int gid;
+int mode;
+{
+ perm("","","",home,S_IFDIR,uid,gid,mode);
+}
+
+void d(home,subdir,uid,gid,mode)
+char *home;
+char *subdir;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ perm("",home,"/",subdir,S_IFDIR,uid,gid,mode);
+}
+
+void p(home,fifo,uid,gid,mode)
+char *home;
+char *fifo;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ perm("",home,"/",fifo,S_IFIFO,uid,gid,mode);
+}
+
+void c(home,subdir,file,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (chdir(subdir) == -1)
+ strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+ perm(".../",subdir,"/",file,S_IFREG,uid,gid,mode);
+}
+
+void z(home,file,len,uid,gid,mode)
+char *home;
+char *file;
+int len;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ perm("",home,"/",file,S_IFREG,uid,gid,mode);
+}
+
+void main()
+{
+ hier();
+ _exit(0);
+}
diff --git a/ip.c b/ip.c
new file mode 100644
index 0000000..5528ad5
--- /dev/null
+++ b/ip.c
@@ -0,0 +1,53 @@
+#include "fmt.h"
+#include "scan.h"
+#include "ip.h"
+
+unsigned int ip_fmt(s,ip)
+char *s;
+struct ip_address *ip;
+{
+ unsigned int len;
+ unsigned int i;
+
+ len = 0;
+ i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip->d[2]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip->d[3]); len += i; if (s) s += i;
+ return len;
+}
+
+unsigned int ip_scan(s,ip)
+char *s;
+struct ip_address *ip;
+{
+ unsigned int i;
+ unsigned int len;
+ unsigned long u;
+
+ len = 0;
+ i = scan_ulong(s,&u); if (!i) return 0; ip->d[0] = u; s += i; len += i;
+ if (*s != '.') return 0; ++s; ++len;
+ i = scan_ulong(s,&u); if (!i) return 0; ip->d[1] = u; s += i; len += i;
+ if (*s != '.') return 0; ++s; ++len;
+ i = scan_ulong(s,&u); if (!i) return 0; ip->d[2] = u; s += i; len += i;
+ if (*s != '.') return 0; ++s; ++len;
+ i = scan_ulong(s,&u); if (!i) return 0; ip->d[3] = u; s += i; len += i;
+ return len;
+}
+
+unsigned int ip_scanbracket(s,ip)
+char *s;
+struct ip_address *ip;
+{
+ unsigned int len;
+
+ if (*s != '[') return 0;
+ len = ip_scan(s + 1,ip);
+ if (!len) return 0;
+ if (s[len + 1] != ']') return 0;
+ return len + 2;
+}
diff --git a/ip.h b/ip.h
new file mode 100644
index 0000000..b99002f
--- /dev/null
+++ b/ip.h
@@ -0,0 +1,11 @@
+#ifndef IP_H
+#define IP_H
+
+struct ip_address { unsigned char d[4]; } ;
+
+extern unsigned int ip_fmt();
+#define IPFMT 19
+extern unsigned int ip_scan();
+extern unsigned int ip_scanbracket();
+
+#endif
diff --git a/ipalloc.c b/ipalloc.c
new file mode 100644
index 0000000..81792d5
--- /dev/null
+++ b/ipalloc.c
@@ -0,0 +1,7 @@
+#include "alloc.h"
+#include "gen_allocdefs.h"
+#include "ip.h"
+#include "ipalloc.h"
+
+GEN_ALLOC_readyplus(ipalloc,struct ip_mx,ix,len,a,i,n,x,10,ipalloc_readyplus)
+GEN_ALLOC_append(ipalloc,struct ip_mx,ix,len,a,i,n,x,10,ipalloc_readyplus,ipalloc_append)
diff --git a/ipalloc.h b/ipalloc.h
new file mode 100644
index 0000000..ad61475
--- /dev/null
+++ b/ipalloc.h
@@ -0,0 +1,14 @@
+#ifndef IPALLOC_H
+#define IPALLOC_H
+
+#include "ip.h"
+
+struct ip_mx { struct ip_address ip; int pref; } ;
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(ipalloc,struct ip_mx,ix,len,a)
+extern int ipalloc_readyplus();
+extern int ipalloc_append();
+
+#endif
diff --git a/ipme.c b/ipme.c
new file mode 100644
index 0000000..3c86127
--- /dev/null
+++ b/ipme.c
@@ -0,0 +1,100 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#ifndef SIOCGIFCONF /* whatever works */
+#include <sys/sockio.h>
+#endif
+#include "hassalen.h"
+#include "byte.h"
+#include "ip.h"
+#include "ipalloc.h"
+#include "stralloc.h"
+#include "ipme.h"
+
+static int ipmeok = 0;
+ipalloc ipme = {0};
+
+int ipme_is(ip)
+struct ip_address *ip;
+{
+ int i;
+ if (ipme_init() != 1) return -1;
+ for (i = 0;i < ipme.len;++i)
+ if (byte_equal(&ipme.ix[i].ip,4,ip))
+ return 1;
+ return 0;
+}
+
+static stralloc buf = {0};
+
+int ipme_init()
+{
+ struct ifconf ifc;
+ char *x;
+ struct ifreq *ifr;
+ struct sockaddr_in *sin;
+ int len;
+ int s;
+ struct ip_mx ix;
+
+ if (ipmeok) return 1;
+ if (!ipalloc_readyplus(&ipme,0)) return 0;
+ ipme.len = 0;
+ ix.pref = 0;
+
+ /* 0.0.0.0 is a special address which always refers to
+ * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
+ */
+ byte_copy(&ix.ip,4,"\0\0\0\0");
+ if (!ipalloc_append(&ipme,&ix)) { return 0; }
+ if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
+
+ len = 256;
+ for (;;) {
+ if (!stralloc_ready(&buf,len)) { close(s); return 0; }
+ buf.len = 0;
+ ifc.ifc_buf = buf.s;
+ ifc.ifc_len = len;
+ if (ioctl(s,SIOCGIFCONF,&ifc) >= 0) /* > is for System V */
+ if (ifc.ifc_len + sizeof(*ifr) + 64 < len) { /* what a stupid interface */
+ buf.len = ifc.ifc_len;
+ break;
+ }
+ if (len > 200000) { close(s); return -1; }
+ len += 100 + (len >> 2);
+ }
+ x = buf.s;
+ while (x < buf.s + buf.len) {
+ ifr = (struct ifreq *) x;
+#ifdef HASSALEN
+ len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
+ if (len < sizeof(*ifr))
+ len = sizeof(*ifr);
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ sin = (struct sockaddr_in *) &ifr->ifr_addr;
+ byte_copy(&ix.ip,4,&sin->sin_addr);
+ if (ioctl(s,SIOCGIFFLAGS,x) == 0)
+ if (ifr->ifr_flags & IFF_UP)
+ if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
+ }
+#else
+ len = sizeof(*ifr);
+ if (ioctl(s,SIOCGIFFLAGS,x) == 0)
+ if (ifr->ifr_flags & IFF_UP)
+ if (ioctl(s,SIOCGIFADDR,x) == 0)
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ sin = (struct sockaddr_in *) &ifr->ifr_addr;
+ byte_copy(&ix.ip,4,&sin->sin_addr);
+ if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
+ }
+#endif
+ x += len;
+ }
+ close(s);
+ ipmeok = 1;
+ return 1;
+}
diff --git a/ipme.h b/ipme.h
new file mode 100644
index 0000000..5bbf515
--- /dev/null
+++ b/ipme.h
@@ -0,0 +1,12 @@
+#ifndef IPME_H
+#define IPME_H
+
+#include "ip.h"
+#include "ipalloc.h"
+
+extern ipalloc ipme;
+
+extern int ipme_init();
+extern int ipme_is();
+
+#endif
diff --git a/ipmeprint.c b/ipmeprint.c
new file mode 100644
index 0000000..1ef56e3
--- /dev/null
+++ b/ipmeprint.c
@@ -0,0 +1,24 @@
+#include "subfd.h"
+#include "substdio.h"
+#include "ip.h"
+#include "ipme.h"
+#include "exit.h"
+
+char temp[IPFMT];
+
+void main()
+{
+ int j;
+ switch(ipme_init())
+ {
+ case 0: substdio_putsflush(subfderr,"out of memory\n"); _exit(111);
+ case -1: substdio_putsflush(subfderr,"hard error\n"); _exit(100);
+ }
+ for (j = 0;j < ipme.len;++j)
+ {
+ substdio_put(subfdout,temp,ip_fmt(temp,&ipme.ix[j].ip));
+ substdio_puts(subfdout,"\n");
+ }
+ substdio_flush(subfdout);
+ _exit(0);
+}
diff --git a/lock.h b/lock.h
new file mode 100644
index 0000000..a7dee41
--- /dev/null
+++ b/lock.h
@@ -0,0 +1,8 @@
+#ifndef LOCK_H
+#define LOCK_H
+
+extern int lock_ex();
+extern int lock_un();
+extern int lock_exnb();
+
+#endif
diff --git a/lock_ex.c b/lock_ex.c
new file mode 100644
index 0000000..a3351c9
--- /dev/null
+++ b/lock_ex.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include "hasflock.h"
+#include "lock.h"
+
+#ifdef HASFLOCK
+int lock_ex(fd) int fd; { return flock(fd,LOCK_EX); }
+#else
+int lock_ex(fd) int fd; { return lockf(fd,1,0); }
+#endif
diff --git a/lock_exnb.c b/lock_exnb.c
new file mode 100644
index 0000000..5d2a14a
--- /dev/null
+++ b/lock_exnb.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include "hasflock.h"
+#include "lock.h"
+
+#ifdef HASFLOCK
+int lock_exnb(fd) int fd; { return flock(fd,LOCK_EX | LOCK_NB); }
+#else
+int lock_exnb(fd) int fd; { return lockf(fd,2,0); }
+#endif
diff --git a/lock_un.c b/lock_un.c
new file mode 100644
index 0000000..16e2f17
--- /dev/null
+++ b/lock_un.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include "hasflock.h"
+#include "lock.h"
+
+#ifdef HASFLOCK
+int lock_un(fd) int fd; { return flock(fd,LOCK_UN); }
+#else
+int lock_un(fd) int fd; { return lockf(fd,0,0); }
+#endif
diff --git a/maildir.5 b/maildir.5
new file mode 100644
index 0000000..5da9573
--- /dev/null
+++ b/maildir.5
@@ -0,0 +1,239 @@
+.TH maildir 5
+.SH "NAME"
+maildir \- directory for incoming mail messages
+.SH "INTRODUCTION"
+.I maildir
+is a structure for
+directories of incoming mail messages.
+It solves the reliability problems that plague
+.I mbox
+files and
+.I mh
+folders.
+.SH "RELIABILITY ISSUES"
+A machine may crash while it is delivering a message.
+For both
+.I mbox
+files and
+.I mh
+folders this means that the message will be silently truncated.
+Even worse: for
+.I mbox
+format, if the message is truncated in the middle of a line,
+it will be silently joined to the next message.
+The mail transport agent will try again later to deliver the message,
+but it is unacceptable that a corrupted message should show up at all.
+In
+.IR maildir ,
+every message is guaranteed complete upon delivery.
+
+A machine may have two programs simultaneously delivering mail
+to the same user.
+The
+.I mbox
+and
+.I mh
+formats require the programs to update a single central file.
+If the programs do not use some locking mechanism,
+the central file will be corrupted.
+There are several
+.I mbox
+and
+.I mh
+locking mechanisms,
+none of which work portably and reliably.
+In contrast, in
+.IR maildir ,
+no locks are ever necessary.
+Different delivery processes never touch the same file.
+
+A user may try to delete messages from his mailbox at the same
+moment that the machine delivers a new message.
+For
+.I mbox
+and
+.I mh
+formats, the user's mail-reading program must know
+what locking mechanism the mail-delivery programs use.
+In contrast, in
+.IR maildir ,
+any delivered message
+can be safely updated or deleted by a mail-reading program.
+
+Many sites use Sun's
+.B Network F\fPa\fBil\fPur\fBe System
+(NFS),
+presumably because the operating system vendor does not offer
+anything else.
+NFS exacerbates all of the above problems.
+Some NFS implementations don't provide
+.B any
+reliable locking mechanism.
+With
+.I mbox
+and
+.I mh
+formats,
+if two machines deliver mail to the same user,
+or if a user reads mail anywhere except the delivery machine,
+the user's mail is at risk.
+.I maildir
+works without trouble over NFS.
+.SH "THE MAILDIR STRUCTURE"
+A directory in
+.I maildir
+format has three subdirectories,
+all on the same filesystem:
+.BR tmp ,
+.BR new ,
+and
+.BR cur .
+
+Each file in
+.B new
+is a newly delivered mail message.
+The modification time of the file is the delivery date of the message.
+The message is delivered
+.I without
+an extra UUCP-style
+.B From_
+line,
+.I without
+any
+.B >From
+quoting,
+and
+.I without
+an extra blank line at the end.
+The message is normally in RFC 822 format,
+starting with a
+.B Return-Path
+line and a
+.B Delivered-To
+line,
+but it could contain arbitrary binary data.
+It might not even end with a newline.
+
+Files in
+.B cur
+are just like files in
+.BR new .
+The big difference is that files in
+.B cur
+are no longer new mail:
+they have been seen by the user's mail-reading program.
+.SH "HOW A MESSAGE IS DELIVERED"
+The
+.B tmp
+directory is used to ensure reliable delivery,
+as discussed here.
+
+A program delivers a mail message in six steps.
+First, it
+.B chdir()\fPs
+to the
+.I maildir
+directory.
+Second, it
+.B stat()s
+the name
+.BR tmp/\fItime.pid.host ,
+where
+.I time
+is the number of seconds since the beginning of 1970 GMT,
+.I pid
+is the program's process ID,
+and
+.I host
+is the host name.
+Third, if
+.B stat()
+returned anything other than ENOENT,
+the program sleeps for two seconds, updates
+.IR time ,
+and tries the
+.B stat()
+again, a limited number of times.
+Fourth, the program
+creates
+.BR tmp/\fItime.pid.host .
+Fifth, the program
+.I NFS-writes
+the message to the file.
+Sixth, the program
+.BR link() s
+the file to
+.BR new/\fItime.pid.host .
+At that instant the message has been successfully delivered.
+
+The delivery program is required to start a 24-hour timer before
+creating
+.BR tmp/\fItime.pid.host ,
+and to abort the delivery
+if the timer expires.
+Upon error, timeout, or normal completion,
+the delivery program may attempt to
+.B unlink()
+.BR tmp/\fItime.pid.host .
+
+.I NFS-writing
+means
+(1) as usual, checking the number of bytes returned from each
+.B write()
+call;
+(2) calling
+.B fsync()
+and checking its return value;
+(3) calling
+.B close()
+and checking its return value.
+(Standard NFS implementations handle
+.B fsync()
+incorrectly
+but make up for it by abusing
+.BR close() .)
+.SH "HOW A MESSAGE IS READ"
+A mail reader operates as follows.
+
+It looks through the
+.B new
+directory for new messages.
+Say there is a new message,
+.BR new/\fIunique .
+The reader may freely display the contents of
+.BR new/\fIunique ,
+delete
+.BR new/\fIunique ,
+or rename
+.B new/\fIunique
+as
+.BR cur/\fIunique:info .
+See
+.B http://pobox.com/~djb/proto/maildir.html
+for the meaning of
+.IR info .
+
+The reader is also expected to look through the
+.B tmp
+directory and to clean up any old files found there.
+A file in
+.B tmp
+may be safely removed if it
+has not been accessed in 36 hours.
+
+It is a good idea for readers to skip all filenames in
+.B new
+and
+.B cur
+starting with a dot.
+Other than this, readers should not attempt to parse filenames.
+.SH "ENVIRONMENT VARIABLES"
+Mail readers supporting
+.I maildir
+use the
+.B MAILDIR
+environment variable
+as the name of the user's primary mail directory.
+.SH "SEE ALSO"
+mbox(5),
+qmail-local(8)
diff --git a/maildir.c b/maildir.c
new file mode 100644
index 0000000..efbd3d4
--- /dev/null
+++ b/maildir.c
@@ -0,0 +1,108 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "prioq.h"
+#include "env.h"
+#include "stralloc.h"
+#include "direntry.h"
+#include "datetime.h"
+#include "now.h"
+#include "str.h"
+#include "maildir.h"
+
+struct strerr maildir_chdir_err;
+struct strerr maildir_scan_err;
+
+int maildir_chdir()
+{
+ char *maildir;
+ maildir = env_get("MAILDIR");
+ if (!maildir)
+ STRERR(-1,maildir_chdir_err,"MAILDIR not set")
+ if (chdir(maildir) == -1)
+ STRERR_SYS3(-1,maildir_chdir_err,"unable to chdir to ",maildir,": ")
+ return 0;
+}
+
+void maildir_clean(tmpname)
+stralloc *tmpname;
+{
+ DIR *dir;
+ direntry *d;
+ datetime_sec time;
+ struct stat st;
+
+ time = now();
+
+ dir = opendir("tmp");
+ if (!dir) return;
+
+ while (d = readdir(dir))
+ {
+ if (d->d_name[0] == '.') continue;
+ if (!stralloc_copys(tmpname,"tmp/")) break;
+ if (!stralloc_cats(tmpname,d->d_name)) break;
+ if (!stralloc_0(tmpname)) break;
+ if (stat(tmpname->s,&st) == 0)
+ if (time > st.st_atime + 129600)
+ unlink(tmpname->s);
+ }
+ closedir(dir);
+}
+
+static int append(pq,filenames,subdir,time)
+prioq *pq;
+stralloc *filenames;
+char *subdir;
+datetime_sec time;
+{
+ DIR *dir;
+ direntry *d;
+ struct prioq_elt pe;
+ unsigned int pos;
+ struct stat st;
+
+ dir = opendir(subdir);
+ if (!dir)
+ STRERR_SYS3(-1,maildir_scan_err,"unable to scan $MAILDIR/",subdir,": ")
+
+ while (d = readdir(dir))
+ {
+ if (d->d_name[0] == '.') continue;
+ pos = filenames->len;
+ if (!stralloc_cats(filenames,subdir)) break;
+ if (!stralloc_cats(filenames,"/")) break;
+ if (!stralloc_cats(filenames,d->d_name)) break;
+ if (!stralloc_0(filenames)) break;
+ if (stat(filenames->s + pos,&st) == 0)
+ if (st.st_mtime < time) /* don't want to mix up the order */
+ {
+ pe.dt = st.st_mtime;
+ pe.id = pos;
+ if (!prioq_insert(pq,&pe)) break;
+ }
+ }
+
+ closedir(dir);
+ if (d) STRERR_SYS3(-1,maildir_scan_err,"unable to read $MAILDIR/",subdir,": ")
+ return 0;
+}
+
+int maildir_scan(pq,filenames,flagnew,flagcur)
+prioq *pq;
+stralloc *filenames;
+int flagnew;
+int flagcur;
+{
+ struct prioq_elt pe;
+ datetime_sec time;
+ int r;
+
+ if (!stralloc_copys(filenames,"")) return 0;
+ while (prioq_min(pq,&pe)) prioq_delmin(pq);
+
+ time = now();
+
+ if (flagnew) if (append(pq,filenames,"new",time) == -1) return -1;
+ if (flagcur) if (append(pq,filenames,"cur",time) == -1) return -1;
+ return 0;
+}
diff --git a/maildir.h b/maildir.h
new file mode 100644
index 0000000..7dd8826
--- /dev/null
+++ b/maildir.h
@@ -0,0 +1,12 @@
+#ifndef MAILDIR_H
+#define MAILDIR_H
+
+#include "strerr.h"
+extern struct strerr maildir_chdir_err;
+extern struct strerr maildir_scan_err;
+
+extern int maildir_chdir();
+extern void maildir_clean();
+extern int maildir_scan();
+
+#endif
diff --git a/maildir2mbox.1 b/maildir2mbox.1
new file mode 100644
index 0000000..31423d9
--- /dev/null
+++ b/maildir2mbox.1
@@ -0,0 +1,53 @@
+.TH maildir2mbox 1
+.SH NAME
+maildir2mbox \- move mail from a maildir to an mbox
+.SH SYNOPSIS
+.B maildir2mbox
+.SH DESCRIPTION
+.B maildir2mbox
+moves mail from a
+.IR maildir -format
+directory to an
+.IR mbox -format
+file.
+
+You must supply three environment variables to
+.BR maildir2mbox :
+.B MAILDIR
+is the name of your
+.I maildir
+directory;
+.B MAIL
+is the name of your
+.I mbox
+file;
+and
+.B MAILTMP
+is a temporary file that
+.B maildir2mbox
+can overwrite.
+.B MAILTMP
+and
+.B MAIL
+must be on the same filesystem.
+
+.B maildir2mbox
+is reliable:
+it will not remove messages
+from
+.B MAILDIR
+until the messages have been successfully appended to
+.BR MAIL .
+
+.B maildir2mbox
+locks
+.B MAIL
+to protect against simultaneous access by a mail reader.
+This locking system does not protect against simultaneous access
+by another
+.BR maildir2mbox ;
+you should run only one
+.B maildir2mbox
+at a time.
+.SH "SEE ALSO"
+maildir(5)
diff --git a/maildir2mbox.c b/maildir2mbox.c
new file mode 100644
index 0000000..7364441
--- /dev/null
+++ b/maildir2mbox.c
@@ -0,0 +1,162 @@
+#include "readwrite.h"
+#include "prioq.h"
+#include "env.h"
+#include "stralloc.h"
+#include "subfd.h"
+#include "substdio.h"
+#include "getln.h"
+#include "error.h"
+#include "open.h"
+#include "lock.h"
+#include "gfrom.h"
+#include "str.h"
+#include "exit.h"
+#include "myctime.h"
+#include "maildir.h"
+
+char *mbox;
+char *mboxtmp;
+
+stralloc filenames = {0};
+prioq pq = {0};
+prioq pq2 = {0};
+
+stralloc line = {0};
+
+stralloc ufline = {0};
+
+char inbuf[SUBSTDIO_INSIZE];
+char outbuf[SUBSTDIO_OUTSIZE];
+
+#define FATAL "maildir2mbox: fatal: "
+#define WARNING "maildir2mbox: warning: "
+
+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+
+void main()
+{
+ substdio ssin;
+ substdio ssout;
+ struct prioq_elt pe;
+ int fdoldmbox;
+ int fdnewmbox;
+ int fd;
+ int match;
+ int fdlock;
+
+ umask(077);
+
+ mbox = env_get("MAIL");
+ if (!mbox) strerr_die2x(111,FATAL,"MAIL not set");
+ mboxtmp = env_get("MAILTMP");
+ if (!mboxtmp) strerr_die2x(111,FATAL,"MAILTMP not set");
+
+ if (maildir_chdir() == -1)
+ strerr_die1(111,FATAL,&maildir_chdir_err);
+ maildir_clean(&filenames);
+ if (maildir_scan(&pq,&filenames,1,1) == -1)
+ strerr_die1(111,FATAL,&maildir_scan_err);
+
+ if (!prioq_min(&pq,&pe)) _exit(0); /* nothing new */
+
+ fdlock = open_append(mbox);
+ if (fdlock == -1)
+ strerr_die4sys(111,FATAL,"unable to lock ",mbox,": ");
+ if (lock_ex(fdlock) == -1)
+ strerr_die4sys(111,FATAL,"unable to lock ",mbox,": ");
+
+ fdoldmbox = open_read(mbox);
+ if (fdoldmbox == -1)
+ strerr_die4sys(111,FATAL,"unable to read ",mbox,": ");
+
+ fdnewmbox = open_trunc(mboxtmp);
+ if (fdnewmbox == -1)
+ strerr_die4sys(111,FATAL,"unable to create ",mboxtmp,": ");
+
+ substdio_fdbuf(&ssin,read,fdoldmbox,inbuf,sizeof(inbuf));
+ substdio_fdbuf(&ssout,write,fdnewmbox,outbuf,sizeof(outbuf));
+
+ switch(substdio_copy(&ssout,&ssin))
+ {
+ case -2: strerr_die4sys(111,FATAL,"unable to read ",mbox,": ");
+ case -3: strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ }
+
+ while (prioq_min(&pq,&pe))
+ {
+ prioq_delmin(&pq);
+ if (!prioq_insert(&pq2,&pe)) die_nomem();
+
+ fd = open_read(filenames.s + pe.id);
+ if (fd == -1)
+ strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": ");
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+ if (getln(&ssin,&line,&match,'\n') != 0)
+ strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": ");
+
+ if (!stralloc_copys(&ufline,"From XXX ")) die_nomem();
+ if (match)
+ if (stralloc_starts(&line,"Return-Path: <"))
+ {
+ if (line.s[14] == '>')
+ {
+ if (!stralloc_copys(&ufline,"From MAILER-DAEMON ")) die_nomem();
+ }
+ else
+ {
+ int i;
+ if (!stralloc_ready(&ufline,line.len)) die_nomem();
+ if (!stralloc_copys(&ufline,"From ")) die_nomem();
+ for (i = 14;i < line.len - 2;++i)
+ if ((line.s[i] == ' ') || (line.s[i] == '\t'))
+ ufline.s[ufline.len++] = '-';
+ else
+ ufline.s[ufline.len++] = line.s[i];
+ if (!stralloc_cats(&ufline," ")) die_nomem();
+ }
+ }
+ if (!stralloc_cats(&ufline,myctime(pe.dt))) die_nomem();
+ if (substdio_put(&ssout,ufline.s,ufline.len) == -1)
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+
+ while (match && line.len)
+ {
+ if (gfrom(line.s,line.len))
+ if (substdio_puts(&ssout,">") == -1)
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ if (substdio_put(&ssout,line.s,line.len) == -1)
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ if (!match)
+ {
+ if (substdio_puts(&ssout,"\n") == -1)
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ break;
+ }
+ if (getln(&ssin,&line,&match,'\n') != 0)
+ strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": ");
+ }
+ if (substdio_puts(&ssout,"\n"))
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+
+ close(fd);
+ }
+
+ if (substdio_flush(&ssout) == -1)
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ if (fsync(fdnewmbox) == -1)
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ if (close(fdnewmbox) == -1) /* NFS dorks */
+ strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
+ if (rename(mboxtmp,mbox) == -1)
+ strerr_die6(111,FATAL,"unable to move ",mboxtmp," to ",mbox,": ",&strerr_sys);
+
+ while (prioq_min(&pq2,&pe))
+ {
+ prioq_delmin(&pq2);
+ if (unlink(filenames.s + pe.id) == -1)
+ strerr_warn4(WARNING,"$MAILDIR/",filenames.s + pe.id," will be delivered twice; unable to unlink: ",&strerr_sys);
+ }
+
+ _exit(0);
+}
diff --git a/maildirmake.1 b/maildirmake.1
new file mode 100644
index 0000000..e5bd042
--- /dev/null
+++ b/maildirmake.1
@@ -0,0 +1,15 @@
+.TH maildirmake 1
+.SH NAME
+maildirmake \- create a maildir for incoming mail
+.SH SYNOPSIS
+.B maildirmake
+.I dir
+.SH DESCRIPTION
+.B maildirmake
+makes a new directory,
+.IR dir ,
+in
+.B maildir
+format.
+.SH "SEE ALSO"
+maildir(5)
diff --git a/maildirmake.c b/maildirmake.c
new file mode 100644
index 0000000..9863fef
--- /dev/null
+++ b/maildirmake.c
@@ -0,0 +1,24 @@
+#include "strerr.h"
+#include "exit.h"
+
+#define FATAL "maildirmake: fatal: "
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ umask(077);
+ if (!argv[1])
+ strerr_die1x(100,"maildirmake: usage: maildirmake name");
+ if (mkdir(argv[1],0700) == -1)
+ strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],": ");
+ if (chdir(argv[1]) == -1)
+ strerr_die4sys(111,FATAL,"unable to chdir to ",argv[1],": ");
+ if (mkdir("tmp",0700) == -1)
+ 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);
+}
diff --git a/maildirwatch.1 b/maildirwatch.1
new file mode 100644
index 0000000..d1d70a0
--- /dev/null
+++ b/maildirwatch.1
@@ -0,0 +1,23 @@
+.TH maildirwatch 1
+.SH NAME
+maildirwatch \- look for new mail in a maildir
+.SH SYNOPSIS
+.B maildirwatch
+.SH DESCRIPTION
+.B maildirwatch
+watches your
+.I maildir
+for new mail.
+You must supply a
+.B MAILDIR
+environment variable
+with the name of your
+.I maildir
+directory.
+
+.B maildirwatch
+prints a new mail summary twice per minute.
+It is designed to run inside a (VT100-compatible) window;
+it clears the window before each summary.
+.SH "SEE ALSO"
+maildir(5)
diff --git a/maildirwatch.c b/maildirwatch.c
new file mode 100644
index 0000000..40d8322
--- /dev/null
+++ b/maildirwatch.c
@@ -0,0 +1,125 @@
+#include "getln.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "prioq.h"
+#include "stralloc.h"
+#include "str.h"
+#include "exit.h"
+#include "hfield.h"
+#include "readwrite.h"
+#include "open.h"
+#include "headerbody.h"
+#include "maildir.h"
+
+#define FATAL "maildirwatch: fatal: "
+
+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+
+stralloc recipient = {0};
+stralloc sender = {0};
+stralloc fromline = {0};
+stralloc text = {0};
+
+void addtext(s,n) char *s; int n;
+{
+ if (!stralloc_catb(&text,s,n)) die_nomem();
+ if (text.len > 158) text.len = 158;
+}
+void dobody(h) stralloc *h; { addtext(h->s,h->len); }
+void doheader(h) stralloc *h;
+{
+ int i;
+ switch(hfield_known(h->s,h->len))
+ {
+ case H_SUBJECT:
+ i = hfield_skipname(h->s,h->len);
+ addtext(h->s + i,h->len - i);
+ break;
+ case H_DELIVEREDTO:
+ i = hfield_skipname(h->s,h->len);
+ if (i < h->len)
+ if (!stralloc_copyb(&recipient,h->s + i,h->len - i - 1)) die_nomem();
+ break;
+ case H_RETURNPATH:
+ i = hfield_skipname(h->s,h->len);
+ if (i < h->len)
+ if (!stralloc_copyb(&sender,h->s + i,h->len - i - 1)) die_nomem();
+ break;
+ case H_FROM:
+ if (!stralloc_copyb(&fromline,h->s,h->len - 1)) die_nomem();
+ break;
+ }
+}
+void finishheader() { ; }
+
+stralloc filenames = {0};
+prioq pq = {0};
+
+char inbuf[SUBSTDIO_INSIZE];
+substdio ssin;
+
+void main()
+{
+ struct prioq_elt pe;
+ int fd;
+ int i;
+
+ if (maildir_chdir() == -1)
+ strerr_die1(111,FATAL,&maildir_chdir_err);
+
+ for (;;)
+ {
+ maildir_clean(&filenames);
+ if (maildir_scan(&pq,&filenames,1,0) == -1)
+ strerr_die1(111,FATAL,&maildir_scan_err);
+
+ substdio_putsflush(subfdout,"\033[;H\033[;J");
+
+ while (prioq_min(&pq,&pe))
+ {
+ prioq_delmin(&pq);
+
+ fd = open_read(filenames.s + pe.id);
+ if (fd == -1) continue;
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+ if (!stralloc_copys(&sender,"?")) die_nomem();
+ if (!stralloc_copys(&recipient,"?")) die_nomem();
+ if (!stralloc_copys(&fromline,"")) die_nomem();
+ if (!stralloc_copys(&text,"")) die_nomem();
+ if (headerbody(&ssin,doheader,finishheader,dobody) == -1)
+ strerr_die2x(111,FATAL,"trouble reading new message");
+
+ for (i = 0;i < fromline.len;++i)
+ if ((fromline.s[i] < 32) || (fromline.s[i] > 126))
+ fromline.s[i] = '/';
+ for (i = 0;i < sender.len;++i)
+ if ((sender.s[i] < 32) || (sender.s[i] > 126))
+ sender.s[i] = '?';
+ for (i = 0;i < recipient.len;++i)
+ if ((recipient.s[i] < 32) || (recipient.s[i] > 126))
+ recipient.s[i] = '?';
+ for (i = 0;i < text.len;++i)
+ if ((text.s[i] < 32) || (text.s[i] > 126))
+ text.s[i] = '/';
+ substdio_puts(subfdout,"FROM ");
+ substdio_put(subfdout,sender.s,sender.len);
+ substdio_puts(subfdout," TO <");
+ substdio_put(subfdout,recipient.s,recipient.len);
+ substdio_puts(subfdout,">\n");
+ if (fromline.len)
+ {
+ substdio_puts(subfdout,"\033[1m");
+ substdio_put(subfdout,fromline.s,fromline.len);
+ substdio_puts(subfdout,"\033[0m\n");
+ }
+ substdio_put(subfdout,text.s,text.len);
+ substdio_puts(subfdout,"\n\n");
+
+ close(fd);
+ }
+
+ substdio_flush(subfdout);
+ sleep(30);
+ }
+}
diff --git a/mailsubj.1 b/mailsubj.1
new file mode 100644
index 0000000..687b8e8
--- /dev/null
+++ b/mailsubj.1
@@ -0,0 +1,38 @@
+.TH mailsubj 1
+.SH NAME
+mailsubj \- send a mail message with a subject line
+.SH SYNOPSIS
+.B mailsubj
+.I subject
+.I recip ...
+.SH DESCRIPTION
+.B mailsubj
+inserts
+.I subject
+and the list of
+.IR recip s
+into a mail message:
+
+.EX
+ Subject: subject
+.br
+ To: recip ...
+.br
+
+.br
+ body
+.EE
+
+.B mailsubj
+reads the body of the message from its standard input.
+Then it sends the message.
+
+Note that
+.I subject
+and
+.I recip
+must be quoted properly for the message header.
+.SH "SEE ALSO"
+addresses(5),
+qmail-header(8),
+qmail-inject(8)
diff --git a/mailsubj.sh b/mailsubj.sh
new file mode 100644
index 0000000..a93d3f4
--- /dev/null
+++ b/mailsubj.sh
@@ -0,0 +1,7 @@
+subject="$1"
+shift
+( echo Subject: "$subject"
+ echo To: ${1+"$@"}
+ echo ''
+ cat
+) | QMAIL/bin/qmail-inject
diff --git a/make-compile.sh b/make-compile.sh
new file mode 100644
index 0000000..a1eb501
--- /dev/null
+++ b/make-compile.sh
@@ -0,0 +1 @@
+echo exec "$CC" -c '${1+"$@"}'
diff --git a/make-load.sh b/make-load.sh
new file mode 100644
index 0000000..de07d2e
--- /dev/null
+++ b/make-load.sh
@@ -0,0 +1,2 @@
+echo 'main="$1"; shift'
+echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}'
diff --git a/make-makelib.sh b/make-makelib.sh
new file mode 100644
index 0000000..d6b7c8c
--- /dev/null
+++ b/make-makelib.sh
@@ -0,0 +1,16 @@
+echo 'main="$1"; shift'
+echo 'rm -f "$main"'
+echo 'ar cr "$main" ${1+"$@"}'
+
+case "$1" in
+sunos-5.*) ;;
+unix_sv*) ;;
+irix64-*) ;;
+irix-*) ;;
+dgux-*) ;;
+hp-ux-*) ;;
+sco*) ;;
+*)
+ echo 'ranlib "$main"'
+ ;;
+esac
diff --git a/mbox.5 b/mbox.5
new file mode 100644
index 0000000..de1f837
--- /dev/null
+++ b/mbox.5
@@ -0,0 +1,235 @@
+.TH mbox 5
+.SH "NAME"
+mbox \- file containing mail messages
+.SH "INTRODUCTION"
+The most common format for storage of mail messages is
+.I mbox
+format.
+An
+.I mbox
+is a single file containing zero or more mail messages.
+.SH "MESSAGE FORMAT"
+A message encoded in
+.I mbox
+format begins with a
+.B From_
+line, continues with a series of
+.B \fRnon-\fBFrom_
+lines,
+and ends with a blank line.
+A
+.B From_
+line means any line that begins with the characters
+F, r, o, m, space:
+
+.EX
+ From god@heaven.af.mil Sat Jan 3 01:05:34 1996
+.br
+ Return-Path: <god@heaven.af.mil>
+.br
+ Delivered-To: djb@silverton.berkeley.edu
+.br
+ Date: 3 Jan 1996 01:05:34 -0000
+.br
+ From: God <god@heaven.af.mil>
+.br
+ To: djb@silverton.berkeley.edu (D. J. Bernstein)
+.br
+
+.br
+ How's that mail system project coming along?
+.br
+
+.EE
+
+The final line is a completely blank line (no spaces or tabs).
+Notice that blank lines may also appear elsewhere in the message.
+
+The
+.B From_
+line always looks like
+.B From
+.I envsender
+.I date
+.IR moreinfo .
+.I envsender
+is one word, without spaces or tabs;
+it is usually the envelope sender of the message.
+.I date
+is the delivery date of the message.
+It always contains exactly 24 characters in
+.B asctime
+format.
+.I moreinfo
+is optional; it may contain arbitrary information.
+
+Between the
+.B From_
+line and the blank line is a message in RFC 822 format,
+as described in
+.BR qmail-header(5) ,
+subject to
+.B >From quoting
+as described below.
+.SH "HOW A MESSAGE IS DELIVERED"
+Here is how a program appends a message to an
+.I mbox
+file.
+
+It first creates a
+.B From_
+line given the message's envelope sender and the current date.
+If the envelope sender is empty (i.e., if this is a bounce message),
+the program uses
+.B MAILER-DAEMON
+instead.
+If the envelope sender contains spaces, tabs, or newlines,
+the program replaces them with hyphens.
+
+The program then copies the message, applying
+.B >From quoting
+to each line.
+.B >From quoting
+ensures that the resulting lines are not
+.B From_
+lines:
+the program prepends a
+.B >
+to any
+.B From_
+line,
+.B >From_
+line,
+.B >>From_
+line,
+.B >>>From_
+line,
+etc.
+
+Finally the program appends a blank line to the message.
+If the last line of the message was a partial line,
+it writes two newlines;
+otherwise it writes one.
+.SH "HOW A MESSAGE IS READ"
+A reader scans through an
+.I mbox
+file looking for
+.B From_
+lines.
+Any
+.B From_
+line marks the beginning of a message.
+The reader should not attempt to take advantage of the fact that every
+.B From_
+line (past the beginning of the file)
+is preceded by a blank line.
+
+Once the reader finds a message,
+it extracts a (possibly corrupted) envelope sender
+and delivery date out of the
+.B From_
+line.
+It then reads until the next
+.B From_
+line or end of file, whichever comes first.
+It strips off the final blank line
+and
+deletes the
+quoting of
+.B >From_
+lines and
+.B >>From_
+lines and so on.
+The result is an RFC 822 message.
+.SH "COMMON MBOX VARIANTS"
+There are many variants of
+.I mbox
+format.
+The variant described above is
+.I mboxrd
+format, popularized by Rahul Dhesi in June 1995.
+
+The original
+.I mboxo
+format quotes only
+.B From_
+lines, not
+.B >From_
+lines.
+As a result it is impossible to tell whether
+
+.EX
+ From: djb@silverton.berkeley.edu (D. J. Bernstein)
+.br
+ To: god@heaven.af.mil
+.br
+
+.br
+ >From now through August I'll be doing beta testing.
+.br
+ Thanks for your interest.
+.EE
+
+was quoted in the original message.
+An
+.I mboxrd
+reader will always strip off the quoting.
+
+.I mboxcl
+format is like
+.I mboxo
+format, but includes a Content-Length field with the
+number of bytes in the message.
+.I mboxcl2
+format is like
+.I mboxcl
+but has no
+.B >From
+quoting.
+These formats are used by SVR4 mailers.
+.I mboxcl2
+cannot be read safely by
+.I mboxrd
+readers.
+.SH "UNSPECIFIED DETAILS"
+There are many locking mechanisms for
+.I mbox
+files.
+.B qmail-local
+always uses
+.B flock
+on systems that have it, otherwise
+.BR lockf .
+
+The delivery date in a
+.B From_
+line does not specify a time zone.
+.B qmail-local
+always creates the delivery date in GMT
+so that
+.I mbox
+files can be safely transported from one time zone to another.
+
+If the mtime on a nonempty
+.I mbox
+file is greater than the atime,
+the file has new mail.
+If the mtime is smaller than the atime,
+the new mail has been read.
+If the atime equals the mtime,
+there is no way to tell whether the file has new mail,
+since
+.B qmail-local
+takes much less than a second to run.
+One solution is for a mail reader to artificially set the
+atime to the mtime plus 1.
+Then the file has new mail if and only if the atime is
+less than or equal to the mtime.
+
+Some mail readers place
+.B Status
+fields in each message to indicate which messages have been read.
+.SH "SEE ALSO"
+maildir(5),
+qmail-header(5),
+qmail-local(8)
diff --git a/myctime.c b/myctime.c
new file mode 100644
index 0000000..5003c39
--- /dev/null
+++ b/myctime.c
@@ -0,0 +1,37 @@
+#include "datetime.h"
+#include "fmt.h"
+#include "myctime.h"
+
+static char *daytab[7] = {
+"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+};
+static char *montab[12] = {
+"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+static char result[30];
+
+char *myctime(t)
+datetime_sec t;
+{
+ struct datetime dt;
+ unsigned int len;
+ datetime_tai(&dt,t);
+ len = 0;
+ len += fmt_str(result + len,daytab[dt.wday]);
+ result[len++] = ' ';
+ len += fmt_str(result + len,montab[dt.mon]);
+ result[len++] = ' ';
+ len += fmt_uint0(result + len,dt.mday,2);
+ result[len++] = ' ';
+ len += fmt_uint0(result + len,dt.hour,2);
+ result[len++] = ':';
+ len += fmt_uint0(result + len,dt.min,2);
+ result[len++] = ':';
+ len += fmt_uint0(result + len,dt.sec,2);
+ result[len++] = ' ';
+ len += fmt_uint(result + len,1900 + dt.year);
+ result[len++] = '\n';
+ result[len++] = 0;
+ return result;
+}
diff --git a/myctime.h b/myctime.h
new file mode 100644
index 0000000..ec1d03c
--- /dev/null
+++ b/myctime.h
@@ -0,0 +1,6 @@
+#ifndef MYCTIME_H
+#define MYCTIME_H
+
+extern char *myctime();
+
+#endif
diff --git a/ndelay.c b/ndelay.c
new file mode 100644
index 0000000..438d1d8
--- /dev/null
+++ b/ndelay.c
@@ -0,0 +1,13 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "ndelay.h"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+int ndelay_on(fd)
+int fd;
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+}
diff --git a/ndelay.h b/ndelay.h
new file mode 100644
index 0000000..68c6bce
--- /dev/null
+++ b/ndelay.h
@@ -0,0 +1,7 @@
+#ifndef NDELAY_H
+#define NDELAY_H
+
+extern int ndelay_on();
+extern int ndelay_off();
+
+#endif
diff --git a/ndelay_off.c b/ndelay_off.c
new file mode 100644
index 0000000..86f8fbf
--- /dev/null
+++ b/ndelay_off.c
@@ -0,0 +1,13 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "ndelay.h"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+int ndelay_off(fd)
+int fd;
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
diff --git a/newfield.c b/newfield.c
new file mode 100644
index 0000000..78d6bb8
--- /dev/null
+++ b/newfield.c
@@ -0,0 +1,68 @@
+#include "fmt.h"
+#include "datetime.h"
+#include "stralloc.h"
+#include "date822fmt.h"
+#include "newfield.h"
+
+/* "Date: 26 Sep 1995 04:46:53 -0000\n" */
+stralloc newfield_date = {0};
+/* "Message-ID: <19950926044653.12345.qmail@silverton.berkeley.edu>\n" */
+stralloc newfield_msgid = {0};
+
+static unsigned int datefmt(s,when)
+char *s;
+datetime_sec when;
+{
+ unsigned int i;
+ unsigned int len;
+ struct datetime dt;
+ datetime_tai(&dt,when);
+ len = 0;
+ i = fmt_str(s,"Date: "); len += i; if (s) s += i;
+ i = date822fmt(s,&dt); len += i; if (s) s += i;
+ return len;
+}
+
+static unsigned int msgidfmt(s,idhost,idhostlen,when)
+char *s;
+char *idhost;
+int idhostlen;
+datetime_sec when;
+{
+ unsigned int i;
+ unsigned int len;
+ struct datetime dt;
+ datetime_tai(&dt,when);
+ len = 0;
+ i = fmt_str(s,"Message-ID: <"); len += i; if (s) s += i;
+ i = fmt_uint(s,dt.year + 1900); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt.mon + 1,2); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt.mday,2); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt.hour,2); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt.min,2); len += i; if (s) s += i;
+ i = fmt_uint0(s,dt.sec,2); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_uint(s,getpid()); len += i; if (s) s += i;
+ i = fmt_str(s,".qmail@"); len += i; if (s) s += i;
+ i = fmt_strn(s,idhost,idhostlen); len += i; if (s) s += i;
+ i = fmt_str(s,">\n"); len += i; if (s) s += i;
+ return len;
+}
+
+int newfield_datemake(when)
+datetime_sec when;
+{
+ if (!stralloc_ready(&newfield_date,datefmt(FMT_LEN,when))) return 0;
+ newfield_date.len = datefmt(newfield_date.s,when);
+ return 1;
+}
+
+int newfield_msgidmake(idhost,idhostlen,when)
+char *idhost;
+int idhostlen;
+datetime_sec when;
+{
+ if (!stralloc_ready(&newfield_msgid,msgidfmt(FMT_LEN,idhost,idhostlen,when))) return 0;
+ newfield_msgid.len = msgidfmt(newfield_msgid.s,idhost,idhostlen,when);
+ return 1;
+}
diff --git a/newfield.h b/newfield.h
new file mode 100644
index 0000000..a51352a
--- /dev/null
+++ b/newfield.h
@@ -0,0 +1,12 @@
+#ifndef NEWFIELD_H
+#define NEWFIELD_H
+
+#include "stralloc.h"
+
+extern stralloc newfield_date;
+extern int newfield_datemake();
+
+extern stralloc newfield_msgid;
+extern int newfield_msgidmake();
+
+#endif
diff --git a/now.3 b/now.3
new file mode 100644
index 0000000..3d845b1
--- /dev/null
+++ b/now.3
@@ -0,0 +1,14 @@
+.TH now 3
+.SH NAME
+now \- get current time, in seconds
+.SH SYNTAX
+.B #include <now.h>
+
+datetime_sec \fBnow\fP();
+.SH DESCRIPTION
+.B now
+returns the number of real-time seconds that have elapsed
+since the end of 1969 TAI.
+.SH "SEE ALSO"
+datetime(3),
+time(3)
diff --git a/now.c b/now.c
new file mode 100644
index 0000000..5ce4d90
--- /dev/null
+++ b/now.c
@@ -0,0 +1,8 @@
+#include <time.h>
+#include "datetime.h"
+#include "now.h"
+
+datetime_sec now()
+{
+ return time((long *) 0);
+}
diff --git a/now.h b/now.h
new file mode 100644
index 0000000..b8be182
--- /dev/null
+++ b/now.h
@@ -0,0 +1,8 @@
+#ifndef NOW_H
+#define NOW_H
+
+#include "datetime.h"
+
+extern datetime_sec now();
+
+#endif
diff --git a/old-patches/README b/old-patches/README
new file mode 100644
index 0000000..40fad8f
--- /dev/null
+++ b/old-patches/README
@@ -0,0 +1,3 @@
+These files are not used. Their purpose is to show the changes between
+netqmail-1.04 and netqmail-1.05. The latter is a combination of the
+original netqmail-1.04 patch and James Craig Burley's qmail-isoc patch.
diff --git a/old-patches/netqmail-1.04.patch b/old-patches/netqmail-1.04.patch
new file mode 100644
index 0000000..9b1eb54
--- /dev/null
+++ b/old-patches/netqmail-1.04.patch
@@ -0,0 +1,414 @@
+diff -urN qmail-1.03/CHANGES netqmail-1.04/CHANGES
+--- qmail-1.03/CHANGES Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/CHANGES Tue Oct 28 08:26:09 2003
+@@ -1,3 +1,17 @@
++20031027 version: netqmail 1.04.
++20031027 doc: INSTALL points to http://lifewithqmail.org/lwq.html
++20031027 doc: qmail.7 identifies installation as netqmail and points to
++ http://qmail.org/
++20031027 doc: qmail-queue.8 adds explanation of $QMAILQUEUE
++20031027 doc: qmail-log.5 adds reference to errors from $QMAILQUEUE script
++20031027 doc: FAQ also points to http://cr.yp.to/qmail/faq.html and
++ http://qmail.org/
++20031027 code: qmail-smtpd identifies itself as netqmail
++20031027 code: if $QMAILQUEUE is set, it's invoked instead of qmail-queue
++20031024 code: changed errno from int to #include.
++20031024 code: fixed .qmail parsing bug.
++20031024 code: recognize 0.0.0.0 as a local address.
++20031024 code: sendmail's -f flag now overrides environment variables.
+ 19980615 version: qmail 1.03.
+ 19980614 doc: eliminated BIN.setup in favor of a web page.
+ 19980614 code: added other auto* to qmail-showctl output.
+diff -urN qmail-1.03/FAQ netqmail-1.04/FAQ
+--- qmail-1.03/FAQ Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/FAQ Mon Oct 27 17:33:12 2003
+@@ -1,3 +1,6 @@
++See http://cr.yp.to/qmail/faq.html for newer FAQs not included in this
++document, and http://qmail.org/ for qmail community contributions.
++
+ 1. Controlling the appearance of outgoing messages
+ 1.1. How do I set up host masquerading?
+ 1.2. How do I set up user masquerading?
+diff -urN qmail-1.03/FILES netqmail-1.04/FILES
+--- qmail-1.03/FILES Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/FILES Mon Oct 27 17:33:12 2003
+@@ -10,6 +10,7 @@
+ INSTALL.ids
+ INSTALL.maildir
+ INSTALL.mbox
++INSTALL-1.03
+ INSTALL.vsm
+ REMOVE.sendmail
+ REMOVE.binmail
+diff -urN qmail-1.03/INSTALL netqmail-1.04/INSTALL
+--- qmail-1.03/INSTALL Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/INSTALL Mon Oct 27 17:33:12 2003
+@@ -1,84 +1 @@
+-SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and
+-information generally), the qmail system comes with NO WARRANTY. It's
+-much more secure and reliable than sendmail, but that's not saying much.
+-
+-
+-Things you have to decide before starting:
+-
+-* The qmail home directory, normally /var/qmail. To change this
+-directory, edit conf-qmail now.
+-
+-* The names of the qmail users and the qmail groups. To change these
+-names, edit conf-users and conf-groups now.
+-
+-
+-To create /var/qmail and configure qmail (won't interfere with sendmail):
+-
+- 1. Create the qmail home directory:
+- # mkdir /var/qmail
+-
+- 2. Read INSTALL.ids. You must set up the qmail group and the qmail
+- users before compiling the programs.
+-
+- 3. Compile the programs and create the qmail directory tree:
+- # make setup check
+-
+- 4. Read INSTALL.ctl and FAQ. Minimal survival command:
+- # ./config
+-
+- 5. Read INSTALL.alias. Minimal survival command:
+- # (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root)
+- # chmod 644 ~alias/.qmail*
+-
+- 6. Read INSTALL.mbox and INSTALL.vsm.
+-
+- 7. Read INSTALL.maildir.
+-
+- 8. Copy /var/qmail/boot/home (or proc) to /var/qmail/rc.
+-
+-
+-To test qmail deliveries (won't interfere with sendmail):
+-
+- 9. Enable deliveries of messages injected into qmail:
+- # csh -cf '/var/qmail/rc &'
+-
+-10. Read TEST.deliver.
+-
+-
+-To upgrade from sendmail to qmail:
+-
+-11. Read SENDMAIL. This is what your users will want to know about the
+- switch from sendmail to qmail.
+-
+-12. Read REMOVE.sendmail. You must remove sendmail before installing
+- qmail.
+-
+-13. Read REMOVE.binmail.
+-
+-14. Add
+- csh -cf '/var/qmail/rc &'
+- to your boot scripts, so that the qmail daemons are restarted
+- whenever your system reboots. Make sure you include the &.
+-
+-15. Make qmail's ``sendmail'' wrapper available to MUAs:
+- # ln -s /var/qmail/bin/sendmail /usr/lib/sendmail
+- # ln -s /var/qmail/bin/sendmail /usr/sbin/sendmail
+- /usr/sbin might not exist on your system.
+-
+-16. Set up qmail-smtpd in /etc/inetd.conf (all on one line):
+- smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env
+- tcp-env /var/qmail/bin/qmail-smtpd
+-
+-17. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons
+- are running.)
+-
+-18. Read TEST.receive.
+-
+-
+-
+-That's it! To report success:
+- % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to
+-Replace First M. Last with your name.
+-
+-If you have questions about qmail, join the qmail mailing list; see
+-http://pobox.com/~djb/qmail.html.
++See http://lifewithqmail.org/lwq.html
+diff -urN qmail-1.03/Makefile netqmail-1.04/Makefile
+--- qmail-1.03/Makefile Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/Makefile Mon Oct 27 17:33:13 2003
+@@ -1483,12 +1483,12 @@
+ trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
+ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
+ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
+-auto_split.o
++auto_split.o env.a
+ ./load qmail-send qsutil.o control.o constmap.o newfield.o \
+ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
+ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
+ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
+- substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
++ substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a
+
+ qmail-send.0: \
+ qmail-send.8
+diff -urN qmail-1.03/README netqmail-1.04/README
+--- qmail-1.03/README Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/README Mon Oct 27 17:33:13 2003
+@@ -3,6 +3,15 @@
+ Copyright 1998
+ D. J. Bernstein, qmail@pobox.com
+
++netqmail 1.04
++20031024
++No copyright claimed for changes from qmail 1.03 to netqmail 1.04
++Russell Nelson, nelson@qmail.org
++NOTE: netqmail 1.04 is a community-assembled distribution of qmail from
++the official qmail-1.03.tar.gz and patches approved by the community.
++D. J. Bernstein did not participate in, nor has he been asked to
++approve of this distribution.
++
+ qmail is a secure, reliable, efficient, simple message transfer agent.
+ It is meant as a replacement for the entire sendmail-binmail system on
+ typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and
+diff -urN qmail-1.03/UPGRADE netqmail-1.04/UPGRADE
+--- qmail-1.03/UPGRADE Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/UPGRADE Mon Oct 27 17:33:13 2003
+@@ -3,7 +3,7 @@
+ much more secure and reliable than sendmail, but that's not saying much.
+
+
+-Here's how to upgrade to qmail 1.03. This procedure will overwrite the
++Here's how to upgrade to netqmail 1.04. This procedure will overwrite the
+ old qmail binaries. Furthermore, it may begin delivering messages from
+ the queue before you have had a chance to test it.
+
+@@ -17,7 +17,7 @@
+
+
+ Before starting, compare conf* to your old conf*, and make any necessary
+-changes. You can copy conf* from 1.02.
++changes. You can copy conf* from 1.02 or 1.03.
+
+
+ How to install:
+diff -urN qmail-1.03/VERSION netqmail-1.04/VERSION
+--- qmail-1.03/VERSION Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/VERSION Mon Oct 27 17:33:13 2003
+@@ -1 +1 @@
+-qmail 1.03
++netqmail 1.04
+diff -urN qmail-1.03/cdb_seek.c netqmail-1.04/cdb_seek.c
+--- qmail-1.03/cdb_seek.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/cdb_seek.c Mon Oct 27 17:33:13 2003
+@@ -1,6 +1,5 @@
+ #include <sys/types.h>
+ #include <errno.h>
+-extern int errno;
+ #include "cdb.h"
+
+ #ifndef SEEK_SET
+diff -urN qmail-1.03/dns.c netqmail-1.04/dns.c
+--- qmail-1.03/dns.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/dns.c Mon Oct 27 17:33:13 2003
+@@ -7,8 +7,6 @@
+ #include <errno.h>
+ extern int res_query();
+ extern int res_search();
+-extern int errno;
+-extern int h_errno;
+ #include "ip.h"
+ #include "ipalloc.h"
+ #include "fmt.h"
+diff -urN qmail-1.03/error.3 netqmail-1.04/error.3
+--- qmail-1.03/error.3 Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/error.3 Mon Oct 27 17:33:13 2003
+@@ -3,8 +3,7 @@
+ error \- syscall error codes
+ .SH SYNTAX
+ .B #include <error.h>
+-
+-extern int \fBerrno\fP;
++.B #include <errno.h>
+
+ extern int \fBerror_intr\fP;
+ .br
+diff -urN qmail-1.03/error.h netqmail-1.04/error.h
+--- qmail-1.03/error.h Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/error.h Mon Oct 27 17:33:13 2003
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
+diff -urN qmail-1.03/ipme.c netqmail-1.04/ipme.c
+--- qmail-1.03/ipme.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/ipme.c Mon Oct 27 17:33:13 2003
+@@ -46,6 +46,11 @@
+ ipme.len = 0;
+ ix.pref = 0;
+
++ /* 0.0.0.0 is a special address which always refers to
++ * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
++ */
++ byte_copy(&ix.ip,4,"\0\0\0\0");
++ if (!ipalloc_append(&ipme,&ix)) { return 0; }
+ if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
+
+ len = 256;
+diff -urN qmail-1.03/qmail-local.c netqmail-1.04/qmail-local.c
+--- qmail-1.03/qmail-local.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/qmail-local.c Mon Oct 27 17:33:13 2003
+@@ -645,7 +645,7 @@
+ {
+ cmds.s[j] = 0;
+ k = j;
+- while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
++ while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')))
+ cmds.s[--k] = 0;
+ switch(cmds.s[i])
+ {
+diff -urN qmail-1.03/qmail-log.5 netqmail-1.04/qmail-log.5
+--- qmail-1.03/qmail-log.5 Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/qmail-log.5 Mon Oct 27 17:33:13 2003
+@@ -232,6 +232,11 @@
+ is unable to queue a bounce message,
+ usually because the machine is almost out of memory.
+ It will try again later.
++This can also be caused by incorrect settings of
++.B $QMAILQUEUE
++or errors in a program or script which
++.B $QMAILQUEUE
++points to.
+ .TP
+ .B unable to stat ...
+ .B qmail-send
+diff -urN qmail-1.03/qmail-queue.8 netqmail-1.04/qmail-queue.8
+--- qmail-1.03/qmail-queue.8 Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/qmail-queue.8 Mon Oct 27 17:33:13 2003
+@@ -40,6 +40,12 @@
+ However, the recipients probably expect to see a proper header,
+ as described in
+ .BR qmail-header(5) .
++
++Programs included with qmail which invoke
++.B qmail-queue
++will invoke the contents of
++.B $QMAILQUEUE
++instead, if that environment variable is set.
+ .SH "FILESYSTEM RESTRICTIONS"
+ .B qmail-queue
+ imposes two constraints on the queue structure:
+diff -urN qmail-1.03/qmail-smtpd.c netqmail-1.04/qmail-smtpd.c
+--- qmail-1.03/qmail-smtpd.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/qmail-smtpd.c Mon Oct 27 17:33:13 2003
+@@ -69,7 +69,7 @@
+ }
+ void smtp_help()
+ {
+- out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
++ out("214 netqmail home page: http://qmail.org/netqmail\r\n");
+ }
+ void smtp_quit()
+ {
+diff -urN qmail-1.03/qmail.7 netqmail-1.04/qmail.7
+--- qmail-1.03/qmail.7 Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/qmail.7 Mon Oct 27 17:33:13 2003
+@@ -55,12 +55,14 @@
+ and
+ .BR qmail-pop3d (8).
+
+-This documentation describes version
+-1.03
++This documentation describes netqmail version
++1.04
+ of
+ .BR qmail .
+ See
+ .B http://pobox.com/~djb/qmail.html
+ for other
+ .BR qmail -related
+-software.
++software, and
++.B http://qmail.org/
++for other qmail community contributions.
+diff -urN qmail-1.03/qmail.c netqmail-1.04/qmail.c
+--- qmail-1.03/qmail.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/qmail.c Mon Oct 27 17:33:13 2003
+@@ -6,14 +6,25 @@
+ #include "fd.h"
+ #include "qmail.h"
+ #include "auto_qmail.h"
++#include "env.h"
+
+-static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
++static char *binqqargs[2] = { 0, 0 } ;
++
++static void setup_qqargs()
++{
++ if(!binqqargs[0])
++ binqqargs[0] = env_get("QMAILQUEUE");
++ if(!binqqargs[0])
++ binqqargs[0] = "bin/qmail-queue";
++}
+
+ int qmail_open(qq)
+ struct qmail *qq;
+ {
+ int pim[2];
+ int pie[2];
++
++ setup_qqargs();
+
+ if (pipe(pim) == -1) return -1;
+ if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
+diff -urN qmail-1.03/sendmail.c netqmail-1.04/sendmail.c
+--- qmail-1.03/sendmail.c Mon Jun 15 04:53:16 1998
++++ netqmail-1.04/sendmail.c Mon Oct 27 17:33:13 2003
+@@ -45,6 +45,38 @@
+ _exit(111);
+ }
+
++void do_sender(s)
++const char *s;
++{
++ char *x;
++ int n;
++ int a;
++ int i;
++
++ env_unset("QMAILNAME");
++ env_unset("MAILNAME");
++ env_unset("NAME");
++ env_unset("QMAILHOST");
++ env_unset("MAILHOST");
++
++ n = str_len(s);
++ a = str_rchr(s, '@');
++ if (a == n)
++ {
++ env_put2("QMAILUSER", s);
++ return;
++ }
++ env_put2("QMAILHOST", s + a + 1);
++
++ x = (char *) alloc((a + 1) * sizeof(char));
++ if (!x) nomem();
++ for (i = 0; i < a; i++)
++ x[i] = s[i];
++ x[i] = 0;
++ env_put2("QMAILUSER", x);
++ alloc_free(x);
++}
++
+ int flagh;
+ char *sender;
+
+@@ -118,6 +150,7 @@
+ if (sender) {
+ *arg++ = "-f";
+ *arg++ = sender;
++ do_sender(sender);
+ }
+ *arg++ = "--";
+ for (i = 0;i < argc;++i) *arg++ = argv[i];
+
diff --git a/old-patches/qmail-isoc.patch b/old-patches/qmail-isoc.patch
new file mode 100644
index 0000000..6594dde
--- /dev/null
+++ b/old-patches/qmail-isoc.patch
@@ -0,0 +1,362 @@
+This patch is Copyright (C) 2004 by James Craig Burley. License
+below.
+
+2004-01-19 0040 EST James Craig Burley <craig-qmail@jcb-sc.com>
+
+This patch improves ISO C conformance of qmail code -- specifically,
+of qmail-lspawn, qmail-newmrh, qmail-newu, qmail-pop3d, qmail-popup,
+qmail-rspawn, and qmail-smtpd. This fixes two known bugs:
+
+ - qmail-smtpd can be crashed by a client sending a sufficiently
+ long (e.g. 2GB) header line in an email.
+
+ - qmail_lspawn, qmail-newmrh, qmail-newu, and qmail-rspawn might
+ crash or otherwise misbehave on hosts with a smaller "int" type
+ than "char *" type, e.g. 64-bit hosts with 32-bit "int"s.
+
+The other changes are unlikely to have any effect, except possibly on
+unusual architectures and/or in the presence of rare optimizations.
+
+This is Version 1 of this patch. It adds the changes to
+cdbmake_add.c, spawn.c, and, correspondingly, Makefile, which pertain
+to the second bug listed above.
+
+See all my qmail patches at <http://www.jcb-sc.com/qmail/patches/>.
+
+License:
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+*** qmail-1.03/cdbmake_add.c Mon Jun 15 06:53:16 1998
+--- qmail-1.03.0/cdbmake_add.c Mon Jan 19 00:35:38 2004
+***************
+*** 1,2 ****
+--- 1,3 ----
++ #include "alloc.h"
+ #include "cdbmake.h"
+
+*** qmail-1.03/Makefile Mon Jun 15 06:53:16 1998
+--- qmail-1.03.0/Makefile Mon Jan 19 00:35:35 2004
+*************** makelib cdbmake_pack.o cdbmake_hash.o cd
+*** 264,268 ****
+
+ cdbmake_add.o: \
+! compile cdbmake_add.c cdbmake.h uint32.h
+ ./compile cdbmake_add.c
+
+--- 264,268 ----
+
+ cdbmake_add.o: \
+! compile cdbmake_add.c cdbmake.h alloc.h uint32.h
+ ./compile cdbmake_add.c
+
+*************** trylsock.c compile load
+*** 1893,1897 ****
+ spawn.o: \
+ compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \
+! stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \
+ auto_qmail.h auto_uids.h auto_spawn.h
+ ./chkspawn
+--- 1893,1897 ----
+ spawn.o: \
+ compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \
+! stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \
+ auto_qmail.h auto_uids.h auto_spawn.h
+ ./chkspawn
+*** qmail-1.03/qmail-pop3d.c Mon Jun 15 06:53:16 1998
+--- qmail-1.03.0/qmail-pop3d.c Thu Jan 15 22:08:57 2004
+*************** void die_scan() { err("unable to scan $H
+*** 67,71 ****
+
+ void err_syntax() { err("syntax error"); }
+! void err_unimpl() { err("unimplemented"); }
+ void err_deleted() { err("already deleted"); }
+ void err_nozero() { err("messages are counted from 1"); }
+--- 67,71 ----
+
+ void err_syntax() { err("syntax error"); }
+! void err_unimpl(arg) char *arg; { err("unimplemented"); }
+ void err_deleted() { err("already deleted"); }
+ void err_nozero() { err("messages are counted from 1"); }
+*************** void err_nosuch() { err("unable to open
+*** 74,78 ****
+ void err_nounlink() { err("unable to unlink all deleted messages"); }
+
+! void okay() { puts("+OK \r\n"); flush(); }
+
+ void printfn(fn) char *fn;
+--- 74,78 ----
+ void err_nounlink() { err("unable to unlink all deleted messages"); }
+
+! void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
+
+ void printfn(fn) char *fn;
+*************** void getlist()
+*** 147,151 ****
+ }
+
+! void pop3_stat()
+ {
+ int i;
+--- 147,151 ----
+ }
+
+! void pop3_stat(arg) char *arg;
+ {
+ int i;
+*************** void pop3_stat()
+*** 162,174 ****
+ }
+
+! void pop3_rset()
+ {
+ int i;
+ for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
+ last = 0;
+! okay();
+ }
+
+! void pop3_last()
+ {
+ puts("+OK ");
+--- 162,174 ----
+ }
+
+! void pop3_rset(arg) char *arg;
+ {
+ int i;
+ for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
+ last = 0;
+! okay(0);
+ }
+
+! void pop3_last(arg) char *arg;
+ {
+ puts("+OK ");
+*************** void pop3_last()
+*** 178,182 ****
+ }
+
+! void pop3_quit()
+ {
+ int i;
+--- 178,182 ----
+ }
+
+! void pop3_quit(arg) char *arg;
+ {
+ int i;
+*************** void pop3_quit()
+*** 193,197 ****
+ rename(m[i].fn,line.s); /* if it fails, bummer */
+ }
+! okay();
+ die();
+ }
+--- 193,197 ----
+ rename(m[i].fn,line.s); /* if it fails, bummer */
+ }
+! okay(0);
+ die();
+ }
+*************** void pop3_dele(arg) char *arg;
+*** 215,219 ****
+ m[i].flagdeleted = 1;
+ if (i + 1 > last) last = i + 1;
+! okay();
+ }
+
+--- 215,219 ----
+ m[i].flagdeleted = 1;
+ if (i + 1 > last) last = i + 1;
+! okay(0);
+ }
+
+*************** void dolisting(arg,flaguidl) char *arg;
+*** 239,243 ****
+ }
+ else {
+! okay();
+ for (i = 0;i < numm;++i)
+ if (!m[i].flagdeleted)
+--- 239,243 ----
+ }
+ else {
+! okay(0);
+ for (i = 0;i < numm;++i)
+ if (!m[i].flagdeleted)
+*************** void pop3_top(arg) char *arg;
+*** 268,272 ****
+ fd = open_read(m[i].fn);
+ if (fd == -1) { err_nosuch(); return; }
+! okay();
+ substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
+ blast(&ssmsg,limit);
+--- 268,272 ----
+ fd = open_read(m[i].fn);
+ if (fd == -1) { err_nosuch(); return; }
+! okay(0);
+ substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
+ blast(&ssmsg,limit);
+*************** char **argv;
+*** 300,304 ****
+ getlist();
+
+! okay();
+ commands(&ssin,pop3commands);
+ die();
+--- 300,304 ----
+ getlist();
+
+! okay(0);
+ commands(&ssin,pop3commands);
+ die();
+*** qmail-1.03/qmail-popup.c Mon Jun 15 06:53:16 1998
+--- qmail-1.03.0/qmail-popup.c Thu Jan 15 22:09:21 2004
+*************** void die_badauth() { err("authorization
+*** 65,72 ****
+ void err_syntax() { err("syntax error"); }
+ void err_wantuser() { err("USER first"); }
+! void err_authoriz() { err("authorization first"); }
+
+! void okay() { puts("+OK \r\n"); flush(); }
+! void pop3_quit() { okay(); die(); }
+
+
+--- 65,72 ----
+ void err_syntax() { err("syntax error"); }
+ void err_wantuser() { err("USER first"); }
+! void err_authoriz(arg) char *arg; { err("authorization first"); }
+
+! void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
+! void pop3_quit(arg) char *arg; { okay(0); die(); }
+
+
+*************** void pop3_user(arg) char *arg;
+*** 137,141 ****
+ {
+ if (!*arg) { err_syntax(); return; }
+! okay();
+ seenuser = 1;
+ if (!stralloc_copys(&username,arg)) die_nomem();
+--- 137,141 ----
+ {
+ if (!*arg) { err_syntax(); return; }
+! okay(0);
+ seenuser = 1;
+ if (!stralloc_copys(&username,arg)) die_nomem();
+*** qmail-1.03/qmail-smtpd.c Mon Jun 15 06:53:16 1998
+--- qmail-1.03.0/qmail-smtpd.c Thu Jan 15 22:12:02 2004
+*************** void straynewline() { out("451 See http:
+*** 52,61 ****
+ void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+ void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
+! void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
+ void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
+ void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
+ void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
+! void err_noop() { out("250 ok\r\n"); }
+! void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
+ void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+
+--- 52,61 ----
+ void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+ void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
+! void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
+ void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
+ void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
+ void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
+! void err_noop(arg) char *arg; { out("250 ok\r\n"); }
+! void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); }
+ void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+
+*************** void smtp_greet(code) char *code;
+*** 68,76 ****
+ substdio_put(&ssout,greeting.s,greeting.len);
+ }
+! void smtp_help()
+ {
+ out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
+ }
+! void smtp_quit()
+ {
+ smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
+--- 68,76 ----
+ substdio_put(&ssout,greeting.s,greeting.len);
+ }
+! void smtp_help(arg) char *arg;
+ {
+ out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
+ }
+! void smtp_quit(arg) char *arg;
+ {
+ smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
+*************** void smtp_ehlo(arg) char *arg;
+*** 233,237 ****
+ seenmail = 0; dohelo(arg);
+ }
+! void smtp_rset()
+ {
+ seenmail = 0;
+--- 233,237 ----
+ seenmail = 0; dohelo(arg);
+ }
+! void smtp_rset(arg) char *arg;
+ {
+ seenmail = 0;
+*************** int *hops;
+*** 317,322 ****
+ if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
+ if (flagmaybey) if (pos == 1) flaginheader = 0;
+ }
+- ++pos;
+ if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
+ }
+--- 317,322 ----
+ if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
+ if (flagmaybey) if (pos == 1) flaginheader = 0;
++ ++pos;
+ }
+ if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
+ }
+*************** void acceptmessage(qp) unsigned long qp;
+*** 366,370 ****
+ }
+
+! void smtp_data() {
+ int hops;
+ unsigned long qp;
+--- 366,370 ----
+ }
+
+! void smtp_data(arg) char *arg; {
+ int hops;
+ unsigned long qp;
+*** qmail-1.03/spawn.c Mon Jun 15 06:53:16 1998
+--- qmail-1.03.0/spawn.c Mon Jan 19 00:35:38 2004
+***************
+*** 6,9 ****
+--- 6,10 ----
+ #include "byte.h"
+ #include "str.h"
++ #include "alloc.h"
+ #include "stralloc.h"
+ #include "select.h"
diff --git a/open.h b/open.h
new file mode 100644
index 0000000..c903113
--- /dev/null
+++ b/open.h
@@ -0,0 +1,10 @@
+#ifndef OPEN_H
+#define OPEN_H
+
+extern int open_read();
+extern int open_excl();
+extern int open_append();
+extern int open_trunc();
+extern int open_write();
+
+#endif
diff --git a/open_append.c b/open_append.c
new file mode 100644
index 0000000..93a0862
--- /dev/null
+++ b/open_append.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_append(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); }
diff --git a/open_excl.c b/open_excl.c
new file mode 100644
index 0000000..887e7c8
--- /dev/null
+++ b/open_excl.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_excl(fn) char *fn;
+{ return open(fn,O_WRONLY | O_EXCL | O_CREAT,0644); }
diff --git a/open_read.c b/open_read.c
new file mode 100644
index 0000000..f503e48
--- /dev/null
+++ b/open_read.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_read(fn) char *fn;
+{ return open(fn,O_RDONLY | O_NDELAY); }
diff --git a/open_trunc.c b/open_trunc.c
new file mode 100644
index 0000000..e275085
--- /dev/null
+++ b/open_trunc.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_trunc(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); }
diff --git a/open_write.c b/open_write.c
new file mode 100644
index 0000000..dcdcb95
--- /dev/null
+++ b/open_write.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_write(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY); }
diff --git a/other-patches/README b/other-patches/README
new file mode 100644
index 0000000..9fc7e78
--- /dev/null
+++ b/other-patches/README
@@ -0,0 +1,51 @@
+If you have glibc-2.3.1 or later then none of the software written by
+djb will compile because of an incompatible declaration of errno.
+
+Here you can find patches correcting this problem for software likely
+you need if you run qmail.
+
+To use the patches
+==================
+
+From the top distribution directory, apply the appropriate errno patch
+with -p1. So, for example, for mess822, you would do
+
+tar zxvf mess822-0.58.tar.gz
+cd mess822-0.58
+patch -p1 < /path/to/mess822-0.58.errno.patch
+
+In case of daemontools, you need the following adjustment:
+
+tar zxvf daemontools-0.76.tar.gz
+cd admin/daemontools-0.76
+patch -p1 /path/to/daemontools-0.76.errno.patch
+
+Notes
+-----
+
+ucspi-tcp:
+
+ In addition to the errno patch, there are two other patches
+ included here; both update rblsmtpd's behavior to current
+ practices on the net.
+
+ The a_record patch allows you to specify the error that will
+ be returned by rblsmtpd for a zone that has only A records in
+ it.
+
+ The nodefaultrbl patch changes the behavior of rblsmtpd so
+ that if no `-r' flag is given, than instead of falling back to
+ rbl.maps.vix.com as the default RBL, rblsmtpd assumes that no
+ RBL is to be used.
+
+qmailanalog:
+
+ In addition to the required errno change, the qmailanalog
+ patch also changes qmailanalog to accept both tai and tai64n
+ times. This is necessary because djb never updated qmailanalog
+ after he changed daemontools to tai64n.
+
+These notes were originally written by Mate Wierdl, and have been
+modified for this distribution by Russell Nelson, and then were rewritten
+by Mate again, and then added to by Russell Nelson.
+
diff --git a/other-patches/checkpassword-0.90.errno.patch b/other-patches/checkpassword-0.90.errno.patch
new file mode 100644
index 0000000..1412ca0
--- /dev/null
+++ b/other-patches/checkpassword-0.90.errno.patch
@@ -0,0 +1,12 @@
+diff -u checkpassword-0.90.old/error.h checkpassword-0.90/error.h
+--- checkpassword-0.90.old/error.h 2000-12-23 00:40:46.000000000 -0600
++++ checkpassword-0.90/error.h 2003-01-14 00:49:57.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
diff --git a/other-patches/daemontools-0.76.errno.patch b/other-patches/daemontools-0.76.errno.patch
new file mode 100644
index 0000000..d1884f5
--- /dev/null
+++ b/other-patches/daemontools-0.76.errno.patch
@@ -0,0 +1,12 @@
+diff -ur daemontools-0.76.old/src/error.h daemontools-0.76/src/error.h
+--- daemontools-0.76.old/src/error.h 2001-07-12 11:49:49.000000000 -0500
++++ daemontools-0.76/src/error.h 2003-01-09 21:52:01.000000000 -0600
+@@ -3,7 +3,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
diff --git a/other-patches/djbdns-1.05.errno.patch b/other-patches/djbdns-1.05.errno.patch
new file mode 100644
index 0000000..5099c16
--- /dev/null
+++ b/other-patches/djbdns-1.05.errno.patch
@@ -0,0 +1,12 @@
+diff -u djbdns-1.05.old/error.h djbdns-1.05/error.h
+--- djbdns-1.05.old/error.h 2001-02-11 15:11:23.000000000 -0600
++++ djbdns-1.05/error.h 2003-01-08 16:08:42.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
diff --git a/other-patches/dot-forward-0.71.errno.patch b/other-patches/dot-forward-0.71.errno.patch
new file mode 100644
index 0000000..fb21349
--- /dev/null
+++ b/other-patches/dot-forward-0.71.errno.patch
@@ -0,0 +1,12 @@
+diff -u dot-forward-0.71.orig/error.h dot-forward-0.71/error.h
+--- dot-forward-0.71.orig/error.h 1998-05-19 05:15:50.000000000 -0500
++++ dot-forward-0.71/error.h 2004-01-29 11:16:01.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
diff --git a/other-patches/fastforward-0.51.errno.patch b/other-patches/fastforward-0.51.errno.patch
new file mode 100644
index 0000000..8dc0d4a
--- /dev/null
+++ b/other-patches/fastforward-0.51.errno.patch
@@ -0,0 +1,22 @@
+diff -u fastforward-0.51.orig/cdb_seek.c fastforward-0.51/cdb_seek.c
+--- fastforward-0.51.orig/cdb_seek.c 1998-05-19 11:25:42.000000000 -0500
++++ fastforward-0.51/cdb_seek.c 2004-01-29 11:22:24.000000000 -0600
+@@ -1,6 +1,5 @@
+ #include <sys/types.h>
+ #include <errno.h>
+-extern int errno;
+ #include "cdb.h"
+
+ #ifndef SEEK_SET
+diff -u fastforward-0.51.orig/error.h fastforward-0.51/error.h
+--- fastforward-0.51.orig/error.h 1998-05-19 11:25:42.000000000 -0500
++++ fastforward-0.51/error.h 2004-01-29 11:14:47.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
diff --git a/other-patches/mess822-0.58.errno.patch b/other-patches/mess822-0.58.errno.patch
new file mode 100644
index 0000000..172396e
--- /dev/null
+++ b/other-patches/mess822-0.58.errno.patch
@@ -0,0 +1,33 @@
+diff -u mess822-0.58.old/cdb_seek.c mess822-0.58/cdb_seek.c
+--- mess822-0.58.old/cdb_seek.c 1998-09-04 21:33:37.000000000 -0500
++++ mess822-0.58/cdb_seek.c 2003-01-13 23:17:30.000000000 -0600
+@@ -1,6 +1,5 @@
+ #include <sys/types.h>
+ #include <errno.h>
+-extern int errno;
+ #include "cdb.h"
+
+ #ifndef SEEK_SET
+diff -u mess822-0.58.old/error.h mess822-0.58/error.h
+--- mess822-0.58.old/error.h 1998-09-04 21:33:37.000000000 -0500
++++ mess822-0.58/error.h 2003-01-13 23:18:09.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
+diff -u mess822-0.58.old/leapsecs_read.c mess822-0.58/leapsecs_read.c
+--- mess822-0.58.old/leapsecs_read.c 1998-09-04 21:33:37.000000000 -0500
++++ mess822-0.58/leapsecs_read.c 2003-01-13 23:19:17.000000000 -0600
+@@ -2,7 +2,6 @@
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <errno.h>
+-extern int errno;
+ #include "tai.h"
+ #include "leapsecs.h"
+
diff --git a/other-patches/qmailanalog-0.70.errno.patch b/other-patches/qmailanalog-0.70.errno.patch
new file mode 100644
index 0000000..94f4583
--- /dev/null
+++ b/other-patches/qmailanalog-0.70.errno.patch
@@ -0,0 +1,87 @@
+diff -u orig/error.3 ./error.3
+--- orig/error.3 1998-08-30 17:39:27.000000000 -0400
++++ ./error.3 2004-09-06 12:17:26.000000000 -0400
+@@ -3,8 +3,8 @@
+ error \- syscall error codes
+ .SH SYNTAX
+ .B #include <error.h>
+-
+-extern int \fBerrno\fP;
++.br
++.B #include <errno.h>
+
+ extern int \fBerror_intr\fP;
+ .br
+diff -u orig/error.c ./error.c
+--- orig/error.c 1998-08-30 17:39:27.000000000 -0400
++++ ./error.c 2004-09-06 12:17:26.000000000 -0400
+@@ -1,4 +1,3 @@
+-#include <errno.h>
+ #include "error.h"
+
+ /* warning: as coverage improves here, should update error_{str,temp} */
+diff -u orig/error.h ./error.h
+--- orig/error.h 1998-08-30 17:39:27.000000000 -0400
++++ ./error.h 2004-09-06 12:17:26.000000000 -0400
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
+diff -u orig/Makefile ./Makefile
+--- orig/Makefile 1998-08-30 17:39:26.000000000 -0400
++++ ./Makefile 2004-09-06 12:17:26.000000000 -0400
+@@ -259,7 +259,7 @@
+ matchup.o: \
+ compile matchup.c stralloc.h gen_alloc.h gen_alloc.h gen_allocdefs.h \
+ strerr.h getln.h substdio.h subfd.h substdio.h readwrite.h exit.h \
+-str.h fmt.h scan.h case.h
++str.h fmt.h scan.h case.h alloc.h
+ ./compile matchup.c
+
+ open.a: \
+diff -u orig/matchup.c ./matchup.c
+--- orig/matchup.c 1998-08-30 17:39:27.000000000 -0400
++++ ./matchup.c 2004-09-06 12:23:20.000000000 -0400
+@@ -1,3 +1,4 @@
++#include "alloc.h"
+ #include "stralloc.h"
+ #include "gen_alloc.h"
+ #include "gen_allocdefs.h"
+@@ -439,6 +440,32 @@
+ if (getln(subfdin,&line,&match,'\n') == -1) die_read();
+ if (!match) break;
+
++ if (line.s[0] == '@' && line.len >= 25) {
++ unsigned long secs;
++ unsigned long nanosecs;
++ unsigned long u;
++
++ secs = 0;
++ nanosecs = 0;
++ for (i = 1; i < line.len;i++) {
++ u = line.s[i] - '0';
++ if (u >= 10) {
++ u = line.s[i] - 'a';
++ if (u >= 6) break;
++ u += 10;
++ }
++ secs <<= 4;
++ secs += nanosecs >> 28;
++ nanosecs &= 0xfffffff;
++ nanosecs <<= 4;
++ nanosecs += u;
++ }
++ secs -= 4611686018427387914ULL;
++ i = fmt_uint0(line.s,secs,9);
++ line.s[i++] = '.';
++ i += fmt_uint0(line.s+i,nanosecs,9);
++ while (i < 25) line.s[i++] = ' ';
++ }
+ if (!stralloc_copy(&outline,&line)) nomem();
+
+ for (i = 0;i < line.len;++i) {
diff --git a/other-patches/serialmail-0.75.errno.patch b/other-patches/serialmail-0.75.errno.patch
new file mode 100644
index 0000000..bf01d1b
--- /dev/null
+++ b/other-patches/serialmail-0.75.errno.patch
@@ -0,0 +1,23 @@
+diff -u serialmail-0.75.orig/error.h serialmail-0.75/error.h
+--- serialmail-0.75.orig/error.h 1998-11-14 20:28:23.000000000 -0600
++++ serialmail-0.75/error.h 2004-01-29 11:15:32.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
+diff -u serialmail-0.75.orig/leapsecs_read.c serialmail-0.75/leapsecs_read.c
+--- serialmail-0.75.orig/leapsecs_read.c 1998-11-14 20:28:23.000000000 -0600
++++ serialmail-0.75/leapsecs_read.c 2004-01-29 11:20:09.000000000 -0600
+@@ -2,7 +2,6 @@
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <errno.h>
+-extern int errno;
+ #include "tai.h"
+ #include "leapsecs.h"
+
diff --git a/other-patches/ucspi-tcp-0.88.a_record.patch b/other-patches/ucspi-tcp-0.88.a_record.patch
new file mode 100644
index 0000000..903125e
--- /dev/null
+++ b/other-patches/ucspi-tcp-0.88.a_record.patch
@@ -0,0 +1,64 @@
+diff -ruN --exclude conf-* ucspi-tcp-0.88/rblsmtpd.c ucspi-tcp-0.88.fix/rblsmtpd.c
+--- ucspi-tcp-0.88/rblsmtpd.c Sat Mar 18 10:18:42 2000
++++ ucspi-tcp-0.88.fix/rblsmtpd.c Wed Aug 9 16:42:33 2000
+@@ -60,16 +60,54 @@
+
+ void rbl(char *base)
+ {
++ int i;
++ char *altreply = 0;
+ if (decision) return;
+ if (!stralloc_copy(&tmp,&ip_reverse)) nomem();
++ i = str_chr(base, ':');
++ if (base[i]) {
++ base[i] = 0;
++ altreply = base+i+1;
++ }
+ if (!stralloc_cats(&tmp,base)) nomem();
+- if (dns_txt(&text,&tmp) == -1) {
+- flagmustnotbounce = 1;
+- if (flagfailclosed) {
+- if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem();
+- decision = 2;
++ if (altreply) {
++ if (dns_ip4(&text,&tmp) == -1) {
++ flagmustnotbounce = 1;
++ if (flagfailclosed) {
++ if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem();
++ decision = 2;
++ }
++ return;
++ }
++ if (text.len) {
++ if(!stralloc_copys(&text, "")) nomem();
++ while(*altreply) {
++ char *x;
++ i = str_chr(altreply, '%');
++ if(!stralloc_catb(&text, altreply, i)) nomem();
++ if(altreply[i] &&
++ altreply[i+1]=='I' &&
++ altreply[i+2]=='P' &&
++ altreply[i+3]=='%') {
++ if(!stralloc_catb(&text, ip_env, str_len(ip_env))) nomem();
++ altreply+=i+4;
++ } else if(altreply[i]) {
++ if(!stralloc_cats(&text, "%")) nomem();
++ altreply+=i+1;
++ } else {
++ altreply+=i;
++ }
++ }
++ }
++ } else {
++ if (dns_txt(&text,&tmp) == -1) {
++ flagmustnotbounce = 1;
++ if (flagfailclosed) {
++ if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem();
++ decision = 2;
++ }
++ return;
+ }
+- return;
+ }
+ if (text.len)
+ if (flagrblbounce)
diff --git a/other-patches/ucspi-tcp-0.88.errno.patch b/other-patches/ucspi-tcp-0.88.errno.patch
new file mode 100644
index 0000000..fd93909
--- /dev/null
+++ b/other-patches/ucspi-tcp-0.88.errno.patch
@@ -0,0 +1,12 @@
+diff -u ucspi-tcp-0.88.old/error.h ucspi-tcp-0.88/error.h
+--- ucspi-tcp-0.88.old/error.h 2000-03-18 09:18:20.000000000 -0600
++++ ucspi-tcp-0.88/error.h 2003-01-08 13:39:12.000000000 -0600
+@@ -1,7 +1,7 @@
+ #ifndef ERROR_H
+ #define ERROR_H
+
+-extern int errno;
++#include <errno.h>
+
+ extern int error_intr;
+ extern int error_nomem;
diff --git a/other-patches/ucspi-tcp-0.88.nodefaultrbl.patch b/other-patches/ucspi-tcp-0.88.nodefaultrbl.patch
new file mode 100644
index 0000000..fd9e90e
--- /dev/null
+++ b/other-patches/ucspi-tcp-0.88.nodefaultrbl.patch
@@ -0,0 +1,28 @@
+diff -ur ucspi-tcp-0.88.orig/rblsmtpd.c ucspi-tcp-0.88/rblsmtpd.c
+--- ucspi-tcp-0.88.orig/rblsmtpd.c Thu Jan 16 14:33:56 2003
++++ ucspi-tcp-0.88/rblsmtpd.c Thu Jan 16 14:38:17 2003
+@@ -155,7 +155,6 @@
+
+ main(int argc,char **argv,char **envp)
+ {
+- int flagwantdefaultrbl = 1;
+ char *x;
+ int opt;
+
+@@ -182,7 +181,7 @@
+ case 'c': flagfailclosed = 1; break;
+ case 'C': flagfailclosed = 0; break;
+ case 't': scan_ulong(optarg,&timeout); break;
+- case 'r': rbl(optarg); flagwantdefaultrbl = 0; break;
++ case 'r': rbl(optarg); break;
+ case 'a': antirbl(optarg); break;
+ default: usage();
+ }
+@@ -190,7 +189,6 @@
+ argv += optind;
+ if (!*argv) usage();
+
+- if (flagwantdefaultrbl) rbl("rbl.maps.vix.com");
+ if (decision >= 2) rblsmtpd();
+
+ pathexec_run(*argv,argv,envp);
diff --git a/pinq.sh b/pinq.sh
new file mode 100644
index 0000000..93e747a
--- /dev/null
+++ b/pinq.sh
@@ -0,0 +1 @@
+QMAIL/bin/maildir2mbox && exec pine ${1+"$@"}
diff --git a/predate.c b/predate.c
new file mode 100644
index 0000000..9648f6e
--- /dev/null
+++ b/predate.c
@@ -0,0 +1,116 @@
+#include <sys/types.h>
+#include <time.h>
+#include "datetime.h"
+#include "fork.h"
+#include "wait.h"
+#include "fd.h"
+#include "fmt.h"
+#include "strerr.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "readwrite.h"
+#include "exit.h"
+
+#define FATAL "predate: fatal: "
+
+static char *montab[12] = {
+"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+char num[FMT_ULONG];
+char outbuf[1024];
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ time_t now;
+ struct tm *tm;
+ struct datetime dt;
+ datetime_sec utc;
+ datetime_sec local;
+ int minutes;
+ int pi[2];
+ substdio ss;
+ int wstat;
+ int pid;
+
+ sig_pipeignore();
+
+ if (!argv[1])
+ strerr_die1x(100,"predate: usage: predate child");
+
+ if (pipe(pi) == -1)
+ strerr_die2sys(111,FATAL,"unable to create pipe: ");
+
+ switch(pid = fork()) {
+ case -1:
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+ case 0:
+ close(pi[1]);
+ if (fd_move(0,pi[0]) == -1)
+ strerr_die2sys(111,FATAL,"unable to set up fds: ");
+ sig_pipedefault();
+ execvp(argv[1],argv + 1);
+ strerr_die4sys(111,FATAL,"unable to run ",argv[1],": ");
+ }
+ close(pi[0]);
+ substdio_fdbuf(&ss,write,pi[1],outbuf,sizeof(outbuf));
+
+ time(&now);
+
+ tm = gmtime(&now);
+ dt.year = tm->tm_year;
+ dt.mon = tm->tm_mon;
+ dt.mday = tm->tm_mday;
+ dt.hour = tm->tm_hour;
+ dt.min = tm->tm_min;
+ dt.sec = tm->tm_sec;
+ utc = datetime_untai(&dt); /* utc == now, if gmtime ignores leap seconds */
+
+ tm = localtime(&now);
+ dt.year = tm->tm_year;
+ dt.mon = tm->tm_mon;
+ dt.mday = tm->tm_mday;
+ dt.hour = tm->tm_hour;
+ dt.min = tm->tm_min;
+ dt.sec = tm->tm_sec;
+ local = datetime_untai(&dt);
+
+ substdio_puts(&ss,"Date: ");
+ substdio_put(&ss,num,fmt_uint(num,dt.mday));
+ substdio_puts(&ss," ");
+ substdio_puts(&ss,montab[dt.mon]);
+ substdio_puts(&ss," ");
+ substdio_put(&ss,num,fmt_uint(num,dt.year + 1900));
+ substdio_puts(&ss," ");
+ substdio_put(&ss,num,fmt_uint0(num,dt.hour,2));
+ substdio_puts(&ss,":");
+ substdio_put(&ss,num,fmt_uint0(num,dt.min,2));
+ substdio_puts(&ss,":");
+ substdio_put(&ss,num,fmt_uint0(num,dt.sec,2));
+
+ if (local < utc) {
+ minutes = (utc - local + 30) / 60;
+ substdio_puts(&ss," -");
+ substdio_put(&ss,num,fmt_uint0(num,minutes / 60,2));
+ substdio_put(&ss,num,fmt_uint0(num,minutes % 60,2));
+ }
+ else {
+ minutes = (local - utc + 30) / 60;
+ substdio_puts(&ss," +");
+ substdio_put(&ss,num,fmt_uint0(num,minutes / 60,2));
+ substdio_put(&ss,num,fmt_uint0(num,minutes % 60,2));
+ }
+
+ substdio_puts(&ss,"\n");
+ substdio_copy(&ss,subfdin);
+ substdio_flush(&ss);
+ close(pi[1]);
+
+ if (wait_pid(&wstat,pid) == -1)
+ strerr_die2sys(111,FATAL,"wait failed: ");
+ if (wait_crashed(wstat))
+ strerr_die2x(111,FATAL,"child crashed");
+ _exit(wait_exitcode(wstat));
+}
diff --git a/preline.1 b/preline.1
new file mode 100644
index 0000000..69a757c
--- /dev/null
+++ b/preline.1
@@ -0,0 +1,57 @@
+.TH preline 1
+.SH NAME
+preline \- prepend lines to message
+.SH SYNOPSIS
+in
+.BR .qmail\fIext :
+.B | preline \fIcommand
+.SH DESCRIPTION
+.B preline
+feeds each incoming mail message through
+.IR command .
+At the top of each message it inserts
+a UUCP-style
+.B From_
+line, a
+.B Return-Path
+line, and a
+.B Delivered-To
+line.
+
+.B preline
+is useful for
+.B procmail
+and
+ELM's
+.BR filter ,
+which
+do not understand the
+.B qmail-command
+environment variables.
+.SH OPTIONS
+.TP
+.B \-d
+Do not include the
+.B Delivered-To
+line. You should use this option when the
+recipient of the incoming mail message is actually under remote control,
+but was sent here through
+.B control/virtualdomains
+for manual routing.
+.TP
+.B \-f
+Do not include the
+.B From_
+line. You should use this option except for
+.IR command s
+that create
+.I mbox
+files.
+.TP
+.B \-r
+Do not include the
+.B Return-Path
+line.
+.SH "SEE ALSO"
+mbox(5),
+qmail-command(8)
diff --git a/preline.c b/preline.c
new file mode 100644
index 0000000..1a4cef8
--- /dev/null
+++ b/preline.c
@@ -0,0 +1,90 @@
+#include "fd.h"
+#include "sgetopt.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "substdio.h"
+#include "exit.h"
+#include "fork.h"
+#include "wait.h"
+#include "env.h"
+#include "sig.h"
+#include "error.h"
+
+#define FATAL "preline: fatal: "
+
+void die_usage()
+{
+ strerr_die1x(100,"preline: usage: preline cmd [ arg ... ]");
+}
+
+int flagufline = 1; char *ufline;
+int flagrpline = 1; char *rpline;
+int flagdtline = 1; char *dtline;
+
+char outbuf[SUBSTDIO_OUTSIZE];
+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)
+int argc;
+char **argv;
+{
+ int opt;
+ int pi[2];
+ int pid;
+ int wstat;
+
+ sig_pipeignore();
+
+ if (!(ufline = env_get("UFLINE"))) die_usage();
+ if (!(rpline = env_get("RPLINE"))) die_usage();
+ if (!(dtline = env_get("DTLINE"))) die_usage();
+
+ while ((opt = getopt(argc,argv,"frdFRD")) != opteof)
+ switch(opt) {
+ case 'f': flagufline = 0; break;
+ case 'r': flagrpline = 0; break;
+ case 'd': flagdtline = 0; break;
+ case 'F': flagufline = 1; break;
+ case 'R': flagrpline = 1; break;
+ case 'D': flagdtline = 1; break;
+ default: die_usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (!*argv) die_usage();
+
+ if (pipe(pi) == -1)
+ strerr_die2sys(111,FATAL,"unable to create pipe: ");
+
+ pid = fork();
+ if (pid == -1)
+ strerr_die2sys(111,FATAL,"unable to fork: ");
+
+ if (pid == 0) {
+ close(pi[1]);
+ if (fd_move(0,pi[0]) == -1)
+ strerr_die2sys(111,FATAL,"unable to set up fds: ");
+ sig_pipedefault();
+ execvp(*argv,argv);
+ strerr_die4sys(error_temp(errno) ? 111 : 100,FATAL,"unable to run ",*argv,": ");
+ }
+ close(pi[0]);
+ if (fd_move(1,pi[1]) == -1)
+ strerr_die2sys(111,FATAL,"unable to set up fds: ");
+
+ if (flagufline) substdio_bputs(&ssout,ufline);
+ if (flagrpline) substdio_bputs(&ssout,rpline);
+ if (flagdtline) substdio_bputs(&ssout,dtline);
+ if (substdio_copy(&ssout,&ssin) != 0)
+ strerr_die2sys(111,FATAL,"unable to copy input: ");
+ substdio_flush(&ssout);
+ close(1);
+
+ if (wait_pid(&wstat,pid) == -1)
+ strerr_die2sys(111,FATAL,"wait failed: ");
+ if (wait_crashed(wstat))
+ strerr_die2x(111,FATAL,"child crashed");
+ _exit(wait_exitcode(wstat));
+}
diff --git a/prioq.c b/prioq.c
new file mode 100644
index 0000000..051aa45
--- /dev/null
+++ b/prioq.c
@@ -0,0 +1,58 @@
+#include "alloc.h"
+#include "gen_allocdefs.h"
+#include "prioq.h"
+
+GEN_ALLOC_readyplus(prioq,struct prioq_elt,p,len,a,i,n,x,100,prioq_readyplus)
+
+int prioq_insert(pq,pe)
+prioq *pq;
+struct prioq_elt *pe;
+{
+ int i;
+ int j;
+ if (!prioq_readyplus(pq,1)) return 0;
+ j = pq->len++;
+ while (j)
+ {
+ i = (j - 1)/2;
+ if (pq->p[i].dt <= pe->dt) break;
+ pq->p[j] = pq->p[i];
+ j = i;
+ }
+ pq->p[j] = *pe;
+ return 1;
+}
+
+int prioq_min(pq,pe)
+prioq *pq;
+struct prioq_elt *pe;
+{
+ if (!pq->p) return 0;
+ if (!pq->len) return 0;
+ *pe = pq->p[0];
+ return 1;
+}
+
+void prioq_delmin(pq)
+prioq *pq;
+{
+ int i;
+ int j;
+ int n;
+ if (!pq->p) return;
+ n = pq->len;
+ if (!n) return;
+ i = 0;
+ --n;
+ for (;;)
+ {
+ j = i + i + 2;
+ if (j > n) break;
+ if (pq->p[j - 1].dt <= pq->p[j].dt) --j;
+ if (pq->p[n].dt <= pq->p[j].dt) break;
+ pq->p[i] = pq->p[j];
+ i = j;
+ }
+ pq->p[i] = pq->p[n];
+ pq->len = n;
+}
diff --git a/prioq.h b/prioq.h
new file mode 100644
index 0000000..32a0d7c
--- /dev/null
+++ b/prioq.h
@@ -0,0 +1,15 @@
+#ifndef PRIOQ_H
+#define PRIOQ_H
+
+#include "datetime.h"
+#include "gen_alloc.h"
+
+struct prioq_elt { datetime_sec dt; unsigned long id; } ;
+
+GEN_ALLOC_typedef(prioq,struct prioq_elt,p,len,a)
+
+extern int prioq_insert();
+extern int prioq_min();
+extern void prioq_delmin();
+
+#endif
diff --git a/proc+df.sh b/proc+df.sh
new file mode 100644
index 0000000..eb9e92c
--- /dev/null
+++ b/proc+df.sh
@@ -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
diff --git a/proc.sh b/proc.sh
new file mode 100644
index 0000000..3c76220
--- /dev/null
+++ b/proc.sh
@@ -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
diff --git a/prot.c b/prot.c
new file mode 100644
index 0000000..a38e0f9
--- /dev/null
+++ b/prot.c
@@ -0,0 +1,21 @@
+#include "hasshsgr.h"
+#include "prot.h"
+
+/* XXX: there are more portability problems here waiting to leap out at me */
+
+int prot_gid(gid) int gid;
+{
+#ifdef HASSHORTSETGROUPS
+ short x[2];
+ x[0] = gid; x[1] = 73; /* catch errors */
+ if (setgroups(1,x) == -1) return -1;
+#else
+ if (setgroups(1,&gid) == -1) return -1;
+#endif
+ return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
+}
+
+int prot_uid(uid) int uid;
+{
+ return setuid(uid);
+}
diff --git a/prot.h b/prot.h
new file mode 100644
index 0000000..8c5d81a
--- /dev/null
+++ b/prot.h
@@ -0,0 +1,7 @@
+#ifndef PROT_H
+#define PROT_H
+
+extern int prot_gid();
+extern int prot_uid();
+
+#endif
diff --git a/qail.sh b/qail.sh
new file mode 100644
index 0000000..7e31c33
--- /dev/null
+++ b/qail.sh
@@ -0,0 +1 @@
+QMAIL/bin/maildir2mbox && exec Mail ${1+"$@"}
diff --git a/qbiff.1 b/qbiff.1
new file mode 100644
index 0000000..14db027
--- /dev/null
+++ b/qbiff.1
@@ -0,0 +1,31 @@
+.TH qbiff 1
+.SH NAME
+qbiff \- announce new mail the moment it arrives
+.SH SYNOPSIS
+in
+.BR .qmail :
+.B |qbiff
+.SH DESCRIPTION
+.B qbiff
+writes a message to your screen
+whenever a new mail message is delivered,
+if you ran
+.B biff y
+after logging in.
+
+.B WARNING:
+If you create a
+.B .qmail
+file to enable
+.BR qbiff ,
+make sure to also add a line specifying delivery to your normal mailbox.
+For example:
+
+.EX
+ /home/joe/Mailbox
+.br
+ |qbiff
+.EE
+.SH "SEE ALSO"
+biff(1),
+dot-qmail(5)
diff --git a/qbiff.c b/qbiff.c
new file mode 100644
index 0000000..4e9f1d8
--- /dev/null
+++ b/qbiff.c
@@ -0,0 +1,113 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utmp.h>
+#ifndef UTMP_FILE
+#ifdef _PATH_UTMP
+#define UTMP_FILE _PATH_UTMP
+#else
+#define UTMP_FILE "/etc/utmp"
+#endif
+#endif
+#include "readwrite.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "open.h"
+#include "byte.h"
+#include "str.h"
+#include "headerbody.h"
+#include "hfield.h"
+#include "env.h"
+#include "exit.h"
+
+substdio ssutmp;
+char bufutmp[sizeof(struct utmp) * 16];
+int fdutmp;
+substdio sstty;
+char buftty[1024];
+int fdtty;
+
+struct utmp ut;
+char line[sizeof(ut.ut_line) + 1];
+stralloc woof = {0};
+stralloc tofrom = {0};
+stralloc text = {0};
+
+void doit(s,n) char *s; int n;
+{
+ if (!stralloc_catb(&text,s,n)) _exit(0);
+ if (text.len > 78) text.len = 78;
+}
+void dobody(h) stralloc *h; { doit(h->s,h->len); }
+void doheader(h) stralloc *h;
+{
+ int i;
+ if (hfield_known(h->s,h->len) == H_SUBJECT)
+ {
+ i = hfield_skipname(h->s,h->len);
+ doit(h->s + i,h->len - i);
+ }
+}
+void finishheader() { ; }
+
+void main()
+{
+ char *user;
+ char *sender;
+ char *userext;
+ struct stat st;
+ int i;
+
+ if (chdir("/dev") == -1) _exit(0);
+
+ if (!(user = env_get("USER"))) _exit(0);
+ if (!(sender = env_get("SENDER"))) _exit(0);
+ if (!(userext = env_get("LOCAL"))) _exit(0);
+ if (str_len(user) > sizeof(ut.ut_name)) _exit(0);
+
+ if (!stralloc_copys(&tofrom,"*** TO <")) _exit(0);
+ if (!stralloc_cats(&tofrom,userext)) _exit(0);
+ if (!stralloc_cats(&tofrom,"> FROM <")) _exit(0);
+ if (!stralloc_cats(&tofrom,sender)) _exit(0);
+ if (!stralloc_cats(&tofrom,">")) _exit(0);
+
+ for (i = 0;i < tofrom.len;++i)
+ if ((tofrom.s[i] < 32) || (tofrom.s[i] > 126))
+ tofrom.s[i] = '_';
+
+ if (!stralloc_copys(&text," ")) _exit(0);
+ if (headerbody(subfdin,doheader,finishheader,dobody) == -1) _exit(0);
+
+ for (i = 0;i < text.len;++i)
+ if ((text.s[i] < 32) || (text.s[i] > 126))
+ text.s[i] = '/';
+
+ if (!stralloc_copys(&woof,"\015\n\007")) _exit(0);
+ if (!stralloc_cat(&woof,&tofrom)) _exit(0);
+ if (!stralloc_cats(&woof,"\015\n")) _exit(0);
+ if (!stralloc_cat(&woof,&text)) _exit(0);
+ if (!stralloc_cats(&woof,"\015\n")) _exit(0);
+
+ fdutmp = open_read(UTMP_FILE);
+ if (fdutmp == -1) _exit(0);
+ substdio_fdbuf(&ssutmp,read,fdutmp,bufutmp,sizeof(bufutmp));
+
+ while (substdio_get(&ssutmp,&ut,sizeof(ut)) == sizeof(ut))
+ if (!str_diffn(ut.ut_name,user,sizeof(ut.ut_name)))
+ {
+ byte_copy(line,sizeof(ut.ut_line),ut.ut_line);
+ line[sizeof(ut.ut_line)] = 0;
+ if (line[0] == '/') continue;
+ if (!line[0]) continue;
+ if (line[str_chr(line,'.')]) continue;
+ fdtty = open_append(line);
+ if (fdtty == -1) continue;
+ if (fstat(fdtty,&st) == -1) { close(fdtty); continue; }
+ if (!(st.st_mode & 0100)) { close(fdtty); continue; }
+ if (st.st_uid != getuid()) { close(fdtty); continue; }
+ substdio_fdbuf(&sstty,write,fdtty,buftty,sizeof(buftty));
+ substdio_putflush(&sstty,woof.s,woof.len);
+ close(fdtty);
+ }
+ _exit(0);
+}
diff --git a/qlx.h b/qlx.h
new file mode 100644
index 0000000..713946d
--- /dev/null
+++ b/qlx.h
@@ -0,0 +1,18 @@
+#ifndef QLX_H
+#define QLX_H
+
+/* 0, 111, 100 are qmail-local success, soft, hard */
+
+#define QLX_USAGE 112
+#define QLX_BUG 101
+#define QLX_ROOT 113
+#define QLX_NFS 115
+#define QLX_NOALIAS 116
+#define QLX_CDB 117
+#define QLX_SYS 118
+#define QLX_NOMEM 119
+#define QLX_EXECSOFT 120
+#define QLX_EXECPW 121
+#define QLX_EXECHARD 126
+
+#endif
diff --git a/qmail-clean.8 b/qmail-clean.8
new file mode 100644
index 0000000..41795c6
--- /dev/null
+++ b/qmail-clean.8
@@ -0,0 +1,13 @@
+.TH qmail-clean 8
+.SH NAME
+qmail-clean \- clean up the queue directory
+.SH SYNOPSIS
+.B qmail-clean
+.SH DESCRIPTION
+.B qmail-clean
+reads a cleanup command from descriptor 0,
+performs the cleanup,
+prints the results to descriptor 1,
+and repeats.
+.SH "SEE ALSO"
+qmail-send(8)
diff --git a/qmail-clean.c b/qmail-clean.c
new file mode 100644
index 0000000..7539007
--- /dev/null
+++ b/qmail-clean.c
@@ -0,0 +1,98 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "readwrite.h"
+#include "sig.h"
+#include "now.h"
+#include "str.h"
+#include "direntry.h"
+#include "getln.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "byte.h"
+#include "scan.h"
+#include "fmt.h"
+#include "error.h"
+#include "exit.h"
+#include "fmtqfn.h"
+#include "auto_qmail.h"
+
+#define OSSIFIED 129600 /* see qmail-send.c */
+
+stralloc line = {0};
+
+void cleanuppid()
+{
+ DIR *dir;
+ direntry *d;
+ struct stat st;
+ datetime_sec time;
+
+ time = now();
+ dir = opendir("pid");
+ if (!dir) return;
+ while (d = readdir(dir))
+ {
+ if (str_equal(d->d_name,".")) continue;
+ if (str_equal(d->d_name,"..")) continue;
+ if (!stralloc_copys(&line,"pid/")) continue;
+ if (!stralloc_cats(&line,d->d_name)) continue;
+ if (!stralloc_0(&line)) continue;
+ if (stat(line.s,&st) == -1) continue;
+ if (time < st.st_atime + OSSIFIED) continue;
+ unlink(line.s);
+ }
+ closedir(dir);
+}
+
+char fnbuf[FMTQFN];
+
+void respond(s) char *s; { if (substdio_putflush(subfdoutsmall,s,1) == -1) _exit(100); }
+
+void main()
+{
+ int i;
+ int match;
+ int cleanuploop;
+ unsigned long id;
+
+ if (chdir(auto_qmail) == -1) _exit(111);
+ if (chdir("queue") == -1) _exit(111);
+
+ sig_pipeignore();
+
+ if (!stralloc_ready(&line,200)) _exit(111);
+
+ cleanuploop = 0;
+
+ for (;;)
+ {
+ if (cleanuploop) --cleanuploop; else { cleanuppid(); cleanuploop = 30; }
+ if (getln(subfdinsmall,&line,&match,'\0') == -1) break;
+ if (!match) break;
+ if (line.len < 7) { respond("x"); continue; }
+ if (line.len > 100) { respond("x"); continue; }
+ if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */
+ for (i = 5;i < line.len - 1;++i)
+ if ((unsigned char) (line.s[i] - '0') > 9)
+ { respond("x"); continue; }
+ if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; }
+ if (byte_equal(line.s,5,"foop/"))
+ {
+#define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \
+if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; }
+ U("intd/",0)
+ U("mess/",1)
+ respond("+");
+ }
+ else if (byte_equal(line.s,4,"todo/"))
+ {
+ U("intd/",0)
+ U("todo/",0)
+ respond("+");
+ }
+ else
+ respond("x");
+ }
+ _exit(0);
+}
diff --git a/qmail-command.8 b/qmail-command.8
new file mode 100644
index 0000000..ad46377
--- /dev/null
+++ b/qmail-command.8
@@ -0,0 +1,149 @@
+.TH qmail-command 8
+.SH NAME
+qmail-command \- user-specified mail delivery program
+.SH SYNOPSIS
+in
+.BR .qmail\fIext :
+.B |\fIcommand
+.SH DESCRIPTION
+.B qmail-local
+will, upon your request,
+feed each incoming mail message through a program of your choice.
+
+When a mail message arrives,
+.B qmail-local
+runs
+.B sh -c \fIcommand
+in your home directory.
+It makes the message available on
+.IR command 's
+standard input.
+
+.B WARNING:
+The mail message does not begin with
+.BR qmail-local 's
+usual
+.B Return-Path
+and
+.B Delivered-To
+lines.
+
+Note that
+.B qmail-local
+uses the same file descriptor for every delivery
+in your
+.B .qmail
+file, so it is not safe for
+.I command
+to fork a child that
+reads the message in the background while the parent exits.
+.SH "EXIT CODES"
+.IR command 's
+exit codes are interpreted as follows:
+0 means that the delivery was successful;
+99 means that the delivery was successful,
+but that
+.B qmail-local
+should ignore all further delivery instructions;
+100 means that the delivery failed permanently (hard error);
+111 means that the delivery failed but should be tried again
+in a little while (soft error).
+
+Currently 64, 65, 70, 76, 77, 78, and 112 are considered hard errors,
+and all other codes are considered soft errors,
+but
+.I command
+should avoid relying on this.
+.SH "ENVIRONMENT VARIABLES"
+.B qmail-local
+supplies several useful environment variables to
+.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
+is the envelope sender address.
+.B NEWSENDER
+is the forwarding envelope sender address,
+as described in
+.BR dot-qmail(5) .
+.B RECIPIENT
+is the envelope recipient address,
+.IR local@domain .
+.B USER
+is
+.IR user .
+.B HOME
+is your home directory,
+.IR homedir .
+.B HOST
+is the
+.I domain
+part of the recipient address.
+.B LOCAL
+is the
+.I local
+part.
+.B EXT
+is the
+address extension,
+.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
+is the portion of
+.B EXT
+following the first dash;
+.B EXT3
+is the portion
+following the second dash;
+.B EXT4
+is the portion
+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
+and
+.B RPLINE
+are the usual
+.B Delivered-To
+and
+.B Return-Path
+lines,
+including newlines.
+.B UFLINE
+is the UUCP-style
+.B From_
+line that
+.B qmail-local
+adds to
+.IR mbox -format
+files.
+.SH "SEE ALSO"
+dot-qmail(5),
+envelopes(5),
+qmail-local(8)
diff --git a/qmail-control.9 b/qmail-control.9
new file mode 100644
index 0000000..503ce93
--- /dev/null
+++ b/qmail-control.9
@@ -0,0 +1,78 @@
+.TH qmail-control 5
+.SH "NAME"
+qmail-control \- qmail configuration files
+.SH "INTRODUCTION"
+You can change the behavior of the
+.B qmail
+system by modifying
+.BR qmail 's
+.I control files
+in
+.BR QMAILHOME/control .
+
+.B qmail
+can survive with just one control file,
+.IR me ,
+containing the
+fully-qualified name of the current host.
+This file is used as the default for
+other hostname-related control files.
+
+Comments are allowed
+in
+.IR badmailfrom ,
+.IR locals ,
+.IR percenthack ,
+.IR qmqpservers ,
+.IR rcpthosts ,
+.IR smtproutes ,
+and
+.IR virtualdomains .
+Trailing spaces and tabs are allowed in any control file.
+
+The following table lists all control files
+other than
+.IR me .
+See the corresponding man pages for further details.
+
+.RS
+.nf
+.ta 5c 10c
+control default used by
+
+.I badmailfrom \fR(none) \fRqmail-smtpd
+.I bouncefrom \fRMAILER-DAEMON \fRqmail-send
+.I bouncehost \fIme \fRqmail-send
+.I concurrencylocal \fR10 \fRqmail-send
+.I concurrencyremote \fR20 \fRqmail-send
+.I defaultdomain \fIme \fRqmail-inject
+.I defaulthost \fIme \fRqmail-inject
+.I databytes \fR0 \fRqmail-smtpd
+.I doublebouncehost \fIme \fRqmail-send
+.I doublebounceto \fRpostmaster \fRqmail-send
+.I envnoathost \fIme \fRqmail-send
+.I helohost \fIme \fRqmail-remote
+.I idhost \fIme \fRqmail-inject
+.I localiphost \fIme \fRqmail-smtpd
+.I locals \fIme \fRqmail-send
+.I morercpthosts \fR(none) \fRqmail-smtpd
+.I percenthack \fR(none) \fRqmail-send
+.I plusdomain \fIme \fRqmail-inject
+.I qmqpservers \fR(none) \fRqmail-qmqpc
+.I queuelifetime \fR604800 \fRqmail-send
+.I rcpthosts \fR(none) \fRqmail-smtpd
+.I smtpgreeting \fIme \fRqmail-smtpd
+.I smtproutes \fR(none) \fRqmail-remote
+.I timeoutconnect \fR60 \fRqmail-remote
+.I timeoutremote \fR1200 \fRqmail-remote
+.I timeoutsmtpd \fR1200 \fRqmail-smtpd
+.I virtualdomains \fR(none) \fRqmail-send
+.fi
+.RE
+.SH "SEE ALSO"
+qmail-inject(8),
+qmail-qmqpc(8),
+qmail-remote(8),
+qmail-send(8),
+qmail-showctl(8),
+qmail-smtpd(8)
diff --git a/qmail-getpw.9 b/qmail-getpw.9
new file mode 100644
index 0000000..0f12e1c
--- /dev/null
+++ b/qmail-getpw.9
@@ -0,0 +1,114 @@
+.TH qmail-getpw 8
+.SH NAME
+qmail-getpw \- give addresses to users
+.SH SYNOPSIS
+.B qmail-getpw
+.I local
+.SH DESCRIPTION
+In
+.BR qmail ,
+each user controls a vast array of local addresses.
+.B qmail-getpw
+finds the user that controls a particular address,
+.IR local .
+It prints six pieces of information,
+each terminated by NUL:
+.IR user ;
+.IR uid ;
+.IR gid ;
+.IR homedir ;
+.IR dash ;
+and
+.IR ext .
+The user's account name is
+.IR user ;
+the user's uid and gid in decimal are
+.I uid
+and
+.IR gid ;
+the user's home directory is
+.IR homedir ;
+and messages to
+.I local
+will be handled by
+.IR homedir\fB/.qmail\fIdashext .
+
+In case of trouble,
+.B qmail-getpw
+exits nonzero without printing anything.
+
+.B WARNING:
+The operating system's
+.B getpwnam
+function, which is at the heart of
+.BR qmail-getpw ,
+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"
+.B qmail-getpw
+considers an account in
+.B /etc/passwd
+to be a user if
+(1) the account has a nonzero uid,
+(2) the account's home directory exists (and is visible to
+.BR qmail-getpw ),
+and
+(3) the account owns its home directory.
+.B qmail-getpw
+ignores account names containing uppercase letters.
+.B qmail-getpw
+also assumes that all account names are shorter than 32 characters.
+
+.B qmail-getpw
+gives each user
+control over the basic
+.I user
+address and
+all addresses of the form
+.IR user\fBBREAK\fIanything .
+When
+.I local
+is
+.IR user ,
+.I dash
+and
+.I ext
+are both empty.
+When
+.I local
+is
+.IR user\fBBREAK\fIanything ,
+.I dash
+is a hyphen and
+.I ext
+is
+.IR anything .
+.I user
+may appear in any combination of uppercase and lowercase letters
+at the front of
+.IR local .
+
+A catch-all user,
+.BR alias ,
+controls all other addresses.
+In this case
+.I ext
+is
+.I local
+and
+.I dash
+is a hyphen.
+
+You can override all of
+.BR qmail-getpw 's
+decisions with the
+.B qmail-users
+mechanism, which is reliable, highly configurable, and much faster than
+.BR qmail-getpw .
+.SH "SEE ALSO"
+qmail-users(5),
+qmail-lspawn(8)
diff --git a/qmail-getpw.c b/qmail-getpw.c
new file mode 100644
index 0000000..128c682
--- /dev/null
+++ b/qmail-getpw.c
@@ -0,0 +1,88 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "error.h"
+#include "exit.h"
+#include "byte.h"
+#include "str.h"
+#include "case.h"
+#include "fmt.h"
+#include "auto_usera.h"
+#include "auto_break.h"
+#include "qlx.h"
+
+#define GETPW_USERLEN 32
+
+char *local;
+struct passwd *pw;
+char *dash;
+char *extension;
+
+int userext()
+{
+ char username[GETPW_USERLEN];
+ struct stat st;
+
+ extension = local + str_len(local);
+ for (;;) {
+ if (extension - local < sizeof(username))
+ if (!*extension || (*extension == *auto_break)) {
+ byte_copy(username,extension - local,local);
+ username[extension - local] = 0;
+ case_lowers(username);
+ errno = 0;
+ pw = getpwnam(username);
+ if (errno == error_txtbsy) _exit(QLX_SYS);
+ if (pw)
+ if (pw->pw_uid)
+ if (stat(pw->pw_dir,&st) == 0) {
+ if (st.st_uid == pw->pw_uid) {
+ dash = "";
+ if (*extension) { ++extension; dash = "-"; }
+ return 1;
+ }
+ }
+ else
+ if (error_temp(errno)) _exit(QLX_NFS);
+ }
+ if (extension == local) return 0;
+ --extension;
+ }
+}
+
+char num[FMT_ULONG];
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ local = argv[1];
+ if (!local) _exit(100);
+
+ if (!userext()) {
+ extension = local;
+ dash = "-";
+ pw = getpwnam(auto_usera);
+ }
+
+ if (!pw) _exit(QLX_NOALIAS);
+
+ substdio_puts(subfdoutsmall,pw->pw_name);
+ substdio_put(subfdoutsmall,"",1);
+ substdio_put(subfdoutsmall,num,fmt_ulong(num,(long) pw->pw_uid));
+ substdio_put(subfdoutsmall,"",1);
+ substdio_put(subfdoutsmall,num,fmt_ulong(num,(long) pw->pw_gid));
+ substdio_put(subfdoutsmall,"",1);
+ substdio_puts(subfdoutsmall,pw->pw_dir);
+ substdio_put(subfdoutsmall,"",1);
+ substdio_puts(subfdoutsmall,dash);
+ substdio_put(subfdoutsmall,"",1);
+ substdio_puts(subfdoutsmall,extension);
+ substdio_put(subfdoutsmall,"",1);
+ substdio_flush(subfdoutsmall);
+
+ _exit(0);
+}
diff --git a/qmail-header.5 b/qmail-header.5
new file mode 100644
index 0000000..d90323a
--- /dev/null
+++ b/qmail-header.5
@@ -0,0 +1,332 @@
+.TH qmail-header 5
+.SH NAME
+qmail-header \- format of a mail message
+.SH OVERVIEW
+At the top of every mail message is a
+highly structured
+.BR header .
+Many programs expect the header to carry certain information,
+as described below.
+The main function of
+.B qmail-inject
+is to make sure that each outgoing message has an appropriate header.
+
+For more detailed information, see
+.BR http://pobox.com/~djb/proto/immhf.html .
+.SH "MESSAGE STRUCTURE"
+A message contains a series of
+.I header fields\fR,
+a blank line,
+and a
+.IR body :
+
+.EX
+ Received: (qmail-queue invoked by uid 666);
+.br
+ 30 Jul 1996 11:54:54 -0000
+.br
+ From: djb@silverton.berkeley.edu (D. J. Bernstein)
+.br
+ To: fred@silverton.berkeley.edu
+.br
+ Date: 30 Jul 1996 11:54:54 -0000
+.br
+ Subject: Go, Bears!
+.br
+
+.br
+ I've got money on this one. How about you?
+.br
+
+.br
+ ---Dan (this is the third line of the body)
+.EE
+
+Each header field has a
+.IR name ,
+a colon,
+some
+.IR contents ,
+and a newline:
+
+.EX
+ Subject: Go, Bears!
+.EE
+
+The field contents may be folded across several lines.
+Each line past the first must begin with a space or tab:
+
+.EX
+ Received: (qmail-queue invoked by uid 666);
+.br
+ 30 Jul 1996 11:54:54 -0000
+.EE
+
+The field name must not contain spaces, tabs, or colons.
+Also, an empty field name is illegal.
+.B qmail-inject
+does not allow field names with unprintable characters.
+
+Case is irrelevant in field names:
+.B subject
+and
+.B SUBJECT
+and
+.B SuBjEcT
+have the same meaning.
+.SH "ADDRESS LISTS"
+Certain fields, such as
+.BR To ,
+contain
+.I address lists\fR.
+
+An address list contains some number of
+.I addresses
+or
+.I address groups\fR,
+separated by commas:
+
+.EX
+ a@b, c@d (Somebody), A Person <e@f>,
+.br
+ random group: g@h, i@j;, k@l
+.EE
+
+An
+.I address group
+has some text, a colon, a list of addresses,
+and a semicolon:
+
+.EX
+ random group: g@h, i@j;
+.EE
+
+An address can appear in several forms.
+The most common form is
+.IR box@host .
+
+Every address must include a host name.
+If
+.B qmail-inject
+sees a lone box name
+it adds the
+.I default host name\fR.
+
+All host names should be fully qualified.
+.B qmail-inject
+appends the
+.I default domain name
+to any name without dots:
+
+.EX
+ djb@silverton -> djb@silverton.berkeley.edu
+.EE
+
+It appends the
+.I plus domain name
+to any name
+that ends with a plus sign:
+
+.EX
+ eric@mammoth.cs+ -> eric@mammoth.cs.berkeley.edu
+.EE
+
+A host name may be a dotted-decimal address:
+
+.EX
+ djb@[128.32.183.163]
+.EE
+
+RFC 822 allows mailbox names inside angle brackets
+to include
+.I source routes\fR,
+but
+.B qmail-inject
+strips all source routes out of addresses.
+.SH "SENDER ADDRESSES"
+.B qmail-inject
+looks for sender address lists in the following fields:
+.BR Sender ,
+.BR From ,
+.BR Reply-To ,
+.BR Return-Path ,
+.BR Return-Receipt-To ,
+.BR Errors-To ,
+.BR Resent-Sender ,
+.BR Resent-From ,
+.BR Resent-Reply-To .
+
+If there is no
+.B From
+field,
+.B qmail-inject
+adds a new
+.B From
+field with the name of the user invoking
+.B qmail-inject.
+
+RFC 822 requires that certain sender fields contain
+only a single address, but
+.B qmail-inject
+does not enforce this restriction.
+.SH "RECIPIENT ADDRESSES"
+.B qmail-inject
+looks for recipient address lists in the following fields:
+.BR To ,
+.BR Cc ,
+.BR Bcc ,
+.BR Apparently-To ,
+.BR Resent-To ,
+.BR Resent-Cc ,
+.BR Resent-Bcc .
+
+Every message must contain at least one
+.B To
+or
+.B Cc
+or
+.BR Bcc .
+.B qmail-inject
+deletes any
+.B Bcc
+field.
+If there is no
+.B To
+or
+.B Cc
+field,
+.B qmail-inject
+adds a line
+
+.EX
+ Cc: recipient list not shown: ;
+.EE
+
+This complies with RFC 822;
+it also works around some strange
+.B sendmail
+behavior, in case the message is passed through
+.B sendmail
+on another machine.
+.SH STAMPS
+Every message must contain a
+.B Date
+field, with the date in a strict format defined by RFC 822.
+If necessary
+.B qmail-inject
+creates a new
+.B Date
+field with the current date (in GMT).
+
+Every message should contain a
+.B Message-Id
+field.
+The field contents are a unique worldwide identifier for this message.
+If necessary
+.B qmail-inject
+creates a new
+.B Message-Id
+field.
+
+Another important field is
+.BR Received .
+Every time the message is sent from one system to another,
+a new
+.B Received
+field is added to the top of the message.
+.B qmail-inject
+does not create any
+.B Received
+fields.
+.SH "RESENT MESSAGES"
+A message is
+.I resent
+if it contains any of the following fields:
+.BR Resent-Sender ,
+.BR Resent-From ,
+.BR Resent-Reply-To ,
+.BR Resent-To ,
+.BR Resent-Cc ,
+.BR Resent-Bcc ,
+.BR Resent-Date ,
+.BR Resent-Message-ID .
+
+If a message is resent,
+.B qmail-inject
+changes its behavior as follows.
+
+It deletes any
+.B Resent-Bcc
+field (as well as any
+.B Bcc
+field);
+if there are no
+.B Resent-To
+or
+.B Resent-Cc
+fields,
+.B qmail-inject
+adds an appropriate
+.B Resent-Cc
+line.
+It does
+.I not
+add a
+.B Cc
+line,
+even if neither
+.B To
+nor
+.B Cc
+is present.
+
+If there is no
+.B Resent-From
+field,
+.B qmail-inject
+adds a new
+.B Resent-From
+field.
+It does
+.I not
+add a new
+.B From
+field.
+
+.B qmail-inject
+adds
+.B Resent-Date
+if one is not already present;
+same for
+.BR Resent-Message-Id .
+It does
+.I not
+add new
+.B Date
+or
+.B Message-Id
+fields.
+.SH "OTHER FEATURES"
+Addresses are separated by commas, not spaces.
+When
+.B qmail-inject
+sees an illegal space,
+it inserts a comma:
+
+.EX
+ djb fred -> djb, fred
+.EE
+
+.B qmail-inject
+removes all
+.B Return-Path
+header fields.
+
+.B qmail-inject
+also removes any
+.B Content-Length
+fields.
+.SH "SEE ALSO"
+addresses(5),
+envelopes(5),
+qmail-inject(8)
diff --git a/qmail-inject.8 b/qmail-inject.8
new file mode 100644
index 0000000..59e11a7
--- /dev/null
+++ b/qmail-inject.8
@@ -0,0 +1,309 @@
+.TH qmail-inject 8
+.SH NAME
+qmail-inject \- preprocess and send a mail message
+.SH SYNOPSIS
+.B qmail-inject
+[
+.B \-nNaAhH
+] [
+.B \-f\fIsender
+] [
+.I recip ...
+]
+.SH DESCRIPTION
+.B qmail-inject
+reads a mail message from its standard input,
+adds appropriate information to the message header,
+and invokes
+.B qmail-queue
+to send the message
+to one or more recipients.
+
+See
+.B qmail-header(5)
+for information on how
+.B qmail-inject
+rewrites header fields.
+
+.B qmail-inject
+normally exits 0.
+It exits 100 if it was invoked improperly
+or if there is a severe syntax error in the message.
+It exits 111 for temporary errors.
+.SH "ENVIRONMENT VARIABLES"
+For the convenience of users who do not run
+.B qmail-inject
+directly,
+.B qmail-inject
+takes many options through environment variables.
+
+The user name in the
+.B From
+header field is set by
+.BR QMAILUSER ,
+.BR MAILUSER ,
+.BR USER ,
+or
+.BR LOGNAME ,
+whichever comes first.
+
+The host name is normally set by the
+.I defaulthost
+control
+but can be overridden with
+.B QMAILHOST
+or
+.BR MAILHOST .
+
+The personal name is
+.BR QMAILNAME ,
+.BR MAILNAME ,
+or
+.BR NAME .
+
+The default envelope sender address is the same as the
+default
+.B From
+address,
+but it can be overridden with
+.B QMAILSUSER
+and
+.BR QMAILSHOST .
+It may also be modified by the
+.B r
+and
+.B m
+letters described below.
+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
+.B QMAILINJECT
+environment variable
+can contain any of the following letters:
+.TP
+.B c
+Use address-comment style for the
+.B From
+field.
+Normally
+.B qmail-inject
+uses name-address style.
+.TP
+.B s
+Do not look at any incoming
+.B Return-Path
+field.
+Normally, if
+.B Return-Path
+is supplied, it sets the envelope sender address,
+overriding all environment variables.
+.B Return-Path
+is deleted in any case.
+.TP
+.B f
+Delete any incoming
+.B From
+field.
+Normally, if
+.B From
+is supplied, it overrides the usual
+.B From
+field created by
+.BR qmail-inject .
+.TP
+.B i
+Delete any incoming
+.B Message-ID
+field.
+Normally, if
+.B Message-ID
+is supplied, it overrides the usual
+.B Message-ID
+field created by
+.BR qmail-inject .
+.TP
+.B r
+Use a per-recipient VERP.
+.B qmail-inject
+will append each recipient address to the envelope sender
+of the copy going to that recipient.
+.TP
+.B m
+Use a per-message VERP.
+.B qmail-inject
+will append the current date and process ID to the envelope sender.
+.SH OPTIONS
+.TP
+.B \-a
+Send the message to all addresses given as
+.I recip
+arguments;
+do not use header recipient addresses.
+.TP
+.B \-h
+Send the message to all header recipient addresses.
+For non-forwarded messages, this means
+the addresses listed under
+.BR To ,
+.BR Cc ,
+.BR Bcc ,
+.BR Apparently-To .
+For forwarded messages, this means
+the addresses listed under
+.BR Resent-To ,
+.BR Resent-Cc ,
+.BR Resent-Bcc .
+Do not use any
+.I recip
+arguments.
+.TP
+.B \-A
+(Default.)
+Send the message to all addresses given as
+.I recip
+arguments.
+If no
+.I recip
+arguments are supplied,
+send the message to all header recipient addresses.
+.TP
+.B \-H
+Send the message to all header recipient addresses,
+and to all addresses given as
+.I recip
+arguments.
+.TP
+.B \-f\fIsender
+Pass
+.I sender
+to
+.B qmail-queue
+as the envelope sender address.
+This overrides
+.B Return-Path
+and all environment variables.
+.TP
+.B \-N
+(Default.)
+Feed the resulting message to
+.BR qmail-queue .
+.TP
+.B \-n
+Print the message rather than feeding it to
+.BR qmail-queue .
+.SH "CONTROL FILES"
+.TP 5
+.I defaultdomain
+Default domain name.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR defaultdomain ,
+which is probably not what you want.
+.B qmail-inject
+adds this name to any host name without dots,
+including
+.I defaulthost
+if
+.I defaulthost
+does not have dots.
+(Exception: see
+.IR plusdomain .)
+
+The
+.B QMAILDEFAULTDOMAIN
+environment variable
+overrides
+.IR defaultdomain .
+.TP 5
+.I defaulthost
+Default host name.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR defaulthost ,
+which is probably not what you want.
+.B qmail-inject
+adds this name to any address without a host name.
+.I defaulthost
+need not be the current host's name.
+For example,
+you may prefer that outgoing mail show
+just your domain name.
+
+The
+.B QMAILDEFAULTHOST
+environment variable overrides
+.IR defaulthost .
+.TP 5
+.I idhost
+Host name for Message-IDs.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR idhost ,
+which is certainly not what you want.
+.I idhost
+need not be the current host's name.
+For example, you may prefer to use fake
+host names in Message-IDs.
+However,
+.I idhost
+must be a fully-qualified name within your domain,
+and each host in your domain should use a different
+.IR idhost .
+
+The
+.B QMAILIDHOST
+environment variable overrides
+.IR idhost .
+.TP 5
+.I plusdomain
+Plus domain name.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR plusdomain ,
+which is probably not what you want.
+.B qmail-inject
+adds this name to any host name that ends with a plus sign,
+including
+.I defaulthost
+if
+.I defaulthost
+ends with a plus sign.
+If a host name does not have dots but ends with a plus sign,
+.B qmail-inject
+uses
+.IR plusdomain ,
+not
+.IR defaultdomain .
+
+The
+.B QMAILPLUSDOMAIN
+environment variable overrides
+.IR plusdomain .
+.SH "SEE ALSO"
+addresses(5),
+qmail-control(5),
+qmail-header(5),
+qmail-queue(8)
diff --git a/qmail-inject.c b/qmail-inject.c
new file mode 100644
index 0000000..753c18a
--- /dev/null
+++ b/qmail-inject.c
@@ -0,0 +1,773 @@
+#include "sig.h"
+#include "substdio.h"
+#include "stralloc.h"
+#include "subfd.h"
+#include "sgetopt.h"
+#include "getln.h"
+#include "alloc.h"
+#include "str.h"
+#include "fmt.h"
+#include "hfield.h"
+#include "token822.h"
+#include "control.h"
+#include "env.h"
+#include "gen_alloc.h"
+#include "gen_allocdefs.h"
+#include "error.h"
+#include "qmail.h"
+#include "now.h"
+#include "exit.h"
+#include "quote.h"
+#include "headerbody.h"
+#include "auto_qmail.h"
+#include "newfield.h"
+#include "constmap.h"
+
+#define LINELEN 80
+
+datetime_sec starttime;
+
+char *qmopts;
+int flagdeletesender = 0;
+int flagdeletefrom = 0;
+int flagdeletemessid = 0;
+int flagnamecomment = 0;
+int flaghackmess = 0;
+int flaghackrecip = 0;
+char *mailhost;
+char *mailuser;
+int mailusertokentype;
+char *mailrhost;
+char *mailruser;
+
+stralloc control_idhost = {0};
+stralloc control_defaultdomain = {0};
+stralloc control_defaulthost = {0};
+stralloc control_plusdomain = {0};
+
+stralloc sender = {0};
+stralloc envsbuf = {0};
+token822_alloc envs = {0};
+int flagrh;
+
+int flagqueue;
+struct qmail qqt;
+
+void put(s,len) char *s; int len;
+{ if (flagqueue) qmail_put(&qqt,s,len); else substdio_put(subfdout,s,len); }
+void puts(s) char *s; { put(s,str_len(s)); }
+
+void perm() { _exit(100); }
+void temp() { _exit(111); }
+void die_nomem() {
+ substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); }
+void die_invalid(sa) stralloc *sa; {
+ substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: ");
+ substdio_putflush(subfderr,sa->s,sa->len); perm(); }
+void die_qqt() {
+ substdio_putsflush(subfderr,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); }
+void die_chdir() {
+ substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); }
+void die_read() {
+ if (errno == error_nomem) die_nomem();
+ substdio_putsflush(subfderr,"qmail-inject: fatal: read error\n"); temp(); }
+void doordie(sa,r) stralloc *sa; int r; {
+ if (r == 1) return; if (r == -1) die_nomem();
+ substdio_putsflush(subfderr,"qmail-inject: fatal: unable to parse this line:\n");
+ substdio_putflush(subfderr,sa->s,sa->len); perm(); }
+
+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};
+saa hrlist = {0};
+saa tocclist = {0};
+saa hrrlist = {0};
+saa reciplist = {0};
+int flagresent;
+
+void exitnicely()
+{
+ char *qqx;
+
+ if (!flagqueue) substdio_flush(subfdout);
+
+ if (flagqueue)
+ {
+ int i;
+
+ if (!stralloc_0(&sender)) die_nomem();
+ qmail_from(&qqt,sender.s);
+
+ for (i = 0;i < reciplist.len;++i)
+ {
+ if (!stralloc_0(&reciplist.sa[i])) die_nomem();
+ qmail_to(&qqt,reciplist.sa[i].s);
+ }
+ if (flagrh)
+ if (flagresent)
+ for (i = 0;i < hrrlist.len;++i)
+ {
+ if (!stralloc_0(&hrrlist.sa[i])) die_nomem();
+ qmail_to(&qqt,hrrlist.sa[i].s);
+ }
+ else
+ for (i = 0;i < hrlist.len;++i)
+ {
+ if (!stralloc_0(&hrlist.sa[i])) die_nomem();
+ qmail_to(&qqt,hrlist.sa[i].s);
+ }
+
+ qqx = qmail_close(&qqt);
+ if (*qqx)
+ if (*qqx == 'D') {
+ substdio_puts(subfderr,"qmail-inject: fatal: ");
+ substdio_puts(subfderr,qqx + 1);
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+ perm();
+ }
+ else {
+ substdio_puts(subfderr,"qmail-inject: fatal: ");
+ substdio_puts(subfderr,qqx + 1);
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+ temp();
+ }
+ }
+
+ _exit(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)
+ put(savedh.sa[i].s,savedh.sa[i].len);
+}
+
+stralloc defaultdomainbuf = {0};
+token822_alloc defaultdomain = {0};
+stralloc defaulthostbuf = {0};
+token822_alloc defaulthost = {0};
+stralloc plusdomainbuf = {0};
+token822_alloc plusdomain = {0};
+
+void rwroute(addr)
+token822_alloc *addr;
+{
+ if (addr->t[addr->len - 1].type == TOKEN822_AT)
+ while (addr->len)
+ if (addr->t[--addr->len].type == TOKEN822_COLON)
+ return;
+}
+
+void rwextraat(addr)
+token822_alloc *addr;
+{
+ int i;
+ if (addr->t[0].type == TOKEN822_AT)
+ {
+ --addr->len;
+ for (i = 0;i < addr->len;++i)
+ addr->t[i] = addr->t[i + 1];
+ }
+}
+
+void rwextradot(addr)
+token822_alloc *addr;
+{
+ int i;
+ if (addr->t[0].type == TOKEN822_DOT)
+ {
+ --addr->len;
+ for (i = 0;i < addr->len;++i)
+ addr->t[i] = addr->t[i + 1];
+ }
+}
+
+void rwnoat(addr)
+token822_alloc *addr;
+{
+ int i;
+ int shift;
+
+ for (i = 0;i < addr->len;++i)
+ if (addr->t[i].type == TOKEN822_AT)
+ return;
+ shift = defaulthost.len;
+ if (!token822_readyplus(addr,shift)) die_nomem();
+ for (i = addr->len - 1;i >= 0;--i)
+ addr->t[i + shift] = addr->t[i];
+ addr->len += shift;
+ for (i = 0;i < shift;++i)
+ addr->t[i] = defaulthost.t[shift - 1 - i];
+}
+
+void rwnodot(addr)
+token822_alloc *addr;
+{
+ int i;
+ int shift;
+ for (i = 0;i < addr->len;++i)
+ {
+ if (addr->t[i].type == TOKEN822_DOT)
+ return;
+ if (addr->t[i].type == TOKEN822_AT)
+ break;
+ }
+ for (i = 0;i < addr->len;++i)
+ {
+ if (addr->t[i].type == TOKEN822_LITERAL)
+ return;
+ if (addr->t[i].type == TOKEN822_AT)
+ break;
+ }
+ shift = defaultdomain.len;
+ if (!token822_readyplus(addr,shift)) die_nomem();
+ for (i = addr->len - 1;i >= 0;--i)
+ addr->t[i + shift] = addr->t[i];
+ addr->len += shift;
+ for (i = 0;i < shift;++i)
+ addr->t[i] = defaultdomain.t[shift - 1 - i];
+}
+
+void rwplus(addr)
+token822_alloc *addr;
+{
+ int i;
+ int shift;
+
+ if (addr->t[0].type != TOKEN822_ATOM) return;
+ if (!addr->t[0].slen) return;
+ if (addr->t[0].s[addr->t[0].slen - 1] != '+') return;
+
+ --addr->t[0].slen; /* remove + */
+
+ shift = plusdomain.len;
+ if (!token822_readyplus(addr,shift)) die_nomem();
+ for (i = addr->len - 1;i >= 0;--i)
+ addr->t[i + shift] = addr->t[i];
+ addr->len += shift;
+ for (i = 0;i < shift;++i)
+ addr->t[i] = plusdomain.t[shift - 1 - i];
+}
+
+void rwgeneric(addr)
+token822_alloc *addr;
+{
+ if (!addr->len) return; /* don't rewrite <> */
+ if (addr->len >= 2)
+ if (addr->t[1].type == TOKEN822_AT)
+ if (addr->t[0].type == TOKEN822_LITERAL)
+ if (!addr->t[0].slen) /* don't rewrite <foo@[]> */
+ return;
+ rwroute(addr);
+ if (!addr->len) return; /* <@foo:> -> <> */
+ rwextradot(addr);
+ if (!addr->len) return; /* <.> -> <> */
+ rwextraat(addr);
+ if (!addr->len) return; /* <@> -> <> */
+ rwnoat(addr);
+ rwplus(addr);
+ rwnodot(addr);
+}
+
+int setreturn(addr)
+token822_alloc *addr;
+{
+ if (!sender.s)
+ {
+ token822_reverse(addr);
+ if (token822_unquote(&sender,addr) != 1) die_nomem();
+ if (flaghackrecip)
+ if (!stralloc_cats(&sender,"-@[]")) die_nomem();
+ token822_reverse(addr);
+ }
+ return 1;
+}
+
+int rwreturn(addr)
+token822_alloc *addr;
+{
+ rwgeneric(addr);
+ setreturn(addr);
+ return 1;
+}
+
+int rwsender(addr)
+token822_alloc *addr;
+{
+ rwgeneric(addr);
+ return 1;
+}
+
+void rwappend(addr,xl)
+token822_alloc *addr;
+saa *xl;
+{
+ token822_reverse(addr);
+ if (!saa_readyplus(xl,1)) die_nomem();
+ xl->sa[xl->len] = sauninit;
+ if (token822_unquote(&xl->sa[xl->len],addr) != 1) die_nomem();
+ ++xl->len;
+ token822_reverse(addr);
+}
+
+int rwhrr(addr) token822_alloc *addr;
+{ rwgeneric(addr); rwappend(addr,&hrrlist); return 1; }
+int rwhr(addr) token822_alloc *addr;
+{ 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];
+stralloc hfbuf = {0};
+token822_alloc hfin = {0};
+token822_alloc hfrewrite = {0};
+token822_alloc hfaddr = {0};
+
+void doheaderfield(h)
+stralloc *h;
+{
+ int htype;
+ int (*rw)() = 0;
+
+ htype = hfield_known(h->s,h->len);
+ if (flagdeletefrom) if (htype == H_FROM) return;
+ if (flagdeletemessid) if (htype == H_MESSAGEID) return;
+ if (flagdeletesender) if (htype == H_RETURNPATH) return;
+
+ if (htype)
+ htypeseen[htype] = 1;
+ else
+ if (!hfield_valid(h->s,h->len))
+ die_invalid(h);
+
+ switch(htype) {
+ case H_TO: case H_CC:
+ rw = rwtocc; break;
+ case H_BCC: case H_APPARENTLYTO:
+ rw = rwhr; break;
+ case H_R_TO: case H_R_CC: case H_R_BCC:
+ rw = rwhrr; break;
+ case H_RETURNPATH:
+ rw = rwreturn; break;
+ case H_SENDER: case H_FROM: case H_REPLYTO:
+ case H_RETURNRECEIPTTO: case H_ERRORSTO:
+ case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO:
+ rw = rwsender; break;
+ }
+
+ if (rw) {
+ doordie(h,token822_parse(&hfin,h,&hfbuf));
+ doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rw));
+ if (token822_unparse(h,&hfrewrite,LINELEN) != 1)
+ die_nomem();
+ }
+
+ if (htype == H_BCC) return;
+ if (htype == H_R_BCC) return;
+ if (htype == H_RETURNPATH) return;
+ if (htype == H_CONTENTLENGTH) return; /* some things are just too stupid */
+ savedh_append(h);
+}
+
+void dobody(h)
+stralloc *h;
+{
+ put(h->s,h->len);
+}
+
+stralloc torecip = {0};
+token822_alloc tr = {0};
+
+void dorecip(s)
+char *s;
+{
+ if (!quote2(&torecip,s)) die_nomem();
+ switch(token822_parse(&tr,&torecip,&hfbuf))
+ {
+ case -1: die_nomem();
+ case 0:
+ substdio_puts(subfderr,"qmail-inject: fatal: unable to parse address: ");
+ substdio_puts(subfderr,s);
+ substdio_putsflush(subfderr,"\n");
+ perm();
+ }
+ token822_reverse(&tr);
+ rwgeneric(&tr);
+ rwappend(&tr,&reciplist);
+}
+
+stralloc defaultfrom = {0};
+token822_alloc df = {0};
+
+void defaultfrommake()
+{
+ char *fullname;
+ fullname = env_get("QMAILNAME");
+ if (!fullname) fullname = env_get("MAILNAME");
+ if (!fullname) fullname = env_get("NAME");
+ if (!token822_ready(&df,20)) die_nomem();
+ df.len = 0;
+ df.t[df.len].type = TOKEN822_ATOM;
+ df.t[df.len].s = "From";
+ df.t[df.len].slen = 4;
+ ++df.len;
+ df.t[df.len].type = TOKEN822_COLON;
+ ++df.len;
+ if (fullname && !flagnamecomment)
+ {
+ df.t[df.len].type = TOKEN822_QUOTE;
+ df.t[df.len].s = fullname;
+ df.t[df.len].slen = str_len(fullname);
+ ++df.len;
+ df.t[df.len].type = TOKEN822_LEFT;
+ ++df.len;
+ }
+ df.t[df.len].type = mailusertokentype;
+ df.t[df.len].s = mailuser;
+ df.t[df.len].slen = str_len(mailuser);
+ ++df.len;
+ if (mailhost)
+ {
+ df.t[df.len].type = TOKEN822_AT;
+ ++df.len;
+ df.t[df.len].type = TOKEN822_ATOM;
+ df.t[df.len].s = mailhost;
+ df.t[df.len].slen = str_len(mailhost);
+ ++df.len;
+ }
+ if (fullname && !flagnamecomment)
+ {
+ df.t[df.len].type = TOKEN822_RIGHT;
+ ++df.len;
+ }
+ if (fullname && flagnamecomment)
+ {
+ df.t[df.len].type = TOKEN822_COMMENT;
+ df.t[df.len].s = fullname;
+ df.t[df.len].slen = str_len(fullname);
+ ++df.len;
+ }
+ if (token822_unparse(&defaultfrom,&df,LINELEN) != 1) die_nomem();
+ doordie(&defaultfrom,token822_parse(&df,&defaultfrom,&hfbuf));
+ doordie(&defaultfrom,token822_addrlist(&hfrewrite,&hfaddr,&df,rwsender));
+ if (token822_unparse(&defaultfrom,&hfrewrite,LINELEN) != 1) die_nomem();
+}
+
+stralloc defaultreturnpath = {0};
+token822_alloc drp = {0};
+stralloc hackedruser = {0};
+char strnum[FMT_ULONG];
+
+void dodefaultreturnpath()
+{
+ if (!stralloc_copys(&hackedruser,mailruser)) die_nomem();
+ if (flaghackmess)
+ {
+ if (!stralloc_cats(&hackedruser,"-")) die_nomem();
+ if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) starttime))) die_nomem();
+ if (!stralloc_cats(&hackedruser,".")) die_nomem();
+ if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+ }
+ if (flaghackrecip)
+ if (!stralloc_cats(&hackedruser,"-")) die_nomem();
+ if (!token822_ready(&drp,10)) die_nomem();
+ drp.len = 0;
+ drp.t[drp.len].type = TOKEN822_ATOM;
+ drp.t[drp.len].s = "Return-Path";
+ drp.t[drp.len].slen = 11;
+ ++drp.len;
+ drp.t[drp.len].type = TOKEN822_COLON;
+ ++drp.len;
+ drp.t[drp.len].type = TOKEN822_QUOTE;
+ drp.t[drp.len].s = hackedruser.s;
+ drp.t[drp.len].slen = hackedruser.len;
+ ++drp.len;
+ if (mailrhost)
+ {
+ drp.t[drp.len].type = TOKEN822_AT;
+ ++drp.len;
+ drp.t[drp.len].type = TOKEN822_ATOM;
+ drp.t[drp.len].s = mailrhost;
+ drp.t[drp.len].slen = str_len(mailrhost);
+ ++drp.len;
+ }
+ if (token822_unparse(&defaultreturnpath,&drp,LINELEN) != 1) die_nomem();
+ doordie(&defaultreturnpath,token822_parse(&drp,&defaultreturnpath,&hfbuf));
+ doordie(&defaultreturnpath
+ ,token822_addrlist(&hfrewrite,&hfaddr,&drp,rwreturn));
+ 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()
+{
+ flagresent =
+ htypeseen[H_R_SENDER] || htypeseen[H_R_FROM] || htypeseen[H_R_REPLYTO]
+ || htypeseen[H_R_TO] || htypeseen[H_R_CC] || htypeseen[H_R_BCC]
+ || htypeseen[H_R_DATE] || htypeseen[H_R_MESSAGEID];
+
+ if (!sender.s)
+ dodefaultreturnpath();
+
+ if (!flagqueue)
+ {
+ static stralloc sa = {0};
+ static stralloc sa2 = {0};
+
+ if (!stralloc_copy(&sa,&sender)) die_nomem();
+ if (!stralloc_0(&sa)) die_nomem();
+ if (!quote2(&sa2,sa.s)) die_nomem();
+
+ puts("Return-Path: <");
+ put(sa2.s,sa2.len);
+ puts(">\n");
+ }
+
+ /* could check at this point whether there are any recipients */
+ if (flagqueue)
+ if (qmail_open(&qqt) == -1) die_qqt();
+
+ if (flagresent)
+ {
+ if (!htypeseen[H_R_DATE])
+ {
+ if (!newfield_datemake(starttime)) die_nomem();
+ puts("Resent-");
+ put(newfield_date.s,newfield_date.len);
+ }
+ if (!htypeseen[H_R_MESSAGEID])
+ {
+ if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem();
+ puts("Resent-");
+ put(newfield_msgid.s,newfield_msgid.len);
+ }
+ if (!htypeseen[H_R_FROM])
+ {
+ defaultfrommake();
+ puts("Resent-");
+ put(defaultfrom.s,defaultfrom.len);
+ }
+ if (!htypeseen[H_R_TO] && !htypeseen[H_R_CC])
+ puts("Resent-Cc: recipient list not shown: ;\n");
+ }
+ else
+ {
+ if (!htypeseen[H_DATE])
+ {
+ if (!newfield_datemake(starttime)) die_nomem();
+ put(newfield_date.s,newfield_date.len);
+ }
+ if (!htypeseen[H_MESSAGEID])
+ {
+ if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem();
+ put(newfield_msgid.s,newfield_msgid.len);
+ }
+ if (!htypeseen[H_FROM])
+ {
+ defaultfrommake();
+ put(defaultfrom.s,defaultfrom.len);
+ }
+ if (!htypeseen[H_TO] && !htypeseen[H_CC])
+ puts("Cc: recipient list not shown: ;\n");
+ finishmft();
+ }
+
+ savedh_print();
+}
+
+void getcontrols()
+{
+ static stralloc sa = {0};
+ char *x;
+
+ mft_init();
+
+ if (chdir(auto_qmail) == -1) die_chdir();
+ if (control_init() == -1) die_read();
+
+ if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1)
+ die_read();
+ x = env_get("QMAILDEFAULTDOMAIN");
+ if (x) if (!stralloc_copys(&control_defaultdomain,x)) die_nomem();
+ if (!stralloc_copys(&sa,".")) die_nomem();
+ if (!stralloc_cat(&sa,&control_defaultdomain)) die_nomem();
+ doordie(&sa,token822_parse(&defaultdomain,&sa,&defaultdomainbuf));
+
+ if (control_rldef(&control_defaulthost,"control/defaulthost",1,"defaulthost") != 1)
+ die_read();
+ x = env_get("QMAILDEFAULTHOST");
+ if (x) if (!stralloc_copys(&control_defaulthost,x)) die_nomem();
+ if (!stralloc_copys(&sa,"@")) die_nomem();
+ if (!stralloc_cat(&sa,&control_defaulthost)) die_nomem();
+ doordie(&sa,token822_parse(&defaulthost,&sa,&defaulthostbuf));
+
+ if (control_rldef(&control_plusdomain,"control/plusdomain",1,"plusdomain") != 1)
+ die_read();
+ x = env_get("QMAILPLUSDOMAIN");
+ if (x) if (!stralloc_copys(&control_plusdomain,x)) die_nomem();
+ if (!stralloc_copys(&sa,".")) die_nomem();
+ if (!stralloc_cat(&sa,&control_plusdomain)) die_nomem();
+ doordie(&sa,token822_parse(&plusdomain,&sa,&plusdomainbuf));
+
+ if (control_rldef(&control_idhost,"control/idhost",1,"idhost") != 1)
+ die_read();
+ x = env_get("QMAILIDHOST");
+ if (x) if (!stralloc_copys(&control_idhost,x)) die_nomem();
+}
+
+#define RECIP_DEFAULT 1
+#define RECIP_ARGS 2
+#define RECIP_HEADER 3
+#define RECIP_AH 4
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int i;
+ int opt;
+ int recipstrategy;
+
+ sig_pipeignore();
+
+ starttime = now();
+
+ qmopts = env_get("QMAILINJECT");
+ if (qmopts)
+ while (*qmopts)
+ switch(*qmopts++)
+ {
+ case 'c': flagnamecomment = 1; break;
+ case 's': flagdeletesender = 1; break;
+ case 'f': flagdeletefrom = 1; break;
+ case 'i': flagdeletemessid = 1; break;
+ case 'r': flaghackrecip = 1; break;
+ case 'm': flaghackmess = 1; break;
+ }
+
+ mailhost = env_get("QMAILHOST");
+ if (!mailhost) mailhost = env_get("MAILHOST");
+ mailrhost = env_get("QMAILSHOST");
+ if (!mailrhost) mailrhost = mailhost;
+
+ mailuser = env_get("QMAILUSER");
+ if (!mailuser) mailuser = env_get("MAILUSER");
+ if (!mailuser) mailuser = env_get("USER");
+ if (!mailuser) mailuser = env_get("LOGNAME");
+ if (!mailuser) mailuser = "anonymous";
+ mailusertokentype = TOKEN822_ATOM;
+ if (quote_need(mailuser,str_len(mailuser))) mailusertokentype = TOKEN822_QUOTE;
+ mailruser = env_get("QMAILSUSER");
+ if (!mailruser) mailruser = mailuser;
+
+ for (i = 0;i < H_NUM;++i) htypeseen[i] = 0;
+
+ recipstrategy = RECIP_DEFAULT;
+ flagqueue = 1;
+
+ getcontrols();
+
+ if (!saa_readyplus(&hrlist,1)) die_nomem();
+ if (!saa_readyplus(&tocclist,1)) die_nomem();
+ if (!saa_readyplus(&hrrlist,1)) die_nomem();
+ if (!saa_readyplus(&reciplist,1)) die_nomem();
+
+ while ((opt = getopt(argc,argv,"aAhHnNf:")) != opteof)
+ switch(opt)
+ {
+ case 'a': recipstrategy = RECIP_ARGS; break;
+ case 'A': recipstrategy = RECIP_DEFAULT; break;
+ case 'h': recipstrategy = RECIP_HEADER; break;
+ case 'H': recipstrategy = RECIP_AH; break;
+ case 'n': flagqueue = 0; break;
+ case 'N': flagqueue = 1; break;
+ case 'f':
+ if (!quote2(&sender,optarg)) die_nomem();
+ doordie(&sender,token822_parse(&envs,&sender,&envsbuf));
+ token822_reverse(&envs);
+ rwgeneric(&envs);
+ token822_reverse(&envs);
+ if (token822_unquote(&sender,&envs) != 1) die_nomem();
+ break;
+ case '?':
+ default:
+ perm();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (recipstrategy == RECIP_DEFAULT)
+ recipstrategy = (*argv ? RECIP_ARGS : RECIP_HEADER);
+
+ if (recipstrategy != RECIP_HEADER)
+ while (*argv)
+ dorecip(*argv++);
+
+ flagrh = (recipstrategy != RECIP_ARGS);
+
+ if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1)
+ die_read();
+ exitnicely();
+}
diff --git a/qmail-limits.9 b/qmail-limits.9
new file mode 100644
index 0000000..dca5426
--- /dev/null
+++ b/qmail-limits.9
@@ -0,0 +1,30 @@
+.TH qmail-limits 7
+.SH "NAME"
+qmail-limits \- artificial limits in the qmail system
+.SH "DESCRIPTION"
+The
+.B qmail
+system is able to handle messages of any size,
+addresses of any size, mailing lists of any size, and so on,
+except as limited by the available memory and disk space.
+
+However, it imposes certain artificial limits:
+.TP 5
+1.
+.B qmail-lspawn
+silently limits the number of simultaneous local deliveries to SPAWN.
+.B qmail-rspawn
+silently limits the number of simultaneous remote deliveries to SPAWN.
+.TP 5
+2.
+.B qmail-queue
+rejects any message with an envelope address longer than 1000 characters.
+.TP 5
+3.
+.B qmail-lspawn
+truncates any overly long error report from a delivery program.
+It appends a note saying that it did so.
+.SH "SEE ALSO"
+qmail-lspawn(8),
+qmail-queue(8),
+qmail-rspawn(8)
diff --git a/qmail-local.8 b/qmail-local.8
new file mode 100644
index 0000000..c047697
--- /dev/null
+++ b/qmail-local.8
@@ -0,0 +1,99 @@
+.TH qmail-local 8
+.SH NAME
+qmail-local \- deliver or forward a mail message
+.SH SYNOPSIS
+.B qmail-local
+[
+.B \-nN
+]
+.I user
+.I homedir
+.I local
+.I dash
+.I ext
+.I domain
+.I sender
+.I defaultdelivery
+.SH DESCRIPTION
+.B qmail-local
+reads a mail message
+and delivers it to
+.I user
+by the procedure described in
+.BR dot-qmail(5) .
+
+The message's envelope recipient is
+.IR local@domain .
+.B qmail-local
+records
+.I local@domain
+in a new
+.B Delivered-To
+header field.
+If exactly the same
+.B Delivered-To: \fIlocal@domain
+already appears in the header,
+.B qmail-local
+bounces the message,
+to prevent mail forwarding loops.
+
+The message's envelope sender is
+.IR sender .
+.B qmail-local
+records
+.I sender
+in a new
+.B Return-Path
+header field.
+
+.I homedir
+is the user's home directory.
+It must be an absolute directory name.
+
+.I dash
+and
+.I ext
+identify the
+.B .qmail\fIdashext
+file used by
+.BR qmail-local ;
+see
+.BR dot-qmail(5) .
+Normally
+.I dash
+is either empty or a lone hyphen.
+If it is empty,
+.B qmail-local
+treats a nonexistent
+.B .qmail\fIext
+the same way as an empty
+.BR .qmail\fIext :
+namely, following the delivery instructions in
+.IR defaultdelivery .
+
+The standard input for
+.B qmail-local
+must be a seekable file,
+so that
+.B qmail-local
+can read it more than once.
+.SH "OPTIONS"
+.TP
+.B \-n
+Instead of reading and delivering the message,
+print a description of the delivery instructions.
+.TP
+.B \-N
+(Default.) Read and deliver the message.
+.SH "EXIT CODES"
+0 if the delivery is completely successful;
+nonzero if any delivery instruction failed.
+Exit code 111
+indicates temporary failure.
+.SH "SEE ALSO"
+dot-qmail(5),
+envelopes(5),
+qmail-command(8),
+qmail-queue(8),
+qmail-send(8),
+qmail-lspawn(8)
diff --git a/qmail-local.c b/qmail-local.c
new file mode 100644
index 0000000..6fec288
--- /dev/null
+++ b/qmail-local.c
@@ -0,0 +1,698 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "readwrite.h"
+#include "sig.h"
+#include "env.h"
+#include "byte.h"
+#include "exit.h"
+#include "fork.h"
+#include "open.h"
+#include "wait.h"
+#include "lock.h"
+#include "seek.h"
+#include "substdio.h"
+#include "getln.h"
+#include "strerr.h"
+#include "subfd.h"
+#include "sgetopt.h"
+#include "alloc.h"
+#include "error.h"
+#include "stralloc.h"
+#include "fmt.h"
+#include "str.h"
+#include "now.h"
+#include "case.h"
+#include "quote.h"
+#include "qmail.h"
+#include "slurpclose.h"
+#include "myctime.h"
+#include "gfrom.h"
+#include "auto_patrn.h"
+
+void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
+
+void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); }
+void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
+void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
+void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); }
+void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno),". (#4.3.0)"); }
+void temp_slowlock()
+{ strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
+void temp_qmail(fn) char *fn;
+{ strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
+
+int flagdoit;
+int flag99;
+
+char *user;
+char *homedir;
+char *local;
+char *dash;
+char *ext;
+char *host;
+char *sender;
+char *aliasempty;
+
+stralloc safeext = {0};
+stralloc ufline = {0};
+stralloc rpline = {0};
+stralloc envrecip = {0};
+stralloc dtline = {0};
+stralloc qme = {0};
+stralloc ueo = {0};
+stralloc cmds = {0};
+stralloc messline = {0};
+stralloc foo = {0};
+
+char buf[1024];
+char outbuf[1024];
+
+/* child process */
+
+char fntmptph[80 + FMT_ULONG * 2];
+char fnnewtph[80 + FMT_ULONG * 2];
+void tryunlinktmp() { unlink(fntmptph); }
+void sigalrm() { tryunlinktmp(); _exit(3); }
+
+void maildir_child(dir)
+char *dir;
+{
+ unsigned long pid;
+ unsigned long time;
+ char host[64];
+ char *s;
+ int loop;
+ struct stat st;
+ int fd;
+ substdio ss;
+ substdio ssout;
+
+ sig_alarmcatch(sigalrm);
+ if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
+ pid = getpid();
+ host[0] = 0;
+ gethostname(host,sizeof(host));
+ for (loop = 0;;++loop)
+ {
+ time = now();
+ s = fntmptph;
+ s += fmt_str(s,"tmp/");
+ s += fmt_ulong(s,time); *s++ = '.';
+ s += fmt_ulong(s,pid); *s++ = '.';
+ s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
+ if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
+ /* really should never get to this point */
+ if (loop == 2) _exit(1);
+ sleep(2);
+ }
+ str_copy(fnnewtph,fntmptph);
+ byte_copy(fnnewtph,3,"new");
+
+ alarm(86400);
+ fd = open_excl(fntmptph);
+ if (fd == -1) _exit(1);
+
+ substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
+ substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+ if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail;
+ if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail;
+
+ switch(substdio_copy(&ssout,&ss))
+ {
+ case -2: tryunlinktmp(); _exit(4);
+ case -3: goto fail;
+ }
+
+ if (substdio_flush(&ssout) == -1) goto fail;
+ if (fsync(fd) == -1) goto fail;
+ if (close(fd) == -1) goto fail; /* NFS dorks */
+
+ if (link(fntmptph,fnnewtph) == -1) goto fail;
+ /* if it was error_exist, almost certainly successful; i hate NFS */
+ tryunlinktmp(); _exit(0);
+
+ fail: tryunlinktmp(); _exit(1);
+}
+
+/* end child process */
+
+void maildir(fn)
+char *fn;
+{
+ int child;
+ int wstat;
+
+ if (seek_begin(0) == -1) temp_rewind();
+
+ switch(child = fork())
+ {
+ case -1:
+ temp_fork();
+ case 0:
+ maildir_child(fn);
+ _exit(111);
+ }
+
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat))
+ temp_childcrashed();
+ switch(wait_exitcode(wstat))
+ {
+ case 0: break;
+ case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
+ case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
+ case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
+ default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
+ }
+}
+
+void mailfile(fn)
+char *fn;
+{
+ int fd;
+ substdio ss;
+ substdio ssout;
+ int match;
+ seek_pos pos;
+ int flaglocked;
+
+ if (seek_begin(0) == -1) temp_rewind();
+
+ fd = open_append(fn);
+ if (fd == -1)
+ strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)");
+
+ sig_alarmcatch(temp_slowlock);
+ alarm(30);
+ flaglocked = (lock_ex(fd) != -1);
+ alarm(0);
+ sig_alarmdefault();
+
+ seek_end(fd);
+ pos = seek_cur(fd);
+
+ substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
+ substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+ if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs;
+ if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs;
+ if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs;
+ for (;;)
+ {
+ if (getln(&ss,&messline,&match,'\n') != 0)
+ {
+ 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 (gfrom(messline.s,messline.len))
+ if (substdio_bput(&ssout,">",1)) goto writeerrs;
+ if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs;
+ if (!match)
+ {
+ if (substdio_bputs(&ssout,"\n")) goto writeerrs;
+ break;
+ }
+ }
+ if (substdio_bputs(&ssout,"\n")) goto writeerrs;
+ if (substdio_flush(&ssout)) goto writeerrs;
+ if (fsync(fd) == -1) goto writeerrs;
+ close(fd);
+ return;
+
+ writeerrs:
+ strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
+ if (flaglocked) seek_trunc(fd,pos);
+ close(fd);
+ _exit(111);
+}
+
+void mailprogram(prog)
+char *prog;
+{
+ int child;
+ char *(args[4]);
+ int wstat;
+
+ if (seek_begin(0) == -1) temp_rewind();
+
+ switch(child = fork())
+ {
+ case -1:
+ temp_fork();
+ case 0:
+ args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
+ sig_pipedefault();
+ execv(*args,args);
+ strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)");
+ }
+
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat))
+ temp_childcrashed();
+ switch(wait_exitcode(wstat))
+ {
+ case 100:
+ case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
+ case 0: break;
+ case 99: flag99 = 1; break;
+ default: _exit(111);
+ }
+}
+
+unsigned long mailforward_qp = 0;
+
+void mailforward(recips)
+char **recips;
+{
+ struct qmail qqt;
+ char *qqx;
+ substdio ss;
+ int match;
+
+ if (seek_begin(0) == -1) temp_rewind();
+ substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
+
+ if (qmail_open(&qqt) == -1) temp_fork();
+ mailforward_qp = qmail_qp(&qqt);
+ qmail_put(&qqt,dtline.s,dtline.len);
+ do
+ {
+ if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; }
+ qmail_put(&qqt,messline.s,messline.len);
+ }
+ while (match);
+ qmail_from(&qqt,ueo.s);
+ while (*recips) qmail_to(&qqt,*recips++);
+ qqx = qmail_close(&qqt);
+ if (!*qqx) return;
+ strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,".");
+}
+
+void bouncexf()
+{
+ int match;
+ substdio ss;
+
+ if (seek_begin(0) == -1) temp_rewind();
+ substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
+ for (;;)
+ {
+ if (getln(&ss,&messline,&match,'\n') != 0) temp_read();
+ if (!match) break;
+ if (messline.len <= 1)
+ break;
+ if (messline.len == dtline.len)
+ if (!str_diffn(messline.s,dtline.s,dtline.len))
+ strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)");
+ }
+}
+
+void checkhome()
+{
+ struct stat st;
+
+ if (stat(".",&st) == -1)
+ 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 (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)
+char *dashowner;
+{
+ struct stat st;
+
+ if (!stralloc_copys(&qme,".qmail")) 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_0(&qme)) temp_nomem();
+ if (stat(qme.s,&st) == -1)
+ {
+ if (error_temp(errno)) temp_qmail(qme.s);
+ return -1;
+ }
+ return 0;
+}
+
+int qmeexists(fd,cutable)
+int *fd;
+int *cutable;
+{
+ 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;
+
+ if (!stralloc_copys(&qme,".qmail")) temp_nomem();
+ if (!stralloc_cats(&qme,dash)) temp_nomem();
+ if (!stralloc_cat(&qme,&safeext)) temp_nomem();
+ if (qmeexists(fd,cutable)) {
+ if (safeext.len >= 7) {
+ i = safeext.len - 7;
+ if (!byte_diff("default",7,safeext.s + i))
+ if (i <= str_len(ext)) /* paranoia */
+ if (!env_put2("DEFAULT",ext + i)) temp_nomem();
+ }
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ *fd = -1;
+}
+
+unsigned long count_file = 0;
+unsigned long count_forward = 0;
+unsigned long count_program = 0;
+char count_buf[FMT_ULONG];
+
+void count_print()
+{
+ substdio_puts(subfdoutsmall,"did ");
+ substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file));
+ substdio_puts(subfdoutsmall,"+");
+ substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward));
+ substdio_puts(subfdoutsmall,"+");
+ substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program));
+ substdio_puts(subfdoutsmall,"\n");
+ if (mailforward_qp)
+ {
+ substdio_puts(subfdoutsmall,"qp ");
+ substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp));
+ substdio_puts(subfdoutsmall,"\n");
+ }
+ substdio_flush(subfdoutsmall);
+}
+
+void sayit(type,cmd,len)
+char *type;
+char *cmd;
+int len;
+{
+ substdio_puts(subfdoutsmall,type);
+ substdio_put(subfdoutsmall,cmd,len);
+ substdio_putsflush(subfdoutsmall,"\n");
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int opt;
+ int i;
+ int j;
+ int k;
+ int fd;
+ int numforward;
+ char **recips;
+ datetime_sec starttime;
+ int flagforwardonly;
+ char *x;
+
+ umask(077);
+ sig_pipeignore();
+
+ if (!env_init()) temp_nomem();
+
+ flagdoit = 1;
+ while ((opt = getopt(argc,argv,"nN")) != opteof)
+ switch(opt)
+ {
+ case 'n': flagdoit = 0; break;
+ case 'N': flagdoit = 1; break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!(user = *argv++)) usage();
+ if (!(homedir = *argv++)) usage();
+ if (!(local = *argv++)) usage();
+ if (!(dash = *argv++)) usage();
+ if (!(ext = *argv++)) usage();
+ if (!(host = *argv++)) usage();
+ if (!(sender = *argv++)) usage();
+ if (!(aliasempty = *argv++)) usage();
+ if (*argv) usage();
+
+ if (homedir[0] != '/') usage();
+ if (chdir(homedir) == -1)
+ strerr_die5x(111,"Unable to switch to ",homedir,": ",error_str(errno),". (#4.3.0)");
+ checkhome();
+
+ if (!env_put2("HOST",host)) temp_nomem();
+ if (!env_put2("HOME",homedir)) temp_nomem();
+ if (!env_put2("USER",user)) temp_nomem();
+ if (!env_put2("LOCAL",local)) temp_nomem();
+
+ if (!stralloc_copys(&envrecip,local)) temp_nomem();
+ if (!stralloc_cats(&envrecip,"@")) temp_nomem();
+ if (!stralloc_cats(&envrecip,host)) temp_nomem();
+
+ if (!stralloc_copy(&foo,&envrecip)) temp_nomem();
+ if (!stralloc_0(&foo)) temp_nomem();
+ if (!env_put2("RECIPIENT",foo.s)) temp_nomem();
+
+ if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem();
+ if (!stralloc_cat(&dtline,&envrecip)) temp_nomem();
+ for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_';
+ if (!stralloc_cats(&dtline,"\n")) temp_nomem();
+
+ if (!stralloc_copy(&foo,&dtline)) temp_nomem();
+ if (!stralloc_0(&foo)) temp_nomem();
+ if (!env_put2("DTLINE",foo.s)) temp_nomem();
+
+ if (flagdoit)
+ bouncexf();
+
+ if (!env_put2("SENDER",sender)) temp_nomem();
+
+ if (!quote2(&foo,sender)) temp_nomem();
+ if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem();
+ if (!stralloc_cat(&rpline,&foo)) temp_nomem();
+ for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_';
+ if (!stralloc_cats(&rpline,">\n")) temp_nomem();
+
+ if (!stralloc_copy(&foo,&rpline)) temp_nomem();
+ if (!stralloc_0(&foo)) temp_nomem();
+ if (!env_put2("RPLINE",foo.s)) temp_nomem();
+
+ if (!stralloc_copys(&ufline,"From ")) temp_nomem();
+ if (*sender)
+ {
+ int len; int i; char ch;
+
+ len = str_len(sender);
+ if (!stralloc_readyplus(&ufline,len)) temp_nomem();
+ for (i = 0;i < len;++i)
+ {
+ ch = sender[i];
+ if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-';
+ ufline.s[ufline.len + i] = ch;
+ }
+ ufline.len += len;
+ }
+ else
+ if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem();
+ if (!stralloc_cats(&ufline," ")) temp_nomem();
+ starttime = now();
+ if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem();
+
+ if (!stralloc_copy(&foo,&ufline)) temp_nomem();
+ if (!stralloc_0(&foo)) temp_nomem();
+ if (!env_put2("UFLINE",foo.s)) temp_nomem();
+
+ x = ext;
+ if (!env_put2("EXT",x)) temp_nomem();
+ x += str_chr(x,'-'); if (*x) ++x;
+ if (!env_put2("EXT2",x)) temp_nomem();
+ x += str_chr(x,'-'); if (*x) ++x;
+ if (!env_put2("EXT3",x)) temp_nomem();
+ x += str_chr(x,'-'); if (*x) ++x;
+ if (!env_put2("EXT4",x)) temp_nomem();
+
+ if (!stralloc_copys(&safeext,ext)) temp_nomem();
+ case_lowerb(safeext.s,safeext.len);
+ for (i = 0;i < safeext.len;++i)
+ if (safeext.s[i] == '.')
+ safeext.s[i] = ':';
+
+ i = str_len(host);
+ 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;
+ qmesearch(&fd,&flagforwardonly);
+ 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 (str_diff(sender,""))
+ if (str_diff(sender,"#@[]"))
+ if (qmeox("-owner") == 0)
+ {
+ if (qmeox("-owner-default") == 0)
+ {
+ if (!stralloc_copys(&ueo,local)) temp_nomem();
+ if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem();
+ if (!stralloc_cats(&ueo,host)) temp_nomem();
+ if (!stralloc_cats(&ueo,"-@[]")) temp_nomem();
+ }
+ else
+ {
+ if (!stralloc_copys(&ueo,local)) temp_nomem();
+ if (!stralloc_cats(&ueo,"-owner@")) temp_nomem();
+ if (!stralloc_cats(&ueo,host)) temp_nomem();
+ }
+ }
+ if (!stralloc_0(&ueo)) temp_nomem();
+ if (!env_put2("NEWSENDER",ueo.s)) temp_nomem();
+
+ if (!stralloc_ready(&cmds,0)) temp_nomem();
+ cmds.len = 0;
+ if (fd != -1)
+ if (slurpclose(fd,&cmds,256) == -1) temp_nomem();
+
+ if (!cmds.len)
+ {
+ if (!stralloc_copys(&cmds,aliasempty)) temp_nomem();
+ flagforwardonly = 0;
+ }
+ if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
+ if (!stralloc_cats(&cmds,"\n")) temp_nomem();
+
+ numforward = 0;
+ i = 0;
+ for (j = 0;j < cmds.len;++j)
+ if (cmds.s[j] == '\n')
+ {
+ switch(cmds.s[i]) { case '#': case '.': case '/': case '|': break;
+ default: ++numforward; }
+ i = j + 1;
+ }
+
+ recips = (char **) alloc((numforward + 1) * sizeof(char *));
+ if (!recips) temp_nomem();
+ numforward = 0;
+
+ flag99 = 0;
+
+ i = 0;
+ for (j = 0;j < cmds.len;++j)
+ if (cmds.s[j] == '\n')
+ {
+ cmds.s[j] = 0;
+ k = j;
+ while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')))
+ cmds.s[--k] = 0;
+ switch(cmds.s[i])
+ {
+ case 0: /* k == i */
+ if (i) break;
+ strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
+ case '#':
+ break;
+ case '.':
+ case '/':
+ ++count_file;
+ 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 (flagdoit) maildir(cmds.s + i);
+ else sayit("maildir ",cmds.s + i,k - i);
+ else
+ if (flagdoit) mailfile(cmds.s + i);
+ else sayit("mbox ",cmds.s + i,k - i);
+ break;
+ case '|':
+ ++count_program;
+ 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);
+ else sayit("program ",cmds.s + i + 1,k - i - 1);
+ break;
+ case '+':
+ if (str_equal(cmds.s + i + 1,"list"))
+ flagforwardonly = 1;
+ break;
+ case '&':
+ ++i;
+ default:
+ ++count_forward;
+ if (flagdoit) recips[numforward++] = cmds.s + i;
+ else sayit("forward ",cmds.s + i,k - i);
+ break;
+ }
+ i = j + 1;
+ if (flag99) break;
+ }
+
+ if (numforward) if (flagdoit)
+ {
+ recips[numforward] = 0;
+ mailforward(recips);
+ }
+
+ count_print();
+ _exit(0);
+}
diff --git a/qmail-log.5 b/qmail-log.5
new file mode 100644
index 0000000..52cd0e6
--- /dev/null
+++ b/qmail-log.5
@@ -0,0 +1,266 @@
+.TH qmail-log 5
+.SH NAME
+qmail-log \- the qmail activity record
+.SH DESCRIPTION
+.B qmail-send
+prints a series of lines describing its activities.
+Each possible line is described below.
+.SH "STATUS"
+.TP
+.B status: local \fIl\fB/\fIL\fB remote \fIr\fB/\fIR\fB ...
+.B qmail-send
+is waiting for
+.I l
+local deliveries
+and
+.I r
+remote deliveries.
+The concurrency limits are
+.I L
+and
+.IR R .
+.TP
+.B status: exiting
+.B qmail-send
+is done.
+.SH "FATAL PROBLEMS"
+.TP
+.B alert: cannot start: ...
+.B qmail-send
+is unable to prepare itself for delivering messages;
+it is giving up.
+This normally indicates a serious configuration error,
+but it can be caused by a temporary lack of resources.
+.TP
+.B alert: oh no! lost ...
+One of the other daemons has died.
+.B qmail-send
+will exit as soon as possible.
+.SH "SERIOUS PROBLEMS"
+.TP
+.B alert: unable to append to bounce message...
+.B qmail-send
+is unable to record a permanent failure,
+usually because the disk is full.
+This is a very serious problem;
+.B qmail-send
+cannot proceed without recording the results.
+It will try again in ten seconds.
+.TP
+.B alert: out of memory...
+.B qmail-send
+tried to allocate more memory and failed.
+It will try again in ten seconds.
+.TP
+.B alert: unable to opendir...
+.B qmail-send
+is having trouble reading a file list from disk,
+usually because the system's file descriptor table is full,
+but possibly because permissions are set incorrectly.
+It will try again in ten seconds.
+.TP
+.B alert: unable to switch back...
+.B qmail-send
+was sent SIGHUP,
+and it is unable to reenter the queue directory.
+This is a very serious problem;
+.B qmail-send
+cannot proceed outside the queue directory.
+It will try again in ten seconds.
+.TP
+.B alert: unable to reread...
+.B qmail-send
+was sent SIGHUP,
+but it is unable to read the new controls.
+It will continue operating with the original controls.
+.SH "MESSAGES"
+.TP
+.B new msg \fIm\fB
+.B qmail-send
+is going to preprocess a queued message.
+The message number,
+.IR m ,
+is its disk inode number.
+After a message is removed from the queue,
+its number can be reused immediately.
+.TP
+.B info msg \fIm\fB: bytes \fIb\fB from <\fIs\fB> qp \fIq\fB uid \fIu\fB
+Message
+.I m
+contains
+.I b
+bytes;
+its envelope sender is
+.IR s ;
+it was queued by a user with user ID
+.IR u .
+.I q
+is a long-term queue identifier,
+the process ID of the
+.B qmail-queue
+that queued the message.
+.TP
+.B bounce msg \fIm\fB qp \fIq\fB
+Message
+.I m
+had some delivery failures.
+The long-term queue identifier of the bounce (or double-bounce) message
+is
+.IR q .
+.TP
+.B triple bounce: discarding ...
+Message
+.I m
+had some delivery failures,
+but it is already a double-bounce message,
+so it must be thrown away.
+Triple-bounce messages do not exist.
+.TP
+.B end msg \fIm\fB
+.B qmail-send
+is about to remove
+message
+.I m
+from the queue.
+.SH "DELIVERIES"
+.TP
+.B starting delivery \fId\fB: msg \fIm\fB to ...
+.B qmail-send
+is telling
+.B qmail-lspawn
+or
+.B qmail-rspawn
+to deliver message
+.I m
+to one recipient.
+The delivery number,
+.IR d ,
+starts at 1 and increases by 1 for each new delivery.
+.TP
+.B delivery \fId\fB: success: ...
+Delivery
+.I d
+was successful.
+.TP
+.B delivery \fId\fB: failure: ...
+Delivery
+.I d
+failed permanently.
+The message will bounce.
+.TP
+.B delivery \fId\fB: deferral: ...
+Delivery
+.I d
+failed temporarily.
+This recipient will be retried later.
+.TP
+.B delivery \fId\fB: report mangled, will defer
+There is a serious bug in
+.B qmail-lspawn
+or
+.BR qmail-rspawn .
+This recipient will be retried later.
+.SH "WARNINGS"
+.TP
+.B internal error: delivery report out of range
+.B qmail-lspawn
+or
+.B qmail-rspawn
+has supplied a report on a nonexistent delivery.
+This is a serious bug.
+.TP
+.B qmail-clean unable to clean up ...
+For some reason
+.B qmail-clean
+is unable to remove the indicated file.
+It will try again later.
+.TP
+.B trouble fsyncing ...
+.B qmail-send
+was unable to write to disk the results of preprocessing a queued message.
+It will try again later.
+.TP
+.B trouble in select
+There is an operating system bug.
+.TP
+.B trouble injecting bounce message...
+.B qmail-send
+was unable to queue a bounce message,
+usually because the disk is full.
+It will try again later.
+.TP
+.B trouble marking ...
+.B qmail-send
+was unable to record the result of a successful or permanently
+unsuccessful delivery.
+This means that the delivery will be tried again later.
+.TP
+.B trouble opening ...
+.B qmail-send
+was unable to open the list of local or remote recipients
+for a message.
+It will try again later.
+.TP
+.B trouble reading ...
+Either
+.B qmail-send
+is unable to read a recipient list,
+or it is unable to read the envelope of a queued
+message, or it is out of memory.
+Whatever it was doing, it will try again later.
+.TP
+.B trouble writing to ...
+.B qmail-send
+was unable to preprocess a queued message,
+usually because the disk is full.
+It will try again later.
+.TP
+.B unable to create ...
+.B qmail-send
+was unable to preprocess a queued message,
+usually because the disk is out of inodes.
+It will try again later.
+.TP
+.B unable to open ...
+.B qmail-send
+is unable to read the envelope of a queued message
+for preprocessing.
+It will try again later.
+.TP
+.B unable to start qmail-queue...
+.B qmail-send
+is unable to queue a bounce message,
+usually because the machine is almost out of memory.
+It will try again later.
+This can also be caused by incorrect settings of
+.B $QMAILQUEUE
+or errors in a program or script which
+.B $QMAILQUEUE
+points to.
+.TP
+.B unable to stat ...
+.B qmail-send
+is unable to obtain information about a file that should exist.
+It will try again later.
+.TP
+.B unable to unlink ...
+.B qmail-send
+is unable to remove a file.
+It will try again later.
+.TP
+.B unable to utime ...
+.B qmail-send
+is about to exit,
+and it is unable to record on disk
+the next scheduled delivery time for a message.
+The message will be retried as soon as
+.B qmail-send
+is restarted.
+.TP
+.B unknown record type in ...
+There is a serious bug in either
+.B qmail-queue
+or
+.BR qmail-send .
+.SH "SEE ALSO"
+qmail-send(8)
diff --git a/qmail-lspawn.8 b/qmail-lspawn.8
new file mode 100644
index 0000000..da01741
--- /dev/null
+++ b/qmail-lspawn.8
@@ -0,0 +1,46 @@
+.TH qmail-lspawn 8
+.SH NAME
+qmail-lspawn \- schedule local deliveries
+.SH SYNOPSIS
+.B qmail-lspawn
+.I defaultdelivery
+.SH DESCRIPTION
+.B qmail-lspawn
+reads a series of local delivery commands from descriptor 0,
+invokes
+.B qmail-local
+to perform the deliveries,
+and prints the results to descriptor 1.
+It passes
+.I defaultdelivery
+to
+.B qmail-local
+as the default delivery instruction.
+
+.B qmail-lspawn
+invokes
+.B qmail-local
+asynchronously,
+so the results may not be in the same order as the commands.
+
+For each recipient address,
+.B qmail-lspawn
+finds out which local user controls that address.
+It first checks the
+.B qmail-users
+mechanism; if the address is not listed there, it invokes
+.BR qmail-getpw .
+.B qmail-lspawn
+then runs
+.B qmail-local
+under the user's uid and gid.
+It does not set up any supplementary groups.
+
+.B qmail-lspawn
+treats an empty mailbox name as a trash address.
+.SH "SEE ALSO"
+envelopes(5),
+qmail-users(5),
+qmail-getpw(8),
+qmail-send(8),
+qmail-local(8)
diff --git a/qmail-lspawn.c b/qmail-lspawn.c
new file mode 100644
index 0000000..5109cc3
--- /dev/null
+++ b/qmail-lspawn.c
@@ -0,0 +1,234 @@
+#include "fd.h"
+#include "wait.h"
+#include "prot.h"
+#include "substdio.h"
+#include "stralloc.h"
+#include "scan.h"
+#include "exit.h"
+#include "fork.h"
+#include "error.h"
+#include "cdb.h"
+#include "case.h"
+#include "slurpclose.h"
+#include "auto_qmail.h"
+#include "auto_uids.h"
+#include "qlx.h"
+
+char *aliasempty;
+
+void initialize(argc,argv)
+int argc;
+char **argv;
+{
+ aliasempty = argv[1];
+ if (!aliasempty) _exit(100);
+}
+
+int truncreport = 3000;
+
+void report(ss,wstat,s,len)
+substdio *ss;
+int wstat;
+char *s;
+int len;
+{
+ int i;
+ if (wait_crashed(wstat))
+ { substdio_puts(ss,"Zqmail-local crashed.\n"); return; }
+ switch(wait_exitcode(wstat))
+ {
+ case QLX_CDB:
+ substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); return;
+ case QLX_NOMEM:
+ substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); return;
+ case QLX_SYS:
+ substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); return;
+ case QLX_NOALIAS:
+ substdio_puts(ss,"ZUnable to find alias user!\n"); return;
+ case QLX_ROOT:
+ substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); return;
+ case QLX_USAGE:
+ substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); return;
+ case QLX_NFS:
+ substdio_puts(ss,"ZNFS failure in qmail-local.\n"); return;
+ case QLX_EXECHARD:
+ substdio_puts(ss,"DUnable to run qmail-local.\n"); return;
+ case QLX_EXECSOFT:
+ substdio_puts(ss,"ZUnable to run qmail-local.\n"); return;
+ case QLX_EXECPW:
+ substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); return;
+ case 111: case 71: case 74: case 75:
+ substdio_put(ss,"Z",1); break;
+ case 0:
+ substdio_put(ss,"K",1); break;
+ case 100:
+ default:
+ substdio_put(ss,"D",1); break;
+ }
+
+ for (i = 0;i < len;++i) if (!s[i]) break;
+ substdio_put(ss,s,i);
+}
+
+stralloc lower = {0};
+stralloc nughde = {0};
+stralloc wildchars = {0};
+
+void nughde_get(local)
+char *local;
+{
+ char *(args[3]);
+ int pi[2];
+ int gpwpid;
+ int gpwstat;
+ int r;
+ int fd;
+ int flagwild;
+
+ if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM);
+ if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM);
+ if (!stralloc_0(&lower)) _exit(QLX_NOMEM);
+ case_lowerb(lower.s,lower.len);
+
+ if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM);
+
+ fd = open_read("users/cdb");
+ if (fd == -1)
+ if (errno != error_noent)
+ _exit(QLX_CDB);
+
+ if (fd != -1)
+ {
+ uint32 dlen;
+ unsigned int i;
+
+ r = cdb_seek(fd,"",0,&dlen);
+ if (r != 1) _exit(QLX_CDB);
+ if (!stralloc_ready(&wildchars,(unsigned int) dlen)) _exit(QLX_NOMEM);
+ wildchars.len = dlen;
+ if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) _exit(QLX_CDB);
+
+ i = lower.len;
+ flagwild = 0;
+
+ do
+ {
+ /* i > 0 */
+ if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len))
+ {
+ r = cdb_seek(fd,lower.s,i,&dlen);
+ if (r == -1) _exit(QLX_CDB);
+ if (r == 1)
+ {
+ if (!stralloc_ready(&nughde,(unsigned int) dlen)) _exit(QLX_NOMEM);
+ nughde.len = dlen;
+ if (cdb_bread(fd,nughde.s,nughde.len) == -1) _exit(QLX_CDB);
+ if (flagwild)
+ if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM);
+ if (!stralloc_0(&nughde)) _exit(QLX_NOMEM);
+ close(fd);
+ return;
+ }
+ }
+ --i;
+ flagwild = 1;
+ }
+ while (i);
+
+ close(fd);
+ }
+
+ if (pipe(pi) == -1) _exit(QLX_SYS);
+ args[0] = "bin/qmail-getpw";
+ args[1] = local;
+ args[2] = 0;
+ switch(gpwpid = vfork())
+ {
+ case -1:
+ _exit(QLX_SYS);
+ case 0:
+ if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE);
+ if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE);
+ close(pi[0]);
+ if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS);
+ execv(*args,args);
+ _exit(QLX_EXECPW);
+ }
+ close(pi[1]);
+
+ if (slurpclose(pi[0],&nughde,128) == -1) _exit(QLX_SYS);
+
+ if (wait_pid(&gpwstat,gpwpid) != -1)
+ {
+ if (wait_crashed(gpwstat)) _exit(QLX_SYS);
+ if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat));
+ }
+}
+
+int spawn(fdmess,fdout,s,r,at)
+int fdmess; int fdout;
+char *s; char *r; int at;
+{
+ int f;
+
+ if (!(f = fork()))
+ {
+ char *(args[11]);
+ unsigned long u;
+ int n;
+ int uid;
+ int gid;
+ char *x;
+ unsigned int xlen;
+
+ r[at] = 0;
+ if (!r[0]) _exit(0); /* <> */
+
+ if (chdir(auto_qmail) == -1) _exit(QLX_USAGE);
+
+ nughde_get(r);
+
+ x = nughde.s;
+ xlen = nughde.len;
+
+ args[0] = "bin/qmail-local";
+ args[1] = "--";
+ args[2] = x;
+ n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
+
+ scan_ulong(x,&u);
+ uid = u;
+ n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
+
+ scan_ulong(x,&u);
+ gid = u;
+ n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
+
+ args[3] = x;
+ n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
+
+ args[4] = r;
+ args[5] = x;
+ n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
+
+ args[6] = x;
+ n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
+
+ args[7] = r + at + 1;
+ args[8] = s;
+ args[9] = aliasempty;
+ args[10] = 0;
+
+ if (fd_move(0,fdmess) == -1) _exit(QLX_SYS);
+ if (fd_move(1,fdout) == -1) _exit(QLX_SYS);
+ if (fd_copy(2,1) == -1) _exit(QLX_SYS);
+ if (prot_gid(gid) == -1) _exit(QLX_USAGE);
+ if (prot_uid(uid) == -1) _exit(QLX_USAGE);
+ if (!getuid()) _exit(QLX_ROOT);
+
+ execv(*args,args);
+ if (error_temp(errno)) _exit(QLX_EXECSOFT);
+ _exit(QLX_EXECHARD);
+ }
+ return f;
+}
diff --git a/qmail-newmrh.9 b/qmail-newmrh.9
new file mode 100644
index 0000000..2f02f10
--- /dev/null
+++ b/qmail-newmrh.9
@@ -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)
diff --git a/qmail-newmrh.c b/qmail-newmrh.c
new file mode 100644
index 0000000..25a4a10
--- /dev/null
+++ b/qmail-newmrh.c
@@ -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);
+}
diff --git a/qmail-newu.9 b/qmail-newu.9
new file mode 100644
index 0000000..12f1b3f
--- /dev/null
+++ b/qmail-newu.9
@@ -0,0 +1,43 @@
+.TH qmail-newu 8
+.SH NAME
+qmail-newu \- prepare address assignments for qmail-lspawn
+.SH SYNOPSIS
+.B qmail-newu
+.SH DESCRIPTION
+.B qmail-newu
+reads the assignments in
+.B QMAILHOME/users/assign
+and writes them into
+.B QMAILHOME/users/cdb
+in a binary format suited
+for quick access by
+.BR qmail-lspawn .
+
+If there is a problem with
+.BR users/assign ,
+.B qmail-newu
+complains and leaves
+.B users/cdb
+alone.
+
+.B qmail-newu
+ensures that
+.B users/cdb
+is updated atomically,
+so
+.B qmail-lspawn
+never has to wait for
+.B qmail-newu
+to finish.
+However,
+.B qmail-newu
+makes no attempt to protect against two simultaneous updates of
+.BR users/cdb .
+
+The binary
+.B users/cdb
+format is portable across machines.
+.SH "SEE ALSO"
+qmail-users(5),
+qmail-lspawn(8),
+qmail-pw2u(8)
diff --git a/qmail-newu.c b/qmail-newu.c
new file mode 100644
index 0000000..1f9ecc3
--- /dev/null
+++ b/qmail-newu.c
@@ -0,0 +1,137 @@
+#include "stralloc.h"
+#include "subfd.h"
+#include "getln.h"
+#include "substdio.h"
+#include "cdbmss.h"
+#include "exit.h"
+#include "readwrite.h"
+#include "open.h"
+#include "error.h"
+#include "case.h"
+#include "auto_qmail.h"
+
+void die_temp() { _exit(111); }
+
+void die_chdir()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: unable to chdir\n");
+ die_temp();
+}
+void die_nomem()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: out of memory\n");
+ die_temp();
+}
+void die_opena()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: unable to open users/assign\n");
+ die_temp();
+}
+void die_reada()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: unable to read users/assign\n");
+ die_temp();
+}
+void die_format()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: bad format in users/assign\n");
+ die_temp();
+}
+void die_opent()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: unable to open users/cdb.tmp\n");
+ die_temp();
+}
+void die_writet()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: unable to write users/cdb.tmp\n");
+ die_temp();
+}
+void die_rename()
+{
+ substdio_putsflush(subfderr,"qmail-newu: fatal: unable to move users/cdb.tmp to users/cdb\n");
+ die_temp();
+}
+
+struct cdbmss cdbmss;
+stralloc key = {0};
+stralloc data = {0};
+
+char inbuf[1024];
+substdio ssin;
+
+int fd;
+int fdtemp;
+
+stralloc line = {0};
+int match;
+
+stralloc wildchars = {0};
+
+void main()
+{
+ int i;
+ int numcolons;
+
+ umask(033);
+ if (chdir(auto_qmail) == -1) die_chdir();
+
+ fd = open_read("users/assign");
+ if (fd == -1) die_opena();
+
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+ fdtemp = open_trunc("users/cdb.tmp");
+ if (fdtemp == -1) die_opent();
+
+ if (cdbmss_start(&cdbmss,fdtemp) == -1) die_writet();
+
+ if (!stralloc_copys(&wildchars,"")) die_nomem();
+
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') != 0) die_reada();
+ if (line.len && (line.s[0] == '.')) break;
+ if (!match) die_format();
+
+ if (byte_chr(line.s,line.len,'\0') < line.len) die_format();
+ i = byte_chr(line.s,line.len,':');
+ if (i == line.len) die_format();
+ if (i == 0) die_format();
+ if (!stralloc_copys(&key,"!")) die_nomem();
+ if (line.s[0] == '+') {
+ if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem();
+ case_lowerb(key.s,key.len);
+ if (i >= 2)
+ if (byte_chr(wildchars.s,wildchars.len,line.s[i - 1]) == wildchars.len)
+ if (!stralloc_append(&wildchars,line.s + i - 1)) die_nomem();
+ }
+ else {
+ if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem();
+ if (!stralloc_0(&key)) die_nomem();
+ case_lowerb(key.s,key.len);
+ }
+
+ if (!stralloc_copyb(&data,line.s + i + 1,line.len - i - 1)) die_nomem();
+
+ numcolons = 0;
+ for (i = 0;i < data.len;++i)
+ if (data.s[i] == ':') {
+ data.s[i] = 0;
+ if (++numcolons == 6)
+ break;
+ }
+ if (numcolons < 6) die_format();
+ data.len = i;
+
+ if (cdbmss_add(&cdbmss,key.s,key.len,data.s,data.len) == -1) die_writet();
+ }
+
+ if (cdbmss_add(&cdbmss,"",0,wildchars.s,wildchars.len) == -1) die_writet();
+
+ if (cdbmss_finish(&cdbmss) == -1) die_writet();
+ if (fsync(fdtemp) == -1) die_writet();
+ if (close(fdtemp) == -1) die_writet(); /* NFS stupidity */
+ if (rename("users/cdb.tmp","users/cdb") == -1) die_rename();
+
+ _exit(0);
+}
diff --git a/qmail-pop3d.8 b/qmail-pop3d.8
new file mode 100644
index 0000000..de6b2ba
--- /dev/null
+++ b/qmail-pop3d.8
@@ -0,0 +1,43 @@
+.TH qmail-pop3d 8
+.SH NAME
+qmail-pop3d \- distribute mail via POP
+.SH SYNOPSIS
+.B qmail-pop3d
+.I maildirname
+.SH DESCRIPTION
+.B qmail-pop3d
+lets a user read and delete his mail through the network.
+
+Mail is stored in a
+.B maildir
+called
+.IR maildirname ,
+normally
+.BR Maildir ,
+in the user's home directory.
+
+.B qmail-pop3d
+is normally invoked
+under
+.BR qmail-popup ,
+which reads a username and password,
+and
+.BR /bin/checkpassword ,
+which checks the password and sets up environment variables.
+
+.B qmail-pop3d
+has a 20-minute idle timeout.
+
+.B qmail-pop3d
+supports UIDL, TOP, and LAST.
+
+.B qmail-pop3d
+appends an extra blank line to every message
+to work around serious bugs in certain clients.
+
+.B qmail-pop3d
+is based on a program contributed by Russ Nelson.
+.SH "SEE ALSO"
+maildir(5),
+qmail-local(8),
+qmail-popup(8)
diff --git a/qmail-pop3d.c b/qmail-pop3d.c
new file mode 100644
index 0000000..0ca4f9c
--- /dev/null
+++ b/qmail-pop3d.c
@@ -0,0 +1,305 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "commands.h"
+#include "sig.h"
+#include "getln.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "alloc.h"
+#include "open.h"
+#include "prioq.h"
+#include "scan.h"
+#include "fmt.h"
+#include "str.h"
+#include "exit.h"
+#include "maildir.h"
+#include "readwrite.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+
+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;
+{
+ substdio_puts(&ssout,s);
+}
+void flush()
+{
+ substdio_flush(&ssout);
+}
+void err(s) char *s;
+{
+ puts("-ERR ");
+ puts(s);
+ puts("\r\n");
+ flush();
+}
+
+void die_nomem() { err("out of memory"); 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_unimpl(arg) char *arg; { err("unimplemented"); }
+void err_deleted() { err("already deleted"); }
+void err_nozero() { err("messages are counted from 1"); }
+void err_toobig() { err("not that many messages"); }
+void err_nosuch() { err("unable to open that message"); }
+void err_nounlink() { err("unable to unlink all deleted messages"); }
+
+void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
+
+void printfn(fn) char *fn;
+{
+ fn += 4;
+ put(fn,str_chr(fn,':'));
+}
+
+char strnum[FMT_ULONG];
+stralloc line = {0};
+
+void blast(ssfrom,limit)
+substdio *ssfrom;
+unsigned long limit;
+{
+ int match;
+ int inheaders = 1;
+
+ for (;;) {
+ if (getln(ssfrom,&line,&match,'\n') != 0) die();
+ if (!match && !line.len) break;
+ if (match) --line.len; /* no way to pass this info over POP */
+ if (limit) if (!inheaders) if (!--limit) break;
+ if (!line.len)
+ inheaders = 0;
+ else
+ if (line.s[0] == '.')
+ put(".",1);
+ put(line.s,line.len);
+ put("\r\n",2);
+ if (!match) break;
+ }
+ put("\r\n.\r\n",5);
+ flush();
+}
+
+stralloc filenames = {0};
+prioq pq = {0};
+
+struct message {
+ int flagdeleted;
+ unsigned long size;
+ char *fn;
+} *m;
+int numm;
+
+int last = 0;
+
+void getlist()
+{
+ struct prioq_elt pe;
+ struct stat st;
+ int i;
+
+ maildir_clean(&line);
+ if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan();
+
+ numm = pq.p ? pq.len : 0;
+ m = (struct message *) alloc(numm * sizeof(struct message));
+ if (!m) die_nomem();
+
+ for (i = 0;i < numm;++i) {
+ if (!prioq_min(&pq,&pe)) { numm = i; break; }
+ prioq_delmin(&pq);
+ m[i].fn = filenames.s + pe.id;
+ m[i].flagdeleted = 0;
+ if (stat(m[i].fn,&st) == -1)
+ m[i].size = 0;
+ else
+ m[i].size = st.st_size;
+ }
+}
+
+void pop3_stat(arg) char *arg;
+{
+ int i;
+ unsigned long total;
+
+ total = 0;
+ for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
+ puts("+OK ");
+ put(strnum,fmt_uint(strnum,numm));
+ puts(" ");
+ put(strnum,fmt_ulong(strnum,total));
+ puts("\r\n");
+ flush();
+}
+
+void pop3_rset(arg) char *arg;
+{
+ int i;
+ for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
+ last = 0;
+ okay(0);
+}
+
+void pop3_last(arg) char *arg;
+{
+ puts("+OK ");
+ put(strnum,fmt_uint(strnum,last));
+ puts("\r\n");
+ flush();
+}
+
+void pop3_quit(arg) char *arg;
+{
+ int i;
+ for (i = 0;i < numm;++i)
+ if (m[i].flagdeleted) {
+ 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(0);
+ die();
+}
+
+int msgno(arg) char *arg;
+{
+ unsigned long u;
+ if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
+ if (!u) { err_nozero(); return -1; }
+ --u;
+ if (u >= numm) { err_toobig(); return -1; }
+ if (m[u].flagdeleted) { err_deleted(); return -1; }
+ return u;
+}
+
+void pop3_dele(arg) char *arg;
+{
+ int i;
+ i = msgno(arg);
+ if (i == -1) return;
+ m[i].flagdeleted = 1;
+ if (i + 1 > last) last = i + 1;
+ okay(0);
+}
+
+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;
+{
+ unsigned int i;
+ if (*arg) {
+ i = msgno(arg);
+ if (i == -1) return;
+ puts("+OK ");
+ list(i,flaguidl);
+ }
+ else {
+ okay(0);
+ for (i = 0;i < numm;++i)
+ if (!m[i].flagdeleted)
+ list(i,flaguidl);
+ puts(".\r\n");
+ }
+ flush();
+}
+
+void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
+void pop3_list(arg) char *arg; { dolisting(arg,0); }
+
+substdio ssmsg; char ssmsgbuf[1024];
+
+void pop3_top(arg) char *arg;
+{
+ int i;
+ unsigned long limit;
+ int fd;
+
+ i = msgno(arg);
+ if (i == -1) return;
+
+ arg += scan_ulong(arg,&limit);
+ while (*arg == ' ') ++arg;
+ if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
+
+ fd = open_read(m[i].fn);
+ if (fd == -1) { err_nosuch(); return; }
+ okay(0);
+ substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
+ blast(&ssmsg,limit);
+ close(fd);
+}
+
+struct commands pop3commands[] = {
+ { "quit", pop3_quit, 0 }
+, { "stat", pop3_stat, 0 }
+, { "list", pop3_list, 0 }
+, { "uidl", pop3_uidl, 0 }
+, { "dele", pop3_dele, 0 }
+, { "retr", pop3_top, 0 }
+, { "rset", pop3_rset, 0 }
+, { "last", pop3_last, 0 }
+, { "top", pop3_top, 0 }
+, { "noop", okay, 0 }
+, { 0, err_unimpl, 0 }
+} ;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ sig_alarmcatch(die);
+ sig_pipeignore();
+
+ if (!argv[1]) die_nomaildir();
+ if (chdir(argv[1]) == -1) die_nomaildir();
+
+ getlist();
+
+ okay(0);
+ commands(&ssin,pop3commands);
+ die();
+}
diff --git a/qmail-popup.8 b/qmail-popup.8
new file mode 100644
index 0000000..95f01bc
--- /dev/null
+++ b/qmail-popup.8
@@ -0,0 +1,65 @@
+.TH qmail-popup 8
+.SH NAME
+qmail-popup \- read a POP username and password
+.SH SYNOPSIS
+.B qmail-popup
+.I hostname
+.I subprogram
+.SH DESCRIPTION
+.B qmail-popup
+reads a POP username and password from the network.
+It then runs
+.IR subprogram .
+
+.B qmail-popup
+is most commonly invoked from
+.B inetd
+as
+
+.EX
+ qmail-popup CHANGEME checkpassword qmail-pop3d Maildir
+.EE
+
+with
+CHANGEME
+replaced by the fully qualified domain name of the local host.
+
+.B qmail-popup
+expects descriptor 0 to read from the network
+and descriptor 1 to write to the network.
+It reads a username and password from descriptor 0
+in POP's USER-PASS style or APOP style.
+It invokes
+.IR subprogram ,
+with the same descriptors 0 and 1;
+descriptor 2 writing to the network;
+and descriptor 3 reading the username, a 0 byte, the password,
+another 0 byte,
+an APOP timestamp derived from
+.IR hostname ,
+and a final 0 byte.
+.B qmail-popup
+then waits for
+.I subprogram
+to finish.
+It prints an error message if
+.I subprogram
+crashes or exits nonzero.
+
+.B qmail-popup
+should be used only within
+a secure network.
+Otherwise an eavesdropper can steal passwords.
+Even if you use APOP,
+an active attacker can still take over the connection
+and wreak havoc.
+
+.B qmail-popup
+has a 20-minute idle timeout.
+
+.B qmail-popup
+is based on a program contributed by Russ Nelson.
+.SH "SEE ALSO"
+maildir(5),
+qmail-local(8),
+qmail-pop3d(8)
diff --git a/qmail-popup.c b/qmail-popup.c
new file mode 100644
index 0000000..906da67
--- /dev/null
+++ b/qmail-popup.c
@@ -0,0 +1,183 @@
+#include "commands.h"
+#include "fd.h"
+#include "sig.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "alloc.h"
+#include "wait.h"
+#include "str.h"
+#include "byte.h"
+#include "now.h"
+#include "fmt.h"
+#include "exit.h"
+#include "readwrite.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+
+void die() { _exit(1); }
+
+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[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;
+{
+ puts("-ERR ");
+ puts(s);
+ puts("\r\n");
+ flush();
+}
+
+void die_usage() { err("usage: popup hostname subprogram"); die(); }
+void die_nomem() { err("out of memory"); die(); }
+void die_pipe() { err("unable to open pipe"); die(); }
+void die_write() { err("unable to write pipe"); die(); }
+void die_fork() { err("unable to fork"); die(); }
+void die_childcrashed() { err("aack, child crashed"); }
+void die_badauth() { err("authorization failed"); }
+
+void err_syntax() { err("syntax error"); }
+void err_wantuser() { err("USER first"); }
+void err_authoriz(arg) char *arg; { err("authorization first"); }
+
+void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
+void pop3_quit(arg) char *arg; { okay(0); die(); }
+
+
+char unique[FMT_ULONG + FMT_ULONG + 3];
+char *hostname;
+stralloc username = {0};
+int seenuser = 0;
+char **childargs;
+substdio ssup;
+char upbuf[128];
+
+
+void doanddie(user,userlen,pass)
+char *user;
+unsigned int userlen; /* including 0 byte */
+char *pass;
+{
+ int child;
+ int wstat;
+ int pi[2];
+
+ if (fd_copy(2,1) == -1) die_pipe();
+ close(3);
+ if (pipe(pi) == -1) die_pipe();
+ if (pi[0] != 3) die_pipe();
+ switch(child = fork()) {
+ case -1:
+ die_fork();
+ case 0:
+ close(pi[1]);
+ sig_pipedefault();
+ execvp(*childargs,childargs);
+ _exit(1);
+ }
+ close(pi[0]);
+ substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
+ if (substdio_put(&ssup,user,userlen) == -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,unique) == -1) die_write();
+ if (substdio_puts(&ssup,hostname) == -1) die_write();
+ if (substdio_put(&ssup,">",2) == -1) die_write();
+ if (substdio_flush(&ssup) == -1) die_write();
+ close(pi[1]);
+ byte_zero(pass,str_len(pass));
+ byte_zero(upbuf,sizeof upbuf);
+ if (wait_pid(&wstat,child) == -1) die();
+ if (wait_crashed(wstat)) die_childcrashed();
+ if (wait_exitcode(wstat)) die_badauth();
+ die();
+}
+void pop3_greet()
+{
+ char *s;
+ s = unique;
+ s += fmt_uint(s,getpid());
+ *s++ = '.';
+ s += fmt_ulong(s,(unsigned long) now());
+ *s++ = '@';
+ *s++ = 0;
+ puts("+OK <");
+ puts(unique);
+ puts(hostname);
+ puts(">\r\n");
+ flush();
+}
+void pop3_user(arg) char *arg;
+{
+ if (!*arg) { err_syntax(); return; }
+ okay(0);
+ seenuser = 1;
+ if (!stralloc_copys(&username,arg)) die_nomem();
+ if (!stralloc_0(&username)) die_nomem();
+}
+void pop3_pass(arg) char *arg;
+{
+ if (!seenuser) { err_wantuser(); return; }
+ if (!*arg) { err_syntax(); return; }
+ doanddie(username.s,username.len,arg);
+}
+void pop3_apop(arg) char *arg;
+{
+ char *space;
+ space = arg + str_chr(arg,' ');
+ if (!*space) { err_syntax(); return; }
+ *space++ = 0;
+ doanddie(arg,space - arg,space);
+}
+
+struct commands pop3commands[] = {
+ { "user", pop3_user, 0 }
+, { "pass", pop3_pass, 0 }
+, { "apop", pop3_apop, 0 }
+, { "quit", pop3_quit, 0 }
+, { "noop", okay, 0 }
+, { 0, err_authoriz, 0 }
+} ;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ sig_alarmcatch(die);
+ sig_pipeignore();
+
+ hostname = argv[1];
+ if (!hostname) die_usage();
+ childargs = argv + 2;
+ if (!*childargs) die_usage();
+
+ pop3_greet();
+ commands(&ssin,pop3commands);
+ die();
+}
diff --git a/qmail-pw2u.9 b/qmail-pw2u.9
new file mode 100644
index 0000000..932cd4d
--- /dev/null
+++ b/qmail-pw2u.9
@@ -0,0 +1,241 @@
+.TH qmail-pw2u 8
+.SH NAME
+qmail-pw2u \- build address assignments from a passwd file
+.SH SYNOPSIS
+.B qmail-pw2u
+[
+.B \-/ohHuUC
+]
+[
+.B \-c\fIchar
+]
+.SH DESCRIPTION
+.B qmail-pw2u
+reads a V7-format passwd file from standard input
+and prints a
+.BR qmail-users -format
+assignment file.
+
+A V7-format passwd file is a series of lines.
+Each line has the format
+
+.EX
+ user:password:uid:gid:gecos:home:shell
+.EE
+
+where
+.I user
+is an account name,
+.I uid
+and
+.I gid
+are the user id and group id of that account,
+and
+.I home
+is the account's home directory.
+.IR password ,
+.IR gecos ,
+and
+.I shell
+are ignored by
+.BR qmail-pw2u .
+
+If you put the output of
+.B qmail-pw2u
+into
+.BR QMAILHOME/users/assign ,
+and then run
+.BR qmail-newu ,
+.B qmail-lspawn
+will obey the assignments printed by
+.BR qmail-pw2u .
+.B WARNING:
+After changing any users, uids, gids, or home directories
+in your passwd file,
+you must run
+.B qmail-pw2u
+and
+.B qmail-newu
+again if you want
+.B qmail-lspawn
+to see the changes.
+.SH RULES
+By default,
+.B qmail-pw2u
+follows the same rules as
+.BR qmail-getpw .
+It skips
+.I user
+if (1)
+.I uid
+is zero,
+(2)
+.I home
+does not exist,
+(3)
+.I user
+does not own
+.IR home ,
+or
+(4)
+.I user
+contains uppercase letters.
+It then gives each remaining
+.I user
+control over the basic
+.I user
+address and
+all addresses of the form
+.IR user\fBBREAK\fIanything .
+A catch-all user,
+.BR alias ,
+controls all other addresses.
+
+You may change these rules by setting up files in
+.BR QMAILHOME/users :
+.TP
+.B include
+Allowed users, one per line.
+If
+.B include
+exists, and
+.I user
+is not listed in
+.BR include ,
+.I user
+is ignored.
+.TP
+.B exclude
+Ignored users, one per line.
+If
+.B exclude
+exists, and
+.I user
+is listed in
+.BR exclude ,
+.I user
+is ignored.
+.TP
+.B mailnames
+Replacement names for users.
+Each line has the form
+
+.EX
+ user:mailname1:mailname2:...
+.EE
+
+The addresses
+.I mailname1
+and
+.I mailname1\fBBREAK\fIext
+and
+.I mailname2
+and so on will be delivered
+to
+.IR user .
+
+.B WARNING:
+The addresses
+.I user
+and
+.I user\fBBREAK\fIext
+will not be delivered to
+.I user
+unless
+.I user
+is listed as one of the
+.IR mailname s.
+
+A line in
+.B mailnames
+is silently ignored if the user does not exist.
+.TP
+.B subusers
+Extra addresses.
+Each line has the form
+
+.EX
+ sub:user:pre:
+.EE
+
+.I sub
+will be handled by
+.IR home\fB/.qmail\-\fIpre ,
+where
+.I home
+is
+.IR user 's
+home directory;
+.I sub\fBBREAK\fIext
+will be handled by
+.IR home\fB/.qmail\-\fIpre\fB\-\fIext .
+.TP
+.B append
+Extra assignments,
+printed at the end of
+.BR qmail-pw2u 's
+output.
+.SH OPTIONS
+.TP
+.B \-o
+(Default.)
+Skip
+.I user
+if
+.I home
+does not exist (or is not visible to
+.BR qmail-pw2u ).
+Skip
+.I user
+if
+.I home
+is not owned by
+.IR user .
+.TP
+.B \-h
+Stop if
+.I home
+does not exist.
+This is appropriate if every user is supposed to have a home directory.
+Skip
+.I user
+if
+.I home
+is not owned by
+.IR user .
+.TP
+.B \-H
+Do not check the existence or ownership of
+.IR home .
+.TP
+.B \-U
+(Default.)
+Skip
+.I user
+if there are any uppercase letters in
+.IR user .
+.TP
+.B \-u
+Allow uppercase letters in
+.IR user .
+.TP
+.B \-c\fIchar
+Use
+.I char
+as the user-extension delimiter
+in place of
+.BR BREAK .
+.TP
+.B \-C
+Disable the user-extension mechanism.
+.TP
+.B \-/
+Use
+.IR home\fB/.qmail\-/ ...
+instead of
+.IR home\fB/.qmail\- ...
+.SH "SEE ALSO"
+qmail-users(5),
+qmail-lspawn(8),
+qmail-newu(8),
+qmail-getpw(8)
diff --git a/qmail-pw2u.c b/qmail-pw2u.c
new file mode 100644
index 0000000..4146067
--- /dev/null
+++ b/qmail-pw2u.c
@@ -0,0 +1,312 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "substdio.h"
+#include "readwrite.h"
+#include "subfd.h"
+#include "sgetopt.h"
+#include "control.h"
+#include "constmap.h"
+#include "stralloc.h"
+#include "fmt.h"
+#include "str.h"
+#include "scan.h"
+#include "open.h"
+#include "error.h"
+#include "getln.h"
+#include "auto_break.h"
+#include "auto_qmail.h"
+#include "auto_usera.h"
+
+void die_chdir()
+{
+ substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to chdir\n");
+ _exit(111);
+}
+void die_nomem()
+{
+ substdio_putsflush(subfderr,"qmail-pw2u: fatal: out of memory\n");
+ _exit(111);
+}
+void die_read()
+{
+ substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to read input\n");
+ _exit(111);
+}
+void die_write()
+{
+ substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to write output\n");
+ _exit(111);
+}
+void die_control()
+{
+ substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to read controls\n");
+ _exit(111);
+}
+void die_alias()
+{
+ substdio_puts(subfderr,"qmail-pw2u: fatal: unable to find ");
+ substdio_puts(subfderr,auto_usera);
+ substdio_puts(subfderr," user\n");
+ substdio_flush(subfderr);
+ _exit(111);
+}
+void die_home(fn) char *fn;
+{
+ substdio_puts(subfderr,"qmail-pw2u: fatal: unable to stat ");
+ substdio_puts(subfderr,fn);
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+ _exit(111);
+}
+void die_user(s,len) char *s; unsigned int len;
+{
+ substdio_puts(subfderr,"qmail-pw2u: fatal: unable to find ");
+ substdio_put(subfderr,s,len);
+ substdio_puts(subfderr," user for subuser\n");
+ substdio_flush(subfderr);
+ _exit(111);
+}
+
+char *dashcolon = "-:";
+int flagalias = 0;
+int flagnoupper = 1;
+int homestrategy = 2;
+/* 2: skip if home does not exist; skip if home is not owned by user */
+/* 1: stop if home does not exist; skip if home is not owned by user */
+/* 0: don't worry about home */
+
+int okincl; stralloc incl = {0}; struct constmap mapincl;
+int okexcl; stralloc excl = {0}; struct constmap mapexcl;
+int okmana; stralloc mana = {0}; struct constmap mapmana;
+
+stralloc allusers = {0}; struct constmap mapuser;
+
+stralloc uugh = {0};
+stralloc user = {0};
+stralloc uidstr = {0};
+stralloc gidstr = {0};
+stralloc home = {0};
+unsigned long uid;
+
+stralloc line = {0};
+
+void doaccount()
+{
+ struct stat st;
+ int i;
+ char *mailnames;
+ char *x;
+ unsigned int xlen;
+
+ if (byte_chr(line.s,line.len,'\0') < line.len) return;
+
+ x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ if (!stralloc_copyb(&user,x,i)) die_nomem();
+ if (!stralloc_0(&user)) die_nomem();
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ if (!stralloc_copyb(&uidstr,x,i)) die_nomem();
+ if (!stralloc_0(&uidstr)) die_nomem();
+ scan_ulong(uidstr.s,&uid);
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ if (!stralloc_copyb(&gidstr,x,i)) die_nomem();
+ if (!stralloc_0(&gidstr)) die_nomem();
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ if (!stralloc_copyb(&home,x,i)) die_nomem();
+ if (!stralloc_0(&home)) die_nomem();
+
+ if (!uid) return;
+ if (flagnoupper)
+ for (i = 0;i < user.len;++i)
+ if ((user.s[i] >= 'A') && (user.s[i] <= 'Z'))
+ return;
+ if (okincl)
+ if (!constmap(&mapincl,user.s,user.len - 1))
+ return;
+ if (okexcl)
+ if (constmap(&mapexcl,user.s,user.len - 1))
+ return;
+ if (homestrategy) {
+ if (stat(home.s,&st) == -1) {
+ if (errno != error_noent) die_home(home.s);
+ if (homestrategy == 1) die_home(home.s);
+ return;
+ }
+ if (st.st_uid != uid) return;
+ }
+
+ if (!stralloc_copys(&uugh,":")) die_nomem();
+ if (!stralloc_cats(&uugh,user.s)) die_nomem();
+ if (!stralloc_cats(&uugh,":")) die_nomem();
+ if (!stralloc_cats(&uugh,uidstr.s)) die_nomem();
+ if (!stralloc_cats(&uugh,":")) die_nomem();
+ if (!stralloc_cats(&uugh,gidstr.s)) die_nomem();
+ if (!stralloc_cats(&uugh,":")) die_nomem();
+ if (!stralloc_cats(&uugh,home.s)) die_nomem();
+ if (!stralloc_cats(&uugh,":")) die_nomem();
+
+ /* XXX: avoid recording in allusers unless sub actually needs it */
+ if (!stralloc_cats(&allusers,user.s)) die_nomem();
+ if (!stralloc_cats(&allusers,":")) die_nomem();
+ if (!stralloc_catb(&allusers,uugh.s,uugh.len)) die_nomem();
+ if (!stralloc_0(&allusers)) die_nomem();
+
+ if (str_equal(user.s,auto_usera)) {
+ if (substdio_puts(subfdout,"+") == -1) die_write();
+ if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
+ if (substdio_puts(subfdout,dashcolon) == -1) die_write();
+ if (substdio_puts(subfdout,":\n") == -1) die_write();
+ flagalias = 1;
+ }
+
+ mailnames = 0;
+ if (okmana)
+ mailnames = constmap(&mapmana,user.s,user.len - 1);
+ if (!mailnames)
+ mailnames = user.s;
+
+ for (;;) {
+ while (*mailnames == ':') ++mailnames;
+ if (!*mailnames) break;
+
+ i = str_chr(mailnames,':');
+
+ if (substdio_puts(subfdout,"=") == -1) die_write();
+ if (substdio_put(subfdout,mailnames,i) == -1) die_write();
+ if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
+ if (substdio_puts(subfdout,"::\n") == -1) die_write();
+
+ if (*auto_break) {
+ if (substdio_puts(subfdout,"+") == -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,uugh.s,uugh.len) == -1) die_write();
+ if (substdio_puts(subfdout,dashcolon) == -1) die_write();
+ if (substdio_puts(subfdout,":\n") == -1) die_write();
+ }
+
+ mailnames += i;
+ }
+}
+
+stralloc sub = {0};
+
+void dosubuser()
+{
+ int i;
+ char *x;
+ unsigned int xlen;
+ char *uugh;
+
+ x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ if (!stralloc_copyb(&sub,x,i)) die_nomem();
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+ uugh = constmap(&mapuser,x,i);
+ if (!uugh) die_user(x,i);
+ ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
+
+ if (substdio_puts(subfdout,"=") == -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,dashcolon) == -1) die_write();
+ if (substdio_put(subfdout,x,i) == -1) die_write();
+ if (substdio_puts(subfdout,":\n") == -1) die_write();
+
+ if (*auto_break) {
+ if (substdio_puts(subfdout,"+") == -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_puts(subfdout,uugh) == -1) die_write();
+ if (substdio_puts(subfdout,dashcolon) == -1) die_write();
+ if (substdio_put(subfdout,x,i) == -1) die_write();
+ if (substdio_puts(subfdout,"-:\n") == -1) die_write();
+ }
+}
+
+int fd;
+substdio ss;
+char ssbuf[SUBSTDIO_INSIZE];
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int opt;
+ int match;
+
+ while ((opt = getopt(argc,argv,"/ohHuUc:C")) != opteof)
+ switch(opt) {
+ case '/': dashcolon = "-/:"; break;
+ case 'o': homestrategy = 2; break;
+ case 'h': homestrategy = 1; break;
+ case 'H': homestrategy = 0; break;
+ case 'u': flagnoupper = 0; break;
+ case 'U': flagnoupper = 1; break;
+ case 'c': *auto_break = *optarg; break;
+ case 'C': *auto_break = 0; break;
+ case '?':
+ default:
+ _exit(100);
+ }
+
+ if (chdir(auto_qmail) == -1) die_chdir();
+
+ /* no need for control_init() */
+
+ okincl = control_readfile(&incl,"users/include",0);
+ if (okincl == -1) die_control();
+ if (okincl) if (!constmap_init(&mapincl,incl.s,incl.len,0)) die_nomem();
+
+ okexcl = control_readfile(&excl,"users/exclude",0);
+ if (okexcl == -1) die_control();
+ if (okexcl) if (!constmap_init(&mapexcl,excl.s,excl.len,0)) die_nomem();
+
+ okmana = control_readfile(&mana,"users/mailnames",0);
+ if (okmana == -1) die_control();
+ if (okmana) if (!constmap_init(&mapmana,mana.s,mana.len,1)) die_nomem();
+
+ if (!stralloc_copys(&allusers,"")) die_nomem();
+
+ for (;;) {
+ if (getln(subfdin,&line,&match,'\n') == -1) die_read();
+ doaccount();
+ if (!match) break;
+ }
+ if (!flagalias) die_alias();
+
+ fd = open_read("users/subusers");
+ if (fd == -1) {
+ if (errno != error_noent) die_control();
+ }
+ else {
+ substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+ if (!constmap_init(&mapuser,allusers.s,allusers.len,1)) die_nomem();
+
+ for (;;) {
+ if (getln(&ss,&line,&match,'\n') == -1) die_read();
+ dosubuser();
+ if (!match) break;
+ }
+
+ close(fd);
+ }
+
+ fd = open_read("users/append");
+ if (fd == -1) {
+ if (errno != error_noent) die_control();
+ }
+ else {
+ substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+ for (;;) {
+ if (getln(&ss,&line,&match,'\n') == -1) die_read();
+ if (substdio_put(subfdout,line.s,line.len) == -1) die_write();
+ if (!match) break;
+ }
+ }
+
+ if (substdio_puts(subfdout,".\n") == -1) die_write();
+ if (substdio_flush(subfdout) == -1) die_write();
+ _exit(0);
+}
diff --git a/qmail-qmqpc.8 b/qmail-qmqpc.8
new file mode 100644
index 0000000..e11a15e
--- /dev/null
+++ b/qmail-qmqpc.8
@@ -0,0 +1,29 @@
+.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 .
+.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)
diff --git a/qmail-qmqpc.c b/qmail-qmqpc.c
new file mode 100644
index 0000000..d5adf05
--- /dev/null
+++ b/qmail-qmqpc.c
@@ -0,0 +1,159 @@
+#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);
+}
diff --git a/qmail-qmqpd.8 b/qmail-qmqpd.8
new file mode 100644
index 0000000..5142dfa
--- /dev/null
+++ b/qmail-qmqpd.8
@@ -0,0 +1,25 @@
+.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.
+.SH "SEE ALSO"
+tcp-env(1),
+tcpserver(1),
+tcp-environ(5),
+qmail-qmqpc(8),
+qmail-queue(8)
diff --git a/qmail-qmqpd.c b/qmail-qmqpd.c
new file mode 100644
index 0000000..86cb284
--- /dev/null
+++ b/qmail-qmqpd.c
@@ -0,0 +1,174 @@
+#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)";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+ _exit(0);
+}
diff --git a/qmail-qmtpd.8 b/qmail-qmtpd.8
new file mode 100644
index 0000000..244fdbf
--- /dev/null
+++ b/qmail-qmtpd.8
@@ -0,0 +1,32 @@
+.TH qmail-qmtpd 8
+.SH NAME
+qmail-qmtpd \- receive mail via QMTP
+.SH SYNOPSIS
+.B qmail-qmtpd
+.SH DESCRIPTION
+.B qmail-qmtpd
+receives mail messages via the Quick Mail Transfer Protocol (QMTP)
+and invokes
+.B qmail-queue
+to deposit them into the outgoing queue.
+.B qmail-qmtpd
+must be supplied several environment variables;
+see
+.BR tcp-environ(5) .
+
+.B qmail-qmtpd
+supports the
+.IR rcpthosts ,
+.IR morercpthosts ,
+.BR RELAYCLIENT ,
+.IR databytes ,
+and
+.B DATABYTES
+mechanisms described in
+.BR qmail-smtpd(8) .
+.SH "SEE ALSO"
+tcp-env(1),
+tcp-environ(5),
+qmail-control(5),
+qmail-queue(8),
+qmail-smtpd(8)
diff --git a/qmail-qmtpd.c b/qmail-qmtpd.c
new file mode 100644
index 0000000..df911a6
--- /dev/null
+++ b/qmail-qmtpd.c
@@ -0,0 +1,268 @@
+#include "stralloc.h"
+#include "substdio.h"
+#include "qmail.h"
+#include "now.h"
+#include "str.h"
+#include "fmt.h"
+#include "env.h"
+#include "sig.h"
+#include "rcpthosts.h"
+#include "auto_qmail.h"
+#include "readwrite.h"
+#include "control.h"
+#include "received.h"
+
+void badproto() { _exit(100); }
+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;
+}
+
+char ssoutbuf[256];
+substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
+
+int saferead(fd,buf,len) int fd; char *buf; int len;
+{
+ int r;
+ substdio_flush(&ssout);
+ r = read(fd,buf,len);
+ if (r <= 0) _exit(0);
+ return r;
+}
+
+char ssinbuf[512];
+substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
+
+unsigned long getlen()
+{
+ unsigned long len = 0;
+ char ch;
+ for (;;) {
+ substdio_get(&ssin,&ch,1);
+ if (ch == ':') return len;
+ if (len > 200000000) resources();
+ len = 10 * len + (ch - '0');
+ }
+}
+
+void getcomma()
+{
+ char ch;
+ substdio_get(&ssin,&ch,1);
+ if (ch != ',') badproto();
+}
+
+unsigned int databytes = 0;
+unsigned int bytestooverflow = 0;
+struct qmail qq;
+
+char buf[1000];
+char buf2[100];
+
+char *remotehost;
+char *remoteinfo;
+char *remoteip;
+char *local;
+
+stralloc failure = {0};
+
+char *relayclient;
+int relayclientlen;
+
+main()
+{
+ char ch;
+ int i;
+ unsigned long biglen;
+ unsigned long len;
+ int flagdos;
+ int flagsenderok;
+ int flagbother;
+ unsigned long qp;
+ char *result;
+ char *x;
+ unsigned long u;
+
+ sig_pipeignore();
+ sig_alarmcatch(resources);
+ alarm(3600);
+
+ if (chdir(auto_qmail) == -1) resources();
+
+ if (control_init() == -1) resources();
+ if (rcpthosts_init() == -1) resources();
+ relayclient = env_get("RELAYCLIENT");
+ relayclientlen = relayclient ? str_len(relayclient) : 0;
+
+ if (control_readint(&databytes,"control/databytes") == -1) resources();
+ x = env_get("DATABYTES");
+ if (x) { scan_ulong(x,&u); databytes = u; }
+ if (!(databytes + 1)) --databytes;
+
+ 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";
+
+ for (;;) {
+ if (!stralloc_copys(&failure,"")) resources();
+ flagsenderok = 1;
+
+ len = getlen();
+ if (len == 0) badproto();
+
+ if (databytes) bytestooverflow = databytes + 1;
+ if (qmail_open(&qq) == -1) resources();
+ qp = qmail_qp(&qq);
+
+ substdio_get(&ssin,&ch,1);
+ --len;
+ if (ch == 10) flagdos = 0;
+ else if (ch == 13) flagdos = 1;
+ else badproto();
+
+ received(&qq,"QMTP",local,remoteip,remotehost,remoteinfo,(char *) 0);
+
+ /* XXX: check for loops? only if len is big? */
+
+ if (flagdos)
+ while (len > 0) {
+ substdio_get(&ssin,&ch,1);
+ --len;
+ while ((ch == 13) && len) {
+ substdio_get(&ssin,&ch,1);
+ --len;
+ if (ch == 10) break;
+ if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
+ qmail_put(&qq,"\015",1);
+ }
+ if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
+ qmail_put(&qq,&ch,1);
+ }
+ else {
+ if (databytes)
+ if (len > databytes) {
+ bytestooverflow = 0;
+ qmail_fail(&qq);
+ }
+ while (len > 0) { /* XXX: could speed this up, obviously */
+ substdio_get(&ssin,&ch,1);
+ --len;
+ qmail_put(&qq,&ch,1);
+ }
+ }
+ getcomma();
+
+ len = getlen();
+
+ if (len >= 1000) {
+ buf[0] = 0;
+ flagsenderok = 0;
+ for (i = 0;i < len;++i)
+ substdio_get(&ssin,&ch,1);
+ }
+ else {
+ for (i = 0;i < len;++i) {
+ substdio_get(&ssin,buf + i,1);
+ if (!buf[i]) flagsenderok = 0;
+ }
+ buf[len] = 0;
+ }
+ getcomma();
+
+ flagbother = 0;
+ qmail_from(&qq,buf);
+ if (!flagsenderok) qmail_fail(&qq);
+
+ biglen = getlen();
+ while (biglen > 0) {
+ if (!stralloc_append(&failure,"")) resources();
+
+ len = 0;
+ for (;;) {
+ if (!biglen) badproto();
+ substdio_get(&ssin,&ch,1);
+ --biglen;
+ if (ch == ':') break;
+ if (len > 200000000) resources();
+ len = 10 * len + (ch - '0');
+ }
+ if (len >= biglen) badproto();
+ if (len + relayclientlen >= 1000) {
+ failure.s[failure.len - 1] = 'L';
+ for (i = 0;i < len;++i)
+ substdio_get(&ssin,&ch,1);
+ }
+ else {
+ for (i = 0;i < len;++i) {
+ substdio_get(&ssin,buf + i,1);
+ if (!buf[i]) failure.s[failure.len - 1] = 'N';
+ }
+ buf[len] = 0;
+
+ if (relayclient)
+ str_copy(buf + len,relayclient);
+ else
+ switch(rcpthosts(buf,len)) {
+ case -1: resources();
+ case 0: failure.s[failure.len - 1] = 'D';
+ }
+
+ if (!failure.s[failure.len - 1]) {
+ qmail_to(&qq,buf);
+ flagbother = 1;
+ }
+ }
+ getcomma();
+ biglen -= (len + 1);
+ }
+ getcomma();
+
+ if (!flagbother) qmail_fail(&qq);
+ result = qmail_close(&qq);
+ if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)";
+ if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
+
+ if (*result)
+ len = str_len(result);
+ else {
+ /* success! */
+ len = 0;
+ len += fmt_str(buf2 + len,"Kok ");
+ len += fmt_ulong(buf2 + len,(unsigned long) now());
+ len += fmt_str(buf2 + len," qp ");
+ len += fmt_ulong(buf2 + len,qp);
+ buf2[len] = 0;
+ result = buf2;
+ }
+
+ len = fmt_ulong(buf,len);
+ buf[len++] = ':';
+ len += fmt_str(buf + len,result);
+ buf[len++] = ',';
+
+ for (i = 0;i < failure.len;++i)
+ switch(failure.s[i]) {
+ case 0:
+ substdio_put(&ssout,buf,len);
+ break;
+ case 'D':
+ substdio_puts(&ssout,"66:Dsorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),");
+ break;
+ default:
+ substdio_puts(&ssout,"46:Dsorry, I can't handle that recipient (#5.1.3),");
+ break;
+ }
+
+ /* ssout will be flushed when we read from the network again */
+ }
+}
diff --git a/qmail-qread.8 b/qmail-qread.8
new file mode 100644
index 0000000..a4c31ef
--- /dev/null
+++ b/qmail-qread.8
@@ -0,0 +1,24 @@
+.TH qmail-qread 8
+.SH NAME
+qmail-qread \- list outgoing messages and recipients
+.SH SYNOPSIS
+.B qmail-qread
+.SH DESCRIPTION
+.B qmail-qread
+scans the outgoing queue of messages.
+For each message it prints various human-readable information,
+including the date the message entered the queue,
+the number of bytes in the message,
+the message sender,
+and all the recipients still under consideration.
+
+.B qmail-qread
+must be run either as
+.B root
+or with user id
+.B qmails
+and group id
+.BR qmail .
+.SH "SEE ALSO"
+qmail-qstat(8),
+qmail-send(8)
diff --git a/qmail-qread.c b/qmail-qread.c
new file mode 100644
index 0000000..8ec0fbd
--- /dev/null
+++ b/qmail-qread.c
@@ -0,0 +1,175 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "fmt.h"
+#include "str.h"
+#include "getln.h"
+#include "fmtqfn.h"
+#include "readsubdir.h"
+#include "auto_qmail.h"
+#include "open.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "readwrite.h"
+#include "error.h"
+#include "exit.h"
+
+readsubdir rs;
+
+void die(n) int n; { substdio_flush(subfdout); _exit(n); }
+
+void warn(s1,s2) char *s1; char *s2;
+{
+ char *x;
+ x = error_str(errno);
+ substdio_puts(subfdout,s1);
+ substdio_puts(subfdout,s2);
+ substdio_puts(subfdout,": ");
+ substdio_puts(subfdout,x);
+ substdio_puts(subfdout,"\n");
+}
+
+void die_nomem() { substdio_puts(subfdout,"fatal: out of memory\n"); die(111); }
+void die_chdir() { warn("fatal: unable to chdir",""); die(111); }
+void die_opendir(fn) char *fn; { warn("fatal: unable to opendir ",fn); die(111); }
+
+void err(id) unsigned long id;
+{
+ char foo[FMT_ULONG];
+ foo[fmt_ulong(foo,id)] = 0;
+ warn("warning: trouble with #",foo);
+}
+
+char fnmess[FMTQFN];
+char fninfo[FMTQFN];
+char fnlocal[FMTQFN];
+char fnremote[FMTQFN];
+char fnbounce[FMTQFN];
+
+char inbuf[1024];
+stralloc sender = {0};
+
+unsigned long id;
+datetime_sec qtime;
+int flagbounce;
+unsigned long size;
+
+unsigned int fmtstats(s)
+char *s;
+{
+ struct datetime dt;
+ unsigned int len;
+ unsigned int i;
+
+ len = 0;
+ datetime_tai(&dt,qtime);
+ i = date822fmt(s,&dt) - 7/*XXX*/; len += i; if (s) s += i;
+ i = fmt_str(s," GMT #"); len += i; if (s) s += i;
+ i = fmt_ulong(s,id); len += i; if (s) s += i;
+ i = fmt_str(s," "); len += i; if (s) s += i;
+ i = fmt_ulong(s,size); len += i; if (s) s += i;
+ i = fmt_str(s," <"); len += i; if (s) s += i;
+ i = fmt_str(s,sender.s + 1); len += i; if (s) s += i;
+ i = fmt_str(s,"> "); len += i; if (s) s += i;
+ if (flagbounce)
+ {
+ i = fmt_str(s," bouncing"); len += i; if (s) s += i;
+ }
+
+ return len;
+}
+
+stralloc stats = {0};
+
+void out(s,n) char *s; unsigned int n;
+{
+ while (n > 0)
+ {
+ substdio_put(subfdout,((*s >= 32) && (*s <= 126)) ? s : "_",1);
+ --n;
+ ++s;
+ }
+}
+void outs(s) char *s; { out(s,str_len(s)); }
+void outok(s) char *s; { substdio_puts(subfdout,s); }
+
+void putstats()
+{
+ if (!stralloc_ready(&stats,fmtstats(FMT_LEN))) die_nomem();
+ stats.len = fmtstats(stats.s);
+ out(stats.s,stats.len);
+ outok("\n");
+}
+
+stralloc line = {0};
+
+void main()
+{
+ int channel;
+ int match;
+ struct stat st;
+ int fd;
+ substdio ss;
+ int x;
+
+ if (chdir(auto_qmail) == -1) die_chdir();
+ if (chdir("queue") == -1) die_chdir();
+ readsubdir_init(&rs,"info",die_opendir);
+
+ while (x = readsubdir_next(&rs,&id))
+ if (x > 0)
+ {
+ fmtqfn(fnmess,"mess/",id,1);
+ fmtqfn(fninfo,"info/",id,1);
+ fmtqfn(fnlocal,"local/",id,1);
+ fmtqfn(fnremote,"remote/",id,1);
+ fmtqfn(fnbounce,"bounce/",id,0);
+
+ if (stat(fnmess,&st) == -1) { err(id); continue; }
+ size = st.st_size;
+ flagbounce = !stat(fnbounce,&st);
+
+ fd = open_read(fninfo);
+ if (fd == -1) { err(id); continue; }
+ substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
+ if (getln(&ss,&sender,&match,0) == -1) die_nomem();
+ if (fstat(fd,&st) == -1) { close(fd); err(id); continue; }
+ close(fd);
+ qtime = st.st_mtime;
+
+ putstats();
+
+ for (channel = 0;channel < 2;++channel)
+ {
+ fd = open_read(channel ? fnremote : fnlocal);
+ if (fd == -1)
+ {
+ if (errno != error_noent)
+ err(id);
+ }
+ else
+ {
+ for (;;)
+ {
+ if (getln(&ss,&line,&match,0) == -1) die_nomem();
+ if (!match) break;
+ switch(line.s[0])
+ {
+ case 'D':
+ outok(" done");
+ case 'T':
+ outok(channel ? "\tremote\t" : "\tlocal\t");
+ outs(line.s + 1);
+ outok("\n");
+ break;
+ }
+ }
+ close(fd);
+ }
+ }
+ }
+
+ die(0);
+}
diff --git a/qmail-qstat.8 b/qmail-qstat.8
new file mode 100644
index 0000000..3491e34
--- /dev/null
+++ b/qmail-qstat.8
@@ -0,0 +1,18 @@
+.TH qmail-qstat 8
+.SH NAME
+qmail-qstat \- summarize status of mail queue
+.SH SYNOPSIS
+.B qmail-qstat
+.SH DESCRIPTION
+.B qmail-qstat
+gives a human-readable breakdown
+of the number of messages at various spots in the mail queue.
+
+.B qmail-qstat
+must be run either as
+.B root
+or with group id
+.BR qmail .
+.SH "SEE ALSO"
+qmail-qread(8),
+qmail-send(8)
diff --git a/qmail-qstat.sh b/qmail-qstat.sh
new file mode 100644
index 0000000..26a6d3f
--- /dev/null
+++ b/qmail-qstat.sh
@@ -0,0 +1,7 @@
+cd QMAIL
+messdirs=`echo queue/mess/* | wc -w`
+messfiles=`find queue/mess/* -print | wc -w`
+tododirs=`echo queue/todo | wc -w`
+todofiles=`find queue/todo -print | wc -w`
+echo messages in queue: `expr $messfiles - $messdirs`
+echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs`
diff --git a/qmail-queue.8 b/qmail-queue.8
new file mode 100644
index 0000000..ded3285
--- /dev/null
+++ b/qmail-queue.8
@@ -0,0 +1,161 @@
+.TH qmail-queue 8
+.SH NAME
+qmail-queue \- queue a mail message for delivery
+.SH SYNOPSIS
+.B qmail-queue
+.SH DESCRIPTION
+.B qmail-queue
+reads a mail message from descriptor 0.
+It then reads envelope information from descriptor 1.
+It places the message into the outgoing queue
+for future delivery by
+.BR qmail-send .
+
+The envelope information is
+an envelope sender address
+followed by a list of envelope recipient addresses.
+The sender address is preceded by the letter F
+and terminated by a 0 byte.
+Each recipient address is preceded by the letter T
+and terminated by a 0 byte.
+The list of recipient addresses is terminated by an extra 0 byte.
+If
+.B qmail-queue
+sees end-of-file before the extra 0 byte,
+it aborts without placing the message into the queue.
+
+Every envelope recipient address
+should contain a username,
+an @ sign,
+and a fully qualified domain name.
+
+.B qmail-queue
+always adds a
+.B Received
+line to the top of the message.
+Other than this,
+.B qmail-queue
+does not inspect the message
+and does not enforce any restrictions on its contents.
+However, the recipients probably expect to see a proper header,
+as described in
+.BR qmail-header(5) .
+
+Programs included with qmail which invoke
+.B qmail-queue
+will invoke the contents of
+.B $QMAILQUEUE
+instead, if that environment variable is set.
+.SH "FILESYSTEM RESTRICTIONS"
+.B qmail-queue
+imposes two constraints on the queue structure:
+each
+.B mess
+subdirectory must be in the same filesystem as the
+.B pid
+directory; and each
+.B todo
+subdirectory must be in the same filesystem as the
+.B intd
+directory.
+.SH "EXIT CODES"
+.B qmail-queue
+does not print diagnostics.
+It exits
+0 if
+it has successfully queued the message.
+It exits between 1 and 99 if
+it has failed to queue the message.
+
+All
+.B qmail-queue
+error codes between 11 and 40
+indicate permanent errors:
+.TP 5
+.B 11
+Address too long.
+.TP
+.B 31
+Mail server permanently refuses to send the message to any recipients.
+(Not used by
+.BR qmail-queue ,
+but can be used by programs offering the same interface.)
+.PP
+All other
+.B qmail-queue
+error codes indicate temporary errors:
+.TP 5
+.B 51
+Out of memory.
+.TP
+.B 52
+Timeout.
+.TP
+.B 53
+Write error; e.g., disk full.
+.TP
+.B 54
+Unable to read the message or envelope.
+.TP
+.B 55
+Unable to read a configuration file.
+(Not used by
+.BR qmail-queue .)
+.TP
+.B 56
+Problem making a network connection from this host.
+(Not used by
+.BR qmail-queue .)
+.TP
+.B 61
+Problem with the qmail home directory.
+.TP
+.B 62
+Problem with the queue directory.
+.TP
+.B 63
+Problem with queue/pid.
+.TP
+.B 64
+Problem with queue/mess.
+.TP
+.B 65
+Problem with queue/intd.
+.TP
+.B 66
+Problem with queue/todo.
+.TP
+.B 71
+Mail server temporarily refuses to send the message to any recipients.
+(Not used by
+.BR qmail-queue .)
+.TP
+.B 72
+Connection to mail server timed out.
+(Not used by
+.BR qmail-queue .)
+.TP
+.B 73
+Connection to mail server rejected.
+(Not used by
+.BR qmail-queue .)
+.TP
+.B 74
+Connection to mail server succeeded,
+but communication failed.
+(Not used by
+.BR qmail-queue .)
+.TP
+.B 81
+Internal bug; e.g., segmentation fault.
+.TP
+.B 91
+Envelope format error.
+.SH "SEE ALSO"
+addresses(5),
+envelopes(5),
+qmail-header(5),
+qmail-inject(8),
+qmail-qmqpc(8),
+qmail-send(8),
+qmail-smtpd(8)
diff --git a/qmail-queue.c b/qmail-queue.c
new file mode 100644
index 0000000..4b39a8f
--- /dev/null
+++ b/qmail-queue.c
@@ -0,0 +1,254 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "readwrite.h"
+#include "sig.h"
+#include "exit.h"
+#include "open.h"
+#include "seek.h"
+#include "fmt.h"
+#include "alloc.h"
+#include "substdio.h"
+#include "datetime.h"
+#include "now.h"
+#include "triggerpull.h"
+#include "extra.h"
+#include "auto_qmail.h"
+#include "auto_uids.h"
+#include "date822fmt.h"
+#include "fmtqfn.h"
+
+#define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
+#define ADDR 1003
+
+char inbuf[2048];
+struct substdio ssin;
+char outbuf[256];
+struct substdio ssout;
+
+datetime_sec starttime;
+struct datetime dt;
+unsigned long mypid;
+unsigned long uid;
+char *pidfn;
+struct stat pidst;
+unsigned long messnum;
+char *messfn;
+char *todofn;
+char *intdfn;
+int messfd;
+int intdfd;
+int flagmademess = 0;
+int flagmadeintd = 0;
+
+void cleanup()
+{
+ if (flagmadeintd)
+ {
+ seek_trunc(intdfd,0);
+ if (unlink(intdfn) == -1) return;
+ }
+ if (flagmademess)
+ {
+ seek_trunc(messfd,0);
+ if (unlink(messfn) == -1) return;
+ }
+}
+
+void die(e) int e; { _exit(e); }
+void die_write() { cleanup(); die(53); }
+void die_read() { cleanup(); die(54); }
+void sigalrm() { /* thou shalt not clean up here */ die(52); }
+void sigbug() { die(81); }
+
+unsigned int receivedlen;
+char *received;
+/* "Received: (qmail-queue invoked by alias); 26 Sep 1995 04:46:54 -0000\n" */
+
+static unsigned int receivedfmt(s)
+char *s;
+{
+ unsigned int i;
+ unsigned int len;
+ len = 0;
+ i = fmt_str(s,"Received: (qmail "); len += i; if (s) s += i;
+ i = fmt_ulong(s,mypid); len += i; if (s) s += i;
+ i = fmt_str(s," invoked "); len += i; if (s) s += i;
+ if (uid == auto_uida)
+ { i = fmt_str(s,"by alias"); len += i; if (s) s += i; }
+ else if (uid == auto_uidd)
+ { i = fmt_str(s,"from network"); len += i; if (s) s += i; }
+ else if (uid == auto_uids)
+ { i = fmt_str(s,"for bounce"); len += i; if (s) s += i; }
+ else
+ {
+ i = fmt_str(s,"by uid "); len += i; if (s) s += i;
+ i = fmt_ulong(s,uid); len += i; if (s) s += i;
+ }
+ i = fmt_str(s,"); "); len += i; if (s) s += i;
+ i = date822fmt(s,&dt); len += i; if (s) s += i;
+ return len;
+}
+
+void received_setup()
+{
+ receivedlen = receivedfmt((char *) 0);
+ received = alloc(receivedlen + 1);
+ if (!received) die(51);
+ receivedfmt(received);
+}
+
+unsigned int pidfmt(s,seq)
+char *s;
+unsigned long seq;
+{
+ unsigned int i;
+ unsigned int len;
+
+ len = 0;
+ i = fmt_str(s,"pid/"); len += i; if (s) s += i;
+ i = fmt_ulong(s,mypid); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,starttime); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,seq); len += i; if (s) s += i;
+ ++len; if (s) *s++ = 0;
+
+ return len;
+}
+
+char *fnnum(dirslash,flagsplit)
+char *dirslash;
+int flagsplit;
+{
+ char *s;
+
+ s = alloc(fmtqfn((char *) 0,dirslash,messnum,flagsplit));
+ if (!s) die(51);
+ fmtqfn(s,dirslash,messnum,flagsplit);
+ return s;
+}
+
+void pidopen()
+{
+ unsigned int len;
+ unsigned long seq;
+
+ seq = 1;
+ len = pidfmt((char *) 0,seq);
+ pidfn = alloc(len);
+ if (!pidfn) die(51);
+
+ for (seq = 1;seq < 10;++seq)
+ {
+ if (pidfmt((char *) 0,seq) > len) die(81); /* paranoia */
+ pidfmt(pidfn,seq);
+ messfd = open_excl(pidfn);
+ if (messfd != -1) return;
+ }
+
+ die(63);
+}
+
+char tmp[FMT_ULONG];
+
+void main()
+{
+ unsigned int len;
+ char ch;
+
+ sig_blocknone();
+ umask(033);
+ if (chdir(auto_qmail) == -1) die(61);
+ if (chdir("queue") == -1) die(62);
+
+ mypid = getpid();
+ uid = getuid();
+ starttime = now();
+ datetime_tai(&dt,starttime);
+
+ received_setup();
+
+ sig_pipeignore();
+ sig_miscignore();
+ sig_alarmcatch(sigalrm);
+ sig_bugcatch(sigbug);
+
+ alarm(DEATH);
+
+ pidopen();
+ if (fstat(messfd,&pidst) == -1) die(63);
+
+ messnum = pidst.st_ino;
+ messfn = fnnum("mess/",1);
+ todofn = fnnum("todo/",0);
+ intdfn = fnnum("intd/",0);
+
+ if (link(pidfn,messfn) == -1) die(64);
+ if (unlink(pidfn) == -1) die(63);
+ flagmademess = 1;
+
+ substdio_fdbuf(&ssout,write,messfd,outbuf,sizeof(outbuf));
+ substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+ if (substdio_bput(&ssout,received,receivedlen) == -1) die_write();
+
+ switch(substdio_copy(&ssout,&ssin))
+ {
+ case -2: die_read();
+ case -3: die_write();
+ }
+
+ if (substdio_flush(&ssout) == -1) die_write();
+ if (fsync(messfd) == -1) die_write();
+
+ intdfd = open_excl(intdfn);
+ if (intdfd == -1) die(65);
+ flagmadeintd = 1;
+
+ substdio_fdbuf(&ssout,write,intdfd,outbuf,sizeof(outbuf));
+ substdio_fdbuf(&ssin,read,1,inbuf,sizeof(inbuf));
+
+ if (substdio_bput(&ssout,"u",1) == -1) die_write();
+ if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,uid)) == -1) die_write();
+ if (substdio_bput(&ssout,"",1) == -1) die_write();
+
+ if (substdio_bput(&ssout,"p",1) == -1) die_write();
+ if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,mypid)) == -1) die_write();
+ if (substdio_bput(&ssout,"",1) == -1) die_write();
+
+ if (substdio_get(&ssin,&ch,1) < 1) die_read();
+ if (ch != 'F') die(91);
+ if (substdio_bput(&ssout,&ch,1) == -1) die_write();
+ for (len = 0;len < ADDR;++len)
+ {
+ if (substdio_get(&ssin,&ch,1) < 1) die_read();
+ if (substdio_put(&ssout,&ch,1) == -1) die_write();
+ if (!ch) break;
+ }
+ if (len >= ADDR) die(11);
+
+ if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write();
+
+ for (;;)
+ {
+ if (substdio_get(&ssin,&ch,1) < 1) die_read();
+ if (!ch) break;
+ if (ch != 'T') die(91);
+ if (substdio_bput(&ssout,&ch,1) == -1) die_write();
+ for (len = 0;len < ADDR;++len)
+ {
+ if (substdio_get(&ssin,&ch,1) < 1) die_read();
+ if (substdio_bput(&ssout,&ch,1) == -1) die_write();
+ if (!ch) break;
+ }
+ if (len >= ADDR) die(11);
+ }
+
+ if (substdio_flush(&ssout) == -1) die_write();
+ if (fsync(intdfd) == -1) die_write();
+
+ if (link(intdfn,todofn) == -1) die(66);
+
+ triggerpull();
+ die(0);
+}
diff --git a/qmail-remote.8 b/qmail-remote.8
new file mode 100644
index 0000000..08bae85
--- /dev/null
+++ b/qmail-remote.8
@@ -0,0 +1,205 @@
+.TH qmail-remote 8
+.SH NAME
+qmail-remote \- send mail via SMTP
+.SH SYNOPSIS
+.B qmail-remote
+.I host
+.I sender
+.I recip
+[
+.I recip ...
+]
+.SH DESCRIPTION
+.B qmail-remote
+reads a mail message from its input
+and sends the message
+to one or more recipients
+at a remote host.
+
+The remote host is
+.BR qmail-remote 's
+first argument,
+.IR host .
+.B qmail-remote
+sends the message to
+.IR host ,
+or to a mail exchanger for
+.I host
+listed in the Domain Name System,
+via the Simple Mail Transfer Protocol (SMTP).
+.I host
+can be either a fully-qualified domain name:
+
+.EX
+ silverton.berkeley.edu
+.EE
+
+or an IP address enclosed in brackets:
+
+.EX
+ [128.32.183.163]
+.EE
+
+The envelope recipient addresses are listed as
+.I recip
+arguments to
+.BR qmail-remote .
+The envelope sender address is listed as
+.I sender\fP.
+
+Note that
+.B qmail-remote
+does not take options
+and does not follow the
+.B getopt
+standard.
+.SH TRANSPARENCY
+End-of-file in SMTP is encoded as dot CR LF.
+A dot at the beginning of a line is encoded as dot dot.
+It is impossible in SMTP to send a message that does not end with a newline.
+.B qmail-remote
+converts the UNIX newline convention into the SMTP newline convention
+by inserting CR before each LF.
+
+It is a violation of the SMTP protocol
+to send a message that contains long lines or non-ASCII characters.
+However,
+.B qmail-remote
+will happily send such messages.
+It is the user's responsibility to avoid generating illegal messages.
+.SH "RESULTS"
+.B qmail-remote
+prints some number of
+.I recipient reports\fP,
+followed by a
+.I message report\fR.
+Each report is terminated by a 0 byte.
+Each report begins with a single letter:
+.TP 5
+r
+Recipient report: acceptance.
+.TP 5
+h
+Recipient report: permanent rejection.
+.TP 5
+s
+Recipient report: temporary rejection.
+.TP 5
+K
+Message report: success.
+.I host
+has taken responsibility for delivering the message to each
+acceptable recipient.
+.TP 5
+Z
+Message report: temporary failure.
+.TP 5
+D
+Message report: permanent failure.
+.PP
+After this letter comes a human-readable description of
+what happened.
+
+The recipient reports will always be printed in the same order as
+.BR qmail-remote 's
+.I recip
+arguments.
+Note that in failure cases there may be fewer
+recipient reports
+than
+.I recip
+arguments.
+
+.B qmail-remote
+always exits zero.
+.SH "CONTROL FILES"
+.TP 5
+.I helohost
+Current host name,
+for use solely in saying hello to the remote SMTP server.
+Default:
+.IR me ,
+if that is supplied;
+otherwise
+.B qmail-remote
+refuses to run.
+.TP 5
+.I smtproutes
+Artificial SMTP routes.
+Each route has the form
+.IR domain\fB:\fIrelay ,
+without any extra spaces.
+If
+.I domain
+matches
+.IR host ,
+.B qmail-remote
+will connect to
+.IR relay ,
+as if
+.I host
+had
+.I relay
+as its only MX.
+(It will also avoid doing any CNAME lookups on
+.IR recip .)
+.I host
+may include a colon and a port number to use instead of the
+normal SMTP port, 25:
+
+.EX
+ inside.af.mil:firewall.af.mil:26
+.EE
+
+.I relay
+may be empty;
+this tells
+.B qmail-remote
+to look up MX records as usual.
+.I smtproutes
+may include wildcards:
+
+.EX
+ .af.mil:
+ :heaven.af.mil
+.EE
+
+Here
+any address ending with
+.B .af.mil
+(but not
+.B af.mil
+itself)
+is routed by its MX records;
+any other address is artificially routed to
+.BR heaven.af.mil .
+
+The
+.B qmail
+system does not protect you if you create an artificial
+mail loop between machines.
+However,
+you are always safe using
+.I smtproutes
+if you do not accept mail from the network.
+.TP 5
+.I timeoutconnect
+Number of seconds
+.B qmail-remote
+will wait for the remote SMTP server to accept a connection.
+Default: 60.
+The kernel normally imposes a 75-second upper limit.
+.TP 5
+.I timeoutremote
+Number of seconds
+.B qmail-remote
+will wait for each response from the remote SMTP server.
+Default: 1200.
+.SH "SEE ALSO"
+addresses(5),
+envelopes(5),
+qmail-control(5),
+qmail-send(8),
+qmail-smtpd(8),
+qmail-tcpok(8),
+qmail-tcpto(8)
diff --git a/qmail-remote.c b/qmail-remote.c
new file mode 100644
index 0000000..7d65473
--- /dev/null
+++ b/qmail-remote.c
@@ -0,0 +1,427 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "sig.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "scan.h"
+#include "case.h"
+#include "error.h"
+#include "auto_qmail.h"
+#include "control.h"
+#include "dns.h"
+#include "alloc.h"
+#include "quote.h"
+#include "ip.h"
+#include "ipalloc.h"
+#include "ipme.h"
+#include "gen_alloc.h"
+#include "gen_allocdefs.h"
+#include "str.h"
+#include "now.h"
+#include "exit.h"
+#include "constmap.h"
+#include "tcpto.h"
+#include "readwrite.h"
+#include "timeoutconn.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+
+#define HUGESMTPTEXT 5000
+
+#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
+unsigned long port = PORT_SMTP;
+
+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};
+
+stralloc helohost = {0};
+stralloc routes = {0};
+struct constmap maproutes;
+stralloc host = {0};
+stralloc sender = {0};
+
+saa reciplist = {0};
+
+struct ip_address partner;
+
+void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
+void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
+void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); }
+void outsafe(sa) stralloc *sa; { int i; char ch;
+for (i = 0;i < sa->len;++i) {
+ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
+if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } }
+
+void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
+void temp_oserr() { out("Z\
+System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
+void temp_noconn() { out("Z\
+Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); }
+void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); }
+void temp_dnscanon() { out("Z\
+CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); }
+void temp_dns() { out("Z\
+Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); }
+void temp_chdir() { out("Z\
+Unable to switch to home directory. (#4.3.0)\n"); zerodie(); }
+void temp_control() { out("Z\
+Unable to read control files. (#4.3.0)\n"); zerodie(); }
+void perm_partialline() { out("D\
+SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); }
+void perm_usage() { out("D\
+I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); }
+void perm_dns() { out("D\
+Sorry, I couldn't find any host named ");
+outsafe(&host);
+out(". (#5.1.2)\n"); zerodie(); }
+void perm_nomx() { out("D\
+Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
+zerodie(); }
+void perm_ambigmx() { out("D\
+Sorry. Although I'm listed as a best-preference MX or A for that host,\n\
+it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
+zerodie(); }
+
+void outhost()
+{
+ char x[IPFMT];
+ if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0);
+}
+
+int flagcritical = 0;
+
+void dropped() {
+ out("ZConnected to ");
+ outhost();
+ out(" but connection died. ");
+ if (flagcritical) out("Possible duplicate! ");
+ out("(#4.4.2)\n");
+ zerodie();
+}
+
+int timeoutconnect = 60;
+int smtpfd;
+int timeout = 1200;
+
+int saferead(fd,buf,len) int fd; char *buf; int len;
+{
+ int r;
+ r = timeoutread(timeout,smtpfd,buf,len);
+ if (r <= 0) dropped();
+ return r;
+}
+int safewrite(fd,buf,len) int fd; char *buf; int len;
+{
+ int r;
+ r = timeoutwrite(timeout,smtpfd,buf,len);
+ if (r <= 0) dropped();
+ return r;
+}
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
+char smtptobuf[1024];
+substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof smtptobuf);
+char smtpfrombuf[128];
+substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf);
+
+stralloc smtptext = {0};
+
+void get(ch)
+char *ch;
+{
+ substdio_get(&smtpfrom,ch,1);
+ if (*ch != '\r')
+ if (smtptext.len < HUGESMTPTEXT)
+ if (!stralloc_append(&smtptext,ch)) temp_nomem();
+}
+
+unsigned long smtpcode()
+{
+ unsigned char ch;
+ unsigned long code;
+
+ if (!stralloc_copys(&smtptext,"")) temp_nomem();
+
+ get(&ch); code = ch - '0';
+ get(&ch); code = code * 10 + (ch - '0');
+ get(&ch); code = code * 10 + (ch - '0');
+ for (;;) {
+ get(&ch);
+ if (ch != '-') break;
+ while (ch != '\n') get(&ch);
+ get(&ch);
+ get(&ch);
+ get(&ch);
+ }
+ while (ch != '\n') get(&ch);
+
+ return code;
+}
+
+void outsmtptext()
+{
+ int i;
+ if (smtptext.s) if (smtptext.len) {
+ out("Remote host said: ");
+ for (i = 0;i < smtptext.len;++i)
+ if (!smtptext.s[i]) smtptext.s[i] = '?';
+ if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0);
+ smtptext.len = 0;
+ }
+}
+
+void quit(prepend,append)
+char *prepend;
+char *append;
+{
+ substdio_putsflush(&smtpto,"QUIT\r\n");
+ /* waiting for remote side is just too ridiculous */
+ out(prepend);
+ outhost();
+ out(append);
+ out(".\n");
+ outsmtptext();
+ zerodie();
+}
+
+void blast()
+{
+ int r;
+ char ch;
+
+ for (;;) {
+ r = substdio_get(&ssin,&ch,1);
+ if (r == 0) break;
+ if (r == -1) temp_read();
+ if (ch == '.')
+ substdio_put(&smtpto,".",1);
+ while (ch != '\n') {
+ substdio_put(&smtpto,&ch,1);
+ r = substdio_get(&ssin,&ch,1);
+ if (r == 0) perm_partialline();
+ if (r == -1) temp_read();
+ }
+ substdio_put(&smtpto,"\r\n",2);
+ }
+
+ flagcritical = 1;
+ substdio_put(&smtpto,".\r\n",3);
+ substdio_flush(&smtpto);
+}
+
+stralloc recip = {0};
+
+void smtp()
+{
+ unsigned long code;
+ int flagbother;
+ int i;
+
+ if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
+
+ substdio_puts(&smtpto,"HELO ");
+ substdio_put(&smtpto,helohost.s,helohost.len);
+ substdio_puts(&smtpto,"\r\n");
+ substdio_flush(&smtpto);
+ if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
+
+ substdio_puts(&smtpto,"MAIL FROM:<");
+ substdio_put(&smtpto,sender.s,sender.len);
+ substdio_puts(&smtpto,">\r\n");
+ substdio_flush(&smtpto);
+ code = smtpcode();
+ if (code >= 500) quit("DConnected to "," but sender was rejected");
+ if (code >= 400) quit("ZConnected to "," but sender was rejected");
+
+ flagbother = 0;
+ for (i = 0;i < reciplist.len;++i) {
+ substdio_puts(&smtpto,"RCPT TO:<");
+ substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len);
+ substdio_puts(&smtpto,">\r\n");
+ substdio_flush(&smtpto);
+ code = smtpcode();
+ if (code >= 500) {
+ out("h"); outhost(); out(" does not like recipient.\n");
+ outsmtptext(); zero();
+ }
+ else if (code >= 400) {
+ out("s"); outhost(); out(" does not like recipient.\n");
+ outsmtptext(); zero();
+ }
+ else {
+ out("r"); zero();
+ flagbother = 1;
+ }
+ }
+ if (!flagbother) quit("DGiving up on ","");
+
+ substdio_putsflush(&smtpto,"DATA\r\n");
+ code = smtpcode();
+ if (code >= 500) quit("D"," failed on DATA command");
+ if (code >= 400) quit("Z"," failed on DATA command");
+
+ blast();
+ code = smtpcode();
+ flagcritical = 0;
+ if (code >= 500) quit("D"," failed after I sent the message");
+ if (code >= 400) quit("Z"," failed after I sent the message");
+ quit("K"," accepted message");
+}
+
+stralloc canonhost = {0};
+stralloc canonbox = {0};
+
+void addrmangle(saout,s,flagalias,flagcname)
+stralloc *saout; /* host has to be canonical, box has to be quoted */
+char *s;
+int *flagalias;
+int flagcname;
+{
+ int j;
+
+ *flagalias = flagcname;
+
+ j = str_rchr(s,'@');
+ if (!s[j]) {
+ if (!stralloc_copys(saout,s)) temp_nomem();
+ return;
+ }
+ if (!stralloc_copys(&canonbox,s)) temp_nomem();
+ canonbox.len = j;
+ if (!quote(saout,&canonbox)) temp_nomem();
+ if (!stralloc_cats(saout,"@")) temp_nomem();
+
+ if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
+ if (flagcname)
+ switch(dns_cname(&canonhost)) {
+ case 0: *flagalias = 0; break;
+ case DNS_MEM: temp_nomem();
+ case DNS_SOFT: temp_dnscanon();
+ case DNS_HARD: ; /* alias loop, not our problem */
+ }
+
+ if (!stralloc_cat(saout,&canonhost)) temp_nomem();
+}
+
+void getcontrols()
+{
+ if (control_init() == -1) temp_control();
+ if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
+ if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
+ temp_control();
+ if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1)
+ temp_control();
+ switch(control_readfile(&routes,"control/smtproutes",0)) {
+ case -1:
+ temp_control();
+ case 0:
+ if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break;
+ case 1:
+ if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
+ }
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ static ipalloc ip = {0};
+ int i;
+ unsigned long random;
+ char **recips;
+ unsigned long prefme;
+ int flagallaliases;
+ int flagalias;
+ char *relayhost;
+
+ sig_pipeignore();
+ if (argc < 4) perm_usage();
+ if (chdir(auto_qmail) == -1) temp_chdir();
+ getcontrols();
+
+
+ if (!stralloc_copys(&host,argv[1])) temp_nomem();
+
+ relayhost = 0;
+ for (i = 0;i <= host.len;++i)
+ if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
+ if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
+ break;
+ if (relayhost && !*relayhost) relayhost = 0;
+
+ if (relayhost) {
+ i = str_chr(relayhost,':');
+ if (relayhost[i]) {
+ scan_ulong(relayhost + i + 1,&port);
+ relayhost[i] = 0;
+ }
+ if (!stralloc_copys(&host,relayhost)) temp_nomem();
+ }
+
+
+ addrmangle(&sender,argv[2],&flagalias,0);
+
+ if (!saa_readyplus(&reciplist,0)) temp_nomem();
+ if (ipme_init() != 1) temp_oserr();
+
+ flagallaliases = 1;
+ recips = argv + 3;
+ while (*recips) {
+ if (!saa_readyplus(&reciplist,1)) temp_nomem();
+ reciplist.sa[reciplist.len] = sauninit;
+ addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost);
+ if (!flagalias) flagallaliases = 0;
+ ++reciplist.len;
+ ++recips;
+ }
+
+
+ random = now() + (getpid() << 16);
+ switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) {
+ case DNS_MEM: temp_nomem();
+ case DNS_SOFT: temp_dns();
+ case DNS_HARD: perm_dns();
+ case 1:
+ if (ip.len <= 0) temp_dns();
+ }
+
+ if (ip.len <= 0) perm_nomx();
+
+ prefme = 100000;
+ for (i = 0;i < ip.len;++i)
+ if (ipme_is(&ip.ix[i].ip))
+ if (ip.ix[i].pref < prefme)
+ prefme = ip.ix[i].pref;
+
+ if (relayhost) prefme = 300000;
+ if (flagallaliases) prefme = 500000;
+
+ for (i = 0;i < ip.len;++i)
+ if (ip.ix[i].pref < prefme)
+ break;
+
+ if (i >= ip.len)
+ perm_ambigmx();
+
+ for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) {
+ if (tcpto(&ip.ix[i].ip)) continue;
+
+ smtpfd = socket(AF_INET,SOCK_STREAM,0);
+ if (smtpfd == -1) temp_oserr();
+
+ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
+ tcpto_err(&ip.ix[i].ip,0);
+ partner = ip.ix[i].ip;
+ smtp(); /* does not return */
+ }
+ tcpto_err(&ip.ix[i].ip,errno == error_timeout);
+ close(smtpfd);
+ }
+
+ temp_noconn();
+}
diff --git a/qmail-rspawn.8 b/qmail-rspawn.8
new file mode 100644
index 0000000..33e8d0d
--- /dev/null
+++ b/qmail-rspawn.8
@@ -0,0 +1,21 @@
+.TH qmail-rspawn 8
+.SH NAME
+qmail-rspawn \- schedule remote deliveries
+.SH SYNOPSIS
+.B qmail-rspawn
+.SH DESCRIPTION
+.B qmail-rspawn
+reads a series of remote delivery commands from descriptor 0,
+invokes
+.B qmail-remote
+to perform the deliveries,
+and prints the results to descriptor 1.
+
+.B qmail-rspawn
+invokes
+.B qmail-remote
+asynchronously,
+so the results may not be in the same order as the commands.
+.SH "SEE ALSO"
+qmail-send(8),
+qmail-remote(8)
diff --git a/qmail-rspawn.c b/qmail-rspawn.c
new file mode 100644
index 0000000..9d838e6
--- /dev/null
+++ b/qmail-rspawn.c
@@ -0,0 +1,103 @@
+#include "fd.h"
+#include "wait.h"
+#include "substdio.h"
+#include "exit.h"
+#include "fork.h"
+#include "error.h"
+#include "tcpto.h"
+
+void initialize(argc,argv)
+int argc;
+char **argv;
+{
+ tcpto_clean();
+}
+
+int truncreport = 0;
+
+void report(ss,wstat,s,len)
+substdio *ss;
+int wstat;
+char *s;
+int len;
+{
+ int j;
+ int k;
+ int result;
+ int orr;
+
+ if (wait_crashed(wstat))
+ { substdio_puts(ss,"Zqmail-remote crashed.\n"); return; }
+ switch(wait_exitcode(wstat))
+ {
+ case 0: break;
+ case 111: substdio_puts(ss,"ZUnable to run qmail-remote.\n"); return;
+ default: substdio_puts(ss,"DUnable to run qmail-remote.\n"); return;
+ }
+ if (!len)
+ { substdio_puts(ss,"Zqmail-remote produced no output.\n"); return; }
+
+ result = -1;
+ j = 0;
+ for (k = 0;k < len;++k)
+ if (!s[k])
+ {
+ if (s[j] == 'K') { result = 1; break; }
+ if (s[j] == 'Z') { result = 0; break; }
+ if (s[j] == 'D') break;
+ j = k + 1;
+ }
+
+ orr = result;
+ switch(s[0])
+ {
+ case 's': orr = 0; break;
+ case 'h': orr = -1;
+ }
+
+ switch(orr)
+ {
+ case 1: substdio_put(ss,"K",1); break;
+ case 0: substdio_put(ss,"Z",1); break;
+ case -1: substdio_put(ss,"D",1); break;
+ }
+
+ for (k = 1;k < len;)
+ if (!s[k++])
+ {
+ substdio_puts(ss,s + 1);
+ if (result <= orr)
+ if (k < len)
+ switch(s[k])
+ {
+ case 'Z': case 'D': case 'K':
+ substdio_puts(ss,s + k + 1);
+ }
+ break;
+ }
+}
+
+int spawn(fdmess,fdout,s,r,at)
+int fdmess; int fdout;
+char *s; char *r; int at;
+{
+ int f;
+ char *(args[5]);
+
+ args[0] = "qmail-remote";
+ args[1] = r + at + 1;
+ args[2] = s;
+ args[3] = r;
+ args[4] = 0;
+
+ if (!(f = vfork()))
+ {
+ if (fd_move(0,fdmess) == -1) _exit(111);
+ if (fd_move(1,fdout) == -1) _exit(111);
+ if (fd_copy(2,1) == -1) _exit(111);
+ execvp(*args,args);
+ if (error_temp(errno)) _exit(111);
+ _exit(100);
+ }
+ return f;
+}
diff --git a/qmail-send.9 b/qmail-send.9
new file mode 100644
index 0000000..acb04d0
--- /dev/null
+++ b/qmail-send.9
@@ -0,0 +1,246 @@
+.TH qmail-send 8
+.SH NAME
+qmail-send \- deliver mail messages from the queue
+.SH SYNOPSIS
+.B qmail-send
+.SH DESCRIPTION
+.B qmail-send
+handles messages placed into the outgoing queue by
+.BR qmail-queue .
+It uses
+.B qmail-lspawn
+to deliver messages to local recipients and
+.B qmail-rspawn
+to deliver messages to remote recipients.
+If a message is temporarily undeliverable to one or more addresses,
+.B qmail-send
+leaves it in the queue and tries the addresses again later.
+
+.B qmail-send
+prints a readable record of its activities to descriptor 0.
+It writes commands to
+.BR qmail-lspawn ,
+.BR qmail-rspawn ,
+and
+.B qmail-clean
+on descriptors 1, 3, and 5,
+and reads responses from descriptors 2, 4, and 6.
+.B qmail-send
+is responsible for avoiding deadlock.
+
+If
+.B qmail-send
+receives a TERM signal,
+it will exit cleanly, after waiting
+(possibly more than a minute)
+for current delivery attempts to finish.
+
+If
+.B qmail-send
+receives an ALRM signal,
+it will reschedule every message in the queue for immediate delivery.
+.SH "CONTROL FILES"
+.B WARNING:
+.B qmail-send
+reads its control files only when it starts.
+If you change the control files,
+you must stop and restart
+.BR qmail-send .
+Exception:
+If
+.B qmail-send
+receives a HUP signal,
+it will reread
+.I locals
+and
+.IR virtualdomains .
+.TP 5
+.I bouncefrom
+Bounce username.
+Default:
+.BR MAILER-DAEMON .
+.TP 5
+.I bouncehost
+Bounce host.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR bouncehost ,
+which is probably not what you want.
+If a message is permanently undeliverable,
+.B qmail-send
+sends a
+.B single-bounce
+notice back to the message's envelope sender.
+The notice is
+.B From: \fIbouncefrom\fB@\fIbouncehost\fR,
+although its envelope sender is empty.
+.TP 5
+.I concurrencylocal
+Maximum number of simultaneous local delivery attempts.
+Default: 10.
+If 0, local deliveries will be put on hold.
+.I concurrencylocal
+is limited at compile time to
+SPAWN.
+.TP 5
+.I concurrencyremote
+Maximum number of simultaneous remote delivery attempts.
+Default: 20.
+If 0, remote deliveries will be put on hold.
+.I concurrencyremote
+is limited at compile time to
+SPAWN.
+.TP 5
+.I doublebouncehost
+Double-bounce host.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR doublebouncehost ,
+which is probably not what you want.
+.TP 5
+.I doublebounceto
+User to receive double-bounces.
+Default:
+.BR postmaster .
+If a single-bounce notice is permanently undeliverable,
+.B qmail-send
+sends a
+.B double-bounce
+notice to
+.IR doublebounceto\fB@\fIdoublebouncehost .
+(If that bounces,
+.B qmail-send
+gives up.)
+.TP 5
+.I envnoathost
+Presumed domain name for addresses without @ signs.
+Default:
+.IR me ,
+if that is supplied;
+otherwise the literal name
+.BR envnoathost ,
+which is probably not what you want.
+If
+.B qmail-send
+sees an envelope recipient address without an @ sign,
+it appends
+.B @\fIenvnoathost\fR.
+.TP 5
+.I locals
+List of domain names that the current host
+receives mail for,
+one per line.
+Default:
+.IR me ,
+if that is supplied;
+otherwise
+.B qmail-send
+refuses to run.
+An address
+.I user@domain
+is considered local if
+.I domain
+is listed in
+.IR locals .
+.TP 5
+.I percenthack
+List of domain names where the percent hack is applied.
+If
+.I domain
+is listed in
+.IR percenthack ,
+any address of the form
+.I user%fqdn@domain
+is rewritten as
+.IR user@fqdn .
+.I user
+may contain %,
+so the percent hack may be applied repeatedly.
+.B qmail-send
+handles
+.I percenthack
+before
+.IR locals .
+.TP 5
+.I queuelifetime
+Number of seconds
+a message can stay in the queue.
+Default: 604800 (one week).
+After this time expires,
+.B qmail-send
+will try the message once more,
+but it will treat any temporary delivery failures as
+permanent failures.
+.TP 5
+.I virtualdomains
+List of virtual users or domains, one per line.
+A virtual user has the form
+.IR user\fB@\fIdomain\fB:\fIprepend ,
+without any extra spaces.
+When
+.B qmail-send
+sees the recipient address
+.IR user\fB@\fIdomain ,
+it converts it to
+.I prepend\fB-\fIuser\fB@\fIdomain
+and treats it as local.
+
+A virtual domain has the form
+.IR domain\fB:\fIprepend .
+It applies to any recipient address at
+.IR domain .
+For example, if
+
+.EX
+ nowhere.mil:joeBREAKfoo
+.EE
+
+is in
+.IR virtualdomains ,
+and a message arrives for
+.BR info@nowhere.mil ,
+.B qmail-send
+will rewrite the recipient address as
+.B joeBREAKfoo-info@nowhere.mil
+and deliver the message locally.
+
+.I virtualdomains
+may contain wildcards:
+
+.EX
+ .fax:uucpBREAKfax
+ :aliasBREAKcatchall
+ .nowhere.mil:joeBREAKfoo-host
+.EE
+
+.I virtualdomains
+may also contain exceptions:
+an empty
+.I prepend
+means that
+.I domain
+is not a virtual domain.
+
+.B qmail-send
+handles
+.I virtualdomains
+after
+.IR locals :
+if a domain is listed in
+.IR locals ,
+.I virtualdomains
+does not apply.
+.SH "SEE ALSO"
+nice(1),
+addresses(5),
+envelopes(5),
+qmail-control(5),
+qmail-log(5),
+qmail-queue(8),
+qmail-clean(8),
+qmail-lspawn(8),
+qmail-rspawn(8)
diff --git a/qmail-send.c b/qmail-send.c
new file mode 100644
index 0000000..c31b522
--- /dev/null
+++ b/qmail-send.c
@@ -0,0 +1,1612 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "readwrite.h"
+#include "sig.h"
+#include "direntry.h"
+#include "control.h"
+#include "select.h"
+#include "open.h"
+#include "seek.h"
+#include "exit.h"
+#include "lock.h"
+#include "ndelay.h"
+#include "now.h"
+#include "getln.h"
+#include "substdio.h"
+#include "alloc.h"
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "byte.h"
+#include "fmt.h"
+#include "scan.h"
+#include "case.h"
+#include "auto_qmail.h"
+#include "trigger.h"
+#include "newfield.h"
+#include "quote.h"
+#include "qmail.h"
+#include "qsutil.h"
+#include "prioq.h"
+#include "constmap.h"
+#include "fmtqfn.h"
+#include "readsubdir.h"
+
+/* critical timing feature #1: if not triggered, do not busy-loop */
+/* critical timing feature #2: if triggered, respond within fixed time */
+/* important timing feature: when triggered, respond instantly */
+#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
+#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
+#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
+#define SLEEP_CLEANUP 76431 /* time between cleanups */
+#define SLEEP_SYSFAIL 123
+#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */
+
+int lifetime = 604800;
+
+stralloc percenthack = {0};
+struct constmap mappercenthack;
+stralloc locals = {0};
+struct constmap maplocals;
+stralloc vdoms = {0};
+struct constmap mapvdoms;
+stralloc envnoathost = {0};
+stralloc bouncefrom = {0};
+stralloc bouncehost = {0};
+stralloc doublebounceto = {0};
+stralloc doublebouncehost = {0};
+
+char strnum2[FMT_ULONG];
+char strnum3[FMT_ULONG];
+
+#define CHANNELS 2
+char *chanaddr[CHANNELS] = { "local/", "remote/" };
+char *chanstatusmsg[CHANNELS] = { " local ", " remote " };
+char *tochan[CHANNELS] = { " to local ", " to remote " };
+int chanfdout[CHANNELS] = { 1, 3 };
+int chanfdin[CHANNELS] = { 2, 4 };
+int chanskip[CHANNELS] = { 10, 20 };
+
+int flagexitasap = 0; void sigterm() { flagexitasap = 1; }
+int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }
+int flagreadasap = 0; void sighup() { flagreadasap = 1; }
+
+void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n");
+ flagexitasap = 1; }
+
+int flagspawnalive[CHANNELS];
+void spawndied(c) int c; { log1("alert: oh no! lost spawn connection! dying...\n");
+ flagspawnalive[c] = 0; flagexitasap = 1; }
+
+#define REPORTMAX 10000
+
+datetime_sec recent;
+
+
+/* this file is too long ----------------------------------------- FILENAMES */
+
+stralloc fn = {0};
+stralloc fn2 = {0};
+char fnmake_strnum[FMT_ULONG];
+
+void fnmake_init()
+{
+ while (!stralloc_ready(&fn,FMTQFN)) nomem();
+ while (!stralloc_ready(&fn2,FMTQFN)) nomem();
+}
+
+void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }
+void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }
+void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }
+void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }
+void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }
+void fnmake2_bounce(id) unsigned long id;
+{ fn2.len = fmtqfn(fn2.s,"bounce/",id,0); }
+void fnmake_chanaddr(id,c) unsigned long id; int c;
+{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
+
+
+/* this file is too long ----------------------------------------- REWRITING */
+
+stralloc rwline = {0};
+
+/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
+/* may trash recip. must set up rwline, between a T and a \0. */
+int rewrite(recip)
+char *recip;
+{
+ int i;
+ int j;
+ char *x;
+ static stralloc addr = {0};
+ int at;
+
+ if (!stralloc_copys(&rwline,"T")) return 0;
+ if (!stralloc_copys(&addr,recip)) return 0;
+
+ i = byte_rchr(addr.s,addr.len,'@');
+ if (i == addr.len) {
+ if (!stralloc_cats(&addr,"@")) return 0;
+ if (!stralloc_cat(&addr,&envnoathost)) return 0;
+ }
+
+ while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
+ j = byte_rchr(addr.s,i,'%');
+ if (j == i) break;
+ addr.len = i;
+ i = j;
+ addr.s[i] = '@';
+ }
+
+ at = byte_rchr(addr.s,addr.len,'@');
+
+ if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
+ if (!stralloc_cat(&rwline,&addr)) return 0;
+ if (!stralloc_0(&rwline)) return 0;
+ return 1;
+ }
+
+ for (i = 0;i <= addr.len;++i)
+ if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
+ if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) {
+ if (!*x) break;
+ if (!stralloc_cats(&rwline,x)) return 0;
+ if (!stralloc_cats(&rwline,"-")) return 0;
+ if (!stralloc_cat(&rwline,&addr)) return 0;
+ if (!stralloc_0(&rwline)) return 0;
+ return 1;
+ }
+
+ if (!stralloc_cat(&rwline,&addr)) return 0;
+ if (!stralloc_0(&rwline)) return 0;
+ return 2;
+}
+
+void senderadd(sa,sender,recip)
+stralloc *sa;
+char *sender;
+char *recip;
+{
+ int i;
+ int j;
+ int k;
+
+ i = str_len(sender);
+ if (i >= 4)
+ if (str_equal(sender + i - 4,"-@[]"))
+ {
+ j = byte_rchr(sender,i - 4,'@');
+ k = str_rchr(recip,'@');
+ if (recip[k] && (j + 5 <= i))
+ {
+ /* owner-@host-@[] -> owner-recipbox=reciphost@host */
+ while (!stralloc_catb(sa,sender,j)) nomem();
+ while (!stralloc_catb(sa,recip,k)) nomem();
+ while (!stralloc_cats(sa,"=")) nomem();
+ while (!stralloc_cats(sa,recip + k + 1)) nomem();
+ while (!stralloc_cats(sa,"@")) nomem();
+ while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem();
+ return;
+ }
+ }
+ while (!stralloc_cats(sa,sender)) nomem();
+}
+
+
+/* this file is too long ---------------------------------------------- INFO */
+
+int getinfo(sa,dt,id)
+stralloc *sa;
+datetime_sec *dt;
+unsigned long id;
+{
+ int fdinfo;
+ struct stat st;
+ static stralloc line = {0};
+ int match;
+ substdio ss;
+ char buf[128];
+
+ fnmake_info(id);
+ fdinfo = open_read(fn.s);
+ if (fdinfo == -1) return 0;
+ if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; }
+ substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf));
+ if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; }
+ close(fdinfo);
+ if (!match) return 0;
+ if (line.s[0] != 'F') return 0;
+
+ *dt = st.st_mtime;
+ while (!stralloc_copys(sa,line.s + 1)) nomem();
+ while (!stralloc_0(sa)) nomem();
+ return 1;
+}
+
+
+/* this file is too long ------------------------------------- COMMUNICATION */
+
+substdio sstoqc; char sstoqcbuf[1024];
+substdio ssfromqc; char ssfromqcbuf[1024];
+stralloc comm_buf[CHANNELS] = { {0}, {0} };
+int comm_pos[CHANNELS];
+
+void comm_init()
+{
+ int c;
+ substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf));
+ substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf));
+ for (c = 0;c < CHANNELS;++c)
+ if (ndelay_on(chanfdout[c]) == -1)
+ /* this is so stupid: NDELAY semantics should be default on write */
+ spawndied(c); /* drastic, but better than risking deadlock */
+}
+
+int comm_canwrite(c)
+int c;
+{
+ /* XXX: could allow a bigger buffer; say 10 recipients */
+ if (comm_buf[c].s && comm_buf[c].len) return 0;
+ return 1;
+}
+
+void comm_write(c,delnum,id,sender,recip)
+int c;
+int delnum;
+unsigned long id;
+char *sender;
+char *recip;
+{
+ char ch;
+ if (comm_buf[c].s && comm_buf[c].len) return;
+ while (!stralloc_copys(&comm_buf[c],"")) nomem();
+ ch = delnum;
+ while (!stralloc_append(&comm_buf[c],&ch)) nomem();
+ fnmake_split(id);
+ while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
+ while (!stralloc_0(&comm_buf[c])) nomem();
+ senderadd(&comm_buf[c],sender,recip);
+ while (!stralloc_0(&comm_buf[c])) nomem();
+ while (!stralloc_cats(&comm_buf[c],recip)) nomem();
+ while (!stralloc_0(&comm_buf[c])) nomem();
+ comm_pos[c] = 0;
+}
+
+void comm_selprep(nfds,wfds)
+int *nfds;
+fd_set *wfds;
+{
+ int c;
+ for (c = 0;c < CHANNELS;++c)
+ if (flagspawnalive[c])
+ if (comm_buf[c].s && comm_buf[c].len)
+ {
+ FD_SET(chanfdout[c],wfds);
+ if (*nfds <= chanfdout[c])
+ *nfds = chanfdout[c] + 1;
+ }
+}
+
+void comm_do(wfds)
+fd_set *wfds;
+{
+ int c;
+ for (c = 0;c < CHANNELS;++c)
+ if (flagspawnalive[c])
+ if (comm_buf[c].s && comm_buf[c].len)
+ if (FD_ISSET(chanfdout[c],wfds))
+ {
+ int w;
+ int len;
+ len = comm_buf[c].len;
+ w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]);
+ if (w <= 0)
+ {
+ if ((w == -1) && (errno == error_pipe))
+ spawndied(c);
+ else
+ continue; /* kernel select() bug; can't avoid busy-looping */
+ }
+ else
+ {
+ comm_pos[c] += w;
+ if (comm_pos[c] == len)
+ comm_buf[c].len = 0;
+ }
+ }
+}
+
+
+/* this file is too long ------------------------------------------ CLEANUPS */
+
+int flagcleanup; /* if 1, cleanupdir is initialized and ready */
+readsubdir cleanupdir;
+datetime_sec cleanuptime;
+
+void cleanup_init()
+{
+ flagcleanup = 0;
+ cleanuptime = now();
+}
+
+void cleanup_selprep(wakeup)
+datetime_sec *wakeup;
+{
+ if (flagcleanup) *wakeup = 0;
+ if (*wakeup > cleanuptime) *wakeup = cleanuptime;
+}
+
+void cleanup_do()
+{
+ char ch;
+ struct stat st;
+ unsigned long id;
+
+ if (!flagcleanup)
+ {
+ if (recent < cleanuptime) return;
+ readsubdir_init(&cleanupdir,"mess",pausedir);
+ flagcleanup = 1;
+ }
+
+ switch(readsubdir_next(&cleanupdir,&id))
+ {
+ case 1:
+ break;
+ case 0:
+ flagcleanup = 0;
+ cleanuptime = recent + SLEEP_CLEANUP;
+ default:
+ return;
+ }
+
+ fnmake_mess(id);
+ if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */
+ if (recent <= st.st_atime + OSSIFIED) return;
+
+ fnmake_info(id);
+ if (stat(fn.s,&st) == 0) return;
+ if (errno != error_noent) return;
+ fnmake_todo(id);
+ if (stat(fn.s,&st) == 0) return;
+ if (errno != error_noent) return;
+
+ fnmake_foop(id);
+ if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
+ if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
+ if (ch != '+')
+ log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
+}
+
+
+/* this file is too long ----------------------------------- PRIORITY QUEUES */
+
+prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */
+prioq pqchan[CHANNELS] = { {0}, {0} };
+/* pqchan 0: -todo +info +local ?remote */
+/* pqchan 1: -todo +info ?local +remote */
+prioq pqfail = {0}; /* stat() failure; has to be pqadded again */
+
+void pqadd(id)
+unsigned long id;
+{
+ struct prioq_elt pe;
+ struct prioq_elt pechan[CHANNELS];
+ int flagchan[CHANNELS];
+ struct stat st;
+ int c;
+
+#define CHECKSTAT if (errno != error_noent) goto fail;
+
+ fnmake_info(id);
+ if (stat(fn.s,&st) == -1)
+ {
+ CHECKSTAT
+ return; /* someone yanking our chain */
+ }
+
+ fnmake_todo(id);
+ if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */
+ CHECKSTAT
+
+ for (c = 0;c < CHANNELS;++c)
+ {
+ fnmake_chanaddr(id,c);
+ if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT }
+ else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; }
+ }
+
+ for (c = 0;c < CHANNELS;++c)
+ if (flagchan[c])
+ while (!prioq_insert(&pqchan[c],&pechan[c])) nomem();
+
+ for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
+ if (c == CHANNELS)
+ {
+ pe.id = id; pe.dt = now();
+ while (!prioq_insert(&pqdone,&pe)) nomem();
+ }
+
+ return;
+
+ fail:
+ log3("warning: unable to stat ",fn.s,"; will try again later\n");
+ pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
+ while (!prioq_insert(&pqfail,&pe)) nomem();
+}
+
+void pqstart()
+{
+ readsubdir rs;
+ int x;
+ unsigned long id;
+
+ readsubdir_init(&rs,"info",pausedir);
+
+ while (x = readsubdir_next(&rs,&id))
+ if (x > 0)
+ pqadd(id);
+}
+
+void pqfinish()
+{
+ int c;
+ struct prioq_elt pe;
+ time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */
+
+ for (c = 0;c < CHANNELS;++c)
+ while (prioq_min(&pqchan[c],&pe))
+ {
+ prioq_delmin(&pqchan[c]);
+ fnmake_chanaddr(pe.id,c);
+ ut[0] = ut[1] = pe.dt;
+ if (utime(fn.s,ut) == -1)
+ log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n");
+ }
+}
+
+void pqrun()
+{
+ int c;
+ int i;
+ for (c = 0;c < CHANNELS;++c)
+ if (pqchan[c].p)
+ if (pqchan[c].len)
+ for (i = 0;i < pqchan[c].len;++i)
+ pqchan[c].p[i].dt = recent;
+}
+
+
+/* this file is too long ---------------------------------------------- JOBS */
+
+struct job
+ {
+ int refs; /* if 0, this struct is unused */
+ unsigned long id;
+ int channel;
+ datetime_sec retry;
+ stralloc sender;
+ int numtodo;
+ int flaghiteof;
+ int flagdying;
+ }
+;
+
+int numjobs;
+struct job *jo;
+
+void job_init()
+{
+ int j;
+ while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem();
+ for (j = 0;j < numjobs;++j)
+ {
+ jo[j].refs = 0;
+ jo[j].sender.s = 0;
+ }
+}
+
+int job_avail()
+{
+ int j;
+ for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1;
+ return 0;
+}
+
+int job_open(id,channel)
+unsigned long id;
+int channel;
+{
+ int j;
+ for (j = 0;j < numjobs;++j) if (!jo[j].refs) break;
+ if (j == numjobs) return -1;
+ jo[j].refs = 1;
+ jo[j].id = id;
+ jo[j].channel = channel;
+ jo[j].numtodo = 0;
+ jo[j].flaghiteof = 0;
+ return j;
+}
+
+void job_close(j)
+int j;
+{
+ struct prioq_elt pe;
+ struct stat st;
+
+ if (0 < --jo[j].refs) return;
+
+ pe.id = jo[j].id;
+ pe.dt = jo[j].retry;
+ if (jo[j].flaghiteof && !jo[j].numtodo)
+ {
+ fnmake_chanaddr(jo[j].id,jo[j].channel);
+ if (unlink(fn.s) == -1)
+ {
+ log3("warning: unable to unlink ",fn.s,"; will try again later\n");
+ pe.dt = now() + SLEEP_SYSFAIL;
+ }
+ else
+ {
+ int c;
+ for (c = 0;c < CHANNELS;++c) if (c != jo[j].channel)
+ {
+ fnmake_chanaddr(jo[j].id,c);
+ if (stat(fn.s,&st) == 0) return; /* more channels going */
+ if (errno != error_noent)
+ {
+ log3("warning: unable to stat ",fn.s,"\n");
+ break; /* this is the only reason for HOPEFULLY */
+ }
+ }
+ pe.dt = now();
+ while (!prioq_insert(&pqdone,&pe)) nomem();
+ return;
+ }
+ }
+
+ while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem();
+}
+
+
+/* this file is too long ------------------------------------------- BOUNCES */
+
+char *stripvdomprepend(recip)
+char *recip;
+{
+ int i;
+ char *domain;
+ int domainlen;
+ char *prepend;
+
+ i = str_rchr(recip,'@');
+ if (!recip[i]) return recip;
+ domain = recip + i + 1;
+ domainlen = str_len(domain);
+
+ for (i = 0;i <= domainlen;++i)
+ if ((i == 0) || (i == domainlen) || (domain[i] == '.'))
+ if (prepend = constmap(&mapvdoms,domain + i,domainlen - i))
+ {
+ if (!*prepend) break;
+ i = str_len(prepend);
+ if (str_diffn(recip,prepend,i)) break;
+ if (recip[i] != '-') break;
+ return recip + i + 1;
+ }
+ return recip;
+}
+
+stralloc bouncetext = {0};
+
+void addbounce(id,recip,report)
+unsigned long id;
+char *recip;
+char *report;
+{
+ int fd;
+ int pos;
+ int w;
+ while (!stralloc_copys(&bouncetext,"<")) nomem();
+ while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem();
+ for (pos = 0;pos < bouncetext.len;++pos)
+ if (bouncetext.s[pos] == '\n')
+ bouncetext.s[pos] = '_';
+ while (!stralloc_cats(&bouncetext,">:\n")) nomem();
+ while (!stralloc_cats(&bouncetext,report)) nomem();
+ if (report[0])
+ if (report[str_len(report) - 1] != '\n')
+ while (!stralloc_cats(&bouncetext,"\n")) nomem();
+ for (pos = bouncetext.len - 2;pos > 0;--pos)
+ if (bouncetext.s[pos] == '\n')
+ if (bouncetext.s[pos - 1] == '\n')
+ bouncetext.s[pos] = '/';
+ while (!stralloc_cats(&bouncetext,"\n")) nomem();
+ fnmake2_bounce(id);
+ for (;;)
+ {
+ fd = open_append(fn2.s);
+ if (fd != -1) break;
+ log1("alert: unable to append to bounce message; HELP! sleeping...\n");
+ sleep(10);
+ }
+ pos = 0;
+ while (pos < bouncetext.len)
+ {
+ w = write(fd,bouncetext.s + pos,bouncetext.len - pos);
+ if (w <= 0)
+ {
+ log1("alert: unable to append to bounce message; HELP! sleeping...\n");
+ sleep(10);
+ }
+ else
+ pos += w;
+ }
+ close(fd);
+}
+
+int injectbounce(id)
+unsigned long id;
+{
+ struct qmail qqt;
+ struct stat st;
+ char *bouncesender;
+ char *bouncerecip;
+ int r;
+ int fd;
+ substdio ssread;
+ char buf[128];
+ char inbuf[128];
+ static stralloc sender = {0};
+ static stralloc quoted = {0};
+ datetime_sec birth;
+ unsigned long qp;
+
+ if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */
+
+ /* owner-@host-@[] -> owner-@host */
+ if (sender.len >= 5)
+ if (str_equal(sender.s + sender.len - 5,"-@[]"))
+ {
+ sender.len -= 4;
+ sender.s[sender.len - 1] = 0;
+ }
+
+ fnmake2_bounce(id);
+ fnmake_mess(id);
+ if (stat(fn2.s,&st) == -1)
+ {
+ if (errno == error_noent)
+ return 1;
+ log3("warning: unable to stat ",fn2.s,"\n");
+ return 0;
+ }
+ if (str_equal(sender.s,"#@[]"))
+ log3("triple bounce: discarding ",fn2.s,"\n");
+ else
+ {
+ if (qmail_open(&qqt) == -1)
+ { log1("warning: unable to start qmail-queue, will try later\n"); return 0; }
+ qp = qmail_qp(&qqt);
+
+ if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
+ else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
+
+ while (!newfield_datemake(now())) nomem();
+ qmail_put(&qqt,newfield_date.s,newfield_date.len);
+ qmail_puts(&qqt,"From: ");
+ while (!quote(&quoted,&bouncefrom)) nomem();
+ qmail_put(&qqt,quoted.s,quoted.len);
+ qmail_puts(&qqt,"@");
+ qmail_put(&qqt,bouncehost.s,bouncehost.len);
+ qmail_puts(&qqt,"\nTo: ");
+ while (!quote2(&quoted,bouncerecip)) nomem();
+ qmail_put(&qqt,quoted.s,quoted.len);
+ qmail_puts(&qqt,"\n\
+Subject: failure notice\n\
+\n\
+Hi. This is the qmail-send program at ");
+ qmail_put(&qqt,bouncehost.s,bouncehost.len);
+ qmail_puts(&qqt,*sender.s ? ".\n\
+I'm afraid I wasn't able to deliver your message to the following addresses.\n\
+This is a permanent error; I've given up. Sorry it didn't work out.\n\
+\n\
+" : ".\n\
+I tried to deliver a bounce message to this address, but the bounce bounced!\n\
+\n\
+");
+
+ fd = open_read(fn2.s);
+ if (fd == -1)
+ qmail_fail(&qqt);
+ else
+ {
+ substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
+ while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
+ qmail_put(&qqt,buf,r);
+ close(fd);
+ if (r == -1)
+ qmail_fail(&qqt);
+ }
+
+ qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n");
+ qmail_puts(&qqt,"Return-Path: <");
+ while (!quote2(&quoted,sender.s)) nomem();
+ qmail_put(&qqt,quoted.s,quoted.len);
+ qmail_puts(&qqt,">\n");
+
+ fd = open_read(fn.s);
+ if (fd == -1)
+ qmail_fail(&qqt);
+ else
+ {
+ substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
+ while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
+ qmail_put(&qqt,buf,r);
+ close(fd);
+ if (r == -1)
+ qmail_fail(&qqt);
+ }
+
+ qmail_from(&qqt,bouncesender);
+ qmail_to(&qqt,bouncerecip);
+ if (*qmail_close(&qqt))
+ { log1("warning: trouble injecting bounce message, will try later\n"); return 0; }
+
+ strnum2[fmt_ulong(strnum2,id)] = 0;
+ log2("bounce msg ",strnum2);
+ strnum2[fmt_ulong(strnum2,qp)] = 0;
+ log3(" qp ",strnum2,"\n");
+ }
+ if (unlink(fn2.s) == -1)
+ {
+ log3("warning: unable to unlink ",fn2.s,"\n");
+ return 0;
+ }
+ return 1;
+}
+
+
+/* this file is too long ---------------------------------------- DELIVERIES */
+
+struct del
+ {
+ int used;
+ int j;
+ unsigned long delid;
+ seek_pos mpos;
+ stralloc recip;
+ }
+;
+
+unsigned long masterdelid = 1;
+unsigned int concurrency[CHANNELS] = { 10, 20 };
+unsigned int concurrencyused[CHANNELS] = { 0, 0 };
+struct del *d[CHANNELS];
+stralloc dline[CHANNELS];
+char delbuf[2048];
+
+void del_status()
+{
+ int c;
+
+ log1("status:");
+ for (c = 0;c < CHANNELS;++c) {
+ strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0;
+ strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0;
+ log2(chanstatusmsg[c],strnum2);
+ log2("/",strnum3);
+ }
+ if (flagexitasap) log1(" exitasap");
+ log1("\n");
+}
+
+void del_init()
+{
+ int c;
+ int i;
+ for (c = 0;c < CHANNELS;++c)
+ {
+ flagspawnalive[c] = 1;
+ while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del))))
+ nomem();
+ for (i = 0;i < concurrency[c];++i)
+ { d[c][i].used = 0; d[c][i].recip.s = 0; }
+ dline[c].s = 0;
+ while (!stralloc_copys(&dline[c],"")) nomem();
+ }
+ del_status();
+}
+
+int del_canexit()
+{
+ int c;
+ for (c = 0;c < CHANNELS;++c)
+ if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */
+ if (concurrencyused[c]) return 0;
+ return 1;
+}
+
+int del_avail(c)
+int c;
+{
+ return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]);
+}
+
+void del_start(j,mpos,recip)
+int j;
+seek_pos mpos;
+char *recip;
+{
+ int i;
+ int c;
+
+ c = jo[j].channel;
+ if (!flagspawnalive[c]) return;
+ if (!comm_canwrite(c)) return;
+
+ for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) break;
+ if (i == concurrency[c]) return;
+
+ if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; }
+ if (!stralloc_0(&d[c][i].recip)) { nomem(); return; }
+ d[c][i].j = j; ++jo[j].refs;
+ d[c][i].delid = masterdelid++;
+ d[c][i].mpos = mpos;
+ d[c][i].used = 1; ++concurrencyused[c];
+
+ comm_write(c,i,jo[j].id,jo[j].sender.s,recip);
+
+ strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0;
+ strnum3[fmt_ulong(strnum3,jo[j].id)] = 0;
+ log2("starting delivery ",strnum2);
+ log3(": msg ",strnum3,tochan[c]);
+ logsafe(recip);
+ log1("\n");
+ del_status();
+}
+
+void markdone(c,id,pos)
+int c;
+unsigned long id;
+seek_pos pos;
+{
+ struct stat st;
+ int fd;
+ fnmake_chanaddr(id,c);
+ for (;;)
+ {
+ fd = open_write(fn.s);
+ if (fd == -1) break;
+ if (fstat(fd,&st) == -1) { close(fd); break; }
+ if (seek_set(fd,pos) == -1) { close(fd); break; }
+ if (write(fd,"D",1) != 1) { close(fd); break; }
+ /* further errors -> double delivery without us knowing about it, oh well */
+ close(fd);
+ return;
+ }
+ log3("warning: trouble marking ",fn.s,"; message will be delivered twice!\n");
+}
+
+void del_dochan(c)
+int c;
+{
+ int r;
+ char ch;
+ int i;
+ int delnum;
+ r = read(chanfdin[c],delbuf,sizeof(delbuf));
+ if (r == -1) return;
+ if (r == 0) { spawndied(c); return; }
+ for (i = 0;i < r;++i)
+ {
+ ch = delbuf[i];
+ while (!stralloc_append(&dline[c],&ch)) nomem();
+ if (dline[c].len > REPORTMAX)
+ dline[c].len = REPORTMAX;
+ /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
+ /* but from a security point of view, we don't trust rspawn */
+ if (!ch && (dline[c].len > 1))
+ {
+ delnum = (unsigned int) (unsigned char) dline[c].s[0];
+ if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
+ log1("warning: internal error: delivery report out of range\n");
+ else
+ {
+ strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
+ if (dline[c].s[1] == 'Z')
+ if (jo[d[c][delnum].j].flagdying)
+ {
+ dline[c].s[1] = 'D';
+ --dline[c].len;
+ while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
+ while (!stralloc_0(&dline[c])) nomem();
+ }
+ switch(dline[c].s[1])
+ {
+ case 'K':
+ log3("delivery ",strnum3,": success: ");
+ logsafe(dline[c].s + 2);
+ log1("\n");
+ markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
+ --jo[d[c][delnum].j].numtodo;
+ break;
+ case 'Z':
+ log3("delivery ",strnum3,": deferral: ");
+ logsafe(dline[c].s + 2);
+ log1("\n");
+ break;
+ case 'D':
+ log3("delivery ",strnum3,": failure: ");
+ logsafe(dline[c].s + 2);
+ log1("\n");
+ addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
+ markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
+ --jo[d[c][delnum].j].numtodo;
+ break;
+ default:
+ log3("delivery ",strnum3,": report mangled, will defer\n");
+ }
+ job_close(d[c][delnum].j);
+ d[c][delnum].used = 0; --concurrencyused[c];
+ del_status();
+ }
+ dline[c].len = 0;
+ }
+ }
+}
+
+void del_selprep(nfds,rfds)
+int *nfds;
+fd_set *rfds;
+{
+ int c;
+ for (c = 0;c < CHANNELS;++c)
+ if (flagspawnalive[c])
+ {
+ FD_SET(chanfdin[c],rfds);
+ if (*nfds <= chanfdin[c])
+ *nfds = chanfdin[c] + 1;
+ }
+}
+
+void del_do(rfds)
+fd_set *rfds;
+{
+ int c;
+ for (c = 0;c < CHANNELS;++c)
+ if (flagspawnalive[c])
+ if (FD_ISSET(chanfdin[c],rfds))
+ del_dochan(c);
+}
+
+
+/* this file is too long -------------------------------------------- PASSES */
+
+struct
+ {
+ unsigned long id; /* if 0, need a new pass */
+ int j; /* defined if id; job number */
+ int fd; /* defined if id; reading from {local,remote} */
+ seek_pos mpos; /* defined if id; mark position */
+ substdio ss;
+ char buf[128];
+ }
+pass[CHANNELS];
+
+void pass_init()
+{
+ int c;
+ for (c = 0;c < CHANNELS;++c) pass[c].id = 0;
+}
+
+void pass_selprep(wakeup)
+datetime_sec *wakeup;
+{
+ int c;
+ struct prioq_elt pe;
+ if (flagexitasap) return;
+ for (c = 0;c < CHANNELS;++c)
+ if (pass[c].id)
+ if (del_avail(c))
+ { *wakeup = 0; return; }
+ if (job_avail())
+ for (c = 0;c < CHANNELS;++c)
+ if (!pass[c].id)
+ if (prioq_min(&pqchan[c],&pe))
+ if (*wakeup > pe.dt)
+ *wakeup = pe.dt;
+ if (prioq_min(&pqfail,&pe))
+ if (*wakeup > pe.dt)
+ *wakeup = pe.dt;
+ if (prioq_min(&pqdone,&pe))
+ if (*wakeup > pe.dt)
+ *wakeup = pe.dt;
+}
+
+static datetime_sec squareroot(x) /* result^2 <= x < (result + 1)^2 */
+datetime_sec x; /* assuming: >= 0 */
+{
+ datetime_sec y;
+ datetime_sec yy;
+ datetime_sec y21;
+ int j;
+
+ y = 0; yy = 0;
+ for (j = 15;j >= 0;--j)
+ {
+ y21 = (y << (j + 1)) + (1 << (j + j));
+ if (y21 <= x - yy) { y += (1 << j); yy += y21; }
+ }
+ return y;
+}
+
+datetime_sec nextretry(birth,c)
+datetime_sec birth;
+int c;
+{
+ int n;
+
+ if (birth > recent) n = 0;
+ else n = squareroot(recent - birth); /* no need to add fuzz to recent */
+ n += chanskip[c];
+ return birth + n * n;
+}
+
+void pass_dochan(c)
+int c;
+{
+ datetime_sec birth;
+ struct prioq_elt pe;
+ static stralloc line = {0};
+ int match;
+
+ if (flagexitasap) return;
+
+ if (!pass[c].id)
+ {
+ if (!job_avail()) return;
+ if (!prioq_min(&pqchan[c],&pe)) return;
+ if (pe.dt > recent) return;
+ fnmake_chanaddr(pe.id,c);
+
+ prioq_delmin(&pqchan[c]);
+ pass[c].mpos = 0;
+ pass[c].fd = open_read(fn.s);
+ if (pass[c].fd == -1) goto trouble;
+ if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; }
+ pass[c].id = pe.id;
+ substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf));
+ pass[c].j = job_open(pe.id,c);
+ jo[pass[c].j].retry = nextretry(birth,c);
+ jo[pass[c].j].flagdying = (recent > birth + lifetime);
+ while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem();
+ }
+
+ if (!del_avail(c)) return;
+
+ if (getln(&pass[c].ss,&line,&match,'\0') == -1)
+ {
+ fnmake_chanaddr(pass[c].id,c);
+ log3("warning: trouble reading ",fn.s,"; will try again later\n");
+ close(pass[c].fd);
+ job_close(pass[c].j);
+ pass[c].id = 0;
+ return;
+ }
+ if (!match)
+ {
+ close(pass[c].fd);
+ jo[pass[c].j].flaghiteof = 1;
+ job_close(pass[c].j);
+ pass[c].id = 0;
+ return;
+ }
+ switch(line.s[0])
+ {
+ case 'T':
+ ++jo[pass[c].j].numtodo;
+ del_start(pass[c].j,pass[c].mpos,line.s + 1);
+ break;
+ case 'D':
+ break;
+ default:
+ fnmake_chanaddr(pass[c].id,c);
+ log3("warning: unknown record type in ",fn.s,"!\n");
+ close(pass[c].fd);
+ job_close(pass[c].j);
+ pass[c].id = 0;
+ return;
+ }
+
+ pass[c].mpos += line.len;
+ return;
+
+ trouble:
+ log3("warning: trouble opening ",fn.s,"; will try again later\n");
+ pe.dt = recent + SLEEP_SYSFAIL;
+ while (!prioq_insert(&pqchan[c],&pe)) nomem();
+}
+
+void messdone(id)
+unsigned long id;
+{
+ char ch;
+ int c;
+ struct prioq_elt pe;
+ struct stat st;
+
+ for (c = 0;c < CHANNELS;++c)
+ {
+ fnmake_chanaddr(id,c);
+ if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */
+ if (errno != error_noent)
+ {
+ log3("warning: unable to stat ",fn.s,"; will try again later\n");
+ goto fail;
+ }
+ }
+
+ fnmake_todo(id);
+ if (stat(fn.s,&st) == 0) return;
+ if (errno != error_noent)
+ {
+ log3("warning: unable to stat ",fn.s,"; will try again later\n");
+ goto fail;
+ }
+
+ fnmake_info(id);
+ if (stat(fn.s,&st) == -1)
+ {
+ if (errno == error_noent) return;
+ log3("warning: unable to stat ",fn.s,"; will try again later\n");
+ goto fail;
+ }
+
+ /* -todo +info -local -remote ?bounce */
+ if (!injectbounce(id))
+ goto fail; /* injectbounce() produced error message */
+
+ strnum3[fmt_ulong(strnum3,id)] = 0;
+ log3("end msg ",strnum3,"\n");
+
+ /* -todo +info -local -remote -bounce */
+ fnmake_info(id);
+ if (unlink(fn.s) == -1)
+ {
+ log3("warning: unable to unlink ",fn.s,"; will try again later\n");
+ goto fail;
+ }
+
+ /* -todo -info -local -remote -bounce; we can relax */
+ fnmake_foop(id);
+ if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
+ if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
+ if (ch != '+')
+ log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
+
+ return;
+
+ fail:
+ pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
+ while (!prioq_insert(&pqdone,&pe)) nomem();
+}
+
+void pass_do()
+{
+ int c;
+ struct prioq_elt pe;
+
+ for (c = 0;c < CHANNELS;++c) pass_dochan(c);
+ if (prioq_min(&pqfail,&pe))
+ if (pe.dt <= recent)
+ {
+ prioq_delmin(&pqfail);
+ pqadd(pe.id);
+ }
+ if (prioq_min(&pqdone,&pe))
+ if (pe.dt <= recent)
+ {
+ prioq_delmin(&pqdone);
+ messdone(pe.id);
+ }
+}
+
+
+/* this file is too long ---------------------------------------------- TODO */
+
+datetime_sec nexttodorun;
+DIR *tododir; /* if 0, have to opendir again */
+stralloc todoline = {0};
+char todobuf[SUBSTDIO_INSIZE];
+char todobufinfo[512];
+char todobufchan[CHANNELS][1024];
+
+void todo_init()
+{
+ tododir = 0;
+ nexttodorun = now();
+ trigger_set();
+}
+
+void todo_selprep(nfds,rfds,wakeup)
+int *nfds;
+fd_set *rfds;
+datetime_sec *wakeup;
+{
+ if (flagexitasap) return;
+ trigger_selprep(nfds,rfds);
+ if (tododir) *wakeup = 0;
+ if (*wakeup > nexttodorun) *wakeup = nexttodorun;
+}
+
+void todo_do(rfds)
+fd_set *rfds;
+{
+ struct stat st;
+ substdio ss; int fd;
+ substdio ssinfo; int fdinfo;
+ substdio sschan[CHANNELS];
+ int fdchan[CHANNELS];
+ int flagchan[CHANNELS];
+ struct prioq_elt pe;
+ char ch;
+ int match;
+ unsigned long id;
+ unsigned int len;
+ direntry *d;
+ int c;
+ unsigned long uid;
+ unsigned long pid;
+
+ fd = -1;
+ fdinfo = -1;
+ for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
+
+ if (flagexitasap) return;
+
+ if (!tododir)
+ {
+ if (!trigger_pulled(rfds))
+ if (recent < nexttodorun)
+ return;
+ trigger_set();
+ tododir = opendir("todo");
+ if (!tododir)
+ {
+ pausedir("todo");
+ return;
+ }
+ nexttodorun = recent + SLEEP_TODO;
+ }
+
+ d = readdir(tododir);
+ if (!d)
+ {
+ closedir(tododir);
+ tododir = 0;
+ return;
+ }
+ if (str_equal(d->d_name,".")) return;
+ if (str_equal(d->d_name,"..")) return;
+ len = scan_ulong(d->d_name,&id);
+ if (!len || d->d_name[len]) return;
+
+ fnmake_todo(id);
+
+ fd = open_read(fn.s);
+ if (fd == -1) { log3("warning: unable to open ",fn.s,"\n"); return; }
+
+ fnmake_mess(id);
+ /* just for the statistics */
+ if (stat(fn.s,&st) == -1)
+ { log3("warning: unable to stat ",fn.s,"\n"); goto fail; }
+
+ for (c = 0;c < CHANNELS;++c)
+ {
+ fnmake_chanaddr(id,c);
+ if (unlink(fn.s) == -1) if (errno != error_noent)
+ { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
+ }
+
+ fnmake_info(id);
+ if (unlink(fn.s) == -1) if (errno != error_noent)
+ { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
+
+ fdinfo = open_excl(fn.s);
+ if (fdinfo == -1)
+ { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
+
+ strnum3[fmt_ulong(strnum3,id)] = 0;
+ log3("new msg ",strnum3,"\n");
+
+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
+
+ substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
+ substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
+
+ uid = 0;
+ pid = 0;
+
+ for (;;)
+ {
+ if (getln(&ss,&todoline,&match,'\0') == -1)
+ {
+ /* perhaps we're out of memory, perhaps an I/O error */
+ fnmake_todo(id);
+ log3("warning: trouble reading ",fn.s,"\n"); goto fail;
+ }
+ if (!match) break;
+
+ switch(todoline.s[0])
+ {
+ case 'u':
+ scan_ulong(todoline.s + 1,&uid);
+ break;
+ case 'p':
+ scan_ulong(todoline.s + 1,&pid);
+ break;
+ case 'F':
+ if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
+ {
+ fnmake_info(id);
+ log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
+ }
+ log2("info msg ",strnum3);
+ strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0;
+ log2(": bytes ",strnum2);
+ log1(" from <"); logsafe(todoline.s + 1);
+ strnum2[fmt_ulong(strnum2,pid)] = 0;
+ log2("> qp ",strnum2);
+ strnum2[fmt_ulong(strnum2,uid)] = 0;
+ log2(" uid ",strnum2);
+ log1("\n");
+ break;
+ case 'T':
+ switch(rewrite(todoline.s + 1))
+ {
+ case 0: nomem(); goto fail;
+ case 2: c = 1; break;
+ default: c = 0; break;
+ }
+ if (fdchan[c] == -1)
+ {
+ fnmake_chanaddr(id,c);
+ fdchan[c] = open_excl(fn.s);
+ if (fdchan[c] == -1)
+ { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
+ substdio_fdbuf(&sschan[c]
+ ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
+ flagchan[c] = 1;
+ }
+ if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
+ {
+ fnmake_chanaddr(id,c);
+ log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
+ }
+ break;
+ default:
+ fnmake_todo(id);
+ log3("warning: unknown record type in ",fn.s,"\n"); goto fail;
+ }
+ }
+
+ close(fd); fd = -1;
+
+ fnmake_info(id);
+ if (substdio_flush(&ssinfo) == -1)
+ { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
+ if (fsync(fdinfo) == -1)
+ { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
+ close(fdinfo); fdinfo = -1;
+
+ for (c = 0;c < CHANNELS;++c)
+ if (fdchan[c] != -1)
+ {
+ fnmake_chanaddr(id,c);
+ if (substdio_flush(&sschan[c]) == -1)
+ { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
+ if (fsync(fdchan[c]) == -1)
+ { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
+ close(fdchan[c]); fdchan[c] = -1;
+ }
+
+ fnmake_todo(id);
+ if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
+ if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
+ if (ch != '+')
+ {
+ log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
+ return;
+ }
+
+ pe.id = id; pe.dt = now();
+ for (c = 0;c < CHANNELS;++c)
+ if (flagchan[c])
+ while (!prioq_insert(&pqchan[c],&pe)) nomem();
+
+ for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
+ if (c == CHANNELS)
+ while (!prioq_insert(&pqdone,&pe)) nomem();
+
+ return;
+
+ fail:
+ if (fd != -1) close(fd);
+ if (fdinfo != -1) close(fdinfo);
+ for (c = 0;c < CHANNELS;++c)
+ if (fdchan[c] != -1) close(fdchan[c]);
+}
+
+
+/* this file is too long ---------------------------------------------- MAIN */
+
+int getcontrols() { if (control_init() == -1) return 0;
+ if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
+ if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
+ if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
+ if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
+ if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
+ if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
+ if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
+ if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
+ if (!stralloc_cats(&doublebounceto,"@")) return 0;
+ if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
+ if (!stralloc_0(&doublebounceto)) return 0;
+ if (control_readfile(&locals,"control/locals",1) != 1) return 0;
+ if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
+ switch(control_readfile(&percenthack,"control/percenthack",0))
+ {
+ case -1: return 0;
+ case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
+ case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
+ }
+ switch(control_readfile(&vdoms,"control/virtualdomains",0))
+ {
+ case -1: return 0;
+ case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
+ case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
+ }
+ return 1; }
+
+stralloc newlocals = {0};
+stralloc newvdoms = {0};
+
+void regetcontrols()
+{
+ int r;
+
+ if (control_readfile(&newlocals,"control/locals",1) != 1)
+ { log1("alert: unable to reread control/locals\n"); return; }
+ r = control_readfile(&newvdoms,"control/virtualdomains",0);
+ if (r == -1)
+ { log1("alert: unable to reread control/virtualdomains\n"); return; }
+
+ constmap_free(&maplocals);
+ constmap_free(&mapvdoms);
+
+ while (!stralloc_copy(&locals,&newlocals)) nomem();
+ while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
+
+ if (r)
+ {
+ while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
+ while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
+ }
+ else
+ while (!constmap_init(&mapvdoms,"",0,1)) nomem();
+}
+
+void reread()
+{
+ if (chdir(auto_qmail) == -1)
+ {
+ log1("alert: unable to reread controls: unable to switch to home directory\n");
+ return;
+ }
+ regetcontrols();
+ while (chdir("queue") == -1)
+ {
+ log1("alert: unable to switch back to queue directory; HELP! sleeping...\n");
+ sleep(10);
+ }
+}
+
+void main()
+{
+ int fd;
+ datetime_sec wakeup;
+ fd_set rfds;
+ fd_set wfds;
+ int nfds;
+ struct timeval tv;
+ int c;
+
+ if (chdir(auto_qmail) == -1)
+ { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); }
+ if (!getcontrols())
+ { log1("alert: cannot start: unable to read controls\n"); _exit(111); }
+ if (chdir("queue") == -1)
+ { log1("alert: cannot start: unable to switch to queue directory\n"); _exit(111); }
+ sig_pipeignore();
+ sig_termcatch(sigterm);
+ sig_alarmcatch(sigalrm);
+ sig_hangupcatch(sighup);
+ sig_childdefault();
+ umask(077);
+
+ fd = open_write("lock/sendmutex");
+ if (fd == -1)
+ { log1("alert: cannot start: unable to open mutex\n"); _exit(111); }
+ if (lock_exnb(fd) == -1)
+ { log1("alert: cannot start: qmail-send is already running\n"); _exit(111); }
+
+ numjobs = 0;
+ for (c = 0;c < CHANNELS;++c)
+ {
+ char ch;
+ int u;
+ int r;
+ do
+ r = read(chanfdin[c],&ch,1);
+ while ((r == -1) && (errno == error_intr));
+ if (r < 1)
+ { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
+ u = (unsigned int) (unsigned char) ch;
+ if (concurrency[c] > u) concurrency[c] = u;
+ numjobs += concurrency[c];
+ }
+
+ fnmake_init();
+
+ comm_init();
+
+ pqstart();
+ job_init();
+ del_init();
+ pass_init();
+ todo_init();
+ cleanup_init();
+
+ while (!flagexitasap || !del_canexit())
+ {
+ recent = now();
+
+ if (flagrunasap) { flagrunasap = 0; pqrun(); }
+ if (flagreadasap) { flagreadasap = 0; reread(); }
+
+ wakeup = recent + SLEEP_FOREVER;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ nfds = 1;
+
+ comm_selprep(&nfds,&wfds);
+ del_selprep(&nfds,&rfds);
+ pass_selprep(&wakeup);
+ todo_selprep(&nfds,&rfds,&wakeup);
+ cleanup_selprep(&wakeup);
+
+ if (wakeup <= recent) tv.tv_sec = 0;
+ else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
+ tv.tv_usec = 0;
+
+ if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
+ if (errno == error_intr)
+ ;
+ else
+ log1("warning: trouble in select\n");
+ else
+ {
+ recent = now();
+
+ comm_do(&wfds);
+ del_do(&rfds);
+ todo_do(&rfds);
+ pass_do();
+ cleanup_do();
+ }
+ }
+ pqfinish();
+ log1("status: exiting\n");
+ _exit(0);
+}
diff --git a/qmail-showctl.8 b/qmail-showctl.8
new file mode 100644
index 0000000..e6a211d
--- /dev/null
+++ b/qmail-showctl.8
@@ -0,0 +1,12 @@
+.TH qmail-showctl 8
+.SH NAME
+qmail-showctl \- analyze the qmail configuration files
+.SH SYNOPSIS
+.B qmail-showctl
+.SH DESCRIPTION
+.B qmail-showctl
+explains the current
+.B qmail
+configuration.
+.SH "SEE ALSO"
+qmail-control(8)
diff --git a/qmail-showctl.c b/qmail-showctl.c
new file mode 100644
index 0000000..a24aa63
--- /dev/null
+++ b/qmail-showctl.c
@@ -0,0 +1,306 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "fmt.h"
+#include "str.h"
+#include "control.h"
+#include "constmap.h"
+#include "stralloc.h"
+#include "direntry.h"
+#include "auto_uids.h"
+#include "auto_qmail.h"
+#include "auto_break.h"
+#include "auto_patrn.h"
+#include "auto_spawn.h"
+#include "auto_split.h"
+
+stralloc me = {0};
+int meok;
+
+stralloc line = {0};
+char num[FMT_ULONG];
+
+void safeput(buf,len)
+char *buf;
+unsigned int len;
+{
+ char ch;
+
+ while (len > 0) {
+ ch = *buf;
+ if ((ch < 32) || (ch > 126)) ch = '?';
+ substdio_put(subfdout,&ch,1);
+ ++buf;
+ --len;
+ }
+}
+
+void do_int(fn,def,pre,post)
+char *fn;
+char *def;
+char *pre;
+char *post;
+{
+ int i;
+ substdio_puts(subfdout,"\n");
+ substdio_puts(subfdout,fn);
+ substdio_puts(subfdout,": ");
+ switch(control_readint(&i,fn)) {
+ case 0:
+ substdio_puts(subfdout,"(Default.) ");
+ substdio_puts(subfdout,pre);
+ substdio_puts(subfdout,def);
+ substdio_puts(subfdout,post);
+ substdio_puts(subfdout,".\n");
+ break;
+ case 1:
+ if (i < 0) i = 0;
+ substdio_puts(subfdout,pre);
+ substdio_put(subfdout,num,fmt_uint(num,i));
+ substdio_puts(subfdout,post);
+ substdio_puts(subfdout,".\n");
+ break;
+ default:
+ substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
+ break;
+ }
+}
+
+void do_str(fn,flagme,def,pre)
+char *fn;
+int flagme;
+char *def;
+char *pre;
+{
+ substdio_puts(subfdout,"\n");
+ substdio_puts(subfdout,fn);
+ substdio_puts(subfdout,": ");
+ switch(control_readline(&line,fn)) {
+ case 0:
+ substdio_puts(subfdout,"(Default.) ");
+ if (!stralloc_copys(&line,def)) {
+ substdio_puts(subfdout,"Oops! Out of memory.\n");
+ break;
+ }
+ if (flagme && meok)
+ if (!stralloc_copy(&line,&me)) {
+ substdio_puts(subfdout,"Oops! Out of memory.\n");
+ break;
+ }
+ case 1:
+ substdio_puts(subfdout,pre);
+ safeput(line.s,line.len);
+ substdio_puts(subfdout,".\n");
+ break;
+ default:
+ substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
+ break;
+ }
+}
+
+int do_lst(fn,def,pre,post)
+char *fn;
+char *def;
+char *pre;
+char *post;
+{
+ int i;
+ int j;
+
+ substdio_puts(subfdout,"\n");
+ substdio_puts(subfdout,fn);
+ substdio_puts(subfdout,": ");
+ switch(control_readfile(&line,fn)) {
+ case 0:
+ substdio_puts(subfdout,"(Default.) ");
+ substdio_puts(subfdout,def);
+ substdio_puts(subfdout,"\n");
+ return 0;
+ case 1:
+ substdio_puts(subfdout,"\n");
+ i = 0;
+ for (j = 0;j < line.len;++j)
+ if (!line.s[j]) {
+ substdio_puts(subfdout,pre);
+ safeput(line.s + i,j - i);
+ substdio_puts(subfdout,post);
+ substdio_puts(subfdout,"\n");
+ i = j + 1;
+ }
+ return 1;
+ default:
+ substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
+ return -1;
+ }
+}
+
+void main()
+{
+ DIR *dir;
+ direntry *d;
+ struct stat stmrh;
+ struct stat stmrhcdb;
+
+ substdio_puts(subfdout,"qmail home directory: ");
+ substdio_puts(subfdout,auto_qmail);
+ substdio_puts(subfdout,".\n");
+
+ substdio_puts(subfdout,"user-ext delimiter: ");
+ substdio_puts(subfdout,auto_break);
+ substdio_puts(subfdout,".\n");
+
+ substdio_puts(subfdout,"paternalism (in decimal): ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_patrn));
+ substdio_puts(subfdout,".\n");
+
+ substdio_puts(subfdout,"silent concurrency limit: ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_spawn));
+ substdio_puts(subfdout,".\n");
+
+ substdio_puts(subfdout,"subdirectory split: ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_split));
+ substdio_puts(subfdout,".\n");
+
+ substdio_puts(subfdout,"user ids: ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uida));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidd));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidl));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uido));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidp));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidq));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidr));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uids));
+ substdio_puts(subfdout,".\n");
+
+ substdio_puts(subfdout,"group ids: ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidn));
+ substdio_puts(subfdout,", ");
+ substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidq));
+ substdio_puts(subfdout,".\n");
+
+ if (chdir(auto_qmail) == -1) {
+ substdio_puts(subfdout,"Oops! Unable to chdir to ");
+ substdio_puts(subfdout,auto_qmail);
+ substdio_puts(subfdout,".\n");
+ substdio_flush(subfdout);
+ _exit(111);
+ }
+ if (chdir("control") == -1) {
+ substdio_puts(subfdout,"Oops! Unable to chdir to control.\n");
+ substdio_flush(subfdout);
+ _exit(111);
+ }
+
+ dir = opendir(".");
+ if (!dir) {
+ substdio_puts(subfdout,"Oops! Unable to open current directory.\n");
+ substdio_flush(subfdout);
+ _exit(111);
+ }
+
+ meok = control_readline(&me,"me");
+ if (meok == -1) {
+ substdio_puts(subfdout,"Oops! Trouble reading control/me.");
+ substdio_flush(subfdout);
+ _exit(111);
+ }
+
+ do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
+ do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
+ do_str("bouncehost",1,"bouncehost","Bounce host name is ");
+ do_int("concurrencylocal","10","Local concurrency is ","");
+ do_int("concurrencyremote","20","Remote concurrency is ","");
+ do_int("databytes","0","SMTP DATA limit is "," bytes");
+ do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
+ do_str("defaulthost",1,"defaulthost","Default host name is ");
+ do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
+ do_str("doublebounceto",0,"postmaster","2B recipient user: ");
+ do_str("envnoathost",1,"envnoathost","Presumed domain name is ");
+ do_str("helohost",1,"helohost","SMTP client HELO host name is ");
+ do_str("idhost",1,"idhost","Message-ID host name is ");
+ do_str("localiphost",1,"localiphost","Local IP address becomes ");
+ do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally.");
+ do_str("me",0,"undefined! Uh-oh","My name is ");
+ do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
+ do_str("plusdomain",1,"plusdomain","Plus domain name is ");
+ do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
+ do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds");
+
+ if (do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ","."))
+ do_lst("morercpthosts","No effect.","SMTP clients may send messages to recipients at ",".");
+ else
+ do_lst("morercpthosts","No rcpthosts; morercpthosts is irrelevant.","No rcpthosts; doesn't matter that morercpthosts has ",".");
+ /* XXX: check morercpthosts.cdb contents */
+ substdio_puts(subfdout,"\nmorercpthosts.cdb: ");
+ if (stat("morercpthosts",&stmrh) == -1)
+ if (stat("morercpthosts.cdb",&stmrhcdb) == -1)
+ substdio_puts(subfdout,"(Default.) No effect.\n");
+ else
+ substdio_puts(subfdout,"Oops! morercpthosts.cdb exists but morercpthosts doesn't.\n");
+ else
+ if (stat("morercpthosts.cdb",&stmrhcdb) == -1)
+ substdio_puts(subfdout,"Oops! morercpthosts exists but morercpthosts.cdb doesn't.\n");
+ else
+ if (stmrh.st_mtime > stmrhcdb.st_mtime)
+ substdio_puts(subfdout,"Oops! morercpthosts.cdb is older than morercpthosts.\n");
+ else
+ substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n");
+
+ do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
+ do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
+ do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
+ do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
+ do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
+ do_lst("virtualdomains","No virtual domains.","Virtual domain: ","");
+
+ while (d = readdir(dir)) {
+ if (str_equal(d->d_name,".")) continue;
+ if (str_equal(d->d_name,"..")) continue;
+ if (str_equal(d->d_name,"bouncefrom")) continue;
+ if (str_equal(d->d_name,"bouncehost")) continue;
+ if (str_equal(d->d_name,"badmailfrom")) continue;
+ if (str_equal(d->d_name,"bouncefrom")) continue;
+ if (str_equal(d->d_name,"bouncehost")) continue;
+ if (str_equal(d->d_name,"concurrencylocal")) continue;
+ if (str_equal(d->d_name,"concurrencyremote")) continue;
+ if (str_equal(d->d_name,"databytes")) continue;
+ if (str_equal(d->d_name,"defaultdomain")) continue;
+ if (str_equal(d->d_name,"defaulthost")) continue;
+ if (str_equal(d->d_name,"doublebouncehost")) continue;
+ if (str_equal(d->d_name,"doublebounceto")) continue;
+ if (str_equal(d->d_name,"envnoathost")) continue;
+ if (str_equal(d->d_name,"helohost")) continue;
+ if (str_equal(d->d_name,"idhost")) continue;
+ if (str_equal(d->d_name,"localiphost")) continue;
+ if (str_equal(d->d_name,"locals")) continue;
+ if (str_equal(d->d_name,"me")) continue;
+ if (str_equal(d->d_name,"morercpthosts")) continue;
+ if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
+ if (str_equal(d->d_name,"percenthack")) continue;
+ if (str_equal(d->d_name,"plusdomain")) continue;
+ if (str_equal(d->d_name,"qmqpservers")) continue;
+ if (str_equal(d->d_name,"queuelifetime")) continue;
+ if (str_equal(d->d_name,"rcpthosts")) continue;
+ if (str_equal(d->d_name,"smtpgreeting")) continue;
+ if (str_equal(d->d_name,"smtproutes")) continue;
+ if (str_equal(d->d_name,"timeoutconnect")) continue;
+ if (str_equal(d->d_name,"timeoutremote")) continue;
+ if (str_equal(d->d_name,"timeoutsmtpd")) continue;
+ if (str_equal(d->d_name,"virtualdomains")) continue;
+ substdio_puts(subfdout,"\n");
+ substdio_puts(subfdout,d->d_name);
+ substdio_puts(subfdout,": I have no idea what this file does.\n");
+ }
+
+ substdio_flush(subfdout);
+ _exit(0);
+}
diff --git a/qmail-smtpd.8 b/qmail-smtpd.8
new file mode 100644
index 0000000..c4640b8
--- /dev/null
+++ b/qmail-smtpd.8
@@ -0,0 +1,179 @@
+.TH qmail-smtpd 8
+.SH NAME
+qmail-smtpd \- receive mail via SMTP
+.SH SYNOPSIS
+.B qmail-smtpd
+.SH DESCRIPTION
+.B qmail-smtpd
+receives mail messages via the Simple Mail Transfer Protocol (SMTP)
+and invokes
+.B qmail-queue
+to deposit them into the outgoing queue.
+.B qmail-smtpd
+must be supplied several environment variables;
+see
+.BR tcp-environ(5) .
+
+.B qmail-smtpd
+is responsible for counting hops.
+It rejects any message with 100 or more
+.B Received
+or
+.B Delivered-To
+header fields.
+
+.B qmail-smtpd
+supports ESMTP, including the 8BITMIME and PIPELINING options.
+.SH TRANSPARENCY
+.B qmail-smtpd
+converts the SMTP newline convention into the UNIX newline convention
+by converting CR LF into LF.
+It returns a temporary error and drops the connection on bare LFs;
+see
+.BR http://pobox.com/~djb/docs/smtplf.html .
+
+.B qmail-smtpd
+accepts messages that contain long lines or non-ASCII characters,
+even though such messages violate the SMTP protocol.
+.SH "CONTROL FILES"
+.TP 5
+.I badmailfrom
+Unacceptable envelope sender addresses.
+.B qmail-smtpd
+will reject every recipient address for a message
+if the envelope sender address is listed in
+.IR badmailfrom .
+A line in
+.I badmailfrom
+may be of the form
+.BR @\fIhost ,
+meaning every address at
+.IR host .
+.TP 5
+.I databytes
+Maximum number of bytes allowed in a message,
+or 0 for no limit.
+Default: 0.
+If a message exceeds this limit,
+.B qmail-smtpd
+returns a permanent error code to the client;
+in contrast, if
+the disk is full or
+.B qmail-smtpd
+hits a resource limit,
+.B qmail-smtpd
+returns a temporary error code.
+
+.I databytes
+counts bytes as stored on disk, not as transmitted through the network.
+It does not count the
+.B qmail-smtpd
+Received line, the
+.B qmail-queue
+Received line, or the envelope.
+
+If the environment variable
+.B DATABYTES
+is set, it overrides
+.IR databytes .
+.TP 5
+.I localiphost
+Replacement host name for local IP addresses.
+Default:
+.IR me ,
+if that is supplied.
+.B qmail-smtpd
+is responsible for recognizing dotted-decimal addresses for the
+current host.
+When it sees a recipient address of the form
+.IR box@[d.d.d.d] ,
+where
+.I d.d.d.d
+is a local IP address,
+it replaces
+.IR [d.d.d.d]
+with
+.IR localiphost .
+This is done before
+.IR rcpthosts .
+.TP 5
+.I morercpthosts
+Extra allowed RCPT domains.
+If
+.I rcpthosts
+and
+.I morercpthosts
+both exist,
+.I morercpthosts
+is effectively appended to
+.IR rcpthosts .
+
+You must run
+.B qmail-newmrh
+whenever
+.I morercpthosts
+changes.
+
+Rule of thumb for large sites:
+Put your 50 most commonly used domains into
+.IR rcpthosts ,
+and the rest into
+.IR morercpthosts .
+.TP 5
+.I rcpthosts
+Allowed RCPT domains.
+If
+.I rcpthosts
+is supplied,
+.B qmail-smtpd
+will reject
+any envelope recipient address with a domain not listed in
+.IR rcpthosts .
+
+Exception:
+If the environment variable
+.B RELAYCLIENT
+is set,
+.B qmail-smtpd
+will ignore
+.IR rcpthosts ,
+and will append the value of
+.B RELAYCLIENT
+to each incoming recipient address.
+
+.I rcpthosts
+may include wildcards:
+
+.EX
+ heaven.af.mil
+ .heaven.af.mil
+.EE
+
+Envelope recipient addresses without @ signs are
+always allowed through.
+.TP 5
+.I smtpgreeting
+SMTP greeting message.
+Default:
+.IR me ,
+if that is supplied;
+otherwise
+.B qmail-smtpd
+will refuse to run.
+The first word of
+.I smtpgreeting
+should be the current host's name.
+.TP 5
+.I timeoutsmtpd
+Number of seconds
+.B qmail-smtpd
+will wait for each new buffer of data from the remote SMTP client.
+Default: 1200.
+.SH "SEE ALSO"
+tcp-env(1),
+tcp-environ(5),
+qmail-control(5),
+qmail-inject(8),
+qmail-newmrh(8),
+qmail-queue(8),
+qmail-remote(8)
diff --git a/qmail-smtpd.c b/qmail-smtpd.c
new file mode 100644
index 0000000..54df00c
--- /dev/null
+++ b/qmail-smtpd.c
@@ -0,0 +1,421 @@
+#include "sig.h"
+#include "readwrite.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "alloc.h"
+#include "auto_qmail.h"
+#include "control.h"
+#include "received.h"
+#include "constmap.h"
+#include "error.h"
+#include "ipme.h"
+#include "ip.h"
+#include "qmail.h"
+#include "str.h"
+#include "fmt.h"
+#include "scan.h"
+#include "byte.h"
+#include "case.h"
+#include "env.h"
+#include "now.h"
+#include "exit.h"
+#include "rcpthosts.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+#include "commands.h"
+
+#define MAXHOPS 100
+unsigned int databytes = 0;
+int timeout = 1200;
+
+int safewrite(fd,buf,len) int fd; char *buf; int len;
+{
+ int r;
+ r = timeoutwrite(timeout,fd,buf,len);
+ if (r <= 0) _exit(1);
+ return r;
+}
+
+char ssoutbuf[512];
+substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
+
+void flush() { substdio_flush(&ssout); }
+void out(s) char *s; { substdio_puts(&ssout,s); }
+
+void die_read() { _exit(1); }
+void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
+void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
+void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
+
+void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
+void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
+void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
+void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
+void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
+void err_noop(arg) char *arg; { out("250 ok\r\n"); }
+void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); }
+void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+
+
+stralloc greeting = {0};
+
+void smtp_greet(code) char *code;
+{
+ substdio_puts(&ssout,code);
+ substdio_put(&ssout,greeting.s,greeting.len);
+}
+void smtp_help(arg) char *arg;
+{
+ out("214 netqmail home page: http://qmail.org/netqmail\r\n");
+}
+void smtp_quit(arg) char *arg;
+{
+ smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
+}
+
+char *remoteip;
+char *remotehost;
+char *remoteinfo;
+char *local;
+char *relayclient;
+
+stralloc helohost = {0};
+char *fakehelo; /* pointer into helohost, or 0 */
+
+void dohelo(arg) char *arg; {
+ if (!stralloc_copys(&helohost,arg)) die_nomem();
+ if (!stralloc_0(&helohost)) die_nomem();
+ fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
+}
+
+int liphostok = 0;
+stralloc liphost = {0};
+int bmfok = 0;
+stralloc bmf = {0};
+struct constmap mapbmf;
+
+void setup()
+{
+ char *x;
+ unsigned long u;
+
+ if (control_init() == -1) die_control();
+ if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
+ die_control();
+ liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0);
+ if (liphostok == -1) die_control();
+ if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
+ if (timeout <= 0) timeout = 1;
+
+ if (rcpthosts_init() == -1) die_control();
+
+ bmfok = control_readfile(&bmf,"control/badmailfrom",0);
+ if (bmfok == -1) die_control();
+ if (bmfok)
+ if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+
+ if (control_readint(&databytes,"control/databytes") == -1) die_control();
+ x = env_get("DATABYTES");
+ if (x) { scan_ulong(x,&u); databytes = u; }
+ if (!(databytes + 1)) --databytes;
+
+ remoteip = env_get("TCPREMOTEIP");
+ if (!remoteip) remoteip = "unknown";
+ local = env_get("TCPLOCALHOST");
+ if (!local) local = env_get("TCPLOCALIP");
+ if (!local) local = "unknown";
+ remotehost = env_get("TCPREMOTEHOST");
+ if (!remotehost) remotehost = "unknown";
+ remoteinfo = env_get("TCPREMOTEINFO");
+ relayclient = env_get("RELAYCLIENT");
+ dohelo(remotehost);
+}
+
+
+stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
+
+int addrparse(arg)
+char *arg;
+{
+ int i;
+ char ch;
+ char terminator;
+ struct ip_address ip;
+ int flagesc;
+ int flagquoted;
+
+ terminator = '>';
+ i = str_chr(arg,'<');
+ if (arg[i])
+ arg += i + 1;
+ else { /* partner should go read rfc 821 */
+ terminator = ' ';
+ arg += str_chr(arg,':');
+ if (*arg == ':') ++arg;
+ while (*arg == ' ') ++arg;
+ }
+
+ /* strip source route */
+ if (*arg == '@') while (*arg) if (*arg++ == ':') break;
+
+ if (!stralloc_copys(&addr,"")) die_nomem();
+ flagesc = 0;
+ flagquoted = 0;
+ for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */
+ if (flagesc) {
+ if (!stralloc_append(&addr,&ch)) die_nomem();
+ flagesc = 0;
+ }
+ else {
+ if (!flagquoted && (ch == terminator)) break;
+ switch(ch) {
+ case '\\': flagesc = 1; break;
+ case '"': flagquoted = !flagquoted; break;
+ default: if (!stralloc_append(&addr,&ch)) die_nomem();
+ }
+ }
+ }
+ /* could check for termination failure here, but why bother? */
+ if (!stralloc_append(&addr,"")) die_nomem();
+
+ if (liphostok) {
+ i = byte_rchr(addr.s,addr.len,'@');
+ if (i < addr.len) /* if not, partner should go read rfc 821 */
+ if (addr.s[i + 1] == '[')
+ if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])
+ if (ipme_is(&ip)) {
+ addr.len = i + 1;
+ if (!stralloc_cat(&addr,&liphost)) die_nomem();
+ if (!stralloc_0(&addr)) die_nomem();
+ }
+ }
+
+ if (addr.len > 900) return 0;
+ return 1;
+}
+
+int bmfcheck()
+{
+ int j;
+ if (!bmfok) return 0;
+ if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
+ j = byte_rchr(addr.s,addr.len,'@');
+ if (j < addr.len)
+ if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
+ return 0;
+}
+
+int addrallowed()
+{
+ int r;
+ r = rcpthosts(addr.s,str_len(addr.s));
+ if (r == -1) die_control();
+ return r;
+}
+
+
+int seenmail = 0;
+int flagbarf; /* defined if seenmail */
+stralloc mailfrom = {0};
+stralloc rcptto = {0};
+
+void smtp_helo(arg) char *arg;
+{
+ smtp_greet("250 "); out("\r\n");
+ seenmail = 0; dohelo(arg);
+}
+void smtp_ehlo(arg) char *arg;
+{
+ smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+ seenmail = 0; dohelo(arg);
+}
+void smtp_rset(arg) char *arg;
+{
+ seenmail = 0;
+ out("250 flushed\r\n");
+}
+void smtp_mail(arg) char *arg;
+{
+ if (!addrparse(arg)) { err_syntax(); return; }
+ flagbarf = bmfcheck();
+ seenmail = 1;
+ if (!stralloc_copys(&rcptto,"")) die_nomem();
+ if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
+ if (!stralloc_0(&mailfrom)) die_nomem();
+ out("250 ok\r\n");
+}
+void smtp_rcpt(arg) char *arg; {
+ if (!seenmail) { err_wantmail(); return; }
+ if (!addrparse(arg)) { err_syntax(); return; }
+ if (flagbarf) { err_bmf(); return; }
+ if (relayclient) {
+ --addr.len;
+ if (!stralloc_cats(&addr,relayclient)) die_nomem();
+ if (!stralloc_0(&addr)) die_nomem();
+ }
+ else
+ if (!addrallowed()) { err_nogateway(); return; }
+ if (!stralloc_cats(&rcptto,"T")) die_nomem();
+ if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
+ if (!stralloc_0(&rcptto)) die_nomem();
+ out("250 ok\r\n");
+}
+
+
+int saferead(fd,buf,len) int fd; char *buf; int len;
+{
+ int r;
+ flush();
+ r = timeoutread(timeout,fd,buf,len);
+ if (r == -1) if (errno == error_timeout) die_alarm();
+ if (r <= 0) die_read();
+ return r;
+}
+
+char ssinbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
+
+struct qmail qqt;
+unsigned int bytestooverflow = 0;
+
+void put(ch)
+char *ch;
+{
+ if (bytestooverflow)
+ if (!--bytestooverflow)
+ qmail_fail(&qqt);
+ qmail_put(&qqt,ch,1);
+}
+
+void blast(hops)
+int *hops;
+{
+ char ch;
+ int state;
+ int flaginheader;
+ int pos; /* number of bytes since most recent \n, if fih */
+ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
+ int flagmaybey; /* 1 if this line might match \r\n, if fih */
+ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
+
+ state = 1;
+ *hops = 0;
+ flaginheader = 1;
+ pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
+ for (;;) {
+ substdio_get(&ssin,&ch,1);
+ if (flaginheader) {
+ if (pos < 9) {
+ if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;
+ if (flagmaybez) if (pos == 8) ++*hops;
+ if (pos < 8)
+ if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;
+ if (flagmaybex) if (pos == 7) ++*hops;
+ if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
+ if (flagmaybey) if (pos == 1) flaginheader = 0;
+ ++pos;
+ }
+ if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
+ }
+ switch(state) {
+ case 0:
+ if (ch == '\n') straynewline();
+ if (ch == '\r') { state = 4; continue; }
+ break;
+ case 1: /* \r\n */
+ if (ch == '\n') straynewline();
+ if (ch == '.') { state = 2; continue; }
+ if (ch == '\r') { state = 4; continue; }
+ state = 0;
+ break;
+ case 2: /* \r\n + . */
+ if (ch == '\n') straynewline();
+ if (ch == '\r') { state = 3; continue; }
+ state = 0;
+ break;
+ case 3: /* \r\n + .\r */
+ if (ch == '\n') return;
+ put(".");
+ put("\r");
+ if (ch == '\r') { state = 4; continue; }
+ state = 0;
+ break;
+ case 4: /* + \r */
+ if (ch == '\n') { state = 1; break; }
+ if (ch != '\r') { put("\r"); state = 0; }
+ }
+ put(&ch);
+ }
+}
+
+char accept_buf[FMT_ULONG];
+void acceptmessage(qp) unsigned long qp;
+{
+ datetime_sec when;
+ when = now();
+ out("250 ok ");
+ accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
+ out(accept_buf);
+ out(" qp ");
+ accept_buf[fmt_ulong(accept_buf,qp)] = 0;
+ out(accept_buf);
+ out("\r\n");
+}
+
+void smtp_data(arg) char *arg; {
+ int hops;
+ unsigned long qp;
+ char *qqx;
+
+ if (!seenmail) { err_wantmail(); return; }
+ if (!rcptto.len) { err_wantrcpt(); return; }
+ seenmail = 0;
+ if (databytes) bytestooverflow = databytes + 1;
+ if (qmail_open(&qqt) == -1) { err_qqt(); return; }
+ qp = qmail_qp(&qqt);
+ out("354 go ahead\r\n");
+
+ received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
+ blast(&hops);
+ hops = (hops >= MAXHOPS);
+ if (hops) qmail_fail(&qqt);
+ qmail_from(&qqt,mailfrom.s);
+ qmail_put(&qqt,rcptto.s,rcptto.len);
+
+ qqx = qmail_close(&qqt);
+ if (!*qqx) { acceptmessage(qp); return; }
+ if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
+ if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
+ if (*qqx == 'D') out("554 "); else out("451 ");
+ out(qqx + 1);
+ out("\r\n");
+}
+
+struct commands smtpcommands[] = {
+ { "rcpt", smtp_rcpt, 0 }
+, { "mail", smtp_mail, 0 }
+, { "data", smtp_data, flush }
+, { "quit", smtp_quit, flush }
+, { "helo", smtp_helo, flush }
+, { "ehlo", smtp_ehlo, flush }
+, { "rset", smtp_rset, 0 }
+, { "help", smtp_help, flush }
+, { "noop", err_noop, flush }
+, { "vrfy", err_vrfy, flush }
+, { 0, err_unimpl, flush }
+} ;
+
+void main()
+{
+ sig_pipeignore();
+ if (chdir(auto_qmail) == -1) die_control();
+ setup();
+ if (ipme_init() != 1) die_ipme();
+ smtp_greet("220 ");
+ out(" ESMTP\r\n");
+ if (commands(&ssin,&smtpcommands) == 0) die_read();
+ die_nomem();
+}
diff --git a/qmail-start.9 b/qmail-start.9
new file mode 100644
index 0000000..29876ec
--- /dev/null
+++ b/qmail-start.9
@@ -0,0 +1,94 @@
+.TH qmail-start 8
+.SH NAME
+qmail-start \- turn on mail delivery
+.SH SYNOPSIS
+.B qmail-start
+[
+.I defaultdelivery
+[
+.I logger arg ...
+]
+]
+.SH DESCRIPTION
+.B qmail-start
+invokes
+.BR qmail-send ,
+.BR qmail-lspawn ,
+.BR qmail-rspawn ,
+and
+.BR qmail-clean ,
+under the proper uids and gids.
+These four daemons cooperate to deliver messages from the queue.
+
+.B qmail-start
+arranges for
+.BR qmail-send 's
+activity record to be sent to
+.BR qmail-start 's
+output.
+See
+.B qmail-log(5)
+for the format of the activity record.
+Other than this,
+.B qmail-start
+does not print anything, even on failure.
+
+If
+.I defaultdelivery
+is supplied,
+.B qmail-start
+passes it to
+.BR qmail-lspawn .
+
+If
+.I logger
+is supplied,
+.B qmail-start
+invokes
+.I logger
+with the given arguments,
+and feeds
+.BR qmail-send 's
+activity record through
+.IR logger .
+
+Environment variables given to
+.B qmail-start
+will eventually be passed on to
+.BR qmail-local ,
+so make sure to clean up the environment if you run
+.B qmail-start
+manually:
+
+.EX
+ # env - PATH="QMAILHOME/bin:$PATH"
+.br
+ qmail-start ./Mailbox splogger qmail &
+.br
+ (all on one line)
+.EE
+
+Resource limits, controlling ttys, et al. are also passed from
+.B qmail-start
+to
+.BR qmail-local .
+
+Note that
+.B qmail-send
+normally juggles several simultaneous deliveries.
+To reduce
+.BR qmail-send 's
+impact on other programs,
+you can run
+.B qmail-start
+with a low priority.
+.SH "SEE ALSO"
+logger(1),
+splogger(1),
+nice(1),
+qmail-log(5),
+qmail-local(8),
+qmail-clean(8),
+qmail-lspawn(8),
+qmail-rspawn(8),
+qmail-send(8)
diff --git a/qmail-start.c b/qmail-start.c
new file mode 100644
index 0000000..37423b0
--- /dev/null
+++ b/qmail-start.c
@@ -0,0 +1,120 @@
+#include "fd.h"
+#include "prot.h"
+#include "exit.h"
+#include "fork.h"
+#include "auto_uids.h"
+
+char *(qsargs[]) = { "qmail-send", 0 };
+char *(qcargs[]) = { "qmail-clean", 0 };
+char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 };
+char *(qrargs[]) = { "qmail-rspawn", 0 };
+
+void die() { _exit(111); }
+
+int pi0[2];
+int pi1[2];
+int pi2[2];
+int pi3[2];
+int pi4[2];
+int pi5[2];
+int pi6[2];
+
+void close23456() { close(2); close(3); close(4); close(5); close(6); }
+
+void closepipes() {
+ close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]);
+ close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]);
+ close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ if (chdir("/") == -1) die();
+ umask(077);
+ if (prot_gid(auto_gidq) == -1) die();
+
+ if (fd_copy(2,0) == -1) die();
+ if (fd_copy(3,0) == -1) die();
+ if (fd_copy(4,0) == -1) die();
+ if (fd_copy(5,0) == -1) die();
+ if (fd_copy(6,0) == -1) die();
+
+ if (argv[1]) {
+ qlargs[1] = argv[1];
+ ++argv;
+ }
+
+ if (argv[1]) {
+ if (pipe(pi0) == -1) die();
+ switch(fork()) {
+ case -1:
+ die();
+ case 0:
+ if (prot_gid(auto_gidn) == -1) die();
+ if (prot_uid(auto_uidl) == -1) die();
+ close(pi0[1]);
+ if (fd_move(0,pi0[0]) == -1) die();
+ close23456();
+ execvp(argv[1],argv + 1);
+ die();
+ }
+ close(pi0[0]);
+ if (fd_move(1,pi0[1]) == -1) die();
+ }
+
+ if (pipe(pi1) == -1) die();
+ if (pipe(pi2) == -1) die();
+ if (pipe(pi3) == -1) die();
+ if (pipe(pi4) == -1) die();
+ if (pipe(pi5) == -1) die();
+ if (pipe(pi6) == -1) die();
+
+ switch(fork()) {
+ case -1: die();
+ case 0:
+ if (fd_copy(0,pi1[0]) == -1) die();
+ if (fd_copy(1,pi2[1]) == -1) die();
+ close23456();
+ closepipes();
+ execvp(*qlargs,qlargs);
+ die();
+ }
+
+ switch(fork()) {
+ case -1: die();
+ case 0:
+ if (prot_uid(auto_uidr) == -1) die();
+ if (fd_copy(0,pi3[0]) == -1) die();
+ if (fd_copy(1,pi4[1]) == -1) die();
+ close23456();
+ closepipes();
+ execvp(*qrargs,qrargs);
+ die();
+ }
+
+ switch(fork()) {
+ case -1: die();
+ case 0:
+ if (prot_uid(auto_uidq) == -1) die();
+ if (fd_copy(0,pi5[0]) == -1) die();
+ if (fd_copy(1,pi6[1]) == -1) die();
+ close23456();
+ closepipes();
+ execvp(*qcargs,qcargs);
+ die();
+ }
+
+ if (prot_uid(auto_uids) == -1) die();
+ if (fd_copy(0,1) == -1) die();
+ if (fd_copy(1,pi1[1]) == -1) die();
+ if (fd_copy(2,pi2[0]) == -1) die();
+ if (fd_copy(3,pi3[1]) == -1) die();
+ if (fd_copy(4,pi4[0]) == -1) die();
+ if (fd_copy(5,pi5[1]) == -1) die();
+ if (fd_copy(6,pi6[0]) == -1) die();
+ closepipes();
+ execvp(*qsargs,qsargs);
+ die();
+}
diff --git a/qmail-tcpok.8 b/qmail-tcpok.8
new file mode 100644
index 0000000..ed2efcf
--- /dev/null
+++ b/qmail-tcpok.8
@@ -0,0 +1,24 @@
+.TH qmail-tcpok 8
+.SH NAME
+qmail-tcpok \- clear TCP timeout table
+.SH SYNOPSIS
+.B qmail-tcpok
+.SH DESCRIPTION
+.B qmail-tcpok
+erases
+.BR qmail-remote 's
+current list of timeouts,
+so that
+.B qmail-remote
+does not make any assumptions about failing addresses.
+
+.B qmail-tcpok
+must be run either as
+.B root
+or with user id
+.B qmailr
+and group id
+.BR qmail .
+.SH "SEE ALSO"
+qmail-remote(8),
+qmail-tcpto(8)
diff --git a/qmail-tcpok.c b/qmail-tcpok.c
new file mode 100644
index 0000000..a9b9652
--- /dev/null
+++ b/qmail-tcpok.c
@@ -0,0 +1,35 @@
+#include "strerr.h"
+#include "substdio.h"
+#include "lock.h"
+#include "open.h"
+#include "readwrite.h"
+#include "auto_qmail.h"
+#include "exit.h"
+
+#define FATAL "qmail-tcpok: fatal: "
+
+char buf[1024]; /* XXX: must match size in tcpto_clean.c, tcpto.c */
+substdio ss;
+
+void main()
+{
+ int fd;
+ int i;
+
+ if (chdir(auto_qmail) == -1)
+ strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
+ if (chdir("queue/lock") == -1)
+ strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,"/queue/lock: ");
+
+ fd = open_write("tcpto");
+ if (fd == -1)
+ strerr_die4sys(111,FATAL,"unable to write ",auto_qmail,"/queue/lock/tcpto: ");
+ if (lock_ex(fd) == -1)
+ strerr_die4sys(111,FATAL,"unable to lock ",auto_qmail,"/queue/lock/tcpto: ");
+
+ substdio_fdbuf(&ss,write,fd,buf,sizeof buf);
+ for (i = 0;i < sizeof buf;++i) substdio_put(&ss,"",1);
+ if (substdio_flush(&ss) == -1)
+ strerr_die4sys(111,FATAL,"unable to clear ",auto_qmail,"/queue/lock/tcpto: ");
+ _exit(0);
+}
diff --git a/qmail-tcpto.8 b/qmail-tcpto.8
new file mode 100644
index 0000000..cb4ddd6
--- /dev/null
+++ b/qmail-tcpto.8
@@ -0,0 +1,30 @@
+.TH qmail-tcpto 8
+.SH NAME
+qmail-tcpto \- print TCP timeout table
+.SH SYNOPSIS
+.B qmail-tcpto
+.SH DESCRIPTION
+After an SMTP connection attempt times out,
+.B qmail-remote
+records the relevant IP address.
+If the same address fails again (after at least two minutes with
+no intervening successful connections),
+.B qmail-remote
+assumes that further attempts will fail for at least another hour.
+
+.B qmail-tcpto
+prints
+.BR qmail-remote 's
+current list of timeouts.
+
+.B qmail-tcpto
+must be run either as
+.B root
+or with user id
+.B qmailr
+and group id
+.BR qmail .
+.SH "SEE ALSO"
+qmail-qread(8),
+qmail-remote(8),
+qmail-tcpok(8)
diff --git a/qmail-tcpto.c b/qmail-tcpto.c
new file mode 100644
index 0000000..d181ecf
--- /dev/null
+++ b/qmail-tcpto.c
@@ -0,0 +1,85 @@
+/* XXX: this program knows quite a bit about tcpto's internals */
+
+#include "substdio.h"
+#include "subfd.h"
+#include "auto_qmail.h"
+#include "fmt.h"
+#include "ip.h"
+#include "lock.h"
+#include "error.h"
+#include "exit.h"
+#include "datetime.h"
+#include "now.h"
+
+void die(n) int n; { substdio_flush(subfdout); _exit(n); }
+
+void warn(s) char *s;
+{
+ char *x;
+ x = error_str(errno);
+ substdio_puts(subfdout,s);
+ substdio_puts(subfdout,": ");
+ substdio_puts(subfdout,x);
+ substdio_puts(subfdout,"\n");
+}
+
+void die_chdir() { warn("fatal: unable to chdir"); die(111); }
+void die_open() { warn("fatal: unable to open tcpto"); die(111); }
+void die_lock() { warn("fatal: unable to lock tcpto"); die(111); }
+void die_read() { warn("fatal: unable to read tcpto"); die(111); }
+
+char tcpto_buf[1024];
+
+char tmp[FMT_ULONG + IPFMT];
+
+void main()
+{
+ int fdlock;
+ int fd;
+ int r;
+ int i;
+ char *record;
+ struct ip_address ip;
+ datetime_sec when;
+ datetime_sec start;
+
+ if (chdir(auto_qmail) == -1) die_chdir();
+ if (chdir("queue/lock") == -1) die_chdir();
+
+ fdlock = open_write("tcpto");
+ if (fdlock == -1) die_open();
+ fd = open_read("tcpto");
+ if (fd == -1) die_open();
+ if (lock_ex(fdlock) == -1) die_lock();
+ r = read(fd,tcpto_buf,sizeof(tcpto_buf));
+ close(fd);
+ close(fdlock);
+
+ if (r == -1) die_read();
+ r >>= 4;
+
+ start = now();
+
+ record = tcpto_buf;
+ for (i = 0;i < r;++i)
+ {
+ if (record[4] >= 1)
+ {
+ byte_copy(&ip,4,record);
+ when = (unsigned long) (unsigned char) record[11];
+ when = (when << 8) + (unsigned long) (unsigned char) record[10];
+ when = (when << 8) + (unsigned long) (unsigned char) record[9];
+ when = (when << 8) + (unsigned long) (unsigned char) record[8];
+
+ substdio_put(subfdout,tmp,ip_fmt(tmp,&ip));
+ substdio_puts(subfdout," timed out ");
+ substdio_put(subfdout,tmp,fmt_ulong(tmp,(unsigned long) (start - when)));
+ substdio_puts(subfdout," seconds ago; # recent timeouts: ");
+ substdio_put(subfdout,tmp,fmt_ulong(tmp,(unsigned long) (unsigned char) record[4]));
+ substdio_puts(subfdout,"\n");
+ }
+ record += 16;
+ }
+
+ die(0);
+}
diff --git a/qmail-upq.sh b/qmail-upq.sh
new file mode 100644
index 0000000..a068196
--- /dev/null
+++ b/qmail-upq.sh
@@ -0,0 +1,14 @@
+cd QMAIL
+cd queue
+for dir in mess info local remote
+do
+ ( cd $dir; find . -type f -print ) | (
+ cd $dir
+ while read path
+ do
+ id=`basename "$path"`
+ sub=`expr "$id" % SPLIT`
+ mv "$path" "$sub"/"$id"
+ done
+ )
+done
diff --git a/qmail-users.9 b/qmail-users.9
new file mode 100644
index 0000000..c44a452
--- /dev/null
+++ b/qmail-users.9
@@ -0,0 +1,113 @@
+.TH qmail-users 5
+.SH NAME
+qmail-users \- assign mail addresses to users
+.SH OVERVIEW
+The file
+.B QMAILHOME/users/assign
+assigns addresses to users. For example,
+
+.EX
+ =joe.shmoe:joe:503:78:/home/joe:::
+.EE
+
+says that mail for
+.B joe.shmoe
+should be delivered to user
+.BR joe ,
+with uid 503 and gid 78,
+as specified by
+.BR /home/joe/.qmail .
+
+Assignments fed to
+.B qmail-newu
+will be used by
+.B qmail-lspawn
+to control
+.BR qmail-local 's
+deliveries.
+See
+.BR qmail-newu (8).
+A change to
+.B QMAILHOME/users/assign
+will have no effect until
+.B qmail-newu
+is run.
+.SH STRUCTURE
+.B QMAILHOME/users/assign
+is a series of assignments, one per line.
+It ends with a line containing a single dot.
+Lines must not contain NUL.
+.SH "SIMPLE ASSIGNMENTS"
+A simple assignment is a line of the form
+
+.EX
+ =local:user:uid:gid:homedir:dash:ext:
+.EE
+
+Here
+.I local
+is an address;
+.IR user ,
+.IR uid ,
+and
+.I gid
+are the account name, uid, and gid
+of the user in charge of
+.IR local ;
+and messages to
+.I local
+will be controlled by
+.IR homedir\fB/.qmail\fIdashext .
+
+If there are several assignments for the same
+.I local
+address,
+.B qmail-lspawn
+will use the first one.
+
+.I local
+is interpreted without regard to case.
+.SH "WILDCARD ASSIGNMENTS"
+A wildcard assignment is a line of the form
+
+.EX
+ +loc:user:uid:gid:homedir:dash:pre:
+.EE
+
+This assignment applies to any address beginning with
+.IR loc ,
+including
+.I loc
+itself.
+It means the same as
+
+.EX
+ =locext:user:uid:gid:homedir:dash:preext:
+.EE
+
+for every string
+.IR ext .
+
+A more specific wildcard assignment overrides a less specific
+assignment, and a simple assignment overrides any wildcard assignment.
+For example:
+
+.EX
+ +:alias:7790:2108:QMAILHOME/alias:-::
+ +joe-:joe:507:100:/home/joe:-::
+ =joe:joe:507:100:/home/joe:::
+.EE
+
+The address
+.B joe
+is handled by the third line;
+the address
+.B joe-direct
+is handled by the second line;
+the address
+.B bill
+is handled by the first line.
+.SH "SEE ALSO"
+qmail-pw2u(8),
+qmail-newu(8),
+qmail-lspawn(8)
diff --git a/qmail.7 b/qmail.7
new file mode 100644
index 0000000..df9c885
--- /dev/null
+++ b/qmail.7
@@ -0,0 +1,68 @@
+.TH qmail 7
+.SH "NAME"
+qmail \- overview of qmail documentation
+.SH "INTRODUCTION"
+.B qmail
+is a secure, reliable, efficient, simple message transfer agent.
+
+Users who want to control incoming messages
+should read
+.BR dot-qmail (5).
+Available commands for the
+.B .qmail
+file include
+.BR qbiff (1),
+.BR qreceipt (1),
+.BR forward (1),
+.BR bouncesaying (1),
+and
+.BR condredirect (1).
+Other helpful commands include
+.BR maildirmake (1),
+.BR maildir2mbox (1),
+and
+.BR maildirwatch (1).
+
+System administrators who want to control the entire
+.B qmail
+system should start with
+.BR qmail-control (5)
+and
+.BR qmail-start (8).
+There are three queue-monitoring tools:
+.BR qmail-qread (8),
+.BR qmail-qstat (8),
+and
+.BR qmail-tcpto (8).
+Incoming SMTP connections are handled by
+.BR qmail-smtpd (8).
+
+.B qmail
+offers two command-line message-sending interfaces:
+.BR qmail-inject (8)
+and
+.BR mailsubj (1).
+For background information on Internet mail messages,
+see
+.BR addresses (5),
+.BR envelopes (5),
+.BR qmail-header (5),
+and
+.BR forgeries (7).
+
+Miscellaneous documentation includes
+.BR qmail-limits (7)
+and
+.BR qmail-pop3d (8).
+
+This documentation describes netqmail version
+1.05
+of
+.BR qmail .
+See
+.B http://pobox.com/~djb/qmail.html
+for other
+.BR qmail -related
+software, and
+.B http://qmail.org/
+for other qmail community contributions.
diff --git a/qmail.c b/qmail.c
new file mode 100644
index 0000000..186c092
--- /dev/null
+++ b/qmail.c
@@ -0,0 +1,136 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "wait.h"
+#include "exit.h"
+#include "fork.h"
+#include "fd.h"
+#include "qmail.h"
+#include "auto_qmail.h"
+#include "env.h"
+
+static char *binqqargs[2] = { 0, 0 } ;
+
+static void setup_qqargs()
+{
+ if(!binqqargs[0])
+ binqqargs[0] = env_get("QMAILQUEUE");
+ if(!binqqargs[0])
+ binqqargs[0] = "bin/qmail-queue";
+}
+
+int qmail_open(qq)
+struct qmail *qq;
+{
+ int pim[2];
+ int pie[2];
+
+ setup_qqargs();
+
+ if (pipe(pim) == -1) return -1;
+ if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
+
+ switch(qq->pid = vfork()) {
+ case -1:
+ close(pim[0]); close(pim[1]);
+ close(pie[0]); close(pie[1]);
+ return -1;
+ case 0:
+ close(pim[1]);
+ close(pie[1]);
+ if (fd_move(0,pim[0]) == -1) _exit(120);
+ if (fd_move(1,pie[0]) == -1) _exit(120);
+ if (chdir(auto_qmail) == -1) _exit(61);
+ execv(*binqqargs,binqqargs);
+ _exit(120);
+ }
+
+ qq->fdm = pim[1]; close(pim[0]);
+ qq->fde = pie[1]; close(pie[0]);
+ substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
+ qq->flagerr = 0;
+ return 0;
+}
+
+unsigned long qmail_qp(qq) struct qmail *qq;
+{
+ return qq->pid;
+}
+
+void qmail_fail(qq) struct qmail *qq;
+{
+ qq->flagerr = 1;
+}
+
+void qmail_put(qq,s,len) struct qmail *qq; char *s; int len;
+{
+ if (!qq->flagerr) if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1;
+}
+
+void qmail_puts(qq,s) struct qmail *qq; char *s;
+{
+ if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1;
+}
+
+void qmail_from(qq,s) struct qmail *qq; char *s;
+{
+ if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
+ close(qq->fdm);
+ substdio_fdbuf(&qq->ss,write,qq->fde,qq->buf,sizeof(qq->buf));
+ qmail_put(qq,"F",1);
+ qmail_puts(qq,s);
+ qmail_put(qq,"",1);
+}
+
+void qmail_to(qq,s) struct qmail *qq; char *s;
+{
+ qmail_put(qq,"T",1);
+ qmail_puts(qq,s);
+ qmail_put(qq,"",1);
+}
+
+char *qmail_close(qq)
+struct qmail *qq;
+{
+ int wstat;
+ int exitcode;
+
+ qmail_put(qq,"",1);
+ if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
+ close(qq->fde);
+
+ if (wait_pid(&wstat,qq->pid) != qq->pid)
+ return "Zqq waitpid surprise (#4.3.0)";
+ if (wait_crashed(wstat))
+ return "Zqq crashed (#4.3.0)";
+ exitcode = wait_exitcode(wstat);
+
+ switch(exitcode) {
+ case 115: /* compatibility */
+ case 11: return "Denvelope address too long for qq (#5.1.3)";
+ case 31: return "Dmail server permanently rejected message (#5.3.0)";
+ case 51: return "Zqq out of memory (#4.3.0)";
+ case 52: return "Zqq timeout (#4.3.0)";
+ case 53: return "Zqq write error or disk full (#4.3.0)";
+ case 0: if (!qq->flagerr) return ""; /* fall through */
+ case 54: return "Zqq read error (#4.3.0)";
+ case 55: return "Zqq unable to read configuration (#4.3.0)";
+ case 56: return "Zqq trouble making network connection (#4.3.0)";
+ case 61: return "Zqq trouble in home directory (#4.3.0)";
+ case 63:
+ case 64:
+ case 65:
+ case 66:
+ case 62: return "Zqq trouble creating files in queue (#4.3.0)";
+ case 71: return "Zmail server temporarily rejected message (#4.3.0)";
+ case 72: return "Zconnection to mail server timed out (#4.4.1)";
+ case 73: return "Zconnection to mail server rejected (#4.4.1)";
+ case 74: return "Zcommunication with mail server failed (#4.4.2)";
+ case 91: /* fall through */
+ case 81: return "Zqq internal bug (#4.3.0)";
+ case 120: return "Zunable to exec qq (#4.3.0)";
+ default:
+ if ((exitcode >= 11) && (exitcode <= 40))
+ return "Dqq permanent problem (#5.3.0)";
+ return "Zqq temporary problem (#4.3.0)";
+ }
+}
diff --git a/qmail.h b/qmail.h
new file mode 100644
index 0000000..7fa13e2
--- /dev/null
+++ b/qmail.h
@@ -0,0 +1,24 @@
+#ifndef QMAIL_H
+#define QMAIL_H
+
+#include "substdio.h"
+
+struct qmail {
+ int flagerr;
+ unsigned long pid;
+ int fdm;
+ int fde;
+ substdio ss;
+ char buf[1024];
+} ;
+
+extern int qmail_open();
+extern void qmail_put();
+extern void qmail_puts();
+extern void qmail_from();
+extern void qmail_to();
+extern void qmail_fail();
+extern char *qmail_close();
+extern unsigned long qmail_qp();
+
+#endif
diff --git a/qreceipt.1 b/qreceipt.1
new file mode 100644
index 0000000..4d3cd08
--- /dev/null
+++ b/qreceipt.1
@@ -0,0 +1,33 @@
+.TH qreceipt 1
+.SH NAME
+qreceipt \- respond to delivery notice requests
+.SH SYNOPSIS
+in
+.BR .qmail :
+.B |qreceipt
+.I youraddress
+.SH DESCRIPTION
+When a mail message arrives with
+.I youraddress
+listed in a
+.B Notice-Requested-Upon-Delivery-To
+header field,
+.B qreceipt
+sends a success notice back to the envelope sender.
+
+.B WARNING:
+If you create a
+.B .qmail
+file to enable
+.BR qreceipt ,
+make sure to also add a line specifying delivery to your normal mailbox.
+For example:
+
+.EX
+ /home/joe/Mailbox
+.br
+ |qreceipt joe@nowhere.mil
+.EE
+.SH "SEE ALSO"
+dot-qmail(5),
+envelopes(5)
diff --git a/qreceipt.c b/qreceipt.c
new file mode 100644
index 0000000..49b9807
--- /dev/null
+++ b/qreceipt.c
@@ -0,0 +1,131 @@
+#include "sig.h"
+#include "env.h"
+#include "substdio.h"
+#include "stralloc.h"
+#include "subfd.h"
+#include "getln.h"
+#include "alloc.h"
+#include "str.h"
+#include "hfield.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 "quote.h"
+#include "qmail.h"
+
+void die_noreceipt() { _exit(0); }
+void die() { _exit(100); }
+void die_temp() { _exit(111); }
+void die_nomem() {
+ substdio_putsflush(subfderr,"qreceipt: fatal: out of memory\n"); die_temp(); }
+void die_fork() {
+ substdio_putsflush(subfderr,"qreceipt: fatal: unable to fork\n"); die_temp(); }
+void die_qqperm() {
+ substdio_putsflush(subfderr,"qreceipt: fatal: permanent qmail-queue error\n"); die(); }
+void die_qqtemp() {
+ substdio_putsflush(subfderr,"qreceipt: fatal: temporary qmail-queue error\n"); die_temp(); }
+void die_usage() {
+ substdio_putsflush(subfderr,
+ "qreceipt: usage: qreceipt deliveryaddress\n"); die(); }
+void die_read() {
+ if (errno == error_nomem) die_nomem();
+ substdio_putsflush(subfderr,"qreceipt: 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,"qreceipt: fatal: unable to parse this: ");
+ substdio_putflush(subfderr,sa->s,sa->len); die(); }
+
+char *target;
+
+int flagreceipt = 0;
+
+char *returnpath;
+stralloc messageid = {0};
+stralloc sanotice = {0};
+
+int rwnotice(addr) token822_alloc *addr; { token822_reverse(addr);
+ if (token822_unquote(&sanotice,addr) != 1) die_nomem();
+ if (sanotice.len == str_len(target))
+ if (!str_diffn(sanotice.s,target,sanotice.len))
+ flagreceipt = 1;
+ token822_reverse(addr); return 1; }
+
+struct qmail qqt;
+
+stralloc quoted = {0};
+
+void finishheader()
+{
+ char *qqx;
+
+ if (!flagreceipt) die_noreceipt();
+ if (str_equal(returnpath,"")) die_noreceipt();
+ if (str_equal(returnpath,"#@[]")) die_noreceipt();
+
+ if (!quote2(&quoted,returnpath)) die_nomem();
+
+ if (qmail_open(&qqt) == -1) die_fork();
+
+ qmail_puts(&qqt,"From: DELIVERY NOTICE SYSTEM <");
+ qmail_put(&qqt,quoted.s,quoted.len);
+ qmail_puts(&qqt,">\n");
+ qmail_puts(&qqt,"To: <");
+ qmail_put(&qqt,quoted.s,quoted.len);
+ qmail_puts(&qqt,">\n");
+ qmail_puts(&qqt,"Subject: success notice\n\
+\n\
+Hi! This is the qreceipt program. Your message was delivered to the\n\
+following address: ");
+ qmail_puts(&qqt,target);
+ qmail_puts(&qqt,". Thanks for asking.\n");
+ if (messageid.s)
+ {
+ qmail_puts(&qqt,"Your ");
+ qmail_put(&qqt,messageid.s,messageid.len);
+ }
+
+ qmail_from(&qqt,"");
+ qmail_to(&qqt,returnpath);
+ qqx = qmail_close(&qqt);
+
+ if (*qqx)
+ if (*qqx == 'D') die_qqperm();
+ else die_qqtemp();
+}
+
+stralloc hfbuf = {0};
+token822_alloc hfin = {0};
+token822_alloc hfrewrite = {0};
+token822_alloc hfaddr = {0};
+
+void doheaderfield(h)
+stralloc *h;
+{
+ switch(hfield_known(h->s,h->len))
+ {
+ case H_MESSAGEID:
+ if (!stralloc_copy(&messageid,h)) die_nomem();
+ break;
+ case H_NOTICEREQUESTEDUPONDELIVERYTO:
+ doordie(h,token822_parse(&hfin,h,&hfbuf));
+ doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwnotice));
+ break;
+ }
+}
+
+void dobody(h) stralloc *h; { ; }
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ sig_pipeignore();
+ if (!(target = argv[1])) die_usage();
+ if (!(returnpath = env_get("SENDER"))) die_usage();
+ if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1) die_read();
+ die_noreceipt();
+}
diff --git a/qsmhook.c b/qsmhook.c
new file mode 100644
index 0000000..d5b38aa
--- /dev/null
+++ b/qsmhook.c
@@ -0,0 +1,137 @@
+#include "fd.h"
+#include "stralloc.h"
+#include "readwrite.h"
+#include "sgetopt.h"
+#include "wait.h"
+#include "env.h"
+#include "byte.h"
+#include "str.h"
+#include "alloc.h"
+#include "exit.h"
+#include "fork.h"
+#include "case.h"
+#include "subfd.h"
+#include "error.h"
+#include "substdio.h"
+#include "sig.h"
+
+void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
+void die_usage() { die(100,"qsmhook: fatal: incorrect usage\n"); }
+void die_temp() { die(111,"qsmhook: fatal: temporary problem\n"); }
+void die_read() { die(111,"qsmhook: fatal: unable to read message\n"); }
+void die_badcmd() { die(100,"qsmhook: fatal: command not found\n"); }
+
+int flagrpline = 0; char *rpline;
+int flagufline = 1; char *ufline;
+int flagdtline = 0; char *dtline;
+char *host;
+char *sender;
+char *recip;
+
+stralloc newarg = {0};
+
+substdio ssout;
+char outbuf[SUBSTDIO_OUTSIZE];
+substdio ssin;
+char inbuf[SUBSTDIO_INSIZE];
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int pid;
+ int wstat;
+ int pi[2];
+ int opt;
+ char **arg;
+ char *x;
+ int i;
+ int flagesc;
+
+ sig_pipeignore();
+
+ if (!(dtline = env_get("DTLINE"))) die_usage();
+ if (!(rpline = env_get("RPLINE"))) die_usage();
+ if (!(ufline = env_get("UFLINE"))) die_usage();
+ if (!(recip = env_get("LOCAL"))) die_usage();
+ if (!(host = env_get("HOST"))) die_usage();
+ if (!(sender = env_get("SENDER"))) die_usage();
+
+ while ((opt = getopt(argc,argv,"DFlMmnPsx:")) != opteof)
+ switch(opt)
+ {
+ case 'D': case 'F': case 'M': break; /* be serious */
+ case 'l': flagdtline = 1; break; /* also return-receipt-to? blech */
+ case 'm': break; /* we only handle one recipient anyway */
+ case 'n': flagufline = 0; break;
+ case 's': break; /* could call quote() otherwise, i suppose... */
+ case 'P': flagrpline = 1; break;
+ case 'x':
+ if (case_starts(recip,optarg))
+ recip += str_len(optarg);
+ break;
+ default:
+ _exit(100);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv) die_usage();
+
+ for (arg = argv;x = *arg;++arg)
+ {
+ if (!stralloc_copys(&newarg,"")) die_temp();
+ flagesc = 0;
+ for (i = 0;x[i];++i)
+ if (flagesc)
+ {
+ switch(x[i])
+ {
+ case '%': if (!stralloc_cats(&newarg,"%")) die_temp(); break;
+ case 'g': if (!stralloc_cats(&newarg,sender)) die_temp(); break;
+ case 'h': if (!stralloc_cats(&newarg,host)) die_temp(); break;
+ case 'u': if (!stralloc_cats(&newarg,recip)) die_temp(); break;
+ }
+ flagesc = 0;
+ }
+ else
+ if (x[i] == '%')
+ flagesc = 1;
+ else
+ if (!stralloc_append(&newarg,&x[i])) die_temp();
+ if (!stralloc_0(&newarg)) die_temp();
+ i = str_len(newarg.s) + 1;
+ if (!(x = alloc(i))) die_temp();
+ byte_copy(x,i,newarg.s);
+ *arg = x;
+ }
+
+ if (pipe(pi) == -1) die_temp();
+
+ switch(pid = fork())
+ {
+ case -1:
+ die_temp();
+ case 0:
+ close(pi[1]);
+ if (fd_move(0,pi[0]) == -1) die_temp();
+ sig_pipedefault();
+ execvp(*argv,argv);
+ if (error_temp(errno)) die_temp();
+ die_badcmd();
+ }
+ close(pi[0]);
+
+ substdio_fdbuf(&ssout,write,pi[1],outbuf,sizeof(outbuf));
+ substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+ if (flagufline) substdio_bputs(&ssout,ufline);
+ if (flagrpline) substdio_bputs(&ssout,rpline);
+ if (flagdtline) substdio_bputs(&ssout,dtline);
+ if (substdio_copy(&ssout,&ssin) == -2) die_read();
+ substdio_flush(&ssout);
+ close(pi[1]);
+
+ if (wait_pid(&wstat,pid) == -1) die_temp();
+ if (wait_crashed(wstat)) die_temp();
+ _exit(wait_exitcode(wstat));
+}
diff --git a/qsutil.c b/qsutil.c
new file mode 100644
index 0000000..80c619d
--- /dev/null
+++ b/qsutil.c
@@ -0,0 +1,46 @@
+#include "stralloc.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "qsutil.h"
+
+static stralloc foo = {0};
+
+static char errbuf[1];
+static struct substdio sserr = SUBSTDIO_FDBUF(write,0,errbuf,1);
+
+void logsa(sa) stralloc *sa; {
+ substdio_putflush(&sserr,sa->s,sa->len); }
+void log1(s1) char *s1; {
+ substdio_putsflush(&sserr,s1); }
+void log2(s1,s2) char *s1; char *s2; {
+ substdio_putsflush(&sserr,s1);
+ substdio_putsflush(&sserr,s2); }
+void log3(s1,s2,s3) char *s1; char *s2; char *s3; {
+ substdio_putsflush(&sserr,s1);
+ substdio_putsflush(&sserr,s2);
+ substdio_putsflush(&sserr,s3); }
+void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); }
+
+void pausedir(dir) char *dir;
+{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); }
+
+static int issafe(ch) char ch;
+{
+ if (ch == '%') return 0; /* general principle: allman's code is crap */
+ if (ch < 33) return 0;
+ if (ch > 126) return 0;
+ return 1;
+}
+
+void logsafe(s) char *s;
+{
+ int i;
+ while (!stralloc_copys(&foo,s)) nomem();
+ for (i = 0;i < foo.len;++i)
+ if (foo.s[i] == '\n')
+ foo.s[i] = '/';
+ else
+ if (!issafe(foo.s[i]))
+ foo.s[i] = '_';
+ logsa(&foo);
+}
diff --git a/qsutil.h b/qsutil.h
new file mode 100644
index 0000000..a746845
--- /dev/null
+++ b/qsutil.h
@@ -0,0 +1,12 @@
+#ifndef QSUTIL_H
+#define QSUTIL_H
+
+extern void log1();
+extern void log2();
+extern void log3();
+extern void logsa();
+extern void nomem();
+extern void pausedir();
+extern void logsafe();
+
+#endif
diff --git a/quote.c b/quote.c
new file mode 100644
index 0000000..659cfcd
--- /dev/null
+++ b/quote.c
@@ -0,0 +1,83 @@
+#include "stralloc.h"
+#include "str.h"
+#include "quote.h"
+
+/*
+quote() encodes a box as per rfc 821 and rfc 822,
+while trying to do as little quoting as possible.
+no, 821 and 822 don't have the same encoding. they're not even close.
+no special encoding here for bytes above 127.
+*/
+
+static char ok[128] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+,0,7,0,7,7,7,7,7,0,0,7,7,0,7,7,7 ,7,7,7,7,7,7,7,7,7,7,0,0,0,7,0,7
+,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,0,0,0,7,7
+,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0
+} ;
+
+static int doit(saout,sain)
+stralloc *saout;
+stralloc *sain;
+{
+ char ch;
+ int i;
+ int j;
+
+ if (!stralloc_ready(saout,sain->len * 2 + 2)) return 0;
+ j = 0;
+ saout->s[j++] = '"';
+ for (i = 0;i < sain->len;++i)
+ {
+ ch = sain->s[i];
+ if ((ch == '\r') || (ch == '\n') || (ch == '"') || (ch == '\\'))
+ saout->s[j++] = '\\';
+ saout->s[j++] = ch;
+ }
+ saout->s[j++] = '"';
+ saout->len = j;
+ return 1;
+}
+
+int quote_need(s,n)
+char *s;
+unsigned int n;
+{
+ unsigned char uch;
+ int i;
+ if (!n) return 1;
+ for (i = 0;i < n;++i)
+ {
+ uch = s[i];
+ if (uch >= 128) return 1;
+ if (!ok[uch]) return 1;
+ }
+ if (s[0] == '.') return 1;
+ if (s[n - 1] == '.') return 1;
+ for (i = 0;i < n - 1;++i) if (s[i] == '.') if (s[i + 1] == '.') return 1;
+ return 0;
+}
+
+int quote(saout,sain)
+stralloc *saout;
+stralloc *sain;
+{
+ if (quote_need(sain->s,sain->len)) return doit(saout,sain);
+ return stralloc_copy(saout,sain);
+}
+
+static stralloc foo = {0};
+
+int quote2(sa,s)
+stralloc *sa;
+char *s;
+{
+ int j;
+ if (!*s) return stralloc_copys(sa,s);
+ j = str_rchr(s,'@');
+ if (!stralloc_copys(&foo,s)) return 0;
+ if (!s[j]) return quote(sa,&foo);
+ foo.len = j;
+ if (!quote(sa,&foo)) return 0;
+ return stralloc_cats(sa,s + j);
+}
diff --git a/quote.h b/quote.h
new file mode 100644
index 0000000..8ab7356
--- /dev/null
+++ b/quote.h
@@ -0,0 +1,8 @@
+#ifndef QUOTE_H
+#define QUOTE_H
+
+extern int quote_need();
+extern int quote();
+extern int quote2();
+
+#endif
diff --git a/rcpthosts.c b/rcpthosts.c
new file mode 100644
index 0000000..1bc3018
--- /dev/null
+++ b/rcpthosts.c
@@ -0,0 +1,60 @@
+#include "cdb.h"
+#include "byte.h"
+#include "open.h"
+#include "error.h"
+#include "control.h"
+#include "constmap.h"
+#include "stralloc.h"
+#include "rcpthosts.h"
+
+static int flagrh = 0;
+static stralloc rh = {0};
+static struct constmap maprh;
+static int fdmrh;
+
+int rcpthosts_init()
+{
+ flagrh = control_readfile(&rh,"control/rcpthosts",0);
+ if (flagrh != 1) return flagrh;
+ if (!constmap_init(&maprh,rh.s,rh.len,0)) return flagrh = -1;
+ fdmrh = open_read("control/morercpthosts.cdb");
+ if (fdmrh == -1) if (errno != error_noent) return flagrh = -1;
+ return 0;
+}
+
+static stralloc host = {0};
+
+int rcpthosts(buf,len)
+char *buf;
+int len;
+{
+ int j;
+
+ if (flagrh != 1) return 1;
+
+ j = byte_rchr(buf,len,'@');
+ if (j >= len) return 1; /* presumably envnoathost is acceptable */
+
+ ++j; buf += j; len -= j;
+
+ if (!stralloc_copyb(&host,buf,len)) return -1;
+ buf = host.s;
+ case_lowerb(buf,len);
+
+ for (j = 0;j < len;++j)
+ if (!j || (buf[j] == '.'))
+ if (constmap(&maprh,buf + j,len - j)) return 1;
+
+ if (fdmrh != -1) {
+ uint32 dlen;
+ int r;
+
+ for (j = 0;j < len;++j)
+ if (!j || (buf[j] == '.')) {
+ r = cdb_seek(fdmrh,buf + j,len - j,&dlen);
+ if (r) return r;
+ }
+ }
+
+ return 0;
+}
diff --git a/rcpthosts.h b/rcpthosts.h
new file mode 100644
index 0000000..5e7e6cc
--- /dev/null
+++ b/rcpthosts.h
@@ -0,0 +1,7 @@
+#ifndef RCPTHOSTS_H
+#define RCPTHOSTS_H
+
+extern int rcpthosts_init();
+extern int rcpthosts();
+
+#endif
diff --git a/readsubdir.c b/readsubdir.c
new file mode 100644
index 0000000..81aa241
--- /dev/null
+++ b/readsubdir.c
@@ -0,0 +1,49 @@
+#include "readsubdir.h"
+#include "fmt.h"
+#include "scan.h"
+#include "str.h"
+#include "auto_split.h"
+
+void readsubdir_init(rs,name,pause)
+readsubdir *rs;
+char *name;
+void (*pause)();
+{
+ rs->name = name;
+ rs->pause = pause;
+ rs->dir = 0;
+ rs->pos = 0;
+}
+
+static char namepos[FMT_ULONG + 4 + READSUBDIR_NAMELEN];
+
+int readsubdir_next(rs,id)
+readsubdir *rs;
+unsigned long *id;
+{
+ direntry *d;
+ unsigned int len;
+
+ if (!rs->dir)
+ {
+ if (rs->pos >= auto_split) return 0;
+ if (str_len(rs->name) > READSUBDIR_NAMELEN) { rs->pos++; return -1; }
+ len = 0;
+ len += fmt_str(namepos + len,rs->name);
+ namepos[len++] = '/';
+ len += fmt_ulong(namepos + len,(unsigned long) rs->pos);
+ namepos[len] = 0;
+ while (!(rs->dir = opendir(namepos))) rs->pause(namepos);
+ rs->pos++;
+ return -1;
+ }
+
+ d = readdir(rs->dir);
+ if (!d) { closedir(rs->dir); rs->dir = 0; return -1; }
+
+ if (str_equal(d->d_name,".")) return -1;
+ if (str_equal(d->d_name,"..")) return -1;
+ len = scan_ulong(d->d_name,id);
+ if (!len || d->d_name[len]) return -2;
+ return 1;
+}
diff --git a/readsubdir.h b/readsubdir.h
new file mode 100644
index 0000000..353942f
--- /dev/null
+++ b/readsubdir.h
@@ -0,0 +1,20 @@
+#ifndef READSUBDIR_H
+#define READSUBDIR_H
+
+#include "direntry.h"
+
+typedef struct readsubdir
+ {
+ DIR *dir;
+ int pos;
+ char *name;
+ void (*pause)();
+ }
+readsubdir;
+
+extern void readsubdir_init();
+extern int readsubdir_next();
+
+#define READSUBDIR_NAMELEN 10
+
+#endif
diff --git a/readwrite.h b/readwrite.h
new file mode 100644
index 0000000..2a64968
--- /dev/null
+++ b/readwrite.h
@@ -0,0 +1,7 @@
+#ifndef READWRITE_H
+#define READWRITE_H
+
+extern int read();
+extern int write();
+
+#endif
diff --git a/received.c b/received.c
new file mode 100644
index 0000000..07706d5
--- /dev/null
+++ b/received.c
@@ -0,0 +1,71 @@
+#include "fmt.h"
+#include "qmail.h"
+#include "now.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "received.h"
+
+static int issafe(ch) char ch;
+{
+ if (ch == '.') return 1;
+ if (ch == '@') return 1;
+ if (ch == '%') return 1;
+ if (ch == '+') return 1;
+ if (ch == '/') return 1;
+ if (ch == '=') return 1;
+ if (ch == ':') return 1;
+ if (ch == '-') return 1;
+ if ((ch >= 'a') && (ch <= 'z')) return 1;
+ if ((ch >= 'A') && (ch <= 'Z')) return 1;
+ if ((ch >= '0') && (ch <= '9')) return 1;
+ return 0;
+}
+
+void safeput(qqt,s)
+struct qmail *qqt;
+char *s;
+{
+ char ch;
+ while (ch = *s++) {
+ if (!issafe(ch)) ch = '?';
+ qmail_put(qqt,&ch,1);
+ }
+}
+
+static char buf[DATE822FMT];
+
+/* "Received: from relay1.uu.net (HELO uunet.uu.net) (7@192.48.96.5)\n" */
+/* " by silverton.berkeley.edu with SMTP; 26 Sep 1995 04:46:54 -0000\n" */
+
+void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo)
+struct qmail *qqt;
+char *protocol;
+char *local;
+char *remoteip;
+char *remotehost;
+char *remoteinfo;
+char *helo;
+{
+ struct datetime dt;
+
+ qmail_puts(qqt,"Received: from ");
+ safeput(qqt,remotehost);
+ if (helo) {
+ qmail_puts(qqt," (HELO ");
+ safeput(qqt,helo);
+ qmail_puts(qqt,")");
+ }
+ qmail_puts(qqt," (");
+ if (remoteinfo) {
+ safeput(qqt,remoteinfo);
+ qmail_puts(qqt,"@");
+ }
+ safeput(qqt,remoteip);
+ qmail_puts(qqt,")\n by ");
+ safeput(qqt,local);
+ qmail_puts(qqt," with ");
+ qmail_puts(qqt,protocol);
+ qmail_puts(qqt,"; ");
+ datetime_tai(&dt,now());
+ qmail_put(qqt,buf,date822fmt(buf,&dt));
+}
diff --git a/received.h b/received.h
new file mode 100644
index 0000000..4e39dda
--- /dev/null
+++ b/received.h
@@ -0,0 +1,6 @@
+#ifndef RECEIVED_H
+#define RECEIVED_H
+
+extern void received();
+
+#endif
diff --git a/remoteinfo.c b/remoteinfo.c
new file mode 100644
index 0000000..c7abd70
--- /dev/null
+++ b/remoteinfo.c
@@ -0,0 +1,77 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include "byte.h"
+#include "substdio.h"
+#include "ip.h"
+#include "fmt.h"
+#include "timeoutconn.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+#include "remoteinfo.h"
+
+static char line[999];
+static int t;
+
+static int mywrite(fd,buf,len) int fd; char *buf; int len;
+{
+ return timeoutwrite(t,fd,buf,len);
+}
+static int myread(fd,buf,len) int fd; char *buf; int len;
+{
+ return timeoutread(t,fd,buf,len);
+}
+
+char *remoteinfo_get(ipr,rp,ipl,lp,timeout)
+struct ip_address *ipr;
+unsigned long rp;
+struct ip_address *ipl;
+unsigned long lp;
+int timeout;
+{
+ char *x;
+ int s;
+ struct sockaddr_in sin;
+ substdio ss;
+ char buf[32];
+ unsigned int len;
+ int numcolons;
+ char ch;
+
+ t = timeout;
+
+ s = socket(AF_INET,SOCK_STREAM,0);
+ if (s == -1) return 0;
+
+ byte_zero(&sin,sizeof(sin));
+ sin.sin_family = AF_INET;
+ byte_copy(&sin.sin_addr,4,ipl);
+ sin.sin_port = 0;
+ if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; }
+ if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; }
+ fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY);
+
+ len = 0;
+ len += fmt_ulong(line + len,rp);
+ len += fmt_str(line + len," , ");
+ len += fmt_ulong(line + len,lp);
+ len += fmt_str(line + len,"\r\n");
+
+ substdio_fdbuf(&ss,mywrite,s,buf,sizeof buf);
+ if (substdio_putflush(&ss,line,len) == -1) { close(s); return 0; }
+
+ substdio_fdbuf(&ss,myread,s,buf,sizeof buf);
+ x = line;
+ numcolons = 0;
+ for (;;) {
+ if (substdio_get(&ss,&ch,1) != 1) { close(s); return 0; }
+ if ((ch == ' ') || (ch == '\t') || (ch == '\r')) continue;
+ if (ch == '\n') break;
+ if (numcolons < 3) { if (ch == ':') ++numcolons; }
+ else { *x++ = ch; if (x == line + sizeof(line) - 1) break; }
+ }
+ *x = 0;
+ close(s);
+ return line;
+}
diff --git a/remoteinfo.h b/remoteinfo.h
new file mode 100644
index 0000000..d5d9097
--- /dev/null
+++ b/remoteinfo.h
@@ -0,0 +1,6 @@
+#ifndef REMOTEINFO_H
+#define REMOTEINFO_H
+
+extern char *remoteinfo_get();
+
+#endif
diff --git a/scan.h b/scan.h
new file mode 100644
index 0000000..53ce703
--- /dev/null
+++ b/scan.h
@@ -0,0 +1,27 @@
+#ifndef SCAN_H
+#define SCAN_H
+
+extern unsigned int scan_uint();
+extern unsigned int scan_xint();
+extern unsigned int scan_nbbint();
+extern unsigned int scan_ushort();
+extern unsigned int scan_xshort();
+extern unsigned int scan_nbbshort();
+extern unsigned int scan_ulong();
+extern unsigned int scan_xlong();
+extern unsigned int scan_nbblong();
+
+extern unsigned int scan_plusminus();
+extern unsigned int scan_0x();
+
+extern unsigned int scan_whitenskip();
+extern unsigned int scan_nonwhitenskip();
+extern unsigned int scan_charsetnskip();
+extern unsigned int scan_noncharsetnskip();
+
+extern unsigned int scan_strncmp();
+extern unsigned int scan_memcmp();
+
+extern unsigned int scan_long();
+
+#endif
diff --git a/scan_8long.c b/scan_8long.c
new file mode 100644
index 0000000..8b3a6df
--- /dev/null
+++ b/scan_8long.c
@@ -0,0 +1,11 @@
+#include "scan.h"
+
+unsigned int scan_8long(s,u) register char *s; register unsigned long *u;
+{
+ register unsigned int pos; register unsigned long result;
+ register unsigned long c;
+ pos = 0; result = 0;
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 8)
+ { result = result * 8 + c; ++pos; }
+ *u = result; return pos;
+}
diff --git a/scan_ulong.c b/scan_ulong.c
new file mode 100644
index 0000000..27c41ea
--- /dev/null
+++ b/scan_ulong.c
@@ -0,0 +1,11 @@
+#include "scan.h"
+
+unsigned int scan_ulong(s,u) register char *s; register unsigned long *u;
+{
+ register unsigned int pos; register unsigned long result;
+ register unsigned long c;
+ pos = 0; result = 0;
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10)
+ { result = result * 10 + c; ++pos; }
+ *u = result; return pos;
+}
diff --git a/seek.h b/seek.h
new file mode 100644
index 0000000..964fba3
--- /dev/null
+++ b/seek.h
@@ -0,0 +1,15 @@
+#ifndef SEEK_H
+#define SEEK_H
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur();
+
+extern int seek_set();
+extern int seek_end();
+
+extern int seek_trunc();
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+#endif
diff --git a/seek_cur.c b/seek_cur.c
new file mode 100644
index 0000000..c8a3ee8
--- /dev/null
+++ b/seek_cur.c
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define CUR 1 /* sigh */
+
+seek_pos seek_cur(fd) int fd;
+{ return lseek(fd,(off_t) 0,CUR); }
diff --git a/seek_end.c b/seek_end.c
new file mode 100644
index 0000000..8a7b3c5
--- /dev/null
+++ b/seek_end.c
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define END 2 /* sigh */
+
+int seek_end(fd) int fd;
+{ if (lseek(fd,(off_t) 0,END) == -1) return -1; return 0; }
diff --git a/seek_set.c b/seek_set.c
new file mode 100644
index 0000000..f540664
--- /dev/null
+++ b/seek_set.c
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define SET 0 /* sigh */
+
+int seek_set(fd,pos) int fd; seek_pos pos;
+{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; }
diff --git a/seek_trunc.c b/seek_trunc.c
new file mode 100644
index 0000000..6a1a73e
--- /dev/null
+++ b/seek_trunc.c
@@ -0,0 +1,5 @@
+#include <sys/types.h>
+#include "seek.h"
+
+int seek_trunc(fd,pos) int fd; seek_pos pos;
+{ return ftruncate(fd,(off_t) pos); }
diff --git a/select.h1 b/select.h1
new file mode 100644
index 0000000..32d0968
--- /dev/null
+++ b/select.h1
@@ -0,0 +1,8 @@
+#ifndef SELECT_H
+#define SELECT_H
+
+#include <sys/types.h>
+#include <sys/time.h>
+extern int select();
+
+#endif
diff --git a/select.h2 b/select.h2
new file mode 100644
index 0000000..eb4b8fe
--- /dev/null
+++ b/select.h2
@@ -0,0 +1,9 @@
+#ifndef SELECT_H
+#define SELECT_H
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+extern int select();
+
+#endif
diff --git a/sendmail.c b/sendmail.c
new file mode 100644
index 0000000..b62fef8
--- /dev/null
+++ b/sendmail.c
@@ -0,0 +1,162 @@
+#include "sgetopt.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "alloc.h"
+#include "auto_qmail.h"
+#include "exit.h"
+#include "env.h"
+#include "str.h"
+
+void nomem()
+{
+ substdio_putsflush(subfderr,"sendmail: fatal: out of memory\n");
+ _exit(111);
+}
+
+void die_usage()
+{
+ substdio_putsflush(subfderr,"sendmail: usage: sendmail [ -t ] [ -fsender ] [ -Fname ] [ -bp ] [ -bs ] [ arg ... ]\n");
+ _exit(100);
+}
+
+char *smtpdarg[] = { "bin/qmail-smtpd", 0 };
+void smtpd()
+{
+ if (!env_get("PROTO")) {
+ if (!env_put("RELAYCLIENT=")) nomem();
+ if (!env_put("DATABYTES=0")) nomem();
+ if (!env_put("PROTO=TCP")) nomem();
+ if (!env_put("TCPLOCALIP=127.0.0.1")) nomem();
+ if (!env_put("TCPLOCALHOST=localhost")) nomem();
+ if (!env_put("TCPREMOTEIP=127.0.0.1")) nomem();
+ if (!env_put("TCPREMOTEHOST=localhost")) nomem();
+ if (!env_put("TCPREMOTEINFO=sendmail-bs")) nomem();
+ }
+ execv(*smtpdarg,smtpdarg);
+ substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-smtpd\n");
+ _exit(111);
+}
+
+char *qreadarg[] = { "bin/qmail-qread", 0 };
+void mailq()
+{
+ execv(*qreadarg,qreadarg);
+ substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-qread\n");
+ _exit(111);
+}
+
+void do_sender(s)
+const char *s;
+{
+ char *x;
+ int n;
+ int a;
+ int i;
+
+ env_unset("QMAILNAME");
+ env_unset("MAILNAME");
+ env_unset("NAME");
+ env_unset("QMAILHOST");
+ env_unset("MAILHOST");
+
+ n = str_len(s);
+ a = str_rchr(s, '@');
+ if (a == n)
+ {
+ env_put2("QMAILUSER", s);
+ return;
+ }
+ env_put2("QMAILHOST", s + a + 1);
+
+ x = (char *) alloc((a + 1) * sizeof(char));
+ if (!x) nomem();
+ for (i = 0; i < a; i++)
+ x[i] = s[i];
+ x[i] = 0;
+ env_put2("QMAILUSER", x);
+ alloc_free(x);
+}
+
+int flagh;
+char *sender;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int opt;
+ char **qiargv;
+ char **arg;
+ int i;
+
+ if (chdir(auto_qmail) == -1) {
+ substdio_putsflush(subfderr,"sendmail: fatal: unable to switch to qmail home directory\n");
+ _exit(111);
+ }
+
+ flagh = 0;
+ sender = 0;
+ while ((opt = getopt(argc,argv,"vimte:f:p:o:B:F:EJxb:")) != opteof)
+ switch(opt) {
+ case 'B': break;
+ case 't': flagh = 1; break;
+ case 'f': sender = optarg; break;
+ case 'F': if (!env_put2("MAILNAME",optarg)) nomem(); break;
+ case 'p': break; /* could generate a Received line from optarg */
+ case 'v': break;
+ case 'i': break; /* what an absurd concept */
+ case 'x': break; /* SVR4 stupidity */
+ case 'm': break; /* twisted-paper-path blindness, incompetent design */
+ case 'e': break; /* qmail has only one error mode */
+ case 'o':
+ switch(optarg[0]) {
+ case 'd': break; /* qmail has only one delivery mode */
+ case 'e': break; /* see 'e' above */
+ case 'i': break; /* see 'i' above */
+ case 'm': break; /* see 'm' above */
+ }
+ break;
+ case 'E': case 'J': /* Sony NEWS-OS */
+ while (argv[optind][optpos]) ++optpos; /* skip optional argument */
+ break;
+ case 'b':
+ switch(optarg[0]) {
+ case 'm': break;
+ case 'p': mailq();
+ case 's': smtpd();
+ default: die_usage();
+ }
+ break;
+ default:
+ die_usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (str_equal(optprogname,"mailq"))
+ mailq();
+
+ if (str_equal(optprogname,"newaliases")) {
+ substdio_putsflush(subfderr,"sendmail: fatal: please use fastforward/newaliases instead\n");
+ _exit(100);
+ }
+
+ qiargv = (char **) alloc((argc + 10) * sizeof(char *));
+ if (!qiargv) nomem();
+
+ arg = qiargv;
+ *arg++ = "bin/qmail-inject";
+ *arg++ = (flagh ? "-H" : "-a");
+ if (sender) {
+ *arg++ = "-f";
+ *arg++ = sender;
+ do_sender(sender);
+ }
+ *arg++ = "--";
+ for (i = 0;i < argc;++i) *arg++ = argv[i];
+ *arg = 0;
+
+ execv(*qiargv,qiargv);
+ substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-inject\n");
+ _exit(111);
+}
diff --git a/sgetopt.3 b/sgetopt.3
new file mode 100644
index 0000000..bde0c2b
--- /dev/null
+++ b/sgetopt.3
@@ -0,0 +1,28 @@
+.TH sgetopt 3
+.SH NAME
+sgetopt \- get option character from command line
+.SH SYNTAX
+.B #include <sgetopt.h>
+.SH DESCRIPTION
+The
+.B sgetopt
+library is just like the
+.B getopt
+library,
+except that it prints errors using
+.B substdio
+rather than
+.BR stdio .
+
+See
+.B getopt(3)
+for interface details.
+.SH VERSION
+sgetopt version 1.9, 931201.
+.SH AUTHOR
+Placed into the public domain by Daniel J. Bernstein.
+.SH "SEE ALSO"
+getopt(3),
+subgetopt(3),
+subfd(3),
+substdio(3)
diff --git a/sgetopt.c b/sgetopt.c
new file mode 100644
index 0000000..a8bffc0
--- /dev/null
+++ b/sgetopt.c
@@ -0,0 +1,54 @@
+/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer
+D. J. Bernstein, djb@pobox.com.
+Depends on subgetopt.h, substdio.h, subfd.h.
+No system requirements.
+19970208: Cleanups.
+931201: Baseline.
+No known patent problems.
+
+Documentation in sgetopt.3.
+*/
+
+#include "substdio.h"
+#include "subfd.h"
+#define SGETOPTNOSHORT
+#include "sgetopt.h"
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define getopt sgetoptmine
+#define optind subgetoptind
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+
+int opterr = 1;
+char *optprogname = 0;
+
+int getopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+ int c;
+ char *s;
+
+ if (!optprogname) {
+ optprogname = *argv;
+ if (!optprogname) optprogname = "";
+ for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
+ }
+ c = subgetopt(argc,argv,opts);
+ if (opterr)
+ if (c == '?') {
+ char chp[2]; chp[0] = optproblem; chp[1] = '\n';
+ substdio_puts(subfderr,optprogname);
+ if (argv[optind] && (optind < argc))
+ substdio_puts(subfderr,": illegal option -- ");
+ else
+ substdio_puts(subfderr,": option requires an argument -- ");
+ substdio_put(subfderr,chp,2);
+ substdio_flush(subfderr);
+ }
+ return c;
+}
diff --git a/sgetopt.h b/sgetopt.h
new file mode 100644
index 0000000..5f89127
--- /dev/null
+++ b/sgetopt.h
@@ -0,0 +1,21 @@
+#ifndef SGETOPT_H
+#define SGETOPT_H
+
+#ifndef SGETOPTNOSHORT
+#define getopt sgetoptmine
+#define optarg subgetoptarg
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+#define opteof subgetoptdone
+#endif
+
+#include "subgetopt.h"
+
+extern int sgetoptmine();
+extern int sgetopterr;
+extern char *sgetoptprogname;
+
+#endif
diff --git a/sig.h b/sig.h
new file mode 100644
index 0000000..9c3a28c
--- /dev/null
+++ b/sig.h
@@ -0,0 +1,43 @@
+#ifndef SIG_H
+#define SIG_H
+
+extern void sig_catch();
+extern void sig_block();
+extern void sig_unblock();
+extern void sig_blocknone();
+extern void sig_pause();
+
+extern void sig_dfl();
+
+extern void sig_miscignore();
+extern void sig_bugcatch();
+
+extern void sig_pipeignore();
+extern void sig_pipedefault();
+
+extern void sig_contblock();
+extern void sig_contunblock();
+extern void sig_contcatch();
+extern void sig_contdefault();
+
+extern void sig_termblock();
+extern void sig_termunblock();
+extern void sig_termcatch();
+extern void sig_termdefault();
+
+extern void sig_alarmblock();
+extern void sig_alarmunblock();
+extern void sig_alarmcatch();
+extern void sig_alarmdefault();
+
+extern void sig_childblock();
+extern void sig_childunblock();
+extern void sig_childcatch();
+extern void sig_childdefault();
+
+extern void sig_hangupblock();
+extern void sig_hangupunblock();
+extern void sig_hangupcatch();
+extern void sig_hangupdefault();
+
+#endif
diff --git a/sig_alarm.c b/sig_alarm.c
new file mode 100644
index 0000000..7092fdc
--- /dev/null
+++ b/sig_alarm.c
@@ -0,0 +1,7 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_alarmblock() { sig_block(SIGALRM); }
+void sig_alarmunblock() { sig_unblock(SIGALRM); }
+void sig_alarmcatch(f) void (*f)(); { sig_catch(SIGALRM,f); }
+void sig_alarmdefault() { sig_catch(SIGALRM,SIG_DFL); }
diff --git a/sig_block.c b/sig_block.c
new file mode 100644
index 0000000..c6b096a
--- /dev/null
+++ b/sig_block.c
@@ -0,0 +1,40 @@
+#include <signal.h>
+#include "sig.h"
+#include "hassgprm.h"
+
+void sig_block(sig)
+int sig;
+{
+#ifdef HASSIGPROCMASK
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
+#else
+ sigblock(1 << (sig - 1));
+#endif
+}
+
+void sig_unblock(sig)
+int sig;
+{
+#ifdef HASSIGPROCMASK
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
+#else
+ sigsetmask(sigsetmask(~0) & ~(1 << (sig - 1)));
+#endif
+}
+
+void sig_blocknone()
+{
+#ifdef HASSIGPROCMASK
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+#else
+ sigsetmask(0);
+#endif
+}
diff --git a/sig_bug.c b/sig_bug.c
new file mode 100644
index 0000000..ae09c07
--- /dev/null
+++ b/sig_bug.c
@@ -0,0 +1,17 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_bugcatch(f) void (*f)();
+{
+ sig_catch(SIGILL,f);
+ sig_catch(SIGABRT,f);
+ sig_catch(SIGFPE,f);
+ sig_catch(SIGBUS,f);
+ sig_catch(SIGSEGV,f);
+#ifdef SIGSYS
+ sig_catch(SIGSYS,f);
+#endif
+#ifdef SIGEMT
+ sig_catch(SIGEMT,f);
+#endif
+}
diff --git a/sig_catch.c b/sig_catch.c
new file mode 100644
index 0000000..1888765
--- /dev/null
+++ b/sig_catch.c
@@ -0,0 +1,18 @@
+#include <signal.h>
+#include "sig.h"
+#include "hassgact.h"
+
+void sig_catch(sig,f)
+int sig;
+void (*f)();
+{
+#ifdef HASSIGACTION
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig,&sa,(struct sigaction *) 0);
+#else
+ signal(sig,f); /* won't work under System V, even nowadays---dorks */
+#endif
+}
diff --git a/sig_child.c b/sig_child.c
new file mode 100644
index 0000000..fd5b39b
--- /dev/null
+++ b/sig_child.c
@@ -0,0 +1,7 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_childblock() { sig_block(SIGCHLD); }
+void sig_childunblock() { sig_unblock(SIGCHLD); }
+void sig_childcatch(f) void (*f)(); { sig_catch(SIGCHLD,f); }
+void sig_childdefault() { sig_catch(SIGCHLD,SIG_DFL); }
diff --git a/sig_hup.c b/sig_hup.c
new file mode 100644
index 0000000..4beb87f
--- /dev/null
+++ b/sig_hup.c
@@ -0,0 +1,7 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_hangupblock() { sig_block(SIGHUP); }
+void sig_hangupunblock() { sig_unblock(SIGHUP); }
+void sig_hangupcatch(f) void (*f)(); { sig_catch(SIGHUP,f); }
+void sig_hangupdefault() { sig_catch(SIGHUP,SIG_DFL); }
diff --git a/sig_misc.c b/sig_misc.c
new file mode 100644
index 0000000..287f6cb
--- /dev/null
+++ b/sig_misc.c
@@ -0,0 +1,17 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_miscignore()
+{
+ sig_catch(SIGVTALRM,SIG_IGN);
+ sig_catch(SIGPROF,SIG_IGN);
+ sig_catch(SIGQUIT,SIG_IGN);
+ sig_catch(SIGINT,SIG_IGN);
+ sig_catch(SIGHUP,SIG_IGN);
+#ifdef SIGXCPU
+ sig_catch(SIGXCPU,SIG_IGN);
+#endif
+#ifdef SIGXFSZ
+ sig_catch(SIGXFSZ,SIG_IGN);
+#endif
+}
diff --git a/sig_pause.c b/sig_pause.c
new file mode 100644
index 0000000..3416734
--- /dev/null
+++ b/sig_pause.c
@@ -0,0 +1,14 @@
+#include <signal.h>
+#include "sig.h"
+#include "hassgprm.h"
+
+void sig_pause()
+{
+#ifdef HASSIGPROCMASK
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigsuspend(&ss);
+#else
+ sigpause(0);
+#endif
+}
diff --git a/sig_pipe.c b/sig_pipe.c
new file mode 100644
index 0000000..594ae7d
--- /dev/null
+++ b/sig_pipe.c
@@ -0,0 +1,5 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
+void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); }
diff --git a/sig_term.c b/sig_term.c
new file mode 100644
index 0000000..ca72cc3
--- /dev/null
+++ b/sig_term.c
@@ -0,0 +1,7 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_termblock() { sig_block(SIGTERM); }
+void sig_termunblock() { sig_unblock(SIGTERM); }
+void sig_termcatch(f) void (*f)(); { sig_catch(SIGTERM,f); }
+void sig_termdefault() { sig_catch(SIGTERM,SIG_DFL); }
diff --git a/slurpclose.c b/slurpclose.c
new file mode 100644
index 0000000..2fcef15
--- /dev/null
+++ b/slurpclose.c
@@ -0,0 +1,19 @@
+#include "stralloc.h"
+#include "readwrite.h"
+#include "slurpclose.h"
+#include "error.h"
+
+int slurpclose(fd,sa,bufsize)
+int fd;
+stralloc *sa;
+int bufsize;
+{
+ int r;
+ for (;;) {
+ if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+ r = read(fd,sa->s + sa->len,bufsize);
+ if (r == -1) if (errno == error_intr) continue;
+ if (r <= 0) { close(fd); return r; }
+ sa->len += r;
+ }
+}
diff --git a/slurpclose.h b/slurpclose.h
new file mode 100644
index 0000000..57e9eec
--- /dev/null
+++ b/slurpclose.h
@@ -0,0 +1,6 @@
+#ifndef SLURPCLOSE_H
+#define SLURPCLOSE_H
+
+extern int slurpclose();
+
+#endif
diff --git a/spawn.c b/spawn.c
new file mode 100644
index 0000000..66475c0
--- /dev/null
+++ b/spawn.c
@@ -0,0 +1,260 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sig.h"
+#include "wait.h"
+#include "substdio.h"
+#include "byte.h"
+#include "str.h"
+#include "alloc.h"
+#include "stralloc.h"
+#include "select.h"
+#include "exit.h"
+#include "coe.h"
+#include "open.h"
+#include "error.h"
+#include "auto_qmail.h"
+#include "auto_uids.h"
+#include "auto_spawn.h"
+
+extern int truncreport;
+extern int spawn();
+extern void report();
+extern void initialize();
+
+struct delivery
+ {
+ int used;
+ int fdin; /* pipe input */
+ int pid; /* zero if child is dead */
+ int wstat; /* if !pid: status of child */
+ int fdout; /* pipe output, -1 if !pid; delays eof until after death */
+ stralloc output;
+ }
+;
+
+struct delivery *d;
+
+void sigchld()
+{
+ int wstat;
+ int pid;
+ int i;
+ while ((pid = wait_nohang(&wstat)) > 0)
+ for (i = 0;i < auto_spawn;++i) if (d[i].used)
+ if (d[i].pid == pid)
+ {
+ close(d[i].fdout); d[i].fdout = -1;
+ d[i].wstat = wstat; d[i].pid = 0;
+ }
+}
+
+int flagwriting = 1;
+
+int okwrite(fd,buf,n) int fd; char *buf; int n;
+{
+ int w;
+ if (!flagwriting) return n;
+ w = write(fd,buf,n);
+ if (w != -1) return w;
+ if (errno == error_intr) return -1;
+ flagwriting = 0; close(fd);
+ return n;
+}
+
+int flagreading = 1;
+char outbuf[1024]; substdio ssout;
+
+int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */
+int flagabort = 0; /* if 1, everything except delnum is garbage */
+int delnum;
+stralloc messid = {0};
+stralloc sender = {0};
+stralloc recip = {0};
+
+void err(s) char *s;
+{
+ char ch; ch = delnum; substdio_put(&ssout,&ch,1);
+ substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1);
+}
+
+void docmd()
+{
+ int f;
+ int i;
+ int j;
+ int fdmess;
+ int pi[2];
+ struct stat st;
+
+ if (flagabort) { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; }
+ if (delnum < 0) { err("ZInternal error: delnum negative. (#4.3.5)\n"); return; }
+ if (delnum >= auto_spawn) { err("ZInternal error: delnum too big. (#4.3.5)\n"); return; }
+ if (d[delnum].used) { err("ZInternal error: delnum in use. (#4.3.5)\n"); return; }
+ for (i = 0;i < messid.len;++i)
+ if (messid.s[i])
+ if (!i || (messid.s[i] != '/'))
+ if ((unsigned char) (messid.s[i] - '0') > 9)
+ { err("DInternal error: messid has nonnumerics. (#5.3.5)\n"); return; }
+ if (messid.len > 100) { err("DInternal error: messid too long. (#5.3.5)\n"); return; }
+ if (!messid.s[0]) { err("DInternal error: messid too short. (#5.3.5)\n"); return; }
+
+ if (!stralloc_copys(&d[delnum].output,""))
+ { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; }
+
+ j = byte_rchr(recip.s,recip.len,'@');
+ if (j >= recip.len) { err("DSorry, address must include host name. (#5.1.3)\n"); return; }
+
+ fdmess = open_read(messid.s);
+ if (fdmess == -1) { err("Zqmail-spawn unable to open message. (#4.3.0)\n"); return; }
+
+ if (fstat(fdmess,&st) == -1)
+ { close(fdmess); err("Zqmail-spawn unable to fstat message. (#4.3.0)\n"); return; }
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+ { close(fdmess); err("ZSorry, message has wrong type. (#4.3.5)\n"); return; }
+ if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */
+ /* your security is already toast at this point. damage control... */
+ { close(fdmess); err("ZSorry, message has wrong owner. (#4.3.5)\n"); return; }
+
+ if (pipe(pi) == -1)
+ { close(fdmess); err("Zqmail-spawn unable to create pipe. (#4.3.0)\n"); return; }
+
+ coe(pi[0]);
+
+ f = spawn(fdmess,pi[1],sender.s,recip.s,j);
+ close(fdmess);
+ if (f == -1)
+ { close(pi[0]); close(pi[1]); err("Zqmail-spawn unable to fork. (#4.3.0)\n"); return; }
+
+ d[delnum].fdin = pi[0];
+ d[delnum].fdout = pi[1]; coe(pi[1]);
+ d[delnum].pid = f;
+ d[delnum].used = 1;
+}
+
+char cmdbuf[1024];
+
+void getcmd()
+{
+ int i;
+ int r;
+ char ch;
+
+ r = read(0,cmdbuf,sizeof(cmdbuf));
+ if (r == 0)
+ { flagreading = 0; return; }
+ if (r == -1)
+ {
+ if (errno != error_intr)
+ flagreading = 0;
+ return;
+ }
+
+ for (i = 0;i < r;++i)
+ {
+ ch = cmdbuf[i];
+ switch(stage)
+ {
+ case 0:
+ delnum = (unsigned int) (unsigned char) ch;
+ messid.len = 0; stage = 1; break;
+ case 1:
+ if (!stralloc_append(&messid,&ch)) flagabort = 1;
+ if (ch) break;
+ sender.len = 0; stage = 2; break;
+ case 2:
+ if (!stralloc_append(&sender,&ch)) flagabort = 1;
+ if (ch) break;
+ recip.len = 0; stage = 3; break;
+ case 3:
+ if (!stralloc_append(&recip,&ch)) flagabort = 1;
+ if (ch) break;
+ docmd();
+ flagabort = 0; stage = 0; break;
+ }
+ }
+}
+
+char inbuf[128];
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char ch;
+ int i;
+ int r;
+ fd_set rfds;
+ int nfds;
+
+ if (chdir(auto_qmail) == -1) _exit(111);
+ if (chdir("queue/mess") == -1) _exit(111);
+ if (!stralloc_copys(&messid,"")) _exit(111);
+ if (!stralloc_copys(&sender,"")) _exit(111);
+ if (!stralloc_copys(&recip,"")) _exit(111);
+
+ d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery));
+ if (!d) _exit(111);
+
+ substdio_fdbuf(&ssout,okwrite,1,outbuf,sizeof(outbuf));
+
+ sig_pipeignore();
+ sig_childcatch(sigchld);
+
+ initialize(argc,argv);
+
+ ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
+
+ for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
+
+ for (;;)
+ {
+ if (!flagreading)
+ {
+ for (i = 0;i < auto_spawn;++i) if (d[i].used) break;
+ if (i >= auto_spawn) _exit(0);
+ }
+ sig_childunblock();
+
+ FD_ZERO(&rfds);
+ if (flagreading) FD_SET(0,&rfds);
+ nfds = 1;
+ for (i = 0;i < auto_spawn;++i) if (d[i].used)
+ { FD_SET(d[i].fdin,&rfds); if (d[i].fdin >= nfds) nfds = d[i].fdin + 1; }
+
+ r = select(nfds,&rfds,(fd_set *) 0,(fd_set *) 0,(struct timeval *) 0);
+ sig_childblock();
+
+ if (r != -1)
+ {
+ if (flagreading)
+ if (FD_ISSET(0,&rfds))
+ getcmd();
+ for (i = 0;i < auto_spawn;++i) if (d[i].used)
+ if (FD_ISSET(d[i].fdin,&rfds))
+ {
+ r = read(d[i].fdin,inbuf,128);
+ if (r == -1)
+ continue; /* read error on a readable pipe? be serious */
+ if (r == 0)
+ {
+ ch = i; substdio_put(&ssout,&ch,1);
+ report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
+ substdio_put(&ssout,"",1);
+ substdio_flush(&ssout);
+ close(d[i].fdin); d[i].used = 0;
+ continue;
+ }
+ while (!stralloc_readyplus(&d[i].output,r)) sleep(10); /*XXX*/
+ byte_copy(d[i].output.s + d[i].output.len,r,inbuf);
+ d[i].output.len += r;
+ if (truncreport > 100)
+ if (d[i].output.len > truncreport)
+ {
+ char *truncmess = "\nError report too long, sorry.\n";
+ d[i].output.len = truncreport - str_len(truncmess) - 3;
+ stralloc_cats(&d[i].output,truncmess);
+ }
+ }
+ }
+ }
+}
diff --git a/splogger.8 b/splogger.8
new file mode 100644
index 0000000..15096b4
--- /dev/null
+++ b/splogger.8
@@ -0,0 +1,60 @@
+.TH splogger 8
+.SH NAME
+splogger \- make entries in syslog
+.SH SYNOPSIS
+.B splogger
+[
+.I tag
+[
+.I fac
+]
+]
+.SH DESCRIPTION
+.B splogger
+reads a series of messages and feeds them to
+.BR syslog .
+At the front of each message it puts
+.I tag
+(default:
+.BR splogger )
+and a numerical timestamp.
+
+.B splogger
+checks for
+.B alert:
+or
+.B warning:
+at the beginning of each message.
+It selects a priority of
+LOG_ALERT, LOG_WARNING, or LOG_INFO accordingly.
+
+.B splogger
+logs messages with facility
+.IR fac .
+.I fac
+(default: 2)
+must be numeric.
+
+.B splogger
+converts unprintable characters to question marks.
+
+.B splogger
+does not log blank lines.
+
+.B splogger
+folds messages after 800 characters,
+since
+.B syslog
+can't handle long messages.
+.B splogger
+uses a + after the timestamp
+to mark folded lines.
+
+Note that the
+.B syslog
+mechanism is inherently unreliable:
+it does not guarantee that messages will be logged.
+It is also very slow.
+.SH "SEE ALSO"
+syslog(3),
+logger(8)
diff --git a/splogger.c b/splogger.c
new file mode 100644
index 0000000..fc49a33
--- /dev/null
+++ b/splogger.c
@@ -0,0 +1,72 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include "error.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "str.h"
+#include "scan.h"
+#include "fmt.h"
+
+char buf[800]; /* syslog truncates long lines (or crashes); GPACIC */
+int bufpos = 0; /* 0 <= bufpos < sizeof(buf) */
+int flagcont = 0;
+int priority; /* defined if flagcont */
+char stamp[FMT_ULONG + FMT_ULONG + 3]; /* defined if flagcont */
+
+void stamp_make()
+{
+ struct timeval tv;
+ char *s;
+ gettimeofday(&tv,(struct timezone *) 0);
+ s = stamp;
+ s += fmt_ulong(s,(unsigned long) tv.tv_sec);
+ *s++ = '.';
+ s += fmt_uint0(s,(unsigned int) tv.tv_usec,6);
+ *s = 0;
+}
+
+void flush()
+{
+ if (bufpos) {
+ buf[bufpos] = 0;
+ if (flagcont)
+ syslog(priority,"%s+%s",stamp,buf); /* logger folds invisibly; GPACIC */
+ else {
+ stamp_make();
+ priority = LOG_INFO;
+ if (str_start(buf,"warning:")) priority = LOG_WARNING;
+ if (str_start(buf,"alert:")) priority = LOG_ALERT;
+ syslog(priority,"%s %s",stamp,buf);
+ flagcont = 1;
+ }
+ }
+ bufpos = 0;
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char ch;
+
+ if (argv[1])
+ if (argv[2]) {
+ unsigned long facility;
+ scan_ulong(argv[2],&facility);
+ openlog(argv[1],0,facility << 3);
+ }
+ else
+ openlog(argv[1],0,LOG_MAIL);
+ else
+ openlog("splogger",0,LOG_MAIL);
+
+ for (;;) {
+ if (substdio_get(subfdin,&ch,1) < 1) _exit(0);
+ if (ch == '\n') { flush(); flagcont = 0; continue; }
+ if (bufpos == sizeof(buf) - 1) flush();
+ if ((ch < 32) || (ch > 126)) ch = '?'; /* logger truncates at 0; GPACIC */
+ buf[bufpos++] = ch;
+ }
+}
diff --git a/str.h b/str.h
new file mode 100644
index 0000000..e00773c
--- /dev/null
+++ b/str.h
@@ -0,0 +1,14 @@
+#ifndef STR_H
+#define STR_H
+
+extern unsigned int str_copy();
+extern int str_diff();
+extern int str_diffn();
+extern unsigned int str_len();
+extern unsigned int str_chr();
+extern unsigned int str_rchr();
+extern int str_start();
+
+#define str_equal(s,t) (!str_diff((s),(t)))
+
+#endif
diff --git a/str_chr.c b/str_chr.c
new file mode 100644
index 0000000..3691826
--- /dev/null
+++ b/str_chr.c
@@ -0,0 +1,19 @@
+#include "str.h"
+
+unsigned int str_chr(s,c)
+register char *s;
+int c;
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ }
+ return t - s;
+}
diff --git a/str_cpy.c b/str_cpy.c
new file mode 100644
index 0000000..453d790
--- /dev/null
+++ b/str_cpy.c
@@ -0,0 +1,16 @@
+#include "str.h"
+
+unsigned int str_copy(s,t)
+register char *s;
+register char *t;
+{
+ register int len;
+
+ len = 0;
+ for (;;) {
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ }
+}
diff --git a/str_diff.c b/str_diff.c
new file mode 100644
index 0000000..18f8927
--- /dev/null
+++ b/str_diff.c
@@ -0,0 +1,17 @@
+#include "str.h"
+
+int str_diff(s,t)
+register char *s;
+register char *t;
+{
+ register char x;
+
+ for (;;) {
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_diffn.c b/str_diffn.c
new file mode 100644
index 0000000..89142f1
--- /dev/null
+++ b/str_diffn.c
@@ -0,0 +1,18 @@
+#include "str.h"
+
+int str_diffn(s,t,len)
+register char *s;
+register char *t;
+unsigned int len;
+{
+ register char x;
+
+ for (;;) {
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_len.c b/str_len.c
new file mode 100644
index 0000000..2d2f88b
--- /dev/null
+++ b/str_len.c
@@ -0,0 +1,15 @@
+#include "str.h"
+
+unsigned int str_len(s)
+register char *s;
+{
+ register char *t;
+
+ t = s;
+ for (;;) {
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ }
+}
diff --git a/str_rchr.c b/str_rchr.c
new file mode 100644
index 0000000..1bf19d3
--- /dev/null
+++ b/str_rchr.c
@@ -0,0 +1,22 @@
+#include "str.h"
+
+unsigned int str_rchr(s,c)
+register char *s;
+int c;
+{
+ register char ch;
+ register char *t;
+ register char *u;
+
+ ch = c;
+ t = s;
+ u = 0;
+ for (;;) {
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ }
+ if (!u) u = t;
+ return u - s;
+}
diff --git a/str_start.c b/str_start.c
new file mode 100644
index 0000000..2750af8
--- /dev/null
+++ b/str_start.c
@@ -0,0 +1,15 @@
+#include "str.h"
+
+int str_start(s,t)
+register char *s;
+register char *t;
+{
+ register char x;
+
+ for (;;) {
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ }
+}
diff --git a/stralloc.3 b/stralloc.3
new file mode 100644
index 0000000..3123521
--- /dev/null
+++ b/stralloc.3
@@ -0,0 +1,160 @@
+.TH stralloc 3
+.SH NAME
+stralloc \- dynamically allocated strings
+.SH SYNTAX
+.B #include <stralloc.h>
+
+int \fBstralloc_ready\fP(&\fIsa\fR,\fIlen\fR);
+.br
+int \fBstralloc_readyplus\fP(&\fIsa\fR,\fIlen\fR);
+
+int \fBstralloc_copy\fP(&\fIsa\fR,&\fIsa2\fR);
+.br
+int \fBstralloc_copys\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_copyb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBstralloc_cat\fP(&\fIsa\fR,&\fIsa2\fR);
+.br
+int \fBstralloc_cats\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_catb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBstralloc_append\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_0\fP(&\fIsa\fR);
+
+int \fBstralloc_starts\fP(&\fIsa\fR,\fIbuf\fR);
+
+stralloc \fIsa\fR = {0};
+.br
+stralloc \fIsa2\fR = {0};
+.br
+unsigned int \fIlen\fR;
+.br
+char *\fIbuf\fR;
+.SH DESCRIPTION
+A
+.B stralloc
+variable holds a string in dynamically allocated space.
+String length is limited only by memory.
+String contents are unrestricted.
+
+The
+.B stralloc
+structure has three components:
+.I sa\fB.s
+is a pointer to the string, or 0 if it is not allocated;
+.I sa\fB.len
+is the number of bytes in the string, if it is allocated;
+.I sa\fB.a
+is the number of bytes allocated for the string, if it is allocated.
+A
+.B stralloc
+variable should be initialized to {0},
+meaning unallocated.
+
+.B stralloc_ready
+makes sure that
+.I sa
+has enough space allocated for
+.I len
+characters.
+It allocates extra space if necessary.
+
+.B stralloc_readyplus
+makes sure that
+.I sa
+has enough space allocated for
+.I len
+characters more than its current length.
+If
+.I sa
+is unallocated,
+.B stralloc_readyplus
+is the same as
+.BR stralloc_ready .
+
+.B stralloc_copy
+copies
+.I sa2
+to
+.IR sa ,
+allocating space if necessary.
+Here
+.I sa2
+is an allocated
+.B stralloc
+variable.
+
+.B stralloc_copys
+copies a 0-terminated string,
+.IR buf ,
+to
+.IR sa ,
+without the 0.
+
+.B stralloc_copyb
+copies
+.I len
+characters from
+.I buf
+to
+.IR sa .
+
+.B stralloc_cat
+appends
+.I sa2
+to
+.IR sa ,
+allocating space if necessary.
+If
+.I sa
+is unallocated,
+.B stralloc_cat
+is the same as
+.BR stralloc_copy .
+
+.B stralloc_cats
+and
+.B stralloc_catb
+are analogous to
+.B stralloc_copys
+and
+.BR stralloc_copyb .
+
+.B stralloc_append
+adds a single character,
+.IR *buf ,
+to
+.IR sa ,
+allocating space if necessary.
+
+.B stralloc_0
+adds a single 0 character
+to
+.IR sa .
+
+.B stralloc_starts
+returns 1 if the 0-terminated string
+.IR buf ,
+without the 0,
+is a prefix of
+.IR sa .
+.SH "ERROR HANDLING"
+If a
+.B stralloc
+routine runs out of memory,
+it leaves
+.I sa
+alone and returns 0,
+setting
+.B errno
+appropriately.
+On success it returns 1;
+this guarantees that
+.I sa
+is allocated.
+.SH "SEE ALSO"
+alloc(3),
+error(3)
diff --git a/stralloc.h b/stralloc.h
new file mode 100644
index 0000000..fca496c
--- /dev/null
+++ b/stralloc.h
@@ -0,0 +1,21 @@
+#ifndef STRALLOC_H
+#define STRALLOC_H
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(stralloc,char,s,len,a)
+
+extern int stralloc_ready();
+extern int stralloc_readyplus();
+extern int stralloc_copy();
+extern int stralloc_cat();
+extern int stralloc_copys();
+extern int stralloc_cats();
+extern int stralloc_copyb();
+extern int stralloc_catb();
+extern int stralloc_append(); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts();
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+#endif
diff --git a/stralloc_arts.c b/stralloc_arts.c
new file mode 100644
index 0000000..1ccb5a4
--- /dev/null
+++ b/stralloc_arts.c
@@ -0,0 +1,12 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_starts(sa,s)
+stralloc *sa;
+char *s;
+{
+ int len;
+ len = str_len(s);
+ return (sa->len >= len) && byte_equal(s,len,sa->s);
+}
diff --git a/stralloc_cat.c b/stralloc_cat.c
new file mode 100644
index 0000000..efbb112
--- /dev/null
+++ b/stralloc_cat.c
@@ -0,0 +1,9 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_cat(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_catb.c b/stralloc_catb.c
new file mode 100644
index 0000000..67dbcc0
--- /dev/null
+++ b/stralloc_catb.c
@@ -0,0 +1,15 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_catb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+ if (!sa->s) return stralloc_copyb(sa,s,n);
+ if (!stralloc_readyplus(sa,n + 1)) return 0;
+ byte_copy(sa->s + sa->len,n,s);
+ sa->len += n;
+ sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
diff --git a/stralloc_cats.c b/stralloc_cats.c
new file mode 100644
index 0000000..d300286
--- /dev/null
+++ b/stralloc_cats.c
@@ -0,0 +1,10 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_cats(sa,s)
+stralloc *sa;
+char *s;
+{
+ return stralloc_catb(sa,s,str_len(s));
+}
diff --git a/stralloc_copy.c b/stralloc_copy.c
new file mode 100644
index 0000000..652aed6
--- /dev/null
+++ b/stralloc_copy.c
@@ -0,0 +1,9 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_copy(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+ return stralloc_copyb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_eady.c b/stralloc_eady.c
new file mode 100644
index 0000000..3a31f4b
--- /dev/null
+++ b/stralloc_eady.c
@@ -0,0 +1,6 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
diff --git a/stralloc_opyb.c b/stralloc_opyb.c
new file mode 100644
index 0000000..ac258b3
--- /dev/null
+++ b/stralloc_opyb.c
@@ -0,0 +1,14 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_copyb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+ if (!stralloc_ready(sa,n + 1)) return 0;
+ byte_copy(sa->s,n,s);
+ sa->len = n;
+ sa->s[n] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
diff --git a/stralloc_opys.c b/stralloc_opys.c
new file mode 100644
index 0000000..fdd7807
--- /dev/null
+++ b/stralloc_opys.c
@@ -0,0 +1,10 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_copys(sa,s)
+stralloc *sa;
+char *s;
+{
+ return stralloc_copyb(sa,s,str_len(s));
+}
diff --git a/stralloc_pend.c b/stralloc_pend.c
new file mode 100644
index 0000000..a3443b8
--- /dev/null
+++ b/stralloc_pend.c
@@ -0,0 +1,5 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
diff --git a/strerr.h b/strerr.h
new file mode 100644
index 0000000..d18e833
--- /dev/null
+++ b/strerr.h
@@ -0,0 +1,80 @@
+#ifndef STRERR_H
+#define STRERR_H
+
+struct strerr
+ {
+ struct strerr *who;
+ char *x;
+ char *y;
+ char *z;
+ }
+;
+
+extern struct strerr strerr_sys;
+extern void strerr_sysinit();
+
+extern char *strerr();
+extern void strerr_warn();
+extern void strerr_die();
+
+#define STRERR(r,se,a) \
+{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
+
+#define STRERR_SYS(r,se,a) \
+{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; }
+#define STRERR_SYS3(r,se,a,b,c) \
+{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
+
+#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_warn5(x1,x2,x3,x4,x5,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_warn4(x1,x2,x3,x4,se) \
+strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn3(x1,x2,x3,se) \
+strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn2(x1,x2,se) \
+strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn1(x1,se) \
+strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_die4(e,x1,x2,x3,x4,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die3(e,x1,x2,x3,se) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die2(e,x1,x2,se) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die1(e,x1,se) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
+#define strerr_die4sys(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die3sys(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die2sys(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die1sys(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+
+#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
+#define strerr_die5x(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
+#define strerr_die4x(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die3x(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die2x(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die1x(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+
+#endif
diff --git a/strerr_die.c b/strerr_die.c
new file mode 100644
index 0000000..6092020
--- /dev/null
+++ b/strerr_die.c
@@ -0,0 +1,37 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "strerr.h"
+
+void strerr_warn(x1,x2,x3,x4,x5,x6,se)
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+ strerr_sysinit();
+
+ if (x1) substdio_puts(subfderr,x1);
+ if (x2) substdio_puts(subfderr,x2);
+ if (x3) substdio_puts(subfderr,x3);
+ if (x4) substdio_puts(subfderr,x4);
+ if (x5) substdio_puts(subfderr,x5);
+ if (x6) substdio_puts(subfderr,x6);
+
+ while(se) {
+ if (se->x) substdio_puts(subfderr,se->x);
+ if (se->y) substdio_puts(subfderr,se->y);
+ if (se->z) substdio_puts(subfderr,se->z);
+ se = se->who;
+ }
+
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+}
+
+void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
+int e;
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+ strerr_warn(x1,x2,x3,x4,x5,x6,se);
+ _exit(e);
+}
diff --git a/strerr_sys.c b/strerr_sys.c
new file mode 100644
index 0000000..198198b
--- /dev/null
+++ b/strerr_sys.c
@@ -0,0 +1,12 @@
+#include "error.h"
+#include "strerr.h"
+
+struct strerr strerr_sys;
+
+void strerr_sysinit()
+{
+ strerr_sys.who = 0;
+ strerr_sys.x = error_str(errno);
+ strerr_sys.y = "";
+ strerr_sys.z = "";
+}
diff --git a/subfd.h b/subfd.h
new file mode 100644
index 0000000..bcb2e1e
--- /dev/null
+++ b/subfd.h
@@ -0,0 +1,15 @@
+#ifndef SUBFD_H
+#define SUBFD_H
+
+#include "substdio.h"
+
+extern substdio *subfdin;
+extern substdio *subfdinsmall;
+extern substdio *subfdout;
+extern substdio *subfdoutsmall;
+extern substdio *subfderr;
+
+extern int subfd_read();
+extern int subfd_readsmall();
+
+#endif
diff --git a/subfderr.c b/subfderr.c
new file mode 100644
index 0000000..011ab0f
--- /dev/null
+++ b/subfderr.c
@@ -0,0 +1,7 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_errbuf[256];
+static substdio it = SUBSTDIO_FDBUF(write,2,subfd_errbuf,256);
+substdio *subfderr = &it;
diff --git a/subfdin.c b/subfdin.c
new file mode 100644
index 0000000..a11d323
--- /dev/null
+++ b/subfdin.c
@@ -0,0 +1,13 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+int subfd_read(fd,buf,len) int fd; char *buf; int len;
+{
+ if (substdio_flush(subfdout) == -1) return -1;
+ return read(fd,buf,len);
+}
+
+char subfd_inbuf[SUBSTDIO_INSIZE];
+static substdio it = SUBSTDIO_FDBUF(subfd_read,0,subfd_inbuf,SUBSTDIO_INSIZE);
+substdio *subfdin = &it;
diff --git a/subfdins.c b/subfdins.c
new file mode 100644
index 0000000..36983ac
--- /dev/null
+++ b/subfdins.c
@@ -0,0 +1,13 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+int subfd_readsmall(fd,buf,len) int fd; char *buf; int len;
+{
+ if (substdio_flush(subfdoutsmall) == -1) return -1;
+ return read(fd,buf,len);
+}
+
+char subfd_inbufsmall[256];
+static substdio it = SUBSTDIO_FDBUF(subfd_readsmall,0,subfd_inbufsmall,256);
+substdio *subfdinsmall = &it;
diff --git a/subfdout.c b/subfdout.c
new file mode 100644
index 0000000..0aee102
--- /dev/null
+++ b/subfdout.c
@@ -0,0 +1,7 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_outbuf[SUBSTDIO_OUTSIZE];
+static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbuf,SUBSTDIO_OUTSIZE);
+substdio *subfdout = &it;
diff --git a/subfdouts.c b/subfdouts.c
new file mode 100644
index 0000000..5be356d
--- /dev/null
+++ b/subfdouts.c
@@ -0,0 +1,7 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_outbufsmall[256];
+static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbufsmall,256);
+substdio *subfdoutsmall = &it;
diff --git a/subgetopt.3 b/subgetopt.3
new file mode 100644
index 0000000..aae03aa
--- /dev/null
+++ b/subgetopt.3
@@ -0,0 +1,357 @@
+.TH subgetopt 3
+.SH NAME
+subgetopt \- get option character from command line
+.SH SYNTAX
+.B #include <subgetopt.h>
+
+char *\fBsgoptarg\fP;
+.br
+int \fBsgoptind\fP;
+.br
+int \fBsgoptpos\fP;
+.br
+int \fBsgoptdone\fP;
+.br
+int \fBsgoptproblem\fP;
+
+int \fBsgopt(\fP\fIargc,argv,opts\fR\fB)\fP;
+
+int \fIargc\fR;
+.br
+char **\fIargv\fR;
+.br
+char *\fIopts\fR;
+.SH DESCRIPTION
+.B sgopt
+returns the next valid command-line option character
+from
+.IR argv .
+
+Valid option characters are listed in the
+.I opts
+string.
+.I opts
+may be empty.
+A character in
+.I opts
+may be followed by a colon,
+in which case it
+takes an
+.I option argument\fR.
+Avoid using the characters ?, :, and \- as option characters.
+
+Below
+.I option argument
+is abbreviated
+as
+.I optarg
+and
+.I command-line argument
+is abbreviated as
+.IR cmdarg .
+
+Options are listed in cmdargs which begin with
+a minus sign.
+Several options which do not take optargs may be combined
+into one cmdarg.
+
+An option which takes an optarg may be handled in two ways.
+If it appears at the very end of a cmdarg,
+then the entire next cmdarg is the optarg.
+But if there are any characters in the cmdarg
+after the option character,
+then those characters form the optarg.
+The optarg is returned in
+.BR sgoptarg .
+Next time
+.B sgopt
+looks at the cmdarg which follows the optarg.
+
+If a cmdarg does not begin with a hyphen,
+or if it is a lone hyphen not followed by any characters,
+or if it begins with two hyphens,
+then it terminates option processing,
+and
+.B sgopt
+returns an appropriate code.
+If there are two hyphens,
+.B sgopt
+will advance attention to the next cmdarg,
+so it can be called again to read further options.
+.SH "PROPER USAGE"
+.B sgoptproblem
+should be used only when
+.B sgopt
+returns ?.
+.B sgoptind
+and
+.B sgoptpos
+are defined all the time.
+.B sgoptarg
+is defined all the time;
+it is null unless
+.B sgopt
+has just returned an option with optarg.
+
+.B sgopt
+is typically used as follows.
+
+.EX
+#include <subgetopt.h>
+
+main(argc,argv) int argc; char **argv; { int opt;
+
+while ((opt = sgopt(argc,argv,"a:s")) != sgoptdone)
+.br
+ switch(opt) {
+.br
+ case 'a':
+.br
+ printf("opt a with optarg %s\\n",sgoptarg); break;
+.br
+ case 's':
+.br
+ printf("opt s with no optarg\\n"); break;
+.br
+ case '?':
+.br
+ if (argv[sgoptind] && (sgoptind < argc))
+.br
+ printf("illegal opt %c\\n",sgoptproblem);
+.br
+ else
+.br
+ printf("missing arg, opt %c\\n",sgoptproblem);
+.br
+ exit(1);
+.br
+ }
+
+argv += sgoptind;
+.br
+while (*argv) printf("argument %s\\n",*argv++);
+.br
+exit(0);
+.br
+}
+.EE
+
+The end of the command line is
+marked by either
+.IR argc ,
+or a null pointer in
+.IR argv ,
+whichever comes first.
+Normally
+these two markers coincide,
+so it is redundant
+to test for
+both
+.I argv\fB[sgoptind]
+and
+.B sgoptind < \fIargc\fR.
+The above code shows both tests as an illustration.
+
+.B Multiple option sets:
+One useful technique is to call
+.B sgopt
+with a primary
+.I opts
+until it returns EOF,
+then call
+.B sgopt
+with a secondary
+.I opts
+until it returns EOF.
+The user can provide primary options, then a double hyphen,
+and then secondary options.
+No special handling is needed if some or all of the options are
+omitted.
+The same technique can be used for any number of option sets
+in series.
+
+.B Multiple command lines:
+Before parsing a new
+.BR argv ,
+make sure to
+set
+.B sgoptind
+and
+.B sgoptpos
+back to
+1 and 0.
+.SH "PARSING STAGES"
+.B sgopt
+keeps track of its position in
+.I argv
+with
+.B sgoptind
+and
+.BR sgoptpos ,
+which are initialized to 1 and 0.
+It looks at
+.I argv\fB[sgoptind][sgoptpos]
+and following characters.
+
+.B sgopt
+indicates
+that no more options are available by
+returning
+.BR sgoptdone ,
+which is initialized to
+.BR SUBGETOPTDONE ,
+which is defined as \-1.
+
+.B sgopt
+begins by setting
+.B optarg
+to null.
+
+.B Ending conditions:
+If
+.I argv
+is null, or
+.B sgoptind
+is larger than
+.IR argc ,
+or the current cmdarg
+.I argv\fB[sgoptind]
+is null,
+then
+.B sgopt
+returns
+.BR optdone .
+
+.B Stage one:
+If the current character
+is zero,
+.B sgopt
+moves to the beginning of the next cmdarg.
+It then checks the ending conditions again.
+
+.B Stage two:
+If
+the current position is the begining of the cmdarg,
+.B sgopt
+checks whether
+the current character
+is a minus sign.
+If not it returns
+.BR optdone .
+It then
+moves
+to the next character.
+If that character is zero,
+.B sgopt
+moves
+back to the beginning of the cmdarg,
+and returns
+.BR sgoptdone .
+If the character is a minus sign,
+.B sgopt
+moves to the beginning of the next cmdarg,
+and returns
+.BR sgoptdone .
+
+.B Stage three:
+.B sgopt
+records the current character,
+.IR c ,
+and moves to the next character.
+There are three possibilities:
+(1)
+.I c
+is an option character without optarg in
+.IR opts ,
+or
+(2)
+.I c
+is an option character with optarg in
+.IR opts ,
+or
+(3)
+.I c
+does not appear in
+.IR opts .
+
+(1)
+If
+.I c
+appears as an option character without optarg in
+.IR opts ,
+.B sgopt
+returns
+.IR c .
+
+(2)
+If
+.I c
+appears as an option character with optarg in
+.IR opts ,
+.B sgopt
+sets
+.B sgoptarg
+to the current position,
+and moves to the next cmdarg.
+If
+.B sgoptarg
+is nonempty,
+.B sgopt
+returns
+.IR c .
+
+Then
+.B sgopt
+sets
+.B sgoptarg
+to
+the current cmdarg.
+If
+the current cmdarg is null,
+or past
+.IR argc ,
+.B sgopt
+sets
+.B sgoptproblem
+to
+.I c
+and returns ?.
+Otherwise
+.B sgopt
+moves to the next
+argument
+and returns
+.IR c .
+
+(2)
+If
+.I c
+does not appear in
+.IR opts ,
+.B sgopt
+sets
+.B sgoptproblem
+to
+.I c
+and returns ?.
+.SH "SYNTAX NOTE"
+.B sgopt
+is actually a macro abbreviation for
+.BR subgetopt .
+The external
+.B sg
+variables are also macros
+for
+.BR subget .
+These macros are defined in
+.BR <subgetopt.h> ,
+unless
+.B SUBGETOPTNOSHORT
+is defined
+when
+.B <subgetopt.h>
+is included.
+.SH VERSION
+subgetopt version 0.9, 931129.
+.SH AUTHOR
+Placed into the public domain by Daniel J. Bernstein.
diff --git a/subgetopt.c b/subgetopt.c
new file mode 100644
index 0000000..dacf376
--- /dev/null
+++ b/subgetopt.c
@@ -0,0 +1,79 @@
+/* subgetopt.c, subgetopt.h: (yet another) improved getopt clone, inner layer
+D. J. Bernstein, djb@pobox.com.
+No dependencies.
+No system requirements.
+19970228: Cleanups.
+931129: Adapted from getopt.c.
+No known patent problems.
+
+Documentation in subgetopt.3.
+*/
+
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define sgopt subgetopt
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define optarg subgetoptarg
+#define optproblem subgetoptproblem
+#define optdone subgetoptdone
+
+int optind = 1;
+int optpos = 0;
+char *optarg = 0;
+int optproblem = 0;
+int optdone = SUBGETOPTDONE;
+
+int sgopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+ int c;
+ char *s;
+
+ optarg = 0;
+ if (!argv || (optind >= argc) || !argv[optind]) return optdone;
+ if (optpos && !argv[optind][optpos]) {
+ ++optind;
+ optpos = 0;
+ if ((optind >= argc) || !argv[optind]) return optdone;
+ }
+ if (!optpos) {
+ if (argv[optind][0] != '-') return optdone;
+ ++optpos;
+ c = argv[optind][1];
+ if ((c == '-') || (c == 0)) {
+ if (c) ++optind;
+ optpos = 0;
+ return optdone;
+ }
+ /* otherwise c is reassigned below */
+ }
+ c = argv[optind][optpos];
+ ++optpos;
+ s = opts;
+ while (*s) {
+ if (c == *s) {
+ if (s[1] == ':') {
+ optarg = argv[optind] + optpos;
+ ++optind;
+ optpos = 0;
+ if (!*optarg) {
+ optarg = argv[optind];
+ if ((optind >= argc) || !optarg) { /* argument past end */
+ optproblem = c;
+ return '?';
+ }
+ ++optind;
+ }
+ }
+ return c;
+ }
+ ++s;
+ if (*s == ':') ++s;
+ }
+ optproblem = c;
+ return '?';
+}
diff --git a/subgetopt.h b/subgetopt.h
new file mode 100644
index 0000000..d26c62a
--- /dev/null
+++ b/subgetopt.h
@@ -0,0 +1,24 @@
+#ifndef SUBGETOPT_H
+#define SUBGETOPT_H
+
+#ifndef SUBGETOPTNOSHORT
+#define sgopt subgetopt
+#define sgoptarg subgetoptarg
+#define sgoptind subgetoptind
+#define sgoptpos subgetoptpos
+#define sgoptproblem subgetoptproblem
+#define sgoptprogname subgetoptprogname
+#define sgoptdone subgetoptdone
+#endif
+
+#define SUBGETOPTDONE -1
+
+extern int subgetopt();
+extern char *subgetoptarg;
+extern int subgetoptind;
+extern int subgetoptpos;
+extern int subgetoptproblem;
+extern char *subgetoptprogname;
+extern int subgetoptdone;
+
+#endif
diff --git a/substdi.c b/substdi.c
new file mode 100644
index 0000000..42407a1
--- /dev/null
+++ b/substdi.c
@@ -0,0 +1,91 @@
+#include "substdio.h"
+#include "byte.h"
+#include "error.h"
+
+static int oneread(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+ register int r;
+
+ for (;;) {
+ r = op(fd,buf,len);
+ if (r == -1) if (errno == error_intr) continue;
+ return r;
+ }
+}
+
+static int getthis(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int r;
+ register int q;
+
+ r = s->p;
+ q = r - len;
+ if (q > 0) { r = len; s->p = q; } else s->p = 0;
+ byte_copy(buf,r,s->x + s->n);
+ s->n += r;
+ return r;
+}
+
+int substdio_feed(s)
+register substdio *s;
+{
+ register int r;
+ register int q;
+
+ if (s->p) return s->p;
+ q = s->n;
+ r = oneread(s->op,s->fd,s->x,q);
+ if (r <= 0) return r;
+ s->p = r;
+ q -= r;
+ s->n = q;
+ if (q > 0) /* damn, gotta shift */ byte_copyr(s->x + q,r,s->x);
+ return r;
+}
+
+int substdio_bget(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ r = s->n; if (r <= len) return oneread(s->op,s->fd,buf,r);
+ r = substdio_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+int substdio_get(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+ r = substdio_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+char *substdio_peek(s)
+register substdio *s;
+{
+ return s->x + s->n;
+}
+
+void substdio_seek(s,len)
+register substdio *s;
+register int len;
+{
+ s->n += len;
+ s->p -= len;
+}
diff --git a/substdio.c b/substdio.c
new file mode 100644
index 0000000..d03dff2
--- /dev/null
+++ b/substdio.c
@@ -0,0 +1,15 @@
+#include "substdio.h"
+
+void substdio_fdbuf(s,op,fd,buf,len)
+register substdio *s;
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+ s->x = buf;
+ s->fd = fd;
+ s->op = op;
+ s->p = 0;
+ s->n = len;
+}
diff --git a/substdio.h b/substdio.h
new file mode 100644
index 0000000..c3f7f7d
--- /dev/null
+++ b/substdio.h
@@ -0,0 +1,47 @@
+#ifndef SUBSTDIO_H
+#define SUBSTDIO_H
+
+typedef struct substdio {
+ char *x;
+ int p;
+ int n;
+ int fd;
+ int (*op)();
+} substdio;
+
+#define SUBSTDIO_FDBUF(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+
+extern void substdio_fdbuf();
+
+extern int substdio_flush();
+extern int substdio_put();
+extern int substdio_bput();
+extern int substdio_putflush();
+extern int substdio_puts();
+extern int substdio_bputs();
+extern int substdio_putsflush();
+
+extern int substdio_get();
+extern int substdio_bget();
+extern int substdio_feed();
+
+extern char *substdio_peek();
+extern void substdio_seek();
+
+#define substdio_fileno(s) ((s)->fd)
+
+#define SUBSTDIO_INSIZE 8192
+#define SUBSTDIO_OUTSIZE 8192
+
+#define substdio_PEEK(s) ( (s)->x + (s)->n )
+#define substdio_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define substdio_BPUTC(s,c) \
+ ( ((s)->n != (s)->p) \
+ ? ( (s)->x[(s)->p++] = (c), 0 ) \
+ : substdio_bput((s),&(c),1) \
+ )
+
+extern int substdio_copy();
+
+#endif
diff --git a/substdio_copy.c b/substdio_copy.c
new file mode 100644
index 0000000..71cf200
--- /dev/null
+++ b/substdio_copy.c
@@ -0,0 +1,18 @@
+#include "substdio.h"
+
+int substdio_copy(ssout,ssin)
+register substdio *ssout;
+register substdio *ssin;
+{
+ register int n;
+ register char *x;
+
+ for (;;) {
+ n = substdio_feed(ssin);
+ if (n < 0) return -2;
+ if (!n) return 0;
+ x = substdio_PEEK(ssin);
+ if (substdio_put(ssout,x,n) == -1) return -3;
+ substdio_SEEK(ssin,n);
+ }
+}
diff --git a/substdo.c b/substdo.c
new file mode 100644
index 0000000..fb616f7
--- /dev/null
+++ b/substdo.c
@@ -0,0 +1,108 @@
+#include "substdio.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+
+static int allwrite(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+ register int w;
+
+ while (len) {
+ w = op(fd,buf,len);
+ if (w == -1) {
+ if (errno == error_intr) continue;
+ return -1; /* note that some data may have been written */
+ }
+ if (w == 0) ; /* luser's fault */
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int substdio_flush(s)
+register substdio *s;
+{
+ register int p;
+
+ p = s->p;
+ if (!p) return 0;
+ s->p = 0;
+ return allwrite(s->op,s->fd,s->x,p);
+}
+
+int substdio_bput(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int n;
+
+ while (len > (n = s->n - s->p)) {
+ byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
+ if (substdio_flush(s) == -1) return -1;
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int substdio_put(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int n;
+
+ n = s->n;
+ if (len > n - s->p) {
+ if (substdio_flush(s) == -1) return -1;
+ /* now s->p == 0 */
+ if (n < SUBSTDIO_OUTSIZE) n = SUBSTDIO_OUTSIZE;
+ while (len > s->n) {
+ if (n > len) n = len;
+ if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+ buf += n;
+ len -= n;
+ }
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int substdio_putflush(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ if (substdio_flush(s) == -1) return -1;
+ return allwrite(s->op,s->fd,buf,len);
+}
+
+int substdio_bputs(s,buf)
+register substdio *s;
+register char *buf;
+{
+ return substdio_bput(s,buf,str_len(buf));
+}
+
+int substdio_puts(s,buf)
+register substdio *s;
+register char *buf;
+{
+ return substdio_put(s,buf,str_len(buf));
+}
+
+int substdio_putsflush(s,buf)
+register substdio *s;
+register char *buf;
+{
+ return substdio_putflush(s,buf,str_len(buf));
+}
diff --git a/tcp-env.1 b/tcp-env.1
new file mode 100644
index 0000000..edd46f2
--- /dev/null
+++ b/tcp-env.1
@@ -0,0 +1,67 @@
+.TH tcp-env 1
+.SH NAME
+tcp-env \- set up TCP-related environment variables
+.SH SYNOPSIS
+.B tcp-env
+[
+.B \-rR
+]
+[
+.B \-t\fItimeout
+]
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+The input for
+.B tcp-env
+must be a TCP connection.
+.B tcp-env
+finds out information about that connection,
+puts the information into several environment variables
+as described in
+.B tcp-environ(5),
+and runs
+.I program
+with the given arguments.
+
+Usually
+.B tcp-env
+is run from
+.BR inetd .
+It might instead be run from another server
+that already sets up the right environment variables;
+if
+.B PROTO
+is set to
+.B TCP
+when
+.B tcp-env
+is invoked,
+.B tcp-env
+assumes that all the other variables are set up properly,
+and it does not check whether the input is a TCP connection.
+.SH OPTIONS
+.TP
+.B \-r
+(Default.)
+Attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-R
+Do not attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-t\fItimeout
+Give up on the
+.B TCPREMOTEINFO
+connection attempt after
+.I timeout
+seconds.
+Default: 30.
+.SH "SEE ALSO"
+tcp-environ(5),
+inetd(8)
diff --git a/tcp-env.c b/tcp-env.c
new file mode 100644
index 0000000..feb85cc
--- /dev/null
+++ b/tcp-env.c
@@ -0,0 +1,129 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include "sig.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "fmt.h"
+#include "scan.h"
+#include "subgetopt.h"
+#include "ip.h"
+#include "dns.h"
+#include "byte.h"
+#include "remoteinfo.h"
+#include "exit.h"
+#include "case.h"
+
+void die() { _exit(111); }
+
+struct sockaddr_in salocal;
+unsigned long localport;
+struct ip_address iplocal;
+stralloc localname = {0};
+
+struct sockaddr_in saremote;
+unsigned long remoteport;
+struct ip_address ipremote;
+stralloc remotename = {0};
+
+char temp[IPFMT + FMT_ULONG];
+
+void main(argc,argv)
+int argc;
+char *argv[];
+{
+ int dummy;
+ char *proto;
+ int opt;
+ int flagremoteinfo;
+ unsigned long timeout;
+
+ sig_pipeignore();
+
+ flagremoteinfo = 1;
+ timeout = 30;
+ while ((opt = sgopt(argc,argv,"rRt:")) != sgoptdone)
+ switch(opt)
+ {
+ case 'r': flagremoteinfo = 1; break;
+ case 'R': flagremoteinfo = 0; break;
+ case 't': scan_ulong(sgoptarg,&timeout); break;
+ }
+
+ argv += sgoptind;
+ argc -= sgoptind;
+
+ if (argc < 1) die();
+ if (!env_init()) die();
+
+ proto = env_get("PROTO");
+ if (!proto || str_diff(proto,"TCP"))
+ {
+ if (!env_put("PROTO=TCP")) die();
+
+ dummy = sizeof(salocal);
+ if (getsockname(0,(struct sockaddr *) &salocal,&dummy) == -1) die();
+
+ localport = ntohs(salocal.sin_port);
+ temp[fmt_ulong(temp,localport)] = 0;
+ if (!env_put2("TCPLOCALPORT",temp)) die();
+
+ byte_copy(&iplocal,4,&salocal.sin_addr);
+ temp[ip_fmt(temp,&iplocal)] = 0;
+ if (!env_put2("TCPLOCALIP",temp)) die();
+
+ switch(dns_ptr(&localname,&iplocal))
+ {
+ case DNS_MEM: die();
+ case DNS_SOFT:
+ if (!stralloc_copys(&localname,"softdnserror")) die();
+ case 0:
+ if (!stralloc_0(&localname)) die();
+ case_lowers(localname.s);
+ if (!env_put2("TCPLOCALHOST",localname.s)) die();
+ break;
+ default:
+ if (!env_unset("TCPLOCALHOST")) die();
+ }
+
+ dummy = sizeof(saremote);
+ if (getpeername(0,(struct sockaddr *) &saremote,&dummy) == -1) die();
+
+ remoteport = ntohs(saremote.sin_port);
+ temp[fmt_ulong(temp,remoteport)] = 0;
+ if (!env_put2("TCPREMOTEPORT",temp)) die();
+
+ byte_copy(&ipremote,4,&saremote.sin_addr);
+ temp[ip_fmt(temp,&ipremote)] = 0;
+ if (!env_put2("TCPREMOTEIP",temp)) die();
+
+ switch(dns_ptr(&remotename,&ipremote))
+ {
+ case DNS_MEM: die();
+ case DNS_SOFT:
+ if (!stralloc_copys(&remotename,"softdnserror")) die();
+ case 0:
+ if (!stralloc_0(&remotename)) die();
+ case_lowers(remotename.s);
+ if (!env_put2("TCPREMOTEHOST",remotename.s)) die();
+ break;
+ default:
+ if (!env_unset("TCPREMOTEHOST")) die();
+ }
+
+ if (!env_unset("TCPREMOTEINFO")) die();
+ if (flagremoteinfo)
+ {
+ char *rinfo;
+ rinfo = remoteinfo_get(&ipremote,remoteport,&iplocal,localport,(int) timeout);
+ if (rinfo)
+ if (!env_put2("TCPREMOTEINFO",rinfo)) die();
+ }
+ }
+
+ sig_pipedefault();
+ execvp(*argv,argv);
+ die();
+}
diff --git a/tcp-environ.5 b/tcp-environ.5
new file mode 100644
index 0000000..b5cb83b
--- /dev/null
+++ b/tcp-environ.5
@@ -0,0 +1,62 @@
+.TH tcp-environ 5
+.SH NAME
+tcp-environ \- TCP-related environment variables
+.SH DESCRIPTION
+The following environment variables
+describe a TCP connection.
+They are set up by
+.BR tcp-env ,
+.BR tcpclient ,
+and
+.BR tcpserver .
+Note that
+.BR TCPLOCALHOST ,
+.BR TCPREMOTEHOST ,
+and
+.B TCPREMOTEINFO
+can contain arbitrary characters.
+.TP 5
+PROTO
+The string
+.BR TCP .
+.TP 5
+TCPLOCALHOST
+The domain name of the local host,
+with uppercase letters converted to lowercase.
+If there is no currently available domain name
+for the local IP address,
+.B TCPLOCALHOST
+is not set.
+.TP 5
+TCPLOCALIP
+The IP address of the local host, in dotted-decimal form.
+.TP 5
+TCPLOCALPORT
+The local TCP port number, in decimal.
+.TP 5
+TCPREMOTEHOST
+The domain name of the remote host,
+with uppercase letters converted to lowercase.
+If there is no currently available domain name
+for the remote IP address,
+.B TCPREMOTEHOST
+is not set.
+.TP 5
+TCPREMOTEINFO
+A connection-specific string, perhaps a username,
+supplied by the remote host
+via 931/1413/IDENT/TAP.
+If the remote host did not supply connection information,
+.B TCPREMOTEINFO
+is not set.
+.TP 5
+TCPREMOTEIP
+The IP address of the remote host.
+.TP 5
+TCPREMOTEPORT
+The remote TCP port number.
+.SH "SEE ALSO"
+tcpclient(1),
+tcpserver(1),
+tcp-env(1),
+tcp(4)
diff --git a/tcpto.c b/tcpto.c
new file mode 100644
index 0000000..8d100d5
--- /dev/null
+++ b/tcpto.c
@@ -0,0 +1,165 @@
+#include "tcpto.h"
+#include "open.h"
+#include "lock.h"
+#include "seek.h"
+#include "now.h"
+#include "ip.h"
+#include "byte.h"
+#include "datetime.h"
+#include "readwrite.h"
+
+char tcpto_buf[1024];
+
+static int flagwasthere;
+static int fdlock;
+
+static int getbuf()
+{
+ int r;
+ int fd;
+
+ fdlock = open_write("queue/lock/tcpto");
+ if (fdlock == -1) return 0;
+ fd = open_read("queue/lock/tcpto");
+ if (fd == -1) { close(fdlock); return 0; }
+ if (lock_ex(fdlock) == -1) { close(fdlock); close(fd); return 0; }
+ r = read(fd,tcpto_buf,sizeof(tcpto_buf));
+ close(fd);
+ if (r < 0) { close(fdlock); return 0; }
+ r >>= 4;
+ if (!r) close(fdlock);
+ return r;
+}
+
+int tcpto(ip) struct ip_address *ip;
+{
+ int n;
+ int i;
+ char *record;
+ datetime_sec when;
+
+ flagwasthere = 0;
+
+ n = getbuf();
+ if (!n) return 0;
+ close(fdlock);
+
+ record = tcpto_buf;
+ for (i = 0;i < n;++i)
+ {
+ if (byte_equal(ip->d,4,record))
+ {
+ flagwasthere = 1;
+ if (record[4] >= 2)
+ {
+ when = (unsigned long) (unsigned char) record[11];
+ when = (when << 8) + (unsigned long) (unsigned char) record[10];
+ when = (when << 8) + (unsigned long) (unsigned char) record[9];
+ when = (when << 8) + (unsigned long) (unsigned char) record[8];
+
+ if (now() - when < ((60 + (getpid() & 31)) << 6))
+ return 1;
+ }
+ return 0;
+ }
+ record += 16;
+ }
+ return 0;
+}
+
+void tcpto_err(ip,flagerr) struct ip_address *ip; int flagerr;
+{
+ int n;
+ int i;
+ char *record;
+ datetime_sec when;
+ datetime_sec firstwhen;
+ int firstpos;
+ datetime_sec lastwhen;
+
+ if (!flagerr)
+ if (!flagwasthere)
+ return; /* could have been added, but not worth the effort to check */
+
+ n = getbuf();
+ if (!n) return;
+
+ record = tcpto_buf;
+ for (i = 0;i < n;++i)
+ {
+ if (byte_equal(ip->d,4,record))
+ {
+ if (!flagerr)
+ record[4] = 0;
+ else
+ {
+ lastwhen = (unsigned long) (unsigned char) record[11];
+ lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[10];
+ lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[9];
+ lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[8];
+ when = now();
+
+ if (record[4] && (when < 120 + lastwhen)) { close(fdlock); return; }
+
+ if (++record[4] > 10) record[4] = 10;
+ record[8] = when; when >>= 8;
+ record[9] = when; when >>= 8;
+ record[10] = when; when >>= 8;
+ record[11] = when;
+ }
+ if (seek_set(fdlock,i << 4) == 0)
+ if (write(fdlock,record,16) < 16)
+ ; /*XXX*/
+ close(fdlock);
+ return;
+ }
+ record += 16;
+ }
+
+ if (!flagerr) { close(fdlock); return; }
+
+ record = tcpto_buf;
+ for (i = 0;i < n;++i)
+ {
+ if (!record[4]) break;
+ record += 16;
+ }
+
+ if (i >= n)
+ {
+ firstpos = -1;
+ record = tcpto_buf;
+ for (i = 0;i < n;++i)
+ {
+ when = (unsigned long) (unsigned char) record[11];
+ when = (when << 8) + (unsigned long) (unsigned char) record[10];
+ when = (when << 8) + (unsigned long) (unsigned char) record[9];
+ when = (when << 8) + (unsigned long) (unsigned char) record[8];
+ when += (record[4] << 10);
+ if ((firstpos < 0) || (when < firstwhen))
+ {
+ firstpos = i;
+ firstwhen = when;
+ }
+ record += 16;
+ }
+ i = firstpos;
+ }
+
+ if (i >= 0)
+ {
+ record = tcpto_buf + (i << 4);
+ byte_copy(record,4,ip->d);
+ when = now();
+ record[8] = when; when >>= 8;
+ record[9] = when; when >>= 8;
+ record[10] = when; when >>= 8;
+ record[11] = when;
+ record[4] = 1;
+ if (seek_set(fdlock,i << 4) == 0)
+ if (write(fdlock,record,16) < 16)
+ ; /*XXX*/
+ }
+
+ close(fdlock);
+}
diff --git a/tcpto.h b/tcpto.h
new file mode 100644
index 0000000..cd44a42
--- /dev/null
+++ b/tcpto.h
@@ -0,0 +1,8 @@
+#ifndef TCPTO_H
+#define TCPTO_H
+
+extern int tcpto();
+extern void tcpto_err();
+extern void tcpto_clean();
+
+#endif
diff --git a/tcpto_clean.c b/tcpto_clean.c
new file mode 100644
index 0000000..ed48506
--- /dev/null
+++ b/tcpto_clean.c
@@ -0,0 +1,20 @@
+#include "tcpto.h"
+#include "open.h"
+#include "substdio.h"
+#include "readwrite.h"
+
+char tcpto_cleanbuf[1024];
+
+void tcpto_clean() /* running from queue/mess */
+{
+ int fd;
+ int i;
+ substdio ss;
+
+ fd = open_write("../lock/tcpto");
+ if (fd == -1) return;
+ substdio_fdbuf(&ss,write,fd,tcpto_cleanbuf,sizeof(tcpto_cleanbuf));
+ for (i = 0;i < sizeof(tcpto_cleanbuf);++i) substdio_put(&ss,"",1);
+ substdio_flush(&ss); /* if it fails, bummer */
+ close(fd);
+}
diff --git a/timeoutconn.c b/timeoutconn.c
new file mode 100644
index 0000000..33a16d9
--- /dev/null
+++ b/timeoutconn.c
@@ -0,0 +1,59 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "ndelay.h"
+#include "select.h"
+#include "error.h"
+#include "readwrite.h"
+#include "ip.h"
+#include "byte.h"
+#include "timeoutconn.h"
+
+int timeoutconn(s,ip,port,timeout)
+int s;
+struct ip_address *ip;
+unsigned int port;
+int timeout;
+{
+ char ch;
+ struct sockaddr_in sin;
+ char *x;
+ fd_set wfds;
+ struct timeval tv;
+
+ byte_zero(&sin,sizeof(sin));
+ byte_copy(&sin.sin_addr,4,ip);
+ x = (char *) &sin.sin_port;
+ x[1] = port; port >>= 8; x[0] = port;
+ sin.sin_family = AF_INET;
+
+ if (ndelay_on(s) == -1) return -1;
+
+ /* XXX: could bind s */
+
+ if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) {
+ ndelay_off(s);
+ return 0;
+ }
+ if ((errno != error_inprogress) && (errno != error_wouldblock)) return -1;
+
+ FD_ZERO(&wfds);
+ FD_SET(s,&wfds);
+ tv.tv_sec = timeout; tv.tv_usec = 0;
+
+ if (select(s + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1) return -1;
+ if (FD_ISSET(s,&wfds)) {
+ int dummy;
+ dummy = sizeof(sin);
+ if (getpeername(s,(struct sockaddr *) &sin,&dummy) == -1) {
+ read(s,&ch,1);
+ return -1;
+ }
+ ndelay_off(s);
+ return 0;
+ }
+
+ errno = error_timeout; /* note that connect attempt is continuing */
+ return -1;
+}
diff --git a/timeoutconn.h b/timeoutconn.h
new file mode 100644
index 0000000..88aab06
--- /dev/null
+++ b/timeoutconn.h
@@ -0,0 +1,6 @@
+#ifndef TIMEOUTCONN_H
+#define TIMEOUTCONN_H
+
+extern int timeoutconn();
+
+#endif
diff --git a/timeoutread.c b/timeoutread.c
new file mode 100644
index 0000000..c75e29c
--- /dev/null
+++ b/timeoutread.c
@@ -0,0 +1,22 @@
+#include "timeoutread.h"
+#include "select.h"
+#include "error.h"
+#include "readwrite.h"
+
+int timeoutread(t,fd,buf,len) int t; int fd; char *buf; int len;
+{
+ fd_set rfds;
+ struct timeval tv;
+
+ tv.tv_sec = t;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd,&rfds);
+
+ if (select(fd + 1,&rfds,(fd_set *) 0,(fd_set *) 0,&tv) == -1) return -1;
+ if (FD_ISSET(fd,&rfds)) return read(fd,buf,len);
+
+ errno = error_timeout;
+ return -1;
+}
diff --git a/timeoutread.h b/timeoutread.h
new file mode 100644
index 0000000..20d3bfc
--- /dev/null
+++ b/timeoutread.h
@@ -0,0 +1,6 @@
+#ifndef TIMEOUTREAD_H
+#define TIMEOUTREAD_H
+
+extern int timeoutread();
+
+#endif
diff --git a/timeoutwrite.c b/timeoutwrite.c
new file mode 100644
index 0000000..516d283
--- /dev/null
+++ b/timeoutwrite.c
@@ -0,0 +1,22 @@
+#include "timeoutwrite.h"
+#include "select.h"
+#include "error.h"
+#include "readwrite.h"
+
+int timeoutwrite(t,fd,buf,len) int t; int fd; char *buf; int len;
+{
+ fd_set wfds;
+ struct timeval tv;
+
+ tv.tv_sec = t;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&wfds);
+ FD_SET(fd,&wfds);
+
+ if (select(fd + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1) return -1;
+ if (FD_ISSET(fd,&wfds)) return write(fd,buf,len);
+
+ errno = error_timeout;
+ return -1;
+}
diff --git a/timeoutwrite.h b/timeoutwrite.h
new file mode 100644
index 0000000..4725861
--- /dev/null
+++ b/timeoutwrite.h
@@ -0,0 +1,6 @@
+#ifndef TIMEOUTWRITE_H
+#define TIMEOUTWRITE_H
+
+extern int timeoutwrite();
+
+#endif
diff --git a/token822.c b/token822.c
new file mode 100644
index 0000000..48a4388
--- /dev/null
+++ b/token822.c
@@ -0,0 +1,513 @@
+#include "stralloc.h"
+#include "alloc.h"
+#include "str.h"
+#include "token822.h"
+#include "gen_allocdefs.h"
+
+static struct token822 comma = { TOKEN822_COMMA };
+
+void token822_reverse(ta)
+token822_alloc *ta;
+{
+ int i;
+ int n;
+ struct token822 temp;
+
+ n = ta->len - 1;
+ for (i = 0;i + i < n;++i)
+ {
+ temp = ta->t[i];
+ ta->t[i] = ta->t[n - i];
+ ta->t[n - i] = temp;
+ }
+}
+
+GEN_ALLOC_ready(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_ready)
+GEN_ALLOC_readyplus(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus)
+GEN_ALLOC_append(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus,token822_append)
+
+static int needspace(t1,t2)
+int t1;
+int t2;
+{
+ if (!t1) return 0;
+ if (t1 == TOKEN822_COLON) return 1;
+ if (t1 == TOKEN822_COMMA) return 1;
+ if (t2 == TOKEN822_LEFT) return 1;
+ switch(t1)
+ {
+ case TOKEN822_ATOM: case TOKEN822_LITERAL:
+ case TOKEN822_QUOTE: case TOKEN822_COMMENT:
+ switch(t2)
+ {
+ case TOKEN822_ATOM: case TOKEN822_LITERAL:
+ case TOKEN822_QUOTE: case TOKEN822_COMMENT:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int atomok(ch)
+char ch;
+{
+ switch(ch)
+ {
+ case ' ': case '\t': case '\r': case '\n':
+ case '(': case '[': case '"':
+ case '<': case '>': case ';': case ':':
+ case '@': case ',': case '.':
+ return 0;
+ }
+ return 1;
+}
+
+static void atomcheck(t)
+struct token822 *t;
+{
+ int i;
+ char ch;
+ for (i = 0;i < t->slen;++i)
+ {
+ ch = t->s[i];
+ if ((ch < 32) || (ch > 126) || (ch == ')') || (ch == ']') || (ch == '\\'))
+ {
+ t->type = TOKEN822_QUOTE;
+ return;
+ }
+ }
+}
+
+int token822_unparse(sa,ta,linelen)
+stralloc *sa;
+token822_alloc *ta;
+unsigned int linelen;
+{
+ struct token822 *t;
+ int len;
+ int ch;
+ int i;
+ int j;
+ int lasttype;
+ int newtype;
+ char *s;
+ char *lineb;
+ char *linee;
+
+ len = 0;
+ lasttype = 0;
+ for (i = 0;i < ta->len;++i)
+ {
+ t = ta->t + i;
+ newtype = t->type;
+ if (needspace(lasttype,newtype))
+ ++len;
+ lasttype = newtype;
+ switch(newtype)
+ {
+ case TOKEN822_COMMA:
+ len += 3; break;
+ case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: case TOKEN822_RIGHT:
+ case TOKEN822_SEMI: case TOKEN822_COLON:
+ ++len; break;
+ case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
+ if (t->type != TOKEN822_ATOM) len += 2;
+ for (j = 0;j < t->slen;++j)
+ switch(ch = t->s[j])
+ {
+ case '"': case '[': case ']': case '(': case ')':
+ case '\\': case '\r': case '\n': ++len;
+ default: ++len;
+ }
+ break;
+ }
+ }
+ len += 2;
+
+ if (!stralloc_ready(sa,len))
+ return -1;
+
+ s = sa->s;
+ lineb = s;
+ linee = 0;
+
+ lasttype = 0;
+ for (i = 0;i < ta->len;++i)
+ {
+ t = ta->t + i;
+ newtype = t->type;
+ if (needspace(lasttype,newtype))
+ *s++ = ' ';
+ lasttype = newtype;
+ switch(newtype)
+ {
+ case TOKEN822_COMMA:
+ *s++ = ',';
+#define NSUW \
+ s[0] = '\n'; s[1] = ' '; \
+ if (linee && (!linelen || (s - lineb <= linelen))) \
+ { while (linee < s) { linee[0] = linee[2]; ++linee; } linee -= 2; } \
+ else { if (linee) lineb = linee + 1; linee = s; s += 2; }
+ NSUW
+ break;
+ case TOKEN822_AT: *s++ = '@'; break;
+ case TOKEN822_DOT: *s++ = '.'; break;
+ case TOKEN822_LEFT: *s++ = '<'; break;
+ case TOKEN822_RIGHT: *s++ = '>'; break;
+ case TOKEN822_SEMI: *s++ = ';'; break;
+ case TOKEN822_COLON: *s++ = ':'; break;
+ case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
+ if (t->type == TOKEN822_QUOTE) *s++ = '"';
+ if (t->type == TOKEN822_LITERAL) *s++ = '[';
+ if (t->type == TOKEN822_COMMENT) *s++ = '(';
+ for (j = 0;j < t->slen;++j)
+ switch(ch = t->s[j])
+ {
+ case '"': case '[': case ']': case '(': case ')':
+ case '\\': case '\r': case '\n': *s++ = '\\';
+ default: *s++ = ch;
+ }
+ if (t->type == TOKEN822_QUOTE) *s++ = '"';
+ if (t->type == TOKEN822_LITERAL) *s++ = ']';
+ if (t->type == TOKEN822_COMMENT) *s++ = ')';
+ break;
+ }
+ }
+ NSUW
+ --s;
+ sa->len = s - sa->s;
+ return 1;
+}
+
+int token822_unquote(sa,ta)
+stralloc *sa;
+token822_alloc *ta;
+{
+ struct token822 *t;
+ int len;
+ int i;
+ int j;
+ char *s;
+
+ len = 0;
+ for (i = 0;i < ta->len;++i)
+ {
+ t = ta->t + i;
+ switch(t->type)
+ {
+ case TOKEN822_COMMA: case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT:
+ case TOKEN822_RIGHT: case TOKEN822_SEMI: case TOKEN822_COLON:
+ ++len; break;
+ case TOKEN822_LITERAL:
+ len += 2;
+ case TOKEN822_ATOM: case TOKEN822_QUOTE:
+ len += t->slen;
+ }
+ }
+
+ if (!stralloc_ready(sa,len))
+ return -1;
+
+ s = sa->s;
+
+ for (i = 0;i < ta->len;++i)
+ {
+ t = ta->t + i;
+ switch(t->type)
+ {
+ case TOKEN822_COMMA: *s++ = ','; break;
+ case TOKEN822_AT: *s++ = '@'; break;
+ case TOKEN822_DOT: *s++ = '.'; break;
+ case TOKEN822_LEFT: *s++ = '<'; break;
+ case TOKEN822_RIGHT: *s++ = '>'; break;
+ case TOKEN822_SEMI: *s++ = ';'; break;
+ case TOKEN822_COLON: *s++ = ':'; break;
+ case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
+ if (t->type == TOKEN822_LITERAL) *s++ = '[';
+ for (j = 0;j < t->slen;++j)
+ *s++ = t->s[j];
+ if (t->type == TOKEN822_LITERAL) *s++ = ']';
+ break;
+ case TOKEN822_COMMENT: break;
+ }
+ }
+ sa->len = s - sa->s;
+ return 1;
+}
+
+int token822_parse(ta,sa,buf)
+token822_alloc *ta;
+stralloc *sa;
+stralloc *buf;
+{
+ int i;
+ int salen;
+ int level;
+ struct token822 *t;
+ int numtoks;
+ int numchars;
+ char *cbuf;
+
+ salen = sa->len;
+
+ numchars = 0;
+ numtoks = 0;
+ for (i = 0;i < salen;++i)
+ switch(sa->s[i])
+ {
+ case '.': case ',': case '@': case '<': case '>': case ':': case ';':
+ ++numtoks; break;
+ case ' ': case '\t': case '\r': case '\n': break;
+ case ')': case ']': return 0;
+ /* other control chars and non-ASCII chars are also bad, in theory */
+ case '(':
+ level = 1;
+ while (level)
+ {
+ if (++i >= salen) return 0;
+ switch(sa->s[i])
+ {
+ case '(': ++level; break;
+ case ')': --level; break;
+ case '\\': if (++i >= salen) return 0;
+ default: ++numchars;
+ }
+ }
+ ++numtoks;
+ break;
+ case '"':
+ level = 1;
+ while (level)
+ {
+ if (++i >= salen) return 0;
+ switch(sa->s[i])
+ {
+ case '"': --level; break;
+ case '\\': if (++i >= salen) return 0;
+ default: ++numchars;
+ }
+ }
+ ++numtoks;
+ break;
+ case '[':
+ level = 1;
+ while (level)
+ {
+ if (++i >= salen) return 0;
+ switch(sa->s[i])
+ {
+ case ']': --level; break;
+ case '\\': if (++i >= salen) return 0;
+ default: ++numchars;
+ }
+ }
+ ++numtoks;
+ break;
+ default:
+ do
+ {
+ if (sa->s[i] == '\\') if (++i >= salen) break;
+ ++numchars;
+ if (++i >= salen)
+ break;
+ }
+ while (atomok(sa->s[i]));
+ --i;
+ ++numtoks;
+ }
+
+ if (!token822_ready(ta,numtoks))
+ return -1;
+ if (!stralloc_ready(buf,numchars))
+ return -1;
+ cbuf = buf->s;
+ ta->len = numtoks;
+
+ t = ta->t;
+ for (i = 0;i < salen;++i)
+ switch(sa->s[i])
+ {
+ case '.': t->type = TOKEN822_DOT; ++t; break;
+ case ',': t->type = TOKEN822_COMMA; ++t; break;
+ case '@': t->type = TOKEN822_AT; ++t; break;
+ case '<': t->type = TOKEN822_LEFT; ++t; break;
+ case '>': t->type = TOKEN822_RIGHT; ++t; break;
+ case ':': t->type = TOKEN822_COLON; ++t; break;
+ case ';': t->type = TOKEN822_SEMI; ++t; break;
+ case ' ': case '\t': case '\r': case '\n': break;
+ case '(':
+ t->type = TOKEN822_COMMENT; t->s = cbuf; t->slen = 0;
+ level = 1;
+ while (level)
+ {
+ ++i; /* assert: < salen */
+ switch(sa->s[i])
+ {
+ case '(': ++level; break;
+ case ')': --level; break;
+ case '\\': ++i; /* assert: < salen */
+ default: *cbuf++ = sa->s[i]; ++t->slen;
+ }
+ }
+ ++t;
+ break;
+ case '"':
+ t->type = TOKEN822_QUOTE; t->s = cbuf; t->slen = 0;
+ level = 1;
+ while (level)
+ {
+ ++i; /* assert: < salen */
+ switch(sa->s[i])
+ {
+ case '"': --level; break;
+ case '\\': ++i; /* assert: < salen */
+ default: *cbuf++ = sa->s[i]; ++t->slen;
+ }
+ }
+ ++t;
+ break;
+ case '[':
+ t->type = TOKEN822_LITERAL; t->s = cbuf; t->slen = 0;
+ level = 1;
+ while (level)
+ {
+ ++i; /* assert: < salen */
+ switch(sa->s[i])
+ {
+ case ']': --level; break;
+ case '\\': ++i; /* assert: < salen */
+ default: *cbuf++ = sa->s[i]; ++t->slen;
+ }
+ }
+ ++t;
+ break;
+ default:
+ t->type = TOKEN822_ATOM; t->s = cbuf; t->slen = 0;
+ do
+ {
+ if (sa->s[i] == '\\') if (++i >= salen) break;
+ *cbuf++ = sa->s[i]; ++t->slen;
+ if (++i >= salen)
+ break;
+ }
+ while (atomok(sa->s[i]));
+ atomcheck(t);
+ --i;
+ ++t;
+ }
+ return 1;
+}
+
+static int gotaddr(taout,taaddr,callback)
+token822_alloc *taout;
+token822_alloc *taaddr;
+int (*callback)();
+{
+ int i;
+
+ if (callback(taaddr) != 1)
+ return 0;
+
+ if (!token822_readyplus(taout,taaddr->len))
+ return 0;
+
+ for (i = 0;i < taaddr->len;++i)
+ taout->t[taout->len++] = taaddr->t[i];
+
+ taaddr->len = 0;
+ return 1;
+}
+
+int token822_addrlist(taout,taaddr,ta,callback)
+token822_alloc *taout;
+token822_alloc *taaddr;
+token822_alloc *ta;
+int (*callback)();
+{
+ struct token822 *t;
+ struct token822 *beginning;
+ int ingroup;
+ int wordok;
+
+ taout->len = 0;
+ taaddr->len = 0;
+
+ if (!token822_readyplus(taout,1)) return -1;
+ if (!token822_readyplus(taaddr,1)) return -1;
+
+ ingroup = 0;
+ wordok = 1;
+
+ beginning = ta->t + 2;
+ t = ta->t + ta->len - 1;
+
+ /* rfc 822 address lists are easy to parse from right to left */
+
+#define FLUSH if (taaddr->len) if (!gotaddr(taout,taaddr,callback)) return -1;
+#define FLUSHCOMMA if (taaddr->len) { \
+if (!gotaddr(taout,taaddr,callback)) return -1; \
+if (!token822_append(taout,&comma)) return -1; }
+#define ADDRLEFT if (!token822_append(taaddr,t--)) return -1;
+#define OUTLEFT if (!token822_append(taout,t--)) return -1;
+
+ while (t >= beginning)
+ {
+ switch(t->type)
+ {
+ case TOKEN822_SEMI:
+ FLUSHCOMMA
+ if (ingroup) return 0;
+ ingroup = 1;
+ wordok = 1;
+ break;
+ case TOKEN822_COLON:
+ FLUSH
+ if (!ingroup) return 0;
+ ingroup = 0;
+ while ((t >= beginning) && (t->type != TOKEN822_COMMA))
+ OUTLEFT
+ if (t >= beginning)
+ OUTLEFT
+ wordok = 1;
+ continue;
+ case TOKEN822_RIGHT:
+ FLUSHCOMMA
+ OUTLEFT
+ while ((t >= beginning) && (t->type != TOKEN822_LEFT))
+ ADDRLEFT
+ /* important to use address here even if it's empty: <> */
+ if (!gotaddr(taout,taaddr,callback)) return -1;
+ if (t < beginning) return 0;
+ OUTLEFT
+ while ((t >= beginning) && ((t->type == TOKEN822_COMMENT) || (t->type == TOKEN822_ATOM) || (t->type == TOKEN822_QUOTE) || (t->type == TOKEN822_AT) || (t->type == TOKEN822_DOT)))
+ OUTLEFT
+ wordok = 0;
+ continue;
+ case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
+ if (!wordok)
+ FLUSHCOMMA
+ wordok = 0;
+ ADDRLEFT
+ continue;
+ case TOKEN822_COMMENT:
+ /* comment is lexically a space; shouldn't affect wordok */
+ break;
+ case TOKEN822_COMMA:
+ FLUSH
+ wordok = 1;
+ break;
+ default:
+ wordok = 1;
+ ADDRLEFT
+ continue;
+ }
+ OUTLEFT
+ }
+ FLUSH
+ ++t;
+ while (t > ta->t)
+ if (!token822_append(taout,--t)) return -1;
+
+ token822_reverse(taout);
+ return 1;
+}
diff --git a/token822.h b/token822.h
new file mode 100644
index 0000000..9ca35cf
--- /dev/null
+++ b/token822.h
@@ -0,0 +1,37 @@
+#ifndef TOKEN822_H
+#define TOKEN822_H
+
+struct token822
+ {
+ int type;
+ char *s;
+ int slen;
+ }
+;
+
+#include "gen_alloc.h"
+GEN_ALLOC_typedef(token822_alloc,struct token822,t,len,a)
+
+extern int token822_parse();
+extern int token822_addrlist();
+extern int token822_unquote();
+extern int token822_unparse();
+extern void token822_free();
+extern void token822_reverse();
+extern int token822_ready();
+extern int token822_readyplus();
+extern int token822_append();
+
+#define TOKEN822_ATOM 1
+#define TOKEN822_QUOTE 2
+#define TOKEN822_LITERAL 3
+#define TOKEN822_COMMENT 4
+#define TOKEN822_LEFT 5
+#define TOKEN822_RIGHT 6
+#define TOKEN822_AT 7
+#define TOKEN822_COMMA 8
+#define TOKEN822_SEMI 9
+#define TOKEN822_COLON 10
+#define TOKEN822_DOT 11
+
+#endif
diff --git a/trigger.c b/trigger.c
new file mode 100644
index 0000000..39f81b8
--- /dev/null
+++ b/trigger.c
@@ -0,0 +1,41 @@
+#include "select.h"
+#include "open.h"
+#include "trigger.h"
+#include "hasnpbg1.h"
+
+static int fd = -1;
+#ifdef HASNAMEDPIPEBUG1
+static int fdw = -1;
+#endif
+
+void trigger_set()
+{
+ if (fd != -1)
+ close(fd);
+#ifdef HASNAMEDPIPEBUG1
+ if (fdw != -1)
+ close(fdw);
+#endif
+ fd = open_read("lock/trigger");
+#ifdef HASNAMEDPIPEBUG1
+ fdw = open_write("lock/trigger");
+#endif
+}
+
+void trigger_selprep(nfds,rfds)
+int *nfds;
+fd_set *rfds;
+{
+ if (fd != -1)
+ {
+ FD_SET(fd,rfds);
+ if (*nfds < fd + 1) *nfds = fd + 1;
+ }
+}
+
+int trigger_pulled(rfds)
+fd_set *rfds;
+{
+ if (fd != -1) if (FD_ISSET(fd,rfds)) return 1;
+ return 0;
+}
diff --git a/trigger.h b/trigger.h
new file mode 100644
index 0000000..dec24ef
--- /dev/null
+++ b/trigger.h
@@ -0,0 +1,8 @@
+#ifndef TRIGGER_H
+#define TRIGGER_H
+
+extern void trigger_set();
+extern void trigger_selprep();
+extern int trigger_pulled();
+
+#endif
diff --git a/triggerpull.c b/triggerpull.c
new file mode 100644
index 0000000..30b9a97
--- /dev/null
+++ b/triggerpull.c
@@ -0,0 +1,16 @@
+#include "ndelay.h"
+#include "open.h"
+#include "triggerpull.h"
+
+void triggerpull()
+{
+ int fd;
+
+ fd = open_write("lock/trigger");
+ if (fd >= 0)
+ {
+ ndelay_on(fd);
+ write(fd,"",1); /* if it fails, bummer */
+ close(fd);
+ }
+}
diff --git a/triggerpull.h b/triggerpull.h
new file mode 100644
index 0000000..6d097bb
--- /dev/null
+++ b/triggerpull.h
@@ -0,0 +1,6 @@
+#ifndef TRIGGERPULL_H
+#define TRIGGERPULL_H
+
+extern void triggerpull();
+
+#endif
diff --git a/trycpp.c b/trycpp.c
new file mode 100644
index 0000000..d7d83ad
--- /dev/null
+++ b/trycpp.c
@@ -0,0 +1,7 @@
+void main()
+{
+#ifdef NeXT
+ printf("nextstep\n"); exit(0);
+#endif
+ printf("unknown\n"); exit(0);
+}
diff --git a/trydrent.c b/trydrent.c
new file mode 100644
index 0000000..c778176
--- /dev/null
+++ b/trydrent.c
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <dirent.h>
+
+void foo()
+{
+ DIR *dir;
+ struct dirent *d;
+}
diff --git a/tryflock.c b/tryflock.c
new file mode 100644
index 0000000..8c8aa76
--- /dev/null
+++ b/tryflock.c
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+
+void main()
+{
+ flock(0,LOCK_EX | LOCK_UN | LOCK_NB);
+}
diff --git a/trylsock.c b/trylsock.c
new file mode 100644
index 0000000..fbce408
--- /dev/null
+++ b/trylsock.c
@@ -0,0 +1,4 @@
+main()
+{
+ ;
+}
diff --git a/trymkffo.c b/trymkffo.c
new file mode 100644
index 0000000..0b119c6
--- /dev/null
+++ b/trymkffo.c
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+void main()
+{
+ mkfifo("temp-trymkffo",0);
+}
diff --git a/trynpbg1.c b/trynpbg1.c
new file mode 100644
index 0000000..9d5f80b
--- /dev/null
+++ b/trynpbg1.c
@@ -0,0 +1,26 @@
+#include "select.h"
+#include "open.h"
+#include "fifo.h"
+
+#define FN "temp-trynpbg1.fifo"
+
+void main()
+{
+ int flagbug;
+ struct timeval instant;
+ fd_set rfds;
+
+ flagbug = 0;
+ if (fifo_make(FN,0600) != -1) {
+ close(0);
+ if (open_read(FN) == 0) {
+ FD_ZERO(&rfds);
+ FD_SET(0,&rfds);
+ instant.tv_sec = instant.tv_usec = 0;
+ if (select(1,&rfds,(fd_set *) 0,(fd_set *) 0,&instant) > 0)
+ flagbug = 1;
+ }
+ unlink(FN);
+ }
+ _exit(!flagbug);
+}
diff --git a/tryrsolv.c b/tryrsolv.c
new file mode 100644
index 0000000..fbce408
--- /dev/null
+++ b/tryrsolv.c
@@ -0,0 +1,4 @@
+main()
+{
+ ;
+}
diff --git a/trysalen.c b/trysalen.c
new file mode 100644
index 0000000..731a109
--- /dev/null
+++ b/trysalen.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+void foo()
+{
+ struct sockaddr sa;
+ sa.sa_len = 0;
+}
diff --git a/trysgact.c b/trysgact.c
new file mode 100644
index 0000000..263cb21
--- /dev/null
+++ b/trysgact.c
@@ -0,0 +1,10 @@
+#include <signal.h>
+
+void main()
+{
+ struct sigaction sa;
+ sa.sa_handler = 0;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(0,&sa,(struct sigaction *) 0);
+}
diff --git a/trysgprm.c b/trysgprm.c
new file mode 100644
index 0000000..ed28857
--- /dev/null
+++ b/trysgprm.c
@@ -0,0 +1,10 @@
+#include <signal.h>
+
+void main()
+{
+ sigset_t ss;
+
+ sigemptyset(&ss);
+ sigaddset(&ss,SIGCHLD);
+ sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+}
diff --git a/tryshsgr.c b/tryshsgr.c
new file mode 100644
index 0000000..807e15d
--- /dev/null
+++ b/tryshsgr.c
@@ -0,0 +1,14 @@
+void main()
+{
+ short x[4];
+
+ x[0] = x[1] = 1;
+ if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
+
+ if (getgroups(1,x) == -1) _exit(1);
+ if (x[1] != 1) _exit(1);
+ x[1] = 2;
+ if (getgroups(1,x) == -1) _exit(1);
+ if (x[1] != 2) _exit(1);
+ _exit(0);
+}
diff --git a/trysysel.c b/trysysel.c
new file mode 100644
index 0000000..f6ed055
--- /dev/null
+++ b/trysysel.c
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h> /* SVR4 silliness */
+
+void foo()
+{
+ ;
+}
diff --git a/trysyslog.c b/trysyslog.c
new file mode 100644
index 0000000..4b99afc
--- /dev/null
+++ b/trysyslog.c
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+main()
+{
+ openlog("foo",0,LOG_MAIL);
+ syslog(0,"foo");
+}
diff --git a/tryulong32.c b/tryulong32.c
new file mode 100644
index 0000000..a108076
--- /dev/null
+++ b/tryulong32.c
@@ -0,0 +1,11 @@
+void main()
+{
+ unsigned long u;
+ u = 1;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ if (!u) _exit(0);
+ _exit(1);
+}
diff --git a/tryvfork.c b/tryvfork.c
new file mode 100644
index 0000000..21387e4
--- /dev/null
+++ b/tryvfork.c
@@ -0,0 +1,4 @@
+void main()
+{
+ vfork();
+}
diff --git a/trywaitp.c b/trywaitp.c
new file mode 100644
index 0000000..7e73bfa
--- /dev/null
+++ b/trywaitp.c
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void main()
+{
+ waitpid(0,0,0);
+}
diff --git a/uint32.h1 b/uint32.h1
new file mode 100644
index 0000000..6599aa0
--- /dev/null
+++ b/uint32.h1
@@ -0,0 +1,6 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned int uint32;
+
+#endif
diff --git a/uint32.h2 b/uint32.h2
new file mode 100644
index 0000000..716430d
--- /dev/null
+++ b/uint32.h2
@@ -0,0 +1,6 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned long uint32;
+
+#endif
diff --git a/wait.3 b/wait.3
new file mode 100644
index 0000000..8c41f4b
--- /dev/null
+++ b/wait.3
@@ -0,0 +1,93 @@
+.TH wait 3
+.SH NAME
+wait \- check child process status
+.SH SYNTAX
+.B #include <wait.h>
+
+int \fBwait_nohang\fP(&\fIwstat\fR);
+.br
+int \fBwait_stop\fP(&\fIwstat\fR);
+.br
+int \fBwait_stopnohang\fP(&\fIwstat\fR);
+.br
+int \fBwait_pid\fP(&\fIwstat\fR,\fIpid\fR);
+
+int \fBwait_exitcode\fP(\fIwstat\fR);
+.br
+int \fBwait_crashed\fP(\fIwstat\fR);
+.br
+int \fBwait_stopped\fP(\fIwstat\fR);
+.br
+int \fBwait_stopsig\fP(\fIwstat\fR);
+
+int \fIpid\fR;
+.br
+int \fIwstat\fR;
+.SH DESCRIPTION
+.B wait_nohang
+looks for zombies (child processes that have exited).
+If it sees a zombie,
+it eliminates the zombie,
+puts the zombie's exit status into
+.IR wstat ,
+and returns the zombie's process ID.
+If there are several zombies,
+.B wait_nohang
+picks one.
+If there are children but no zombies,
+.B wait_nohang
+returns 0.
+If there are no children,
+.B wait_nohang
+returns -1,
+setting
+.B errno
+appropriately.
+
+.B wait_stopnohang
+is similar to
+.BR wait_nohang ,
+but it also looks for children that have stopped.
+
+.B wait_stop
+is similar to
+.BR wait_stopnohang ,
+but if there are children it will pause waiting for one of them
+to stop or exit.
+
+.B wait_pid
+waits for child process
+.I pid
+to exit.
+It eliminates any zombie that shows up in the meantime,
+discarding the exit status.
+
+.B wait_stop
+and
+.B wait_pid
+retry upon
+.BR error_intr .
+.SH "STATUS PARSING"
+If the child stopped,
+.B wait_stopped
+is nonzero;
+.B wait_stopsig
+is the signal that caused the child to stop.
+
+If the child exited by crashing,
+.B wait_stopped
+is zero;
+.B wait_crashed
+is nonzero.
+
+If the child exited normally,
+.B wait_stopped
+is zero;
+.B wait_crashed
+is zero;
+and
+.B wait_exitcode
+is the child's exit code.
+.SH "SEE ALSO"
+wait(2),
+error(3)
diff --git a/wait.h b/wait.h
new file mode 100644
index 0000000..cdb77c3
--- /dev/null
+++ b/wait.h
@@ -0,0 +1,14 @@
+#ifndef WAIT_H
+#define WAIT_H
+
+extern int wait_pid();
+extern int wait_nohang();
+extern int wait_stop();
+extern int wait_stopnohang();
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
+
+#endif
diff --git a/wait_nohang.c b/wait_nohang.c
new file mode 100644
index 0000000..bea2774
--- /dev/null
+++ b/wait_nohang.c
@@ -0,0 +1,12 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "haswaitp.h"
+
+int wait_nohang(wstat) int *wstat;
+{
+#ifdef HASWAITPID
+ return waitpid(-1,wstat,WNOHANG);
+#else
+ return wait3(wstat,WNOHANG,(struct rusage *) 0);
+#endif
+}
diff --git a/wait_pid.c b/wait_pid.c
new file mode 100644
index 0000000..d7a7e84
--- /dev/null
+++ b/wait_pid.c
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "error.h"
+#include "haswaitp.h"
+
+#ifdef HASWAITPID
+
+int wait_pid(wstat,pid) int *wstat; int pid;
+{
+ int r;
+
+ do
+ r = waitpid(pid,wstat,0);
+ while ((r == -1) && (errno == error_intr));
+ return r;
+}
+
+#else
+
+/* XXX untested */
+/* XXX breaks down with more than two children */
+static int oldpid = 0;
+static int oldwstat; /* defined if(oldpid) */
+
+int wait_pid(wstat,pid) int *wstat; int pid;
+{
+ int r;
+
+ if (pid == oldpid) { *wstat = oldwstat; oldpid = 0; return pid; }
+
+ do {
+ r = wait(wstat);
+ if ((r != pid) && (r != -1)) { oldwstat = *wstat; oldpid = r; continue; }
+ }
+ while ((r == -1) && (errno == error_intr));
+ return r;
+}
+
+#endif
diff --git a/warn-auto.sh b/warn-auto.sh
new file mode 100644
index 0000000..36d2313
--- /dev/null
+++ b/warn-auto.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!
diff --git a/warn-shsgr b/warn-shsgr
new file mode 100644
index 0000000..37c351e
--- /dev/null
+++ b/warn-shsgr
@@ -0,0 +1,3 @@
+Oops. Your getgroups() returned 0, and setgroups() failed; this means
+that I can't reliably do my shsgr test. Please either ``make'' as root
+or ``make'' while you're in one or more supplementary groups.