summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2013-02-04 00:08:53 +0100
committermanuel <manuel@mausz.at>2013-02-04 00:08:53 +0100
commit69aec538b456402170dc723af417ba5c05389c32 (patch)
treee6f34c543f17c6392447ea337b2e2868212424d1
downloadqmail-69aec538b456402170dc723af417ba5c05389c32.tar.gz
qmail-69aec538b456402170dc723af417ba5c05389c32.tar.bz2
qmail-69aec538b456402170dc723af417ba5c05389c32.zip
qmail 1.03 import
-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--CHANGES1460
-rw-r--r--FAQ706
-rw-r--r--FILES433
-rw-r--r--INSTALL84
-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--README269
-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--THANKS337
-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.c95
-rw-r--r--cdb_unpack.c12
-rw-r--r--cdbmake.h35
-rw-r--r--cdbmake_add.c117
-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.c400
-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.c95
-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--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--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.5261
-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.8155
-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.766
-rw-r--r--qmail.c125
-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.c129
-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.c259
-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
433 files changed, 31460 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 @@
1SHELL=/bin/sh
2
3# Files are edited in the installation directory, then copied.
4# There are 40 arguments to idedit after the filename,
5# showing the positions of each byte in the following ten ints:
6# uida, uidd, uidl, uido, uidp, uidq, uidr, uids, gidq, gidn.
7# Normal little-endian positions are n n+1 n+2 ... n+39 for some n.
8# Normal big-endian positions are n+3 n+2 n+1 n n+7 ... n+36 for some n.
9
10setup:
11 mkdir /var/qmail
12 ./idedit install-big XXX
13 ./idedit qmail-lspawn XXX
14 ./idedit qmail-queue XXX
15 ./idedit qmail-rspawn XXX
16 ./idedit qmail-showctl XXX
17 ./idedit qmail-start XXX
18 ./install-big
19 cp /var/qmail/boot/binm1+df /var/qmail/rc
20 chmod 755 /var/qmail/rc
21 echo '|fastforward -d /etc/aliases.cdb' > /var/qmail/alias/.qmail-default
22 chmod 644 /var/qmail/alias/.qmail-default
23 hostname | grep -q '\.'
24 ./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 @@
1Like any other piece of software (and information generally), qmail
2comes with NO WARRANTY.
3
4Configuration: The qmail home directory is /var/qmail. (This must be a
5local directory, not shared among machines. Under Linux, make sure that
6all mail-handling filesystems are mounted with synchronous metadata.)
7The user-ext delimiter is -. The silent concurrency limit is 120. The
8queue subdirectory split is 23.
9
10To install:
11 # make setup
12
13To set up qmail to receive and deliver mail, follow the instructions in
14/var/qmail/doc/fastforward/ALIASES, and then start at step 9 of
15/var/qmail/doc/INSTALL.
16
17Compilation environment: Oops, the package creator forgot to edit this!
18He's supposed to list his OS version, compiler version, hardware, and
19name.
diff --git a/BLURB b/BLURB
new file mode 100644
index 0000000..7bcfb3f
--- /dev/null
+++ b/BLURB
@@ -0,0 +1,43 @@
1qmail is a secure, reliable, efficient, simple message transfer agent.
2It is meant as a replacement for the entire sendmail-binmail system on
3typical Internet-connected UNIX hosts.
4
5Secure: Security isn't just a goal, but an absolute requirement. Mail
6delivery is critical for users; it cannot be turned off, so it must be
7completely secure. (This is why I started writing qmail: I was sick of
8the security holes in sendmail and other MTAs.)
9
10Reliable: qmail's straight-paper-path philosophy guarantees that a
11message, once accepted into the system, will never be lost. qmail also
12supports maildir, a new, super-reliable user mailbox format. Maildirs,
13unlike mbox files and mh folders, won't be corrupted if the system
14crashes during delivery. Even better, not only can a user safely read
15his mail over NFS, but any number of NFS clients can deliver mail to him
16at the same time.
17
18Efficient: On a Pentium under BSD/OS, qmail can easily sustain 200000
19local messages per day---that's separate messages injected and delivered
20to mailboxes in a real test! Although remote deliveries are inherently
21limited by the slowness of DNS and SMTP, qmail overlaps 20 simultaneous
22deliveries by default, so it zooms quickly through mailing lists. (This
23is why I finished qmail: I had to get a big mailing list set up.)
24
25Simple: qmail is vastly smaller than any other Internet MTA. Some
26reasons why: (1) Other MTAs have separate forwarding, aliasing, and
27mailing list mechanisms. qmail has one simple forwarding mechanism that
28lets users handle their own mailing lists. (2) Other MTAs offer a
29spectrum of delivery modes, from fast+unsafe to slow+queued. qmail-send
30is instantly triggered by new items in the queue, so the qmail system
31has just one delivery mode: fast+queued. (3) Other MTAs include, in
32effect, a specialized version of inetd that watches the load average.
33qmail's design inherently limits the machine load, so qmail-smtpd can
34safely run from your system's inetd.
35
36Replacement for sendmail: qmail supports host and user masquerading,
37full host hiding, virtual domains, null clients, list-owner rewriting,
38relay control, double-bounce recording, arbitrary RFC 822 address lists,
39cross-host mailing list loop detection, per-recipient checkpointing,
40downed host backoffs, independent message retry schedules, etc. In
41short, it's up to speed on modern MTA features. qmail also includes a
42drop-in ``sendmail'' wrapper so that it will be used transparently by
43your current UAs.
diff --git a/BLURB2 b/BLURB2
new file mode 100644
index 0000000..1a98d0e
--- /dev/null
+++ b/BLURB2
@@ -0,0 +1,26 @@
1Mailing list management is one of qmail's strengths. Notable features:
2
3* qmail lets each user handle his own mailing lists. The delivery
4instructions for user-whatever go into ~user/.qmail-whatever.
5
6* qmail makes it really easy to set up mailing list owners. If the user
7touches ~user/.qmail-whatever-owner, all bounces will come back to him.
8
9* qmail supports VERPs, which permit completely reliable automated
10bounce handling for mailing lists of any size.
11
12* SPEED---qmail blasts through mailing lists an order of magnitude
13faster than sendmail. For example, one message was successfully
14delivered to 150 hosts around the world in just 70 seconds, with qmail's
15out-of-the-box configuration.
16
17* qmail automatically prevents mailing list loops, even across hosts.
18
19* qmail allows inconceivably gigantic mailing lists. No random limits.
20
21* qmail handles aliasing and forwarding with the same simple mechanism.
22For example, Postmaster is controlled by ~alias/.qmail-postmaster. This
23means that cross-host loop detection also applies to aliases.
24
25* qmail supports the ezmlm mailing list manager, which easily and
26automatically 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 @@
1Here are some of qmail's features.
2
3Setup:
4* automatic adaptation to your UNIX variant---no configuration needed
5* AIX, BSD/OS, FreeBSD, HP/UX, Irix, Linux, OSF/1, SunOS, Solaris, and more
6* automatic per-host configuration (config, config-fast)
7* quick installation---no big list of decisions to make
8
9Security:
10* clear separation between addresses, files, and programs
11* minimization of setuid code (qmail-queue)
12* minimization of root code (qmail-start, qmail-lspawn)
13* five-way trust partitioning---security in depth
14* optional logging of one-way hashes, entire contents, etc. (QUEUE_EXTRA)
15
16Message construction (qmail-inject):
17* RFC 822, RFC 1123
18* full support for address groups
19* automatic conversion of old-style address lists to RFC 822 format
20* sendmail hook for compatibility with current user agents
21* header line length limited only by memory
22* host masquerading (control/defaulthost)
23* user masquerading ($MAILUSER, $MAILHOST)
24* automatic Mail-Followup-To creation ($QMAILMFTFILE)
25
26SMTP service (qmail-smtpd):
27* RFC 821, RFC 1123, RFC 1651, RFC 1652, RFC 1854
28* 8-bit clean
29* 931/1413/ident/TAP callback (tcp-env)
30* relay control---stop unauthorized relaying by outsiders (control/rcpthosts)
31* no interference between relay control and forwarding
32* tcpd hook---reject SMTP connections from known abusers
33* automatic recognition of local IP addresses
34* per-buffer timeouts
35* hop counting
36
37Queue management (qmail-send):
38* instant handling of messages added to queue
39* parallelism limit (control/concurrencyremote, control/concurrencylocal)
40* split queue directory---no slowdown when queue gets big
41* quadratic retry schedule---old messages tried less often
42* independent message retry schedules
43* automatic safe queueing---no loss of mail if system crashes
44* automatic per-recipient checkpointing
45* automatic queue cleanups (qmail-clean)
46* queue viewing (qmail-qread)
47* detailed delivery statistics (qmailanalog, available separately)
48
49Bounces (qmail-send):
50* QSBMF bounce messages---both machine-readable and human-readable
51* HCMSSC support---language-independent RFC 1893 error codes
52* double bounces sent to postmaster
53
54Routing by domain (qmail-send):
55* any number of names for local host (control/locals)
56* any number of virtual domains (control/virtualdomains)
57* domain wildcards (control/virtualdomains)
58* configurable percent hack support (control/percenthack)
59* UUCP hook
60
61SMTP delivery (qmail-remote):
62* RFC 821, RFC 974, RFC 1123
63* 8-bit clean
64* automatic downed host backoffs
65* artificial routing---smarthost, localnet, mailertable (control/smtproutes)
66* per-buffer timeouts
67* passive SMTP queue---perfect for SLIP/PPP (serialmail, available separately)
68
69Forwarding and mailing lists (qmail-local):
70* address wildcards (.qmail-default, .qmail-foo-default, etc.)
71* sendmail .forward compatibility (dot-forward, available separately)
72* fast forwarding databases (fastforward, available separately)
73* sendmail /etc/aliases compatibility (fastforward/newaliases)
74* mailing list owners---automatically divert bounces and vacation messages
75* VERPs---automatic recipient identification for mailing list bounces
76* Delivered-To---automatic loop prevention, even across hosts
77* automatic mailing list management (ezmlm, available separately)
78
79Local delivery (qmail-local):
80* user-controlled address hierarchy---fred controls fred-anything
81* mbox delivery
82* reliable NFS delivery (maildir)
83* user-controlled program delivery: procmail etc. (qmail-command)
84* optional new-mail notification (qbiff)
85* optional NRUDT return receipts (qreceipt)
86* conditional filtering (condredirect, bouncesaying)
87
88POP3 service (qmail-popup, qmail-pop3d):
89* RFC 1939
90* UIDL support
91* TOP support
92* APOP hook
93* 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 @@
1qmail's modular, lightweight design and sensible queue management make
2it the fastest available message transfer agent. Here's how it stacks up
3against the competition in five different speed measurements.
4
5* Scheduling: I sent a message to 8192 ``trash'' recipients on my home
6machine. All the deliveries were done in a mere 78 seconds---a rate of
7over 9 million deliveries a day! Compare this to the speed advertised
8for Zmailer's scheduling: 1.1 million deliveries a day on a
9SparcStation-10/50. (My home machine is a 16MB Pentium-100 under BSD/OS,
10with the default qmail configuration. qmail's logs were piped through
11accustamp and written to disk as usual.)
12
13* Local mailing lists: When qmail is delivering a message to a mailbox,
14it physically writes the message to disk before it announces success---
15that way, mail doesn't get lost if the power goes out. I tried sending a
16message to 1024 local mailboxes on the same disk on my home machine; all
17the deliveries were done in 25.5 seconds. That's more than 3.4 million
18deliveries a day! Sending 1024 copies to a _single_ mailbox was just as
19fast. Compare these figures to Zmailer's advertised rate for throwing
20recipients away without even delivering the message---only 0.48 million
21per day on the SparcStation.
22
23* Mailing lists with remote recipients: qmail uses the same delivery
24strategy that makes LSOFT's LSMTP so fast for outgoing mailing lists---
25you choose how many parallel SMTP connections you want to run, and qmail
26runs exactly that many. Of course, performance varies depending on how
27far away your recipients are. The advantage of qmail over other packages
28is its smallness: for example, one Linux user is running 60 simultaneous
29connections, without swapping, on a machine with just 16MB of memory!
30
31* Separate local messages: What LSOFT doesn't tell you about LSMTP is
32how many _separate_ messages it can handle in a day. Does it get bogged
33down as the queue fills up? On my home machine, I disabled qmail's
34deliveries and then sent 5000 separate messages to one recipient. The
35messages were all safely written to the queue disk in 23 minutes, with
36no slowdown as the queue filled up. After I reenabled deliveries, all
37the messages were delivered to the recipient's mailbox in under 12
38minutes. End-to-end rate: more than 200000 individual messages a day!
39
40* Overall performance: What really matters is how well qmail performs
41with your mail load. Red Hat Software found one day that their mail hub,
42a 48MB Pentium running sendmail 8.7, was running out of steam at 70000
43messages a day. They shifted the load to qmail---on a _smaller_ machine,
44a 16MB 486/66---and now they're doing fine.
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..6bfa516
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,1460 @@
119980615 version: qmail 1.03.
219980614 doc: eliminated BIN.setup in favor of a web page.
319980614 code: added other auto* to qmail-showctl output.
419980614 doc: added pointer to immhf.html in qmail-header.5.
519980614 doc: added note to TEST.receive about SMTP command format.
619980614 doc: added FAQ 5.6 on qmail-qmqpd.
719980614 code: removed unused variables in idedit.c.
819980613 code: changed conf-patrn to 002.
919980613 doc: moved SENDMAIL lower in INSTALL.
1019980612 code: added install-big.
1119980612 code: added BIN.Makefile.
1219980612 doc: added BIN.README, BIN.setup.
1319980612 code: switched to new install.
1419980611 code: added idedit.
1519980611 doc: added FAQ 1.3 on $QMAILMFTFILE.
1619980611 doc: used bouncesaying in FAQ 5.5.
1719980611 code: added except.
1819980611 code: added bouncesaying.
1919980611 code: allowed unbracketed IP addresses in dns_ipplus() and
20 dns_mxip().
2119980611 code: allowed spaces after colon in non-bracketed addresses in
22 qmail-smtpd.
2319980611 doc: cleaned up UPGRADE.
2419980528 bug: qmail-smtpd skips first character in rcpthosts() call.
25 tnx NND. impact: qmail-smtpd crashes on empty address; and it
26 allows relaying to ""@any.host. fix: use addr.s.
2719980515 doc: expanded flock discussion in INSTALL.mbox.
2819980515 doc: eliminated flock warning from INSTALL.maildir.
2919980515 doc: split REMOVE.binmail out of INSTALL.
3019980515 doc: split REMOVE.sendmail out of INSTALL.
3119980515 doc: split TEST.deliver and TEST.receive out of INSTALL and
32 UPGRADE.
3319980515 doc: integrated INSTALL.boot into INSTALL.
3419980515 code: cleaned up final output in qmail-qmqpd.c.
3519980514 doc: updated procmail notes in INSTALL.mbox. tnx JRM.
3619980514 doc: changed FAQ 4.4 to point to INSTALL.mbox for procmail.
37 tnx JRM.
3819980514 code: separated HELO and EHLO; single-line response for HELO.
39 tnx to various people.
4019980430 version: qmail 1.02.
4119980430 doc: updated SECURITY.
4219980430 doc: fixed FAQ 4.9. tnx KB.
4319980430 code: changed quote2() to avoid quoting <>.
4419980429 code: changed quote_need() to quote empty local parts. tnx HHO.
4519980428 doc: added status notes to INSTALL and UPGRADE.
4619980428 code: skip setting environment in sendmail.c if PROTO is set.
4719980428 code: eliminated recipientmap.
4819980428 code: added virtual users to qmail-send.c. tnx RN.
4919980428 code: eliminated domain from rewrite() in qmail-send.c.
5019980428 code: added binm1, binm1+df, binm2, binm2+df, binm3, binm3+df.
5119980428 doc: eliminated most Mailbox references from INSTALL, UPGRADE.
5219980428 code: added config-fast.
5319980428 code: renamed qmail-config as config.
5419980428 code: supported QMAILMFTFILE in qmail-inject.c.
5519980428 code: recognized Mail-Followup-To in hfield.c.
5619980428 code: replaced rwrecip() with rwappend() in qmail-inject.c.
5719980428 code: cleaned up doheaderfile() in qmail-inject.c.
5819980426 code: eliminated -type test from qmail-qstat to speed it up.
59 tnx FT.
6019980421 doc: eliminated remove-rcpthosts comments from FAQ.
6119980421 doc: updated FAQ 4.3 to point to Russ Allbery's FAQ.
6219980421 doc: took account of /var/qmail/boot in INSTALL, UPGRADE, and
63 INSTALL.vsm.
6419980421 code: added /var/qmail/boot, with home, home+df, proc, proc+df.
6519980421 doc: skipped make and make man in INSTALL.
6619980420 doc: cleaned up mbox description in SENDMAIL.
6719980420 code: changed QMQP port to official port 628.
6819980402 doc: updated qmsmac references to fastforward.
6919980402 doc: replaced qmail-upgrade man page with doc/SENDMAIL.
7019980402 code: added qmqpservers output to qmail-showctl.
7119980402 code: added qmail-qmqpd.
7219980402 code: added qmail-qmqpc.
7319980304 code: eliminated del_saywhynoexit in qmail-send.c.
7419980304 code: eliminated concurrencynodel in qmail-send.c.
7519980222 code: added status() to qmail-send.c.
7619980222 code: added concurrencyused to qmail-send.c.
7719980128 doc: added note to qmail-getpw.9 about ETXTBSY.
7819980127 code: eliminated err_seenmail() in qmail-smtpd.c. tnx PO.
7919980126 doc: used $DEFAULT in FAQ where possible.
8019980126 code: added DEFAULT in qmail-local.
8119980126 code: added -/ to qmail-pw2u.
8219980126 code: revamped qmeopen() as qmesearch() with more sensible
83 semantics, separating dash from ext cleanly.
8419980126 code: split qmeexists() out of qmeopen() in qmail-local.c.
8519980126 code: introduced safeext in qmail-local.c.
8619980126 code: changed ~alias to mode 2755, to put files into group
87 qmail rather than group nofiles under System V.
8819980126 doc: switched to /var/qmail/rc in INSTALL*, UPGRADE, FAQ.
8919980126 code: added rc.
9019980119 doc: added .qmail creation warning to condredirect.1.
9119980118 code: made auto_uids.c creation atomic in Makefile. tnx HHO.
9219980118 doc: added PIC.*.
9319980117 portability problem: Solaris 2.5.1 incorrectly converts
94 O_NDELAY into O_NONBLOCK for sockets, so that ndelay_off()
95 fails to undo ndelay_on(). impact: none, since all the network
96 readers here use select() via timeoutread(). fix: use
97 O_NONBLOCK if it is defined.
9819980115 code: reformatted qmail-qmtpd.c.
9919980115 doc: changed tcpcontrol references in FAQ.
10019980115 doc: documented morercpthosts in qmail-qmtpd.9.
10119980115 code: eliminated unused datetime in qmail-qmtpd.c.
10219980115 code: eliminated sigalrm() in qmail-qmtpd.c.
10319980115 code: used rcpthosts() in qmail-smtpd.c, qmail-qmtpd.c.
10419980115 code: introduced rcpthosts.c.
10519980115 code: added morercpthosts.cdb support to qmail-showctl.
10619980115 code: added morercpthosts support to qmail-showctl.
10719980115 code: do_lst now returns file-exists in qmail-showctl.
10819980112 doc: documented morercpthosts in qmail-smtpd.9.
10919980112 code: added qmail-newmrh.
11019980112 code: used commands.c in qmail-popup.
11119980112 code: used commands.c in qmail-pop3d.
11219980112 code: introduced fakehelo in qmail-smtpd.
11319980112 code: moved flagbarf setting out of bmfcheck().
11419980112 code: allowed more address misformatting in qmail-smtpd.
11519980112 code: eliminated qmail@pobox.com help address in qmail-smtpd.
11619980112 code: reorganized qmail-smtpd.
11719980112 code: reformatted qmail-smtpd.
11819980112 code: used commands.c in qmail-smtpd.
11919980112 code: switched from 0 to "" for no arg in commands().
12019980112 code: added commands.c.
12119971230 doc: added -s to FreeBSD commands in INSTALL.ids. tnx TM.
12219971224 doc: added pointer to qmail pictures in README.
12319971223 doc: added note in FAQ about qmail-pop3d using maildir.
12419971219 code: added HOST2, HOST3, HOST4.
12519971219 code: renamed extx as x in qmail-local.c.
12619971219 doc: partitioned qmail-command.0.
12719971219 doc: updated FAQ 4.3 to point to newer majordomo patches.
12819971219 doc: eliminated qlist2 from FAQ.
12919971219 doc: eliminated qlist discussion from SECURITY.
13019971219 code: moved qlist, qlist2 to separate package.
13119971213 doc: added FAQ 4.10 on qmail-users generally.
13219971213 doc: added FAQ 4.9 on dealing with NFS outages.
13319971031 doc: added Linux and FreeBSD commands to INSTALL.ids. tnx TM.
13419971026 doc: added note about smtplf in qmail-smtpd.8. tnx S2S.
13519971014 doc: some tweaks to THOUGHTS.
13619971012 doc: used MAILER-DAEMON in UUCP example in INSTALL.
13719971003 code: eliminated dataline and getln() from qmail-remote.c.
13819971003 code: revamped blast() in qmail-remote.c.
13919971002 doc: added FAQ entries for .forward and /etc/aliases.
14019971002 doc: rewrote INSTALL.mbox and INSTALL.vsm.
14119971002 doc: renamed INSTALL.qsmhook as INSTALL.vsm.
14219971002 doc: emphasized the qmail-popup argv0 in FAQ.
14319971001 doc: added dot-forward note to BLURB3.
14419971001 doc: added more configuration notes to qmail-upgrade.9.
14519971001 doc: added note in INSTALL.qsmhook about dot-forward.
14619970930 code: token822_parse() now supports backslash as a quoting
147 character in atoms.
14819970929 doc: suggested symbolic links in INSTALL.mbox.
14919970925 doc: added note to INTERNALS about bounce stability.
15019970925 doc: added section to THOUGHTS discussing CNAME lookups.
15119970925 code: qmail-remote no longer does CNAME lookup on sender. tnx
152 C2F.
15319970923 portability problem: under SCO OSR5, splogger needs socket
154 libraries. impact: couldn't compile. fix: socket.lib. tnx RB.
15519970906 portability problem: under RISC/OS, Mail invokes sendmail -bm.
156 impact: can't send mail using Mail on RISC/OS. fix: ignore -bm.
157 tnx NW.
15819970813 code: implemented databytes in qmail-qmtpd.
15919970813 code: implemented databytes. tnx M4S for sample code.
16019970813 code: replaced execvp() with execv() for sh in qmail-local.
16119970813 doc: said in qmail-control.9 that recipientmap allows comments.
16219970813 code: used strerr in qmail-local.c.
16319970813 code: changed timeoutread(), timeoutwrite() interface.
16419970813 code: eliminated shutdown() in timeoutread(), timeoutwrite().
16519970813 code: revamped I/O in qmail-smtpd.c.
16619970813 code: used timeoutread(), timeoutwrite() in qmail-smtpd.c.
16719970813 code: simplified getcontrol() logic in qmail-remote.c; some
168 out-of-memory messages are now cannot-read-control messages.
16919970813 code: eliminated scan_nbblong().
17019970813 code: reformatted qmail-remote.c.
17119970813 code: renamed flaganyrecipok as flagbother in qmail-remote.c.
17219970813 code: integrated status report into quit() in qmail-remote.c.
17319970813 code: revamped smtpcode() in qmail-remote.c.
17419970813 code: added flagcritical in qmail-remote.c. eliminates
175 possible-duplicate warning if dot has not yet been sent.
17619970813 code: revamped I/O in qmail-remote.c.
17719970813 code: quit immediately after sending QUIT in qmail-remote.c.
17819970813 code: made many more globals in qmail-remote.c.
17919970813 code: switched qmail-remote.c from subfdin to home-grown.
18019970813 code: switched qmail-remote.c from subfdout to subfdoutsmall.
18119970813 code: added LAST support to qmail-pop3d.
18219970812 code: changed qmail_close() success return from 0 to "".
18319970812 code: revamped I/O in qmail-qmtpd.c.
18419970812 code: added qmail-tcpok.
18519970812 code: used strerr in maildirmake.c.
18619970812 code: reformatted maildirmake.c.
18719970812 code: printed qp in condredirect.c.
18819970812 code: printed qqx in condredirect.c.
18919970812 code: revamped I/O in condredirect.c.
19019970812 code: reformatted condredirect.c.
19119970812 code: used strerr in preline.c.
19219970812 code: revamped I/O in preline.c.
19319970812 code: reformatted preline.c.
19419970812 code: printed qp in forward.c.
19519970812 code: printed qqx in forward.c.
19619970812 code: revamped I/O in forward.c.
19719970812 code: used strerr in forward.c.
19819970812 code: reformatted forward.c.
19919970812 code: used strerr in predate.c.
20019970812 code: forced failure in qmail-qmtpd if no recipients; saves
201 time for qmail-send.
20219970812 code: added smtpd() to sendmail.c.
20319970812 code: added mailq() to sendmail.c.
20419970812 code: added die_usage() to sendmail.c.
20519970812 code: reformatted sendmail.c.
20619970812 code: used byte_zero() in qmail-popup.c.
20719970812 code: reformatted qmail-popup.c.
20819970812 code: eliminated unused header files in qmail-popup.c.
20919970812 code: changed I/O system in qmail-popup.c to match qmail-pop3d.
21019970812 doc: pointed people to the mailing list in INSTALL and UPGRADE.
21119970810 code: added TXTBSY check to qmail-getpw.c. this gives vendors
212 the opportunity to make getpwnam() reliable.
21319970810 code: moved non-deleted messages from new/ to cur/ in
214 qmail-pop3d. tnx to various people.
21519970810 code: introduced list() in qmail-pop3d.c.
21619970810 code: reformatted qmail-pop3d.c.
21719970810 code: merged dataline and newname into line in qmail-pop3d.c.
21819970810 code: chopped filenames in qmail-pop3d at colons for UIDL. tnx
219 to various people.
22019970810 code: eliminated printint(), printlong() in qmail-pop3d.c.
22119970810 code: revamped I/O in qmail-pop3d.c.
22219970810 code: used timeoutread(), timeoutwrite() in qmail-pop3d.c.
22319970810 code: eliminated die_prot() in qmail-pop3d.c.
22419970810 code: eliminated unused header files in qmail-pop3d.c.
22519970810 code: switched qmail-pop3d to use maildir.c. tnx MD.
22619970809 code: added uid/gid printing to qmail-showctl. tnx PGF.
22719970808 code: switched control.c from scan_nbblong to scan_ulong.
22819970808 code: cleaned up wait_pid to use waitpid() when possible, and
229 to support at least one extra child otherwise.
23019970807 code: in qmail-smtpd, treat long envelope addresses as a syntax
231 error, instead of waiting for qmail-queue to reject them.
23219970803 code: changed condredirect, forward, qlist, qmail-inject,
233 qmail-local, qmail-qmtpd, qmail-send, qmail-smtpd, qreceipt for
234 new qmail_close() interface.
23519970803 code: revised qmail_close() to handle qmail-queue exit codes.
23619970802 doc: documented SMTP-related exit codes in qmail-queue.8.
23719970802 doc: documented qmail-queue exit codes in qmail-queue.8.
23819970802 code: revamped qmail-queue exit codes.
23919970802 doc: noted linking restrictions in qmail-queue.8.
24019970802 doc: rewrote INSTALL.mbox.
24119970802 doc: split INSTALL.maildir off of INSTALL.mbox.
24219970802 code: added /var/qmail/doc/ creation to qmail-hier.
24319970802 doc: added ezmlm note to FAQ.
24419970802 doc: replaced qlist blurbs with ezmlm blurbs in BLURB*.
24519970802 doc: added various notes to qmail-start.9.
24619970728 doc: eliminated RFC*.
24719970714 doc: added daemontools notes to FAQ.
24819970714 code: eliminated ESMTP parameter syntax checking.
24919970701 doc: changed ``forwarded'' to ``resent'' in qmail-header.5.
25019970629 code: reformatted constmap.c.
25119970628 code: changed straynewline() message in qmail-smtpd.c to point
252 to http://pobox.com/~djb/smtplf.html. tnx RDM.
25319970609 doc: added preline to vacation example in dot-qmail.9. tnx C2S.
25419970421 code: cleaned up slurpclose to handle interrupts.
25519970421 code: set qmail-popup to mode 711. tnx MD.
25619970421 doc: fixed qmail-local -n example in dot-qmail.9.
25719970415 version: qmail 1.01.
25819970414 doc: tightened up qmail-upgrade.7.
25919970414 code: rewrote rewrite().
26019970414 code: implemented recipientmap. suggested by RDM.
26119970414 doc: auto-configured qmail home directory in qmail-control.5,
262 qmail-newu.8, qmail-pw2u.8, qmail-start.8, qmail-users.5.
26319970414 port: Solaris needs socket libs for gethostname. impact: can't
264 compile under Solaris. fix: use socket.lib for qmail-local.
26519970412 code: introduced stralloc_starts.
26619970412 code: introduced str_equal.
26719970412 code: introduced str_start.
26819970412 code: introduced byte_equal.
26919970412 code: made an optional aliasempty arg for qmail-start.
27019970412 code: made an aliasempty arg for qmail-lspawn.
27119970412 code: changed ALIAS_EMPTY to an arg for qmail-local.
27219970412 port: UnixWare returns >0 for SIOCGIFCONF. impact: ipme fails
273 under UnixWare. fix: check for >=0, not =0. tnx JD.
27419970412 port: DGUX does not have ranlib. impact: can't compile under
275 DGUX. fix: added dgux line to make-makelib. tnx HWM.
27619970412 code: changed maildir library to skip any filename beginning
277 with dot. tnx SP.
27819970412 doc: added FAQ entry about aliases with dots.
27919970412 doc: clarified in qmail-inject.8 that default envelope sender
280 is the same as _default_ From address.
28119970411 code: renamed qmail-makectl as qmail-config.
28219970411 code: renamed qmail-alias as qmail-local.
28319970411 code: switched from signal library to sig library.
28419970411 code: switched from qqtalk library to qmail library.
28519970411 code: switched from getline library to getln library.
28619970411 code: massive library cleanups.
28719970411 code: revamped autoconfiguration system.
28819970411 code: revamped configuration interface.
28919970411 code: eliminated qmail-home.
29019970411 code: eliminated tokenize.
29119970220 qmail 1.00.
29219970219 change: various documentation tweaks.
29319970218 change: updated THOUGHTS.
29419970218 change: talked about SPAWN_NUMD in FAQ. tnx EC.
29519970210 change: noted in maildir.5 that readers should skip any name
296 starting with a dot. tnx SP.
29719970209 change: added note to splogger.8 about reliability. tnx BT.
29819970209 change: added section to FAQ on slow sendmail switch. tnx BT.
29919970206 change: added section to FAQ about dtcm. tnx PJG.
30019970206 change: tweaked maildir.5.
30119970201 change: added MH spost note to FAQ. tnx TU.
30219970131 change: reorganized FAQ.
30319970131 change: added web references to FAQ.
30419970124 change: tweaked qmail-upgrade man page.
30519970120 qmail 0.96, gamma.
30619970120 change: removed various try* in auto-configuration.
30719970120 bug: qmail-inject fails to quote argument addresses. impact:
308 addresses containing special characters could be misinterpreted
309 or rejected. tnx C2F. fix: use quote2().
31019970120 portability problem: ESIX puts syslog() and openlog() into
311 -lgen. impact: can't compile under ESIX. fix: put -lgen into
312 LIBS for unix_sv. tnx RN.
31319961221 qmail 0.95, gamma.
31419961218 change: added various try* to TARGETS. tnx SA.
31519961216 change: clarified in qmail-send.8 that virtualdomains does not
316 apply to domains listed in locals.
31719961216 change: slurpclose() now closes fd on out-of-memory. makes it
318 more widely applicable.
31919961215 change: replaced elm instructions in INSTALL.mbox with an
320 explanation of what source change to make. tnx AB.
32119961212 portability problem: under NEWS-OS, time_t needs sys/types.h.
322 impact: couldn't compile under NEWS-OS. fix: include
323 sys/types.h in predate.c. tnx TU.
32419961211 change: used timeoutread, timeoutwrite in remoteinfo(). tnx
325 REB.
32619961210 portability problem: apparently some SGIs produce a systype of
327 irix64. impact: couldn't compile on those systems. fix: handle
328 irix64 in make-cmds. tnx M3S.
32919961208 change: added note to maildir2mbox.1 about mbox locking.
33019961208 qmail 0.94, gamma.
33119961207 change: added QMAILDEFAULTDOMAIN, QMAILDEFAULTHOST,
332 QMAILIDHOST, QMAILPLUSDOMAIN. tnx BTW.
33319961206 cleanup: readsubdir() protects itself against name overflow,
334 rather than depending on caller.
33519961204 change: changed FAQ 7.3 to prohibit fixup relaying.
33619961203 change: added note to FAQ about possibly having to put a space
337 before "$SENDER" for uux. tnx FW.
33819961202 change: added FAQ entry on QUEUE_EXTRA.
33919961202 change: added FAQ entry on backups. tnx DP.
34019961202 change: added note to INSTALL.mbox about qpopper. tnx VV.
34119961201 change: replaced logger with splogger in FAQ 5.1. tnx FPL.
34219961201 change: used netmask example for tcpcontrol in FAQ. tnx FPL.
34319961201 change: added note to README about the mailing list. tnx FPL.
34419961201 change: documented rcpthosts wildcards. tnx RN.
34519961201 change: added note to FAQ about making mailx use datemail.
34619961201 change: added datemail. function requested by several people;
347 approach suggested by TG.
34819961201 change: added predate.
34919961129 change: added QUEUE_EXTRA, QUEUE_EXTRALEN.
35019961129 change: qmail-remote bounces messages with partial final lines.
35119961129 change: added atomcheck() to quote crappy atoms.
35219961129 change: revised atomok() to let atoms deal with more crap.
35319961127 change: qmail-send adds note to deferral if flagdying. tnx TG.
35419961127 change: split off maildirbounce, maildir2qmtp, and maildir2smtp
355 into a separate serialmail package.
35619961126 change: eliminated beta success reports from README.
35719961124 change: forced res_query() return value to fit inside incoming
358 buffer size. allegedly there are buggy versions of res_query()
359 that don't guarantee this.
36019961122 qmail 0.93, gamma.
36119961122 change: allowed empty arg list in forward.
36219961121 change: qmail-smtpd now uses unknown (like qmail-qmtpd) rather
363 than dying if environment variables are not set.
36419961121 cleanup: reorganized helo handling in qmail-smtpd.c.
36519961121 cleanup: eliminated newfield_rec.
36619961121 cleanup: introduced DATE822FMT. used it in received.c.
36719961121 cleanup: introduced received.c. used it in qmail-qmtpd.c,
368 qmail-smtpd.c.
36919961121 change: qmail-smtpd now generates a new timestamp for each
370 message. tnx PJG.
37119961121 cleanup: used stralloc in newfield.
37219961121 cleanup: eliminated newfield_cc.
37319961121 change: eliminated hfield_mort().
37419961119 change: added 2-minute damper on tcpto.
37519961118 change: wrote defaults for readfile controls in showctl.
37619961117 change: control_readfile() now allows comments and blank lines.
377 tnx LW.
37819961117 change: qmail-start sets logger gid to GID_NOFILES.
37919961117 bug: ipme_init() uses a fixed-length buffer for SIOCGIFCONF.
380 impact: qmail-smtpd and qmail-remote will die if there are too
381 many local IP addresses. tnx MD. fix: ipme_init() now
382 dynamically allocates space, up to 200000 bytes, as long as
383 SIOCGIFCONF keeps failing. note that this is a very widespread
384 bug; it's in amd, exim, mrouted, named, nntpd, rarpd, sendmail,
385 tcpdump, timed, xntpd, and probably dozens more programs.
38619961117 portability problem: on BSD 4.4 and various other systems,
387 SIOCGIFCONF will truncate long lists and return success.
388 impact: on those systems, qmail-smtpd and qmail-remote will
389 miss some local IP addresses if there are too many. fix:
390 ipme_init() now checks whether there is enough space left in
391 the buffer for another ifreq, plus 64 bytes JIC. yuck.
39219961117 change: ipmeprint now flushes only at end.
39319961117 cleanup: introduced subfdinsmall. used it in qmail-clean.c,
394 qmail-qmtpd.c.
39519961117 cleanup: introduced subfdoutsmall. used it in hostname.c,
396 printbreak.c, printnumd.c, printsplit.c, qmail-alias.c,
397 qmail-clean.c, qmail-getpw.c, qmail-qmtpd.c, maildir2smtp.c,
398 maildir2qmtp.c.
39919961117 change: moved subfderr buf up to 256 characters.
40019961117 change: added maildirbounce. tnx MD, TG.
40119961116 change: maildir2smtp and maildir2qmtp now print filenames for
402 permanent bounces. tnx MD.
40319961116 change: in SECURITY, ``eleven most recent sendmail security
404 holes, five'' -> ``twelve most recent sendmail security holes,
405 six.''
40619961116 change: rewrote qmail-showctl more professionally.
40719961115 change: added several tests to find-systype.sh. this will
408 affect many systypes.
40919961115 change: qmail-alias now treats most exit codes as soft errors.
41019961115 change: revamped exit codes everywhere for 0, 100, 111.
41119961114 change: added splogger.
41219961114 portability problem: Sun's cc recognizes sqrt() as a builtin,
413 even if math.h is not included and sqrt is defined statically.
414 yuck. impact: when qmail is compiled with Sun's cc, next-retry
415 times are all screwed up. tnx PJG. fix: my sqrt() is now called
416 squareroot().
41719961114 change: dns_ip() now recognizes [1.2.3.4]. tnx DS.
41819961112 change: enabled x option in sendmail. tnx DS.
41919961111 change: added SIGHUP handling to qmail-send. suggested by many
420 people.
42119961111 bug: control routines returned incorrect codes for some
422 out-of-memory conditions. impact: none; conditions cannot
423 happen with sane control files. fix: return -1.
42419961111 change: added SIGALRM handling to qmail-send. suggested by many
425 people.
42619961111 change: eliminated flagnobreak (-b/-B) from qmail-pw2u.
42719961111 change: qmail-getpw now allows hyphens inside usernames.
42819961111 change: added users/append to qmail-pw2u. tnx G2A.
42919961111 change: added badmailfrom to qmail-smtpd. requested by several
430 people.
43119961110 change: replaced elm instructions in INSTALL.mbox with a simple
432 note to set incomingfolders in elm.rc. tnx AB.
43319961110 change: replaced ``owner hack'' with ``variable envelope
434 return paths'' throughout the documentation. tnx DS.
43519961110 change: qmail-setup installs man pages as well as cat pages.
43619961110 change: renamed qmail-newuser as qmail-newu. tnx G2A.
43719961110 change: renamed qmail-pw2user as qmail-pw2u. tnx G2A.
43819961105 change: set path in INSTALL.boot. tnx TJH.
43919961105 change: noted in qmail-smtpd.8 that addresses without @ are
440 always allowed through.
44119961105 change: indicated at various spots in FAQ that rcpthosts has to
442 be updated. suggested by various people.
44319961105 change: indicated at various spots in FAQ that qmail has to be
444 restarted. suggested by various people.
44519961029 change: fixed typo in maildir2qmtp.1. tnx BG.
44619961026 qmail 0.92, gamma.
44719961026 bug: qmail-getpw did not 0-terminate usernames. tnx CF. impact:
448 qmail-getpw would crash on some systems, deferring local
449 deliveries. fix: 0-terminate.
45019961025 cleanup: renamed auto-hassgprm.h to hassgprm.h.
45119961025 cleanup: renamed auto-hassgact.h to hassgact.h.
45219961024 change: replaced qmail-alias.0 with dot-qmail.0 in
453 INSTALL.alias. tnx MW.
45419961022 change: switched uids as early as possible in qmail-start.c.
45519961022 change: in SECURITY, ``ten most recent sendmail security
456 holes, five'' -> ``eleven most recent sendmail security holes,
457 five.''
45819961022 change: quote_need() now treats non-ASCII characters the same
459 way as control characters.
46019961022 change: added version and home page to qmail.7.
46119961022 cleanup: introduced slurpclose.c. used it in qmail-alias.c,
462 qmail-lspawn.c.
46319961021 portability problem: AT&T NCR boxes need stdio.h before
464 arpa/nameser.h. impact: dns.c would not compile. fix: include
465 stdio.h. tnx HS.
46619961021 change: added AIX section to INSTALL.ids. tnx SSB.
46719961021 change: added qmail-pw2user.
46819961020 change: added qmail-pw2user.8.
46919961020 change: qmail-alias now dies soft on EACCES/EPERM for .qmail.
47019961020 change: eliminated root comments from INSTALL.qsmhook.
47119961020 change: various improvements in FAQ.
47219961017 change: added QLX_ROOT.
47319961017 change: renamed hosts in FAQ. tnx SLB.
47419961017 change: in dot-qmail.5, documented envnoathost effects. tnx RN.
47519961017 change: revamped addresses.5.
47619961017 change: added stripvdomprepend() for better bounces. tnx PT.
47719961012 portability problem: under HP-UX 9, can't setgroups() to 65537.
478 impact: couldn't compile under HP-UX 9. fix: use 0 instead of
479 65537 in chkshsgr.c. tnx HHO.
48019961008 change: added several qlx codes.
48119961008 cleanup: eliminated qlx from qmail-alias.
48219961008 change: qmail-lspawn runs qmail-getpw as UID_PW.
48319961008 change: added qmail-newuser.
48419961008 change: added cdb support to qmail-lspawn.
48519961008 change: integrated cdb.
48619961007 change: added qmail-users.5.
48719961007 change: eliminated usermap.
48819961007 cleanup: switched execvp to execv in sendmail, qmail-lspawn.
48919961007 change: used qmail-getpw in qmail-lspawn.
49019961007 change: renamed LSPAWN_USERLEN as GETPW_USERLEN.
49119961007 change: added qmail-getpw.
49219961007 change: created users subdirectory of CONF_HOME.
49319961007 change: fixed typo in FAQ. tnx J1B.
49419961006 change: replaced subfdout with a small ss in qmail-alias.
49519961006 change: reduced qmail-alias buffer sizes to 1024.
49619961003 change: added note to maildir2smtp.0 about maildirmake. tnx SM.
49719961003 bug: if ipme_init() returned -1, qmail-remote would continue,
498 blindly assuming that all addresses are local. impact: on
499 systems with too many aliases, all remote deliveries fail. tnx
500 MD. fix: qmail-remote now dies with temp_oserr() on any result
501 other than 1.
50219961003 portability problem: all pre-4.9.4 versions of bind barf,
503 badly, on CNAME queries to lame servers. what a crappy system.
504 even if the resolver doesn't barf, the next name server down
505 the line may barf. impact: qmail can't get mail through to
506 domains that are (1) lame and (2) running old versions of bind.
507 fix: never, ever, do a CNAME query. dns_cname() now does an ANY
508 query instead. this, like sendmail's analogous procedure, is
509 unreliable when a CNAME is mixed with other records.
51019961001 cleanup: switched to libfd in qmail-start.c.
51119960929 cleanup: renamed auto-hasmkffo.h to hasmkffo.h.
51219960928 cleanup: reorganized qmail-start.c.
51319960928 cleanup: used libfd in preline.c, qmail-lspawn.c,
514 qmail-popup.c, qmail-rspawn.c, qmail-start.c, qqtalk.c,
515 qsmhook.c.
51619960928 cleanup: added libfd.
51719960927 change: in SECURITY, ``nine most recent sendmail security
518 holes, four'' -> ``ten most recent sendmail security holes,
519 five.''
52019960926 change: added tcpcontrol notes to FAQ.
52119960926 change: qmail-smtpd now immediately closes connection, with a
522 warning message dedicated to Solaris, if stray newlines show up
523 in the incoming data.
52419960926 change: added INSTALL.boot.
52519960926 portability problem: on systems that can handle IP interface
526 aliases (i.e., on sa_len systems), SIOCGIFADDR returns the
527 primary address for an alias. impact: ipme_init() did not
528 include alias addresses. fix: ipme_init() avoids SIOCGIFADDR on
529 sa_len systems; on these systems, the address we want is
530 already in ifr. tnx DM.
53119960926 change: qmail-alias kills itself if locking takes longer than
532 30 seconds.
53319960926 change: qmail-pop3d no longer moves messages. tnx RS.
53419960924 change: added note to FAQ about descriptors limit. tnx RD.
53519960922 change: open_trunc() now uses 644.
53619960922 change: qmail-setup now does umask(077).
53719960922 change: maildir2mbox now does umask(077).
53819960922 change: moved subfderr buf up to 64 characters.
53919960920 change: in SECURITY, ``eight most recent sendmail security
540 holes, three'' -> ``nine most recent sendmail security holes,
541 four.''
54219960920 portability problem: init run commands are subject to job
543 control signals under more systems than HP-UX. impact: on some
544 systems (e.g., Solaris), qmail daemons would be killed. fix:
545 INSTALL now tells everybody to use csh -cf.
54619960920 change: added queue-run section to FAQ.
54719960920 change: in pine-crashing question in FAQ, added -oem and -oi,
548 so that change will work with the real sendmail too.
54919960919 change: added CNAME section to FAQ. tnx to various people.
55019960919 change: eliminated QQX_EXECHARD and QQT_EXECHARD. this means
551 that all qmail-queue invocation failures are now soft, even
552 things like EPERM.
55319960919 change: replaced ``No such address'' with ``Sorry, no mailbox
554 here by that name.'' tnx G2A.
55519960919 change: qmail-remote now includes host name in no-such-host
556 messages. tnx G2A.
55719960919 change: replaced ``Temporarily unable to canonicalize address''
558 with ``CNAME lookup failed temporarily.''
55919960918 change: improved an error message in qmail-alias.c. tnx TG.
56019960918 change: added SHELL=/bin/sh to Makefile. tnx JL.
56119960916 change: reorganized INSTALL.ids a bit.
56219960916 change: ``from smtpd'' is now ``from network''.
56319960916 change: SMTPD is now DAEMON.
56419960916 change: qmail-start sets logger uid to UID_LOG. tnx JLH.
56519960916 change: added CONF_USERL.
56619960916 change: iaafmt() now puts a dot on in-addr.arpa.
56719960915 change: added UPGRADE. suggested by several people.
56819960915 change: added qsutil error messages to qmail-log.5.
56919960915 change: qsutil error messages are now alerts.
57019960915 portability problem: on some systems, logger appears to use
571 syslog(pri,buf) instead of syslog(pri,"%s",buf). tnx JC.
572 impact: logger could barf or crash if fed messages containing
573 %. an attacker could easily cause a crash, eliminating qmail's
574 logs. fix: % is no longer considered safe for logs.
57519960912 cleanup: split seek.c into seek_*.c.
57619960912 cleanup: replaced seek_to() with seek_set().
57719960912 cleanup: introduced libseek.a.
57819960907 cleanup: split case.c into case_*.c.
57919960907 cleanup: introduced libcase.a.
58019960907 cleanup: split wait.c into wait_*.c.
58119960907 cleanup: introduced libwait.a.
58219960907 cleanup: renamed auto-haswaitp.h to haswaitp.h.
58319960907 cleanup: split open.c into open_*.c.
58419960907 cleanup: introduced libopen.a.
58519960904 change: added generic pointer to qmail-control.5. tnx HW.
58619960904 change: rewrote rcpthosts section in FAQ. tnx HW.
58719960904 change: added organization section to FAQ. tnx HW.
58819960902 qmail 0.91, gamma.
58919960902 change: control_readfile() can now handle partial lines. tnx
590 JDHB.
59119960902 change: eliminated non-fqdn note from FAQ. next version of
592 tcpserver will use DNS directly.
59319960902 change: qlist now uses NEWSENDER, not SENDER.
59419960902 change: qmail-pop3d no longer obtains a lock. tnx RS.
59519960902 change: put }g on all seds in Makefile.
59619960831 change: noted in qmail-control.5 that comments are not allowed
597 in control files. tnx J2B.
59819960829 change: used double union in alloc.c. tnx ME.
59919960829 change: replaced semicolon with colon for smtproutes port.
60019960829 change: in INSTALL, put make man just before make setup.
60119960829 change: changed a few qmail messages into alerts.
60219960829 cleanup: renamed datetime_gmt as datetime_tai.
60319960829 change: added note to UUCP question that some UUCP software
604 doesn't want preline -f. tnx SB.
60519960829 change: added question 2.4 to FAQ on SLIP/PPP.
60619960828 change: replaced owner- with owner-@host-@[] in qmail-inject.
60719690828 change: replaced owner- with owner-@host-@[] in qmail-alias.
60819960828 change: replaced owner- with owner-@host-@[] in injectbounce().
60919960828 change: replaced owner- with owner-@host-@[] in senderadd() for
610 owner hack.
61119960828 change: qmail-inject -n now prints Return-Path.
61219960825 cleanup: revised ending code in token_addrlist().
61319960825 change: tokenize now uses linelen 0 for unparse.
61419960825 change: if linelen is 0 in token822_unparse, no length limit.
61519960825 change: added LINELEN macro to qmail-inject for unparse.
61619960825 change: token822_unparse now takes linelen argument. (leaving
617 two spaces on the right before linelen.)
61819960824 cleanup: renamed token as token822.
61919960822 portability problem: under NEWS-OS, /bin/mail and /usr/ucb/mail
620 invoke sendmail with -E and -J options. tnx TU. impact:
621 couldn't send mail with those programs. fix: accept opts,
622 including _optional_ args. ugh.
62319960821 change: sendmail now quits if invoked as newaliases. tnx TU.
62419960821 portability problem: under NEWS-OS, dirent.h needs sys/types.h.
625 tnx TU. this POSIX violation also appears in some versions of
626 FreeBSD. impact: couldn't compile under NEWS-OS. fix: include
627 sys/types.h in direntry.h* and trydrent.c. [sigh]
62819960821 change: added concurrencyremote question to FAQ.
62919960821 change: added chkspawn.
63019960821 change: moved default SPAWN_NUMD up to 120.
63119960818 change: allowed ;port in smtproutes. tnx AL.
63219960818 change: introduced port in qmail-remote.c.
63319960818 change: qmail-queue records qp in Received lines. 2 lines of
634 code. tnx ME.
63519960818 change: in SECURITY, ``seven most recent sendmail security
636 holes'' -> ``eight most recent sendmail security holes.''
63719960818 change: qmail-pop3d now appends an extra blank line to every
638 message, for compatibility with popper. some clients can't
639 deal with the right thing, unfortunately. tnx FPL.
64019960818 change: added qmail-tcpto.
64119960818 change: eliminated cc -posix for NeXTs. tnx SA.
64219960818 change: eliminated loadfifo. tnx SA.
64319960818 change: integrated auto-configured fifo.c code from SA.
64419960817 change: put SYSDEPS into a more reasonable order.
64519960813 change: indicated possibility of duplication when qmail-remote
646 gets a dead connection after DATA. tnx ME.
64719960813 change: documented qmail-inject environment variables.
64819960813 change: supported per-recipient owner hack in qmail-inject.
64919960813 change: supported per-message owner hack in qmail-inject.
65019960813 change: introduced hackedruser into qmail-inject.
65119960813 change: introduced QMAILRUSER, QMAILRHOST.
65219960812 change: added QMAILINJECT option to allow address-comment form.
65319960812 change: made name-address form the default in qmail-inject.
65419960812 change: added QMAILINJECT options f and m to delete From and
655 Message-ID on input. tnx LL.
65619960812 change: added QMAILINJECT environment variable.
65719960812 change: added QMAILHOST, QMAILUSER, QMAILNAME to override
658 MAILHOST, MAILUSER, MAILNAME. tnx MG.
65919960812 change: added qmail-showctl.
66019960812 portability problem: under Solaris 2.4 and possibly other
661 systems, the linker does not give generic alignment to an array
662 of 4096 chars. tnx JP. impact: some subset of the programs
663 would (reliably) die with a bus error; in the Solaris case,
664 maildir2mbox. fix: redefine space in alloc.c to be aligned.
66519960812 change: qmail-remote no longer does CNAME lookups if there's an
666 artificial SMTP route. tnx ME.
66719960812 change: added flagcname arg to addrmangle() in qmail-remote.
66819960812 cleanup: moved host/relayhost processing earlier in
669 qmail-remote.
67019960812 change: qmail-remote stops before DATA if no RCPTs were
671 successful. tnx JLH.
67219960812 change: rewrote rcpthosts explanation in FAQ.
67319960811 change: added qmail-log.5.
67419960811 change: introduced ALIAS_PATERNALISM. configurability requested
675 by several people.
67619960811 change: eliminated go-writability test for qmeox(). the alleged
677 value of paternalism is nonexistent if nobody even notices
678 you're doing it.
67919960811 change: in qbiff, changed no-/-allowed to no-/-at-beginning,
680 no-dots-allowed, must-be-nonempty. tnx MD.
68119960811 change: in mbox.5, discouraged mail readers from looking for
682 From_ lines only after blank lines. too much crap in the world.
68319960811 change: added subject line to qreceipt success notices.
68419960811 change: added subject line to qmail-send bounce messages.
68519960811 change: qmail-alias now expects dash arg. this finally gives
686 lspawn complete control over the local -> ~user/.qmail-ext map.
68719960811 change: qmail-lspawn passes dash arg to qmail-alias.
68819960811 change: reorganized qlist acknowledgment format. again.
68919960811 change: documented EXT, EXT2, EXT3, EXT4. tnx BB.
69019960810 change: qmail-makectl now copies locals to rcpthosts. should be
691 a better default. suggested by TK.
69219960805 portability problem: new makefile generator put in tabs again.
693 sigh. impact: couldn't compile under some systems. fix: same as
694 before. tnx TG.
69519960804 change: added tcpserver instructions to FAQ.
69619960804 change: reorganized FAQ server instructions into a new section.
69719960801 qmail 0.90, gamma.
69819960801 change: qmail-qmtpd now supports rcpthosts, RELAYCLIENT.
69919960731 change: default NUMD is now 29. this prepares for weird systems
700 where getpwnam() needs more than one descriptor (but the
701 descriptor limit is still 64! ... you never know), and for
702 possible future getpwnam() replacements.
70319960731 change: popped subfderr buffer up to 32 characters. made sure
704 that everybody flushed subfderr as necessary.
70519960731 change: maildir2qmtp now prints filenames and responses.
70619960731 change: maildir2smtp now prints filenames it's trying and
707 relevant portion of SMTP responses.
70819960731 change: used succwrite() in maildir2smtp, maildir2qmtp.
709 simplifies code quite a bit.
71019960731 change: qmail-remote's blast() checks sooner for write errors.
71119960731 change: added better -e option to sendmail. tnx TG.
71219960731 change: added maildir2qmtp.
71319960730 cleanup: eliminated die_nomem() in maildir2smtp.c.
71419960730 change: dns_cname now pretends that "foo." is a CNAME for "foo"
715 to give the desired behavior for people who misuse DNS and
716 violate RFC 822. tnx RN.
71719960730 change: dns_cname now tests for empty names and ] on every
718 loop.
71919960730 change: used LSPAWN_BREAK in qmail-send.c for usermap.
72019960730 change: updated header example in qmail-header.5.
72119960730 change: added printbreak. auto-configured BREAK in dot-qmail.5,
722 qmail-lspawn.7, qmail-send.8, qmail-upgrade.7, qlist2.
72319960730 change: added printnumd. auto-configured NUMD in qmail-send.8,
724 qmail-limits.8.
72519960730 change: added printsplit. auto-configured split in qmail-upq.
72619960730 change: added dot-qmail.5.
72719960730 change: qmail-smtpd now treats HELO as including RSET.
72819960730 change: added moreinfo to qlist usage message.
72919960729 change: improved an error message in qmail-alias.
73019960729 cleanup: merged qmeox(), qmeodx().
73119960729 bug: failure to stat .qmail-owner was not an error. impact: if
732 stat failed temporarily (e.g., because of NFS), .qmail-owner
733 would be incorrectly ignored, so outgoing message would have
734 wrong envelope sender. fix: qmail-alias does temp_nfsqmail() if
735 stat() returns a temporary error.
73619960729 change: added RFCOWNER.
73719960729 change: added qmtpd setup question to FAQ.
73819960729 change: added qmail-qmtpd.
73919960728 change: revamped maildir2smtp error messages.
74019960728 change: revamped maildirwatch error messages.
74119960728 change: revamped maildir2mbox error messages.
74219960728 change: used strerr in maildir_scan().
74319960728 change: used strerr in maildir_chdir().
74419960728 change: introduced strerr.
74519960728 bug: the new tcp-env tried to read from an ndelay socket.
746 impact: TCPREMOTEINFO would always be empty. fix: unset ndelay
747 in remoteinfo.c.
74819960728 bug: if maildir2smtp saw a permanent failure after MAIL, it
749 failed to do RSET. impact: all further messages would be
750 rejected at the MAIL stage. fix: maildir2smtp now always does
751 RSET. tnx JW.
75219960728 cleanup: qmail-alias now applies lowercase and dot-to-colon
753 conversion directly to dashext, leaving everything else alone.
754 this works since all .qmail access is factored through dashext.
75519960727 portability problem: under NeXTStep, -posix is almost entirely
756 broken. impact: qmail daemons would dump core under NeXTStep.
757 fix: turn off -posix, except for loading qmail-setup, which
758 needs mkfifo(); NeXT, bless them, didn't put mkfifo() into the
759 C library where it belongs. this requires a new make command,
760 namely loadfifo.
76119960727 change: all characters 33-126 are now considered safe for logs.
762 tnx MD.
76319960727 cleanup: eliminated qp variable from mailforward().
76419960727 cleanup: maildirwatch.c includes headerbody.h.
76519960727 cleanup: eliminated match from maildirwatch.c.
76619960727 cleanup: eliminated code variable from maildir2smtp.c:doit().
76719960727 cleanup: maildir2smtp.c includes scan.h.
76819960727 cleanup: maildir.c includes str.h.
76919960727 cleanup: qmail-popup.c now includes exit.h.
77019960727 cleanup: qmail-pop3d.c now includes exit.h.
77119960727 cleanup: eliminated path from qmail-start.c.
77219960727 cleanup: eliminated birthplusnn from nextretry().
77319960727 cleanup: eliminated r from timeoutconn().
77419960727 cleanup: tcpto.c now includes byte.h.
77519960727 cleanup: spawn.c now declares initialize().
77619960727 cleanup: qmail-lspawn.c now includes str.h, byte.h.
77719960727 cleanup: qmail-inject.c now includes quote.h.
77819960727 change: qmail-check now checks separately for group
779 readability and other readability.
78019960727 bug: maildir2smtp didn't check flagehlo in PIPELINING parsing.
781 impact: a server that said PIPELINING at any point, not just
782 EHLO, would receive pipelined data. fix: check flagehlo.
78319960727 bug: readsubdir was calling pause(). impact: if a subdirectory
784 was removed, qmail-send would hang. fix: use rs->pause().
78519960727 change: used error_str in qmail-qread.
78619960727 change: qmail-qread now looks for local/remote open errors.
78719960727 cleanup: added warn() in qmail-qread.c.
78819960727 change: qmail-qread now exits 111 for temporary errors.
78919960727 change: used error_str in qmail-setup.
79019960727 change: introduced error_str.
79119960727 change: replaced qmail-check with make check in INSTALL.
79219960727 change: added check target to Makefile.
79319960727 change: replaced qmail-setup with make setup in INSTALL.
79419960727 change: indirected fake targets through do- targets.
79519960727 change: added setup target to Makefile.
79619960727 change: qmail-makectl now makes sure that defaultdomain has
797 at least one dot. e.g., enteract.com -> enteract.com, not com.
79819960726 bug: quote() failed to quote commas. impact: addresses
799 containing commas would not have been quoted correctly for
800 Return-Path or for SMTP MAIL FROM. fix: quote commas.
80119960726 change: sendmail now mentions qmail-qread, not qmail-mailq.
80219960726 change: qmail-alias now expects ext arg. this eliminates
803 LSPAWN_BREAK from qmail-alias and gives lspawn almost complete
804 control over the local -> ~user/.qmail-ext transformation. the
805 exception is that qmail-alias always uses ~user/.qmail,
806 ignoring ext, if local is the same as user.
80719960726 change: qmail-lspawn passes ext to qmail-alias.
80819960726 change: alloc() now uses up a 4K space before calling malloc().
80919960726 change: ipalloc allocation base is now 10. 100 was silly.
81019960726 change: stralloc allocation base is now 30.
81119960726 change: injectbounce() now supports the owner hack.
81219960726 change: qmail-smtpd no longer requires HELO. tnx K1J.
81319960726 cleanup: replaced makereceived() with dohelo().
81419960726 change: qmail-smtpd is back to 555 for syntax errors.
81519960725 change: qmail-alias now supports the owner hack. tnx to RN for
816 prodding me to look at this problem.
81719960725 change: senderadd() now supports the owner hack.
81819960725 cleanup: split off senderadd().
81919960725 change: added pine-crashing note to FAQ.
82019960725 change: added procmail config.h note to INSTALL.mbox.
82119960725 change: added elm TMPDIR note to INSTALL.mbox.
82219960725 change: added pine.conf note to INSTALL.mbox.
82319960724 change: added fixup note to FAQ.
82419960724 change: qmail-inject now exits 111 for temporary errors.
82519960724 change: qmail-smtpd now appends RELAYCLIENT to incoming
826 recipient domain names.
82719960724 cleanup: moved relayclient out of qmail-smtpd's addrallowed()
828 into caller.
82919960724 change: added rcpthosts wildcards.
83019960724 change: added clean target to Makefile.
83119960723 change: added virtualdomains exceptions.
83219960722 change: added BLURB4.
83319960722 change: added BLURB3.
83419960722 change: eliminated smarthost and localnet.
83519960722 change: incorporated relaymap, contributed by LW. renamed it
836 as smtproutes.
83719960722 change: qmail-popup now supports APOP. suggested by BG, who
838 distributed similar changes.
83919960722 change: qmail-popup now sends APOP timestamp to checkpassword.
84019960722 cleanup: in qmail-popup, split off doanddie().
84119960722 change: qmail-popup now prints APOP timestamp in banner.
84219960722 change: added hostname argument to qmail-popup.
84319960722 cleanup: in qmail-popup, split out() into out(), outflush().
84419960722 cleanup: in qmail-popup, introduced pop3_greet().
84519960721 portability problem: under Unisys SVR4, hostname is not in the
846 usual path. impact: qmail-makectl fails. fix: added hostname
847 command here, used it in qmail-makectl.
84819960721 portability problem: on some sysctl-based systems, apparently
849 gethostname() doesn't write anything if the output buffer is
850 too small. it should write a truncated name. impact: if anyone
851 has a hostname longer than 64 characters, maildirs could get up
852 to 64 characters of garbage, rather than a truncated hostname.
853 fix: qmail-alias now does *host = 0 before calling gethostname.
85419960721 change: updated FAQ examples from qsmhook to preline.
85519960721 change: added preline.
85619960721 change: qsmhook now uses signal_init, signal_uninit.
85719960721 change: qsmhook now checks specifically for empty args.
85819960721 change: documented mbox.
85919960721 change: added EXT, EXT2, EXT3, EXT4.
86019960721 change: added LAST response to qmail-pop3d, always returning
861 OK 0. tnx RN.
86219960721 change: added qmail home page to README.
86319960721 change: added HELP response to qmail-smtpd. tnx RN.
86419960720 change: expanded, vertically, the qmail-inject error message
865 for unparseable header fields.
86619960720 change: logo is now dolphin. tnx CEJ.
86719960719 qmail 0.76, beta.
86819960719 change: used LSPAWN_BREAK in qmail-alias for deciding how to
869 handle extensions. this should produce better behavior in the
870 (unsupported) case that LSPAWN_BREAK is not a hyphen.
87119960719 bug: qmail-smtpd didn't check for null arg on MAIL, RCPT.
872 impact: qmail-smtpd would deref 0 and crash. fix: qmail-smtpd
873 now gives syntax error on null arg.
87419960719 change: documented UFLINE in qmail-command.8. tnx TG.
87519960718 change: added maildir2smtp.
87619960718 cleanup: introduced maildir.c. used it in maildir2mbox.c,
877 maildirwatch.c.
87819960718 change: added maildirwatch.
87919960718 cleanup: maildir2mbox now sets up pq2 as it is deleting from
880 pq, rather than simultaneously with pq.
88119960718 change: added H_DELIVEREDTO.
88219960718 portability problem: Unisys requires -lsocket -lnsl. impact:
883 couldn't compile under Unisys. fix: added unix_sv section to
884 make-cmds.sh. tnx TVP.
88519960718 change: added unix_sv section in find-systype. tnx TVP.
88619960717 change: qmail-alias now appends newline if .qmail does not end
887 with a newline. tnx MC.
88819960717 change: qmail-alias now defers delivery for a blank line only
889 if it is the first line of the file. handles user behavior
890 described by MC of putting many newlines at end of file.
89119960717 bug: qmail-inject looked for dots in user part, not just host
892 part, when deciding whether to use defaultdomain. impact: the
893 address joe.bloggs@here didn't have defaultdomain added. fix:
894 qmail-inject now stops at the @.
89519960717 change: updated INSTALL.alias to mention qmsmac.
89619960717 change: syntax error code for SMTP is now 501.
89719960717 change: added -e option to sendmail. tnx TG.
89819960716 change: changed ~alias files to .qmail-local, not .qmaillocal.
899 suggested by many people.
90019960716 change: redid qmail-alias/qmail-lspawn interface.
90119960716 change: replaced EXTENSION, USEREXT with LOCAL.
90219960716 change: qmail-queue now removes intd, mess upon error, as long
903 as it doesn't time out. suggested by BB et al.
90419960716 change: added flagmademess, flagmadeintd to qmail-queue.c.
90519960716 cleanup: changed todofd to intdfd in qmail-queue.c.
90619960716 cleanup: added cleanup() to qmail-queue.c.
90719960716 change: added timeout to tcp-env.c, default 30 seconds.
90819960716 change: remoteinfo_get() now uses timeoutconn().
90919960715 change: added procmail config.h note to FAQ.
91019960704 change: qmail-upgrade.7 now warns administrators that ~alias
911 generally doesn't apply to addresses starting with a user name.
91219960703 change: added echo \c note to FAQ. tnx PJG.
91319960702 change: qmail-smtpd now accepts HELO without an argument.
914 tnx K1J, J1B.
91519960627 change: qmail-lspawn.8 now mentions that qmail-lspawn doesn't
916 set up supplementary groups. tnx TG.
91719960625 portability problem: under Linux, read(,,0) doesn't do proper
918 error slippage. impact: timeoutconn() would always report
919 success; if a connection failed, qmail-remote would report a
920 greeting failure and skip all further MX records. tnx ME. fix:
921 timeoutconn() now uses getpeername() to check for success.
92219960625 change: qmail-smtpd now mentions disk full for QQT_WRITE.
92319960625 change: qmail-inject now mentions disk full for QQT_WRITE.
92419960622 change: if RELAYCLIENT is set, qmail-smtpd skips rcpthosts.
92519960609 change: updated INSTALL for current SMTP responses.
92619960607 change: clarified INSTALL.qsmhook examples. tnx S1R.
92719960607 change: added subject parsing to qlist.c. tnx RN.
92819960607 cleanup: used case_diffb in qlist.c.
92919960607 change: added extra log information to INSTALL examples.
93019960606 change: added -Pn to uucp line in FAQ. tnx DWS.
93119960605 portability problem: under Solaris, /usr/bin/groups incorrectly
932 reports your groups in /etc/group, rather than the results of
933 getgroups(). tnx MD, PJG. impact: test #19 in INSTALL fails.
934 fix: added special note to test #19 (sigh) about Solaris.
93519960605 change: improved maildir setup commands in INSTALL.mbox.
93619960605 change: on success, qmail-alias logs forwarding qp. 9 lines
937 extra code.
93819960605 change: qmail-send logs qp for bounce. 6 lines extra code.
93919960605 change: qmail-smtpd includes qp in its response when it accepts
940 a message. 7 lines extra code. requested by MD and others.
94119960605 change: added qqtalk_qp.
94219960605 change: qmail-send now logs uid and qp from todo file. 14 lines
943 extra code.
94419960605 change: qmail-queue now records uid and qp in u and p lines
945 in todo file. 7 lines extra code.
94619960605 change: improved qmail-alias x-bit error messages.
94719960605 change: newline in log is now converted to /, not underscore.
94819960604 change: when it accepts a message, qmail-smtpd includes the
949 local time in its 250 response.
95019960604 change: on success, qmail-alias prints delivery counts,
951 file+forward+program.
95219960603 change: qmail-remote now reports IP address on success. tnx MD.
95319960603 change: qmail-send now logs success and failure reports, not
954 just deferral reports.
95519960603 change: added netbsd section in find-systype, same as bsd.os
956 section. this will affect netbsd-* systypes. tnx MBS.
95719960530 qmail 0.75, beta.
95819960528 change: added qmail.7. tnx MD.
95919960525 change: added qmail-pop3d. tnx RN.
96019960525 change: added qmail-popup. tnx RN.
96119960525 change: added elm filter section to FAQ. tnx GB.
96219960502 portability problem: on many systems, select() on an
963 almost-full pipe incorrectly says writable. tnx ME for running
964 into this and helping track it down. impact: if qmail-send
965 writes a pipeful to qmail-lspawn or qmail-rspawn before they
966 can react (because of high concurrency, high load, or long
967 addresses), it will receive an incorrect -1/EAGAIN, and will
968 conclude that spawn died. sysadmin will have to restart qmail,
969 and messages will be duplicated. fix: in qmail-send.c,
970 busy-loop if write() to spawn returns any error other than
971 EPIPE.
97219960501 bug: qmail-alias treated NAMETOOLONG and NOTDIR as temporary
973 errors. impact: qmail-alias never looked for -default; even if
974 mail was destined to bounce, it would have to time out first.
975 fix: qmail-alias now uses error_temp().
97619960430 bug: qmail-smtpd treated qq crash as permanent error. impact:
977 if somebody actively kills qq, mail will be incorrectly
978 bounced. tnx SS. fix: qmail-smtpd now treats only TOOLONG and
979 EXECHARD as permanent errors.
98019960430 cleanup: eliminated QQT_TTY from qqtalk.h.
98119960428 change: added ``warning: '' before trouble-marking message.
98219960428 change: added percenthack. requested by GB.
98319960428 cleanup: switched to auto-generated Makefile.
98419960428 cleanup: switched to auto-generated .o dependencies.
98519960428 cleanup: eliminated fmt.o, scan.o from Makefile.
98619960428 portability problem: under HP-UX 10, the rc pgrp is sent HUP
987 when rc finishes. tnx BG. impact: the qmail daemons are killed
988 when rc finishes. fix: added special note in INSTALL (sigh) to
989 use csh -cf.
99019960427 cleanup: added PORT_SMTP in qmail-remote.c.
99119960427 cleanup: introduced timeoutwrite.c. used it in qmail-remote.c.
99219960427 cleanup: introduced timeoutread.c. used it in qmail-remote.c.
99319960427 cleanup: introduced timeoutconn.c. used it in qmail-remote.c.
99419960427 change: added timeoutconnect. default: 60 seconds.
99519960427 change: added pop3d instructions to FAQ. tnx RN.
99619960427 change: eliminated env manipulation from qmail-start. tnx BG.
99719960427 change: headerbody now ends header, inserting blank line, if
998 first line of a header field doesn't pass hfield_valid. tnx TG.
99919960427 change: headerbody now prepends MBOX-Line: to any header line
1000 starting From_. this lets qmail-inject work with elm's bounce.
1001 tnx OR, K1J, et al.
100219960426 change: added moreinfo arg to qlist and qlist2.
100319960426 change: added signal_uncatchchild() to qmail-send.c. tnx BG.
1004 now, if sysadmin sets SIGCHLD to SIG_IGN before invoking
1005 qmail-send [sigh], qmail-send won't screw up bounce messages.
100619960426 change: dns_cname now checks whether last character is ],
1007 rather than whether first character is [, for quick return.
100819960426 cleanup: glue is now global in dns.c.
100919960426 cleanup: qmail-remote no longer does stralloc_0 for host and
1010 canonhost.
101119960426 change: dns_mxip no longer rejects [foo].bar.
101219960426 change: dns_mxip no longer requires for bracket that input
1013 be 0-terminated.
101419960426 change: qmail-start can now take logger as an argument.
101519960426 change: qmail-start now invokes qmail-send in foreground (as
1016 parent of other processes).
101719960426 change: added mailsubj. tnx GAW.
101819960426 portability problem: under some systems, can't lock read-only
1019 file. impact: maildir2mbox would always fail on those systems.
1020 fix: maildir2mbox now opens a separate lock fd. tnx BG.
102119960426 cleanup: removed unnecessary #!/bin/sh and # AUTO from mctl.sh.
102219960426 change: added qmail-qstat.
102319960426 change: added qmail-qread.8.
102419960426 change: renamed qmail-mailq as qmail-qread.
102519960419 change: qmail-alias now defers delivery rather than skipping
1026 blank lines in .qmail.
102719960419 change: in qmail-lspawn.c, lowercased name before getpwnam().
1028 really getpwnam() should do this, but oh well.
102919960419 change: added username to qmail-lspawn.c, with LSPAWN_USERLEN
1030 in conf-unusual.h. names longer than LSPAWN_USERLEN will skip
1031 getpwnam().
103219960419 change: if qlist doesn't see any cmds, it presumes that you
1033 meant to subscribe.
103419960419 change: reorganized qlist acknowledgment format.
103519960415 change: reorganized and rewrote FAQ.
103619960415 change: renamed HOWTO as FAQ.
103719960414 change: in qmail-alias, converted extension to lowercase just
1038 before qmeopen(), qmeox() calls. thus EXTENSION and USEREXT and
1039 RECIPIENT will preserve case passed by qmail-lspawn, while
1040 .qmailext lookups will not.
104119960414 change: removed case_lowers(r) from qmail-lspawn.c. tnx JLH.
104219960414 change: moved extension . -> : conversion to just before
1043 qmeopen(), qmeox() calls in qmail-alias.c. thus EXTENSION and
1044 USEREXT and RECIPIENT will preserve dots.
104519960414 change: qsmhook -x now does case-independent comparison.
104619960413 change: added procmail instructions to HOWTO.
104719960409 bug: qmail-alias does not check for newlines when it generates
1048 Return-Path. impact: resulting Return-Path header field will be
1049 illegal, if sender address contains newline followed by
1050 something other than whitespace. fix: qmail-alias now replaces
1051 newline with underscore in rpline.
105219960409 change: added leaf UUCP description to HOWTO. tnx J2K.
105319960409 change: added -B option to sendmail. tnx OR.
105419960409 change: qlist now makes lists unwritable (after renaming from
1055 .qtemp to .qmail). tnx MLH.
105619960409 change: added flagdtline to qsmhook.c, based on -l option.
105719960409 change: added PIPELINING declaration to qmail-smtpd. tnx JGM.
105819960409 change: qmail-smtpd now flushes output instantly after DATA,
1059 QUIT, HELO, EHLO, NOOP, VRFY, or any 502.
106019960409 change: qmail-smtpd now flushes output upon read() and death.
106119960409 change: qmail-smtpd no longer flushes output in out().
106219960409 change: increased qmail-smtpd outbuf size from 128 to 512.
106319960409 cleanup: in qmail-smtpd, eliminated ssinit() in favor of FDBUF.
106419960409 bug: qmail-alias produced aliasfoo-owner rather than foo-owner
1065 as envelope sender for ~alias/.qmailfoo. tnx DS. impact: wrong
1066 envelope sender whenever ~alias/.qmailfoo-owner existed. fix:
1067 qmail-alias now checks for hyphen at beginning of extension.
106819960409 change: added _ESMTP to end of 220. tnx JLH.
106919960409 change: moved out("\r\n") out of smtp_greet() into callers.
1070 this improves the flushing behavior on 221.
107119960328 qmail 0.74, beta.
107219960326 change: changed subdirectory split from 32 to 23.
107319960326 portability problem: some versions of make don't understand
1074 that a line with just a tab is blank. impact: couldn't compile
1075 under those systems. fix: eliminated extra tab from Makefile.
1076 tnx TG.
107719960325 change: added qmail-mailq.
107819960325 change: introduced readsubdir.
107919960325 change: qmail-setup makes split; qmail-check checks split.
108019960325 change: used split in qmail-send, qmail-clean, qmail-queue
1081 for mess, info, local, remote.
108219960325 change: fmtqfn now supports split queue subdirectories.
108319960325 cleanup: eliminated cat2s().
108419960325 cleanup: introduced fmtqfn.c. used it in qmail-queue.c,
1085 qmail-send.c, qmail-clean.c.
108619960325 change: in protocol between qmail-clean and qmail-send, now
1087 using intd/ instead of mess/.
108819960325 change: qmail-queue.c and triggerpull.c now work inside queue
1089 subdirectory.
109019960325 change: spawn.c now checks whether message is a regular file.
109119960325 change: spawn.c now allows slashes in messid except at
1092 beginning.
109319960325 cleanup: introduced fnmake_split in qmail-send.c.
109419960325 cleanup: eliminated strnum in qmail-send.c in favor of
1095 fnmake_{info,todo,mess,chanaddr} and fnmake2_bounce.
109619960325 cleanup: introduced strnum3 in qmail-send.c for the logging
1097 uses of strnum.
109819960325 cleanup: in qmail-send.c, getinfo() now takes id argument.
109919960325 cleanup: qmail-send.c now preallocates space for fn, fn2.
110019960325 change: time zone is now -0000 instead of +0000. encouraging
1101 DRUMS to use this as an i-don't-know-the-local-time indicator.
110219960324 change: qmail-rspawn.c now calls tcpto_clean().
110319960324 cleanup: spawn.c now calls initialize().
110419960324 change: qmail-setup makes lock/tcpto; qmail-check checks it.
110519960324 change: qmail-remote now quickly skips connect() to a host that
1106 seems to be down. tnx BP for pressuring me to get this done.
110719960323 change: in qmail-alias.8, renamed mboxg as mboxrd. tnx RD.
1108 idea was popularized by RD in June 1995.
110919960322 cleanup: eliminated subfd_init().
111019960322 change: qbiff now removes the word Subject.
111119960322 change: now /bin/true instead of /dev/null in the generic
1112 INSTALL.ids instructions. tnx JPR.
111319960322 change: added hfield_skipname(). tnx RN.
111419960322 bug: qmail-inject did not check whether USER needed quoting.
1115 impact: if USER had weird characters, the From address would
1116 generally be wrong, unless the user manually set up MAILUSER
1117 with proper quoting. fix: qmail-inject sets up a quoted-string
1118 if necessary.
111919960322 cleanup: separated out quote_need() in quote.c.
112019960322 cleanup: added stralloc_catb.c. used it in qmail-alias.c,
1121 qmail-send.c.
112219960322 change: qmail-send now uses a quadratic retry schedule from
1123 birth of each message. this also eliminates clustering.
112419960322 cleanup: separated out nextretry() in qmail-send.c.
112519960322 change: qmail-remote now passes all non-@ addresses through
1126 without comment, not just <> and <#>.
112719960322 change: replaced # test with anything@[] test in qmail-inject.
112819960322 change: replaced # with #@[] in qlist.c, qmail-alias.c,
1129 qmail-send.c, qreceipt.c.
113019960322 change: qmail-lspawn no longer discards messages to <#>.
113119960322 cleanup: in qlist, used str_diff for <> and <#> tests.
113219960322 change: qmail-alias is now back to testing envelope sender for
1133 <> and <#>, rather than things without an @.
113419960321 change: added 8BITMIME support to qmail-smtpd.
113519960321 change: added ESMTP support to qmail-smtpd.
113619960318 change: used NEWSENDER in place of SENDER for |forward.
113719960318 change: added NEWSENDER.
113819960318 change: added HCMSSC support to qmail-alias.c.
113919960318 change: added HCMSSC support to spawn.c.
114019960318 change: added HCMSSC support to qmail-remote.c.
114119960318 change: added HCMSSC support to qmail-smtpd.c.
114219960317 portability problem: SCO requires -lsocket -lnsl. impact:
1143 couldn't compile under SCO. fix: added SCO section in
1144 make-cmds.sh. tnx JPR. note that this is for OSR 5; 3.2v4.2
1145 will need more fixes, and old 3.2 is basically hopeless.
114619960317 bug: newfield_datemake would leave newfield_date alone if it
1147 was already initialized, even though qmail-send calls
1148 newfield_datemake anew for each bounce. impact: bounce messages
1149 would usually have an incorrect Date field. fix: redid
1150 newfield_datemake to update newfield_date each time.
115119960317 change: allowed . and @ in 822 phrases; 822 doesn't allow them,
1152 but they do show up. tnx to the DRUMS group.
115319960317 change: replaced GMT with +0000 in date822fmt.c. this confuses
1154 a few versions of getdate(), but the DRUMS group is going to
1155 outlaw GMT, not just recommend against it as in 1123.
115619960317 change: redefined ALIAS_EMPTY to take advantage of . for file
1157 deliveries. tnx RN.
115819960317 change: qmail-alias now allows . as well as / to start file
1159 deliveries. tnx RN.
116019960317 change: qmail-alias now dies (soft) if .qmail is writable to
1161 others, rather than silently ignoring it.
116219960317 change: qmail-alias now dies (soft) if flagforwardonly is
1163 violated, rather than silently ignoring the bad instructions.
116419960317 change: qmail-alias now ignores x bit on empty .qmail files.
116519960317 bug: if RCPT gave 4xx and DATA gave 5xx, qmail-rspawn would
1166 incorrectly assign a permanent failure to that recipient.
1167 impact: in that case, mail would be incorrectly bounced. fix:
1168 remove orr > 0 test from qmail-rspawn.c.
116919960310 change: tcp-env now uses signal_uninit(). [sigh]
117019960310 change: tcp-env now specifically unsets HOST and INFO if they
1171 are not applicable. just trying to make it more widely usable.
117219960310 cleanup: used byte_* in remoteinfo.c, ipme.c, tcp-env.c.
117319960310 cleanup: added readwrite.h, eliminated sys.h.
117419960310 cleanup: included byte.h in qmail-send.c.
117519960310 cleanup: eliminated i and j from forward.c's main().
117619960310 cleanup: eliminated wstat from qlist.c.
117719960310 cleanup: eliminated die_nomem() parameter in qmail-setup.c.
117819960310 cleanup: eliminated i from qmail-remote's addrmangle().
117919960310 cleanup: added exit.h.
118019960310 cleanup: split ipalloc.c off of ip.c.
118119960310 cleanup: added fmt_strn.c, eliminated fmt_strncpy.c.
118219960310 change: reorganized INSTALL to do some pre-upgrade tests.
1183 tnx RN.
118419960310 change: reordered steps in upgrade procedure in INSTALL.
118519960308 change: eliminated ownership test in qmail-alias.c. tnx DS.
118619960304 change: in SECURITY, ``six most recent sendmail security
1187 holes'' -> ``seven most recent sendmail security holes.''
118819960303 qmail 0.73, beta.
118919960303 change: added SYSDEPS.
119019960303 cleanup: revamped select.h autoconfiguration.
119119960303 cleanup: revamped fork.h autoconfiguration.
119219960303 cleanup: revamped direntry.h autoconfiguration. target is now
1193 direntry.h; auto-hasdrent.h is gone.
119419960303 change: tryflock.c now includes <sys/types.h>, for consistency
1195 with lock.c. may affect portability.
119619960302 portability problem: under BSDI, can't set sticky on normal
1197 files. dorks. impact: the new qlist doesn't work under BSDI;
1198 be glad I test things before release. fix: qmail-alias and
1199 qlist now use executable instead of sticky.
120019960302 change: gfrom now quotes >From and >>From etc. as well as From;
1201 in other words, I'm switching from mbox format to mboxg format.
120219960302 cleanup: added gfrom.c. used it in qmail-alias.c, maildir2mbox.c.
120319960302 change: addbounce() now substitutes \n\n -> \n/ in reports,
1204 and \n -> _ in recips. thus bounces can now be reliably parsed.
120519960302 change: if qmail-send had trouble reading the original message
1206 or the list of addresses for a bounce, it used to give up and
1207 send a bounce with "Oh no! I had trouble reading the rest of
1208 your message" or some such. now it aborts the bounce attempt
1209 and tries again later.
121019960302 cleanup: added qqtalk_fail(). used it in qmail-alias.c,
1211 qmail-smtpd.c.
121219960302 bug: if mailforward() had trouble reading message (e.g.,
1213 because of an I/O error), it marked an error but kept reading.
1214 impact: could loop forever. fix: upon error, break.
121519960302 change: maildir2mbox now scans (restrictively) for return-path.
121619960302 change: qbiff now prints subject and body, up to 74 chars.
121719960302 change: added H_SUBJECT to hfield.
121819960302 change: qbiff now puts TO before FROM.
121919960301 cleanup: added fmt_str.c. used it in many places.
122019960301 change: qmail-send now says something if you've told it to exit
1221 but it's waiting for some deliveries. tnx RN.
122219960301 change: qmail-alias -n now continues (with warning) if home
1223 directory is sticky. tnx RN.
122419960301 change: improved usage messages in qmail-alias.c. tnx RN.
122519960301 change: put limit on length of addresses in qlist.
122619960301 change: added exit 99 support to qmail-alias. tnx RN.
122719960301 change: qmail-alias now exits immediately on temporary or
1228 permanent error. rewrote section in qmail-alias.8 accordingly.
122919960301 cleanup: eliminated flagsuccesses from qmail-alias.c.
123019960301 change: added usermap.
123119960301 bug: failure to append to mbox was a permanent error. impact:
1232 if mbox was temporarily unopenable (e.g., because fds were
1233 low), mail would be incorrectly bounced. fix: failure is now
1234 temporary. tnx DS.
123519960229 change: qmail-alias now preserves any envelope sender that
1236 doesn't contain an @, not just <> and <#>.
123719960229 cleanup: revamped byte_* interface.
123819960229 cleanup: renamed str_cpy as str_copy.
123919960229 cleanup: added str_chr.c. used it in qbiff.c, qmail-smtpd.c.
124019960229 cleanup: added str_rchr.c. used it in qmail-send.c, quote.c,
1241 qmail-remote.c.
124219960229 cleanup: added byte_rchr.c. used it in qmail-smtpd.c, spawn.c.
124319960229 cleanup: used USEREXT instead of RECIPIENT in qsmhook.c.
124419960229 cleanup: used USEREXT instead of RECIPIENT in qbiff.c.
124519960229 cleanup: removed j and k from rewrite() in qmail-send.c.
124619960229 portability problem: under HP-UX 10 and Solaris 2.5, can't
1247 setgroups()/setgid() to the system's nogroup/nobody gid. dorks.
1248 impact: inetd chokes, so all SMTP connections are rejected; and
1249 ``alias'' mail, including postmaster, bounces. fix: in
1250 INSTALL.ids, set up a separate powerless gid (tentatively
1251 ``nofiles'') for qmaild and alias. tnx DS and PJG.
125219960229 change: qreceipt now uses qqtalk rather than qmail-inject.
125319960229 change: qlist now uses qqtalk rather than qmail-inject.
125419960229 change: incorporated qmail-setup patch from RN for better
1255 error messages.
125619960228 change: added LSPAWN_BREAK in conf-unusual.h; used it in
1257 lspawn.c. configurability requested by PJG.
125819960228 portability problem: on several systems, including everything
1259 from DEC, select() on a pipe reader returns 1 if there aren't
1260 any writers yet. pointed out by DS. impact: qmail-send chewed
1261 up lots of CPU time. fix: trigger_set() now opens the pipe for
1262 writing after opening it for reading. also added trynpbg1; on
1263 working systems, no point in wasting the extra fd.
126419960228 change: qmail-alias uses .qmail sticky bit for forwardonly.
126519960228 change: qlist now sets sticky bit on .qmail file.
126619960228 change: un-documented +list.
126719960228 portability problem: on HP-UX and possibly other systems, the
1268 supplementary group list does not include the gid. pointed out
1269 by DS. impact: on those systems, tryshsgr could incorrectly set
1270 hasshsgr; this would prevent qmail-send from running. fix: if
1271 tryshsgr sees that getgroups() returns 0, now it actively sets
1272 up a supplementary group list. added chkshsgr to make sure the
1273 setgroups() will work.
127419960227 cleanup: eliminated GETSHORT in dns.c in favor of getshort().
127519960227 cleanup: deleted h->len < 3 test from qlist.c:dobody. tnx RN.
127619960227 change: replaced ~ with $HOME in INSTALL.mbox.
127719960227 change: added note about setgid-mail bits to INSTALL.mbox.
127819960227 change: added forward.1.
127919960227 change: modified forward to allow multiple addresses.
128019960227 change: modified forward to take an entire address, not just a
1281 hostname.
128219960227 change: renamed qrelay as forward.
128319960227 change: added USEREXT support to qmail-alias.
128419960227 change: added -F to sendmail. the need for this was pointed
1285 out by RN.
128619960227 change: added 2 bytes of slop in alloc().
128719960227 bug: received_setup() was not allowing space for the final \0.
1288 impact: none; the line length is always between 65 and 75
1289 characters, which gives at least 45 characters of slop with
1290 existing malloc() implementations. fix: leave space. tnx NH.
1291 note that the bug here is really in fmt_strncpy, which was
1292 written before i was truly free of the curse of libc.a.
129319960227 change: added ALIAS_EMPTY in conf-unusual.h; used it in
1294 qmail-alias.c. tnx PJG.
129519960227 change: added SPAWN_NUMD in conf-unusual.h; used it in spawn.c.
129619960227 change: added conf-unusual.h.
129719960227 cleanup: replaced sizeof(short) with 2 in dns.c.
129819960227 portability problem: on an Alpha, long is 64 bits. pointed out
1299 by DS. impact: address lookups produced incorrect results on an
1300 Alpha; qmail-makectl and qmail-remote failed. fix: replaced
1301 sizeof(long) with 4 in dns.c.
130219960227 portability problem: on an Alpha, bzero() is declared properly
1303 via sys/time.h. impact: couldn't compile on an Alpha. fix:
1304 removed bzero() declaration from select.h. tnx DS.
130519960227 portability problem: under SCO, sys/file.h is not protected.
1306 impact: couldn't compile under SCO. fix: include sys/types.h in
1307 lock.c. tnx RN.
130819960219 change: added some .qmail-list hints to qlist.1.
130919960219 change: added +list support to qmail-alias.
131019960215 change: added THANKS.
131119960212 bug: foo was not initialized in qrelay.c. impact: depends on
1312 the machine; on some machines, no effect; on other machines,
1313 guaranteed core dump. fix: initialized foo. tnx DS.
131419960209 qmail 0.72, beta.
131519960209 change: qmail-alias now replaces dot, not slash, with colon.
1316 also, qmeopen() makes sure that .qmail file is S_IFREG; I hope
1317 this doesn't cause portability problems.
131819960209 change: added success-reporting procedure to INSTALL.
131919960208 change: added VERSION.
132019960208 change: added qlist2.
132119960208 change: revamped qlist interface. tnx RN.
132219960208 change: improved an error message in qlist.c.
132319960208 change: added qrelay. added relay section to HOWTO. tnx DS.
132419960208 cleanup: included substdio.h in qqtalk.h.
132519960207 bug: prioq_delmin() wasn't guaranteeing heap structure on the
1326 last element. impact: scheduled passes could have been delayed,
1327 conceivably as long as half an hour. fix: prioq_delmin() now
1328 checks when it can safely move the last element.
132919960207 change: added maildirmake.1, maildir2mbox.1.
133019960206 change: revised logo paragraph in THOUGHTS.
133119960206 change: replaced nowhere.org with nowhere.mil in examples.
1332 nowhere.org is a real domain... [sigh]
133319960206 change: added qreceipt.1.
133419960206 portability problem: IRIX doesn't have vfork. pointed out by
1335 DS. impact: couldn't compile under IRIX. fix: added fork.h,
1336 tryvfork.c.
133719960206 portability problem: IRIX doesn't have ranlib. pointed out by
1338 DS. impact: couldn't compile under IRIX. fix: added IRIX
1339 section in make-cmds.sh.
134019960205 cleanup: removed warning from substdio_copy() documentation; in
1341 fact, substdio_copy() can be used safely on a fed substdio.
134219960205 change: added qbiff.1.
134319960204 change: implemented localnet. removed relevant paragraph from
1344 THOUGHTS. tnx IS.
134519960204 change: in qmail-remote.8, explained the dangers of smarthost.
1346 tnx IS.
134719960204 change: implemented virtualdomains wildcards. tnx JLH.
134819960203 change: qmail-send now handles virtualdomains _after_ locals.
1349 updated INSTALL.qsmhook appropriately.
135019960203 change: added note to INSTALL.alias about ~ftp, ~www, ~uucp
1351 being owned by root.
135219960130 cleanup: in qlist.c, renamed flagremoved as flagwasthere.
135319960130 bug: qmail-send did not pay attention to flagexitasap in
1354 pass_dochan(). impact: qmail-send would happily start new
1355 deliveries even if it wanted to exit. fix: qmail-send now
1356 returns immediately in pass_selprep() and pass_dochan() if
1357 flagexitasap.
135819960130 change: in qlist.c and qlist.1, renamed ext as list.
135919960130 change: in qlist.c and qlist.1, renamed manager as owner.
136019960129 qmail 0.71, beta.
136119960129 change: mentioned djb-qmailbeta in README. tnx MWE.
136219960129 change: added a note to INSTALL.mbox making clear that
1363 Mailbox is in mbox format. tnx MWE.
136419960129 change: qlist now warns you if it didn't see any cmds. tnx RN.
136519960129 change: incorporated qlist patch from RN to refuse double subs.
136619960129 change: added qlist.1, contributed by RN. mangled it a bit.
136719960129 bug: comment was not allowed in ``phrase (comment) <route>'';
1368 pointed out by RN. impact: some correct address lists could be
1369 mis-parsed by qmail-inject or qlist. fix: token.c now allows
1370 TOKEN_COMMENT in the appropriate scan.
137119960128 change: added a logo paragraph to THOUGHTS.
137219960127 change: implemented rcpthosts.
137319960127 change: split up some uses of putflush in qmail-remote,
1374 qmail-smtpd, spawn.c. eliminated NODELAY and corresponding
1375 paragraph in THOUGHTS.
137619960127 change: added quote2(). used it in qmail-alias, qmail-send,
1377 qreceipt. now addresses are properly quoted in the From, To,
1378 and internal Return-Path of bounces; the From and To of
1379 receipts; and the Return-Path/RPLINE of delivered messages.
1380 removed relevant paragraph from THOUGHTS.
138119960127 change: in RFCLOOPS, documented fact that Delivered-To address
1382 is conventionally not quoted.
138319960127 change: knocked default SMTP timeouts down to 20 minutes.
138419960127 change: added INSTALL.ids. tnx RN.
138519960127 change: in INSTALL, noted that nogroup should already exist.
138619960127 bug: pass_selprep checked pqchan[c] even if pass[c].id. impact:
1387 qmail-send wasted CPU time whenever more than one message was
1388 waiting on a blocked channel. fix: pass_selprep now checks
1389 !pass[c].id.
139019960127 bug: programs invoked from qmail-alias were immune to SIGPIPE.
1391 impact: a delivery pipeline such as |yes|head -1000 would loop
1392 forever, since yes does not check for write errors. fix: added
1393 signal_uninit(). used it before execvp in qmail-alias. [sigh]
139419960127 cleanup: added date822fmt.c. used it in newfield.c, qmail-queue.
139519960127 cleanup: added fmt_uint0.c. used it in myctime.c, newfield.c,
1396 qmail-queue.
139719960127 cleanup: added dnsdoe.c. used it in dnscname, dnsfq, dnsip,
1398 dnsmxip, dnsptr.
139919960127 cleanup: eliminated temp from dnsfq.c.
140019960127 bug: gen_allocdefs was making assumptions incompatible with the
1401 alloc_re interface. impact: qmail-send would dump core if you
1402 ran out of memory. fix: changed alloc_re interface.
140319960126 portability problem: some versions of Linux don't have
1404 net/route.h. pointed out by RN. impact: couldn't compile under
1405 those versions. fix: ipme.c no longer includes net/route.h;
1406 hopefully this won't cause new portability problems.
140719960126 change: added chmod instructions to INSTALL and INSTALL.alias.
1408 tnx RN.
140919960126 change: INSTALL now refers to the traditional sendmail spot
1410 (/usr/lib), not the BSD 4.4 spot (/usr/sbin). tnx RN.
141119960126 change: make auto-uids.h now creates auto-uids.h.tmp first.
1412 thus, if someone disobeys the installation instructions, and
1413 his make fails to remove targets upon error, he'll still be
1414 okay. tnx RN.
141519960126 change: added forgeries.7.
141619960125 cleanup: eliminated flagverbose, flagmetoo in sendmail.
141719960125 cleanup: added substdio_copy.c. used it at several spots.
141819960125 cleanup: added constmap.c. qmail-send now uses constmap for
1419 locals and virtualdomains. this will speed things up: no
1420 problem now to have thousands of virtual domains. removed
1421 relevant paragraph from THOUGHTS.
142219960125 change: added linux section in find-systype. this will affect
1423 linux-* systypes. tnx RN for relevant info.
142419960124 change: added -od, -oe, -p, -f to sendmail. the need for
1425 these was pointed out by TN.
142619960124 bug: qmail-smtpd was reading from descriptor 1. impact: none;
1427 in normal use, both 0 and 1 point to the network. fix: changed
1428 1 to 0.
142919960124 bug: qmail-alias treated any .qmail open failure as permanent.
1430 impact: if a .qmail file was temporarily unopenable (e.g.,
1431 because of NFS), it was incorrectly ignored. fix: qmail-alias
1432 now dies QLX_SOFT on any open failure other than ENOENT.
143319960124 change: added freebsd section in find-systype, same as bsd.os
1434 section. this will affect freebsd-* systypes.
143519960124 cleanup: find-systype now immediately converts sys to lowercase.
143619960124 change: qmail-setup now copies man pages into /var/qmail/man;
1437 qmail-check checks /var/qmail/man. using .0 style, which might
1438 cause trouble on various machines, but better than not trying.
143919960124 change: in qmail-remote.c, changed perm_control to temp_control
1440 (and D to Z, thanks); thus failure to read control files (e.g.,
1441 because of permissions) is now a temporary error.
144219960124 bug: in qmail-remote.c, temp_chdir() used D, not Z. impact: if
1443 chdir() to CONF_HOME failed (e.g., because of NFS), message
1444 would be bounced. fix: changed D to Z.
144519960124 change: reorganized README.
144619960124 portability problem: Linux has the fifo kernel bug that I had
1447 hoped I'd never run into. impact: messages under Linux (and any
1448 other systems with this bug) were picked up only in sweeps, not
1449 instantly. fix: triggerpull.c now writes a byte (non-blocking)
1450 to the fifo. updated INTERNALS accordingly.
145119960124 bug: in qmail-remote.c, if quit() saw a remote write error, it
1452 would call writeerr() even though a message report had already
1453 been produced. impact: the mess report would include an extra
1454 ``ZConnected but communications failed,'' which was confusing
1455 to humans. fix: quit() now simply skips the wait-for-QUIT
1456 smtpcode() upon write error.
145719960124 portability problem: Linux does not have SIGSYS or SIGEMT.
1458 impact: couldn't compile under Linux. fix: added appropriate
1459 ifdefs in signal.c.
146019960124 qmail 0.70, beta.
diff --git a/FAQ b/FAQ
new file mode 100644
index 0000000..8540dbd
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,706 @@
11. Controlling the appearance of outgoing messages
21.1. How do I set up host masquerading?
31.2. How do I set up user masquerading?
41.3. How do I set up Mail-Followup-To automatically?
5
62. Routing outgoing messages
72.1. How do I send local messages to another host?
82.2. How do I set up a null client?
92.3. How do I send outgoing mail through UUCP?
102.4. How do I set up a separate queue for a SLIP/PPP link?
112.5. How do I deal with ``CNAME lookup failed temporarily''?
12
133. Routing incoming messages by host
143.1. How do I receive mail for another host name?
153.2. How do I set up a virtual domain?
163.3. How do I set up several virtual domains for one user?
17
184. Routing incoming messages by user
194.1. How do I forward unrecognized usernames to another host?
204.2. How do I set up a mailing list?
214.3. How do I use majordomo with qmail?
224.4. How do I use procmail with qmail?
234.5. How do I use elm's filter with qmail?
244.6. How do I create aliases with dots?
254.7. How do I use sendmail's .forward files with qmail?
264.8. How do I use sendmail's /etc/aliases with qmail?
274.9. How do I make qmail defer messages during NFS or NIS outages?
284.10. How do I change which account controls an address?
29
305. Setting up servers
315.1. How do I run qmail-smtpd under tcpserver?
325.2. How do I set up qmail-qmtpd?
335.3. How do I set up qmail-pop3d?
345.4. How do I allow selected clients to use this host as a relay?
355.5. How do I fix up messages from broken SMTP clients?
365.6. How do I set up qmail-qmqpd?
37
386. Configuring MUAs to work with qmail
396.1. How do I make BSD mail generate a Date with the local time zone?
406.2. How do I make pine work with qmail?
416.3. How do I make MH work with qmail?
426.4. How do I stop Sun's dtcm from hanging?
43
447. Managing the mail system
457.1. How do I safely stop qmail-send?
467.2. How do I manually run the queue?
477.3. How do I rejuvenate a message?
487.4. How do I organize a big network?
497.5. How do I back up and restore the queue disk?
507.6. How do I run a supervised copy of qmail?
517.7. How do I avoid syslog?
52
538. Miscellany
548.1. How do I tell qmail to do more deliveries at once?
558.2. How do I keep a copy of all incoming and outgoing mail messages?
568.3. How do I switch slowly from sendmail to qmail?
57
58
59
601. Controlling the appearance of outgoing messages
61
62
631.1. How do I set up host masquerading? All the users on this host,
64zippy.af.mil, are users on af.mil. When joe sends a message to fred, the
65message should say ``From: joe@af.mil'' and ``To: fred@af.mil'', without
66``zippy'' anywhere.
67
68Answer: echo af.mil > /var/qmail/control/defaulthost; chmod 644
69/var/qmail/control/defaulthost.
70
71
721.2. How do I set up user masquerading? I'd like my own From lines to
73show boss@af.mil rather than god@heaven.af.mil.
74
75Answer: Add MAILHOST=af.mil and MAILUSER=boss to your environment. To
76override From lines supplied by your MUA, add QMAILINJECT=f to your
77environment.
78
79
801.3. How do I set up Mail-Followup-To automatically? When I send a
81message to the sos@heaven.af.mil mailing list, I'd like to include
82``Mail-Followup-To: sos@heaven.af.mil''.
83
84Answer: Add QMAILMFTFILE=$HOME/.lists to your environment, and put
85sos@heaven.af.mil into ~/.lists.
86
87
88
892. Routing outgoing messages
90
91
922.1. How do I send local messages to another host? All the mail for
93af.mil should be delivered to our disk server, pokey.af.mil. I've set up
94an MX from af.mil to pokey.af.mil, but when a user on the af.mil host
95sends a message to boss@af.mil, af.mil tries to deliver it locally. How
96do I stop that?
97
98Answer: Remove af.mil from /var/qmail/control/locals. If qmail-send is
99running, give it a HUP. Make sure the MX is set up properly before you
100do this. Also make sure that pokey can receive mail for af.mil---see
101question 3.1.
102
103
1042.2. How do I set up a null client? I'd like zippy.af.mil to
105send all mail to bigbang.af.mil.
106
107Answer: echo :bigbang.af.mil > /var/qmail/control/smtproutes;
108chmod 644 /var/qmail/control/smtproutes. Disable local delivery as in
109question 2.1. Turn off qmail-smtpd in /etc/inetd.conf.
110
111
1122.3. How do I send outgoing mail through UUCP? I need qmail to send all
113outgoing mail via UUCP to my upstream UUCP site, gonzo.
114
115Answer: Put
116
117 :alias-uucp
118
119into control/virtualdomains and
120
121 |preline -df /usr/bin/uux - -r -gC
122 -a"${SENDER:-MAILER-DAEMON}" gonzo!rmail "($DEFAULT@$HOST)"
123
124(all on one line) into ~alias/.qmail-uucp-default. (For some UUCP
125software you will need to use -d instead of -df.) If qmail-send is
126running, give it a HUP.
127
128
1292.4. How do I set up a separate queue for a SLIP/PPP link?
130
131Answer: Use serialmail (http://pobox.com/~djb/serialmail.html).
132
133
1342.5. How do I deal with ``CNAME lookup failed temporarily''? The log
135showed that a message was deferred for this reason. Why is qmail doing
136CNAME lookups, anyway?
137
138Answer: The SMTP standard does not permit aliased hostnames, so qmail
139has to do a CNAME lookup in DNS for every recipient host. If the
140relevant DNS server is down, qmail defers the message. It will try again
141soon.
142
143
144
1453. Routing incoming messages by host
146
147
1483.1. How do I receive mail for another host name? I'd like our disk
149server, pokey.af.mil, to receive mail addressed to af.mil. I've set up
150an MX from af.mil to pokey.af.mil, but how do I get pokey to treat
151af.mil as a name for the local host?
152
153Answer: Add af.mil to /var/qmail/control/locals and to
154/var/qmail/control/rcpthosts. If qmail-send is running, give it a HUP
155(or do svc -h /var/run/qmail if qmail is supervised).
156
157
1583.2. How do I set up a virtual domain? I'd like any mail for
159nowhere.mil, including root@nowhere.mil and postmaster@nowhere.mil and
160so on, to be delivered to Bob. I've set up the MX already.
161
162Answer: Put
163
164 nowhere.mil:bob
165
166into control/virtualdomains. Add nowhere.mil to control/rcpthosts. If
167qmail-send is running, give it a HUP (or do svc -h /var/run/qmail if
168qmail is supervised).
169
170Now mail for whatever@nowhere.mil will be delivered locally to
171bob-whatever. Bob can set up ~bob/.qmail-default to catch all the
172possible addresses, ~bob/.qmail-info to catch info@nowhere.mil, etc.
173
174
1753.3. How do I set up several virtual domains for one user? Bob wants
176another virtual domain, everywhere.org, but he wants to handle
177nowhere.mil users and everywhere.org users differently. How can we do
178that without setting up a second account?
179
180Answer: Put two lines into control/virtualdomains:
181
182 nowhere.mil:bob-nowhere
183 everywhere.org:bob-everywhere
184
185Add nowhere.mil and everywhere.org to control/rcpthosts. If qmail-send
186is running, give it a HUP (or do svc -h /var/run/qmail if qmail is
187supervised).
188
189Now Bob can set up separate .qmail-nowhere-* and everywhere-* files. He
190can even set up .qmail-nowhere-default and .qmail-everywhere-default.
191
192
193
1944. Routing incoming messages by user
195
196
1974.1. How do I forward unrecognized usernames to another host? I'd like
198to set up a LUSER_RELAY pointing at bigbang.af.mil.
199
200Answer: Put
201
202 | forward "$LOCAL"@bigbang.af.mil
203
204into ~alias/.qmail-default.
205
206
2074.2. How do I set up a mailing list? I'd like me-sos@my.host.name to be
208forwarded to a bunch of people.
209
210Answer: Put a list of addresses into ~me/.qmail-sos, one per line. Then
211incoming mail for me-sos will be forwarded to each of those addresses.
212You should also touch ~me/.qmail-sos-owner so that bounces come back to
213you rather than the original sender.
214
215Alternative: ezmlm (http://pobox.com/~djb/ezmlm.html) is a modern
216mailing list manager, supporting automatic subscriptions, confirmations,
217archives, fully automatic bounce handling (including warnings to
218subscribers saying which messages they've missed), and more.
219
220
2214.3. How do I use majordomo with qmail?
222
223Answer: See ftp://ftp.eyrie.org/pub/software/majordomo/mjqmail and
224http://www.qmail.org for various methods. majordomo 2.0 is expected to
225support qmail directly.
226
227Beware that majordomo's lists are not crashproof.
228
229
230
2314.4. How do I use procmail with qmail?
232
233Answer: Put
234
235 | preline procmail
236
237into ~/.qmail. You'll have to use a full path for procmail unless
238procmail is in the system's startup PATH. Note that procmail will try to
239deliver to /var/spool/mail/$USER by default; to change this, see
240INSTALL.mbox.
241
242
2434.5. How do I use elm's filter with qmail?
244
245Answer: Put
246
247 | preline filter
248
249into ~/.qmail. You'll have to use a full path for filter unless filter
250is in the system's startup PATH.
251
252
2534.6. How do I create aliases with dots? I tried setting up
254~alias/.qmail-P.D.Q.Bach, but it doesn't do anything.
255
256Answer: Use .qmail-p:d:q:bach. Dots are converted to colons, and
257uppercase is converted to lowercase.
258
259
2604.7. How do I use sendmail's .forward files with qmail?
261
262Answer: Install the dot-forward package
263(http://pobox.com/~djb/dot-forward.html).
264
265
2664.8. How do I use sendmail's /etc/aliases with qmail?
267
268Answer: Install the fastforward package
269(http://pobox.com/~djb/fastforward.html).
270
271
2724.9. How do I make qmail defer messages during NFS or NIS outages? If
273~joe suddenly disappears, I'd like mail for joe to be deferred.
274
275Answer: Build a qmail-users database, so that qmail no longer checks
276home directories and the password database. This takes three steps.
277First, put your complete user list (including local and NIS passwords)
278into /var/qmail/users/passwd. Second, run
279
280 # qmail-pw2u -h < /var/qmail/users/passwd > /var/qmail/users/assign
281
282Here -h means that every user must have a home directory; if you happen
283to run qmail-pw2u during an NFS outage, it will print an error message
284and stop. Third, run
285
286 # qmail-newu
287
288Make sure to rebuild the database whenever you change your user list.
289
290
2914.10. How do I change which account controls an address? I set up
292~alias/.qmail-www, but qmail is looking at ~www/.qmail instead.
293
294Answer: If you do
295
296 # chown root ~www
297
298then qmail will no longer consider www to be a user; see qmail-getpw.0.
299For more precise control over address assignments, see qmail-users.0.
300
301
302
3035. Setting up servers
304
305
3065.1. How do I run qmail-smtpd under tcpserver? inetd is barfing at high
307loads, cutting off service for ten-minute stretches. I'd also like
308better connection logging.
309
310Answer: First, install the tcpserver program, part of the ucspi-tcp
311package (http://pobox.com/~djb/ucspi-tcp.html). Second, remove the smtp
312line from /etc/inetd.conf, and put the line
313
314 tcpserver -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd &
315
316into your system startup files. Replace 7770 with your qmaild uid, and
317replace 2108 with your nofiles gid. Don't forget the &. The change will
318take effect at your next reboot.
319
320By default, tcpserver allows at most 40 simultaneous qmail-smtpd
321processes. To raise this limit to 400, use tcpserver -c 400. To keep
322track of who's connecting and for how long, run (on two lines)
323
324 tcpserver -v -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd \
325 2>&1 | /var/qmail/bin/splogger smtpd 3 &
326
327
3285.2. How do I set up qmail-qmtpd?
329
330Answer: Two steps. First, put a
331
332 qmtp 209/tcp
333
334line into /etc/services. Second, put (all on one line)
335
336 qmtp stream tcp nowait qmaild
337 /var/qmail/bin/tcp-env tcp-env /var/qmail/bin/qmail-qmtpd
338
339into /etc/inetd.conf, and give inetd a HUP.
340
341If you have tcpserver installed, skip the inetd step, and set up
342
343 tcpserver -u 7770 -g 2108 0 qmtp /var/qmail/bin/qmail-qmtpd &
344
345replacing 7770 and 2108 with the qmaild uid and nofiles gid. See
346question 5.1 for more details on tcpserver.
347
348
3495.3. How do I set up qmail-pop3d? My old POP server works with mbox
350delivery; I'd like to switch to maildir delivery.
351
352Answer: Four steps. First, install the checkpassword program
353(http://pobox.com/~djb/checkpwd.html). Second, make sure you have a
354
355 pop3 110/tcp
356
357line in /etc/services. Third, put (all on one line, including
358qmail-popup twice)
359
360 pop3 stream tcp nowait root
361 /var/qmail/bin/qmail-popup qmail-popup
362 YOURHOST /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir
363
364into /etc/inetd.conf, and give inetd a HUP; replace YOURHOST with your
365host's fully qualified domain name. Fourth, set up Maildir delivery for
366any user who wants to read mail via POP.
367
368If you have tcpserver installed, skip the inetd step, and set up (on two
369lines)
370
371 tcpserver 0 pop3 /var/qmail/bin/qmail-popup YOURHOST \
372 /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir &
373
374replacing YOURHOST with your host's fully qualified domain name. See
375question 5.1 for more details on tcpserver.
376
377Security note: pop3d should be used only within a secure network;
378otherwise an eavesdropper can steal passwords.
379
380
3815.4. How do I allow selected clients to use this host as a relay? I see
382that qmail-smtpd rejects messages to any host not listed in
383control/rcpthosts.
384
385Answer: Three steps. First, install tcp-wrappers, available separately,
386including hosts_options. Second, change your qmail-smtpd line in
387inetd.conf to
388
389 smtp stream tcp nowait qmaild /usr/local/bin/tcpd
390 /var/qmail/bin/tcp-env /var/qmail/bin/qmail-smtpd
391
392(all on one line) and give inetd a HUP. Third, in tcpd's hosts.allow,
393make a line setting the environment variable RELAYCLIENT to the empty
394string for the selected clients:
395
396 tcp-env: 1.2.3.4, 1.2.3.5: setenv = RELAYCLIENT
397
398Here 1.2.3.4 and 1.2.3.5 are the clients' IP addresses. qmail-smtpd
399ignores control/rcpthosts when RELAYCLIENT is set. (It also appends
400RELAYCLIENT to each envelope recipient address. See question 5.5 for an
401application.)
402
403Alternative procedure, if you are using tcpserver 0.80 or above: Create
404/etc/tcp.smtp containing
405
406 1.2.3.6:allow,RELAYCLIENT=""
407 127.:allow,RELAYCLIENT=""
408
409to allow clients with IP addresses 1.2.3.6 and 127.*. Run
410
411 tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
412
413Finally, insert
414
415 -x /etc/tcp.smtp.cdb
416
417after tcpserver in your qmail-smtpd invocation.
418
419
4205.5. How do I fix up messages from broken SMTP clients?
421
422Answer: Three steps. First, put
423
424 | bouncesaying 'Permission denied' [ "@$HOST" != "@fixme" ]
425 | qmail-inject -f "$SENDER" -- "$DEFAULT"
426
427into ~alias/.qmail-fixup-default. Second, put
428
429 fixme:fixup
430
431into /var/qmail/control/virtualdomains, and give qmail-send a HUP.
432Third, follow the procedure in question 5.4, but set RELAYCLIENT to the
433string ``@fixme'':
434
435 tcp-env: 1.2.3.6, 1.2.3.7: setenv = RELAYCLIENT @fixme
436
437Here 1.2.3.6 and 1.2.3.7 are the clients' IP addresses. If you are using
438tcpserver instead of inetd and tcpd, put
439
440 1.2.3.6:allow,RELAYCLIENT="@fixme"
441 1.2.3.7:allow,RELAYCLIENT="@fixme"
442
443into /etc/tcp.smtp, and run tcprules as in question 5.4.
444
445
4465.6. How do I set up qmail-qmqpd? I'd like to allow fast queueing of
447outgoing mail from authorized clients.
448
449Answer: Make sure you have installed tcpserver 0.80 or above. Create
450/etc/qmqp.tcp in tcprules format to allow connections from authorized
451hosts. For example, if queueing is allowed from 1.2.3.*:
452
453 1.2.3.:allow
454 :deny
455
456Convert /etc/qmqp.tcp to /etc/qmqp.cdb:
457
458 tcprules /etc/qmqp.cdb /etc/qmqp.tmp < /etc/qmqp.tcp
459
460Finally, set up
461
462 tcpserver -x /etc/qmqp.cdb -u 7770 -g 2108 0 628 /var/qmail/bin/qmail-qmqpd &
463
464replacing 7770 and 2108 with the qmaild uid and nofiles gid. See
465question 5.1 for more details on tcpserver.
466
467
468
4696. Configuring MUAs to work with qmail
470
471
4726.1. How do I make BSD mail generate a Date with the local time zone?
473When I send mail, I'd rather use the local time zone than GMT, since
474some MUAs don't know how to display Date in the receiver's time zone.
475
476Answer: Put
477
478 set sendmail=/var/qmail/bin/datemail
479
480into your .mailrc or your system-wide Mail.rc. Beware that BSD mail is
481neither secure nor reliable.
482
483
4846.2. How do I make pine work with qmail?
485
486Answer: Put
487
488 sendmail-path=/usr/lib/sendmail -oem -oi -t
489
490into /usr/local/lib/pine.conf. (This will work with sendmail too.)
491Beware that pine is neither secure nor reliable.
492
493
4946.3. How do I make MH work with qmail?
495
496Answer: Put
497
498 postproc: /usr/mh/lib/spost
499
500into each user's .mh_profile. (This will work with sendmail too.) Beware
501that MH is neither secure nor reliable.
502
503
5046.4. How do I stop Sun's dtcm from hanging?
505
506Answer: There is a novice programming error in dtcm, known as ``failure
507to close the output side of the pipe in the child.'' Sun has, at the
508time of this writing, not yet provided a patch. Sorry.
509
510
511
5127. Managing the mail system
513
514
5157.1. How do I safely stop qmail-send? Back when we were running
516sendmail, it was always tricky to kill sendmail without risking the loss
517of current deliveries; what should I do with qmail-send?
518
519Answer: Go ahead and kill the qmail-send process. It will shut down
520cleanly. Wait for ``exiting'' to show up in the log. To restart qmail,
521run /var/qmail/rc the same way it is run from your system boot scripts,
522with the proper PATH, resource limits, etc.
523
524Alternative, if qmail is supervised: svc -t /var/run/qmail. The
525supervise process will kill qmail, wait for it to stop, and restart it.
526Use -d instead of -t if you don't want qmail to restart automatically;
527to manually restart it, use -u.
528
529
5307.2. How do I manually run the queue? I'd like qmail to try delivering
531all the remote messages right now.
532
533Answer: Give the qmail-send process an ALRM. (Do svc -a /var/run/qmail
534if qmail is supervised.)
535
536You may want to run qmail-tcpok first, to guarantee that qmail-remote
537will try all addresses. Normally, if an address fails repeatedly,
538qmail-remote leaves it alone for an hour.
539
540
5417.3. How do I rejuvenate a message? Somebody broke into Eric's computer
542again; it's going to be down for at least another two days. I know Eric
543has been expecting an important message---in fact, I see it sitting here
544in /var/qmail/queue/mess/15/26902. It's been in the queue for six days;
545how can I make sure it isn't bounced tomorrow?
546
547Answer: Just touch /var/qmail/queue/info/15/26902. (This is the only
548form of queue modification that's safe while qmail is running.)
549
550
5517.4. How do I organize a big network? I have a lot of machines, and I
552don't know where to start.
553
554Answer: First, choose the domain name where your users will receive
555mail. This is normally the shortest domain name you control. If you are
556in charge of *.movie.edu, you can use addresses like joe@movie.edu.
557
558Second, choose the machine that will know what to do with different
559users at movie.edu. Set up a host name in DNS for this machine:
560
561 mailhost.movie.edu IN A 1.2.3.4
562 4.3.2.1.in-addr.arpa IN PTR mailhost.movie.edu
563
564Here 1.2.3.4 is the IP address of that machine.
565
566Third, make a list of machines where mail should end up. For example, if
567mail for Bob should end up on Bob's workstation, put Bob's workstation
568onto the list. For each of these machines, set up a host name in DNS:
569
570 bobshost.movie.edu IN A 1.2.3.7
571 7.3.2.1.in-addr.arpa IN PTR bobshost.movie.edu
572
573Fourth, install qmail on bobshost.movie.edu. qmail will automatically
574configure itself to accept messages for bob@bobshost.movie.edu and
575deliver them to ~bob/Mailbox on bobshost. Do the same for the other
576machines where mail should end up.
577
578Fifth, install qmail on mailhost.movie.edu. Put
579
580 movie.edu:alias-movie
581
582into control/virtualdomains on mailhost. Then forward bob@movie.edu to
583bob@bobshost.movie.edu, by putting
584
585 bob@bobshost.movie.edu
586
587into ~alias/.qmail-movie-bob. Do the same for other users.
588
589Sixth, put movie.edu into control/rcpthosts on mailhost.movie.edu, so
590that mailhost.movie.edu will accept messages for users at movie.edu.
591
592Seventh, set up an MX record in DNS to deliver movie.edu messages to
593mailhost:
594
595 movie.edu IN MX 10 mailhost.movie.edu
596
597Eighth, on all your machines, put movie.edu into control/defaulthost.
598
599
6007.5. How do I back up and restore the queue disk?
601
602Answer: You can't.
603
604One difficulty is that you can't get a consistent snapshot of the queue
605while qmail-send is running. Another difficulty is that messages in the
606queue must have filenames that match their inode numbers.
607
608However, the big problem is that backups---even twice-daily backups---
609are far too unreliable for mail. If your disk dies, there will be very
610little overlap between the messages saved in the last backup and the
611messages that were lost.
612
613There are several ways to add real reliability to a mail server. Battery
614backups will keep your server alive, letting you park the disk to avoid
615a head crash, when the power goes out. Solid-state disks have their own
616battery backups. RAID boxes let you replace dead disks without losing
617any data.
618
619
6207.6. How do I run a supervised copy of qmail? svc sounds useful.
621
622Answer: Install daemontools (http://pobox.com/~djb/daemontools.html).
623Create a /var/run/qmail directory. Change
624
625 /var/qmail/rc
626
627to
628
629 supervise /var/run/qmail /var/qmail/rc
630
631in your boot scripts. Make sure that supervise is in the startup PATH.
632Now you can use svc to stop or restart qmail, and svstat to check
633whether qmail is running.
634
635
6367.7. How do I avoid syslog? It chews up a lot of CPU time and isn't
637reliable.
638
639Answer: Install daemontools (http://pobox.com/~djb/daemontools.html).
640Make a /var/log/qmail directory, owned by qmaill, mode 2700. Do
641
642 qmail-start ./Mailbox /usr/local/bin/accustamp \
643 | setuser qmaill /usr/local/bin/cyclog /var/log/qmail &
644
645in /var/qmail/rc.
646
647If you are logging tcpserver connections, make a /var/log/smtpd
648directory, and use cyclog /var/log/smtpd for tcpserver. You shouldn't
649run several copies of cyclog with the same log directory.
650
651By default, cyclog keeps 10 automatically rotated log files, each
652containing up to 100KB of log data. To keep 20 files with 1MB each, use
653cyclog -s 1000000 -n 20.
654
655
656
6578. Miscellany
658
659
6608.1. How do I tell qmail to do more deliveries at once? It's running
661only 20 parallel qmail-remote processes.
662
663Answer: Decide how many deliveries you want to allow at once. Put that
664number into control/concurrencyremote. Restart qmail-send as in question
6657.1. If your system has resource limits, make sure you set the
666descriptors limit to at least double the concurrency plus 5; otherwise
667you'll get lots of unnecessary deferrals whenever a big burst of mail
668shows up. Note that qmail also imposes a compile-time concurrency limit,
669120 by default; this is set in conf-spawn.
670
671
6728.2. How do I keep a copy of all incoming and outgoing mail messages?
673
674Answer: Set QUEUE_EXTRA to "Tlog\0" and QUEUE_EXTRALEN to 5 in extra.h.
675Recompile qmail. Put ./msg-log into ~alias/.qmail-log.
676
677You can also use QUEUE_EXTRA to, e.g., record the Message-ID of every
678message: run
679
680 | awk '/^$/ { exit } /^[mM][eE][sS][sS][aA][gG][eE]-/ { print }'
681
682from ~alias/.qmail-log.
683
684
6858.3. How do I switch slowly from sendmail to qmail? I'm thinking of
686moving the heaven.af.mil network over to qmail, but first I'd like to
687give my users a chance to try out qmail without affecting current
688sendmail deliveries. We're using NFS.
689
690Answer: Find a host in your network, say pc.heaven.af.mil, that isn't
691running an SMTP server. (If addresses at pc.heaven.af.mil are used, you
692should already have an MX pointing pc.heaven.af.mil to your mail hub.)
693
694Set up a new MX record pointing lists.heaven.af.mil to pc.heaven.af.mil.
695Install qmail on pc.heaven.af.mil. Replace pc with lists in the control
696files. Make the qmail man pages available on all your machines.
697
698Now tell your users about qmail. A user can forward joe@heaven.af.mil to
699joe@lists.heaven.af.mil to get ~/Mailbox delivery; he can set up .qmail
700files; he can start running his own mailing lists @lists.heaven.af.mil.
701
702When you're ready to turn sendmail off, you can set up pc.heaven.af.mil
703as your new mail hub. Add heaven.af.mil to control/locals, and change
704the heaven.af.mil MX to point to pc.heaven.af.mil. Make sure you leave
705lists.heaven.af.mil in control/locals so that transition addresses will
706continue to work.
diff --git a/FILES b/FILES
new file mode 100644
index 0000000..bbf25c2
--- /dev/null
+++ b/FILES
@@ -0,0 +1,433 @@
1BLURB
2BLURB2
3BLURB3
4BLURB4
5README
6FAQ
7INSTALL
8INSTALL.alias
9INSTALL.ctl
10INSTALL.ids
11INSTALL.maildir
12INSTALL.mbox
13INSTALL.vsm
14REMOVE.sendmail
15REMOVE.binmail
16TEST.deliver
17TEST.receive
18UPGRADE
19THOUGHTS
20TODO
21THANKS
22CHANGES
23SECURITY
24INTERNALS
25SENDMAIL
26PIC.local2alias
27PIC.local2ext
28PIC.local2local
29PIC.local2rem
30PIC.local2virt
31PIC.nullclient
32PIC.relaybad
33PIC.relaygood
34PIC.rem2local
35FILES
36VERSION
37SYSDEPS
38TARGETS
39Makefile
40BIN.README
41BIN.Makefile
42idedit.c
43conf-break
44auto_break.h
45conf-spawn
46auto_spawn.h
47chkspawn.c
48conf-split
49auto_split.h
50conf-patrn
51auto_patrn.h
52conf-users
53conf-groups
54auto_uids.h
55auto_usera.h
56extra.h
57addresses.5
58except.1
59bouncesaying.1
60condredirect.1
61dot-qmail.9
62envelopes.5
63forgeries.7
64forward.1
65maildir2mbox.1
66maildirmake.1
67maildirwatch.1
68mailsubj.1
69mbox.5
70preline.1
71qbiff.1
72qmail-clean.8
73qmail-command.8
74qmail-control.9
75qmail-getpw.9
76qmail-header.5
77qmail-inject.8
78qmail-limits.9
79qmail-local.8
80qmail-log.5
81qmail-lspawn.8
82qmail-newmrh.9
83qmail-newu.9
84qmail-pop3d.8
85qmail-popup.8
86qmail-pw2u.9
87qmail-qmqpc.8
88qmail-qmqpd.8
89qmail-qmtpd.8
90qmail-qread.8
91qmail-qstat.8
92qmail-queue.8
93qmail-remote.8
94qmail-rspawn.8
95qmail-send.9
96qmail-showctl.8
97qmail-smtpd.8
98qmail-start.9
99qmail-tcpok.8
100qmail-tcpto.8
101qmail-users.9
102qmail.7
103qreceipt.1
104splogger.8
105tcp-env.1
106config.sh
107config-fast.sh
108qmail-clean.c
109qmail-getpw.c
110qmail-inject.c
111qmail-local.c
112qmail-lspawn.c
113qmail-newmrh.c
114qmail-newu.c
115qmail-pop3d.c
116qmail-popup.c
117qmail-pw2u.c
118qmail-qmqpc.c
119qmail-qmqpd.c
120qmail-qmtpd.c
121qmail-qread.c
122qmail-qstat.sh
123qmail-queue.c
124qmail-remote.c
125qmail-rspawn.c
126qmail-send.c
127qmail-showctl.c
128qmail-smtpd.c
129qmail-start.c
130qmail-tcpok.c
131qmail-tcpto.c
132spawn.c
133dnscname.c
134dnsfq.c
135dnsip.c
136dnsmxip.c
137dnsptr.c
138hostname.c
139ipmeprint.c
140tcp-env.c
141sendmail.c
142qreceipt.c
143qsmhook.c
144qbiff.c
145forward.c
146preline.c
147predate.c
148except.c
149bouncesaying.c
150condredirect.c
151maildirmake.c
152maildir2mbox.c
153maildirwatch.c
154splogger.c
155qail.sh
156elq.sh
157pinq.sh
158qmail-upq.sh
159datemail.sh
160mailsubj.sh
161qlx.h
162rcpthosts.h
163rcpthosts.c
164commands.h
165commands.c
166dnsdoe.h
167dnsdoe.c
168fmtqfn.h
169fmtqfn.c
170gfrom.h
171gfrom.c
172myctime.h
173myctime.c
174newfield.h
175newfield.c
176qsutil.h
177qsutil.c
178readsubdir.h
179readsubdir.c
180received.h
181received.c
182tcpto.h
183tcpto.c
184tcpto_clean.c
185trigger.h
186trigger.c
187triggerpull.h
188triggerpull.c
189trynpbg1.c
190trysyslog.c
191conf-cc
192conf-ld
193home.sh
194home+df.sh
195proc.sh
196proc+df.sh
197binm1.sh
198binm2.sh
199binm3.sh
200binm1+df.sh
201binm2+df.sh
202binm3+df.sh
203find-systype.sh
204make-compile.sh
205make-load.sh
206make-makelib.sh
207trycpp.c
208warn-auto.sh
209auto-str.c
210auto-int.c
211auto-int8.c
212auto-gid.c
213auto-uid.c
214hier.c
215install.c
216instcheck.c
217install-big.c
218alloc.3
219alloc.h
220alloc.c
221alloc_re.c
222case.3
223case.h
224case_diffb.c
225case_diffs.c
226case_lowerb.c
227case_lowers.c
228case_starts.c
229cdb.3
230cdb.h
231cdb_hash.c
232cdb_seek.c
233cdb_unpack.c
234cdbmake.h
235cdbmake_add.c
236cdbmake_hash.c
237cdbmake_pack.c
238cdbmss.h
239cdbmss.c
240coe.3
241coe.h
242coe.c
243fd.h
244fd_copy.3
245fd_copy.c
246fd_move.3
247fd_move.c
248fifo_make.3
249fifo.h
250fifo.c
251trymkffo.c
252fork.h1
253fork.h2
254tryvfork.c
255now.3
256now.h
257now.c
258open.h
259open_append.c
260open_excl.c
261open_read.c
262open_trunc.c
263open_write.c
264seek.h
265seek_cur.c
266seek_end.c
267seek_set.c
268seek_trunc.c
269conf-qmail
270auto_qmail.h
271qmail.h
272qmail.c
273gen_alloc.h
274gen_allocdefs.h
275stralloc.3
276stralloc.h
277stralloc_eady.c
278stralloc_pend.c
279stralloc_copy.c
280stralloc_opyb.c
281stralloc_opys.c
282stralloc_cat.c
283stralloc_catb.c
284stralloc_cats.c
285stralloc_arts.c
286strerr.h
287strerr_sys.c
288strerr_die.c
289substdio.h
290substdio.c
291substdi.c
292substdo.c
293substdio_copy.c
294subfd.h
295subfderr.c
296subfdouts.c
297subfdout.c
298subfdins.c
299subfdin.c
300readwrite.h
301exit.h
302timeoutconn.h
303timeoutconn.c
304timeoutread.h
305timeoutread.c
306timeoutwrite.h
307timeoutwrite.c
308remoteinfo.h
309remoteinfo.c
310uint32.h1
311uint32.h2
312tryulong32.c
313wait.3
314wait.h
315wait_pid.c
316wait_nohang.c
317trywaitp.c
318sig.h
319sig_alarm.c
320sig_block.c
321sig_catch.c
322sig_pause.c
323sig_pipe.c
324sig_child.c
325sig_term.c
326sig_hup.c
327sig_misc.c
328sig_bug.c
329trysgact.c
330trysgprm.c
331env.3
332env.h
333env.c
334envread.c
335byte.h
336byte_chr.c
337byte_copy.c
338byte_cr.c
339byte_diff.c
340byte_rchr.c
341byte_zero.c
342str.h
343str_chr.c
344str_cpy.c
345str_diff.c
346str_diffn.c
347str_len.c
348str_rchr.c
349str_start.c
350lock.h
351lock_ex.c
352lock_exnb.c
353lock_un.c
354tryflock.c
355getln.3
356getln.h
357getln.c
358getln2.3
359getln2.c
360sgetopt.3
361sgetopt.h
362sgetopt.c
363subgetopt.3
364subgetopt.h
365subgetopt.c
366error.3
367error_str.3
368error_temp.3
369error.h
370error.c
371error_str.c
372error_temp.c
373fmt.h
374fmt_str.c
375fmt_strn.c
376fmt_uint.c
377fmt_uint0.c
378fmt_ulong.c
379scan.h
380scan_ulong.c
381scan_8long.c
382slurpclose.h
383slurpclose.c
384quote.h
385quote.c
386hfield.h
387hfield.c
388headerbody.h
389headerbody.c
390token822.h
391token822.c
392control.h
393control.c
394datetime.3
395datetime.h
396datetime.c
397datetime_un.c
398prioq.h
399prioq.c
400date822fmt.h
401date822fmt.c
402dns.h
403dns.c
404trylsock.c
405tryrsolv.c
406ip.h
407ip.c
408ipalloc.h
409ipalloc.c
410select.h1
411select.h2
412trysysel.c
413ndelay.h
414ndelay.c
415ndelay_off.c
416direntry.3
417direntry.h1
418direntry.h2
419trydrent.c
420prot.h
421prot.c
422chkshsgr.c
423warn-shsgr
424tryshsgr.c
425ipme.h
426ipme.c
427trysalen.c
428maildir.5
429maildir.h
430maildir.c
431tcp-environ.5
432constmap.h
433constmap.c
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..e3b0f09
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,84 @@
1SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and
2information generally), the qmail system comes with NO WARRANTY. It's
3much more secure and reliable than sendmail, but that's not saying much.
4
5
6Things you have to decide before starting:
7
8* The qmail home directory, normally /var/qmail. To change this
9directory, edit conf-qmail now.
10
11* The names of the qmail users and the qmail groups. To change these
12names, edit conf-users and conf-groups now.
13
14
15To create /var/qmail and configure qmail (won't interfere with sendmail):
16
17 1. Create the qmail home directory:
18 # mkdir /var/qmail
19
20 2. Read INSTALL.ids. You must set up the qmail group and the qmail
21 users before compiling the programs.
22
23 3. Compile the programs and create the qmail directory tree:
24 # make setup check
25
26 4. Read INSTALL.ctl and FAQ. Minimal survival command:
27 # ./config
28
29 5. Read INSTALL.alias. Minimal survival command:
30 # (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root)
31 # chmod 644 ~alias/.qmail*
32
33 6. Read INSTALL.mbox and INSTALL.vsm.
34
35 7. Read INSTALL.maildir.
36
37 8. Copy /var/qmail/boot/home (or proc) to /var/qmail/rc.
38
39
40To test qmail deliveries (won't interfere with sendmail):
41
42 9. Enable deliveries of messages injected into qmail:
43 # csh -cf '/var/qmail/rc &'
44
4510. Read TEST.deliver.
46
47
48To upgrade from sendmail to qmail:
49
5011. Read SENDMAIL. This is what your users will want to know about the
51 switch from sendmail to qmail.
52
5312. Read REMOVE.sendmail. You must remove sendmail before installing
54 qmail.
55
5613. Read REMOVE.binmail.
57
5814. Add
59 csh -cf '/var/qmail/rc &'
60 to your boot scripts, so that the qmail daemons are restarted
61 whenever your system reboots. Make sure you include the &.
62
6315. Make qmail's ``sendmail'' wrapper available to MUAs:
64 # ln -s /var/qmail/bin/sendmail /usr/lib/sendmail
65 # ln -s /var/qmail/bin/sendmail /usr/sbin/sendmail
66 /usr/sbin might not exist on your system.
67
6816. Set up qmail-smtpd in /etc/inetd.conf (all on one line):
69 smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env
70 tcp-env /var/qmail/bin/qmail-smtpd
71
7217. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons
73 are running.)
74
7518. Read TEST.receive.
76
77
78
79That's it! To report success:
80 % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to
81Replace First M. Last with your name.
82
83If you have questions about qmail, join the qmail mailing list; see
84http://pobox.com/~djb/qmail.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 @@
1qmail lets each user control all addresses of the form user-anything.
2Addresses that don't start with a username are controlled by a special
3user, alias. Delivery instructions for foo go into ~alias/.qmail-foo;
4delivery instructions for user-foo go into ~user/.qmail-foo. See
5dot-qmail.0 for the full story.
6
7qmail doesn't have any built-in support for /etc/aliases. If you have a
8big /etc/aliases and you'd like to keep it, install the fastforward
9package, available separately. /etc/aliases should already include the
10aliases discussed below---Postmaster, MAILER-DAEMON, and root.
11
12If you don't have a big /etc/aliases, you'll find it easier to use
13qmail's native alias mechanism. Here's a checklist of aliases you should
14set up right now.
15
16* Postmaster. You're not an Internet citizen if this address doesn't
17work. Simply touch (and chmod 644) ~alias/.qmail-postmaster; any mail
18for Postmaster will be delivered to ~alias/Mailbox.
19
20* MAILER-DAEMON. Not required, but users sometimes respond to bounce
21messages. Touch (and chmod 644) ~alias/.qmail-mailer-daemon.
22
23* root. Under qmail, root never receives mail. Your system may generate
24mail messages to root every night; if you don't have an alias for root,
25those messages will bounce. (They'll end up double-bouncing to the
26postmaster.) Set up an alias for root in ~alias/.qmail-root. .qmail
27files are similar to .forward files, but beware that they are strictly
28line-oriented---see dot-qmail.0 for details.
29
30* Other non-user accounts. Under qmail, non-user accounts don't get
31mail; ``user'' means a non-root account that owns ~account. Set up
32aliases for any non-user accounts that normally receive mail.
33
34Note that special accounts such as ftp, www, and uucp should always have
35home directories owned by root.
36
37* Default. If you want, you can touch ~alias/.qmail-default to catch
38everything else. Beware: this will also catch typos and other addresses
39that should probably be bounced instead. It won't catch addresses that
40start 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 @@
1As you've seen, qmail has essentially no pre-compilation configuration.
2You should never have to recompile it unless you want to change the
3qmail home directory, usernames, or uids.
4
5qmail does allow quite a bit of easy post-installation configuration. If
6you care how your machine greets other machines via SMTP, for example,
7you can put an appropriate line into /var/qmail/control/smtpgreeting.
8
9But this is all optional---if control/smtpgreeting doesn't exist, qmail
10will do something reasonable by default. You shouldn't worry much about
11configuration right now. You can always come back and tune things later.
12
13There's one big exception. You MUST tell qmail your hostname. Just run
14the config-fast script:
15
16 # ./config-fast your.full.host.name
17
18config-fast puts your.full.host.name into control/me. It also puts it
19into control/locals and control/rcpthosts, so that qmail will accept
20mail for your.full.host.name.
21
22You can instead use the config script, which looks up your host name in
23DNS:
24
25 # ./config
26
27config also looks up your local IP addresses in DNS to decide which
28hosts to accept mail for.
29
30(Why doesn't qmail do these lookups on the fly? This was a deliberate
31design decision. qmail does all its local functions---header rewriting,
32checking if a recipient is local, etc.---without talking to the network.
33The point is that qmail can continue accepting and delivering local mail
34even if your network connection goes down.)
35
36Next, read through FAQ for information on setting up optional features
37like masquerading. If you really want to learn right now what all the
38configuration 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 @@
1Here's how to set up the qmail groups and the qmail users.
2
3On some systems there are commands that make this easy. Solaris and
4Linux:
5
6 # groupadd nofiles
7 # useradd -g nofiles -d /var/qmail/alias alias
8 # useradd -g nofiles -d /var/qmail qmaild
9 # useradd -g nofiles -d /var/qmail qmaill
10 # useradd -g nofiles -d /var/qmail qmailp
11 # groupadd qmail
12 # useradd -g qmail -d /var/qmail qmailq
13 # useradd -g qmail -d /var/qmail qmailr
14 # useradd -g qmail -d /var/qmail qmails
15
16FreeBSD 2.2:
17
18 # pw groupadd nofiles
19 # pw useradd alias -g nofiles -d /var/qmail/alias -s /nonexistent
20 # pw useradd qmaild -g nofiles -d /var/qmail -s /nonexistent
21 # pw useradd qmaill -g nofiles -d /var/qmail -s /nonexistent
22 # pw useradd qmailp -g nofiles -d /var/qmail -s /nonexistent
23 # pw groupadd qmail
24 # pw useradd qmailq -g qmail -d /var/qmail -s /nonexistent
25 # pw useradd qmailr -g qmail -d /var/qmail -s /nonexistent
26 # pw useradd qmails -g qmail -d /var/qmail -s /nonexistent
27
28BSDI 2.0:
29
30 # addgroup nofiles
31 # adduser -g nofiles -H/var/qmail/alias -G,,, -s/dev/null -P'*' alias
32 # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaild
33 # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaill
34 # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmailp
35 # addgroup qmail
36 # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailq
37 # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailr
38 # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmails
39
40AIX:
41
42 # mkgroup -A nofiles
43 # mkuser pgrp=nofiles home=/var/qmail/alias shell=/bin/true alias
44 # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaild
45 # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaill
46 # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmailp
47 # mkgroup -A qmail
48 # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailq
49 # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailr
50 # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmails
51
52On other systems, you will have to edit /etc/group and /etc/passwd
53manually. First add two new lines to /etc/group, something like
54
55 qmail:*:2107:
56 nofiles:*:2108:
57
58where 2107 and 2108 are different from the other gids in /etc/group.
59Next (using vipw) add six new lines to /etc/passwd, something like
60
61 alias:*:7790:2108::/var/qmail/alias:/bin/true
62 qmaild:*:7791:2108::/var/qmail:/bin/true
63 qmaill:*:7792:2108::/var/qmail:/bin/true
64 qmailp:*:7793:2108::/var/qmail:/bin/true
65 qmailq:*:7794:2107::/var/qmail:/bin/true
66 qmailr:*:7795:2107::/var/qmail:/bin/true
67 qmails:*:7796:2107::/var/qmail:/bin/true
68
69where 7790 through 7796 are _new_ uids, 2107 is the qmail gid, and 2108
70is the nofiles gid. Make sure you use the nofiles gid for qmaild,
71qmaill, qmailp, and alias, and the qmail gid for qmailq, qmailr, and
72qmails.
diff --git a/INSTALL.maildir b/INSTALL.maildir
new file mode 100644
index 0000000..72373aa
--- /dev/null
+++ b/INSTALL.maildir
@@ -0,0 +1,59 @@
1This file points out some reasons that you might want to switch from
2mbox format to a new format, maildir.
3
4
51. The trouble with mbox
6
7The mbox format---the format of ~user/Mailbox, understood by BSD Mail
8and lots of other MUAs---is inherently unreliable.
9
10Think about it: what happens if the system crashes while a program is
11appending a new message to ~user/Mailbox? The message will be truncated.
12Even worse, if it was truncated in the middle of a line, it will end up
13being merged with the next message! Sure, the mailer understands that it
14wasn't successful, so it'll try delivering the message again later, but
15it can't fix your corrupted mbox.
16
17Other formats, such as mh folders, are just as unreliable.
18
19qmail supports maildir, a crashproof format for incoming mail messages.
20maildir is fast and easy for MUAs to use. Even better, maildir works
21wonders over NFS---see below.
22
23I don't want to cram maildir down people's throats, so it's not the
24default. Nevertheless, I encourage you to start asking for maildir
25versions of your favorite MUAs, and to switch over to maildir as soon as
26you can.
27
28
292. Sun's Network F_ail_u_re System
30
31Anyone who tells you that mail can be safely delivered in mbox format
32over NFS is pulling your leg---as explained above, mbox format is
33inherently unreliable even on a single machine.
34
35Anyway, NFS is the most unreliable computing environment ever invented,
36and qmail doesn't even pretend to support mbox over NFS.
37
38You should switch to maildir, which works fine over NFS without any
39locking. You can safely read your mail over NFS if it's in maildir
40format. Any number of machines can deliver mail to you at the same time.
41(On the other hand, for efficiency, it's better to get NFS out of the
42picture---your mail should be delivered on the server that contains your
43home directory.)
44
45Here's how to set up qmail to use maildir for your incoming mail:
46
47 % maildirmake $HOME/Maildir
48 % echo ./Maildir/ > ~/.qmail
49
50Make sure you include the trailing slash on Maildir/.
51
52The system administrator can set up Maildir as the default for everybody
53by creating a maildir in the new-user template directory and replacing
54./Mailbox with ./Maildir/ in /var/qmail/rc.
55
56Until your MUA supports maildir, you'll probably want to convert maildir
57format to (gaaack) mbox format. I've supplied a maildir2mbox utility
58that does the trick, along with some tiny qail and elq and pinq wrappers
59that 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 @@
1The qmail package includes a local delivery agent, qmail-local, which
2provides user-controlled mailing lists, cross-host alias loop detection,
3and many other important qmail features.
4
5There's one important difference between qmail-local and binmail:
6qmail-local delivers mail by default into ~user/Mailbox, rather than
7/var/spool/mail/user. It uses mbox format, with lockf locking on systems
8that don't have flock (HP/UX, Solaris), and flock locking otherwise.
9
10This file explains how to switch your system to ~user/Mailbox. You
11aren't required to do this; for further discussion of /var/spool/mail,
12and an explanation of how to continue using binmail for local
13deliveries, see INSTALL.vsm.
14
15The basic procedure for switching to ~user/Mailbox is simple:
16
17 * Move each /var/spool/mail/user to ~user/Mailbox. For safety, do
18 this in single-user mode.
19
20 * As root, set up a symbolic link from /var/spool/mail/user to
21 ~user/Mailbox for each user. /var/spool/mail should be mode 1777,
22 so users will not be able to accidentally remove these links.
23
24A few mail programs are unable to handle symbolic links, so you will
25have to configure them to look at ~user/Mailbox directly:
26
27 * procmail: Change SYSTEM_MBOX in config.h and recompile; or, with
28 recent versions, define MAILSPOOLHOME in src/authenticate.c.
29
30An alternative to symbolic links is hlfsd. Consult the documentation for
31hlfsd if it is included in your operating system.
32
33If /var/spool/mail is large, you can gain extra speed by configuring
34all your mail software to look at ~user/Mailbox directly:
35
36 * Most MUAs: Put ``setenv MAIL $HOME/Mailbox'' in your system-wide
37 .cshrc and ``MAIL=$HOME/Mailbox; export MAIL'' in your system-wide
38 .profile.
39
40 * elm: Change "mailbox" to "Mailbox" around line 388 of newmbox.c and
41 recompile. (elm looks at $MAIL, but without this change elm will
42 fail if two users try to read mail simultaneously.)
43
44 * pine: Put ``inbox-path=Mailbox'' in your system-wide pine.conf.
45 (For pine versions more recent than 3.91, see also FAQ 6.2.)
46
47 * qpopper 2.2: Change /.mail to /Mailbox in pop_dropcopy.c and
48 recompile with -DHOMEDIRMAIL in CFLAGS.
49
50Some vendors, in a misguided attempt to solve the security problems of
51/var/spool/mail, have made all their mail software setgid mail. After
52you move the mailboxes, you can---and, for security, should---remove
53those 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 @@
1UNIX has traditionally delivered mail into a central spool directory,
2/var/spool/mail. (The original name was /usr/spool/mail; some systems
3now use /var/mail.) There are two basic problems with /var/spool/mail:
4
5 * It's slow. On systems with thousands of users, /var/spool/mail has
6 thousands of entries. A few UNIX systems support fast operations on
7 large directories, but most don't.
8
9 * It's insecure. Writing code that works safely in a world-writable
10 directory is not easy. See, for example, CERT advisory 95:02.
11
12These may not be problems at your site, so you may want to leave your
13mailboxes in /var/spool/mail.
14
15This file explains several ways that you can configure qmail to use
16existing /var/spool/mail delivery tools. Please note that I do not vouch
17for the security or reliability of any of those tools.
18
19
201. What to configure
21
22The qmail system is started from /var/qmail/rc with
23
24 qmail-start ./Mailbox splogger qmail
25
26The first argument to qmail-start, ./Mailbox, is the default delivery
27instruction. You can change it to run a program such as binmail or
28procmail. (See dot-qmail.0 for the format of delivery instructions.)
29
30
312. Using procmail
32
33You may already have installed procmail for mail filtering. procmail
34delivers to /var/spool/mail by default.
35
36To set up qmail to use procmail, simply copy /var/qmail/boot/proc to
37/var/qmail/rc.
38
39Note that procmail must be in your system's boot PATH; if it isn't, you
40will have edit /var/qmail/rc to include the full path.
41
42
433. Using sendmail's delivery agent
44
45sendmail uses binmail to deliver to /var/spool/mail. binmail is shipped
46with the operating system as /bin/mail or /usr/libexec/mail.local.
47
48There is some variation in binmail syntax among systems. The most common
49interfaces are shown in /var/qmail/boot/binm1, /var/qmail/boot/binm2,
50and /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 @@
11. Overview
2
3Here's the data flow in the qmail suite:
4
5 qmail-smtpd --- qmail-queue --- qmail-send --- qmail-rspawn --- qmail-remote
6 / | \
7qmail-inject _/ qmail-clean \_ qmail-lspawn --- qmail-local
8
9Every message is added to a central queue directory by qmail-queue.
10qmail-queue is invoked as needed, usually by qmail-inject for locally
11generated messages, qmail-smtpd for messages received through SMTP,
12qmail-local for forwarded messages, or qmail-send for bounce messages.
13
14Every message is then delivered by qmail-send, in cooperation with
15qmail-lspawn and qmail-rspawn, and cleaned up by qmail-clean. These four
16programs are long-running daemons.
17
18The queue is designed to be crashproof, provided that the underlying
19filesystem is crashproof. All cleanups are handled by qmail-send and
20qmail-clean without human intervention. See section 6 for more details.
21
22
232. Queue structure
24
25Each message in the queue is identified by a unique number, let's say
26457. The queue is organized into several directories, each of which may
27contain files related to message 457:
28
29 mess/457: the message
30 todo/457: the envelope: where the message came from, where it's going
31 intd/457: the envelope, under construction by qmail-queue
32 info/457: the envelope sender address, after preprocessing
33 local/457: local envelope recipient addresses, after preprocessing
34 remote/457: remote envelope recipient addresses, after preprocessing
35 bounce/457: permanent delivery errors
36
37Here are all possible states for a message. + means a file exists; -
38means it does not exist; ? means it may or may not exist.
39
40 S1. -mess -intd -todo -info -local -remote -bounce
41 S2. +mess -intd -todo -info -local -remote -bounce
42 S3. +mess +intd -todo -info -local -remote -bounce
43 S4. +mess ?intd +todo ?info ?local ?remote -bounce (queued)
44 S5. +mess -intd -todo +info ?local ?remote ?bounce (preprocessed)
45
46Guarantee: If mess/457 exists, it has inode number 457.
47
48
493. How messages enter the queue
50
51To add a message to the queue, qmail-queue first creates a file in a
52separate directory, pid/, with a unique name. The filesystem assigns
53that file a unique inode number. qmail-queue looks at that number, say
54457. By the guarantee above, message 457 must be in state S1.
55
56qmail-queue renames pid/whatever as mess/457, moving to S2. It writes
57the message to mess/457. It then creates intd/457, moving to S3, and
58writes the envelope information to intd/457.
59
60Finally qmail-queue creates a new link, todo/457, for intd/457, moving
61to S4. At that instant the message has been successfully queued, and
62qmail-queue leaves it for further handling by qmail-send.
63
64qmail-queue starts a 24-hour timer before touching any files, and
65commits suicide if the timer expires.
66
67
684. How queued messages are preprocessed
69
70Once a message has been queued, qmail-send must decide which recipients
71are local and which recipients are remote. It may also rewrite some
72recipient addresses.
73
74When qmail-send notices todo/457, it knows that message 457 is in S4. It
75removes info/457, local/457, and remote/457 if they exist. Then it reads
76through todo/457. It creates info/457, possibly local/457, and possibly
77remote/457. When it is done, it removes intd/457. The message is still
78in S4 at this point. Finally qmail-send removes todo/457, moving to S5.
79At that instant the message has been successfully preprocessed.
80
81
825. How preprocessed messages are delivered
83
84Messages at S5 are handled as follows. Each address in local/457 and
85remote/457 is marked either NOT DONE or DONE.
86
87 DONE: The message was successfully delivered, or the last delivery
88 attempt met with permanent failure. Either way, qmail-send
89 should not attempt further delivery to this address.
90
91 NOT DONE: If there have been any delivery attempts, they have all
92 met with temporary failure. Either way, qmail-send should
93 try delivery in the future.
94
95qmail-send may at its leisure try to deliver a message to a NOT DONE
96address. If the message is successfully delivered, qmail-send marks the
97address as DONE. If the delivery attempt meets with permanent failure,
98qmail-send first appends a note to bounce/457, creating bounce/457 if
99necessary; then it marks the address as DONE. Note that bounce/457 is
100not crashproof.
101
102qmail-send may handle bounce/457 at any time, as follows: it (1) injects
103a new bounce message, created from bounce/457 and mess/457; (2) deletes
104bounce/457.
105
106When all addresses in local/457 are DONE, qmail-send deletes local/457.
107Same for remote/457.
108
109When local/457 and remote/457 are gone, qmail-send eliminates the
110message, as follows. First, if bounce/457 exists, qmail-send handles it
111as described above. Once bounce/457 is definitely gone, qmail-send
112deletes info/457, moving to S2, and finally mess/457, moving to S1.
113
114
1156. Cleanups
116
117If the computer crashes while qmail-queue is trying to queue a message,
118or while qmail-send is eliminating a message, the message may be left in
119state S2 or S3.
120
121When qmail-send sees a message in state S2 or S3---other than one
122it is currently eliminating!---where mess/457 is more than 36 hours old,
123it deletes intd/457 if that exists, then deletes mess/457. Note that any
124qmail-queue handling the message must be dead.
125
126Similarly, when qmail-send sees a file in the pid/ directory that is
127more than 36 hours old, it deletes it.
128
129Cleanups are not necessary if the computer crashes while qmail-send is
130delivering a message. At worst a message may be delivered twice. (There
131is no way for a distributed mail system to eliminate the possibility of
132duplication. What if an SMTP connection is broken just before the server
133acknowledges successful receipt of the message? The client must assume
134the worst and send the message again. Similarly, if the computer crashes
135just before qmail-send marks a message as DONE, the new qmail-send must
136assume the worst and send the message again. The usual solutions in the
137database literature---e.g., keeping log files---amount to saying that
138it's the recipient's computer's job to discard duplicate messages.)
139
140
1417. Further notes
142
143Currently info/457 serves two purposes: first, it records the envelope
144sender; second, its modification time is used to decide when a message
145has been in the queue too long. In the future info/457 may store more
146information. Any non-backwards-compatible changes will be identified by
147version numbers.
148
149When qmail-queue has successfully placed a message into the queue, it
150pulls a trigger offered by qmail-send. Here is the current triggering
151mechanism: lock/trigger is a named pipe. Before scanning todo/,
152qmail-send opens lock/trigger O_NDELAY for reading. It then selects for
153readability on lock/trigger. qmail-queue pulls the trigger by writing a
154byte O_NDELAY to lock/trigger. This makes lock/trigger readable and
155wakes up qmail-send. Before scanning todo/ again, qmail-send closes and
156reopens lock/trigger.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9230887
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2141 @@
1# Don't edit Makefile! Use conf-* for configuration.
2
3SHELL=/bin/sh
4
5default: it
6
7addresses.0: \
8addresses.5
9 nroff -man addresses.5 > addresses.0
10
11alloc.a: \
12makelib alloc.o alloc_re.o
13 ./makelib alloc.a alloc.o alloc_re.o
14
15alloc.o: \
16compile alloc.c alloc.h error.h
17 ./compile alloc.c
18
19alloc_re.o: \
20compile alloc_re.c alloc.h byte.h
21 ./compile alloc_re.c
22
23auto-ccld.sh: \
24conf-cc conf-ld warn-auto.sh
25 ( cat warn-auto.sh; \
26 echo CC=\'`head -1 conf-cc`\'; \
27 echo LD=\'`head -1 conf-ld`\' \
28 ) > auto-ccld.sh
29
30auto-gid: \
31load auto-gid.o substdio.a error.a str.a fs.a
32 ./load auto-gid substdio.a error.a str.a fs.a
33
34auto-gid.o: \
35compile auto-gid.c subfd.h substdio.h substdio.h readwrite.h exit.h \
36scan.h fmt.h
37 ./compile auto-gid.c
38
39auto-int: \
40load auto-int.o substdio.a error.a str.a fs.a
41 ./load auto-int substdio.a error.a str.a fs.a
42
43auto-int.o: \
44compile auto-int.c substdio.h readwrite.h exit.h scan.h fmt.h
45 ./compile auto-int.c
46
47auto-int8: \
48load auto-int8.o substdio.a error.a str.a fs.a
49 ./load auto-int8 substdio.a error.a str.a fs.a
50
51auto-int8.o: \
52compile auto-int8.c substdio.h readwrite.h exit.h scan.h fmt.h
53 ./compile auto-int8.c
54
55auto-str: \
56load auto-str.o substdio.a error.a str.a
57 ./load auto-str substdio.a error.a str.a
58
59auto-str.o: \
60compile auto-str.c substdio.h readwrite.h exit.h
61 ./compile auto-str.c
62
63auto-uid: \
64load auto-uid.o substdio.a error.a str.a fs.a
65 ./load auto-uid substdio.a error.a str.a fs.a
66
67auto-uid.o: \
68compile auto-uid.c subfd.h substdio.h substdio.h readwrite.h exit.h \
69scan.h fmt.h
70 ./compile auto-uid.c
71
72auto_break.c: \
73auto-str conf-break
74 ./auto-str auto_break \
75 "`head -1 conf-break`" > auto_break.c
76
77auto_break.o: \
78compile auto_break.c
79 ./compile auto_break.c
80
81auto_patrn.c: \
82auto-int8 conf-patrn
83 ./auto-int8 auto_patrn `head -1 conf-patrn` > auto_patrn.c
84
85auto_patrn.o: \
86compile auto_patrn.c
87 ./compile auto_patrn.c
88
89auto_qmail.c: \
90auto-str conf-qmail
91 ./auto-str auto_qmail `head -1 conf-qmail` > auto_qmail.c
92
93auto_qmail.o: \
94compile auto_qmail.c
95 ./compile auto_qmail.c
96
97auto_spawn.c: \
98auto-int conf-spawn
99 ./auto-int auto_spawn `head -1 conf-spawn` > auto_spawn.c
100
101auto_spawn.o: \
102compile auto_spawn.c
103 ./compile auto_spawn.c
104
105auto_split.c: \
106auto-int conf-split
107 ./auto-int auto_split `head -1 conf-split` > auto_split.c
108
109auto_split.o: \
110compile auto_split.c
111 ./compile auto_split.c
112
113auto_uids.c: \
114auto-uid auto-gid conf-users conf-groups
115 ( ./auto-uid auto_uida `head -1 conf-users` \
116 &&./auto-uid auto_uidd `head -2 conf-users | tail -1` \
117 &&./auto-uid auto_uidl `head -3 conf-users | tail -1` \
118 &&./auto-uid auto_uido `head -4 conf-users | tail -1` \
119 &&./auto-uid auto_uidp `head -5 conf-users | tail -1` \
120 &&./auto-uid auto_uidq `head -6 conf-users | tail -1` \
121 &&./auto-uid auto_uidr `head -7 conf-users | tail -1` \
122 &&./auto-uid auto_uids `head -8 conf-users | tail -1` \
123 &&./auto-gid auto_gidq `head -1 conf-groups` \
124 &&./auto-gid auto_gidn `head -2 conf-groups | tail -1` \
125 ) > auto_uids.c.tmp && mv auto_uids.c.tmp auto_uids.c
126
127auto_uids.o: \
128compile auto_uids.c
129 ./compile auto_uids.c
130
131auto_usera.c: \
132auto-str conf-users
133 ./auto-str auto_usera `head -1 conf-users` > auto_usera.c
134
135auto_usera.o: \
136compile auto_usera.c
137 ./compile auto_usera.c
138
139binm1: \
140binm1.sh conf-qmail
141 cat binm1.sh \
142 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
143 > binm1
144 chmod 755 binm1
145
146binm1+df: \
147binm1+df.sh conf-qmail
148 cat binm1+df.sh \
149 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
150 > binm1+df
151 chmod 755 binm1+df
152
153binm2: \
154binm2.sh conf-qmail
155 cat binm2.sh \
156 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
157 > binm2
158 chmod 755 binm2
159
160binm2+df: \
161binm2+df.sh conf-qmail
162 cat binm2+df.sh \
163 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
164 > binm2+df
165 chmod 755 binm2+df
166
167binm3: \
168binm3.sh conf-qmail
169 cat binm3.sh \
170 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
171 > binm3
172 chmod 755 binm3
173
174binm3+df: \
175binm3+df.sh conf-qmail
176 cat binm3+df.sh \
177 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
178 > binm3+df
179 chmod 755 binm3+df
180
181bouncesaying: \
182load bouncesaying.o strerr.a error.a substdio.a str.a wait.a
183 ./load bouncesaying strerr.a error.a substdio.a str.a \
184 wait.a
185
186bouncesaying.0: \
187bouncesaying.1
188 nroff -man bouncesaying.1 > bouncesaying.0
189
190bouncesaying.o: \
191compile bouncesaying.c fork.h strerr.h error.h wait.h sig.h exit.h
192 ./compile bouncesaying.c
193
194byte_chr.o: \
195compile byte_chr.c byte.h
196 ./compile byte_chr.c
197
198byte_copy.o: \
199compile byte_copy.c byte.h
200 ./compile byte_copy.c
201
202byte_cr.o: \
203compile byte_cr.c byte.h
204 ./compile byte_cr.c
205
206byte_diff.o: \
207compile byte_diff.c byte.h
208 ./compile byte_diff.c
209
210byte_rchr.o: \
211compile byte_rchr.c byte.h
212 ./compile byte_rchr.c
213
214byte_zero.o: \
215compile byte_zero.c byte.h
216 ./compile byte_zero.c
217
218case.a: \
219makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \
220case_starts.o
221 ./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \
222 case_lowers.o case_starts.o
223
224case_diffb.o: \
225compile case_diffb.c case.h
226 ./compile case_diffb.c
227
228case_diffs.o: \
229compile case_diffs.c case.h
230 ./compile case_diffs.c
231
232case_lowerb.o: \
233compile case_lowerb.c case.h
234 ./compile case_lowerb.c
235
236case_lowers.o: \
237compile case_lowers.c case.h
238 ./compile case_lowers.c
239
240case_starts.o: \
241compile case_starts.c case.h
242 ./compile case_starts.c
243
244cdb.a: \
245makelib cdb_hash.o cdb_unpack.o cdb_seek.o
246 ./makelib cdb.a cdb_hash.o cdb_unpack.o cdb_seek.o
247
248cdb_hash.o: \
249compile cdb_hash.c cdb.h uint32.h
250 ./compile cdb_hash.c
251
252cdb_seek.o: \
253compile cdb_seek.c cdb.h uint32.h
254 ./compile cdb_seek.c
255
256cdb_unpack.o: \
257compile cdb_unpack.c cdb.h uint32.h
258 ./compile cdb_unpack.c
259
260cdbmake.a: \
261makelib cdbmake_pack.o cdbmake_hash.o cdbmake_add.o
262 ./makelib cdbmake.a cdbmake_pack.o cdbmake_hash.o \
263 cdbmake_add.o
264
265cdbmake_add.o: \
266compile cdbmake_add.c cdbmake.h uint32.h
267 ./compile cdbmake_add.c
268
269cdbmake_hash.o: \
270compile cdbmake_hash.c cdbmake.h uint32.h
271 ./compile cdbmake_hash.c
272
273cdbmake_pack.o: \
274compile cdbmake_pack.c cdbmake.h uint32.h
275 ./compile cdbmake_pack.c
276
277cdbmss.o: \
278compile cdbmss.c readwrite.h seek.h alloc.h cdbmss.h cdbmake.h \
279uint32.h substdio.h
280 ./compile cdbmss.c
281
282check: \
283it man
284 ./instcheck
285
286chkshsgr: \
287load chkshsgr.o
288 ./load chkshsgr
289
290chkshsgr.o: \
291compile chkshsgr.c exit.h
292 ./compile chkshsgr.c
293
294chkspawn: \
295load chkspawn.o substdio.a error.a str.a fs.a auto_spawn.o
296 ./load chkspawn substdio.a error.a str.a fs.a auto_spawn.o
297
298chkspawn.o: \
299compile chkspawn.c substdio.h subfd.h substdio.h fmt.h select.h \
300exit.h auto_spawn.h
301 ./compile chkspawn.c
302
303clean: \
304TARGETS
305 rm -f `cat TARGETS`
306
307coe.o: \
308compile coe.c coe.h
309 ./compile coe.c
310
311commands.o: \
312compile commands.c commands.h substdio.h stralloc.h gen_alloc.h str.h \
313case.h
314 ./compile commands.c
315
316compile: \
317make-compile warn-auto.sh systype
318 ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \
319 compile
320 chmod 755 compile
321
322condredirect: \
323load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \
324substdio.a error.a str.a fs.a auto_qmail.o
325 ./load condredirect qmail.o strerr.a fd.a sig.a wait.a \
326 seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o
327
328condredirect.0: \
329condredirect.1
330 nroff -man condredirect.1 > condredirect.0
331
332condredirect.o: \
333compile condredirect.c sig.h readwrite.h exit.h env.h error.h fork.h \
334wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h
335 ./compile condredirect.c
336
337config: \
338warn-auto.sh config.sh conf-qmail conf-break conf-split
339 cat warn-auto.sh config.sh \
340 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
341 | sed s}BREAK}"`head -1 conf-break`"}g \
342 | sed s}SPLIT}"`head -1 conf-split`"}g \
343 > config
344 chmod 755 config
345
346config-fast: \
347warn-auto.sh config-fast.sh conf-qmail conf-break conf-split
348 cat warn-auto.sh config-fast.sh \
349 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
350 | sed s}BREAK}"`head -1 conf-break`"}g \
351 | sed s}SPLIT}"`head -1 conf-split`"}g \
352 > config-fast
353 chmod 755 config-fast
354
355constmap.o: \
356compile constmap.c constmap.h alloc.h case.h
357 ./compile constmap.c
358
359control.o: \
360compile control.c readwrite.h open.h getln.h stralloc.h gen_alloc.h \
361substdio.h error.h control.h alloc.h scan.h
362 ./compile control.c
363
364date822fmt.o: \
365compile date822fmt.c datetime.h fmt.h date822fmt.h
366 ./compile date822fmt.c
367
368datemail: \
369warn-auto.sh datemail.sh conf-qmail conf-break conf-split
370 cat warn-auto.sh datemail.sh \
371 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
372 | sed s}BREAK}"`head -1 conf-break`"}g \
373 | sed s}SPLIT}"`head -1 conf-split`"}g \
374 > datemail
375 chmod 755 datemail
376
377datetime.a: \
378makelib datetime.o datetime_un.o
379 ./makelib datetime.a datetime.o datetime_un.o
380
381datetime.o: \
382compile datetime.c datetime.h
383 ./compile datetime.c
384
385datetime_un.o: \
386compile datetime_un.c datetime.h
387 ./compile datetime_un.c
388
389direntry.h: \
390compile trydrent.c direntry.h1 direntry.h2
391 ( ./compile trydrent.c >/dev/null 2>&1 \
392 && cat direntry.h2 || cat direntry.h1 ) > direntry.h
393 rm -f trydrent.o
394
395dns.lib: \
396tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \
397alloc.a error.a fs.a str.a
398 ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \
399 ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
400 -lresolv `cat socket.lib` ) >/dev/null 2>&1 \
401 && echo -lresolv || exit 0 ) > dns.lib
402 rm -f tryrsolv.o tryrsolv
403
404dns.o: \
405compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \
406stralloc.h gen_alloc.h dns.h case.h
407 ./compile dns.c
408
409dnscname: \
410load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
411substdio.a error.a str.a fs.a dns.lib socket.lib
412 ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
413 alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
414 socket.lib`
415
416dnscname.o: \
417compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \
418gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h
419 ./compile dnscname.c
420
421dnsdoe.o: \
422compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h
423 ./compile dnsdoe.c
424
425dnsfq: \
426load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
427substdio.a error.a str.a fs.a dns.lib socket.lib
428 ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
429 alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
430 socket.lib`
431
432dnsfq.o: \
433compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
434dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
435 ./compile dnsfq.c
436
437dnsip: \
438load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
439substdio.a error.a str.a fs.a dns.lib socket.lib
440 ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
441 alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
442 socket.lib`
443
444dnsip.o: \
445compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
446dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
447 ./compile dnsip.c
448
449dnsmxip: \
450load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \
451substdio.a error.a str.a fs.a dns.lib socket.lib
452 ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \
453 stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \
454 dns.lib` `cat socket.lib`
455
456dnsmxip.o: \
457compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \
458gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \
459now.h datetime.h exit.h
460 ./compile dnsmxip.c
461
462dnsptr: \
463load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
464substdio.a error.a str.a fs.a dns.lib socket.lib
465 ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
466 alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
467 socket.lib`
468
469dnsptr.o: \
470compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
471str.h scan.h dns.h dnsdoe.h ip.h exit.h
472 ./compile dnsptr.c
473
474dot-qmail.0: \
475dot-qmail.5
476 nroff -man dot-qmail.5 > dot-qmail.0
477
478dot-qmail.5: \
479dot-qmail.9 conf-break conf-spawn
480 cat dot-qmail.9 \
481 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
482 | sed s}BREAK}"`head -1 conf-break`"}g \
483 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
484 > dot-qmail.5
485
486elq: \
487warn-auto.sh elq.sh conf-qmail conf-break conf-split
488 cat warn-auto.sh elq.sh \
489 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
490 | sed s}BREAK}"`head -1 conf-break`"}g \
491 | sed s}SPLIT}"`head -1 conf-split`"}g \
492 > elq
493 chmod 755 elq
494
495env.a: \
496makelib env.o envread.o
497 ./makelib env.a env.o envread.o
498
499env.o: \
500compile env.c str.h alloc.h env.h
501 ./compile env.c
502
503envelopes.0: \
504envelopes.5
505 nroff -man envelopes.5 > envelopes.0
506
507envread.o: \
508compile envread.c env.h str.h
509 ./compile envread.c
510
511error.a: \
512makelib error.o error_str.o error_temp.o
513 ./makelib error.a error.o error_str.o error_temp.o
514
515error.o: \
516compile error.c error.h
517 ./compile error.c
518
519error_str.o: \
520compile error_str.c error.h
521 ./compile error_str.c
522
523error_temp.o: \
524compile error_temp.c error.h
525 ./compile error_temp.c
526
527except: \
528load except.o strerr.a error.a substdio.a str.a wait.a
529 ./load except strerr.a error.a substdio.a str.a wait.a
530
531except.0: \
532except.1
533 nroff -man except.1 > except.0
534
535except.o: \
536compile except.c fork.h strerr.h wait.h error.h exit.h
537 ./compile except.c
538
539fd.a: \
540makelib fd_copy.o fd_move.o
541 ./makelib fd.a fd_copy.o fd_move.o
542
543fd_copy.o: \
544compile fd_copy.c fd.h
545 ./compile fd_copy.c
546
547fd_move.o: \
548compile fd_move.c fd.h
549 ./compile fd_move.c
550
551fifo.o: \
552compile fifo.c hasmkffo.h fifo.h
553 ./compile fifo.c
554
555find-systype: \
556find-systype.sh auto-ccld.sh
557 cat auto-ccld.sh find-systype.sh > find-systype
558 chmod 755 find-systype
559
560fmt_str.o: \
561compile fmt_str.c fmt.h
562 ./compile fmt_str.c
563
564fmt_strn.o: \
565compile fmt_strn.c fmt.h
566 ./compile fmt_strn.c
567
568fmt_uint.o: \
569compile fmt_uint.c fmt.h
570 ./compile fmt_uint.c
571
572fmt_uint0.o: \
573compile fmt_uint0.c fmt.h
574 ./compile fmt_uint0.c
575
576fmt_ulong.o: \
577compile fmt_ulong.c fmt.h
578 ./compile fmt_ulong.c
579
580fmtqfn.o: \
581compile fmtqfn.c fmtqfn.h fmt.h auto_split.h
582 ./compile fmtqfn.c
583
584forgeries.0: \
585forgeries.7
586 nroff -man forgeries.7 > forgeries.0
587
588fork.h: \
589compile load tryvfork.c fork.h1 fork.h2
590 ( ( ./compile tryvfork.c && ./load tryvfork ) >/dev/null \
591 2>&1 \
592 && cat fork.h2 || cat fork.h1 ) > fork.h
593 rm -f tryvfork.o tryvfork
594
595forward: \
596load forward.o qmail.o strerr.a alloc.a fd.a wait.a sig.a env.a \
597substdio.a error.a str.a fs.a auto_qmail.o
598 ./load forward qmail.o strerr.a alloc.a fd.a wait.a sig.a \
599 env.a substdio.a error.a str.a fs.a auto_qmail.o
600
601forward.0: \
602forward.1
603 nroff -man forward.1 > forward.0
604
605forward.o: \
606compile forward.c sig.h readwrite.h exit.h env.h qmail.h substdio.h \
607strerr.h substdio.h fmt.h
608 ./compile forward.c
609
610fs.a: \
611makelib fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o fmt_ulong.o \
612scan_ulong.o scan_8long.o
613 ./makelib fs.a fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o \
614 fmt_ulong.o scan_ulong.o scan_8long.o
615
616getln.a: \
617makelib getln.o getln2.o
618 ./makelib getln.a getln.o getln2.o
619
620getln.o: \
621compile getln.c substdio.h byte.h stralloc.h gen_alloc.h getln.h
622 ./compile getln.c
623
624getln2.o: \
625compile getln2.c substdio.h stralloc.h gen_alloc.h byte.h getln.h
626 ./compile getln2.c
627
628getopt.a: \
629makelib subgetopt.o sgetopt.o
630 ./makelib getopt.a subgetopt.o sgetopt.o
631
632gfrom.o: \
633compile gfrom.c str.h gfrom.h
634 ./compile gfrom.c
635
636hasflock.h: \
637tryflock.c compile load
638 ( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \
639 2>&1 \
640 && echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h
641 rm -f tryflock.o tryflock
642
643hasmkffo.h: \
644trymkffo.c compile load
645 ( ( ./compile trymkffo.c && ./load trymkffo ) >/dev/null \
646 2>&1 \
647 && echo \#define HASMKFIFO 1 || exit 0 ) > hasmkffo.h
648 rm -f trymkffo.o trymkffo
649
650hasnpbg1.h: \
651trynpbg1.c compile load open.h open.a fifo.h fifo.o select.h
652 ( ( ./compile trynpbg1.c \
653 && ./load trynpbg1 fifo.o open.a && ./trynpbg1 ) \
654 >/dev/null 2>&1 \
655 && echo \#define HASNAMEDPIPEBUG1 1 || exit 0 ) > \
656 hasnpbg1.h
657 rm -f trynpbg1.o trynpbg1
658
659hassalen.h: \
660trysalen.c compile
661 ( ./compile trysalen.c >/dev/null 2>&1 \
662 && echo \#define HASSALEN 1 || exit 0 ) > hassalen.h
663 rm -f trysalen.o
664
665hassgact.h: \
666trysgact.c compile load
667 ( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \
668 2>&1 \
669 && echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h
670 rm -f trysgact.o trysgact
671
672hassgprm.h: \
673trysgprm.c compile load
674 ( ( ./compile trysgprm.c && ./load trysgprm ) >/dev/null \
675 2>&1 \
676 && echo \#define HASSIGPROCMASK 1 || exit 0 ) > hassgprm.h
677 rm -f trysgprm.o trysgprm
678
679hasshsgr.h: \
680chkshsgr warn-shsgr tryshsgr.c compile load
681 ./chkshsgr || ( cat warn-shsgr; exit 1 )
682 ( ( ./compile tryshsgr.c \
683 && ./load tryshsgr && ./tryshsgr ) >/dev/null 2>&1 \
684 && echo \#define HASSHORTSETGROUPS 1 || exit 0 ) > \
685 hasshsgr.h
686 rm -f tryshsgr.o tryshsgr
687
688haswaitp.h: \
689trywaitp.c compile load
690 ( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \
691 2>&1 \
692 && echo \#define HASWAITPID 1 || exit 0 ) > haswaitp.h
693 rm -f trywaitp.o trywaitp
694
695headerbody.o: \
696compile headerbody.c stralloc.h gen_alloc.h substdio.h getln.h \
697hfield.h headerbody.h
698 ./compile headerbody.c
699
700hfield.o: \
701compile hfield.c hfield.h
702 ./compile hfield.c
703
704hier.o: \
705compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
706 ./compile hier.c
707
708home: \
709home.sh conf-qmail
710 cat home.sh \
711 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
712 > home
713 chmod 755 home
714
715home+df: \
716home+df.sh conf-qmail
717 cat home+df.sh \
718 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
719 > home+df
720 chmod 755 home+df
721
722hostname: \
723load hostname.o substdio.a error.a str.a dns.lib socket.lib
724 ./load hostname substdio.a error.a str.a `cat dns.lib` \
725 `cat socket.lib`
726
727hostname.o: \
728compile hostname.c substdio.h subfd.h substdio.h readwrite.h exit.h
729 ./compile hostname.c
730
731idedit: \
732load idedit.o strerr.a substdio.a error.a str.a fs.a wait.a open.a \
733seek.a
734 ./load idedit strerr.a substdio.a error.a str.a fs.a \
735 wait.a open.a seek.a
736
737idedit.o: \
738compile idedit.c readwrite.h exit.h scan.h fmt.h strerr.h open.h \
739seek.h fork.h
740 ./compile idedit.c
741
742install: \
743load install.o fifo.o hier.o auto_qmail.o auto_split.o auto_uids.o \
744strerr.a substdio.a open.a error.a str.a fs.a
745 ./load install fifo.o hier.o auto_qmail.o auto_split.o \
746 auto_uids.o strerr.a substdio.a open.a error.a str.a fs.a
747
748install-big: \
749load install-big.o fifo.o install.o auto_qmail.o auto_split.o \
750auto_uids.o strerr.a substdio.a open.a error.a str.a fs.a
751 ./load install-big fifo.o install.o auto_qmail.o \
752 auto_split.o auto_uids.o strerr.a substdio.a open.a error.a \
753 str.a fs.a
754
755install-big.o: \
756compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \
757fifo.h
758 ./compile install-big.c
759
760install.o: \
761compile install.c substdio.h strerr.h error.h open.h readwrite.h \
762exit.h
763 ./compile install.c
764
765instcheck: \
766load instcheck.o fifo.o hier.o auto_qmail.o auto_split.o auto_uids.o \
767strerr.a substdio.a error.a str.a fs.a
768 ./load instcheck fifo.o hier.o auto_qmail.o auto_split.o \
769 auto_uids.o strerr.a substdio.a error.a str.a fs.a
770
771instcheck.o: \
772compile instcheck.c strerr.h error.h readwrite.h exit.h
773 ./compile instcheck.c
774
775ip.o: \
776compile ip.c fmt.h scan.h ip.h
777 ./compile ip.c
778
779ipalloc.o: \
780compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \
781gen_alloc.h
782 ./compile ipalloc.c
783
784ipme.o: \
785compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \
786stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h
787 ./compile ipme.c
788
789ipmeprint: \
790load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \
791error.a str.a fs.a socket.lib
792 ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \
793 substdio.a error.a str.a fs.a `cat socket.lib`
794
795ipmeprint.o: \
796compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
797ipalloc.h ip.h gen_alloc.h exit.h
798 ./compile ipmeprint.c
799
800it: \
801qmail-local qmail-lspawn qmail-getpw qmail-remote qmail-rspawn \
802qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \
803predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
804qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
805qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
806qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
807dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
808forward preline condredirect bouncesaying except maildirmake \
809maildir2mbox maildirwatch qail elq pinq idedit install-big install \
810instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
811binm3 binm3+df
812
813load: \
814make-load warn-auto.sh systype
815 ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load
816 chmod 755 load
817
818lock.a: \
819makelib lock_ex.o lock_exnb.o lock_un.o
820 ./makelib lock.a lock_ex.o lock_exnb.o lock_un.o
821
822lock_ex.o: \
823compile lock_ex.c hasflock.h lock.h
824 ./compile lock_ex.c
825
826lock_exnb.o: \
827compile lock_exnb.c hasflock.h lock.h
828 ./compile lock_exnb.c
829
830lock_un.o: \
831compile lock_un.c hasflock.h lock.h
832 ./compile lock_un.c
833
834maildir.0: \
835maildir.5
836 nroff -man maildir.5 > maildir.0
837
838maildir.o: \
839compile maildir.c prioq.h datetime.h gen_alloc.h env.h stralloc.h \
840gen_alloc.h direntry.h datetime.h now.h datetime.h str.h maildir.h \
841strerr.h
842 ./compile maildir.c
843
844maildir2mbox: \
845load maildir2mbox.o maildir.o prioq.o now.o myctime.o gfrom.o lock.a \
846getln.a env.a open.a strerr.a stralloc.a alloc.a substdio.a error.a \
847str.a fs.a datetime.a
848 ./load maildir2mbox maildir.o prioq.o now.o myctime.o \
849 gfrom.o lock.a getln.a env.a open.a strerr.a stralloc.a \
850 alloc.a substdio.a error.a str.a fs.a datetime.a
851
852maildir2mbox.0: \
853maildir2mbox.1
854 nroff -man maildir2mbox.1 > maildir2mbox.0
855
856maildir2mbox.o: \
857compile maildir2mbox.c readwrite.h prioq.h datetime.h gen_alloc.h \
858env.h stralloc.h gen_alloc.h subfd.h substdio.h substdio.h getln.h \
859error.h open.h lock.h gfrom.h str.h exit.h myctime.h maildir.h \
860strerr.h
861 ./compile maildir2mbox.c
862
863maildirmake: \
864load maildirmake.o strerr.a substdio.a error.a str.a
865 ./load maildirmake strerr.a substdio.a error.a str.a
866
867maildirmake.0: \
868maildirmake.1
869 nroff -man maildirmake.1 > maildirmake.0
870
871maildirmake.o: \
872compile maildirmake.c strerr.h exit.h
873 ./compile maildirmake.c
874
875maildirwatch: \
876load maildirwatch.o hfield.o headerbody.o maildir.o prioq.o now.o \
877getln.a env.a open.a strerr.a stralloc.a alloc.a substdio.a error.a \
878str.a
879 ./load maildirwatch hfield.o headerbody.o maildir.o \
880 prioq.o now.o getln.a env.a open.a strerr.a stralloc.a \
881 alloc.a substdio.a error.a str.a
882
883maildirwatch.0: \
884maildirwatch.1
885 nroff -man maildirwatch.1 > maildirwatch.0
886
887maildirwatch.o: \
888compile maildirwatch.c getln.h substdio.h subfd.h substdio.h prioq.h \
889datetime.h gen_alloc.h stralloc.h gen_alloc.h str.h exit.h hfield.h \
890readwrite.h open.h headerbody.h maildir.h strerr.h
891 ./compile maildirwatch.c
892
893mailsubj: \
894warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split
895 cat warn-auto.sh mailsubj.sh \
896 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
897 | sed s}BREAK}"`head -1 conf-break`"}g \
898 | sed s}SPLIT}"`head -1 conf-split`"}g \
899 > mailsubj
900 chmod 755 mailsubj
901
902mailsubj.0: \
903mailsubj.1
904 nroff -man mailsubj.1 > mailsubj.0
905
906make-compile: \
907make-compile.sh auto-ccld.sh
908 cat auto-ccld.sh make-compile.sh > make-compile
909 chmod 755 make-compile
910
911make-load: \
912make-load.sh auto-ccld.sh
913 cat auto-ccld.sh make-load.sh > make-load
914 chmod 755 make-load
915
916make-makelib: \
917make-makelib.sh auto-ccld.sh
918 cat auto-ccld.sh make-makelib.sh > make-makelib
919 chmod 755 make-makelib
920
921makelib: \
922make-makelib warn-auto.sh systype
923 ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \
924 makelib
925 chmod 755 makelib
926
927man: \
928qmail-local.0 qmail-lspawn.0 qmail-getpw.0 qmail-remote.0 \
929qmail-rspawn.0 qmail-clean.0 qmail-send.0 qmail-start.0 splogger.0 \
930qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 \
931qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \
932qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \
933qmail-smtpd.0 tcp-env.0 qmail-newmrh.0 qreceipt.0 qbiff.0 forward.0 \
934preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \
935maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
936qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
937qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
938envelopes.0 forgeries.0
939
940mbox.0: \
941mbox.5
942 nroff -man mbox.5 > mbox.0
943
944myctime.o: \
945compile myctime.c datetime.h fmt.h myctime.h
946 ./compile myctime.c
947
948ndelay.a: \
949makelib ndelay.o ndelay_off.o
950 ./makelib ndelay.a ndelay.o ndelay_off.o
951
952ndelay.o: \
953compile ndelay.c ndelay.h
954 ./compile ndelay.c
955
956ndelay_off.o: \
957compile ndelay_off.c ndelay.h
958 ./compile ndelay_off.c
959
960newfield.o: \
961compile newfield.c fmt.h datetime.h stralloc.h gen_alloc.h \
962date822fmt.h newfield.h stralloc.h
963 ./compile newfield.c
964
965now.o: \
966compile now.c datetime.h now.h datetime.h
967 ./compile now.c
968
969open.a: \
970makelib open_append.o open_excl.o open_read.o open_trunc.o \
971open_write.o
972 ./makelib open.a open_append.o open_excl.o open_read.o \
973 open_trunc.o open_write.o
974
975open_append.o: \
976compile open_append.c open.h
977 ./compile open_append.c
978
979open_excl.o: \
980compile open_excl.c open.h
981 ./compile open_excl.c
982
983open_read.o: \
984compile open_read.c open.h
985 ./compile open_read.c
986
987open_trunc.o: \
988compile open_trunc.c open.h
989 ./compile open_trunc.c
990
991open_write.o: \
992compile open_write.c open.h
993 ./compile open_write.c
994
995pinq: \
996warn-auto.sh pinq.sh conf-qmail conf-break conf-split
997 cat warn-auto.sh pinq.sh \
998 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
999 | sed s}BREAK}"`head -1 conf-break`"}g \
1000 | sed s}SPLIT}"`head -1 conf-split`"}g \
1001 > pinq
1002 chmod 755 pinq
1003
1004predate: \
1005load predate.o datetime.a strerr.a sig.a fd.a wait.a substdio.a \
1006error.a str.a fs.a
1007 ./load predate datetime.a strerr.a sig.a fd.a wait.a \
1008 substdio.a error.a str.a fs.a
1009
1010predate.o: \
1011compile predate.c datetime.h fork.h wait.h fd.h fmt.h strerr.h \
1012substdio.h subfd.h substdio.h readwrite.h exit.h
1013 ./compile predate.c
1014
1015preline: \
1016load preline.o strerr.a fd.a wait.a sig.a env.a getopt.a substdio.a \
1017error.a str.a
1018 ./load preline strerr.a fd.a wait.a sig.a env.a getopt.a \
1019 substdio.a error.a str.a
1020
1021preline.0: \
1022preline.1
1023 nroff -man preline.1 > preline.0
1024
1025preline.o: \
1026compile preline.c fd.h sgetopt.h subgetopt.h readwrite.h strerr.h \
1027substdio.h exit.h fork.h wait.h env.h sig.h error.h
1028 ./compile preline.c
1029
1030prioq.o: \
1031compile prioq.c alloc.h gen_allocdefs.h prioq.h datetime.h \
1032gen_alloc.h
1033 ./compile prioq.c
1034
1035proc: \
1036proc.sh conf-qmail
1037 cat proc.sh \
1038 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
1039 > proc
1040 chmod 755 proc
1041
1042proc+df: \
1043proc+df.sh conf-qmail
1044 cat proc+df.sh \
1045 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
1046 > proc+df
1047 chmod 755 proc+df
1048
1049prot.o: \
1050compile prot.c hasshsgr.h prot.h
1051 ./compile prot.c
1052
1053qail: \
1054warn-auto.sh qail.sh conf-qmail conf-break conf-split
1055 cat warn-auto.sh qail.sh \
1056 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
1057 | sed s}BREAK}"`head -1 conf-break`"}g \
1058 | sed s}SPLIT}"`head -1 conf-split`"}g \
1059 > qail
1060 chmod 755 qail
1061
1062qbiff: \
1063load qbiff.o headerbody.o hfield.o getln.a env.a open.a stralloc.a \
1064alloc.a substdio.a error.a str.a
1065 ./load qbiff headerbody.o hfield.o getln.a env.a open.a \
1066 stralloc.a alloc.a substdio.a error.a str.a
1067
1068qbiff.0: \
1069qbiff.1
1070 nroff -man qbiff.1 > qbiff.0
1071
1072qbiff.o: \
1073compile qbiff.c readwrite.h stralloc.h gen_alloc.h substdio.h subfd.h \
1074substdio.h open.h byte.h str.h headerbody.h hfield.h env.h exit.h
1075 ./compile qbiff.c
1076
1077qmail-clean: \
1078load qmail-clean.o fmtqfn.o now.o getln.a sig.a stralloc.a alloc.a \
1079substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
1080 ./load qmail-clean fmtqfn.o now.o getln.a sig.a stralloc.a \
1081 alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
1082 auto_split.o
1083
1084qmail-clean.0: \
1085qmail-clean.8
1086 nroff -man qmail-clean.8 > qmail-clean.0
1087
1088qmail-clean.o: \
1089compile qmail-clean.c readwrite.h sig.h now.h datetime.h str.h \
1090direntry.h getln.h stralloc.h gen_alloc.h substdio.h subfd.h \
1091substdio.h byte.h scan.h fmt.h error.h exit.h fmtqfn.h auto_qmail.h
1092 ./compile qmail-clean.c
1093
1094qmail-command.0: \
1095qmail-command.8
1096 nroff -man qmail-command.8 > qmail-command.0
1097
1098qmail-control.0: \
1099qmail-control.5
1100 nroff -man qmail-control.5 > qmail-control.0
1101
1102qmail-control.5: \
1103qmail-control.9 conf-break conf-spawn
1104 cat qmail-control.9 \
1105 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1106 | sed s}BREAK}"`head -1 conf-break`"}g \
1107 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1108 > qmail-control.5
1109
1110qmail-getpw: \
1111load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \
1112auto_usera.o
1113 ./load qmail-getpw case.a substdio.a error.a str.a fs.a \
1114 auto_break.o auto_usera.o
1115
1116qmail-getpw.0: \
1117qmail-getpw.8
1118 nroff -man qmail-getpw.8 > qmail-getpw.0
1119
1120qmail-getpw.8: \
1121qmail-getpw.9 conf-break conf-spawn
1122 cat qmail-getpw.9 \
1123 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1124 | sed s}BREAK}"`head -1 conf-break`"}g \
1125 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1126 > qmail-getpw.8
1127
1128qmail-getpw.o: \
1129compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \
1130error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \
1131qlx.h
1132 ./compile qmail-getpw.c
1133
1134qmail-header.0: \
1135qmail-header.5
1136 nroff -man qmail-header.5 > qmail-header.0
1137
1138qmail-inject: \
1139load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \
1140control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \
1141getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \
1142substdio.a error.a str.a fs.a auto_qmail.o
1143 ./load qmail-inject headerbody.o hfield.o newfield.o \
1144 quote.o now.o control.o date822fmt.o constmap.o qmail.o \
1145 case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \
1146 token822.o env.a stralloc.a alloc.a substdio.a error.a \
1147 str.a fs.a auto_qmail.o
1148
1149qmail-inject.0: \
1150qmail-inject.8
1151 nroff -man qmail-inject.8 > qmail-inject.0
1152
1153qmail-inject.o: \
1154compile qmail-inject.c sig.h substdio.h stralloc.h gen_alloc.h \
1155subfd.h substdio.h sgetopt.h subgetopt.h getln.h alloc.h str.h fmt.h \
1156hfield.h token822.h gen_alloc.h control.h env.h gen_alloc.h \
1157gen_allocdefs.h error.h qmail.h substdio.h now.h datetime.h exit.h \
1158quote.h headerbody.h auto_qmail.h newfield.h stralloc.h constmap.h
1159 ./compile qmail-inject.c
1160
1161qmail-limits.0: \
1162qmail-limits.7
1163 nroff -man qmail-limits.7 > qmail-limits.0
1164
1165qmail-limits.7: \
1166qmail-limits.9 conf-break conf-spawn
1167 cat qmail-limits.9 \
1168 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1169 | sed s}BREAK}"`head -1 conf-break`"}g \
1170 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1171 > qmail-limits.7
1172
1173qmail-local: \
1174load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \
1175slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \
1176wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
1177fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib
1178 ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \
1179 slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \
1180 lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \
1181 substdio.a error.a str.a fs.a datetime.a auto_qmail.o \
1182 auto_patrn.o `cat socket.lib`
1183
1184qmail-local.0: \
1185qmail-local.8
1186 nroff -man qmail-local.8 > qmail-local.0
1187
1188qmail-local.o: \
1189compile qmail-local.c readwrite.h sig.h env.h byte.h exit.h fork.h \
1190open.h wait.h lock.h seek.h substdio.h getln.h strerr.h subfd.h \
1191substdio.h sgetopt.h subgetopt.h alloc.h error.h stralloc.h \
1192gen_alloc.h fmt.h str.h now.h datetime.h case.h quote.h qmail.h \
1193substdio.h slurpclose.h myctime.h gfrom.h auto_patrn.h
1194 ./compile qmail-local.c
1195
1196qmail-log.0: \
1197qmail-log.5
1198 nroff -man qmail-log.5 > qmail-log.0
1199
1200qmail-lspawn: \
1201load qmail-lspawn.o spawn.o prot.o slurpclose.o coe.o sig.a wait.a \
1202case.a cdb.a fd.a open.a stralloc.a alloc.a substdio.a error.a str.a \
1203fs.a auto_qmail.o auto_uids.o auto_spawn.o
1204 ./load qmail-lspawn spawn.o prot.o slurpclose.o coe.o \
1205 sig.a wait.a case.a cdb.a fd.a open.a stralloc.a alloc.a \
1206 substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \
1207 auto_spawn.o
1208
1209qmail-lspawn.0: \
1210qmail-lspawn.8
1211 nroff -man qmail-lspawn.8 > qmail-lspawn.0
1212
1213qmail-lspawn.o: \
1214compile qmail-lspawn.c fd.h wait.h prot.h substdio.h stralloc.h \
1215gen_alloc.h scan.h exit.h fork.h error.h cdb.h uint32.h case.h \
1216slurpclose.h auto_qmail.h auto_uids.h qlx.h
1217 ./compile qmail-lspawn.c
1218
1219qmail-newmrh: \
1220load qmail-newmrh.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
1221stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
1222 ./load qmail-newmrh cdbmss.o getln.a open.a cdbmake.a \
1223 seek.a case.a stralloc.a alloc.a strerr.a substdio.a \
1224 error.a str.a auto_qmail.o
1225
1226qmail-newmrh.0: \
1227qmail-newmrh.8
1228 nroff -man qmail-newmrh.8 > qmail-newmrh.0
1229
1230qmail-newmrh.8: \
1231qmail-newmrh.9 conf-break conf-spawn
1232 cat qmail-newmrh.9 \
1233 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1234 | sed s}BREAK}"`head -1 conf-break`"}g \
1235 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1236 > qmail-newmrh.8
1237
1238qmail-newmrh.o: \
1239compile qmail-newmrh.c strerr.h stralloc.h gen_alloc.h substdio.h \
1240getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \
1241uint32.h substdio.h
1242 ./compile qmail-newmrh.c
1243
1244qmail-newu: \
1245load qmail-newu.o cdbmss.o getln.a open.a seek.a cdbmake.a case.a \
1246stralloc.a alloc.a substdio.a error.a str.a auto_qmail.o
1247 ./load qmail-newu cdbmss.o getln.a open.a seek.a cdbmake.a \
1248 case.a stralloc.a alloc.a substdio.a error.a str.a \
1249 auto_qmail.o
1250
1251qmail-newu.0: \
1252qmail-newu.8
1253 nroff -man qmail-newu.8 > qmail-newu.0
1254
1255qmail-newu.8: \
1256qmail-newu.9 conf-break conf-spawn
1257 cat qmail-newu.9 \
1258 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1259 | sed s}BREAK}"`head -1 conf-break`"}g \
1260 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1261 > qmail-newu.8
1262
1263qmail-newu.o: \
1264compile qmail-newu.c stralloc.h gen_alloc.h subfd.h substdio.h \
1265getln.h substdio.h cdbmss.h cdbmake.h uint32.h substdio.h exit.h \
1266readwrite.h open.h error.h case.h auto_qmail.h
1267 ./compile qmail-newu.c
1268
1269qmail-pop3d: \
1270load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \
1271maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \
1272stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
1273 ./load qmail-pop3d commands.o case.a timeoutread.o \
1274 timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \
1275 open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \
1276 fs.a `cat socket.lib`
1277
1278qmail-pop3d.0: \
1279qmail-pop3d.8
1280 nroff -man qmail-pop3d.8 > qmail-pop3d.0
1281
1282qmail-pop3d.o: \
1283compile qmail-pop3d.c commands.h sig.h getln.h stralloc.h gen_alloc.h \
1284substdio.h alloc.h open.h prioq.h datetime.h gen_alloc.h scan.h fmt.h \
1285str.h exit.h maildir.h strerr.h readwrite.h timeoutread.h \
1286timeoutwrite.h
1287 ./compile qmail-pop3d.c
1288
1289qmail-popup: \
1290load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \
1291case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \
1292fs.a socket.lib
1293 ./load qmail-popup commands.o timeoutread.o timeoutwrite.o \
1294 now.o case.a fd.a sig.a wait.a stralloc.a alloc.a \
1295 substdio.a error.a str.a fs.a `cat socket.lib`
1296
1297qmail-popup.0: \
1298qmail-popup.8
1299 nroff -man qmail-popup.8 > qmail-popup.0
1300
1301qmail-popup.o: \
1302compile qmail-popup.c commands.h fd.h sig.h stralloc.h gen_alloc.h \
1303substdio.h alloc.h wait.h str.h byte.h now.h datetime.h fmt.h exit.h \
1304readwrite.h timeoutread.h timeoutwrite.h
1305 ./compile qmail-popup.c
1306
1307qmail-pw2u: \
1308load qmail-pw2u.o constmap.o control.o open.a getln.a case.a getopt.a \
1309stralloc.a alloc.a substdio.a error.a str.a fs.a auto_usera.o \
1310auto_break.o auto_qmail.o
1311 ./load qmail-pw2u constmap.o control.o open.a getln.a \
1312 case.a getopt.a stralloc.a alloc.a substdio.a error.a str.a \
1313 fs.a auto_usera.o auto_break.o auto_qmail.o
1314
1315qmail-pw2u.0: \
1316qmail-pw2u.8
1317 nroff -man qmail-pw2u.8 > qmail-pw2u.0
1318
1319qmail-pw2u.8: \
1320qmail-pw2u.9 conf-break conf-spawn
1321 cat qmail-pw2u.9 \
1322 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1323 | sed s}BREAK}"`head -1 conf-break`"}g \
1324 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1325 > qmail-pw2u.8
1326
1327qmail-pw2u.o: \
1328compile qmail-pw2u.c substdio.h readwrite.h subfd.h substdio.h \
1329sgetopt.h subgetopt.h control.h constmap.h stralloc.h gen_alloc.h \
1330fmt.h str.h scan.h open.h error.h getln.h auto_break.h auto_qmail.h \
1331auto_usera.h
1332 ./compile qmail-pw2u.c
1333
1334qmail-qmqpc: \
1335load qmail-qmqpc.o slurpclose.o timeoutread.o timeoutwrite.o \
1336timeoutconn.o ip.o control.o auto_qmail.o sig.a ndelay.a open.a \
1337getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a socket.lib
1338 ./load qmail-qmqpc slurpclose.o timeoutread.o \
1339 timeoutwrite.o timeoutconn.o ip.o control.o auto_qmail.o \
1340 sig.a ndelay.a open.a getln.a substdio.a stralloc.a alloc.a \
1341 error.a str.a fs.a `cat socket.lib`
1342
1343qmail-qmqpc.0: \
1344qmail-qmqpc.8
1345 nroff -man qmail-qmqpc.8 > qmail-qmqpc.0
1346
1347qmail-qmqpc.o: \
1348compile qmail-qmqpc.c substdio.h getln.h readwrite.h exit.h \
1349stralloc.h gen_alloc.h slurpclose.h error.h sig.h ip.h timeoutconn.h \
1350timeoutread.h timeoutwrite.h auto_qmail.h control.h fmt.h
1351 ./compile qmail-qmqpc.c
1352
1353qmail-qmqpd: \
1354load qmail-qmqpd.o received.o now.o date822fmt.o qmail.o auto_qmail.o \
1355env.a substdio.a sig.a error.a wait.a fd.a str.a datetime.a fs.a
1356 ./load qmail-qmqpd received.o now.o date822fmt.o qmail.o \
1357 auto_qmail.o env.a substdio.a sig.a error.a wait.a fd.a \
1358 str.a datetime.a fs.a
1359
1360qmail-qmqpd.0: \
1361qmail-qmqpd.8
1362 nroff -man qmail-qmqpd.8 > qmail-qmqpd.0
1363
1364qmail-qmqpd.o: \
1365compile qmail-qmqpd.c auto_qmail.h qmail.h substdio.h received.h \
1366sig.h substdio.h readwrite.h exit.h now.h datetime.h fmt.h env.h
1367 ./compile qmail-qmqpd.c
1368
1369qmail-qmtpd: \
1370load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \
1371date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a open.a \
1372getln.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a \
1373str.a fs.a auto_qmail.o
1374 ./load qmail-qmtpd rcpthosts.o control.o constmap.o \
1375 received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
1376 datetime.a open.a getln.a sig.a case.a env.a stralloc.a \
1377 alloc.a substdio.a error.a str.a fs.a auto_qmail.o
1378
1379qmail-qmtpd.0: \
1380qmail-qmtpd.8
1381 nroff -man qmail-qmtpd.8 > qmail-qmtpd.0
1382
1383qmail-qmtpd.o: \
1384compile qmail-qmtpd.c stralloc.h gen_alloc.h substdio.h qmail.h \
1385substdio.h now.h datetime.h str.h fmt.h env.h sig.h rcpthosts.h \
1386auto_qmail.h readwrite.h control.h received.h
1387 ./compile qmail-qmtpd.c
1388
1389qmail-qread: \
1390load qmail-qread.o fmtqfn.o readsubdir.o date822fmt.o datetime.a \
1391open.a getln.a stralloc.a alloc.a substdio.a error.a str.a fs.a \
1392auto_qmail.o auto_split.o
1393 ./load qmail-qread fmtqfn.o readsubdir.o date822fmt.o \
1394 datetime.a open.a getln.a stralloc.a alloc.a substdio.a \
1395 error.a str.a fs.a auto_qmail.o auto_split.o
1396
1397qmail-qread.0: \
1398qmail-qread.8
1399 nroff -man qmail-qread.8 > qmail-qread.0
1400
1401qmail-qread.o: \
1402compile qmail-qread.c stralloc.h gen_alloc.h substdio.h subfd.h \
1403substdio.h fmt.h str.h getln.h fmtqfn.h readsubdir.h direntry.h \
1404auto_qmail.h open.h datetime.h date822fmt.h readwrite.h error.h \
1405exit.h
1406 ./compile qmail-qread.c
1407
1408qmail-qstat: \
1409warn-auto.sh qmail-qstat.sh conf-qmail conf-break conf-split
1410 cat warn-auto.sh qmail-qstat.sh \
1411 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
1412 | sed s}BREAK}"`head -1 conf-break`"}g \
1413 | sed s}SPLIT}"`head -1 conf-split`"}g \
1414 > qmail-qstat
1415 chmod 755 qmail-qstat
1416
1417qmail-qstat.0: \
1418qmail-qstat.8
1419 nroff -man qmail-qstat.8 > qmail-qstat.0
1420
1421qmail-queue: \
1422load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \
1423datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
1424str.a fs.a auto_qmail.o auto_split.o auto_uids.o
1425 ./load qmail-queue triggerpull.o fmtqfn.o now.o \
1426 date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
1427 alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
1428 auto_split.o auto_uids.o
1429
1430qmail-queue.0: \
1431qmail-queue.8
1432 nroff -man qmail-queue.8 > qmail-queue.0
1433
1434qmail-queue.o: \
1435compile qmail-queue.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
1436alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
1437auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
1438 ./compile qmail-queue.c
1439
1440qmail-remote: \
1441load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
1442timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \
1443ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
1444substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
1445 ./load qmail-remote control.o constmap.o timeoutread.o \
1446 timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
1447 ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
1448 lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
1449 str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
1450
1451qmail-remote.0: \
1452qmail-remote.8
1453 nroff -man qmail-remote.8 > qmail-remote.0
1454
1455qmail-remote.o: \
1456compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \
1457subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \
1458alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \
1459gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \
1460tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h
1461 ./compile qmail-remote.c
1462
1463qmail-rspawn: \
1464load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \
1465seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \
1466auto_qmail.o auto_uids.o auto_spawn.o
1467 ./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \
1468 sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \
1469 substdio.a error.a str.a auto_qmail.o auto_uids.o \
1470 auto_spawn.o
1471
1472qmail-rspawn.0: \
1473qmail-rspawn.8
1474 nroff -man qmail-rspawn.8 > qmail-rspawn.0
1475
1476qmail-rspawn.o: \
1477compile qmail-rspawn.c fd.h wait.h substdio.h exit.h fork.h error.h \
1478tcpto.h
1479 ./compile qmail-rspawn.c
1480
1481qmail-send: \
1482load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \
1483trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
1484datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
1485lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
1486auto_split.o
1487 ./load qmail-send qsutil.o control.o constmap.o newfield.o \
1488 prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
1489 qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
1490 wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
1491 substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
1492
1493qmail-send.0: \
1494qmail-send.8
1495 nroff -man qmail-send.8 > qmail-send.0
1496
1497qmail-send.8: \
1498qmail-send.9 conf-break conf-spawn
1499 cat qmail-send.9 \
1500 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1501 | sed s}BREAK}"`head -1 conf-break`"}g \
1502 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1503 > qmail-send.8
1504
1505qmail-send.o: \
1506compile qmail-send.c readwrite.h sig.h direntry.h control.h select.h \
1507open.h seek.h exit.h lock.h ndelay.h now.h datetime.h getln.h \
1508substdio.h alloc.h error.h stralloc.h gen_alloc.h str.h byte.h fmt.h \
1509scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \
1510qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \
1511fmtqfn.h readsubdir.h direntry.h
1512 ./compile qmail-send.c
1513
1514qmail-showctl: \
1515load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \
1516alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_break.o \
1517auto_patrn.o auto_spawn.o auto_split.o
1518 ./load qmail-showctl auto_uids.o control.o open.a getln.a \
1519 stralloc.a alloc.a substdio.a error.a str.a fs.a \
1520 auto_qmail.o auto_break.o auto_patrn.o auto_spawn.o \
1521 auto_split.o
1522
1523qmail-showctl.0: \
1524qmail-showctl.8
1525 nroff -man qmail-showctl.8 > qmail-showctl.0
1526
1527qmail-showctl.o: \
1528compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \
1529str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \
1530auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \
1531auto_split.h
1532 ./compile qmail-showctl.c
1533
1534qmail-smtpd: \
1535load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
1536timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
1537date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
1538open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
1539fs.a auto_qmail.o socket.lib
1540 ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
1541 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
1542 received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
1543 datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
1544 alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
1545 socket.lib`
1546
1547qmail-smtpd.0: \
1548qmail-smtpd.8
1549 nroff -man qmail-smtpd.8 > qmail-smtpd.0
1550
1551qmail-smtpd.o: \
1552compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
1553substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
1554error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
1555substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
1556exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
1557 ./compile qmail-smtpd.c
1558
1559qmail-start: \
1560load qmail-start.o prot.o fd.a auto_uids.o
1561 ./load qmail-start prot.o fd.a auto_uids.o
1562
1563qmail-start.0: \
1564qmail-start.8
1565 nroff -man qmail-start.8 > qmail-start.0
1566
1567qmail-start.8: \
1568qmail-start.9 conf-break conf-spawn
1569 cat qmail-start.9 \
1570 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1571 | sed s}BREAK}"`head -1 conf-break`"}g \
1572 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1573 > qmail-start.8
1574
1575qmail-start.o: \
1576compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h
1577 ./compile qmail-start.c
1578
1579qmail-tcpok: \
1580load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \
1581auto_qmail.o
1582 ./load qmail-tcpok open.a lock.a strerr.a substdio.a \
1583 error.a str.a auto_qmail.o
1584
1585qmail-tcpok.0: \
1586qmail-tcpok.8
1587 nroff -man qmail-tcpok.8 > qmail-tcpok.0
1588
1589qmail-tcpok.o: \
1590compile qmail-tcpok.c strerr.h substdio.h lock.h open.h readwrite.h \
1591auto_qmail.h exit.h
1592 ./compile qmail-tcpok.c
1593
1594qmail-tcpto: \
1595load qmail-tcpto.o ip.o now.o open.a lock.a substdio.a error.a str.a \
1596fs.a auto_qmail.o
1597 ./load qmail-tcpto ip.o now.o open.a lock.a substdio.a \
1598 error.a str.a fs.a auto_qmail.o
1599
1600qmail-tcpto.0: \
1601qmail-tcpto.8
1602 nroff -man qmail-tcpto.8 > qmail-tcpto.0
1603
1604qmail-tcpto.o: \
1605compile qmail-tcpto.c substdio.h subfd.h substdio.h auto_qmail.h \
1606fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h
1607 ./compile qmail-tcpto.c
1608
1609qmail-upq: \
1610warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split
1611 cat warn-auto.sh qmail-upq.sh \
1612 | sed s}QMAIL}"`head -1 conf-qmail`"}g \
1613 | sed s}BREAK}"`head -1 conf-break`"}g \
1614 | sed s}SPLIT}"`head -1 conf-split`"}g \
1615 > qmail-upq
1616 chmod 755 qmail-upq
1617
1618qmail-users.0: \
1619qmail-users.5
1620 nroff -man qmail-users.5 > qmail-users.0
1621
1622qmail-users.5: \
1623qmail-users.9 conf-break conf-spawn
1624 cat qmail-users.9 \
1625 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1626 | sed s}BREAK}"`head -1 conf-break`"}g \
1627 | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1628 > qmail-users.5
1629
1630qmail.0: \
1631qmail.7
1632 nroff -man qmail.7 > qmail.0
1633
1634qmail.o: \
1635compile qmail.c substdio.h readwrite.h wait.h exit.h fork.h fd.h \
1636qmail.h substdio.h auto_qmail.h
1637 ./compile qmail.c
1638
1639qreceipt: \
1640load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \
1641getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \
1642str.a auto_qmail.o
1643 ./load qreceipt headerbody.o hfield.o quote.o token822.o \
1644 qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \
1645 substdio.a error.a str.a auto_qmail.o
1646
1647qreceipt.0: \
1648qreceipt.1
1649 nroff -man qreceipt.1 > qreceipt.0
1650
1651qreceipt.o: \
1652compile qreceipt.c sig.h env.h substdio.h stralloc.h gen_alloc.h \
1653subfd.h substdio.h getln.h alloc.h str.h hfield.h token822.h \
1654gen_alloc.h error.h gen_alloc.h gen_allocdefs.h headerbody.h exit.h \
1655open.h quote.h qmail.h substdio.h
1656 ./compile qreceipt.c
1657
1658qsmhook: \
1659load qsmhook.o sig.a case.a fd.a wait.a getopt.a env.a stralloc.a \
1660alloc.a substdio.a error.a str.a
1661 ./load qsmhook sig.a case.a fd.a wait.a getopt.a env.a \
1662 stralloc.a alloc.a substdio.a error.a str.a
1663
1664qsmhook.o: \
1665compile qsmhook.c fd.h stralloc.h gen_alloc.h readwrite.h sgetopt.h \
1666subgetopt.h wait.h env.h byte.h str.h alloc.h exit.h fork.h case.h \
1667subfd.h substdio.h error.h substdio.h sig.h
1668 ./compile qsmhook.c
1669
1670qsutil.o: \
1671compile qsutil.c stralloc.h gen_alloc.h readwrite.h substdio.h \
1672qsutil.h
1673 ./compile qsutil.c
1674
1675quote.o: \
1676compile quote.c stralloc.h gen_alloc.h str.h quote.h
1677 ./compile quote.c
1678
1679rcpthosts.o: \
1680compile rcpthosts.c cdb.h uint32.h byte.h open.h error.h control.h \
1681constmap.h stralloc.h gen_alloc.h rcpthosts.h
1682 ./compile rcpthosts.c
1683
1684readsubdir.o: \
1685compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \
1686auto_split.h
1687 ./compile readsubdir.c
1688
1689received.o: \
1690compile received.c fmt.h qmail.h substdio.h now.h datetime.h \
1691datetime.h date822fmt.h received.h
1692 ./compile received.c
1693
1694remoteinfo.o: \
1695compile remoteinfo.c byte.h substdio.h ip.h fmt.h timeoutconn.h \
1696timeoutread.h timeoutwrite.h remoteinfo.h
1697 ./compile remoteinfo.c
1698
1699scan_8long.o: \
1700compile scan_8long.c scan.h
1701 ./compile scan_8long.c
1702
1703scan_ulong.o: \
1704compile scan_ulong.c scan.h
1705 ./compile scan_ulong.c
1706
1707seek.a: \
1708makelib seek_cur.o seek_end.o seek_set.o seek_trunc.o
1709 ./makelib seek.a seek_cur.o seek_end.o seek_set.o \
1710 seek_trunc.o
1711
1712seek_cur.o: \
1713compile seek_cur.c seek.h
1714 ./compile seek_cur.c
1715
1716seek_end.o: \
1717compile seek_end.c seek.h
1718 ./compile seek_end.c
1719
1720seek_set.o: \
1721compile seek_set.c seek.h
1722 ./compile seek_set.c
1723
1724seek_trunc.o: \
1725compile seek_trunc.c seek.h
1726 ./compile seek_trunc.c
1727
1728select.h: \
1729compile trysysel.c select.h1 select.h2
1730 ( ./compile trysysel.c >/dev/null 2>&1 \
1731 && cat select.h2 || cat select.h1 ) > select.h
1732 rm -f trysysel.o trysysel
1733
1734sendmail: \
1735load sendmail.o env.a getopt.a alloc.a substdio.a error.a str.a \
1736auto_qmail.o
1737 ./load sendmail env.a getopt.a alloc.a substdio.a error.a \
1738 str.a auto_qmail.o
1739
1740sendmail.o: \
1741compile sendmail.c sgetopt.h subgetopt.h substdio.h subfd.h \
1742substdio.h alloc.h auto_qmail.h exit.h env.h str.h
1743 ./compile sendmail.c
1744
1745setup: \
1746it man
1747 ./install
1748
1749sgetopt.o: \
1750compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \
1751subgetopt.h
1752 ./compile sgetopt.c
1753
1754shar: \
1755FILES BLURB BLURB2 BLURB3 BLURB4 README FAQ INSTALL INSTALL.alias \
1756INSTALL.ctl INSTALL.ids INSTALL.maildir INSTALL.mbox INSTALL.vsm \
1757REMOVE.sendmail REMOVE.binmail TEST.deliver TEST.receive UPGRADE \
1758THOUGHTS TODO THANKS CHANGES SECURITY INTERNALS SENDMAIL \
1759PIC.local2alias PIC.local2ext PIC.local2local PIC.local2rem \
1760PIC.local2virt PIC.nullclient PIC.relaybad PIC.relaygood \
1761PIC.rem2local FILES VERSION SYSDEPS TARGETS Makefile BIN.README \
1762BIN.Makefile BIN.setup idedit.c conf-break auto_break.h conf-spawn \
1763auto_spawn.h chkspawn.c conf-split auto_split.h conf-patrn \
1764auto_patrn.h conf-users conf-groups auto_uids.h auto_usera.h extra.h \
1765addresses.5 except.1 bouncesaying.1 condredirect.1 dot-qmail.9 \
1766envelopes.5 forgeries.7 forward.1 maildir2mbox.1 maildirmake.1 \
1767maildirwatch.1 mailsubj.1 mbox.5 preline.1 qbiff.1 qmail-clean.8 \
1768qmail-command.8 qmail-control.9 qmail-getpw.9 qmail-header.5 \
1769qmail-inject.8 qmail-limits.9 qmail-local.8 qmail-log.5 \
1770qmail-lspawn.8 qmail-newmrh.9 qmail-newu.9 qmail-pop3d.8 \
1771qmail-popup.8 qmail-pw2u.9 qmail-qmqpc.8 qmail-qmqpd.8 qmail-qmtpd.8 \
1772qmail-qread.8 qmail-qstat.8 qmail-queue.8 qmail-remote.8 \
1773qmail-rspawn.8 qmail-send.9 qmail-showctl.8 qmail-smtpd.8 \
1774qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-users.9 qmail.7 \
1775qreceipt.1 splogger.8 tcp-env.1 config.sh config-fast.sh \
1776qmail-clean.c qmail-getpw.c qmail-inject.c qmail-local.c \
1777qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \
1778qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
1779qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
1780qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
1781qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
1782dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
1783sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
1784except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \
1785maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \
1786datemail.sh mailsubj.sh qlx.h rcpthosts.h rcpthosts.c commands.h \
1787commands.c dnsdoe.h dnsdoe.c fmtqfn.h fmtqfn.c gfrom.h gfrom.c \
1788myctime.h myctime.c newfield.h newfield.c qsutil.h qsutil.c \
1789readsubdir.h readsubdir.c received.h received.c tcpto.h tcpto.c \
1790tcpto_clean.c trigger.h trigger.c triggerpull.h triggerpull.c \
1791trynpbg1.c trysyslog.c conf-cc conf-ld home.sh home+df.sh proc.sh \
1792proc+df.sh binm1.sh binm2.sh binm3.sh binm1+df.sh binm2+df.sh \
1793binm3+df.sh find-systype.sh make-compile.sh make-load.sh \
1794make-makelib.sh trycpp.c warn-auto.sh auto-str.c auto-int.c \
1795auto-int8.c auto-gid.c auto-uid.c hier.c install.c instcheck.c \
1796install-big.c alloc.3 alloc.h alloc.c alloc_re.c case.3 case.h \
1797case_diffb.c case_diffs.c case_lowerb.c case_lowers.c case_starts.c \
1798cdb.3 cdb.h cdb_hash.c cdb_seek.c cdb_unpack.c cdbmake.h \
1799cdbmake_add.c cdbmake_hash.c cdbmake_pack.c cdbmss.h cdbmss.c coe.3 \
1800coe.h coe.c fd.h fd_copy.3 fd_copy.c fd_move.3 fd_move.c fifo_make.3 \
1801fifo.h fifo.c trymkffo.c fork.h1 fork.h2 tryvfork.c now.3 now.h now.c \
1802open.h open_append.c open_excl.c open_read.c open_trunc.c \
1803open_write.c seek.h seek_cur.c seek_end.c seek_set.c seek_trunc.c \
1804conf-qmail auto_qmail.h qmail.h qmail.c gen_alloc.h gen_allocdefs.h \
1805stralloc.3 stralloc.h stralloc_eady.c stralloc_pend.c stralloc_copy.c \
1806stralloc_opyb.c stralloc_opys.c stralloc_cat.c stralloc_catb.c \
1807stralloc_cats.c stralloc_arts.c strerr.h strerr_sys.c strerr_die.c \
1808substdio.h substdio.c substdi.c substdo.c substdio_copy.c subfd.h \
1809subfderr.c subfdouts.c subfdout.c subfdins.c subfdin.c readwrite.h \
1810exit.h timeoutconn.h timeoutconn.c timeoutread.h timeoutread.c \
1811timeoutwrite.h timeoutwrite.c remoteinfo.h remoteinfo.c uint32.h1 \
1812uint32.h2 tryulong32.c wait.3 wait.h wait_pid.c wait_nohang.c \
1813trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \
1814sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \
1815trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \
1816byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \
1817str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \
1818str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \
1819getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \
1820subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \
1821error.h error.c error_str.c error_temp.c fmt.h fmt_str.c fmt_strn.c \
1822fmt_uint.c fmt_uint0.c fmt_ulong.c scan.h scan_ulong.c scan_8long.c \
1823slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \
1824headerbody.h headerbody.c token822.h token822.c control.h control.c \
1825datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
1826date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
1827ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
1828ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
1829prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
1830maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
1831 shar -m `cat FILES` > shar
1832 chmod 400 shar
1833
1834sig.a: \
1835makelib sig_alarm.o sig_block.o sig_catch.o sig_pause.o sig_pipe.o \
1836sig_child.o sig_hup.o sig_term.o sig_bug.o sig_misc.o
1837 ./makelib sig.a sig_alarm.o sig_block.o sig_catch.o \
1838 sig_pause.o sig_pipe.o sig_child.o sig_hup.o sig_term.o \
1839 sig_bug.o sig_misc.o
1840
1841sig_alarm.o: \
1842compile sig_alarm.c sig.h
1843 ./compile sig_alarm.c
1844
1845sig_block.o: \
1846compile sig_block.c sig.h hassgprm.h
1847 ./compile sig_block.c
1848
1849sig_bug.o: \
1850compile sig_bug.c sig.h
1851 ./compile sig_bug.c
1852
1853sig_catch.o: \
1854compile sig_catch.c sig.h hassgact.h
1855 ./compile sig_catch.c
1856
1857sig_child.o: \
1858compile sig_child.c sig.h
1859 ./compile sig_child.c
1860
1861sig_hup.o: \
1862compile sig_hup.c sig.h
1863 ./compile sig_hup.c
1864
1865sig_misc.o: \
1866compile sig_misc.c sig.h
1867 ./compile sig_misc.c
1868
1869sig_pause.o: \
1870compile sig_pause.c sig.h hassgprm.h
1871 ./compile sig_pause.c
1872
1873sig_pipe.o: \
1874compile sig_pipe.c sig.h
1875 ./compile sig_pipe.c
1876
1877sig_term.o: \
1878compile sig_term.c sig.h
1879 ./compile sig_term.c
1880
1881slurpclose.o: \
1882compile slurpclose.c stralloc.h gen_alloc.h readwrite.h slurpclose.h \
1883error.h
1884 ./compile slurpclose.c
1885
1886socket.lib: \
1887trylsock.c compile load
1888 ( ( ./compile trylsock.c && \
1889 ./load trylsock -lsocket -lnsl ) >/dev/null 2>&1 \
1890 && echo -lsocket -lnsl || exit 0 ) > socket.lib
1891 rm -f trylsock.o trylsock
1892
1893spawn.o: \
1894compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \
1895stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \
1896auto_qmail.h auto_uids.h auto_spawn.h
1897 ./chkspawn
1898 ./compile spawn.c
1899
1900splogger: \
1901load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib
1902 ./load splogger substdio.a error.a str.a fs.a `cat \
1903 syslog.lib` `cat socket.lib`
1904
1905splogger.0: \
1906splogger.8
1907 nroff -man splogger.8 > splogger.0
1908
1909splogger.o: \
1910compile splogger.c error.h substdio.h subfd.h substdio.h exit.h str.h \
1911scan.h fmt.h
1912 ./compile splogger.c
1913
1914str.a: \
1915makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
1916str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \
1917byte_cr.o byte_zero.o
1918 ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
1919 str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
1920 byte_diff.o byte_copy.o byte_cr.o byte_zero.o
1921
1922str_chr.o: \
1923compile str_chr.c str.h
1924 ./compile str_chr.c
1925
1926str_cpy.o: \
1927compile str_cpy.c str.h
1928 ./compile str_cpy.c
1929
1930str_diff.o: \
1931compile str_diff.c str.h
1932 ./compile str_diff.c
1933
1934str_diffn.o: \
1935compile str_diffn.c str.h
1936 ./compile str_diffn.c
1937
1938str_len.o: \
1939compile str_len.c str.h
1940 ./compile str_len.c
1941
1942str_rchr.o: \
1943compile str_rchr.c str.h
1944 ./compile str_rchr.c
1945
1946str_start.o: \
1947compile str_start.c str.h
1948 ./compile str_start.c
1949
1950stralloc.a: \
1951makelib stralloc_eady.o stralloc_pend.o stralloc_copy.o \
1952stralloc_opys.o stralloc_opyb.o stralloc_cat.o stralloc_cats.o \
1953stralloc_catb.o stralloc_arts.o
1954 ./makelib stralloc.a stralloc_eady.o stralloc_pend.o \
1955 stralloc_copy.o stralloc_opys.o stralloc_opyb.o \
1956 stralloc_cat.o stralloc_cats.o stralloc_catb.o \
1957 stralloc_arts.o
1958
1959stralloc_arts.o: \
1960compile stralloc_arts.c byte.h str.h stralloc.h gen_alloc.h
1961 ./compile stralloc_arts.c
1962
1963stralloc_cat.o: \
1964compile stralloc_cat.c byte.h stralloc.h gen_alloc.h
1965 ./compile stralloc_cat.c
1966
1967stralloc_catb.o: \
1968compile stralloc_catb.c stralloc.h gen_alloc.h byte.h
1969 ./compile stralloc_catb.c
1970
1971stralloc_cats.o: \
1972compile stralloc_cats.c byte.h str.h stralloc.h gen_alloc.h
1973 ./compile stralloc_cats.c
1974
1975stralloc_copy.o: \
1976compile stralloc_copy.c byte.h stralloc.h gen_alloc.h
1977 ./compile stralloc_copy.c
1978
1979stralloc_eady.o: \
1980compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \
1981gen_allocdefs.h
1982 ./compile stralloc_eady.c
1983
1984stralloc_opyb.o: \
1985compile stralloc_opyb.c stralloc.h gen_alloc.h byte.h
1986 ./compile stralloc_opyb.c
1987
1988stralloc_opys.o: \
1989compile stralloc_opys.c byte.h str.h stralloc.h gen_alloc.h
1990 ./compile stralloc_opys.c
1991
1992stralloc_pend.o: \
1993compile stralloc_pend.c alloc.h stralloc.h gen_alloc.h \
1994gen_allocdefs.h
1995 ./compile stralloc_pend.c
1996
1997strerr.a: \
1998makelib strerr_sys.o strerr_die.o
1999 ./makelib strerr.a strerr_sys.o strerr_die.o
2000
2001strerr_die.o: \
2002compile strerr_die.c substdio.h subfd.h substdio.h exit.h strerr.h
2003 ./compile strerr_die.c
2004
2005strerr_sys.o: \
2006compile strerr_sys.c error.h strerr.h
2007 ./compile strerr_sys.c
2008
2009subfderr.o: \
2010compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
2011 ./compile subfderr.c
2012
2013subfdin.o: \
2014compile subfdin.c readwrite.h substdio.h subfd.h substdio.h
2015 ./compile subfdin.c
2016
2017subfdins.o: \
2018compile subfdins.c readwrite.h substdio.h subfd.h substdio.h
2019 ./compile subfdins.c
2020
2021subfdout.o: \
2022compile subfdout.c readwrite.h substdio.h subfd.h substdio.h
2023 ./compile subfdout.c
2024
2025subfdouts.o: \
2026compile subfdouts.c readwrite.h substdio.h subfd.h substdio.h
2027 ./compile subfdouts.c
2028
2029subgetopt.o: \
2030compile subgetopt.c subgetopt.h
2031 ./compile subgetopt.c
2032
2033substdi.o: \
2034compile substdi.c substdio.h byte.h error.h
2035 ./compile substdi.c
2036
2037substdio.a: \
2038makelib substdio.o substdi.o substdo.o subfderr.o subfdout.o \
2039subfdouts.o subfdin.o subfdins.o substdio_copy.o
2040 ./makelib substdio.a substdio.o substdi.o substdo.o \
2041 subfderr.o subfdout.o subfdouts.o subfdin.o subfdins.o \
2042 substdio_copy.o
2043
2044substdio.o: \
2045compile substdio.c substdio.h
2046 ./compile substdio.c
2047
2048substdio_copy.o: \
2049compile substdio_copy.c substdio.h
2050 ./compile substdio_copy.c
2051
2052substdo.o: \
2053compile substdo.c substdio.h str.h byte.h error.h
2054 ./compile substdo.c
2055
2056syslog.lib: \
2057trysyslog.c compile load
2058 ( ( ./compile trysyslog.c && \
2059 ./load trysyslog -lgen ) >/dev/null 2>&1 \
2060 && echo -lgen || exit 0 ) > syslog.lib
2061 rm -f trysyslog.o trysyslog
2062
2063systype: \
2064find-systype trycpp.c
2065 ./find-systype > systype
2066
2067tcp-env: \
2068load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
2069timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \
2070stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
2071 ./load tcp-env dns.o remoteinfo.o timeoutread.o \
2072 timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \
2073 sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
2074 str.a fs.a `cat dns.lib` `cat socket.lib`
2075
2076tcp-env.0: \
2077tcp-env.1
2078 nroff -man tcp-env.1 > tcp-env.0
2079
2080tcp-env.o: \
2081compile tcp-env.c sig.h stralloc.h gen_alloc.h str.h env.h fmt.h \
2082scan.h subgetopt.h ip.h dns.h byte.h remoteinfo.h exit.h case.h
2083 ./compile tcp-env.c
2084
2085tcp-environ.0: \
2086tcp-environ.5
2087 nroff -man tcp-environ.5 > tcp-environ.0
2088
2089tcpto.o: \
2090compile tcpto.c tcpto.h open.h lock.h seek.h now.h datetime.h ip.h \
2091byte.h datetime.h readwrite.h
2092 ./compile tcpto.c
2093
2094tcpto_clean.o: \
2095compile tcpto_clean.c tcpto.h open.h substdio.h readwrite.h
2096 ./compile tcpto_clean.c
2097
2098timeoutconn.o: \
2099compile timeoutconn.c ndelay.h select.h error.h readwrite.h ip.h \
2100byte.h timeoutconn.h
2101 ./compile timeoutconn.c
2102
2103timeoutread.o: \
2104compile timeoutread.c timeoutread.h select.h error.h readwrite.h
2105 ./compile timeoutread.c
2106
2107timeoutwrite.o: \
2108compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
2109 ./compile timeoutwrite.c
2110
2111token822.o: \
2112compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
2113gen_alloc.h gen_allocdefs.h
2114 ./compile token822.c
2115
2116trigger.o: \
2117compile trigger.c select.h open.h trigger.h hasnpbg1.h
2118 ./compile trigger.c
2119
2120triggerpull.o: \
2121compile triggerpull.c ndelay.h open.h triggerpull.h
2122 ./compile triggerpull.c
2123
2124uint32.h: \
2125tryulong32.c compile load uint32.h1 uint32.h2
2126 ( ( ./compile tryulong32.c && ./load tryulong32 && \
2127 ./tryulong32 ) >/dev/null 2>&1 \
2128 && cat uint32.h2 || cat uint32.h1 ) > uint32.h
2129 rm -f tryulong32.o tryulong32
2130
2131wait.a: \
2132makelib wait_pid.o wait_nohang.o
2133 ./makelib wait.a wait_pid.o wait_nohang.o
2134
2135wait_nohang.o: \
2136compile wait_nohang.c haswaitp.h
2137 ./compile wait_nohang.c
2138
2139wait_pid.o: \
2140compile wait_pid.c error.h haswaitp.h
2141 ./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 @@
1 Original message:
2
3 To: help
4 Hi.
5
6qmail-inject Fill in the complete envelope and header:
7
8 | (envelope) from joe@heaven.af.mil to help@heaven.af.mil
9 | From: joe@heaven.af.mil
10 | To: help@heaven.af.mil
11 |
12 | Hi.
13 V
14
15qmail-queue Store message safely on disk.
16 Trigger qmail-send.
17 |
18 V
19
20qmail-send Look at envelope recipient, help@heaven.af.mil.
21 | Is heaven.af.mil in locals? Yes.
22 | Deliver locally to help@heaven.af.mil.
23 V
24
25qmail-lspawn ./Mailbox
26
27 | Look at mailbox name, help.
28 | Is help listed in qmail-users? No.
29 | Is there a help account? No.
30 | Give control of the message to alias.
31 | Run qmail-local.
32 V
33
34qmail-local alias ~alias help - help heaven.af.mil joe@heaven.af.mil ./Mailbox
35
36 Does ~alias/.qmail-help exist? Yes: "john".
37 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 @@
1 Original message:
2
3 To: fred-sos
4 Hi.
5
6qmail-inject Fill in the complete envelope and header:
7
8 | (envelope) from joe@heaven.af.mil to fred-sos@heaven.af.mil
9 | From: joe@heaven.af.mil
10 | To: fred-sos@heaven.af.mil
11 |
12 | Hi.
13 V
14
15qmail-queue Store message safely on disk.
16 Trigger qmail-send.
17 |
18 V
19
20qmail-send Look at envelope recipient, fred-sos@heaven.af.mil.
21 | Is heaven.af.mil in locals? Yes.
22 | Deliver locally to fred-sos@heaven.af.mil.
23 V
24
25qmail-lspawn ./Mailbox
26
27 | Look at mailbox name, fred-sos.
28 | Is fred-sos listed in qmail-users? No.
29 | Is there a fred-sos account? No.
30 | Is there a fred account? Yes.
31 | Is fred's uid nonzero? Yes.
32 | Is ~fred visible to the qmailp user? Yes.
33 | Is ~fred owned by fred? Yes.
34 | Give control of the message to fred.
35 | Run qmail-local.
36 V
37
38qmail-local fred ~fred fred-sos - sos heaven.af.mil joe@heaven.af.mil ./Mailbox
39
40 Does ~fred/.qmail-sos exist? Yes: "./Extramail".
41 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 @@
1 Original message:
2
3 To: fred
4 Hi.
5
6qmail-inject Fill in the complete envelope and header:
7
8 | (envelope) from joe@heaven.af.mil to fred@heaven.af.mil
9 | From: joe@heaven.af.mil
10 | To: fred@heaven.af.mil
11 |
12 | Hi.
13 V
14
15qmail-queue Store message safely on disk.
16 Trigger qmail-send.
17 |
18 V
19
20qmail-send Look at envelope recipient, fred@heaven.af.mil.
21 | Is heaven.af.mil in locals? Yes.
22 | Deliver locally to fred@heaven.af.mil.
23 V
24
25qmail-lspawn ./Mailbox
26
27 | Look at mailbox name, fred.
28 | Is fred listed in qmail-users? No.
29 | Is there a fred account? Yes.
30 | Is fred's uid nonzero? Yes.
31 | Is ~fred visible to the qmailp user? Yes.
32 | Is ~fred owned by fred? Yes.
33 | Give control of the message to fred.
34 | Run qmail-local.
35 V
36
37qmail-local fred ~fred fred '' '' heaven.af.mil joe@heaven.af.mil ./Mailbox
38
39 Does ~fred/.qmail exist? No.
40 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 @@
1 Original message:
2
3 To: bill@irs.gov
4 Hi.
5
6qmail-inject Fill in the complete envelope and header:
7
8 | (envelope) from joe@heaven.af.mil to bill@irs.gov
9 | From: joe@heaven.af.mil
10 | To: bill@irs.gov
11 |
12 | Hi.
13 V
14
15qmail-queue Store message safely on disk.
16 Trigger qmail-send.
17 |
18 V
19
20qmail-send Look at envelope recipient, bill@irs.gov.
21 | Is irs.gov in locals? No.
22 | Is bill@irs.gov in virtualdomains? No.
23 | Is irs.gov in virtualdomains? No.
24 | Is .gov in virtualdomains? No.
25 | Deliver remotely to bill@irs.gov.
26 V
27
28qmail-rspawn Run qmail-remote.
29
30 |
31 V
32
33qmail-remote Look at host name, irs.gov.
34 Is irs.gov listed in smtproutes? No.
35 Look up DNS MX/A for irs.gov and connect to it by SMTP:
36
37 MAIL FROM:<joe@heaven.af.mil>
38 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 @@
1 Original message:
2
3 To: dude@tommy.gov
4 Hi.
5
6qmail-inject Fill in the complete envelope and header:
7
8 | (envelope) from joe@heaven.af.mil to dude@tommy.gov
9 | From: joe@heaven.af.mil
10 | To: dude@tommy.gov
11 |
12 | Hi.
13 V
14
15qmail-queue Store message safely on disk.
16 Trigger qmail-send.
17 |
18 V
19
20qmail-send Look at envelope recipient, dude@tommy.gov.
21 | Is tommy.gov in locals? No.
22 | Is dude@tommy.gov in virtualdomains? No.
23 | Is tommy.gov in virtualdomains? Yes: "tommy.gov:fred".
24 | Deliver locally to fred-dude@tommy.gov.
25 V
26
27qmail-lspawn ./Mailbox
28
29 | Look at mailbox name, fred-dude.
30 | Is fred-dude listed in qmail-users? No.
31 | Is there a fred-dude account? No.
32 | Is there a fred account? Yes.
33 | Is fred's uid nonzero? Yes.
34 | Is ~fred visible to the qmailp user? Yes.
35 | Is ~fred owned by fred? Yes.
36 | Give control of the message to fred.
37 | Run qmail-local.
38 V
39
40qmail-local fred ~fred fred-dude - dude tommy.gov joe@heaven.af.mil ./Mailbox
41
42 Does ~fred/.qmail-dude exist? No.
43 Does ~fred/.qmail-default exist? Yes: "./Mail.tommy".
44 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 @@
1 Original message:
2
3 To: bill@irs.gov
4 Hi.
5
6qmail-inject Fill in the complete envelope and header:
7
8 | (envelope) from joe@heaven.af.mil to bill@irs.gov
9 | From: joe@heaven.af.mil
10 | To: bill@irs.gov
11 |
12 | Hi.
13 V
14
15qmail-queue Store message safely on disk.
16 Trigger qmail-send.
17 |
18 V
19
20qmail-send Look at envelope recipient, bill@irs.gov.
21 | Is irs.gov in locals? No.
22 | Is bill@irs.gov in virtualdomains? No.
23 | Is irs.gov in virtualdomains? No.
24 | Is .gov in virtualdomains? No.
25 | Deliver remotely to bill@irs.gov.
26 V
27
28qmail-rspawn Run qmail-remote.
29
30 |
31 V
32
33qmail-remote Look at host name, irs.gov.
34 Is irs.gov listed in smtproutes? Yes: ":bigbang.af.mil".
35 Look up DNS A for bigbang.af.mil and connect by SMTP:
36
37 MAIL FROM:<joe@heaven.af.mil>
38 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 @@
1qmail-smtpd Receive message by SMTP from another host:
2
3 MAIL FROM:<spammer@aol.com>
4 RCPT TO:<bill@irs.gov>
5
6 Is $RELAYCLIENT set? No.
7 Is irs.gov in rcpthosts? No.
8 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 @@
1qmail-smtpd Receive message by SMTP from another host:
2
3 | MAIL FROM:<joe@heaven.af.mil>
4 | RCPT TO:<bill@irs.gov>
5 |
6 | Is $RELAYCLIENT set? Yes: "".
7 | Accept RCPT.
8 V
9
10qmail-queue Store message safely on disk.
11 Trigger qmail-send.
12 |
13 V
14
15qmail-send Look at envelope recipient, bill@irs.gov.
16 | Is irs.gov in locals? No.
17 | Is bill@irs.gov in virtualdomains? No.
18 | Is irs.gov in virtualdomains? No.
19 | Is .gov in virtualdomains? No.
20 | Deliver remotely to bill@irs.gov.
21 V
22
23qmail-rspawn Run qmail-remote.
24
25 |
26 V
27
28qmail-remote Look at host name, irs.gov.
29 Is irs.gov listed in smtproutes? No.
30 Look up DNS MX/A for irs.gov and connect to it by SMTP:
31
32 MAIL FROM:<joe@heaven.af.mil>
33 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 @@
1qmail-smtpd Receive message by SMTP from another host:
2
3 | MAIL FROM:<bill@irs.gov>
4 | RCPT TO:<joe@heaven.af.mil>
5 |
6 | Is $RELAYCLIENT set? No.
7 | Is heaven.af.mil in rcpthosts? Yes.
8 | Accept RCPT.
9 V
10
11qmail-queue Store message safely on disk.
12 Trigger qmail-send.
13 |
14 V
15
16qmail-send Look at envelope recipient, joe@heaven.af.mil.
17 | Is heaven.af.mil in locals? Yes.
18 | Deliver locally to joe@heaven.af.mil.
19 V
20
21qmail-lspawn ./Mailbox
22
23 | Look at mailbox name, joe.
24 | Is joe listed in qmail-users? No.
25 | Is there a joe account? Yes.
26 | Is joe's uid nonzero? Yes.
27 | Is ~joe visible to the qmailp user? Yes.
28 | Is ~joe owned by joe? Yes.
29 | Give control of the message to joe.
30 | Run qmail-local.
31 V
32
33qmail-local joe ~joe joe '' '' heaven.af.mil bill@irs.gov ./Mailbox
34
35 Does ~joe/.qmail exist? No.
36 Write message to ./Mailbox in mbox format.
diff --git a/README b/README
new file mode 100644
index 0000000..5208eaf
--- /dev/null
+++ b/README
@@ -0,0 +1,269 @@
1qmail 1.03
219980615
3Copyright 1998
4D. J. Bernstein, qmail@pobox.com
5
6qmail is a secure, reliable, efficient, simple message transfer agent.
7It is meant as a replacement for the entire sendmail-binmail system on
8typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and
9BLURB4 for more detailed advertisements.
10
11INSTALL says how to set up and test qmail. If you're upgrading from a
12previous version, read UPGRADE instead.
13
14See PIC.* for some ``end-to-end'' pictures of mail flowing through the
15qmail system.
16
17See http://pobox.com/~djb/qmail.html for other qmail-related software
18and a pointer to the qmail mailing list.
19
20Other documentation: http://pobox.com/~djb/proto.html shows solutions to
21several Internet mail problems; many of these solutions are implemented
22in qmail. CHANGES and THANKS show how qmail has changed since it was
23first released. SECURITY, INTERNALS, THOUGHTS, and TODO record many of
24the qmail design decisions.
25
26The rest of this file is a list of systypes where various versions of
27qmail have been reported to work. 0.96 was the final gamma version; 1.00
28had exactly the same code as 0.96. To see your systype, make systype;
29cat systype.
30
311.00: a.ux-3.0-svr2-:-:-:mc68030-:- (tnx RF)
321.01: aix-3-2-:-:-:000000406300-:- (tnx DG)
331.01: aix-3-2-:-:-:000011216700-:- (tnx JLB)
341.01: aix-4-1-:-:-:000041574c00-:- (tnx M2H)
351.01: aix-4-1-:-:-:000088581000-:- (tnx HJB)
361.01: aix-4-1-:-:-:002b51134c00-:- (tnx MP)
371.00: aix-4-1-:-:-:00910033a000-:- (tnx KJJ)
381.01: aix-4-2-:-:-:000055247900-:- (tnx JLB)
391.01: aix-4-2-:-:-:000062295800-:- (tnx TD)
401.01: aix-4-2-:-:-:000136094c00-:- (tnx T2U)
411.00: aix-4-2-:-:-:000205254600-:- (tnx MGM)
421.01: aix-4-2-:-:-:005255bc4c00-:- (tnx DS)
431.01: aix-4-2-:-:-:006030944c00-:-
441.01: bsd.386-1.1-0-:i386-:-:i386-:- (tnx T2M)
451.01: bsd.os-2.0-:i386-:-:pentium-:- (tnx MSS)
461.01: bsd.os-2.0.1-:i386-:-:i486-:- (tnx KR)
470.96: bsd.os-2.1-:i386-:-:-:- (tnx DAR)
481.00: bsd.os-2.1-:i386-:-:i486-:- (tnx RJC)
490.96: bsd.os-2.1-:i386-:-:pentium-:- (tnx UO)
501.01: bsd.os-3.0-:i386-:-:-:- (tnx VU)
511.01: bsd.os-3.0-:i386-:-:pentium-:- (tnx RJO)
521.01: bsd.os-3.1-:i386-:-:pentium-:- (tnx ABC)
531.01: bsd.os-3.1-:i386-:-:pentium.ii-:- (tnx UO)
540.96: dgux-5.4r2.01-generic-:-:-:aviion-:- (tnx HWM)
551.01: freebsd-2.1.0-release-:i386-:-:i486-dx-:- (tnx VV)
561.01: freebsd-2.1.0-release-:i386-:-:i486.dx2-:- (tnx JLB)
571.00: freebsd-2.1.0-release-:i386-:-:i486dx-:- (tnx chrisj=???)
581.01: freebsd-2.1.0-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MBS)
591.01: freebsd-2.1.5-release-:i386-:-:i486-dx-:- (tnx B1F)
600.96: freebsd-2.1.5-release-:i386-:-:i486dx-:- (tnx FN)
611.01: freebsd-2.1.5-release-:i386-:-:unknown.-:- (tnx BMF)
621.00: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM)
630.96: freebsd-2.1.6-release-:i386-:-:Pentium-Pro.150-:- (tnx CH)
641.01: freebsd-2.1.6-release-:i386-:-:cy486dlc-:- (tnx M3H)
650.96: freebsd-2.1.6.1-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MF)
661.01: freebsd-2.1.7-release-:i386-:-:i486-dx-:- (tnx AAF)
671.00: freebsd-2.1.7-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx JBB)
681.01: freebsd-2.1.7-release-:i386-:-:pentium.815\100-:- (tnx B1F)
691.01: freebsd-2.2-970422-releng-:i386-:-:-:- (tnx TM)
701.00: freebsd-2.2-release-:i386-:-:-:- (tnx MT)
711.01: freebsd-2.2-stable-:i386-:-:cyrix.5x86-:- (tnx A2B)
721.01: freebsd-2.2-stable-:i386-:-:pentium-:- (tnx gary@systemics=???)
731.01: freebsd-2.2.1-release-:i386-:-:-:- (tnx M2R)
741.01: freebsd-2.2.1-release-:i386-:-:i486-dx-:- (tnx PGR)
751.00: freebsd-2.2.1-release-:i386-:-:i486.dx2-:- (tnx BR)
761.01: freebsd-2.2.1-release-:i386-:-:pentium-:- (tnx REB)
771.01: freebsd-2.2.1-release-:i386-:-:pentium.pro-:- (tnx JS)
781.01: freebsd-2.2.2-release-:i386-:-:amd.am5x86.write-through-:- (tnx AGB)
791.01: freebsd-2.2.2-release-:i386-:-:i486-dx-:- (tnx A2L)
801.01: freebsd-2.2.2-release-:i386-:-:i486.dx2-:- (tnx D3S)
811.01: freebsd-2.2.2-release-:i386-:-:pentium-:- (tnx B2F)
821.01: freebsd-2.2.2-release-:i386-:-:pentium.pro-:- (tnx M2G)
831.01: freebsd-2.2.5-release-:i386-:-:i486-dx-:- (tnx R2N)
841.01: freebsd-2.2.5-release-:i386-:-:i486.dx2-:- (tnx AY)
851.01: freebsd-2.2.5-release-:i386-:-:pentium.pro-:- (tnx AI)
861.01: freebsd-2.2.5-stable-:i386-:-:i486.dx2-:- (tnx JK)
871.01: freebsd-2.2.5-stable-:i386-:-:pentium-:- (tnx root@defiant=???)
881.01: freebsd-2.2.6-release-:i386-:-:-:- (tnx TM)
891.01: freebsd-2.2.6-release-:i386-:-:amd.am5x86.write-through-:- (tnx root@skully=???)
901.00: freebsd-3.0-970209-snap-:i386-:-:-:- (tnx YF)
911.01: freebsd-3.0-970428-snap-:i386-:-:pentium-:- (tnx M3S)
921.01: freebsd-3.0-970807-snap-:i386-:-:amd.k6-:- (tnx KMD)
931.01: freebsd-3.0-980309-snap-:i386-:-:pentium-:- (tnx MM)
941.01: freebsd-3.0-current-:i386-:-:pentium-:- (tnx KB)
951.01: hp-ux-a.09.05-a-:-:-:9000.712-:- (tnx SV)
961.01: hp-ux-a.09.07-a-:-:-:9000.712-:- (tnx LB)
971.00: hp-ux-b.09.00-a-:-:-:9000.360-:- (tnx VV)
981.01: hp-ux-b.10.20-a-:-:-:9000.755-:- (tnx BCK)
991.01: irix-5.3-11091812-:-:-:ip22-:- (tnx JL)
1001.01: irix-6.2-03131015-:-:-:ip22-:- (tnx DS)
1011.01: irix64-6.2-03131016-:-:-:ip19-:- (tnx AH)
1021.01: irix64-6.2-06101031-:-:-:ip28-:- (tnx DB)
1031.01: linux-1.2.13-:i386-:-:i486-:- (tnx RF)
1041.01: linux-1.2.13-:i386-:-:pentium-:- (tnx MEE)
1051.01: linux-1.99.4-:i386-:-:pentium-:- (tnx C2H)
1061.01: linux-2.0.0-:i386-:-:i486-:- (tnx kragen@gentle=???)
1071.01: linux-2.0.0-:i386-:-:pentium-:- (tnx MJD)
1081.01: linux-2.0.6-:i386-:-:pentium-:-
1091.00: linux-2.0.6-:i386-:-:ppro-:- (tnx MR)
1101.01: linux-2.0.7-:i386-:-:i486-:- (tnx TLM)
1111.01: linux-2.0.9-:i386-:-:i486-:- (tnx VBM)
1120.96: linux-2.0.13-:i386-:-:pentium-:- (tnx BW)
1131.01: linux-2.0.15-:i386-:-:i486-:- (tnx JCD)
1141.01: linux-2.0.18-:i386-:-:i486-:- (tnx tk@avalon=???)
1151.01: linux-2.0.18-:i386-:-:pentium-:- (tnx root@webtvchat=???)
1161.00: linux-2.0.22-:i386-:-:pentium-:- (tnx MDI)
1171.00: linux-2.0.23-:i386-:-:i486-:- (tnx B2L)
1181.01: linux-2.0.24-:i386-:-:i486-:- (tnx GLM)
1191.00: linux-2.0.24-:i386-:-:pentium-:- (tnx VV)
1200.96: linux-2.0.25-:i386-:-:i486-:- (tnx BDB)
1211.01: linux-2.0.25-:i386-:-:pentium-:- (tnx KA)
1220.93: linux-2.0.26-:i386-:-:i486-:- (tnx blynch@texas=???)
1231.01: linux-2.0.26-:i386-:-:pentium-:- (tnx robbie@opus=???)
1241.00: linux-2.0.27-:-:-:sparc-:- (tnx SVD)
1251.00: linux-2.0.27-:i386-:-:i386-:- (tnx ECG)
1261.01: linux-2.0.27-:i386-:-:i486-:- (tnx BN)
1271.01: linux-2.0.27-:i386-:-:pentium-:- (tnx EK)
1281.01: linux-2.0.27-:i386-:-:ppro-:- (tnx L3L)
1291.01: linux-2.0.28-:i386-:-:i486-:- (tnx AAF)
1301.00: linux-2.0.28-:i386-:-:pentium-:- (tnx root@duggy=???)
1311.01: linux-2.0.28-:i386-:-:ppro-:- (tnx S3T)
1321.01: linux-2.0.28-osfmach3-:-:-:ppc-:- (tnx CG)
1331.01: linux-2.0.29-:alpha-:-:alpha-:- (tnx MB)
1341.01: linux-2.0.29-:i386-:-:i386-:- (tnx AJK)
1351.01: linux-2.0.29-:i386-:-:i486-:- (tnx FPL)
1361.01: linux-2.0.29-:i386-:-:pentium-:- (tnx FW)
1371.00: linux-2.0.29-:i386-:-:ppro-:- (tnx MMM)
1381.01: linux-2.0.30-:-:-:sparc-:- (tnx J2P)
1391.01: linux-2.0.30-:alpha-:-:alpha-:- (tnx WS)
1401.01: linux-2.0.30-:i386-:-:i386-:- (tnx OK)
1411.00: linux-2.0.30-:i386-:-:i486-:- (tnx KUT)
1421.01: linux-2.0.30-:i386-:-:i486-:- (tnx PK)
1431.01: linux-2.0.30-:i386-:-:pentium-:- (tnx AV)
1441.00: linux-2.0.30-:i386-:-:ppro-:- (tnx root@gate=???)
1451.01: linux-2.0.30-osfmach3-:-:-:ppc-:- (tnx PTW)
1461.01: linux-2.0.30u11-:i386-:-:pentium-:- (tnx JTB)
1471.01: linux-2.0.31-:i386-:-:i486-:- (tnx SAE)
1481.01: linux-2.0.31-:i386-:-:pentium-:- (tnx B3W)
1491.01: linux-2.0.31-:i386-:-:ppro-:- (tnx JAK)
1501.01: linux-2.0.32-:-:-:ie86-:- (tnx root@vmlinuz=???)
1511.01: linux-2.0.32-:alpha-:-:alpha-:- (tnx NR)
1521.01: linux-2.0.32-:i386-:-:i486-:- (tnx SC)
1531.01: linux-2.0.32-:i386-:-:pentium-:- (tnx HT)
1541.01: linux-2.0.32-:i386-:-:ppro-:- (tnx RK)
1551.01: linux-2.0.33-:i386-:-:i486-:- (tnx RAB)
1561.01: linux-2.0.33-:i386-:-:pentium-:- (tnx AF)
1571.01: linux-2.0.33-:i386-:-:ppro-:- (tnx B2W)
1581.01: linux-2.1.9-:i386-:-:i486-:- (tnx SJB)
1591.01: linux-2.1.10-:i386-:-:i486-:- (tnx JB)
1600.96: linux-2.1.13-:i386-:-:i486-:- (tnx ML)
1610.96: linux-2.1.14-:i386-:-:pentium-:- (tnx SCW)
1620.96: linux-2.1.23-:i386-:-:pentium-:- (tnx JF)
1631.01: linux-2.1.24-:-:-:ppc-:- (tnx meta=???)
1640.96: linux-2.1.25-:i386-:-:i486-:- (tnx JBF)
1650.96: linux-2.1.25-:i386-:-:pentium-:- (tnx UO)
1661.00: linux-2.1.26-:i386-:-:i486-:- (tnx DK)
1671.00: linux-2.1.27-:i386-:-:pentium-:- (tnx JF)
1681.01: linux-2.1.28-:i386-:-:i486-:- (tnx HDG)
1691.00: linux-2.1.28-:i386-:-:pentium-:- (tnx RGS)
1701.00: linux-2.1.29-:i386-:-:i486-:- (tnx SJW)
1711.01: linux-2.1.35-:i386-:-:pentium-:- (tnx JF)
1721.01: linux-2.1.36-:i386-:-:i486-:- (tnx ML)
1731.01: linux-2.1.42-:i386-:-:i486-:- (tnx wtanaka=???)
1741.01: linux-2.1.46-:i386-:-:pentium-:- (tnx VR)
1751.01: linux-2.1.51-:i386-:-:pentium-:- (tnx KO)
1761.01: linux-2.1.61-:i386-:-:i486-:- (tnx RO)
1771.01: linux-2.1.65-:i386-:-:i486-:- (tnx F2T)
1781.01: linux-2.1.71-:i386-:-:ppro-:- (tnx MJG)
1791.01: linux-2.1.78-:i386-:-:pentium-:- (tnx AS)
1801.01: linux-2.1.82-:i386-:-:pentium-:- (tnx AY)
1811.01: linux-2.1.85-:i386-:-:pentium-:- (tnx PJH)
1821.00: machten-4-0.4-:-:-:powerpc-:- (tnx RAM)
1831.01: netbsd-1.1-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GL)
1841.01: netbsd-1.2-:hp300-:-:-:- (tnx ML)
1851.01: netbsd-1.2-:i386-:-:i486dx.(genuineintel.486-class.cpu)-:- (tnx T2K)
1860.96: netbsd-1.2-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GH)
1871.01: netbsd-1.2.1-:mac68k-:-:apple.macintosh.se/30..(68030)-:- (tnx HM)
1881.01: netbsd-1.2.1-:sparc-:-:fmi,mb86904.@.110.mhz,.on-chip.fpu-:- (tnx ZU)
1890.96: netbsd-1.2c-:pmax-:-:-:- (tnx JLW)
1901.01: netbsd-1.3-:hp300-:-:hp.9000/433.(33mhz.mc68040.cpu+mmu+fpu,.4k.on-chip.physical.i/d.caches)-:- (tnx TB)
1911.01: netbsd-1.3.1-:sun3-:-:sun.3/60-:- (tnx MBS)
1921.01: netbsd-1.3_alpha-:i386-:-:intel.pentium.(p54c).(586-class)-:- (tnx GL)
1931.01: nextstep-3.1-:mc680x0-:-:68040-:- (tnx JRY)
1941.01: nextstep-3.3-:hppa-:-:7100lc-:-
1951.01: nextstep-3.3-:i386-:-:pentium-:- (tnx HM)
1961.01: nextstep-3.3-:mc680x0-:-:68040-:- (tnx WEB)
1971.01: nextstep-4.1-:mc680x0-:-:68040-:- (tnx FN)
1981.00: openbsd-2.0-hoth#0-:openbsd.i386-:-:i386-:- (tnx MBS)
1991.00: openbsd-2.0-mr_potatoe_head#2-:openbsd.i386-:-:i386-:- (tnx JJMK)
2000.96: openbsd-2.0-puma#1-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
2011.01: openbsd-2.1-asgard#1-:openbsd.i386-:-:i386-:- (tnx ETT)
2021.01: openbsd-2.1-generic#71-:openbsd.sparc-:-:sparc-:- (tnx MMM2)
2031.01: openbsd-2.1-katana#2-:openbsd.i386-:-:i386-:- (tnx CHR)
2041.01: openbsd-2.1-puma#0-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
2051.01: openbsd-2.2-ele#2-:openbsd.i386-:-:i386-:- (tnx RC)
2061.01: openbsd-2.2-generic#424-:openbsd.i386-:-:i386-:- (tnx ETT)
2071.01: osf1-v2.0-240-:-:-:alpha-:- (tnx JF)
2081.00: osf1-v3.2-148-:-:-:alpha-:- (tnx DL)
2091.01: osf1-v3.2-148-:-:-:alpha-:- (tnx RSK)
2101.01: osf1-v3.2-41-:-:-:alpha-:- (tnx MSD)
2111.01: osf1-v3.2-mp-4.2-:-:-:alpha-:- (tnx MSD)
2121.01: osf1-v4.0-386-:-:-:alpha-:- (tnx TEE)
2131.01: osf1-v4.0-464-:-:-:alpha-:- (tnx AWB)
2141.01: osf1-v4.0-564-:-:-:alpha-:- (tnx A2P)
2151.01: osf1-v4.0-564.32-:-:-:alpha-:- (tnx TLF)
2161.01: osf1-v4.0-878-:-:-:alpha-:- (tnx BJM)
2171.01: sco_sv-3.2-2-:-:-:i386-:- (tnx PW)
2181.01: sinix-l-5.41-d0005-:-:-:mx300i-:- (tnx IH)
2191.01: sunos-4.1.1-1-:mc68020-:sun3-:sun3-:sun3- (tnx JWB)
2201.01: sunos-4.1.1-1-:mc68020-:sun3-:sun3x-:sun3x- (tnx TT)
2211.01: sunos-4.1.3-jl-2-:sparc-:sun4-:sun4c-:sun4c- (tnx T2K)
2221.01: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4c-:sun4c- (tnx MBS)
2231.01: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4m-:sun4m- (tnx RSK)
2241.01: sunos-4.1.3_u1-10-:sparc-:sun4-:sun4m-:sun4m- (tnx aoki=???)
2251.00: sunos-4.1.3_u1-4-:unknown-:sun4-:sun4m-:sun4m- (tnx J2B)
2261.01: sunos-4.1.3_u1-6-:sparc-:sun4-:sun4m-:sun4m- (tnx RD)
2271.01: sunos-4.1.4-1-:unknown-:sun4-:sun4m-:sun4m- (tnx M3S)
2281.01: sunos-4.1.4-2-:sparc-:sun4-:sun4m-:sun4m-
2291.01: sunos-5.3-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx JDJ)
2301.01: sunos-5.4-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx jimo=???)
2310.96: sunos-5.4-generic_101945-10-:sparc-:sun4-:sun4m-:sun4m- (tnx W2K)
2321.00: sunos-5.4-generic_101945-34-:sparc-:sun4-:sun4m-:sun4m- (tnx ACB)
2330.96: sunos-5.4-generic_101946-35-:i386-:i86pc-:i86pc-:i86pc- (tnx CK)
2341.01: sunos-5.5-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx seong=???)
2351.01: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx SPM)
2361.01: sunos-5.5-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM)
2371.01: sunos-5.5-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx YC)
2381.01: sunos-5.5-generic_103093-02-:sparc-:sun4-:sun4m-:sun4m- (tnx RF)
2390.96: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM)
2401.01: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (tnx ERH)
2411.01: sunos-5.5-generic_103093-10-:sparc-:sun4-:sun4d-:sun4d- (tnx KT)
2421.01: sunos-5.5-generic_103094-05-:i386-:i86pc-:i86pc-:i86pc- (tnx M2G)
2431.01: sunos-5.5.1-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx cro=???)
2441.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CG)
2451.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx MBS)
2461.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4u-:sun4u-
2470.96: sunos-5.5.1-generic_103640-02-:sparc-:sun4-:sun4m-:sun4m- (tnx SGC)
2481.00: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4u-:sun4u- (tnx EG)
2491.00: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4m-:sun4m- (tnx L2L)
2501.01: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4u-:sun4u- (tnx KY)
2511.01: sunos-5.5.1-generic_103640-06-:sparc-:sun4-:sun4u-:sun4u- (tnx RA)
2521.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4c-:sun4c- (tnx RA)
2531.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4d-:sun4d- (tnx MS)
2541.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4m-:sun4m- (tnx S2P)
2551.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4u-:sun4u- (tnx CM)
2561.01: sunos-5.5.1-generic_103640-12-:sparc-:sun4-:sun4m-:sun4m- (tnx IK)
2571.01: sunos-5.5.1-generic_103640-18-:sparc-:sun4-:sun4u-:sun4u- (tnx PMH)
2581.01: sunos-5.5.1-generic_103641-08-:i386-:i86pc-:i86pc-:i86pc- (tnx TL)
2591.01: sunos-5.5.1-generic_103641-12-:i386-:i86pc-:i86pc-:i86pc- (tnx JS)
2601.01: sunos-5.5.1-generic_105428-01-:sparc-:sun4-:sun4u-:sun4u- (tnx BCM)
2610.96: sunos-5.5.1-generic_patch-:i386-:i86pc-:i86pc-:i86pc- (tnx D2K)
2621.01: sunos-5.6-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx DS)
2631.01: sunos-5.6-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx BDM)
2641.01: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx RPS)
2651.01: sunos-5.6-generic_105182-01-:i386-:i86pc-:i86pc-:i86pc- (tnx JFK)
2661.01: sunos-5.6-generic_105182-04-:i386-:i86pc-:i86pc-:i86pc- (tnx YC)
2670.96: ultrix-4.3-1-:pmax-:-:risc-:- (tnx YF)
2681.01: ultrix-4.4-0-:-:-:risc-:- (tnx RSK)
2691.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 @@
1Here's how to remove binmail from your system. Don't do this if you have
2configured qmail to use binmail for local delivery.
3
4
51. Find the binmail binary on your system: /usr/libexec/mail.local if
6 that exists, otherwise /bin/mail.
7
82. Remove permissions from the binmail binary:
9 # chmod 0 /usr/libexec/mail.local
10
113. If the binmail binary was /bin/mail, make sure that ``mail'' still
12 invokes a usable mailer. Under SVR4 you may want to link mail to
13 mailx.
14
154. Comment out the comsat line in /etc/inetd.conf, and kill -HUP your
16 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 @@
1Here's how to remove sendmail from your system.
2
31. Find sendmail in your boot scripts. It's usually in either /etc/rc or
4 /etc/init.d/sendmail. It looks like
5 sendmail -bd -q15m
6 -q15m means that it should run the queue every 15 minutes; you may
7 see a different number. Comment out this line.
8
92. Kill the sendmail daemon. You should first kill -STOP the daemon; if
10 any children are running, you should kill -CONT, wait, kill -STOP
11 again, and repeat ad nauseam. If there aren't any children, kill
12 -TERM and then kill -CONT.
13
143. Check whether you have any messages in the sendmail queue,
15 /var/spool/mqueue. If you do, you will have to try flushing them with
16 sendmail.bak -q. If necessary, wait a while and run sendmail.bak -q
17 again. Repeat until the queue is empty. This may take several days.
18
194. Remove the setuid bit on the sendmail binary, to prevent local users
20 from gaining extra privileges through sendmail's security holes. The
21 binary may be at several different locations:
22 # chmod 0 /usr/lib/sendmail
23 # chmod 0 /usr/sbin/sendmail
24 # chmod 0 /usr/lib/sendmail.mx
25
265. Move the sendmail binary out of the way:
27 # mv /usr/lib/sendmail /usr/lib/sendmail.bak
28 # 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 @@
1Background: Every few months CERT announces Yet Another Security Hole In
2Sendmail---something that lets local or even remote users take complete
3control of the machine. I'm sure there are many more holes waiting to be
4discovered; sendmail's design means that any minor bug in 46000 lines of
5code is a major security risk. Other popular mailers, such as Smail, and
6even mailing-list managers, such as Majordomo, seem nearly as bad.
7
8Note added in 1998: I wrote the above paragraph in December 1995, when
9the latest version of sendmail was 8.6.12 (with 41000 lines of code).
10Fourteen security holes were discovered from sendmail 8.6.12 through
118.8.5. See http://pobox.com/~djb/docs/maildisasters/sendmail.html.
12
13I started working on qmail because I was sick of this cycle of doom.
14Here are some of the things I did to make sure that qmail will never let
15an intruder into your machine.
16
17
181. Programs and files are not addresses. Don't treat them as addresses.
19
20sendmail treats programs and files as addresses. Obviously random people
21can't be allowed to execute arbitrary programs or write to arbitrary
22files, so sendmail goes through horrendous contortions trying to keep
23track of whether a local user was ``responsible'' for an address. This
24has proven to be an unmitigated disaster.
25
26In qmail, programs and files are not addresses. The local delivery
27agent, qmail-local, can run programs or write to files as directed by
28~user/.qmail, but it's always running as that user. (The notion of
29``user'' is configurable, but root is never a user. To prevent silly
30mistakes, qmail-local makes sure that neither ~user nor ~user/.qmail is
31group-writable or world-writable.)
32
33Security impact: .qmail, like .cshrc and .exrc and various other files,
34means that anyone who can write arbitrary files as a user can execute
35arbitrary programs as that user. That's it.
36
37
382. Do as little as possible in setuid programs.
39
40A setuid program must operate in a very dangerous environment: a user is
41under complete control of its fds, args, environ, cwd, tty, rlimits,
42timers, signals, and more. Even worse, the list of controlled items
43varies from one vendor's UNIX to the next, so it is very difficult to
44write portable code that cleans up everything.
45
46Of the twenty most recent sendmail security holes, eleven worked only
47because the entire sendmail system is setuid.
48
49Only one qmail program is setuid: qmail-queue. Its only purpose is to
50add a new mail message to the outgoing queue.
51
52
533. Do as little as possible as root.
54
55The entire sendmail system runs as root, so there's no way that its
56mistakes can be caught by the operating system's built-in protections.
57In contrast, only two qmail programs, qmail-start and qmail-lspawn,
58run as root.
59
60
614. Move separate functions into mutually untrusting programs.
62
63Five of the qmail programs---qmail-smtpd, qmail-send, qmail-rspawn,
64qmail-remote, and tcp-env---are not security-critical. Even if all of
65these programs are completely compromised, so that an intruder has
66control over the qmaild, qmails, and qmailr accounts and the mail queue,
67he still can't take over your system. None of the other programs trust
68the results from these five.
69
70In fact, these programs don't even trust each other. They are in three
71groups: tcp-env and qmail-smtpd, which run as qmaild; qmail-rspawn and
72qmail-remote, which run as qmailr; and qmail-send, the queue manager,
73which runs as qmails. Each group is immune from attacks by the others.
74
75(From root's point of view, as long as root doesn't send any mail, only
76qmail-start and qmail-lspawn are security-critical. They don't write any
77files or start any other programs as root.)
78
79
805. Don't parse.
81
82I have discovered that there are two types of command interfaces in the
83world of computing: good interfaces and user interfaces.
84
85The essence of user interfaces is _parsing_---converting an unstructured
86sequence of commands, in a format usually determined more by psychology
87than by solid engineering, into structured data.
88
89When another programmer wants to talk to a user interface, he has to
90_quote_: convert his structured data into an unstructured sequence of
91commands that the parser will, he hopes, convert back into the original
92structured data.
93
94This situation is a recipe for disaster. The parser often has bugs: it
95fails to handle some inputs according to the documented interface. The
96quoter often has bugs: it produces outputs that do not have the right
97meaning. Only on rare joyous occasions does it happen that the parser
98and the quoter both misinterpret the interface in the same way.
99
100When the original data is controlled by a malicious user, many of these
101bugs translate into security holes. Some examples: the Linux login
102-froot security hole; the classic find | xargs rm security hole; the
103Majordomo injection security hole. Even a simple parser like getopt is
104complicated enough for people to screw up the quoting.
105
106In qmail, all the internal file structures are incredibly simple: text0
107lines beginning with single-character commands. (text0 format means that
108lines are separated by a 0 byte instead of line feed.) The program-level
109interfaces don't take options.
110
111All the complexity of parsing RFC 822 address lists and rewriting
112headers is in the qmail-inject program, which runs without privileges
113and is essentially part of the UA.
114
115
1166. Keep it simple, stupid.
117
118See BLURB for some of the reasons that qmail is so much smaller than
119sendmail. There's nothing inherently complicated about writing a mailer.
120(Except RFC 822 support; but that's only in qmail-inject.) Security
121holes can't show up in features that don't exist.
122
123
1247. Write bug-free code.
125
126I've mostly given up on the standard C library. Many of its facilities,
127particularly stdio, seem designed to encourage bugs. A big chunk of
128qmail is stolen from a basic C library that I've been developing for
129several years for a variety of applications. The stralloc concept and
130getln() make it very easy to avoid buffer overruns, memory leaks, and
131artificial line length limits.
diff --git a/SENDMAIL b/SENDMAIL
new file mode 100644
index 0000000..9280c24
--- /dev/null
+++ b/SENDMAIL
@@ -0,0 +1,76 @@
1This document explains what you, as a user, will notice when the system
2switches from sendmail to qmail.
3
4This is a global document, part of the qmail package, not reflecting the
5decisions made by your system administrator. For details on
6
7 * which local delivery agent qmail is configured to use,
8 * whether qmail is configured to use dot-forward,
9 * whether ezmlm is installed,
10 * whether fastforward is installed, and
11 * all other local configuration features,
12
13see your local sendmail-qmail upgrade announcement (which your system
14administrator may have placed into /var/qmail/doc/ANNOUNCE).
15
16
17--- Mailbox location
18
19If your system administrator has configured qmail to use binmail for
20local deliveries, your mailbox will be in /var/spool/mail/you, just as
21it was under sendmail.
22
23If your system administrator has configured qmail to use qmail-local for
24local deliveries, your mailbox will be moved to ~you/Mailbox. There is a
25symbolic link from /var/spool/mail/you to ~you/Mailbox, so your mail
26reader will find the mailbox at its new location.
27
28
29--- Loop control
30
31qmail-local automatically adds a Delivered-To field at the top of every
32delivered message. It uses Delivered-To to prevent mail forwarding
33loops, including cross-host mailing-list loops.
34
35
36--- Outgoing messages
37
38qmail lets you use environment variables to control the appearance of
39your outgoing mail, supplementing the features offered by your MUA. For
40example, qmail-inject will set up Mail-Followup-To for you automatically
41if you tell it which mailing lists you are subscribed to. See
42qmail-inject(8) for a complete list of features.
43
44If you're at (say) sun.ee.movie.edu, qmail lets you type joe@mac for
45joe@mac.ee.movie.edu, and joe@mac+ for joe@mac.movie.edu without the ee.
46sendmail has a different interpretation of hostnames without dots.
47
48
49--- Forwarding and mailing lists
50
51qmail gives you the power to set up your own mailing lists without
52pestering your system administrator.
53
54Under qmail, you are in charge of all addresses of the form
55you-anything. The delivery of you-anything is controlled by
56~you/.qmail-anything, a file in your home directory.
57
58For example, if you want to set up a bug-of-the-month-club mailing list,
59you can put a list of addresses into ~you/.qmail-botmc. Any mail to
60you-botmc will be forwarded to all of those addresses. Mail directly to
61you is controlled by ~you/.qmail. You can even set up a catch-all,
62~you/.qmail-default, to handle unknown you- addresses.
63
64See dot-qmail(5) for the complete story. Beware that the syntax of
65.qmail is different from the syntax of sendmail's .forward file.
66
67If your system administrator has configured qmail to use the dot-forward
68compatibility tool, you can put forwarding addresses (and programs) into
69.forward the same way you did with sendmail.
70
71If your system administrator has installed ezmlm, you can use ezmlm-make
72to instantly set up a professional-quality mailing list, handling
73subscriptions and archives automatically.
74
75If your system administrator has installed fastforward, you can easily
76manage 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 @@
1VERSION
2systype
3hasshsgr.h
4hasnpbg1.h
5select.h
6hasflock.h
7hassalen.h
8fork.h
9hassgact.h
10direntry.h
11hassgprm.h
12haswaitp.h
13hasmkffo.h
14uint32.h
15dns.lib
16socket.lib
17syslog.lib
diff --git a/TARGETS b/TARGETS
new file mode 100644
index 0000000..facdad7
--- /dev/null
+++ b/TARGETS
@@ -0,0 +1,387 @@
1auto-ccld.sh
2make-load
3find-systype
4systype
5load
6make-compile
7compile
8fork.h
9qmail-local.o
10qmail.o
11quote.o
12now.o
13gfrom.o
14myctime.o
15slurpclose.o
16make-makelib
17makelib
18case_diffb.o
19case_diffs.o
20case_lowerb.o
21case_lowers.o
22case_starts.o
23case.a
24getln.o
25getln2.o
26getln.a
27subgetopt.o
28sgetopt.o
29getopt.a
30sig_alarm.o
31hassgprm.h
32sig_block.o
33hassgact.h
34sig_catch.o
35sig_pause.o
36sig_pipe.o
37sig_child.o
38sig_hup.o
39sig_term.o
40sig_bug.o
41sig_misc.o
42sig.a
43open_append.o
44open_excl.o
45open_read.o
46open_trunc.o
47open_write.o
48open.a
49seek_cur.o
50seek_end.o
51seek_set.o
52seek_trunc.o
53seek.a
54hasflock.h
55lock_ex.o
56lock_exnb.o
57lock_un.o
58lock.a
59fd_copy.o
60fd_move.o
61fd.a
62haswaitp.h
63wait_pid.o
64wait_nohang.o
65wait.a
66env.o
67envread.o
68env.a
69stralloc_eady.o
70stralloc_pend.o
71stralloc_copy.o
72stralloc_opys.o
73stralloc_opyb.o
74stralloc_cat.o
75stralloc_cats.o
76stralloc_catb.o
77stralloc_arts.o
78stralloc.a
79alloc.o
80alloc_re.o
81alloc.a
82strerr_sys.o
83strerr_die.o
84strerr.a
85substdio.o
86substdi.o
87substdo.o
88subfderr.o
89subfdout.o
90subfdouts.o
91subfdin.o
92subfdins.o
93substdio_copy.o
94substdio.a
95error.o
96error_str.o
97error_temp.o
98error.a
99str_len.o
100str_diff.o
101str_diffn.o
102str_cpy.o
103str_chr.o
104str_rchr.o
105str_start.o
106byte_chr.o
107byte_rchr.o
108byte_diff.o
109byte_copy.o
110byte_cr.o
111byte_zero.o
112str.a
113fmt_str.o
114fmt_strn.o
115fmt_uint.o
116fmt_uint0.o
117fmt_ulong.o
118scan_ulong.o
119scan_8long.o
120fs.a
121datetime.o
122datetime_un.o
123datetime.a
124auto-str.o
125auto-str
126auto_qmail.c
127auto_qmail.o
128auto-int8.o
129auto-int8
130auto_patrn.c
131auto_patrn.o
132socket.lib
133qmail-local
134uint32.h
135qmail-lspawn.o
136select.h
137chkspawn.o
138auto-int.o
139auto-int
140auto_spawn.c
141auto_spawn.o
142chkspawn
143spawn.o
144chkshsgr.o
145chkshsgr
146hasshsgr.h
147prot.o
148coe.o
149cdb_hash.o
150cdb_unpack.o
151cdb_seek.o
152cdb.a
153auto-uid.o
154auto-uid
155auto-gid.o
156auto-gid
157auto_uids.c
158auto_uids.o
159qmail-lspawn
160qmail-getpw.o
161auto_break.c
162auto_break.o
163auto_usera.c
164auto_usera.o
165qmail-getpw
166qmail-remote.o
167control.o
168constmap.o
169timeoutread.o
170timeoutwrite.o
171timeoutconn.o
172tcpto.o
173dns.o
174ip.o
175ipalloc.o
176hassalen.h
177ipme.o
178ndelay.o
179ndelay_off.o
180ndelay.a
181dns.lib
182qmail-remote
183qmail-rspawn.o
184tcpto_clean.o
185qmail-rspawn
186direntry.h
187qmail-clean.o
188fmtqfn.o
189auto_split.c
190auto_split.o
191qmail-clean
192qmail-send.o
193qsutil.o
194newfield.o
195prioq.o
196hasmkffo.h
197fifo.o
198hasnpbg1.h
199trigger.o
200readsubdir.o
201date822fmt.o
202qmail-send
203qmail-start.o
204qmail-start
205splogger.o
206syslog.lib
207splogger
208qmail-queue.o
209triggerpull.o
210qmail-queue
211qmail-inject.o
212headerbody.o
213hfield.o
214token822.o
215qmail-inject
216predate.o
217predate
218datemail
219mailsubj
220qmail-upq
221qmail-showctl.o
222qmail-showctl
223qmail-newu.o
224cdbmss.o
225cdbmake_pack.o
226cdbmake_hash.o
227cdbmake_add.o
228cdbmake.a
229qmail-newu
230qmail-pw2u.o
231qmail-pw2u
232qmail-qread.o
233qmail-qread
234qmail-qstat
235qmail-tcpto.o
236qmail-tcpto
237qmail-tcpok.o
238qmail-tcpok
239qmail-pop3d.o
240commands.o
241maildir.o
242qmail-pop3d
243qmail-popup.o
244qmail-popup
245qmail-qmqpc.o
246qmail-qmqpc
247qmail-qmqpd.o
248received.o
249qmail-qmqpd
250qmail-qmtpd.o
251rcpthosts.o
252qmail-qmtpd
253qmail-smtpd.o
254qmail-smtpd
255sendmail.o
256sendmail
257tcp-env.o
258remoteinfo.o
259tcp-env
260qmail-newmrh.o
261qmail-newmrh
262config
263config-fast
264dnscname.o
265dnsdoe.o
266dnscname
267dnsptr.o
268dnsptr
269dnsip.o
270dnsip
271dnsmxip.o
272dnsmxip
273dnsfq.o
274dnsfq
275hostname.o
276hostname
277ipmeprint.o
278ipmeprint
279qreceipt.o
280qreceipt
281qsmhook.o
282qsmhook
283qbiff.o
284qbiff
285forward.o
286forward
287preline.o
288preline
289condredirect.o
290condredirect
291bouncesaying.o
292bouncesaying
293except.o
294except
295maildirmake.o
296maildirmake
297maildir2mbox.o
298maildir2mbox
299maildirwatch.o
300maildirwatch
301qail
302elq
303pinq
304idedit.o
305idedit
306install-big.o
307install.o
308install-big
309hier.o
310install
311instcheck.o
312instcheck
313home
314home+df
315proc
316proc+df
317binm1
318binm1+df
319binm2
320binm2+df
321binm3
322binm3+df
323it
324qmail-local.0
325qmail-lspawn.0
326qmail-getpw.8
327qmail-getpw.0
328qmail-remote.0
329qmail-rspawn.0
330qmail-clean.0
331qmail-send.8
332qmail-send.0
333qmail-start.8
334qmail-start.0
335splogger.0
336qmail-queue.0
337qmail-inject.0
338mailsubj.0
339qmail-showctl.0
340qmail-newu.8
341qmail-newu.0
342qmail-pw2u.8
343qmail-pw2u.0
344qmail-qread.0
345qmail-qstat.0
346qmail-tcpto.0
347qmail-tcpok.0
348qmail-pop3d.0
349qmail-popup.0
350qmail-qmqpc.0
351qmail-qmqpd.0
352qmail-qmtpd.0
353qmail-smtpd.0
354tcp-env.0
355qmail-newmrh.8
356qmail-newmrh.0
357qreceipt.0
358qbiff.0
359forward.0
360preline.0
361condredirect.0
362bouncesaying.0
363except.0
364maildirmake.0
365maildir2mbox.0
366maildirwatch.0
367qmail.0
368qmail-limits.7
369qmail-limits.0
370qmail-log.0
371qmail-control.5
372qmail-control.0
373qmail-header.0
374qmail-users.5
375qmail-users.0
376dot-qmail.5
377dot-qmail.0
378qmail-command.0
379tcp-environ.0
380maildir.0
381mbox.0
382addresses.0
383envelopes.0
384forgeries.0
385man
386setup
387check
diff --git a/TEST.deliver b/TEST.deliver
new file mode 100644
index 0000000..4fc4c32
--- /dev/null
+++ b/TEST.deliver
@@ -0,0 +1,82 @@
1You can do several tests of qmail delivery without setting up qmail to
2accept messages through SMTP or through /usr/lib/sendmail:
3
41. After you start qmail, look for a
5 qmail: status: local 0/10 remote 0/20
6 line in syslog. qmail-send always prints either ``cannot start'' or
7 ``status''. (The big number is a splogger timestamp.)
8
92. Do a ps and look for the qmail daemons. There should be four of
10 them, all idle: qmail-send, running as qmails; qmail-lspawn, running
11 as root; qmail-rspawn, running as qmailr; and qmail-clean, running
12 as qmailq. You will also see splogger, running as qmaill.
13
143. Local-local test: Send yourself an empty message. (Replace ``me''
15 with your username. Make sure to include the ``to:'' colon.)
16 % echo to: me | /var/qmail/bin/qmail-inject
17 The message will show up immediately in your mailbox, and syslog
18 will show something like this:
19 qmail: new msg 53
20 qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
21 qmail: starting delivery 1: msg 53 to local me@domain
22 qmail: status: local 1/10 remote 0/20
23 qmail: delivery 1: success: did_1+0+0/
24 qmail: status: local 0/10 remote 0/20
25 qmail: end msg 53
26 (53 is an inode number; 20345 is a process ID; your numbers will
27 probably be different.)
28
294. Local-error test: Send a message to a nonexistent local address.
30 % echo to: nonexistent | /var/qmail/bin/qmail-inject
31 qmail: new msg 53
32 qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
33 qmail: starting delivery 2: msg 53 to local nonexistent@domain
34 qmail: status: local 1/10 remote 0/20
35 qmail: delivery 2: failure: No_such_address.__#5.1.1_/
36 qmail: status: local 0/10 remote 0/20
37 qmail: bounce msg 53 qp 20357
38 qmail: end msg 53
39 qmail: new msg 54
40 qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
41 qmail: starting delivery 3: msg 54 to local me@domain
42 qmail: status: local 1/10 remote 0/20
43 qmail: delivery 3: success: did_1+0+0/
44 qmail: status: local 0/10 remote 0/20
45 qmail: end msg 54
46 You will now have a bounce message in your mailbox.
47
485. Local-remote test: Send an empty message to your account on another
49 machine.
50 % echo to: me@wherever | /var/qmail/bin/qmail-inject
51 qmail: new msg 53
52 qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
53 qmail: starting delivery 4: msg 53 to remote me@wherever
54 qmail: status: local 0/10 remote 1/20
55 qmail: delivery 4: success: 1.2.3.4_accepted_message./...
56 qmail: status: local 0/10 remote 0/20
57 qmail: end msg 53
58 There will be a pause between ``starting delivery'' and ``success'';
59 SMTP is slow. Check that the message is in your mailbox on the other
60 machine.
61
626. Local-postmaster test: Send mail to postmaster, any capitalization.
63 % echo to: POSTmaster | /var/qmail/bin/qmail-inject
64 Look for the message in the alias mailbox, normally ~alias/Mailbox.
65
667. Double-bounce test: Send a message with a completely bad envelope.
67 % /var/qmail/bin/qmail-inject -f nonexistent
68 To: unknownuser
69 Subject: testing
70
71 This is a test. This is only a test.
72 %
73 (Use end-of-file, not dot, to end the message.) Look for the double
74 bounce in the alias mailbox.
75
768. Group membership test:
77 % cat > ~me/.qmail-groups
78 |groups >> MYGROUPS; exit 0
79 % /var/qmail/bin/qmail-inject me-groups < /dev/null
80 % cat ~me/MYGROUPS
81 MYGROUPS will show your normal gid and nothing else. (Under Solaris,
82 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 @@
1You can do several tests of messages entering the qmail system:
2
31. SMTP server test: Forge some mail locally via SMTP. Replace ``me''
4 with your username and ``domain'' with your host's name.
5 % telnet 127.0.0.1 25
6 Trying 127.0.0.1...
7 Connected to 127.0.0.1.
8 Escape character is '^]'.
9 220 domain ESMTP
10 helo dude
11 250 domain
12 mail <me@domain>
13 250 ok
14 rcpt <me@domain>
15 250 ok
16 data
17 354 go ahead
18 Subject: testing
19
20 This is a test.
21 .
22 250 ok 812345679 qp 12345
23 quit
24 221 domain
25 Connection closed by foreign host.
26 %
27 Look for the message in your mailbox. (Note for programmers: Most
28 SMTP servers need more text after MAIL and RCPT. See RFC 821.)
29
302. Remote-local test: Send yourself some mail from another machine.
31 Look for the message in your mailbox.
32
333. Remote-error test: Send some mail from another machine to
34 nonexistent@domain. Look for a bounce message in the remote mailbox.
35
364. UA test: Try sending mail, first to a local account, then to a
37 remote account, with your normal user agent.
38
395. Remote-postmaster test: Send mail from another machine to
40 PoStMaStEr@domain. Look for the message in the alias mailbox,
41 normally ~alias/Mailbox.
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..b1ad88e
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,337 @@
1Thanks to lots of people for success and failure reports, code, ideas,
2and documentation. See CHANGES for details of specific contributions.
3Sorry if I left anyone out.
4
5A2B = Are Bryne
6A2L = Ali Lomonaco
7A2P = Andrea Paolini
8AAF = Adam A. Frey
9AB = Alan Briggs
10ABC = Alan B. Clegg
11AC = Arne Coucheron
12ACB = Andy C. Brandt
13AF = Andreas Faerber
14AG = Armin Gruner
15AGB = Andre Grosse Bley
16AH = Amos Hayes
17AI = Akihiro Iijima
18AJ = Alan Jaffray
19AJK = Antti-Juhani Kaijanaho
20AKB = Allen K. Briggs
21AL = Andreas Lamprecht
22ALB = Allan L. Bazinet
23ANR = Adriano Nagelschmidt Rodrigues
24AP = Andrew Pam
25AS = Akos Szalkai
26AV = Alex Vostrikov
27AWB = Andy W. Barclay
28AY = Araki Yasuhiro
29B1F = Bo Fussing
30B2F = Brad Forschinger
31B2H = Buck Huppmann
32B2L = Brent Laminack
33B2W = Bil Wendling
34B3W = Boris Wedl
35BB = Bruce Bodger
36BC = Bob Collie
37BCK = Benjamin C. Kite
38BCM = Bill C. Miller
39BDB = Boris D. Beletsky
40BDM = Byron D. Miller
41BEO = Bruce E. O'Neel
42BET = Bennett E. Todd
43BG = Bert Gijsbers
44BH = Brad Howes
45BJ = Brian Jackson
46BJM = Barry J. Miller
47BL = Brian Litzinger
48BMF = Brian M. Fisk
49BN = Bill Nugent
50BP = Bruce Perens
51BR = Brian J. Reichert
52BS = Bjoern Stabell
53BT = Brad Templeton
54BTW = Brian T. Wightman
55BW = Bill Weinman
56BZ = Blaz Zupan
57C2F = Chuck Foster
58C2H = Christoph Heidermanns
59C2S = Craig Shrimpton
60CEJ = Colin Eric Johnson
61CF = C. Ferree
62CG = Chris Garrigues
63CH = Chael Hall
64CHR = Craig H. Rowland
65CK = Christoph Kaesling
66CL = Carsten Leonhardt
67CLS = Christopher L. Seawood
68CM = Charles Mattair
69CMP = Chase M. Phillips
70CR = Christian Riede
71CS = Cloyce Spradling
72CSH = Clayton S. Haapala
73D1H = Dieter Heidner
74D2H = Dan Hollis
75D2K = Dax Kelson
76D2S = Dan Senie
77D3S = Don Samek
78DA = Dave Arcuri
79DAR = Daniel A. Reish
80DB = David Buscher
81DBK = Douglas B. Kerry
82DC = Dan Cross
83DCC = Daniel C. Cotey
84DE = Daniel Egnor
85DEH = Daniel E. Harris
86DF = Dale Farnsworth
87DG = David Guntner
88DK = Dave Kopper
89DL = Daniel Lawrence
90DM = David Mazieres
91DML = David M. Lew
92DP = Dave Platt
93DS = Dave Sill
94DST = Daniel S. Thibadeau
95DWS = David Wayne Summers
96EC = Evan Champion
97ECG = Eric C. Garrison
98EG = Eivind Gjelseth
99EK = Eric Krohn
100EP = Emanuele Pucciarelli
101ERH = Eric R. Hankins
102ES = Eric Smith
103ESM = Edward S. Marshall
104ET = Eivind Tagseth
105ETT = Emmanuel T. Tardieu
106F2T = Frank Thieme
107FE = Frank Ederveen
108FN = Faried Nawaz
109FPL = Frederik P. Lindberg
110FT = Frank Tegtmeyer
111FW = Frank Wagner
112G1A = Graham Adams
113G2A = Greg Andrews
114GAW = Greg A. Woods
115GB = Glenn Barry
116GH = Gene Hightower
117GL = Giles Lean
118GLM = Grant L. Miller
119H2S = Harley Silver
120HCJ = Helio Coelho Jr.
121HDG = Hans de Graaff
122HG = Howard Goldstein
123HHO = Harald Hanche-Olsen
124HJB = Herbert J. Bernstein
125HM = Hirokazu Morikawa
126HS = Harlan Stenn
127HT = Henry Timmerman
128HW = Hal Wine
129HWM = Henry W. Miller
130IH = Ingmar Hupp
131IK = Ivan Kohler
132IKW = Ian Keith Wynne
133IS = Icarus Sparry
134IW = Ian Westcott
135J1B = John Banghart
136J1K = Jost Krieger
137J2B = Jos Backus
138J2K = Johannes Kroeger
139J2M = Joel Maslak
140J2P = John Parker
141J2W = Jim Whitby
142JAB = Jeremy A. Bussard
143JAK = Johan A. Kullstam
144JB = Joshua Buysse
145JBB = Jason B. Brown
146JBF = John B. Fleming
147JC = Jim Clausing
148JCD = Jeffrey C. Dege
149JD = Joe Doupnik
150JDHB = Johannes D. H. Beekhuizen
151JDJ = Joshua D. Juran
152JF = Janos Farkas
153JFK = James F. Kane III
154JGM = John G. Myers
155JJB = J. J. Bailey
156JJMK = Jonathan J. M. Katz
157JJR = Jaron J. Rubenstein
158JK = Jari Kirma
159JL = Jim Littlefield
160JLB = Julie L. Baumler
161JLH = Jason L. Haar
162JLW = Jason L. Wright
163JM = Jim Meehan
164JMS = Jason M. Stokes
165JMT = John M. Twilley
166JP = John Palkovic
167JPB = Joe Block
168JPH = Justin P. Hannah
169JPR = Jean-Pierre Radley
170JRL = John R. Levine
171JRM = Jason R. Mastaler
172JRY = Jamie R. Yukes
173JS = Jesper Skriver
174JTB = Jonathan T. Bowie
175JW = John Whittaker
176JWB = James W. Birdsall
177K1J = Kyle Jones
178K2J = Kevin Johnson
179KA = Klaus Aigte
180KB = Keith Burdis
181KE = Kenny Elliott
182KJJ = Kevin J. Johnson
183KJS = Kevin J. Sawyer
184KMD = Kevin M. Dulzo
185KO = Keith Owens
186KR = Kenji Rikitake
187KT = Karsten Thygesen
188KUT = Kai Uwe Tempel
189KY = Kentaro Yoshitomi
190L2L = Louis Larry
191L3L = Luis Lopes
192LB = Laurentiu Badea
193LL = lilo
194LW = Lionel Widdifield
195M2C = Mark Crimmins
196M2G = Michael R. Gile
197M2H = Martin Hager
198M2L = M. Lyons
199M2R = Mark Riekenberg
200M2S = Mikael Suokas
201M3H = Michael Holzt
202M3L = Michael Lazarou
203M3S = Morten Skjelland
204M4S = Michael Shields
205MB = Martin Budsj?
206MBS = Michael B. Scher
207MC = Michael Cooley
208MD = Mark Delany
209MDI = Miguel de Icaza
210ME = Marc Ewing
211MEE = Mads E. Eilertsen
212MF = Massimo Fusaro
213MG = Michael Graff
214MGM = Mitchell G. Morris
215MH = Markus Hofmann
216MJD = Mark-Jason Dominus
217MJG = Manuel J. Galan
218ML = Martin Lucina
219MLH = May Liss Haarstad
220MM = Martin Mersberger
221MMM = Momchil M. Momchev
222MMM2 = Marc M. Martinez
223MP = Matt Paduano
224MR = Mosfeq Rashid
225MRG = Matthew R. Green
226MS = Mark Spears
227MSD = Mandell S. Degerness
228MSS = Matthew S. Soffen
229MT = Mark Thompson
230MW = Mate Wierdl
231MWE = Mark W. Eichin
232NA = Norm Aleks
233NAA = Nicholas A. Amato
234NH = Nick Holloway
235NND = N. Dudorov
236NR = Norbert Roeding
237NW = Nicholas Waples
238OK = Oezguer Kesim
239OR = Ollivier Robert
240OS = Oliver Seiler
241PB = Peter Bowyer
242PCO = Peter C. Olsen
243PGF = Paul Fox
244PGR = Phil G. Rorex
245PH = Paul Harrington
246PJG = Paul Graham
247PJH = Peter J. Hunter
248PK = Petri Kaukasoina
249PMH = Peter M. Haworth
250PO = Paul Overell
251PS = Paul Svensson
252PT = Paul Taylor
253PTW = P. T. Withington
254PW = Peter Wilkinson
255R2N = Rivo Nurges
256RA = Russ Allbery
257RAB = Randolph Allen Bentson
258RAM = Robin A. McCollum
259RB = Robert Bridgham
260RC = Ryan Crum
261RD = Rahul Dhesi
262RDM = Raul D. Miller
263REB = Ronald E. Bickers
264RF = Rainer Fraedrich
265RFH = Robert F. Harrison
266RGS = Richard G. Sharman
267RJC = Robert J. Carter
268RJH = Randy Harmon
269RJO = Richard J. Ohnemus
270RK = Riho Kurg
271RL = Robert Luce
272RM = Rich McClellan
273RN = Russell Nelson
274RO = Roberto Oppedisano
275RPS = Russell P. Sutherland
276RS = Robert Sanders
277RSK = Robert S. Krzaczek
278S1R = Satish Ramachandran
279S2P = Stefan Puscasu
280S2R = Sean Reifschneider
281S2S = Scott Schwartz
282S2T = Steve Taylor
283S3T = Steffen Thorsen
284SA = Satoshi Adachi
285SAE = Stefaan A. Eeckels
286SAS = Steven A. Schrader
287SB = Stephane Bortzmeyer
288SC = Stefan Cars
289SCW = Steven C. Work
290SG = Steven Grimm
291SGC = Stephen G. Comings
292SJ = Sudish Joseph
293SJB = SJ Burns
294SJW = Stephen J. White
295SLB = Steven L. Baur
296SM = Shawn McHorse
297SP = Stephen Parker
298SPM = Salvatore P. Miccicke
299SS = Simon Shapiro
300SSB = Stik Bakken
301ST = Steve Tylock
302SV = Sven Velt
303SVD = Stef Van Dessel
304T2K = Tomoya Konishi
305T2M = Toni Mueller
306T2U = Todd Underwood
307TA = Tetsuo Aoki
308TB = Tobias Brox
309TD = Tom Demmer
310TEE = Thomas E. Erskine
311TG = Tim Goodwin
312TH = Ton Hospel
313TJH = Timothy J. Hunt
314TK = Terry Kennedy
315TL = Timothy Lorenc
316TLF = Timo L. Felbinger
317TLM = Timothy L. Mayo
318TM = Toshinori Maeno
319TN = Thomas Neumann
320TRR = Tracy R. Reed
321TT = Takaki Taniguchi
322TU = Tetsu Ushijima
323TV = Tommi Virtanen
324TVP = Tom van Peer
325UO = Uwe Ohse
326VBM = Vladimir B. Machulsky
327VR = Vincenzo Romano
328VU = Viriya Upatising
329VV = Vince Vielhaber
330W2K = Wolfram Kahl
331WEB = William E. Baxter
332WK = Werner Koch
333WS = Wilbur Sims
334WW = Wei Wu
335YC = Yuji Chikahiro
336YF = Yaroslav Faybishenko
337ZU = Zin Uda
diff --git a/THOUGHTS b/THOUGHTS
new file mode 100644
index 0000000..d6910da
--- /dev/null
+++ b/THOUGHTS
@@ -0,0 +1,418 @@
1Please note that this file is not called ``Internet Mail For Dummies.''
2It _records_ my thoughts on various issues. It does not _explain_ them.
3Paragraphs are not organized except by section. The required background
4varies wildly from one paragraph to the next.
5
6In this file, ``sendmail'' means Allman's creation; ``sendmail-clone''
7means the program in this package.
8
9
101. Security
11
12There are lots of interesting remote denial-of-service attacks on any
13mail system. A long-term solution is to insist on prepayment for
14unauthorized resource use. The tricky technical problem is to make the
15prepayment enforcement mechanism cheaper than the expected cost of the
16attacks. (For local denial-of-service attacks it's enough to be able to
17figure out which user is responsible.)
18
19qmail-send's log was originally designed for profiling. It subsequently
20sprouted some tracing features. However, there's no way to verify
21securely that a particular message came from a particular local user;
22how do you know the recipient is telling you the truth about the
23contents of the message? With QUEUE_EXTRA it'd be possible to record a
24one-way hash of each outgoing message, but a user who wants to send
25``bad'' mail can avoid qmail entirely.
26
27I originally decided on security grounds not to put qmail advertisements
28into SMTP responses: advertisements often act as version identifiers.
29But this problem went away when I found a stable qmail URL.
30
31As qmail grows in popularity, the mere knowledge that rcpthosts is so
32easily available will deter people from setting up unauthorized MXs.
33(I've never seen an unauthorized MX, but I can imagine that it would be
34rather annoying.) Note that, unlike the bat book checkcompat() kludge,
35rcpthosts doesn't interfere with mailing lists.
36
37qmail-start doesn't bother with tty dissociation. On some old machines
38this means that random people can send tty signals to the qmail daemons.
39That's a security flaw in the job control subsystem, not in qmail.
40
41The resolver library isn't too bloated (before 4.9.4, at least), but it
42uses stdio, which _is_ bloated. Reading /etc/resolv.conf costs lots of
43memory in each qmail-remote process. So it's tempting to incorporate a
44smaller resolver library into qmail. (Bonus: I'd avoid system-specific
45problems with old resolvers.) The problem is that I'd then be writing a
46fundamentally insecure library. I'd no longer be able to blame the BIND
47authors and vendors for the fact that attackers can easily use DNS to
48steal mail. Solution: insist that the resolver run on the same host; the
49kernel can guarantee the security of low-numbered 127.0.0.1 UDP ports.
50
51NFS is the primary enemy of security partitioning under UNIX. Here's the
52story. Sun knew from the start that NFS was completely insecure. It
53tried to hide that fact by disallowing root access over NFS. Intruders
54nevertheless broke into system after system, first obtaining bin access
55and then obtaining root access. Various people thus decided to compound
56Sun's error and build a wall between root and all other users: if all
57system files are owned by root, and if there are no security holes other
58than NFS, someone who breaks in via NFS won't be able to wipe out the
59operating system---he'll merely be able to wipe out all user files. This
60clueless policy means that, for example, all the qmail users have to be
61replaced by root. See what I mean by ``enemy''? ... Basic NFS comments:
62Aside from the cryptographic problem of having hosts communicate
63securely, it's obvious that there's an administrative problem of mapping
64client uids to server uids. If a host is secure and under your control,
65you shouldn't have to map anything. If a host is under someone else's
66control, you'll want to map his uids to one local account; it's his
67client's job to decide which of his users get to talk NFS in the first
68place. Sun's original map---root to nobody, everyone else left alone---
69is, as far as I can tell, always wrong.
70
71
722. Injecting mail locally (qmail-inject, sendmail-clone)
73
74RFC 822 section 3.4.9 prohibits certain visual effects in headers, and
75the 822bis draft prohibits even more. qmail-inject could enforce these
76absurd restrictions, but why waste the time? If you will suffer from
77someone sending you ``flash mail,'' go find a better mail reader.
78
79qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops
80sendmail from adding Apparently-To. Unfortunately, old versions of
81sendmail will append a host name. This wasn't fixed until sendmail 8.7.
82How many years has it been since RFC 822 came out?
83
84sendmail discards duplicate addresses. This has probably resulted in
85more lost and stolen mail over the years than the entire Chicago branch
86of the United States Postal Service. The qmail system delivers messages
87exactly as it's told to do. Along the same lines: qmail-inject is both
88unable and unwilling to support anything like sendmail's (default)
89nometoo option. Of course, a list manager could support nometoo.
90
91There should be a mechanism in qmail-inject that does for envelope
92recipients what Return-Path does for the envelope sender. Then
93qmail-inject -n could print the recipients.
94
95Should qmail-inject bounce messages with no recipients? Should there be
96an option for this? If it stays as is (accept the message), qmail-inject
97could at least avoid invoking qmail-queue.
98
99It is possible to extract non-unique Message-IDs out of qmail-inject.
100Here's how: stop qmail-inject before it gets to the third line of
101main(), then wait until the pids wrap around, then restart qmail-inject
102and blast the message through, then start another qmail-inject with the
103same pid in the same second. I'm not sure how to fix this without
104system-supplied sequence numbers. (Of course, the user could just type
105in his own non-unique Message-IDs.)
106
107The bat book says: ``Rules that hide hosts in a domain should be applied
108only to sender addresses.'' Recipient masquerading works fine with
109qmail. None of sendmail's pitfalls apply, basically because qmail has a
110straight paper path.
111
112I predicted that I would receive some pressure to make up for the
113failings of MUA writers who don't understand the concept of reliability.
114(``Like, duh, you mean I'm supposed to check the sendmail exit code?'')
115I was right.
116
117
1183. Receiving mail from the network (tcp-env, qmail-smtpd)
119
120qmail-smtpd doesn't allow privacy-invading commands like VRFY and EXPN.
121If you really want to publish such information, use a mechanism that
122legitimate users actually know about, such as fingerd or httpd.
123
124RFC 1123 says that VRFY and EXPN are important to track down cross-host
125mailing list loops. With Delivered-To, mailing list loops do no damage,
126_and_ one of the list administrators gets a bounce message that shows
127exactly how the loop occurred. Solve the problem, not the symptom.
128
129Should dns.c make special allowances for 127.0.0.1/localhost?
130
131badmailfrom (like 8BITMIME) is a waste of code space.
132
133In theory a MAIL or RCPT argument can contain unquoted LFs. In practice
134there are a huge number of clients that terminate commands with just LF,
135even if they use CR properly inside DATA.
136
137
1384. Adding messages to the queue (qmail-queue)
139
140Should qmail-queue try to make sure enough disk space is free in
141advance? When qmail-queue is invoked by qmail-local or (with ESMTP)
142qmail-smtpd or qmail-qmtpd or qmail-qmqpd, it could be told a size in
143advance. I wish UNIX had an atomic allocate-disk-space routine...
144
145The qmail.h interface (reflecting the qmail-queue interface, which in
146turn reflects the current queue file structure) is constitutionally
147incapable of handling an address that contains a 0 byte. I can't imagine
148that this will be a problem.
149
150Should qmail-queue not bother queueing a message with no recipients?
151
152
1535. Handling queued mail (qmail-send, qmail-clean)
154
155The queue directory must be local. Mounting it over NFS is extremely
156dangerous---not that this stops people from running sendmail that way!
157Diskless hosts should use mini-qmail instead.
158
159Queue reliability demands that single-byte writes be atomic. This is
160true for a fixed-block filesystem such as UFS, and for a logging
161filesystem such as LFS.
162
163qmail-send uses 8 bytes of memory per queued message. Double that for
164reallocation. (Fix: use a small forest of heaps; i.e., keep several
165prioqs.) Double again for buddy malloc()s. (Fix: be clever about the
166heap sizes.) 32 bytes is worrisome, but not devastating. Even on my
167disk-heavy memory-light machine, I'd run out of inodes long before
168running out of memory.
169
170Some mail systems organize the queue by host. This is pointless as a
171means of splitting up the queue directory. The real issue is what to do
172when you suddenly find out that a host is up. For local SLIP/PPP links
173you know in advance which hosts need this treatment, so you can handle
174them with virtualdomains and serialmail.
175
176For the old queue structure I implemented recipient list compression:
177if mail goes out to a giant mailing list, and most of the recipients are
178delivered, make a new, compressed, todo list. But this really isn't
179worth the effort: it saves only a tiny bit of CPU time.
180
181qmail-send doesn't have any notions of precedence, priority, fairness,
182importance, etc. It handles the queue in first-seen-first-served order.
183One could put a lot of work into doing something different, but that
184work would be a waste: given the triggering mechanism and qmail's
185deferral strategy, it is exceedingly rare for the queue to contain more
186than one deliverable message at any given moment.
187
188Exception: Even with all the concurrency tricks, qmail-send can end up
189spending a few minutes on a mailing list with thousands of remote
190entries. A user might send a new message to a remote address in the
191meantime. The simplest way to handle this would be to put big messages
192on a separate channel.
193
194qmail-send will never start a pass for a job that it already has. This
195means that, if one delivery takes longer than the retry interval, the
196next pass will be delayed. I implemented the opposite strategy for the
197old queue structure. Some hassles: mark() had to understand how job
198input was buffered; every new delivery had to check whether the same
199mpos in the same message was already being done.
200
201Some things that qmail-send does synchronously: queueing a bounce
202message; doing a cleanup via qmail-clean; classifying and rewriting all
203the addresses in a new message. As usual, making these asynchronous
204would require some housekeeping, but could speed things up a bit.
205(I'm willing to assume POSIX waitpid() for asynchronous bounces; putting
206an unbounded buffer into wait_pid() for the sake of NeXTSTEP 3 is not
207worthwhile.)
208
209Disk I/O is a bottleneck; UFS is reliable but it isn't fast. A good
210logging filesystem offers much better performance, but logging
211filesystems aren't widely available. Solution: Keep a journal, separate
212from the queue, adequate to rebuild the queue (with at worst some
213duplicate deliveries). Compress the journal. This would dramatically
214reduce total disk I/O.
215
216Bounce aggregation is a dubious feature. Bounce records aren't
217crashproof; there can be a huge delay between a failure and a bounce;
218the resulting bounce format is unnecessarily complicated. I'm tempted to
219scrap the bounce directory and send one bounce for each failing
220recipient, with appropriate modifications in the accompanying text.
221
222qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Or run
223qmail-start under an external service controller, such as supervise;
224that's why it runs in the foreground.
225
226The readdir() interface hides I/O errors. Lower-level interfaces would
227lead me into a thicket of portability problems. I'm really not sure what
228to do about this. Of course, a hard I/O error means that mail is toast,
229but a soft I/O error shouldn't cause any trouble.
230
231job_open() or pass_dochan() could be paranoid about the same id,channel
232already being open; but, since messdone() is so paranoid, the worst
233possible effect of a bug along these lines would be double delivery.
234
235Mathematical amusement: The optimal retry schedule is essentially,
236though not exactly, independent of the actual distribution of message
237delay times. What really matters is how much cost you assign to retries
238and to particular increases in latency. qmail's current quadratic retry
239schedule says that an hour-long delay in a day-old message is worth the
240same as a ten-minute delay in an hour-old message; this doesn't seem so
241unreasonable.
242
243Insider information: AOL retries their messages every five minutes for
244three days straight. Hmmm.
245
246
2476. Sending mail through the network (qmail-rspawn, qmail-remote)
248
249Are there any hosts, anywhere, whose mailers are bogged down by huge
250messages to multiple recipients at a single host? For typical hosts,
251multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a
252_slowness_ feature. Separate SMTP transactions have much lower latency.
253
254I've heard three complaints about bandwidth use from masochists sending
255messages through a modem through a smarthost to thousands of users---
256without sublists! They can get much better performance with QMQP.
257
258In the opposite direction: It's tempting to remove the @host part of the
259qmail-remote recip argument. Or at least avoid double-dns_cname.
260
261There are lots of reasons that qmail-rspawn should take a more active
262role in qmail-remote's activities. It should call separate programs to
263do (1) MX lookups, (2) SMTP connections, (3) QMTP connections. (But this
264wouldn't be so important if the DNS library didn't burn so much memory.)
265
266I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX
267record sending me mail for a host that I don't recognize as local.)
268Automatically treating ambiguous MXs as local is incompatible with my
269design decision to keep local delivery working when the network goes
270down. It puts more faith in DNS than DNS deserves. Much better: Have
271your MX records generated automatically from control/locals.
272
273If I successfully connect to an MX host but it temporarily refuses to
274accept the message, I give up and put the message back into the queue.
275But several documents seem to suggest that I should try further MX
276records. What are they thinking? My approach deals properly with downed
277hosts, hosts that are unreachable through a firewall, and load
278balancing; what else do people use multiple MX records for?
279
280Currently qmail-remote sends data in 1024-byte buffers. Perhaps it
281should try to take account of the MTU.
282
283Perhaps qmail-remote should allocate a fixed amount of DNS/connect()
284time across any number of MXs; this idea is due to Mark Delany.
285
286RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that
287the server's reply text doesn't contain bare LFs.
288
289RFC 821 and RFC 1123 prohibit host names in MAIL FROM and RCPT TO from
290being aliases. qmail-remote, like sendmail, rewrites aliases in RCPT;
291people who don't list aliases in control/locals or sendmail's Cw are
292implicitly relying on this conversion. It is course quite silly for an
293internal DNS detail to have such an effect on mail delivery, but that's
294how the Internet works. On the other hand, the compatibility arguments
295do not apply to MAIL FROM. qmail-remote no longer bothers with CNAME
296lookups for the envelope sender host.
297
298
2997. Delivering mail locally (qmail-lspawn, qmail-local)
300
301qmail-local doesn't support comsat. comsat is a pointless abomination.
302Use qbiff if you want that kind of notification.
303
304The getpwnam() interface hides I/O errors. Solution: qmail-pw2u.
305
306
3078. sendmail V8's new features
308
309sendmail-8.8.0/doc/op/op.me includes a list of big improvements of
310sendmail 8.8.0 over sendmail 5.67. Here's how qmail stacks up against
311each of those improvements. (Of course, qmail has its own improvements,
312but that's not the point of this list.)
313
314Connection caching, MX piggybacking: Nope. (Profile. Don't speculate.)
315
316Response to RCPT command is fast: Yup.
317
318IP addresses show up in Received lines: Yup.
319
320Self domain literal is properly handled: Yup.
321
322Different timeouts for QUIT, RCPT, etc.: No, just a single timeout.
323
324Proper <> handling, route-address pruning: Yes, but not configurable.
325
326ESMTP support: Yup. (Server-side, including PIPELINING.)
327
3288-bit clean: Yup. (Including server-side 8BITMIME support; same as
329sendmail with the 8 option.)
330
331Configurable user database: Yup.
332
333BIND support: Yup.
334
335Keyed files: Yes, in fastforward.
336
337931/1413/Ident/TAP: Yup.
338
339Correct 822 address list parsing: Yup. (Note that sendmail still has
340some major problems with quoting.)
341
342List-owner handling: Yup.
343
344Dynamic header allocation: Yup.
345
346Minimum number of disk blocks: Yes, via tunefs -m. (Or quotas; the right
347setup has qmailq with a small quota, qmails with a larger quota, so that
348qmail-send always has room to work.)
349
350Checkpointing: Yes, but not configurable---qmail always checkpoints.
351
352Error message configuration: Nope.
353
354GECOS matching: Not directly, but easy to hook in.
355
356Hop limit configuration: No. (qmail's limit is 100 hops. qmail offers
357automatic loop protection much more advanced than hop counting.)
358
359MIME error messages: No. (qmail uses QSBMF error messages, which are
360much easier to parse.)
361
362Forward file path: Yes, via /etc/passwd.
363
364Incoming SMTP configuration: Yes, via inetd or tcpserver.
365
366Privacy options: Yes, but they're not options.
367
368Best-MX mangling: Nope. See section 6 for further discussion.
369
3707-bit mangling: Nope. qmail always uses 8 bits.
371
372Support for up to 20 MX records: Yes, and more. qmail has no limits
373other than memory.
374
375Correct quoting of name-and-address headers: Yup.
376
377VRFY and EXPN now different: Nope. qmail always hides this information.
378
379Multi-word classes, deferred macro expansion, separate envelope/header
380$g processing, separate per-mailer envelope and header processing, new
381command line flags, new configuration lines, new mailer flags, new
382macros: These are sendmail-specific; they wouldn't even make sense for
383qmail. For example, _of course_ qmail handles envelopes and headers
384separately; they're almost entirely different objects!
385
386
3879. Miscellany
388
389sendmail-clone and qsmhook are too bletcherous to be documented. (The
390official replacement for qsmhook is preline, together with the
391qmail-command environment variables.)
392
393I've considered making install atomic, but this is very difficult to do
394right, and pointless if it isn't done right.
395
396RN suggests automatically putting together a reasonable set of lines for
397/etc/passwd. I perceive this as getting into the adduser business, which
398is worrisome: I'll be lynched the first time I screw up somebody's
399passwd file. This should be left to OS-specific installation scripts.
400
401The BSD 4.2 inetd didn't allow a username. I think I can safely forget
402about this. (DS notes that the username works under Ultrix even though
403it's undocumented.)
404
405I should clean up the bput/put choices.
406
407Some of the stralloc_0()s indicate that certain lower-level routines
408should grok stralloc.
409
410qmail assumes that all times are positive; that pid_t, time_t and ino_t
411fit into unsigned long; that gid_t fits into int; that the character set
412is ASCII; and that all pointers are interchangeable. Do I care?
413
414The bat book justifies sendmail's insane line-splitting mechanism by
415pointing out that it might be useful for ``a 40-character braille
416print-driving program.'' C'mon, guys, is that your best excuse?
417
418qmail'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 @@
1consider stripping vdoms for VERPs; tnx PJH
2consider ~ in qmail-local for doing defaultdelivery (not recursively)
3consider POP bulletins
4turn qmail-upq into a more serious queue-moving utility
5consider fast-greeting option in qmail-smtpd
6build a returnmail package
7
8expand strerr coverage
9redo control interface
10allow concurrency over 255
11allow more channels at compile time
12test for linux fifo close bug at compile time
13
14eliminate qsmhook
15finish OTBS conversion
16use mess822 in qmail-inject
17use mess822 in qreceipt
18use mess822 in qbiff
19use mess822 in maildirwatch
20eliminate token822, headerbody, hfield
21replace INTERNALS and THOUGHTS with a real paper describing qmail
22handle IPv6
23rewrite everything from scratch
diff --git a/UPGRADE b/UPGRADE
new file mode 100644
index 0000000..d648813
--- /dev/null
+++ b/UPGRADE
@@ -0,0 +1,66 @@
1SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and
2information generally), the qmail system comes with NO WARRANTY. It's
3much more secure and reliable than sendmail, but that's not saying much.
4
5
6Here's how to upgrade to qmail 1.03. This procedure will overwrite the
7old qmail binaries. Furthermore, it may begin delivering messages from
8the queue before you have had a chance to test it.
9
10
11WARNING for upgrades from 1.00 or 1.01: qlist has been split into a
12separate package. You can obtain it from http://pobox.com/~djb/qlist.html
13if you have any users who need it.
14
15WARNING for upgrades from 1.01: recipientmap is gone. The virtualdomains
16mechanism has been expanded to support virtual users.
17
18
19Before starting, compare conf* to your old conf*, and make any necessary
20changes. You can copy conf* from 1.02.
21
22
23How to install:
24
25 1. Compile the programs and create the formatted man pages:
26 # make it man
27
28 2. Inform your users that mail will not be accepted for a few minutes.
29
30 3. Disable deliveries by killing your old qmail-send. Wait for it to
31 print ``exiting'' in the log.
32
33 4. Disable SMTP service by commenting out the smtp line in inetd.conf;
34 kill -HUP your inetd. (If you are using tcpserver, simply kill -STOP
35 your tcpserver. If you are running a QMTP server, disable that too.)
36 Wait for current qmail-smtpd processes to die.
37
38 5. Install the new binaries and man pages:
39 # make setup check
40
41 6. If your boot scripts are using qmail-start instead of /var/qmail/rc:
42 Copy /var/qmail/boot/home to /var/qmail/rc. (Use home+df instead if
43 you have installed dot-forward; use proc or proc+df if you are using
44 procmail by default for local deliveries.) Compare /var/qmail/rc to
45 your qmail-start boot line, and edit /var/qmail/rc if necessary.
46 Replace your qmail-start boot line with
47 csh -cf '/var/qmail/rc &'
48
49 7. Reenable deliveries:
50 # csh -cf '/var/qmail/rc &'
51
52 8. Read TEST.deliver.
53
54 9. Reenable SMTP service by restoring the smtp line in inetd.conf; kill
55 -HUP your inetd. (If you are using tcpserver, simply kill -CONT your
56 tcpserver. If you are running a QMTP server, reenable that too.)
57
5810. Read TEST.receive.
59
60
61That's it! To report success:
62 % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to
63Replace First M. Last with your name.
64
65If you have questions about qmail, join the qmail mailing list; see
66http://pobox.com/~djb/qmail.html.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..78945e5
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
qmail 1.03
diff --git a/addresses.5 b/addresses.5
new file mode 100644
index 0000000..1fd4c5b
--- /dev/null
+++ b/addresses.5
@@ -0,0 +1,260 @@
1.TH addresses 5
2.SH "NAME"
3addresses \- formats for Internet mail addresses
4.SH "INTRODUCTION"
5A
6.B mail address
7is a string of characters containing @.
8
9Every mail address has a
10.B local part
11and a
12.B domain part\fR.
13The domain part is everything after the final @.
14The local part is everything before.
15
16For example, the mail addresses
17
18.EX
19 God@heaven.af.mil
20 @heaven.af.mil
21 @at@@heaven.af.mil
22.EE
23
24all have domain part
25.BR heaven.af.mil .
26The local parts are
27.BR God ,
28empty,
29and
30.BR @at@ .
31
32Some domains have owners.
33It is up to the owner of
34.B heaven.af.mil
35to say how mail messages will be delivered to addresses with domain part
36.BR heaven.af.mil .
37
38The domain part of an address is interpreted without regard to case, so
39
40.EX
41 God@heaven.af.mil
42.br
43 God@HEAVEN.AF.MIL
44.br
45 God@Heaven.AF.Mil
46.EE
47
48all refer to the same domain.
49
50There is one exceptional address that does not contain an @:
51namely, the empty string.
52The empty string cannot be used as a recipient address.
53It can be used as a sender address so that
54the real sender doesn't receive bounces.
55.SH "QMAIL EXTENSIONS"
56The
57.B qmail
58system allows several further types of addresses in mail envelopes.
59
60First, an envelope recipient address without an @ is interpreted as being at
61.IR envnoathost .
62For example, if
63.I envnoathost
64is
65.BR heaven.af.mil ,
66the address
67.B God
68will be rewritten as
69.BR God@heaven.af.mil .
70
71Second, the address
72.B #@[]
73is used as an envelope sender address for double bounces.
74
75Third, envelope sender addresses of the form
76.I pre\fB@\fIhost\fB-@[]
77are used to support variable envelope return paths (VERPs).
78.B qmail-send
79will rewrite
80.I pre\fB@\fIhost\fB-@[]
81as
82.I prerecip\fB=\fIdomain\fB@\fIhost
83for deliveries to
84.IR recip\fB@\fIdomain .
85Bounces directly from
86.B qmail-send
87will come back to
88.IR pre\fB@\fIhost .
89.SH "CHOOSING MAIL ADDRESSES"
90Here are some suggestions on choosing mail addresses for the Internet.
91
92Do not use non-ASCII characters.
93Under RFC 822 and RFC 821,
94these characters cannot be used in mail headers or in SMTP commands.
95In practice, they are regularly corrupted.
96
97Do not use ASCII control characters.
98NUL is regularly corrupted.
99CR and LF cannot be used in some combinations
100and are corrupted in all.
101None of these characters are usable on business cards.
102
103Avoid spaces and the characters
104
105.EX
106 \\"<>()[],;:
107.EE
108
109These all require quoting in mail headers and in SMTP.
110Many existing mail programs do not handle quoting properly.
111
112Do not use @ in a local part.
113@ requires quoting in mail headers and in SMTP.
114Many programs incorrectly look for the first @,
115rather than the last @,
116to find the domain part of an address.
117
118In a local part,
119do not use two consecutive dots, a dot at the beginning, or a dot at the end.
120Any of these would require quoting in mail headers.
121
122Do not use an empty local part; it cannot appear in SMTP commands.
123
124Avoid local parts longer than 64 characters.
125
126Be wary of uppercase letters in local parts.
127Some mail programs (and users!) will incorrectly convert
128.B God@heaven.af.mil
129to
130.BR god@heaven.af.mil .
131
132Be wary of the following characters:
133
134.EX
135 $&!#~`'^*|{}
136.EE
137
138Some users will not know
139how to feed these characters safely to their mail programs.
140
141In domain names, stick to letters, digits, dash, and dot.
142One popular DNS resolver has,
143under the banner of security,
144recently begun destroying domain names
145that contain certain other characters,
146including underscore.
147Exception: A dotted-decimal IP address in brackets,
148such as
149.BR [127.0.0.1] ,
150identifies a domain owned by whoever owns the host at that IP address,
151and can be used safely.
152
153In a domain name,
154do not use two consecutive dots,
155a dot at the beginning,
156or a dot at the end.
157This means that,
158when a domain name is broken down into components separated by dots,
159there are no empty components.
160
161Always use at least one dot in a domain name.
162If you own the
163.B mil
164domain,
165don't bother using the address
166.BR root@mil ;
167most users will be unable to send messages to that address.
168Same for the root domain.
169
170Avoid domain names longer than 64 characters.
171.SH "ENCODED ADDRESSES IN SMTP COMMANDS"
172RFC 821 defines an encoding of mail addresses in SMTP.
173For example, the addresses
174
175.EX
176 God@heaven.af.mil
177.br
178 a"quote@heaven.af.mil
179.br
180 The Almighty.One@heaven.af.mil
181.EE
182
183could be encoded in RCPT commands as
184
185.EX
186 RCPT TO:<God@heaven.af.mil>
187.br
188 RCPT TO:<a\\"quote@heaven.af.mil>
189.br
190 RCPT TO:<The\\ Almighty.One@heaven.af.mil>
191.EE
192
193There are several restrictions in RFC 821
194on the mail addresses that can be used over SMTP.
195Non-ASCII characters are prohibited.
196The local part must not be empty.
197The domain part must be a sequence of elements separated by dots,
198where each element is either a component,
199a sequence of digits preceded by #,
200or a dotted-decimal IP address surrounded by brackets.
201The only allowable characters in components are
202letters, digits, and dashes.
203Every component must (believe it or not)
204have at least three characters;
205the first character must be a letter;
206the last character must not be a hyphen.
207.SH "ENCODED ADDRESSES IN MAIL HEADERS"
208RFC 822 defines an encoding of mail addresses
209in certain header fields in a mail message.
210For example, the addresses
211
212.EX
213 God@heaven.af.mil
214.br
215 a"quote@heaven.af.mil
216.br
217 The Almighty.One@heaven.af.mil
218.EE
219
220could be encoded in a
221.B To
222field as
223
224.EX
225 To: God@heaven.af.mil,
226.br
227 <@brl.mil:"a\\"quote"@heaven.af.mil>,
228.br
229 "The Almighty".One@heaven.af.mil
230.EE
231
232or perhaps
233
234.EX
235 To: < "God"@heaven .af.mil>,
236.br
237 "a\\"quote" (Who?) @ heaven . af. mil
238.br
239 , God<"The Almighty.One"@heaven.af.mil>
240.EE
241
242There are several restrictions on the mail addresses that can
243be used in these header fields.
244Non-ASCII characters are prohibited.
245The domain part must be a sequence of elements separated by dots,
246where each element either (1) begins with [ and ends with ]
247or (2) is a nonempty string of printable ASCII characters
248not including any of
249
250.EX
251 \\".<>()[],;:
252.EE
253
254and not including space.
255.SH "SEE ALSO"
256envelopes(5),
257qmail-header(5),
258qmail-inject(8),
259qmail-remote(8),
260qmail-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 @@
1.TH alloc 3
2.SH NAME
3alloc \- allocate memory
4.SH SYNTAX
5.B #include <alloc.h>
6
7char *\fBalloc\fP(\fInew\fR);
8
9void \fBalloc_free\fP(\fIx\fR);
10
11void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR);
12
13char *\fIx\fR;
14.br
15unsigned int \fIold\fR;
16.br
17unsigned int \fInew\fR;
18.SH DESCRIPTION
19.B alloc
20allocates enough space from the heap for
21.I new
22bytes of data,
23adequately aligned for any data type.
24.I new
25may be 0.
26.B alloc
27returns a pointer to the space.
28If space is not available,
29.B alloc
30returns 0,
31setting
32.B errno
33appropriately.
34
35.B alloc_free
36returns space to the heap.
37
38.B alloc_re
39expands the space allocated to
40.I x
41from
42.I old
43bytes to
44.I new
45bytes.
46It allocates new space,
47copies
48.I old
49bytes from the old space to the new space,
50returns the old space to the heap,
51and changes
52.I x
53to point to the new space.
54It then returns 1.
55If space is not available,
56.B alloc_re
57returns 0,
58leaving the old space alone.
59.SH "SEE ALSO"
60sbrk(2),
61malloc(3),
62error(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 @@
1#include "alloc.h"
2#include "error.h"
3extern char *malloc();
4extern void free();
5
6#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
7#define SPACE 4096 /* must be multiple of ALIGNMENT */
8
9typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
10static aligned realspace[SPACE / ALIGNMENT];
11#define space ((char *) realspace)
12static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
13
14/*@null@*//*@out@*/char *alloc(n)
15unsigned int n;
16{
17 char *x;
18 n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
19 if (n <= avail) { avail -= n; return space + avail; }
20 x = malloc(n);
21 if (!x) errno = error_nomem;
22 return x;
23}
24
25void alloc_free(x)
26char *x;
27{
28 if (x >= space)
29 if (x < space + SPACE)
30 return; /* XXX: assuming that pointers are flat */
31 free(x);
32}
diff --git a/alloc.h b/alloc.h
new file mode 100644
index 0000000..1b1d893
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,8 @@
1#ifndef ALLOC_H
2#define ALLOC_H
3
4extern /*@null@*//*@out@*/char *alloc();
5extern void alloc_free();
6extern int alloc_re();
7
8#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 @@
1#include "alloc.h"
2#include "byte.h"
3
4int alloc_re(x,m,n)
5char **x;
6unsigned int m;
7unsigned int n;
8{
9 char *y;
10
11 y = alloc(n);
12 if (!y) return 0;
13 byte_copy(y,m,*x);
14 alloc_free(*x);
15 *x = y;
16 return 1;
17}
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 @@
1#include <sys/types.h>
2#include <grp.h>
3#include "subfd.h"
4#include "substdio.h"
5#include "readwrite.h"
6#include "exit.h"
7#include "scan.h"
8#include "fmt.h"
9
10char buf1[256];
11substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
12
13void outs(s)
14char *s;
15{
16 if (substdio_puts(&ss1,s) == -1) _exit(111);
17}
18
19void main(argc,argv)
20int argc;
21char **argv;
22{
23 char *name;
24 char *value;
25 struct group *gr;
26 char strnum[FMT_ULONG];
27
28 name = argv[1];
29 if (!name) _exit(100);
30 value = argv[2];
31 if (!value) _exit(100);
32
33 gr = getgrnam(value);
34 if (!gr) {
35 substdio_puts(subfderr,"fatal: unable to find group ");
36 substdio_puts(subfderr,value);
37 substdio_puts(subfderr,"\n");
38 substdio_flush(subfderr);
39 _exit(111);
40 }
41
42 strnum[fmt_ulong(strnum,(unsigned long) gr->gr_gid)] = 0;
43
44 outs("int ");
45 outs(name);
46 outs(" = ");
47 outs(strnum);
48 outs(";\n");
49 if (substdio_flush(&ss1) == -1) _exit(111);
50 _exit(0);
51}
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 @@
1#include "substdio.h"
2#include "readwrite.h"
3#include "exit.h"
4#include "scan.h"
5#include "fmt.h"
6
7char buf1[256];
8substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
9
10void puts(s)
11char *s;
12{
13 if (substdio_puts(&ss1,s) == -1) _exit(111);
14}
15
16void main(argc,argv)
17int argc;
18char **argv;
19{
20 char *name;
21 char *value;
22 unsigned long num;
23 char strnum[FMT_ULONG];
24
25 name = argv[1];
26 if (!name) _exit(100);
27 value = argv[2];
28 if (!value) _exit(100);
29
30 scan_ulong(value,&num);
31 strnum[fmt_ulong(strnum,num)] = 0;
32
33 puts("int ");
34 puts(name);
35 puts(" = ");
36 puts(strnum);
37 puts(";\n");
38 if (substdio_flush(&ss1) == -1) _exit(111);
39 _exit(0);
40}
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 @@
1#include "substdio.h"
2#include "readwrite.h"
3#include "exit.h"
4#include "scan.h"
5#include "fmt.h"
6
7char buf1[256];
8substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
9
10void puts(s)
11char *s;
12{
13 if (substdio_puts(&ss1,s) == -1) _exit(111);
14}
15
16void main(argc,argv)
17int argc;
18char **argv;
19{
20 char *name;
21 char *value;
22 unsigned long num;
23 char strnum[FMT_ULONG];
24
25 name = argv[1];
26 if (!name) _exit(100);
27 value = argv[2];
28 if (!value) _exit(100);
29
30 scan_8long(value,&num);
31 strnum[fmt_ulong(strnum,num)] = 0;
32
33 puts("int ");
34 puts(name);
35 puts(" = ");
36 puts(strnum);
37 puts(";\n");
38 if (substdio_flush(&ss1) == -1) _exit(111);
39 _exit(0);
40}
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 @@
1#include "substdio.h"
2#include "readwrite.h"
3#include "exit.h"
4
5char buf1[256];
6substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
7
8void puts(s)
9char *s;
10{
11 if (substdio_puts(&ss1,s) == -1) _exit(111);
12}
13
14void main(argc,argv)
15int argc;
16char **argv;
17{
18 char *name;
19 char *value;
20 unsigned char ch;
21 char octal[4];
22
23 name = argv[1];
24 if (!name) _exit(100);
25 value = argv[2];
26 if (!value) _exit(100);
27
28 puts("char ");
29 puts(name);
30 puts("[] = \"\\\n");
31
32 while (ch = *value++) {
33 puts("\\");
34 octal[3] = 0;
35 octal[2] = '0' + (ch & 7); ch >>= 3;
36 octal[1] = '0' + (ch & 7); ch >>= 3;
37 octal[0] = '0' + (ch & 7);
38 puts(octal);
39 }
40
41 puts("\\\n\";\n");
42 if (substdio_flush(&ss1) == -1) _exit(111);
43 _exit(0);
44}
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 @@
1#include <sys/types.h>
2#include <pwd.h>
3#include "subfd.h"
4#include "substdio.h"
5#include "readwrite.h"
6#include "exit.h"
7#include "scan.h"
8#include "fmt.h"
9
10char buf1[256];
11substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
12
13void outs(s) /* was named puts, but Solaris pwd.h includes stdio.h. dorks. */
14char *s;
15{
16 if (substdio_puts(&ss1,s) == -1) _exit(111);
17}
18
19void main(argc,argv)
20int argc;
21char **argv;
22{
23 char *name;
24 char *value;
25 struct passwd *pw;
26 char strnum[FMT_ULONG];
27
28 name = argv[1];
29 if (!name) _exit(100);
30 value = argv[2];
31 if (!value) _exit(100);
32
33 pw = getpwnam(value);
34 if (!pw) {
35 substdio_puts(subfderr,"fatal: unable to find user ");
36 substdio_puts(subfderr,value);
37 substdio_puts(subfderr,"\n");
38 substdio_flush(subfderr);
39 _exit(111);
40 }
41
42 strnum[fmt_ulong(strnum,(unsigned long) pw->pw_uid)] = 0;
43
44 outs("int ");
45 outs(name);
46 outs(" = ");
47 outs(strnum);
48 outs(";\n");
49 if (substdio_flush(&ss1) == -1) _exit(111);
50 _exit(0);
51}
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 @@
1#ifndef AUTO_BREAK_H
2#define AUTO_BREAK_H
3
4extern char auto_break[];
5
6#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 @@
1#ifndef AUTO_PATRN_H
2#define AUTO_PATRN_H
3
4extern int auto_patrn;
5
6#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 @@
1#ifndef AUTO_QMAIL_H
2#define AUTO_QMAIL_H
3
4extern char auto_qmail[];
5
6#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 @@
1#ifndef AUTO_SPAWN_H
2#define AUTO_SPAWN_H
3
4extern int auto_spawn;
5
6#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 @@
1#ifndef AUTO_SPLIT_H
2#define AUTO_SPLIT_H
3
4extern int auto_split;
5
6#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 @@
1#ifndef AUTO_UIDS_H
2#define AUTO_UIDS_H
3
4extern int auto_uida;
5extern int auto_uidd;
6extern int auto_uidl;
7extern int auto_uido;
8extern int auto_uidp;
9extern int auto_uidq;
10extern int auto_uidr;
11extern int auto_uids;
12
13extern int auto_gidn;
14extern int auto_gidq;
15
16#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 @@
1#ifndef AUTO_USERA_H
2#define AUTO_USERA_H
3
4extern char auto_usera[];
5
6#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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using dot-forward to support sendmail-style ~/.forward files.
5# Using binmail to deliver messages to /var/spool/mail/$USER by default.
6# Using BSD 4.4 binmail interface: /usr/libexec/mail.local -r
7
8exec env - PATH="QMAIL/bin:$PATH" \
9qmail-start '|dot-forward .forward
10|preline -f /usr/libexec/mail.local -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
11splogger 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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using binmail to deliver messages to /var/spool/mail/$USER by default.
5# Using BSD 4.4 binmail interface: /usr/libexec/mail.local -r
6
7exec env - PATH="QMAIL/bin:$PATH" \
8qmail-start \
9'|preline -f /usr/libexec/mail.local -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
10splogger 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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using dot-forward to support sendmail-style ~/.forward files.
5# Using binmail to deliver messages to /var/spool/mail/$USER by default.
6# Using SVR4 binmail interface: /bin/mail -r
7
8exec env - PATH="QMAIL/bin:$PATH" \
9qmail-start '|dot-forward .forward
10|preline -f /bin/mail -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
11splogger 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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using binmail to deliver messages to /var/spool/mail/$USER by default.
5# Using SVR4 binmail interface: /bin/mail -r
6
7exec env - PATH="QMAIL/bin:$PATH" \
8qmail-start \
9'|preline -f /bin/mail -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
10splogger 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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using dot-forward to support sendmail-style ~/.forward files.
5# Using binmail to deliver messages to /var/spool/mail/$USER by default.
6# Using V7 binmail interface: /bin/mail -f
7
8exec env - PATH="QMAIL/bin:$PATH" \
9qmail-start '|dot-forward .forward
10|preline -f /bin/mail -f "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
11splogger 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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using binmail to deliver messages to /var/spool/mail/$USER by default.
5# Using V7 binmail interface: /bin/mail -f
6
7exec env - PATH="QMAIL/bin:$PATH" \
8qmail-start \
9'|preline -f /bin/mail -f "${SENDER:-MAILER-DAEMON}" -d "$USER"' \
10splogger 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 @@
1.TH bouncesaying 1
2.SH NAME
3bouncesaying \- perhaps bounce each incoming message
4.SH SYNOPSIS
5in
6.BR .qmail :
7.B |bouncesaying
8.I error
9[
10.I program
11[
12.I arg ...
13]
14]
15.SH DESCRIPTION
16.B bouncesaying
17feeds each new mail message to
18.I program
19with the given arguments.
20If
21.I program
22exits 0,
23.B bouncesaying
24prints
25.I error
26and bounces the message.
27
28If
29.I program
30exits 111,
31.B bouncesaying
32exits 111,
33so delivery will be retried later.
34
35If
36.I program
37exits anything else
38(or does not exist),
39.B bouncesaying
40exits 0,
41so the rest of
42.B .qmail
43will be processed as usual.
44
45Note that
46it is not safe for
47.I program
48to fork a child that
49reads the message in the background.
50
51If
52.I program
53is not supplied,
54.B bouncesaying
55always bounces the message:
56
57.EX
58 |bouncesaying 'This address no longer accepts mail.'
59.EE
60
61.B WARNING:
62If you create a
63.B .qmail
64file to enable
65.BR bouncesaying ,
66make sure to also add a line specifying delivery to your normal mailbox.
67.SH "SEE ALSO"
68condredirect(1),
69except(1),
70dot-qmail(5),
71qmail-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 @@
1#include "fork.h"
2#include "strerr.h"
3#include "error.h"
4#include "wait.h"
5#include "sig.h"
6#include "exit.h"
7
8#define FATAL "bouncesaying: fatal: "
9
10void main(argc,argv)
11int argc;
12char **argv;
13{
14 int pid;
15 int wstat;
16
17 if (!argv[1])
18 strerr_die1x(100,"bouncesaying: usage: bouncesaying error [ program [ arg ... ] ]");
19
20 if (argv[2]) {
21 pid = fork();
22 if (pid == -1)
23 strerr_die2sys(111,FATAL,"unable to fork: ");
24 if (pid == 0) {
25 execvp(argv[2],argv + 2);
26 if (error_temp(errno)) _exit(111);
27 _exit(100);
28 }
29 if (wait_pid(&wstat,pid) == -1)
30 strerr_die2x(111,FATAL,"wait failed");
31 if (wait_crashed(wstat))
32 strerr_die2x(111,FATAL,"child crashed");
33 switch(wait_exitcode(wstat)) {
34 case 0: break;
35 case 111: strerr_die2x(111,FATAL,"temporary child error");
36 default: _exit(0);
37 }
38 }
39
40 strerr_die1x(100,argv[1]);
41}
diff --git a/byte.h b/byte.h
new file mode 100644
index 0000000..de06c69
--- /dev/null
+++ b/byte.h
@@ -0,0 +1,13 @@
1#ifndef BYTE_H
2#define BYTE_H
3
4extern unsigned int byte_chr();
5extern unsigned int byte_rchr();
6extern void byte_copy();
7extern void byte_copyr();
8extern int byte_diff();
9extern void byte_zero();
10
11#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
12
13#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 @@
1#include "byte.h"
2
3unsigned int byte_chr(s,n,c)
4char *s;
5register unsigned int n;
6int c;
7{
8 register char ch;
9 register char *t;
10
11 ch = c;
12 t = s;
13 for (;;) {
14 if (!n) break; if (*t == ch) break; ++t; --n;
15 if (!n) break; if (*t == ch) break; ++t; --n;
16 if (!n) break; if (*t == ch) break; ++t; --n;
17 if (!n) break; if (*t == ch) break; ++t; --n;
18 }
19 return t - s;
20}
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 @@
1#include "byte.h"
2
3void byte_copy(to,n,from)
4register char *to;
5register unsigned int n;
6register char *from;
7{
8 for (;;) {
9 if (!n) return; *to++ = *from++; --n;
10 if (!n) return; *to++ = *from++; --n;
11 if (!n) return; *to++ = *from++; --n;
12 if (!n) return; *to++ = *from++; --n;
13 }
14}
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 @@
1#include "byte.h"
2
3void byte_copyr(to,n,from)
4register char *to;
5register unsigned int n;
6register char *from;
7{
8 to += n;
9 from += n;
10 for (;;) {
11 if (!n) return; *--to = *--from; --n;
12 if (!n) return; *--to = *--from; --n;
13 if (!n) return; *--to = *--from; --n;
14 if (!n) return; *--to = *--from; --n;
15 }
16}
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 @@
1#include "byte.h"
2
3int byte_diff(s,n,t)
4register char *s;
5register unsigned int n;
6register char *t;
7{
8 for (;;) {
9 if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
10 if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
11 if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
12 if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
13 }
14 return ((int)(unsigned int)(unsigned char) *s)
15 - ((int)(unsigned int)(unsigned char) *t);
16}
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 @@
1#include "byte.h"
2
3unsigned int byte_rchr(s,n,c)
4char *s;
5register unsigned int n;
6int c;
7{
8 register char ch;
9 register char *t;
10 register char *u;
11
12 ch = c;
13 t = s;
14 u = 0;
15 for (;;) {
16 if (!n) break; if (*t == ch) u = t; ++t; --n;
17 if (!n) break; if (*t == ch) u = t; ++t; --n;
18 if (!n) break; if (*t == ch) u = t; ++t; --n;
19 if (!n) break; if (*t == ch) u = t; ++t; --n;
20 }
21 if (!u) u = t;
22 return u - s;
23}
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 @@
1#include "byte.h"
2
3void byte_zero(s,n)
4char *s;
5register unsigned int n;
6{
7 for (;;) {
8 if (!n) break; *s++ = 0; --n;
9 if (!n) break; *s++ = 0; --n;
10 if (!n) break; *s++ = 0; --n;
11 if (!n) break; *s++ = 0; --n;
12 }
13}
diff --git a/case.3 b/case.3
new file mode 100644
index 0000000..58bd724
--- /dev/null
+++ b/case.3
@@ -0,0 +1,100 @@
1.TH case 3
2.SH NAME
3case \- convert ASCII uppercase bytes to lowercase
4.SH SYNTAX
5.B #include <case.h>
6
7void \fBcase_lowers\fP(\fIs\fR);
8.br
9void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR);
10
11int \fBcase_diffs\fP(\fIs\fR,\fIt\fR);
12.br
13int \fBcase_equals\fP(\fIs\fR,\fIt\fR);
14.br
15int \fBcase_starts\fP(\fIs\fR,\fIt\fR);
16
17int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
18.br
19int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
20
21char *\fIs\fR;
22.br
23char *\fIt\fR;
24.br
25unsigned int \fIlen\fR;
26.SH DESCRIPTION
27.B case_lowers
28converts each uppercase byte in the string
29.I s
30to lowercase.
31.I s
32must be 0-terminated.
33
34.B case_lowerb
35converts each uppercase byte in the buffer
36.IR s ,
37of length
38.IR len ,
39to lowercase.
40
41.B case_diffs
42lexicographically compares lowercase versions of the strings
43.I s
44and
45.IR t .
46It returns something positive, negative, or zero
47when the first is larger than, smaller than, or equal to the second.
48.I s
49and
50.I t
51must be 0-terminated.
52
53.B case_equals
54means
55.BR !case_diffs .
56
57.B case_starts
58returns 1 if a lowercase version of
59.I s
60starts with a lowercase version of
61.IR t .
62.I s
63and
64.I t
65must be 0-terminated.
66
67.B case_diffb
68lexicographically compares lowercase versions of the buffers
69.I s
70and
71.IR t ,
72each of length
73.IR len .
74It returns something positive, negative, or zero
75when the first is larger than, smaller than, or equal to the second.
76
77.B case_startb
78returns 1 if a lowercase version of the buffer
79.IR s ,
80of length
81.IR len ,
82starts with a lowercase version of the string
83.IR t .
84.I t
85must be 0-terminated.
86
87The
88.B case
89routines
90are ASCII-specific.
91They are suitable for programs that handle
92case-independent networking protocols.
93
94All comparisons are performed on unsigned bytes.
95.SH "SEE ALSO"
96byte_diff(3),
97byte_equal(3),
98str_diff(3),
99str_equal(3),
100str_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 @@
1#ifndef CASE_H
2#define CASE_H
3
4extern void case_lowers();
5extern void case_lowerb();
6extern int case_diffs();
7extern int case_diffb();
8extern int case_starts();
9extern int case_startb();
10
11#define case_equals(s,t) (!case_diffs((s),(t)))
12
13#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 @@
1#include "case.h"
2
3int case_diffb(s,len,t)
4register char *s;
5unsigned int len;
6register char *t;
7{
8 register unsigned char x;
9 register unsigned char y;
10
11 while (len > 0) {
12 --len;
13 x = *s++ - 'A';
14 if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
15 y = *t++ - 'A';
16 if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
17 if (x != y)
18 return ((int)(unsigned int) x) - ((int)(unsigned int) y);
19 }
20 return 0;
21}
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 @@
1#include "case.h"
2
3int case_diffs(s,t)
4register char *s;
5register char *t;
6{
7 register unsigned char x;
8 register unsigned char y;
9
10 for (;;) {
11 x = *s++ - 'A';
12 if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
13 y = *t++ - 'A';
14 if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
15 if (x != y) break;
16 if (!x) break;
17 }
18 return ((int)(unsigned int) x) - ((int)(unsigned int) y);
19}
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 @@
1#include "case.h"
2
3void case_lowerb(s,len)
4char *s;
5unsigned int len;
6{
7 unsigned char x;
8 while (len > 0) {
9 --len;
10 x = *s - 'A';
11 if (x <= 'Z' - 'A') *s = x + 'a';
12 ++s;
13 }
14}
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 @@
1#include "case.h"
2
3void case_lowers(s)
4char *s;
5{
6 unsigned char x;
7 while (x = *s) {
8 x -= 'A';
9 if (x <= 'Z' - 'A') *s = x + 'a';
10 ++s;
11 }
12}
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 @@
1#include "case.h"
2
3int case_starts(s,t)
4register char *s;
5register char *t;
6{
7 register unsigned char x;
8 register unsigned char y;
9
10 for (;;) {
11 x = *s++ - 'A';
12 if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
13 y = *t++ - 'A';
14 if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
15 if (!y) return 1;
16 if (x != y) return 0;
17 }
18}
diff --git a/cdb.3 b/cdb.3
new file mode 100644
index 0000000..a85b34c
--- /dev/null
+++ b/cdb.3
@@ -0,0 +1,62 @@
1.TH cdb 3
2.SH NAME
3cdb \- read from a constant database
4.SH SYNTAX
5.B #include <cdb.h>
6
7int \fBcdb_seek(\fP\fIfd,key,len,dlen\fR\fB)\fP;
8
9int \fIfd\fR;
10.br
11char *\fIkey\fR;
12.br
13unsigned int \fIlen\fR;
14.br
15uint32 *\fIdlen\fR;
16.SH DESCRIPTION
17.B cdb_seek
18looks up
19.I key
20in a constant database.
21It returns 1 if
22.I key
23is present,
240 if
25.I key
26is not present,
27or \-1 if there was a read error.
28.I key
29is an array of
30.I len
31characters.
32
33.B cdb_seek
34needs an open file descriptor,
35.IR fd ,
36pointing to the database.
37If
38.B cdb_seek
39returns 1,
40it points
41.I fd
42at the beginning of the data portion of the first record
43indexed by
44.IR key ,
45and it stores the data length in
46.IR dlen.
47.B cdb_seek
48does not provide a way to read subsequent records with the same key.
49
50It's fine to do several
51.B cdb_seek
52lookups with the same open file descriptor.
53Beware, however, that two simultaneous
54.B cdb_seek
55lookups can fail horribly;
56separate processes should not share the same database descriptor.
57Furthermore, any updates after the database was opened
58will be invisible.
59It's rarely a good idea for a long-running program
60to hold a database open.
61.SH "SEE ALSO"
62cdbget(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 @@
1#ifndef CDB_H
2#define CDB_H
3
4#include "uint32.h"
5
6extern uint32 cdb_hash();
7extern uint32 cdb_unpack();
8
9extern int cdb_bread();
10extern int cdb_seek();
11
12#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 @@
1#include "cdb.h"
2
3uint32 cdb_hash(buf,len)
4unsigned char *buf;
5unsigned int len;
6{
7 uint32 h;
8
9 h = 5381;
10 while (len) {
11 --len;
12 h += (h << 5);
13 h ^= (uint32) *buf++;
14 }
15 return h;
16}
diff --git a/cdb_seek.c b/cdb_seek.c
new file mode 100644
index 0000000..87ab614
--- /dev/null
+++ b/cdb_seek.c
@@ -0,0 +1,95 @@
1#include <sys/types.h>
2#include <errno.h>
3extern int errno;
4#include "cdb.h"
5
6#ifndef SEEK_SET
7#define SEEK_SET 0
8#endif
9
10int cdb_bread(fd,buf,len)
11int fd;
12char *buf;
13int len;
14{
15 int r;
16 while (len > 0) {
17 do
18 r = read(fd,buf,len);
19 while ((r == -1) && (errno == EINTR));
20 if (r == -1) return -1;
21 if (r == 0) { errno = EIO; return -1; }
22 buf += r;
23 len -= r;
24 }
25 return 0;
26}
27
28static int match(fd,key,len)
29int fd;
30char *key;
31unsigned int len;
32{
33 char buf[32];
34 int n;
35 int i;
36
37 while (len > 0) {
38 n = sizeof(buf);
39 if (n > len) n = len;
40 if (cdb_bread(fd,buf,n) == -1) return -1;
41 for (i = 0;i < n;++i) if (buf[i] != key[i]) return 0;
42 key += n;
43 len -= n;
44 }
45 return 1;
46}
47
48int cdb_seek(fd,key,len,dlen)
49int fd;
50char *key;
51unsigned int len;
52uint32 *dlen;
53{
54 char packbuf[8];
55 uint32 pos;
56 uint32 h;
57 uint32 lenhash;
58 uint32 h2;
59 uint32 loop;
60 uint32 poskd;
61
62 h = cdb_hash(key,len);
63
64 pos = 8 * (h & 255);
65 if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1;
66
67 if (cdb_bread(fd,packbuf,8) == -1) return -1;
68
69 pos = cdb_unpack(packbuf);
70 lenhash = cdb_unpack(packbuf + 4);
71
72 if (!lenhash) return 0;
73 h2 = (h >> 8) % lenhash;
74
75 for (loop = 0;loop < lenhash;++loop) {
76 if (lseek(fd,(off_t) (pos + 8 * h2),SEEK_SET) == -1) return -1;
77 if (cdb_bread(fd,packbuf,8) == -1) return -1;
78 poskd = cdb_unpack(packbuf + 4);
79 if (!poskd) return 0;
80 if (cdb_unpack(packbuf) == h) {
81 if (lseek(fd,(off_t) poskd,SEEK_SET) == -1) return -1;
82 if (cdb_bread(fd,packbuf,8) == -1) return -1;
83 if (cdb_unpack(packbuf) == len)
84 switch(match(fd,key,len)) {
85 case -1:
86 return -1;
87 case 1:
88 *dlen = cdb_unpack(packbuf + 4);
89 return 1;
90 }
91 }
92 if (++h2 == lenhash) h2 = 0;
93 }
94 return 0;
95}
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 @@
1#include "cdb.h"
2
3uint32 cdb_unpack(buf)
4unsigned char *buf;
5{
6 uint32 num;
7 num = buf[3]; num <<= 8;
8 num += buf[2]; num <<= 8;
9 num += buf[1]; num <<= 8;
10 num += buf[0];
11 return num;
12}
diff --git a/cdbmake.h b/cdbmake.h
new file mode 100644
index 0000000..883a231
--- /dev/null
+++ b/cdbmake.h
@@ -0,0 +1,35 @@
1#ifndef CDBMAKE_H
2#define CDBMAKE_H
3
4#include "uint32.h"
5
6#define CDBMAKE_HPLIST 1000
7
8struct cdbmake_hp { uint32 h; uint32 p; } ;
9
10struct cdbmake_hplist {
11 struct cdbmake_hp hp[CDBMAKE_HPLIST];
12 struct cdbmake_hplist *next;
13 int num;
14} ;
15
16struct cdbmake {
17 char final[2048];
18 uint32 count[256];
19 uint32 start[256];
20 struct cdbmake_hplist *head;
21 struct cdbmake_hp *split; /* includes space for hash */
22 struct cdbmake_hp *hash;
23 uint32 numentries;
24} ;
25
26extern void cdbmake_pack();
27#define CDBMAKE_HASHSTART ((uint32) 5381)
28extern uint32 cdbmake_hashadd();
29
30extern void cdbmake_init();
31extern int cdbmake_add();
32extern int cdbmake_split();
33extern uint32 cdbmake_throw();
34
35#endif
diff --git a/cdbmake_add.c b/cdbmake_add.c
new file mode 100644
index 0000000..115f828
--- /dev/null
+++ b/cdbmake_add.c
@@ -0,0 +1,117 @@
1#include "cdbmake.h"
2
3void cdbmake_init(cdbm)
4struct cdbmake *cdbm;
5{
6 cdbm->head = 0;
7 cdbm->split = 0;
8 cdbm->hash = 0;
9 cdbm->numentries = 0;
10}
11
12int cdbmake_add(cdbm,h,p,alloc)
13struct cdbmake *cdbm;
14uint32 h;
15uint32 p;
16char *(*alloc)();
17{
18 struct cdbmake_hplist *head;
19
20 head = cdbm->head;
21 if (!head || (head->num >= CDBMAKE_HPLIST)) {
22 head = (struct cdbmake_hplist *) alloc(sizeof(struct cdbmake_hplist));
23 if (!head) return 0;
24 head->num = 0;
25 head->next = cdbm->head;
26 cdbm->head = head;
27 }
28 head->hp[head->num].h = h;
29 head->hp[head->num].p = p;
30 ++head->num;
31 ++cdbm->numentries;
32 return 1;
33}
34
35int cdbmake_split(cdbm,alloc)
36struct cdbmake *cdbm;
37char *(*alloc)();
38{
39 int i;
40 uint32 u;
41 uint32 memsize;
42 struct cdbmake_hplist *x;
43
44 for (i = 0;i < 256;++i)
45 cdbm->count[i] = 0;
46
47 for (x = cdbm->head;x;x = x->next) {
48 i = x->num;
49 while (i--)
50 ++cdbm->count[255 & x->hp[i].h];
51 }
52
53 memsize = 1;
54 for (i = 0;i < 256;++i) {
55 u = cdbm->count[i] * 2;
56 if (u > memsize)
57 memsize = u;
58 }
59
60 memsize += cdbm->numentries; /* no overflow possible up to now */
61 u = (uint32) 0 - (uint32) 1;
62 u /= sizeof(struct cdbmake_hp);
63 if (memsize > u) return 0;
64
65 cdbm->split = (struct cdbmake_hp *) alloc(memsize * sizeof(struct cdbmake_hp));
66 if (!cdbm->split) return 0;
67
68 cdbm->hash = cdbm->split + cdbm->numentries;
69
70 u = 0;
71 for (i = 0;i < 256;++i) {
72 u += cdbm->count[i]; /* bounded by numentries, so no overflow */
73 cdbm->start[i] = u;
74 }
75
76 for (x = cdbm->head;x;x = x->next) {
77 i = x->num;
78 while (i--)
79 cdbm->split[--cdbm->start[255 & x->hp[i].h]] = x->hp[i];
80 }
81
82 return 1;
83}
84
85uint32 cdbmake_throw(cdbm,pos,b)
86struct cdbmake *cdbm;
87uint32 pos;
88int b;
89{
90 uint32 len;
91 uint32 j;
92 uint32 count;
93 struct cdbmake_hp *hp;
94 uint32 where;
95
96 count = cdbm->count[b];
97
98 len = count + count; /* no overflow possible */
99 cdbmake_pack(cdbm->final + 8 * b,pos);
100 cdbmake_pack(cdbm->final + 8 * b + 4,len);
101
102 if (len) {
103 for (j = 0;j < len;++j)
104 cdbm->hash[j].h = cdbm->hash[j].p = 0;
105
106 hp = cdbm->split + cdbm->start[b];
107 for (j = 0;j < count;++j) {
108 where = (hp->h >> 8) % len;
109 while (cdbm->hash[where].p)
110 if (++where == len)
111 where = 0;
112 cdbm->hash[where] = *hp++;
113 }
114 }
115
116 return len;
117}
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 @@
1#include "cdbmake.h"
2
3uint32 cdbmake_hashadd(h,c)
4uint32 h;
5unsigned int c;
6{
7 h += (h << 5);
8 h ^= (uint32) (unsigned char) c;
9 return h;
10}
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 @@
1#include "cdbmake.h"
2
3void cdbmake_pack(buf,num)
4unsigned char *buf;
5uint32 num;
6{
7 *buf++ = num; num >>= 8;
8 *buf++ = num; num >>= 8;
9 *buf++ = num; num >>= 8;
10 *buf = num;
11}
diff --git a/cdbmss.c b/cdbmss.c
new file mode 100644
index 0000000..2d8f367
--- /dev/null
+++ b/cdbmss.c
@@ -0,0 +1,65 @@
1#include "readwrite.h"
2#include "seek.h"
3#include "alloc.h"
4#include "cdbmss.h"
5
6int cdbmss_start(c,fd)
7struct cdbmss *c;
8int fd;
9{
10 cdbmake_init(&c->cdbm);
11 c->fd = fd;
12 c->pos = sizeof(c->cdbm.final);
13 substdio_fdbuf(&c->ss,write,fd,c->ssbuf,sizeof(c->ssbuf));
14 return seek_set(fd,(seek_pos) c->pos);
15}
16
17int cdbmss_add(c,key,keylen,data,datalen)
18struct cdbmss *c;
19unsigned char *key;
20unsigned int keylen;
21unsigned char *data;
22unsigned int datalen;
23{
24 uint32 h;
25 int i;
26
27 cdbmake_pack(c->packbuf,(uint32) keylen);
28 cdbmake_pack(c->packbuf + 4,(uint32) datalen);
29 if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
30 if (substdio_put(&c->ss,key,keylen) == -1) return -1;
31 if (substdio_put(&c->ss,data,datalen) == -1) return -1;
32
33 h = CDBMAKE_HASHSTART;
34 for (i = 0;i < keylen;++i)
35 h = cdbmake_hashadd(h,(unsigned int) key[i]);
36
37 if (!cdbmake_add(&c->cdbm,h,c->pos,alloc)) return -1;
38
39 c->pos += 8 + keylen + datalen; /* XXX: overflow? */
40 return 0;
41}
42
43int cdbmss_finish(c)
44struct cdbmss *c;
45{
46 int i;
47 uint32 len;
48 uint32 u;
49
50 if (!cdbmake_split(&c->cdbm,alloc)) return -1;
51
52 for (i = 0;i < 256;++i) {
53 len = cdbmake_throw(&c->cdbm,c->pos,i);
54 for (u = 0;u < len;++u) {
55 cdbmake_pack(c->packbuf,c->cdbm.hash[u].h);
56 cdbmake_pack(c->packbuf + 4,c->cdbm.hash[u].p);
57 if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
58 c->pos += 8; /* XXX: overflow? */
59 }
60 }
61
62 if (substdio_flush(&c->ss) == -1) return -1;
63 if (seek_begin(c->fd) == -1) return -1;
64 return substdio_putflush(&c->ss,c->cdbm.final,sizeof(c->cdbm.final));
65}
diff --git a/cdbmss.h b/cdbmss.h
new file mode 100644
index 0000000..5e6bdf4
--- /dev/null
+++ b/cdbmss.h
@@ -0,0 +1,16 @@
1#ifndef CDBMSS_H
2#define CDBMSS_H
3
4#include "cdbmake.h"
5#include "substdio.h"
6
7struct cdbmss {
8 char ssbuf[1024];
9 struct cdbmake cdbm;
10 substdio ss;
11 char packbuf[8];
12 uint32 pos;
13 int fd;
14} ;
15
16#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 @@
1#include "exit.h"
2void main()
3{
4 short x[4];
5
6 x[0] = x[1] = 0;
7 if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
8 _exit(0);
9}
diff --git a/chkspawn.c b/chkspawn.c
new file mode 100644
index 0000000..d19259e
--- /dev/null
+++ b/chkspawn.c
@@ -0,0 +1,48 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "fmt.h"
4#include "select.h"
5#include "exit.h"
6#include "auto_spawn.h"
7
8char num[FMT_ULONG];
9fd_set fds;
10
11void main()
12{
13 unsigned long hiddenlimit;
14 unsigned long maxnumd;
15
16 hiddenlimit = sizeof(fds) * 8;
17 maxnumd = (hiddenlimit - 5) / 2;
18
19 if (auto_spawn < 1) {
20 substdio_puts(subfderr,"Oops. You have set conf-spawn lower than 1.\n");
21 substdio_flush(subfderr);
22 _exit(1);
23 }
24
25 if (auto_spawn > 255) {
26 substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n");
27 substdio_flush(subfderr);
28 _exit(1);
29 }
30
31 if (auto_spawn > maxnumd) {
32 substdio_puts(subfderr,"Oops. Your system's FD_SET() has a hidden limit of ");
33 substdio_put(subfderr,num,fmt_ulong(num,hiddenlimit));
34 substdio_puts(subfderr," descriptors.\n\
35This means that the qmail daemons could crash if you set the run-time\n\
36concurrency higher than ");
37 substdio_put(subfderr,num,fmt_ulong(num,maxnumd));
38 substdio_puts(subfderr,". So I'm going to insist that the concurrency\n\
39limit in conf-spawn be at most ");
40 substdio_put(subfderr,num,fmt_ulong(num,maxnumd));
41 substdio_puts(subfderr,". Right now it's ");
42 substdio_put(subfderr,num,fmt_ulong(num,(unsigned long) auto_spawn));
43 substdio_puts(subfderr,".\n");
44 substdio_flush(subfderr);
45 _exit(1);
46 }
47 _exit(0);
48}
diff --git a/coe.3 b/coe.3
new file mode 100644
index 0000000..06591b6
--- /dev/null
+++ b/coe.3
@@ -0,0 +1,25 @@
1.TH coe 3
2.SH NAME
3coe \- set close-on-exec flag for a descriptor
4.SH SYNTAX
5.B #include <coe.h>
6
7int \fBcoe\fP(\fIfd\fR);
8
9int \fIfd\fR;
10.SH DESCRIPTION
11.B coe
12sets the close-on-exec flag for
13file descriptor
14.IR fd ,
15returning 0 if it was successful
16or -1 on error.
17If
18.B coe
19is successful,
20.I fd
21will be closed when the process calls
22.BR execve .
23.SH "SEE ALSO"
24execve(2),
25fcntl(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 @@
1#include <fcntl.h>
2#include "coe.h"
3
4int coe(fd)
5int fd;
6{
7 return fcntl(fd,F_SETFD,1);
8}
diff --git a/coe.h b/coe.h
new file mode 100644
index 0000000..1559bc1
--- /dev/null
+++ b/coe.h
@@ -0,0 +1,6 @@
1#ifndef COE_H
2#define COE_H
3
4extern int coe();
5
6#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 @@
1#include "commands.h"
2#include "substdio.h"
3#include "stralloc.h"
4#include "str.h"
5#include "case.h"
6
7static stralloc cmd = {0};
8
9int commands(ss,c)
10substdio *ss;
11struct commands *c;
12{
13 int i;
14 char *arg;
15
16 for (;;) {
17 if (!stralloc_copys(&cmd,"")) return -1;
18
19 for (;;) {
20 if (!stralloc_readyplus(&cmd,1)) return -1;
21 i = substdio_get(ss,cmd.s + cmd.len,1);
22 if (i != 1) return i;
23 if (cmd.s[cmd.len] == '\n') break;
24 ++cmd.len;
25 }
26
27 if (cmd.len > 0) if (cmd.s[cmd.len - 1] == '\r') --cmd.len;
28
29 cmd.s[cmd.len] = 0;
30
31 i = str_chr(cmd.s,' ');
32 arg = cmd.s + i;
33 while (*arg == ' ') ++arg;
34 cmd.s[i] = 0;
35
36 for (i = 0;c[i].text;++i) if (case_equals(c[i].text,cmd.s)) break;
37 c[i].fun(arg);
38 if (c[i].flush) c[i].flush();
39 }
40}
diff --git a/commands.h b/commands.h
new file mode 100644
index 0000000..da05a8d
--- /dev/null
+++ b/commands.h
@@ -0,0 +1,12 @@
1#ifndef COMMANDS_H
2#define COMMANDS_H
3
4struct commands {
5 char *text;
6 void (*fun)();
7 void (*flush)();
8} ;
9
10extern int commands();
11
12#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 @@
1.TH condredirect 1
2.SH NAME
3condredirect \- perhaps redirect mail to another address
4.SH SYNOPSIS
5in
6.BR .qmail :
7.B |condredirect
8.I newaddress
9.I program
10[
11.I arg ...
12]
13.SH DESCRIPTION
14.B condredirect
15feeds each new mail message to
16.I program
17with the given arguments.
18If
19.I program
20exits 0,
21.B condredirect
22forwards the mail message to
23.IR newaddress ,
24and then exits 99,
25so further commands in
26.B .qmail
27are ignored.
28
29If
30.I program
31exits 111,
32.B condredirect
33exits 111,
34so delivery will be retried later.
35
36If
37.I program
38exits anything else
39(or does not exist),
40.B condredirect
41exits 0,
42so the rest of
43.B .qmail
44will be processed as usual.
45
46Note that
47it is not safe for
48.I program
49to fork a child that
50reads the message in the background.
51
52.B WARNING:
53If you create a
54.B .qmail
55file to enable
56.BR condredirect ,
57make sure to also add a line specifying delivery to your normal mailbox.
58.SH "SEE ALSO"
59bouncesaying(1),
60except(1),
61dot-qmail(5),
62qmail-command(8),
63qmail-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 @@
1#include "sig.h"
2#include "readwrite.h"
3#include "exit.h"
4#include "env.h"
5#include "error.h"
6#include "fork.h"
7#include "wait.h"
8#include "seek.h"
9#include "qmail.h"
10#include "strerr.h"
11#include "substdio.h"
12#include "fmt.h"
13
14#define FATAL "condredirect: fatal: "
15
16struct qmail qqt;
17
18int mywrite(fd,buf,len) int fd; char *buf; int len;
19{
20 qmail_put(&qqt,buf,len);
21 return len;
22}
23
24char inbuf[SUBSTDIO_INSIZE];
25char outbuf[1];
26substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
27substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf);
28
29char num[FMT_ULONG];
30
31void main(argc,argv)
32int argc;
33char **argv;
34{
35 char *sender;
36 char *dtline;
37 int pid;
38 int wstat;
39 char *qqx;
40
41 if (!argv[1] || !argv[2])
42 strerr_die1x(100,"condredirect: usage: condredirect newaddress program [ arg ... ]");
43
44 pid = fork();
45 if (pid == -1)
46 strerr_die2sys(111,FATAL,"unable to fork: ");
47 if (pid == 0) {
48 execvp(argv[2],argv + 2);
49 if (error_temp(errno)) _exit(111);
50 _exit(100);
51 }
52 if (wait_pid(&wstat,pid) == -1)
53 strerr_die2x(111,FATAL,"wait failed");
54 if (wait_crashed(wstat))
55 strerr_die2x(111,FATAL,"child crashed");
56 switch(wait_exitcode(wstat)) {
57 case 0: break;
58 case 111: strerr_die2x(111,FATAL,"temporary child error");
59 default: _exit(0);
60 }
61
62 if (seek_begin(0) == -1)
63 strerr_die2sys(111,FATAL,"unable to rewind: ");
64 sig_pipeignore();
65
66 sender = env_get("SENDER");
67 if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
68 dtline = env_get("DTLINE");
69 if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set");
70
71 if (qmail_open(&qqt) == -1)
72 strerr_die2sys(111,FATAL,"unable to fork: ");
73 qmail_puts(&qqt,dtline);
74 if (substdio_copy(&ssout,&ssin) != 0)
75 strerr_die2sys(111,FATAL,"unable to read message: ");
76 substdio_flush(&ssout);
77
78 num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
79
80 qmail_from(&qqt,sender);
81 qmail_to(&qqt,argv[1]);
82 qqx = qmail_close(&qqt);
83 if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
84 strerr_die2x(99,"condredirect: qp ",num);
85}
diff --git a/conf-break b/conf-break
new file mode 100644
index 0000000..28fd977
--- /dev/null
+++ b/conf-break
@@ -0,0 +1,9 @@
1-
2
3This character is the user-ext delimiter. The default delimiter is -,
4meaning that user joe controls joe-anything. Some system administrators
5prefer + or =.
6
7You can override this choice at run time with the qmail-users mechanism.
8
9Multicharacter 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 @@
1cc -O2
2
3This 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 @@
1qmail
2nofiles
3
4These are the qmail groups. The second group should not have access to
5any files, but it must be usable for processes; this requirement
6excludes 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 @@
1cc -s
2
3This 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 @@
1002
2
3These stat bits are not allowed in ~ and ~/.qmail. On most systems, the
4default umask is 022 or 077, so 022 will work here.
5
6Note 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 @@
1/var/qmail
2
3This is the qmail home directory. It must be a local directory, not
4shared among machines. This is where qmail queues all mail messages.
5
6The queue (except for bounce message contents) is crashproof, if the
7filesystem guarantees that single-byte writes are atomic and that
8directory operations are synchronous. These guarantees are provided by
9fixed-block filesystems such as UFS and by journaling filesystems. Under
10Linux, make sure that all mail-handling filesystems are mounted with
11synchronous 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 @@
1120
2
3This is a silent concurrency limit. You can't set it above 255. On some
4systems you can't set it above 125. qmail will refuse to compile if the
5limit 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 @@
123
2
3This 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 @@
1alias
2qmaild
3qmaill
4root
5qmailp
6qmailq
7qmailr
8qmails
9
10The qmail system is heavily partitioned for security; it does almost
11nothing as root.
12
13The first eight lines of this file are the alias user, the daemon user,
14the log user, the owner of miscellaneous files such as binaries, the
15passwd 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 @@
1fqdn="$1"
2echo Your fully qualified host name is "$fqdn".
3
4echo Putting "$fqdn" into control/me...
5echo "$fqdn" > QMAIL/control/me
6chmod 644 QMAIL/control/me
7
8( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | (
9 read ddom
10 echo Putting "$ddom" into control/defaultdomain...
11 echo "$ddom" > QMAIL/control/defaultdomain
12 chmod 644 QMAIL/control/defaultdomain
13) )
14
15( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | (
16 read pdom
17 echo Putting "$pdom" into control/plusdomain...
18 echo "$pdom" > QMAIL/control/plusdomain
19 chmod 644 QMAIL/control/plusdomain
20) )
21
22echo Putting "$fqdn" into control/locals...
23echo "$fqdn" >> QMAIL/control/locals
24chmod 644 QMAIL/control/locals
25
26echo Putting "$fqdn" into control/rcpthosts...
27echo "$fqdn" >> QMAIL/control/rcpthosts
28chmod 644 QMAIL/control/rcpthosts
29echo "Now qmail will refuse to accept SMTP messages except to $fqdn."
30echo '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 @@
1./hostname | tr '[A-Z]' '[a-z]' | (
2 if read host
3 then
4 echo Your hostname is "$host".
5 ./dnsfq "$host" | tr '[A-Z]' '[a-z]' | (
6 if read fqdn
7 then
8 echo Your host\'s fully qualified name in DNS is "$fqdn".
9 echo Putting "$fqdn" into control/me...
10 echo "$fqdn" > QMAIL/control/me
11 chmod 644 QMAIL/control/me
12 ( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | (
13 read ddom
14 echo Putting "$ddom" into control/defaultdomain...
15 echo "$ddom" > QMAIL/control/defaultdomain
16 chmod 644 QMAIL/control/defaultdomain
17 ) )
18 ( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | (
19 read pdom
20 echo Putting "$pdom" into control/plusdomain...
21 echo "$pdom" > QMAIL/control/plusdomain
22 chmod 644 QMAIL/control/plusdomain
23 ) )
24 echo ' '
25 echo Checking local IP addresses:
26 : > QMAIL/control/locals
27 chmod 644 QMAIL/control/locals
28 ( ./dnsip "$fqdn"
29 ./ipmeprint ) | sort -u | \
30 (
31 while read localip
32 do
33 echo "$localip: " | tr -d '\012'
34 ./dnsptr "$localip" 2>/dev/null | (
35 if read local
36 then
37 echo Adding "$local" to control/locals...
38 echo "$local" >> QMAIL/control/locals
39 else
40 echo PTR lookup failed. I assume this address has no DNS name.
41 fi
42 )
43 done
44 )
45 echo ' '
46 echo If there are any other domain names that point to you,
47 echo you will have to add them to QMAIL/control/locals.
48 echo You don\'t have to worry about aliases, i.e., domains with CNAME records.
49 echo ' '
50 echo Copying QMAIL/control/locals to QMAIL/control/rcpthosts...
51 cp QMAIL/control/locals QMAIL/control/rcpthosts
52 chmod 644 QMAIL/control/rcpthosts
53 echo 'Now qmail will refuse to accept SMTP messages except to those hosts.'
54 echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!'
55 else
56 echo Sorry, I couldn\'t find your host\'s canonical name in DNS.
57 echo You will have to set up control/me yourself.
58 fi
59 )
60 else
61 echo Sorry, I couldn\'t find your hostname.
62 echo You will have to set up control/me yourself.
63 fi
64)
diff --git a/constmap.c b/constmap.c
new file mode 100644
index 0000000..722e3b8
--- /dev/null
+++ b/constmap.c
@@ -0,0 +1,114 @@
1#include "constmap.h"
2#include "alloc.h"
3#include "case.h"
4
5static constmap_hash hash(s,len)
6char *s;
7int len;
8{
9 unsigned char ch;
10 constmap_hash h;
11 h = 5381;
12 while (len > 0) {
13 ch = *s++ - 'A';
14 if (ch <= 'Z' - 'A') ch += 'a' - 'A';
15 h = ((h << 5) + h) ^ ch;
16 --len;
17 }
18 return h;
19}
20
21char *constmap(cm,s,len)
22struct constmap *cm;
23char *s;
24int len;
25{
26 constmap_hash h;
27 int pos;
28 h = hash(s,len);
29 pos = cm->first[h & cm->mask];
30 while (pos != -1) {
31 if (h == cm->hash[pos])
32 if (len == cm->inputlen[pos])
33 if (!case_diffb(cm->input[pos],len,s))
34 return cm->input[pos] + cm->inputlen[pos] + 1;
35 pos = cm->next[pos];
36 }
37 return 0;
38}
39
40int constmap_init(cm,s,len,flagcolon)
41struct constmap *cm;
42char *s;
43int len;
44int flagcolon;
45{
46 int i;
47 int j;
48 int k;
49 int pos;
50 constmap_hash h;
51
52 cm->num = 0;
53 for (j = 0;j < len;++j) if (!s[j]) ++cm->num;
54
55 h = 64;
56 while (h && (h < cm->num)) h += h;
57 cm->mask = h - 1;
58
59 cm->first = (int *) alloc(sizeof(int) * h);
60 if (cm->first) {
61 cm->input = (char **) alloc(sizeof(char *) * cm->num);
62 if (cm->input) {
63 cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
64 if (cm->inputlen) {
65 cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
66 if (cm->hash) {
67 cm->next = (int *) alloc(sizeof(int) * cm->num);
68 if (cm->next) {
69 for (h = 0;h <= cm->mask;++h)
70 cm->first[h] = -1;
71 pos = 0;
72 i = 0;
73 for (j = 0;j < len;++j)
74 if (!s[j]) {
75 k = j - i;
76 if (flagcolon) {
77 for (k = i;k < j;++k)
78 if (s[k] == ':')
79 break;
80 if (k >= j) { i = j + 1; continue; }
81 k -= i;
82 }
83 cm->input[pos] = s + i;
84 cm->inputlen[pos] = k;
85 h = hash(s + i,k);
86 cm->hash[pos] = h;
87 h &= cm->mask;
88 cm->next[pos] = cm->first[h];
89 cm->first[h] = pos;
90 ++pos;
91 i = j + 1;
92 }
93 return 1;
94 }
95 alloc_free(cm->hash);
96 }
97 alloc_free(cm->inputlen);
98 }
99 alloc_free(cm->input);
100 }
101 alloc_free(cm->first);
102 }
103 return 0;
104}
105
106void constmap_free(cm)
107struct constmap *cm;
108{
109 alloc_free(cm->next);
110 alloc_free(cm->hash);
111 alloc_free(cm->inputlen);
112 alloc_free(cm->input);
113 alloc_free(cm->first);
114}
diff --git a/constmap.h b/constmap.h
new file mode 100644
index 0000000..3f29179
--- /dev/null
+++ b/constmap.h
@@ -0,0 +1,20 @@
1#ifndef CONSTMAP_H
2#define CONSTMAP_H
3
4typedef unsigned long constmap_hash;
5
6struct constmap {
7 int num;
8 constmap_hash mask;
9 constmap_hash *hash;
10 int *first;
11 int *next;
12 char **input;
13 int *inputlen;
14} ;
15
16extern int constmap_init();
17extern void constmap_free();
18extern char *constmap();
19
20#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 @@
1#include "readwrite.h"
2#include "open.h"
3#include "getln.h"
4#include "stralloc.h"
5#include "substdio.h"
6#include "error.h"
7#include "control.h"
8#include "alloc.h"
9#include "scan.h"
10
11static char inbuf[64];
12static stralloc line = {0};
13static stralloc me = {0};
14static int meok = 0;
15
16static void striptrailingwhitespace(sa)
17stralloc *sa;
18{
19 while (sa->len > 0)
20 switch(sa->s[sa->len - 1])
21 {
22 case '\n': case ' ': case '\t':
23 --sa->len;
24 break;
25 default:
26 return;
27 }
28}
29
30int control_init()
31{
32 int r;
33 r = control_readline(&me,"control/me");
34 if (r == 1) meok = 1;
35 return r;
36}
37
38int control_rldef(sa,fn,flagme,def)
39stralloc *sa;
40char *fn;
41int flagme;
42char *def;
43{
44 int r;
45 r = control_readline(sa,fn);
46 if (r) return r;
47 if (flagme) if (meok) return stralloc_copy(sa,&me) ? 1 : -1;
48 if (def) return stralloc_copys(sa,def) ? 1 : -1;
49 return r;
50}
51
52int control_readline(sa,fn)
53stralloc *sa;
54char *fn;
55{
56 substdio ss;
57 int fd;
58 int match;
59
60 fd = open_read(fn);
61 if (fd == -1) { if (errno == error_noent) return 0; return -1; }
62
63 substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
64
65 if (getln(&ss,sa,&match,'\n') == -1) { close(fd); return -1; }
66
67 striptrailingwhitespace(sa);
68 close(fd);
69 return 1;
70}
71
72int control_readint(i,fn)
73int *i;
74char *fn;
75{
76 unsigned long u;
77 switch(control_readline(&line,fn))
78 {
79 case 0: return 0;
80 case -1: return -1;
81 }
82 if (!stralloc_0(&line)) return -1;
83 if (!scan_ulong(line.s,&u)) return 0;
84 *i = u;
85 return 1;
86}
87
88int control_readfile(sa,fn,flagme)
89stralloc *sa;
90char *fn;
91int flagme;
92{
93 substdio ss;
94 int fd;
95 int match;
96
97 if (!stralloc_copys(sa,"")) return -1;
98
99 fd = open_read(fn);
100 if (fd == -1)
101 {
102 if (errno == error_noent)
103 {
104 if (flagme && meok)
105 {
106 if (!stralloc_copy(sa,&me)) return -1;
107 if (!stralloc_0(sa)) return -1;
108 return 1;
109 }
110 return 0;
111 }
112 return -1;
113 }
114
115 substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
116
117 for (;;)
118 {
119 if (getln(&ss,&line,&match,'\n') == -1) break;
120 if (!match && !line.len) { close(fd); return 1; }
121 striptrailingwhitespace(&line);
122 if (!stralloc_0(&line)) break;
123 if (line.s[0])
124 if (line.s[0] != '#')
125 if (!stralloc_cat(sa,&line)) break;
126 if (!match) { close(fd); return 1; }
127 }
128 close(fd);
129 return -1;
130}
diff --git a/control.h b/control.h
new file mode 100644
index 0000000..7cba89b
--- /dev/null
+++ b/control.h
@@ -0,0 +1,10 @@
1#ifndef CONTROL_H
2#define CONTROL_H
3
4extern int control_init();
5extern int control_readline();
6extern int control_rldef();
7extern int control_readint();
8extern int control_readfile();
9
10#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 @@
1#include "datetime.h"
2#include "fmt.h"
3#include "date822fmt.h"
4
5static char *montab[12] = {
6"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
7};
8
9unsigned int date822fmt(s,dt)
10char *s;
11struct datetime *dt;
12{
13 unsigned int i;
14 unsigned int len;
15 len = 0;
16 i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
17 i = fmt_str(s," "); len += i; if (s) s += i;
18 i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
19 i = fmt_str(s," "); len += i; if (s) s += i;
20 i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
21 i = fmt_str(s," "); len += i; if (s) s += i;
22 i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
23 i = fmt_str(s,":"); len += i; if (s) s += i;
24 i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
25 i = fmt_str(s,":"); len += i; if (s) s += i;
26 i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
27 i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
28 return len;
29}
diff --git a/date822fmt.h b/date822fmt.h
new file mode 100644
index 0000000..1848e5a
--- /dev/null
+++ b/date822fmt.h
@@ -0,0 +1,7 @@
1#ifndef DATE822FMT_H
2#define DATE822FMT_H
3
4extern unsigned int date822fmt();
5#define DATE822FMT 60
6
7#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 @@
1.TH datetime 3
2.SH NAME
3datetime \- convert between TAI labels and seconds
4.SH SYNTAX
5.B #include <datetime.h>
6
7void \fBdatetime_tai\fP(&\fIdt\fR,\fIt\fR);
8
9datetime_sec \fBdatetime_untai\fP(&\fIdt\fR);
10
11struct datetime \fIdt\fR;
12.br
13datetime_sec \fIt\fR;
14.SH DESCRIPTION
15International Atomic Time, TAI,
16is the fundamental unit for time measurements.
17TAI has one label for every second of real time,
18without complications such as leap seconds.
19
20A
21struct datetime
22variable,
23such as
24.IR dt ,
25stores a TAI label.
26.I dt\fB.year
27is the year number minus 1900;
28.I dt\fB.mon
29is the month number, from 0 (January) through 11 (December);
30.I dt\fB.mday
31is the day of the month, from 1 through 31;
32.I dt\fB.hour
33is the hour, from 0 through 23;
34.I dt\fB.min
35is the minute, from 0 through 59;
36.I dt\fB.sec
37is the second, from 0 through 59;
38.I dt\fB.wday
39is the day of the week, from 0 (Sunday) through 6 (Saturday);
40.I dt\fB.yday
41is the day of the year, from 0 through 365.
42
43The
44.B datetime
45library supports more convenient TAI manipulation with
46the datetime_sec type.
47A datetime_sec value, such as
48.IR t ,
49is an integer referring to the
50.IR t th
51second after the beginning of 1970 TAI.
52The first second of 1970 TAI was 0;
53the next second was 1;
54the last second of 1969 TAI was -1.
55The difference between two datetime_sec values is a number
56of real-time seconds.
57
58.B datetime_tai
59converts a datetime_sec to a TAI label.
60
61.B datetime_untai
62reads a TAI label
63(specifically
64.IR dt\fB.year ,
65.IR dt\fB.mon ,
66.IR dt\fB.mday ,
67.IR dt\fB.hour ,
68.IR dt\fB.min ,
69and
70.IR dt\fB.sec )
71and returns a datetime_sec.
72.SH "SEE ALSO"
73now(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 @@
1/* 19950925 */
2#include "datetime.h"
3
4void datetime_tai(dt,t)
5struct datetime *dt;
6datetime_sec t;
7{
8 int day;
9 int tod;
10 int year;
11 int yday;
12 int wday;
13 int mon;
14
15 tod = t % 86400;
16 day = t / 86400;
17 if (tod < 0) { tod += 86400; --day; }
18
19 dt->hour = tod / 3600;
20 tod %= 3600;
21 dt->min = tod / 60;
22 dt->sec = tod % 60;
23
24 wday = (day + 4) % 7; if (wday < 0) wday += 7;
25 dt->wday = wday;
26
27 day -= 11017;
28 /* day 0 is march 1, 2000 */
29 year = 5 + day / 146097;
30 day = day % 146097; if (day < 0) { day += 146097; --year; }
31 /* from now on, day is nonnegative */
32 year *= 4;
33 if (day == 146096) { year += 3; day = 36524; }
34 else { year += day / 36524; day %= 36524; }
35 year *= 25;
36 year += day / 1461;
37 day %= 1461;
38 year *= 4;
39 yday = (day < 306);
40 if (day == 1460) { year += 3; day = 365; }
41 else { year += day / 365; day %= 365; }
42 yday += day;
43
44 day *= 10;
45 mon = (day + 5) / 306;
46 day = day + 5 - 306 * mon;
47 day /= 10;
48 if (mon >= 10) { yday -= 306; ++year; mon -= 10; }
49 else { yday += 59; mon += 2; }
50
51 dt->yday = yday;
52 dt->year = year - 1900;
53 dt->mon = mon;
54 dt->mday = day + 1;
55}
diff --git a/datetime.h b/datetime.h
new file mode 100644
index 0000000..cde2a9b
--- /dev/null
+++ b/datetime.h
@@ -0,0 +1,20 @@
1#ifndef DATETIME_H
2#define DATETIME_H
3
4struct datetime {
5 int hour;
6 int min;
7 int sec;
8 int wday;
9 int mday;
10 int yday;
11 int mon;
12 int year;
13} ;
14
15typedef long datetime_sec;
16
17extern void datetime_tai();
18extern datetime_sec datetime_untai();
19
20#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 @@
1#include "datetime.h"
2
3/* roughly 100x faster than mktime() */
4datetime_sec datetime_untai(dt)
5struct datetime *dt;
6{
7 int year;
8 int day;
9 int mon;
10
11 year = dt->year + 1900;
12
13 mon = dt->mon;
14 if (mon >= 2) { mon -= 2; }
15 else { mon += 10; --year; }
16
17 day = (dt->mday - 1) * 10 + 5 + 306 * mon;
18 day /= 10;
19
20 if (day == 365) { year -= 3; day = 1460; }
21 else { day += 365 * (year % 4); }
22 year /= 4;
23
24 day += 1461 * (year % 25);
25 year /= 25;
26
27 if (day == 36524) { year -= 3; day = 146096; }
28 else { day += 36524 * (year % 4); }
29 year /= 4;
30
31 day += 146097 * (year - 5);
32 day += 11017;
33
34 return ((day * 24 + dt->hour) * 60 + dt->min) * 60 + dt->sec;
35}
diff --git a/direntry.3 b/direntry.3
new file mode 100644
index 0000000..8928fbb
--- /dev/null
+++ b/direntry.3
@@ -0,0 +1,36 @@
1.TH direntry 3
2.SH NAME
3direntry \- read directory entries
4.SH SYNTAX
5.B #include <direntry.h>
6
7DIR *\fBopendir\fP(\fIfn\fR);
8
9struct direntry *\fBreaddir\fP(\fIdir\fP);
10
11void \fBclosedir\fP(\fIdir\fP);
12
13DIR *\fIdir\fR;
14.br
15char *\fIfn\fR;
16.SH DESCRIPTION
17The point of
18.B direntry.h
19is to provide a uniform interface to BSD's
20.B sys/dir.h
21and POSIX's
22.BR dirent.h .
23
24The
25.B readdir
26interface is highly unsatisfactory.
27It does not distinguish between I/O errors and end-of-directory.
28It uses
29.BR malloc .
30The return type for
31.B closedir
32varies: some implementations return the
33.B close
34return value.
35.SH "SEE ALSO"
36readdir(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 @@
1#ifndef DIRENTRY_H
2#define DIRENTRY_H
3
4#include <sys/types.h>
5#include <sys/dir.h>
6#define direntry struct direct
7
8#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 @@
1#ifndef DIRENTRY_H
2#define DIRENTRY_H
3
4#include <sys/types.h>
5#include <dirent.h>
6#define direntry struct dirent
7
8#endif
diff --git a/dns.c b/dns.c
new file mode 100644
index 0000000..ed42787
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,400 @@
1#include <stdio.h>
2#include <netdb.h>
3#include <sys/types.h>
4#include <netinet/in.h>
5#include <arpa/nameser.h>
6#include <resolv.h>
7#include <errno.h>
8extern int res_query();
9extern int res_search();
10extern int errno;
11extern int h_errno;
12#include "ip.h"
13#include "ipalloc.h"
14#include "fmt.h"
15#include "alloc.h"
16#include "str.h"
17#include "stralloc.h"
18#include "dns.h"
19#include "case.h"
20
21static unsigned short getshort(c) unsigned char *c;
22{ unsigned short u; u = c[0]; return (u << 8) + c[1]; }
23
24static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
25static int responselen;
26static unsigned char *responseend;
27static unsigned char *responsepos;
28
29static int numanswers;
30static char name[MAXDNAME];
31static struct ip_address ip;
32unsigned short pref;
33
34static stralloc glue = {0};
35
36static int (*lookup)() = res_query;
37
38static int resolve(domain,type)
39stralloc *domain;
40int type;
41{
42 int n;
43 int i;
44
45 errno = 0;
46 if (!stralloc_copy(&glue,domain)) return DNS_MEM;
47 if (!stralloc_0(&glue)) return DNS_MEM;
48 responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
49 if (responselen <= 0)
50 {
51 if (errno == ECONNREFUSED) return DNS_SOFT;
52 if (h_errno == TRY_AGAIN) return DNS_SOFT;
53 return DNS_HARD;
54 }
55 if (responselen >= sizeof(response))
56 responselen = sizeof(response);
57 responseend = response.buf + responselen;
58 responsepos = response.buf + sizeof(HEADER);
59 n = ntohs(response.hdr.qdcount);
60 while (n-- > 0)
61 {
62 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
63 if (i < 0) return DNS_SOFT;
64 responsepos += i;
65 i = responseend - responsepos;
66 if (i < QFIXEDSZ) return DNS_SOFT;
67 responsepos += QFIXEDSZ;
68 }
69 numanswers = ntohs(response.hdr.ancount);
70 return 0;
71}
72
73static int findname(wanttype)
74int wanttype;
75{
76 unsigned short rrtype;
77 unsigned short rrdlen;
78 int i;
79
80 if (numanswers <= 0) return 2;
81 --numanswers;
82 if (responsepos == responseend) return DNS_SOFT;
83
84 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
85 if (i < 0) return DNS_SOFT;
86 responsepos += i;
87
88 i = responseend - responsepos;
89 if (i < 4 + 3 * 2) return DNS_SOFT;
90
91 rrtype = getshort(responsepos);
92 rrdlen = getshort(responsepos + 8);
93 responsepos += 10;
94
95 if (rrtype == wanttype)
96 {
97 if (dn_expand(response.buf,responseend,responsepos,name,MAXDNAME) < 0)
98 return DNS_SOFT;
99 responsepos += rrdlen;
100 return 1;
101 }
102
103 responsepos += rrdlen;
104 return 0;
105}
106
107static int findip(wanttype)
108int wanttype;
109{
110 unsigned short rrtype;
111 unsigned short rrdlen;
112 int i;
113
114 if (numanswers <= 0) return 2;
115 --numanswers;
116 if (responsepos == responseend) return DNS_SOFT;
117
118 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
119 if (i < 0) return DNS_SOFT;
120 responsepos += i;
121
122 i = responseend - responsepos;
123 if (i < 4 + 3 * 2) return DNS_SOFT;
124
125 rrtype = getshort(responsepos);
126 rrdlen = getshort(responsepos + 8);
127 responsepos += 10;
128
129 if (rrtype == wanttype)
130 {
131 if (rrdlen < 4)
132 return DNS_SOFT;
133 ip.d[0] = responsepos[0];
134 ip.d[1] = responsepos[1];
135 ip.d[2] = responsepos[2];
136 ip.d[3] = responsepos[3];
137 responsepos += rrdlen;
138 return 1;
139 }
140
141 responsepos += rrdlen;
142 return 0;
143}
144
145static int findmx(wanttype)
146int wanttype;
147{
148 unsigned short rrtype;
149 unsigned short rrdlen;
150 int i;
151
152 if (numanswers <= 0) return 2;
153 --numanswers;
154 if (responsepos == responseend) return DNS_SOFT;
155
156 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
157 if (i < 0) return DNS_SOFT;
158 responsepos += i;
159
160 i = responseend - responsepos;
161 if (i < 4 + 3 * 2) return DNS_SOFT;
162
163 rrtype = getshort(responsepos);
164 rrdlen = getshort(responsepos + 8);
165 responsepos += 10;
166
167 if (rrtype == wanttype)
168 {
169 if (rrdlen < 3)
170 return DNS_SOFT;
171 pref = (responsepos[0] << 8) + responsepos[1];
172 if (dn_expand(response.buf,responseend,responsepos + 2,name,MAXDNAME) < 0)
173 return DNS_SOFT;
174 responsepos += rrdlen;
175 return 1;
176 }
177
178 responsepos += rrdlen;
179 return 0;
180}
181
182void dns_init(flagsearch)
183int flagsearch;
184{
185 res_init();
186 if (flagsearch) lookup = res_search;
187}
188
189int dns_cname(sa)
190stralloc *sa;
191{
192 int r;
193 int loop;
194 for (loop = 0;loop < 10;++loop)
195 {
196 if (!sa->len) return loop;
197 if (sa->s[sa->len - 1] == ']') return loop;
198 if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
199 switch(resolve(sa,T_ANY))
200 {
201 case DNS_MEM: return DNS_MEM;
202 case DNS_SOFT: return DNS_SOFT;
203 case DNS_HARD: return loop;
204 default:
205 while ((r = findname(T_CNAME)) != 2)
206 {
207 if (r == DNS_SOFT) return DNS_SOFT;
208 if (r == 1)
209 {
210 if (!stralloc_copys(sa,name)) return DNS_MEM;
211 break;
212 }
213 }
214 if (r == 2) return loop;
215 }
216 }
217 return DNS_HARD; /* alias loop */
218}
219
220#define FMT_IAA 40
221
222static int iaafmt(s,ip)
223char *s;
224struct ip_address *ip;
225{
226 unsigned int i;
227 unsigned int len;
228 len = 0;
229 i = fmt_ulong(s,(unsigned long) ip->d[3]); len += i; if (s) s += i;
230 i = fmt_str(s,"."); len += i; if (s) s += i;
231 i = fmt_ulong(s,(unsigned long) ip->d[2]); len += i; if (s) s += i;
232 i = fmt_str(s,"."); len += i; if (s) s += i;
233 i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i;
234 i = fmt_str(s,"."); len += i; if (s) s += i;
235 i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i;
236 i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i;
237 return len;
238}
239
240int dns_ptr(sa,ip)
241stralloc *sa;
242struct ip_address *ip;
243{
244 int r;
245
246 if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM;
247 sa->len = iaafmt(sa->s,ip);
248 switch(resolve(sa,T_PTR))
249 {
250 case DNS_MEM: return DNS_MEM;
251 case DNS_SOFT: return DNS_SOFT;
252 case DNS_HARD: return DNS_HARD;
253 }
254 while ((r = findname(T_PTR)) != 2)
255 {
256 if (r == DNS_SOFT) return DNS_SOFT;
257 if (r == 1)
258 {
259 if (!stralloc_copys(sa,name)) return DNS_MEM;
260 return 0;
261 }
262 }
263 return DNS_HARD;
264}
265
266static int dns_ipplus(ia,sa,pref)
267ipalloc *ia;
268stralloc *sa;
269int pref;
270{
271 int r;
272 struct ip_mx ix;
273
274 if (!stralloc_copy(&glue,sa)) return DNS_MEM;
275 if (!stralloc_0(&glue)) return DNS_MEM;
276 if (glue.s[0]) {
277 ix.pref = 0;
278 if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
279 {
280 if (!ipalloc_append(ia,&ix)) return DNS_MEM;
281 return 0;
282 }
283 }
284
285 switch(resolve(sa,T_A))
286 {
287 case DNS_MEM: return DNS_MEM;
288 case DNS_SOFT: return DNS_SOFT;
289 case DNS_HARD: return DNS_HARD;
290 }
291 while ((r = findip(T_A)) != 2)
292 {
293 ix.ip = ip;
294 ix.pref = pref;
295 if (r == DNS_SOFT) return DNS_SOFT;
296 if (r == 1)
297 if (!ipalloc_append(ia,&ix)) return DNS_MEM;
298 }
299 return 0;
300}
301
302int dns_ip(ia,sa)
303ipalloc *ia;
304stralloc *sa;
305{
306 if (!ipalloc_readyplus(ia,0)) return DNS_MEM;
307 ia->len = 0;
308 return dns_ipplus(ia,sa,0);
309}
310
311int dns_mxip(ia,sa,random)
312ipalloc *ia;
313stralloc *sa;
314unsigned long random;
315{
316 int r;
317 struct mx { stralloc sa; unsigned short p; } *mx;
318 struct ip_mx ix;
319 int nummx;
320 int i;
321 int j;
322 int flagsoft;
323
324 if (!ipalloc_readyplus(ia,0)) return DNS_MEM;
325 ia->len = 0;
326
327 if (!stralloc_copy(&glue,sa)) return DNS_MEM;
328 if (!stralloc_0(&glue)) return DNS_MEM;
329 if (glue.s[0]) {
330 ix.pref = 0;
331 if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
332 {
333 if (!ipalloc_append(ia,&ix)) return DNS_MEM;
334 return 0;
335 }
336 }
337
338 switch(resolve(sa,T_MX))
339 {
340 case DNS_MEM: return DNS_MEM;
341 case DNS_SOFT: return DNS_SOFT;
342 case DNS_HARD: return dns_ip(ia,sa);
343 }
344
345 mx = (struct mx *) alloc(numanswers * sizeof(struct mx));
346 if (!mx) return DNS_MEM;
347 nummx = 0;
348
349 while ((r = findmx(T_MX)) != 2)
350 {
351 if (r == DNS_SOFT) { alloc_free(mx); return DNS_SOFT; }
352 if (r == 1)
353 {
354 mx[nummx].p = pref;
355 mx[nummx].sa.s = 0;
356 if (!stralloc_copys(&mx[nummx].sa,name))
357 {
358 while (nummx > 0) alloc_free(mx[--nummx].sa.s);
359 alloc_free(mx); return DNS_MEM;
360 }
361 ++nummx;
362 }
363 }
364
365 if (!nummx) return dns_ip(ia,sa); /* e.g., CNAME -> A */
366
367 flagsoft = 0;
368 while (nummx > 0)
369 {
370 unsigned long numsame;
371
372 i = 0;
373 numsame = 1;
374 for (j = 1;j < nummx;++j)
375 if (mx[j].p < mx[i].p)
376 {
377 i = j;
378 numsame = 1;
379 }
380 else if (mx[j].p == mx[i].p)
381 {
382 ++numsame;
383 random = random * 69069 + 1;
384 if ((random / 2) < (2147483647 / numsame))
385 i = j;
386 }
387
388 switch(dns_ipplus(ia,&mx[i].sa,mx[i].p))
389 {
390 case DNS_MEM: case DNS_SOFT:
391 flagsoft = 1; break;
392 }
393
394 alloc_free(mx[i].sa.s);
395 mx[i] = mx[--nummx];
396 }
397
398 alloc_free(mx);
399 return flagsoft;
400}
diff --git a/dns.h b/dns.h
new file mode 100644
index 0000000..bca9490
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,14 @@
1#ifndef DNS_H
2#define DNS_H
3
4#define DNS_SOFT -1
5#define DNS_HARD -2
6#define DNS_MEM -3
7
8void dns_init();
9int dns_cname();
10int dns_mxip();
11int dns_ip();
12int dns_ptr();
13
14#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 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "stralloc.h"
4#include "dns.h"
5#include "dnsdoe.h"
6#include "readwrite.h"
7#include "exit.h"
8
9stralloc sa = {0};
10
11void main(argc,argv)
12int argc;
13char **argv;
14{
15 if (!argv[1]) _exit(100);
16
17 if (!stralloc_copys(&sa,argv[1]))
18 { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
19
20 dns_init(0);
21 dnsdoe(dns_cname(&sa));
22 substdio_putflush(subfdout,sa.s,sa.len);
23 substdio_putsflush(subfdout,"\n");
24 _exit(0);
25}
diff --git a/dnsdoe.c b/dnsdoe.c
new file mode 100644
index 0000000..806ca53
--- /dev/null
+++ b/dnsdoe.c
@@ -0,0 +1,16 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "exit.h"
4#include "dns.h"
5#include "dnsdoe.h"
6
7void dnsdoe(r)
8int r;
9{
10 switch (r)
11 {
12 case DNS_HARD: substdio_putsflush(subfderr,"hard error\n"); _exit(100);
13 case DNS_SOFT: substdio_putsflush(subfderr,"soft error\n"); _exit(111);
14 case DNS_MEM: substdio_putsflush(subfderr,"out of memory\n"); _exit(111);
15 }
16}
diff --git a/dnsdoe.h b/dnsdoe.h
new file mode 100644
index 0000000..0e47bf6
--- /dev/null
+++ b/dnsdoe.h
@@ -0,0 +1,6 @@
1#ifndef DNSDOE_H
2#define DNSDOE_H
3
4extern void dnsdoe();
5
6#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 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "stralloc.h"
4#include "dns.h"
5#include "dnsdoe.h"
6#include "ip.h"
7#include "ipalloc.h"
8#include "exit.h"
9
10stralloc sa = {0};
11ipalloc ia = {0};
12
13void main(argc,argv)
14int argc;
15char **argv;
16{
17 if (!argv[1]) _exit(100);
18
19 if (!stralloc_copys(&sa,argv[1]))
20 { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
21
22 dns_init(1);
23 dnsdoe(dns_ip(&ia,&sa));
24 if (ia.len <= 0)
25 {
26 substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100);
27 }
28 dnsdoe(dns_ptr(&sa,&ia.ix[0].ip));
29 substdio_putflush(subfdout,sa.s,sa.len);
30 substdio_putsflush(subfdout,"\n");
31 _exit(0);
32}
diff --git a/dnsip.c b/dnsip.c
new file mode 100644
index 0000000..e7b671c
--- /dev/null
+++ b/dnsip.c
@@ -0,0 +1,34 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "stralloc.h"
4#include "dns.h"
5#include "dnsdoe.h"
6#include "ip.h"
7#include "ipalloc.h"
8#include "exit.h"
9
10char temp[IPFMT];
11
12stralloc sa = {0};
13ipalloc ia = {0};
14
15void main(argc,argv)
16int argc;
17char **argv;
18{
19 int j;
20
21 if (!argv[1]) _exit(100);
22
23 if (!stralloc_copys(&sa,argv[1]))
24 { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
25
26 dns_init(0);
27 dnsdoe(dns_ip(&ia,&sa));
28 for (j = 0;j < ia.len;++j)
29 {
30 substdio_put(subfdout,temp,ip_fmt(temp,&ia.ix[j].ip));
31 substdio_putsflush(subfdout,"\n");
32 }
33 _exit(0);
34}
diff --git a/dnsmxip.c b/dnsmxip.c
new file mode 100644
index 0000000..6d8e137
--- /dev/null
+++ b/dnsmxip.c
@@ -0,0 +1,40 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "stralloc.h"
4#include "fmt.h"
5#include "dns.h"
6#include "dnsdoe.h"
7#include "ip.h"
8#include "ipalloc.h"
9#include "now.h"
10#include "exit.h"
11
12char temp[IPFMT + FMT_ULONG];
13
14stralloc sa = {0};
15ipalloc ia = {0};
16
17void main(argc,argv)
18int argc;
19char **argv;
20{
21 int j;
22 unsigned long r;
23
24 if (!argv[1]) _exit(100);
25
26 if (!stralloc_copys(&sa,argv[1]))
27 { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
28
29 r = now() + getpid();
30 dns_init(0);
31 dnsdoe(dns_mxip(&ia,&sa,r));
32 for (j = 0;j < ia.len;++j)
33 {
34 substdio_put(subfdout,temp,ip_fmt(temp,&ia.ix[j].ip));
35 substdio_puts(subfdout," ");
36 substdio_put(subfdout,temp,fmt_ulong(temp,(unsigned long) ia.ix[j].pref));
37 substdio_putsflush(subfdout,"\n");
38 }
39 _exit(0);
40}
diff --git a/dnsptr.c b/dnsptr.c
new file mode 100644
index 0000000..6a92fe0
--- /dev/null
+++ b/dnsptr.c
@@ -0,0 +1,27 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "stralloc.h"
4#include "str.h"
5#include "scan.h"
6#include "dns.h"
7#include "dnsdoe.h"
8#include "ip.h"
9#include "exit.h"
10
11stralloc sa = {0};
12struct ip_address ip;
13
14void main(argc,argv)
15int argc;
16char **argv;
17{
18 if (!argv[1]) _exit(100);
19
20 ip_scan(argv[1],&ip);
21
22 dns_init(0);
23 dnsdoe(dns_ptr(&sa,&ip));
24 substdio_putflush(subfdout,sa.s,sa.len);
25 substdio_putsflush(subfdout,"\n");
26 _exit(0);
27}
diff --git a/dot-qmail.9 b/dot-qmail.9
new file mode 100644
index 0000000..4fa1358
--- /dev/null
+++ b/dot-qmail.9
@@ -0,0 +1,394 @@
1.TH dot-qmail 5
2.SH NAME
3dot-qmail \- control the delivery of mail messages
4.SH DESCRIPTION
5Normally the
6.B qmail-local
7program delivers each incoming message to your system mailbox,
8.IR homedir\fB/Mailbox ,
9where
10.I homedir
11is your home directory.
12
13It can instead
14write the mail to a different file or directory,
15forward it to another address,
16distribute it to a mailing list,
17or even execute programs,
18all under your control.
19.SH "THE QMAIL FILE"
20To change
21.BR qmail-local 's
22behavior, set up a
23.B .qmail
24file in your home directory.
25
26.B .qmail
27contains one or more lines.
28Each line is a delivery instruction.
29.B qmail-local
30follows each instruction in turn.
31There are five types of delivery instructions:
32(1) comment; (2) program; (3) forward; (4) mbox; (5) maildir.
33.TP 5
34(1)
35A comment line begins with a number sign:
36
37.EX
38 # this is a comment
39.EE
40
41.B qmail-local
42ignores the line.
43.TP 5
44(2)
45A program line begins with a vertical bar:
46
47.EX
48 |preline /usr/ucb/vacation djb
49.EE
50
51.B qmail-local
52takes the rest of the line as a command to supply to
53.BR sh .
54See
55.B qmail-command(8)
56for further information.
57.TP 5
58(3)
59A forward line begins with an ampersand:
60
61.EX
62 &me@new.job.com
63.EE
64
65.B qmail-local
66takes the rest of the line as a mail address;
67it uses
68.B qmail-queue
69to forward the message to that address.
70The address must contain a fully qualified domain name;
71it must not contain extra spaces, angle brackets, or comments:
72
73.EX
74 # the following examples are WRONG
75.br
76 &me@new
77.br
78 &<me@new.job.com>
79.br
80 & me@new.job.com
81.br
82 &me@new.job.com (New Address)
83.EE
84
85If the address begins with a letter or number,
86you may leave out the ampersand:
87
88.EX
89 me@new.job.com
90.EE
91
92Note that
93.B qmail-local
94omits its new
95.B Return-Path
96line when forwarding messages.
97.TP 5
98(4)
99An
100.I mbox
101line begins with a slash or dot,
102and does not end with a slash:
103
104.EX
105 /home/djb/Mailbox.sos
106.EE
107
108.B qmail-local
109takes the entire line as a filename.
110It appends the mail message to that file,
111using
112.BR flock -style
113file locking if possible.
114.B qmail-local
115stores the mail message in
116.I mbox
117format, as described in
118.BR mbox(5) .
119
120.B WARNING:
121On many systems,
122anyone who can read a file can
123.B flock
124it, and thus hold up
125.BR qmail-local 's
126delivery forever.
127Do not deliver mail to a publicly accessible file!
128
129If
130.B qmail-local
131is able to lock the file, but has trouble writing to it
132(because, for example, the disk is full),
133it will truncate the file back to its original length.
134However, it cannot prevent mailbox corruption if the system
135crashes during delivery.
136.TP 5
137(5)
138A
139.I maildir
140line begins with a slash or dot,
141and ends with a slash:
142
143.EX
144 /home/djb/Maildir/
145.EE
146
147.B qmail-local
148takes the entire line as the name of a directory in
149.I maildir
150format.
151It reliably stores the incoming message in that directory.
152See
153.B maildir(5)
154for more details.
155.PP
156If
157.B .qmail
158has the execute bit set,
159it must not contain any
160program lines,
161.I mbox
162lines,
163or
164.I maildir
165lines.
166If
167.B qmail-local
168sees any such lines,
169it will stop and indicate a temporary failure.
170
171If
172.B .qmail
173is completely empty (0 bytes long), or does not exist,
174.B qmail-local
175follows the
176.I defaultdelivery
177instructions set by your system administrator;
178normally
179.I defaultdelivery
180is
181.BR ./Mailbox ,
182so
183.B qmail-local
184appends the mail message to
185.B Mailbox
186in
187.I mbox
188format.
189
190.B .qmail
191may contain extra spaces and tabs at the end of a line.
192Blank lines are allowed, but not for the first line of
193.BR .qmail .
194
195If
196.B .qmail
197is world-writable or group-writable,
198.B qmail-local
199stops and indicates a temporary failure.
200.SH "SAFE QMAIL EDITING"
201Incoming messages can arrive at any moment.
202If you want to safely edit your
203.B .qmail
204file, first set the sticky bit on your home directory:
205
206.EX
207 chmod +t $HOME
208.EE
209
210.B qmail-local
211will temporarily defer delivery of any message to you
212if your home directory is sticky
213(or group-writable or other-writable,
214which should never happen).
215Make sure to
216
217.EX
218 chmod -t $HOME
219.EE
220
221when you are done!
222It's a good idea to test your new
223.B .qmail
224file as follows:
225
226.EX
227 qmail-local -n $USER ~ $USER '' '' '' '' ./Mailbox
228.EE
229.SH "EXTENSION ADDRESSES"
230In the
231.B qmail
232system,
233you control all local addresses of the form
234.IR user\fBBREAK\fIanything ,
235as well as the address
236.I user
237itself,
238where
239.I user
240is your account name.
241Delivery to
242.I user\fBBREAK\fIanything
243is controlled by the file
244.IR homedir/\fB.qmail\-\fIanything .
245(These rules may be changed by the system administrator;
246see
247.BR qmail-users (5).)
248
249The
250.B alias
251user controls all other addresses.
252Delivery to
253.I local
254is controlled by the file
255.IR homedir/\fB.qmail\-\fIlocal ,
256where
257.I homedir
258is
259.BR alias 's
260home directory.
261
262In the following description,
263.B qmail-local
264is handling a message addressed to
265.IR local@domain ,
266where
267.I local
268is controlled by
269.BR .qmail\-\fIext .
270Here is what it does.
271
272If
273.B .qmail\-\fIext
274is completely empty,
275.B qmail-local
276follows the
277.I defaultdelivery
278instructions set by your system administrator.
279
280If
281.B .qmail\-\fIext
282doesn't exist,
283.B qmail-local
284will try some default
285.B .qmail
286files.
287For example,
288if
289.I ext
290is
291.BR foo-bar ,
292.B qmail-local
293will try first
294.BR .qmail-foo-bar ,
295then
296.BR .qmail-foo-default ,
297and finally
298.BR .qmail-default .
299If none of these exist,
300.B qmail-local
301will bounce the message.
302(Exception: for the basic
303.I user
304address,
305.B qmail-local
306treats a nonexistent
307.B .qmail
308the same as an empty
309.BR .qmail .)
310
311.B WARNING:
312For security,
313.B qmail-local
314replaces any dots in
315.I ext
316with colons before checking
317.BR .qmail\-\fIext .
318For convenience,
319.B qmail-local
320converts any uppercase letters in
321.I ext
322to lowercase.
323
324When
325.B qmail-local
326forwards a message as instructed in
327.B .qmail\-\fIext
328(or
329.BR .qmail-default ),
330it checks whether
331.B .qmail\-\fIext\fB-owner\fP
332exists.
333If so,
334it uses
335.I local\fB-owner@\fIdomain
336as the envelope sender for the forwarded message.
337Otherwise it retains the envelope sender of the original message.
338Exception:
339.B qmail-local
340always retains the original envelope sender
341if it is the empty address or
342.BR #@[] ,
343i.e., if this is a bounce message.
344
345.B qmail-local
346also supports
347.B variable envelope return paths
348(VERPs):
349if
350.B .qmail\-\fIext\fB-owner\fP
351and
352.B .qmail\-\fIext\fB-owner-default\fP
353both exist, it uses
354.I local\fB\-owner\-@\fIdomain\fB-@[]
355as the envelope sender.
356This will cause a recipient
357.I recip\fB@\fIreciphost
358to see an envelope sender of
359.IR local\fB\-owner\-\fIrecip\fB=\fIreciphost\fB@\fIdomain .
360.SH "ERROR HANDLING"
361If a delivery instruction fails,
362.B qmail-local
363stops immediately and reports failure.
364.B qmail-local
365handles forwarding after all other instructions,
366so any error in another type of delivery will prevent all forwarding.
367
368If a program returns exit code 99,
369.B qmail-local
370ignores all succeeding lines in
371.BR .qmail ,
372but it still pays attention to previous forward lines.
373
374To set up independent instructions,
375where a temporary or permanent failure in one instruction
376does not affect the others,
377move each instruction into a separate
378.B .qmail\-\fIext
379file, and set up a central
380.B .qmail
381file that forwards to all of the
382.BR .qmail\-\fIext s.
383Note that
384.B qmail-local
385can handle any number of forward lines simultaneously.
386.SH "SEE ALSO"
387envelopes(5),
388maildir(5),
389mbox(5),
390qmail-users(5),
391qmail-local(8),
392qmail-command(8),
393qmail-queue(8),
394qmail-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 @@
1.TH env 3
2.SH NAME
3env \- manage the environment
4.SH SYNTAX
5.B #include <env.h>
6
7char **\fBenviron\fP;
8
9char *\fBenv_get\fP(\fIname\fR);
10.br
11char *\fBenv_pick\fP();
12
13char *\fIname\fR;
14.SH DESCRIPTION
15The environment,
16.BR environ ,
17is a 0-terminated array of 0-terminated strings,
18called environment variables.
19Each environment variable is of the form
20.IR name\fB=\fIvalue .
21
22.B env_get
23returns the value of the first variable whose name is
24.IR name ,
25or 0 if there is no such variable.
26
27.B env_pick
28returns any variable in the environment,
29or 0 if the environment is empty.
30.SH "SEE ALSO"
31environ(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 @@
1/* env.c, envread.c, env.h: environ library
2Daniel J. Bernstein, djb@silverton.berkeley.edu.
3Depends on str.h, alloc.h.
4Requires environ.
519960113: rewrite. warning: interface is different.
6No known patent problems.
7*/
8
9#include "str.h"
10#include "alloc.h"
11#include "env.h"
12
13int env_isinit = 0; /* if env_isinit: */
14static int ea; /* environ is a pointer to ea+1 char*'s. */
15static int en; /* the first en of those are ALLOCATED. environ[en] is 0. */
16
17static void env_goodbye(i) int i;
18{
19 alloc_free(environ[i]);
20 environ[i] = environ[--en];
21 environ[en] = 0;
22}
23
24static char *null = 0;
25
26void env_clear()
27{
28 if (env_isinit) while (en) env_goodbye(0);
29 else environ = &null;
30}
31
32static void env_unsetlen(s,len) char *s; int len;
33{
34 int i;
35 for (i = en - 1;i >= 0;--i)
36 if (!str_diffn(s,environ[i],len))
37 if (environ[i][len] == '=')
38 env_goodbye(i);
39}
40
41int env_unset(s) char *s;
42{
43 if (!env_isinit) if (!env_init()) return 0;
44 env_unsetlen(s,str_len(s));
45 return 1;
46}
47
48static int env_add(s) char *s;
49{
50 char *t;
51 t = env_findeq(s);
52 if (t) env_unsetlen(s,t - s);
53 if (en == ea)
54 {
55 ea += 30;
56 if (!alloc_re(&environ,(en + 1) * sizeof(char *),(ea + 1) * sizeof(char *)))
57 { ea = en; return 0; }
58 }
59 environ[en++] = s;
60 environ[en] = 0;
61 return 1;
62}
63
64int env_put(s) char *s;
65{
66 char *u;
67 if (!env_isinit) if (!env_init()) return 0;
68 u = alloc(str_len(s) + 1);
69 if (!u) return 0;
70 str_copy(u,s);
71 if (!env_add(u)) { alloc_free(u); return 0; }
72 return 1;
73}
74
75int env_put2(s,t) char *s; char *t;
76{
77 char *u;
78 int slen;
79 if (!env_isinit) if (!env_init()) return 0;
80 slen = str_len(s);
81 u = alloc(slen + str_len(t) + 2);
82 if (!u) return 0;
83 str_copy(u,s);
84 u[slen] = '=';
85 str_copy(u + slen + 1,t);
86 if (!env_add(u)) { alloc_free(u); return 0; }
87 return 1;
88}
89
90int env_init()
91{
92 char **newenviron;
93 int i;
94 for (en = 0;environ[en];++en) ;
95 ea = en + 10;
96 newenviron = (char **) alloc((ea + 1) * sizeof(char *));
97 if (!newenviron) return 0;
98 for (en = 0;environ[en];++en)
99 {
100 newenviron[en] = alloc(str_len(environ[en]) + 1);
101 if (!newenviron[en])
102 {
103 for (i = 0;i < en;++i) alloc_free(newenviron[i]);
104 alloc_free(newenviron);
105 return 0;
106 }
107 str_copy(newenviron[en],environ[en]);
108 }
109 newenviron[en] = 0;
110 environ = newenviron;
111 env_isinit = 1;
112 return 1;
113}
diff --git a/env.h b/env.h
new file mode 100644
index 0000000..9befc79
--- /dev/null
+++ b/env.h
@@ -0,0 +1,17 @@
1#ifndef ENV_H
2#define ENV_H
3
4extern int env_isinit;
5
6extern int env_init();
7extern int env_put();
8extern int env_put2();
9extern int env_unset();
10extern /*@null@*/char *env_get();
11extern char *env_pick();
12extern void env_clear();
13extern char *env_findeq();
14
15extern char **environ;
16
17#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 @@
1.TH envelopes 5
2.SH "NAME"
3envelopes \- sender/recipient lists attached to messages
4.SH "INTRODUCTION"
5Electronic mail messages are delivered in
6.IR envelopes .
7
8An envelope lists a
9.I sender
10and one or more
11.IR recipients .
12Usually these
13envelope addresses are the same
14as the addresses listed in the message header:
15
16.EX
17 (envelope) from djb to root
18.br
19 From: djb
20.br
21 To: root
22.EE
23
24In more complicated situations, though,
25the envelope addresses may differ from the header addresses.
26.SH "ENVELOPE EXAMPLES"
27When a message is delivered to
28several people at different locations,
29it is first photocopied
30and placed into several envelopes:
31
32.EX
33 (envelope) from djb to root
34.br
35 From: djb Copy #1 of message
36.br
37 To: root, god@brl.mil
38.EE
39
40.EX
41 (envelope) from djb to god@brl.mil
42.br
43 From: djb Copy #2 of message
44.br
45 To: root, god@brl.mil
46.EE
47
48When a message is delivered
49to several people at the same location,
50the sender doesn't have to photocopy it.
51He can instead stuff it into
52one envelope with several addresses;
53the recipients will make the photocopy:
54
55.EX
56 (envelope) from djb to god@brl.mil, angel@brl.mil
57.br
58 From: djb
59.br
60 To: god@brl.mil, angel@brl.mil, joe, frde
61.EE
62
63Bounced mail is sent back to the envelope sender address.
64The bounced mail doesn't list an envelope sender,
65so bounce loops are impossible:
66
67.EX
68 (envelope) from <> to djb
69.br
70 From: MAILER-DAEMON
71.br
72 To: djb
73.br
74 Subject: unknown user frde
75.EE
76
77The recipient of a message may make another copy
78and forward it in a new envelope:
79
80.EX
81 (envelope) from djb to joe
82.br
83 From: djb Original message
84.br
85 To: joe
86.EE
87
88.EX
89 (envelope) from joe to fred
90.br
91 From: djb Forwarded message
92.br
93 To: joe
94.EE
95
96A mailing list works almost the same way:
97
98.EX
99 (envelope) from djb to sos-list
100.br
101 From: djb Original message
102.br
103 To: sos-list
104.EE
105
106.EX
107 (envelope) from sos-owner to god@brl.mil
108.br
109 From: djb Forwarded message
110.br
111 To: sos-list to recipient #1
112.EE
113
114.EX
115 (envelope) from sos-owner to frde
116.br
117 From: djb Forwarded message
118.br
119 To: sos-list to recipient #2
120.EE
121
122Notice that the mailing list is set up
123to replace the envelope sender with something new,
124.BR sos-owner .
125So bounces will come back to
126.BR sos-owner :
127
128.EX
129 (envelope) from <> to sos-owner
130.br
131 From: MAILER-DAEMON
132.br
133 To: sos-owner
134.br
135 Subject: unknown user frde
136.EE
137
138It's a good idea to set up an extra address,
139.BR sos-owner ,
140like this:
141the original envelope sender (\fBdjb\fP)
142has no way to fix bad
143.B sos-list
144addresses,
145and of course bounces must not be sent to
146.B sos-list
147itself.
148.SH "HOW ENVELOPE ADDRESSES ARE STORED"
149Envelope sender and envelope recipient addresses
150are transmitted and recorded in several ways.
151
152When a user injects mail through
153.BR qmail-inject ,
154he can supply a
155.B Return-Path
156line or a
157.B \-f
158option for the envelope sender;
159by default the envelope sender is his login name.
160The envelope recipient addresses can be taken
161from the command line or from various header fields,
162depending on the options to
163.BR qmail-inject .
164Similar comments apply to
165.BR sendmail .
166
167When a message is transferred from one machine to another through SMTP,
168the envelope sender is given in a
169.B MAIL FROM
170command,
171the envelope recipients are given in
172.B RCPT TO
173commands,
174and the message is supplied separately by a
175.B DATA
176command.
177
178When a message is delivered by
179.B qmail
180to a single local recipient,
181.B qmail-local
182records the recipient in
183.B Delivered-To
184and the envelope sender in
185.BR Return-Path .
186It uses
187.B Delivered-To
188to detect mail forwarding loops.
189
190.B sendmail
191normally records the envelope sender in
192.BR Return-Path .
193It does not record envelope recipient addresses,
194on the theory that they are redundant:
195you received the mail,
196so you must have been one of the envelope recipients.
197
198Note that,
199if the header doesn't have any recipient addresses,
200.B sendmail
201will move envelope recipient addresses back into the header.
202This situation occurs if all addresses were originally listed as
203.BR Bcc ,
204since
205.B Bcc
206is automatically removed.
207When
208.B sendmail
209sees this, it creates a new
210.B Apparently-To
211header field with the envelope recipient addresses.
212This has the strange effect that each blind-carbon-copy recipient will see
213a list of all recipients on the same machine.
214
215When a message is stored in
216.B mbox
217format,
218the envelope sender is recorded at the top of the message
219as a UUCP-style
220.B From
221(no colon) line.
222Note that this line is less reliable than the
223.B Return-Path
224line added by
225.B qmail-local
226or
227.B sendmail\fP.
228.SH "SEE ALSO"
229qmail-header(5),
230qmail-local(8),
231qmail-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 @@
1#include "env.h"
2#include "str.h"
3
4extern /*@null@*/char *env_get(s)
5char *s;
6{
7 int i;
8 unsigned int slen;
9 char *envi;
10
11 slen = str_len(s);
12 for (i = 0;envi = environ[i];++i)
13 if ((!str_diffn(s,envi,slen)) && (envi[slen] == '='))
14 return envi + slen + 1;
15 return 0;
16}
17
18extern char *env_pick()
19{
20 return environ[0];
21}
22
23extern char *env_findeq(s)
24char *s;
25{
26 for (;*s;++s)
27 if (*s == '=')
28 return s;
29 return 0;
30}
diff --git a/error.3 b/error.3
new file mode 100644
index 0000000..e955b35
--- /dev/null
+++ b/error.3
@@ -0,0 +1,45 @@
1.TH error 3
2.SH NAME
3error \- syscall error codes
4.SH SYNTAX
5.B #include <error.h>
6
7extern int \fBerrno\fP;
8
9extern int \fBerror_intr\fP;
10.br
11extern int \fBerror_nomem\fP;
12.br
13extern int \fBerror_noent\fP;
14.br
15extern int \fBerror_txtbsy\fP;
16.br
17extern int \fBerror_io\fP;
18.br
19extern int \fBerror_exist\fP;
20.br
21extern int \fBerror_timeout\fP;
22.br
23extern int \fBerror_inprogress\fP;
24.br
25extern int \fBerror_wouldblock\fP;
26.br
27extern int \fBerror_again\fP;
28.br
29extern int \fBerror_pipe\fP;
30.br
31extern int \fBerror_perm\fP;
32.br
33extern int \fBerror_acces\fP;
34.SH DESCRIPTION
35UNIX syscalls provide detailed error codes in the
36.B errno
37variable.
38The
39.B error
40library provides portable names for a variety of possible
41.B errno
42values.
43.SH "SEE ALSO"
44error_str(3),
45error_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 @@
1#include <errno.h>
2#include "error.h"
3
4/* warning: as coverage improves here, should update error_{str,temp} */
5
6int error_intr =
7#ifdef EINTR
8EINTR;
9#else
10-1;
11#endif
12
13int error_nomem =
14#ifdef ENOMEM
15ENOMEM;
16#else
17-2;
18#endif
19
20int error_noent =
21#ifdef ENOENT
22ENOENT;
23#else
24-3;
25#endif
26
27int error_txtbsy =
28#ifdef ETXTBSY
29ETXTBSY;
30#else
31-4;
32#endif
33
34int error_io =
35#ifdef EIO
36EIO;
37#else
38-5;
39#endif
40
41int error_exist =
42#ifdef EEXIST
43EEXIST;
44#else
45-6;
46#endif
47
48int error_timeout =
49#ifdef ETIMEDOUT
50ETIMEDOUT;
51#else
52-7;
53#endif
54
55int error_inprogress =
56#ifdef EINPROGRESS
57EINPROGRESS;
58#else
59-8;
60#endif
61
62int error_wouldblock =
63#ifdef EWOULDBLOCK
64EWOULDBLOCK;
65#else
66-9;
67#endif
68
69int error_again =
70#ifdef EAGAIN
71EAGAIN;
72#else
73-10;
74#endif
75
76int error_pipe =
77#ifdef EPIPE
78EPIPE;
79#else
80-11;
81#endif
82
83int error_perm =
84#ifdef EPERM
85EPERM;
86#else
87-12;
88#endif
89
90int error_acces =
91#ifdef EACCES
92EACCES;
93#else
94-13;
95#endif
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..01bd3dc
--- /dev/null
+++ b/error.h
@@ -0,0 +1,23 @@
1#ifndef ERROR_H
2#define ERROR_H
3
4extern int errno;
5
6extern int error_intr;
7extern int error_nomem;
8extern int error_noent;
9extern int error_txtbsy;
10extern int error_io;
11extern int error_exist;
12extern int error_timeout;
13extern int error_inprogress;
14extern int error_wouldblock;
15extern int error_again;
16extern int error_pipe;
17extern int error_perm;
18extern int error_acces;
19
20extern char *error_str();
21extern int error_temp();
22
23#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 @@
1.TH error_str 3
2.SH NAME
3error_str \- names for syscall error codes
4.SH SYNTAX
5.B #include <error.h>
6
7char *\fBerror_str\fP(\fIe\fR);
8
9int \fIe\fR;
10.SH DESCRIPTION
11.B error_str
12returns a printable string describing syscall error code
13.IR e .
14Normally
15.I e
16is
17.BR errno .
18.SH "SEE ALSO"
19error(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 @@
1#include <errno.h>
2#include "error.h"
3
4#define X(e,s) if (i == e) return s;
5
6char *error_str(i)
7int i;
8{
9 X(0,"no error")
10 X(error_intr,"interrupted system call")
11 X(error_nomem,"out of memory")
12 X(error_noent,"file does not exist")
13 X(error_txtbsy,"text busy")
14 X(error_io,"input/output error")
15 X(error_exist,"file already exists")
16 X(error_timeout,"timed out")
17 X(error_inprogress,"operation in progress")
18 X(error_again,"temporary failure")
19 X(error_wouldblock,"input/output would block")
20 X(error_pipe,"broken pipe")
21 X(error_perm,"permission denied")
22 X(error_acces,"access denied")
23#ifdef ESRCH
24 X(ESRCH,"no such process")
25#endif
26#ifdef ENXIO
27 X(ENXIO,"device not configured")
28#endif
29#ifdef E2BIG
30 X(E2BIG,"argument list too long")
31#endif
32#ifdef ENOEXEC
33 X(ENOEXEC,"exec format error")
34#endif
35#ifdef EBADF
36 X(EBADF,"file descriptor not open")
37#endif
38#ifdef ECHILD
39 X(ECHILD,"no child processes")
40#endif
41#ifdef EDEADLK
42 X(EDEADLK,"operation would cause deadlock")
43#endif
44#ifdef EFAULT
45 X(EFAULT,"bad address")
46#endif
47#ifdef ENOTBLK
48 X(ENOTBLK,"not a block device")
49#endif
50#ifdef EBUSY
51 X(EBUSY,"device busy")
52#endif
53#ifdef EXDEV
54 X(EXDEV,"cross-device link")
55#endif
56#ifdef ENODEV
57 X(ENODEV,"device does not support operation")
58#endif
59#ifdef ENOTDIR
60 X(ENOTDIR,"not a directory")
61#endif
62#ifdef EISDIR
63 X(EISDIR,"is a directory")
64#endif
65#ifdef EINVAL
66 X(EINVAL,"invalid argument")
67#endif
68#ifdef ENFILE
69 X(ENFILE,"system cannot open more files")
70#endif
71#ifdef EMFILE
72 X(EMFILE,"process cannot open more files")
73#endif
74#ifdef ENOTTY
75 X(ENOTTY,"not a tty")
76#endif
77#ifdef EFBIG
78 X(EFBIG,"file too big")
79#endif
80#ifdef ENOSPC
81 X(ENOSPC,"out of disk space")
82#endif
83#ifdef ESPIPE
84 X(ESPIPE,"unseekable descriptor")
85#endif
86#ifdef EROFS
87 X(EROFS,"read-only file system")
88#endif
89#ifdef EMLINK
90 X(EMLINK,"too many links")
91#endif
92#ifdef EDOM
93 X(EDOM,"input out of range")
94#endif
95#ifdef ERANGE
96 X(ERANGE,"output out of range")
97#endif
98#ifdef EALREADY
99 X(EALREADY,"operation already in progress")
100#endif
101#ifdef ENOTSOCK
102 X(ENOTSOCK,"not a socket")
103#endif
104#ifdef EDESTADDRREQ
105 X(EDESTADDRREQ,"destination address required")
106#endif
107#ifdef EMSGSIZE
108 X(EMSGSIZE,"message too long")
109#endif
110#ifdef EPROTOTYPE
111 X(EPROTOTYPE,"incorrect protocol type")
112#endif
113#ifdef ENOPROTOOPT
114 X(ENOPROTOOPT,"protocol not available")
115#endif
116#ifdef EPROTONOSUPPORT
117 X(EPROTONOSUPPORT,"protocol not supported")
118#endif
119#ifdef ESOCKTNOSUPPORT
120 X(ESOCKTNOSUPPORT,"socket type not supported")
121#endif
122#ifdef EOPNOTSUPP
123 X(EOPNOTSUPP,"operation not supported")
124#endif
125#ifdef EPFNOSUPPORT
126 X(EPFNOSUPPORT,"protocol family not supported")
127#endif
128#ifdef EAFNOSUPPORT
129 X(EAFNOSUPPORT,"address family not supported")
130#endif
131#ifdef EADDRINUSE
132 X(EADDRINUSE,"address already used")
133#endif
134#ifdef EADDRNOTAVAIL
135 X(EADDRNOTAVAIL,"address not available")
136#endif
137#ifdef ENETDOWN
138 X(ENETDOWN,"network down")
139#endif
140#ifdef ENETUNREACH
141 X(ENETUNREACH,"network unreachable")
142#endif
143#ifdef ENETRESET
144 X(ENETRESET,"network reset")
145#endif
146#ifdef ECONNABORTED
147 X(ECONNABORTED,"connection aborted")
148#endif
149#ifdef ECONNRESET
150 X(ECONNRESET,"connection reset")
151#endif
152#ifdef ENOBUFS
153 X(ENOBUFS,"out of buffer space")
154#endif
155#ifdef EISCONN
156 X(EISCONN,"already connected")
157#endif
158#ifdef ENOTCONN
159 X(ENOTCONN,"not connected")
160#endif
161#ifdef ESHUTDOWN
162 X(ESHUTDOWN,"socket shut down")
163#endif
164#ifdef ETOOMANYREFS
165 X(ETOOMANYREFS,"too many references")
166#endif
167#ifdef ECONNREFUSED
168 X(ECONNREFUSED,"connection refused")
169#endif
170#ifdef ELOOP
171 X(ELOOP,"symbolic link loop")
172#endif
173#ifdef ENAMETOOLONG
174 X(ENAMETOOLONG,"file name too long")
175#endif
176#ifdef EHOSTDOWN
177 X(EHOSTDOWN,"host down")
178#endif
179#ifdef EHOSTUNREACH
180 X(EHOSTUNREACH,"host unreachable")
181#endif
182#ifdef ENOTEMPTY
183 X(ENOTEMPTY,"directory not empty")
184#endif
185#ifdef EPROCLIM
186 X(EPROCLIM,"too many processes")
187#endif
188#ifdef EUSERS
189 X(EUSERS,"too many users")
190#endif
191#ifdef EDQUOT
192 X(EDQUOT,"disk quota exceeded")
193#endif
194#ifdef ESTALE
195 X(ESTALE,"stale NFS file handle")
196#endif
197#ifdef EREMOTE
198 X(EREMOTE,"too many levels of remote in path")
199#endif
200#ifdef EBADRPC
201 X(EBADRPC,"RPC structure is bad")
202#endif
203#ifdef ERPCMISMATCH
204 X(ERPCMISMATCH,"RPC version mismatch")
205#endif
206#ifdef EPROGUNAVAIL
207 X(EPROGUNAVAIL,"RPC program unavailable")
208#endif
209#ifdef EPROGMISMATCH
210 X(EPROGMISMATCH,"program version mismatch")
211#endif
212#ifdef EPROCUNAVAIL
213 X(EPROCUNAVAIL,"bad procedure for program")
214#endif
215#ifdef ENOLCK
216 X(ENOLCK,"no locks available")
217#endif
218#ifdef ENOSYS
219 X(ENOSYS,"system call not available")
220#endif
221#ifdef EFTYPE
222 X(EFTYPE,"bad file type")
223#endif
224#ifdef EAUTH
225 X(EAUTH,"authentication error")
226#endif
227#ifdef ENEEDAUTH
228 X(ENEEDAUTH,"not authenticated")
229#endif
230#ifdef ENOSTR
231 X(ENOSTR,"not a stream device")
232#endif
233#ifdef ETIME
234 X(ETIME,"timer expired")
235#endif
236#ifdef ENOSR
237 X(ENOSR,"out of stream resources")
238#endif
239#ifdef ENOMSG
240 X(ENOMSG,"no message of desired type")
241#endif
242#ifdef EBADMSG
243 X(EBADMSG,"bad message type")
244#endif
245#ifdef EIDRM
246 X(EIDRM,"identifier removed")
247#endif
248#ifdef ENONET
249 X(ENONET,"machine not on network")
250#endif
251#ifdef ERREMOTE
252 X(ERREMOTE,"object not local")
253#endif
254#ifdef ENOLINK
255 X(ENOLINK,"link severed")
256#endif
257#ifdef EADV
258 X(EADV,"advertise error")
259#endif
260#ifdef ESRMNT
261 X(ESRMNT,"srmount error")
262#endif
263#ifdef ECOMM
264 X(ECOMM,"communication error")
265#endif
266#ifdef EPROTO
267 X(EPROTO,"protocol error")
268#endif
269#ifdef EMULTIHOP
270 X(EMULTIHOP,"multihop attempted")
271#endif
272#ifdef EREMCHG
273 X(EREMCHG,"remote address changed")
274#endif
275 return "unknown error";
276}
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 @@
1.TH error_temp 3
2.SH NAME
3error_temp \- identify soft syscall error codes
4.SH SYNTAX
5.B #include <error.h>
6
7int \fBerror_temp\fP(\fIe\fR);
8
9int \fIe\fR;
10.SH DESCRIPTION
11.B error_temp
12returns 1 if syscall error code
13.I e
14is a soft error, 0 if it is a hard error.
15Normally
16.I e
17is
18.BR errno .
19
20A hard error is persistent:
21file not found, read-only file system, symbolic link loop, etc.
22
23A soft error is usually transient:
24out of memory, out of disk space, I/O error, disk quota exceeded,
25connection refused, host unreachable, etc.
26.SH "SEE ALSO"
27error(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 @@
1#include <errno.h>
2#include "error.h"
3
4#define X(n) if (e == n) return 1;
5
6int error_temp(e)
7int e;
8{
9 X(error_intr)
10 X(error_nomem)
11 X(error_txtbsy)
12 X(error_io)
13 X(error_timeout)
14 X(error_wouldblock)
15 X(error_again)
16#ifdef EDEADLK
17 X(EDEADLK)
18#endif
19#ifdef EBUSY
20 X(EBUSY)
21#endif
22#ifdef ENFILE
23 X(ENFILE)
24#endif
25#ifdef EMFILE
26 X(EMFILE)
27#endif
28#ifdef EFBIG
29 X(EFBIG)
30#endif
31#ifdef ENOSPC
32 X(ENOSPC)
33#endif
34#ifdef ENETDOWN
35 X(ENETDOWN)
36#endif
37#ifdef ENETUNREACH
38 X(ENETUNREACH)
39#endif
40#ifdef ENETRESET
41 X(ENETRESET)
42#endif
43#ifdef ECONNABORTED
44 X(ECONNABORTED)
45#endif
46#ifdef ECONNRESET
47 X(ECONNRESET)
48#endif
49#ifdef ENOBUFS
50 X(ENOBUFS)
51#endif
52#ifdef ETOOMANYREFS
53 X(ETOOMANYREFS)
54#endif
55#ifdef ECONNREFUSED
56 X(ECONNREFUSED)
57#endif
58#ifdef EHOSTDOWN
59 X(EHOSTDOWN)
60#endif
61#ifdef EHOSTUNREACH
62 X(EHOSTUNREACH)
63#endif
64#ifdef EPROCLIM
65 X(EPROCLIM)
66#endif
67#ifdef EUSERS
68 X(EUSERS)
69#endif
70#ifdef EDQUOT
71 X(EDQUOT)
72#endif
73#ifdef ESTALE
74 X(ESTALE)
75#endif
76#ifdef ENOLCK
77 X(ENOLCK)
78#endif
79 return 0;
80}
diff --git a/except.1 b/except.1
new file mode 100644
index 0000000..4198cc2
--- /dev/null
+++ b/except.1
@@ -0,0 +1,33 @@
1.TH except 1
2.SH NAME
3except \- reverse the exit code of a program
4.SH SYNOPSIS
5.B except
6.I program
7[
8.I arg ...
9]
10.SH DESCRIPTION
11.B except
12runs
13.I program
14with the given arguments.
15
16If
17.I program
18exits 0,
19.B except
20exits 100.
21If
22.I program
23exits 111,
24.B except
25exits 111.
26If
27.I program
28exits anything else,
29.B except
30exits 0.
31.SH "SEE ALSO"
32bouncesaying(1),
33condredirect(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 @@
1#include "fork.h"
2#include "strerr.h"
3#include "wait.h"
4#include "error.h"
5#include "exit.h"
6
7#define FATAL "except: fatal: "
8
9void main(argc,argv)
10int argc;
11char **argv;
12{
13 int pid;
14 int wstat;
15
16 if (!argv[1])
17 strerr_die1x(100,"except: usage: except program [ arg ... ]");
18
19 pid = fork();
20 if (pid == -1)
21 strerr_die2sys(111,FATAL,"unable to fork: ");
22 if (pid == 0) {
23 execvp(argv[1],argv + 1);
24 if (error_temp(errno)) _exit(111);
25 _exit(100);
26 }
27
28 if (wait_pid(&wstat,pid) == -1)
29 strerr_die2x(111,FATAL,"wait failed");
30 if (wait_crashed(wstat))
31 strerr_die2x(111,FATAL,"child crashed");
32 switch(wait_exitcode(wstat)) {
33 case 0: _exit(100);
34 case 111: strerr_die2x(111,FATAL,"temporary child error");
35 default: _exit(0);
36 }
37}
diff --git a/exit.h b/exit.h
new file mode 100644
index 0000000..39011c8
--- /dev/null
+++ b/exit.h
@@ -0,0 +1,6 @@
1#ifndef EXIT_H
2#define EXIT_H
3
4extern void _exit();
5
6#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 @@
1#ifndef EXTRA_H
2#define EXTRA_H
3
4#define QUEUE_EXTRA ""
5#define QUEUE_EXTRALEN 0
6
7#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 @@
1#ifndef FD_H
2#define FD_H
3
4extern int fd_copy();
5extern int fd_move();
6
7#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 @@
1.TH fd_copy 3
2.SH NAME
3fd_copy \- duplicate a descriptor
4.SH SYNTAX
5.B #include <fd.h>
6
7int \fBfd_copy\fP(\fIto\fR,\fIfrom\fR);
8
9int \fIto\fR;
10.br
11int \fIfrom\fR;
12.SH DESCRIPTION
13.B fd_copy
14copies
15descriptor
16.I from
17to descriptor
18.IR to .
19If
20.I to
21was already open,
22.B fd_copy
23closes it.
24.B fd_copy
25always leaves
26.I from
27intact;
28if
29.I to
30and
31.I from
32are the same number,
33.B fd_copy
34does nothing.
35
36.B fd_copy
37returns 0 on success, -1 on error.
38.B fd_copy
39does not guarantee that
40.I to
41will remain open, if it was open, in case of error.
42.SH "SEE ALSO"
43dup(2),
44fd_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 @@
1#include <fcntl.h>
2#include "fd.h"
3
4int fd_copy(to,from)
5int to;
6int from;
7{
8 if (to == from) return 0;
9 if (fcntl(from,F_GETFL,0) == -1) return -1;
10 close(to);
11 if (fcntl(from,F_DUPFD,to) == -1) return -1;
12 return 0;
13}
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 @@
1.TH fd_move 3
2.SH NAME
3fd_move \- renumber a descriptor
4.SH SYNTAX
5.B #include <fd.h>
6
7int \fBfd_move\fP(\fIto\fR,\fIfrom\fR);
8
9int \fIto\fR;
10.br
11int \fIfrom\fR;
12.SH DESCRIPTION
13.B fd_move
14moves
15descriptor
16.I from
17to descriptor
18.IR to .
19If
20.I to
21was already open,
22.B fd_move
23closes it.
24If the move is successful,
25.B fd_move
26closes
27.IR from .
28Exception:
29if
30.I to
31and
32.I from
33are the same number,
34.B fd_move
35does nothing.
36
37.B fd_move
38returns 0 on success, -1 on error.
39.SH "SEE ALSO"
40dup(2),
41fd_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 @@
1#include "fd.h"
2
3int fd_move(to,from)
4int to;
5int from;
6{
7 if (to == from) return 0;
8 if (fd_copy(to,from) == -1) return -1;
9 close(from);
10 return 0;
11}
diff --git a/fifo.c b/fifo.c
new file mode 100644
index 0000000..1f3d150
--- /dev/null
+++ b/fifo.c
@@ -0,0 +1,10 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "hasmkffo.h"
4#include "fifo.h"
5
6#ifdef HASMKFIFO
7int fifo_make(fn,mode) char *fn; int mode; { return mkfifo(fn,mode); }
8#else
9int fifo_make(fn,mode) char *fn; int mode; { return mknod(fn,S_IFIFO | mode,0); }
10#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 @@
1#ifndef FIFO_H
2#define FIFO_H
3
4extern int fifo_make();
5
6#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 @@
1.TH fifo_make 3
2.SH NAME
3fifo_make \- create a named pipe
4.SH SYNTAX
5.B #include <fifo.h>
6
7int \fBfifo_make\fP(\fIfn\fR,\fImode\fR);
8
9char *\fIfn\fR;
10.br
11int \fImode\fR;
12.SH DESCRIPTION
13.B fifo_make
14creates a new named pipe
15with name
16.I fn
17and mode
18.I mode
19(modified by the process umask).
20
21.B fifo_make
22returns 0 on success, -1 on error.
23.SH "SEE ALSO"
24mkfifo(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 @@
1# oper-:arch-:syst-:chip-:kern-
2# oper = operating system type; e.g., sunos-4.1.4
3# arch = machine language; e.g., sparc
4# syst = which binaries can run; e.g., sun4
5# chip = chip model; e.g., micro-2-80
6# kern = kernel version; e.g., sun4m
7# dependence: arch --- chip
8# \ \
9# oper --- syst --- kern
10# so, for example, syst is interpreted in light of oper, but chip is not.
11# anyway, no slashes, no extra colons, no uppercase letters.
12# the point of the extra -'s is to ease parsing: can add hierarchies later.
13# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
14# and i386-486 (486s do have more instructions, you know) as well as i386.
15# the idea here is to include ALL useful available information.
16
17exec 2>/dev/null
18sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`"
19if [ x"$sys" != x ]
20then
21 unamer="`uname -r | tr /: ..`"
22 unamem="`uname -m | tr /: ..`"
23 unamev="`uname -v | tr /: ..`"
24
25 case "$sys" in
26 bsd.os)
27 # in bsd 4.4, uname -v does not have useful info.
28 # in bsd 4.4, uname -m is arch, not chip.
29 oper="$sys-$unamer"
30 arch="$unamem"
31 syst=""
32 chip="`sysctl -n hw.model`"
33 kern=""
34 ;;
35 freebsd)
36 # see above about bsd 4.4
37 oper="$sys-$unamer"
38 arch="$unamem"
39 syst=""
40 chip="`sysctl -n hw.model`" # hopefully
41 kern=""
42 ;;
43 netbsd)
44 # see above about bsd 4.4
45 oper="$sys-$unamer"
46 arch="$unamem"
47 syst=""
48 chip="`sysctl -n hw.model`" # hopefully
49 kern=""
50 ;;
51 linux)
52 # as in bsd 4.4, uname -v does not have useful info.
53 oper="$sys-$unamer"
54 syst=""
55 chip="$unamem"
56 kern=""
57 case "$chip" in
58 i386|i486|i586|i686)
59 arch="i386"
60 ;;
61 alpha)
62 arch="alpha"
63 ;;
64 esac
65 ;;
66 aix)
67 # naturally IBM has to get uname -r and uname -v backwards. dorks.
68 oper="$sys-$unamev-$unamer"
69 arch="`arch | tr /: ..`"
70 syst=""
71 chip="$unamem"
72 kern=""
73 ;;
74 sunos)
75 oper="$sys-$unamer-$unamev"
76 arch="`(uname -p || mach) | tr /: ..`"
77 syst="`arch | tr /: ..`"
78 chip="$unamem" # this is wrong; is there any way to get the real info?
79 kern="`arch -k | tr /: ..`"
80 ;;
81 unix_sv)
82 oper="$sys-$unamer-$unamev"
83 arch="`uname -m`"
84 syst=""
85 chip="$unamem"
86 kern=""
87 ;;
88 *)
89 oper="$sys-$unamer-$unamev"
90 arch="`arch | tr /: ..`"
91 syst=""
92 chip="$unamem"
93 kern=""
94 ;;
95 esac
96else
97 $CC -c trycpp.c
98 $LD -o trycpp trycpp.o
99 case `./trycpp` in
100 nextstep)
101 oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
102 arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
103 syst=""
104 chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
105 kern=""
106 ;;
107 *)
108 oper="unknown"
109 arch=""
110 syst=""
111 chip=""
112 kern=""
113 ;;
114 esac
115 rm -f trycpp.o trycpp
116fi
117
118case "$chip" in
11980486)
120 # let's try to be consistent here. (BSD/OS)
121 chip=i486
122 ;;
123i486DX)
124 # respect the hyphen hierarchy. (FreeBSD)
125 chip=i486-dx
126 ;;
127i486.DX2)
128 # respect the hyphen hierarchy. (FreeBSD)
129 chip=i486-dx2
130 ;;
131Intel.586)
132 # no, you nitwits, there is no such chip. (NeXTStep)
133 chip=pentium
134 ;;
135i586)
136 # no, you nitwits, there is no such chip. (Linux)
137 chip=pentium
138 ;;
139i686)
140 # STOP SAYING THAT! (Linux)
141 chip=ppro
142esac
143
144echo "$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 @@
1#ifndef FMT_H
2#define FMT_H
3
4#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
5#define FMT_LEN ((char *) 0) /* convenient abbreviation */
6
7extern unsigned int fmt_uint();
8extern unsigned int fmt_uint0();
9extern unsigned int fmt_xint();
10extern unsigned int fmt_nbbint();
11extern unsigned int fmt_ushort();
12extern unsigned int fmt_xshort();
13extern unsigned int fmt_nbbshort();
14extern unsigned int fmt_ulong();
15extern unsigned int fmt_xlong();
16extern unsigned int fmt_nbblong();
17
18extern unsigned int fmt_plusminus();
19extern unsigned int fmt_minus();
20extern unsigned int fmt_0x();
21
22extern unsigned int fmt_str();
23extern unsigned int fmt_strn();
24
25#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 @@
1#include "fmt.h"
2
3unsigned int fmt_str(s,t)
4register char *s; register char *t;
5{
6 register unsigned int len;
7 char ch;
8 len = 0;
9 if (s) { while (ch = t[len]) s[len++] = ch; }
10 else while (t[len]) len++;
11 return len;
12}
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 @@
1#include "fmt.h"
2
3unsigned int fmt_strn(s,t,n)
4register char *s; register char *t; register unsigned int n;
5{
6 register unsigned int len;
7 char ch;
8 len = 0;
9 if (s) { while (n-- && (ch = t[len])) s[len++] = ch; }
10 else while (n-- && t[len]) len++;
11 return len;
12}
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 @@
1#include "fmt.h"
2
3unsigned int fmt_uint(s,u) register char *s; register unsigned int u;
4{
5 register unsigned long l; l = u; return fmt_ulong(s,l);
6}
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 @@
1#include "fmt.h"
2
3unsigned int fmt_uint0(s,u,n) char *s; unsigned int u; unsigned int n;
4{
5 unsigned int len;
6 len = fmt_uint(FMT_LEN,u);
7 while (len < n) { if (s) *s++ = '0'; ++len; }
8 if (s) fmt_uint(s,u);
9 return len;
10}
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 @@
1#include "fmt.h"
2
3unsigned int fmt_ulong(s,u) register char *s; register unsigned long u;
4{
5 register unsigned int len; register unsigned long q;
6 len = 1; q = u;
7 while (q > 9) { ++len; q /= 10; }
8 if (s) {
9 s += len;
10 do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
11 }
12 return len;
13}
diff --git a/fmtqfn.c b/fmtqfn.c
new file mode 100644
index 0000000..6229b4a
--- /dev/null
+++ b/fmtqfn.c
@@ -0,0 +1,24 @@
1#include "fmtqfn.h"
2#include "fmt.h"
3#include "auto_split.h"
4
5unsigned int fmtqfn(s,dirslash,id,flagsplit)
6char *s;
7char *dirslash;
8unsigned long id;
9int flagsplit;
10{
11 unsigned int len;
12 unsigned int i;
13
14 len = 0;
15 i = fmt_str(s,dirslash); len += i; if (s) s += i;
16 if (flagsplit)
17 {
18 i = fmt_ulong(s,id % auto_split); len += i; if (s) s += i;
19 i = fmt_str(s,"/"); len += i; if (s) s += i;
20 }
21 i = fmt_ulong(s,id); len += i; if (s) s += i;
22 if (s) *s++ = 0; ++len;
23 return len;
24}
diff --git a/fmtqfn.h b/fmtqfn.h
new file mode 100644
index 0000000..a449aa1
--- /dev/null
+++ b/fmtqfn.h
@@ -0,0 +1,8 @@
1#ifndef FMTQFN_H
2#define FMTQFN_H
3
4extern unsigned int fmtqfn();
5
6#define FMTQFN 40 /* maximum space needed, if len(dirslash) <= 10 */
7
8#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 @@
1.TH forgeries 7
2.SH "NAME"
3forgeries \- how easy it is to forge mail
4.SH "SUMMARY"
5An electronic mail message can easily be forged.
6Almost everything in it,
7including the return address,
8is completely under the control of the sender.
9
10An electronic mail message can be manually traced to its origin
11if (1) all system administrators of intermediate machines
12are both cooperative and competent,
13(2) the sender did not break low-level TCP/IP security,
14and
15(3) all intermediate machines are secure.
16
17Users of
18.I cryptography
19can automatically ensure the integrity and secrecy
20of their mail messages, as long as
21the sending and receiving machines are secure.
22.SH "FORGERIES"
23Like postal mail,
24electronic mail can be created entirely at the whim of the sender.
25.BR From ,
26.BR Sender ,
27.BR Return-Path ,
28and
29.BR Message-ID
30can all contain whatever information the sender wants.
31
32For example, if you inject a message through
33.B sendmail
34or
35.B qmail-inject
36or
37.BR SMTP ,
38you can simply type in a
39.B From
40field.
41In fact,
42.B qmail-inject
43lets you set up
44.BR MAILUSER ,
45.BR MAILHOST ,
46and
47.B MAILNAME
48environment variables
49to produce your desired
50.B From
51field on every message.
52.SH "TRACING FORGERIES"
53Like postal mail,
54electronic mail is postmarked when it is sent.
55Each machine that receives an electronic mail message
56adds a
57.B Received
58line to the top.
59
60A modern
61.B Received
62line contains quite a bit of information.
63In conjunction with the machine's logs,
64it lets a competent system administrator
65determine where the machine received the message from,
66as long as the sender did not break low-level TCP/IP security
67or security on that machine.
68
69Large multi-user machines often come with inadequate logging software.
70Fortunately, a system administrator can easily obtain a copy of a
71931/1413/Ident/TAP server, such as
72.BR pidentd .
73Unfortunately,
74some system administrators fail to do this,
75and are thus unable to figure out which local user
76was responsible for generating a message.
77
78If all intermediate system administrators are competent,
79and the sender did not break machine security or low-level TCP/IP security,
80it is possible to trace a message backwards.
81Unfortunately, some traces are stymied by intermediate system
82administrators who are uncooperative or untrustworthy.
83.SH "CRYPTOGRAPHY"
84The sender of a mail message may place his message into a
85.I cryptographic
86envelope stamped with his seal.
87Strong cryptography guarantees that any two messages with the same seal
88were sent by the same cryptographic entity:
89perhaps a single person, perhaps a group of cooperating people,
90but in any case somebody who knows a secret originally held
91only by the creator of the seal.
92The seal is called a
93.I public key\fR.
94
95Unfortunately, the creator of the seal is often an insecure machine,
96or an untrustworthy central agency,
97but most of the time seals are kept secure.
98
99One popular cryptographic program is
100.BR pgp .
101.SH "SEE ALSO"
102pgp(1),
103identd(8),
104qmail-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 @@
1#ifndef FORK_H
2#define FORK_H
3
4extern int fork();
5#define vfork fork
6
7#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 @@
1#ifndef FORK_H
2#define FORK_H
3
4extern int fork();
5extern int vfork();
6
7#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 @@
1.TH forward 1
2.SH NAME
3forward \- forward new mail to one or more addresses
4.SH SYNOPSIS
5in
6.BR .qmail :
7.B |forward
8.I address ...
9.SH DESCRIPTION
10.B forward
11forwards each new mail message to the specified list of addresses.
12It is a simple wrapper around
13.BR qmail-queue .
14It achieves the same results as listing each
15.I address
16separately in
17.BR .qmail ,
18but it is more programmable since
19.I address
20can be constructed on the fly.
21.SH "SEE ALSO"
22dot-qmail(5),
23qmail-command(8),
24qmail-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 @@
1#include "sig.h"
2#include "readwrite.h"
3#include "exit.h"
4#include "env.h"
5#include "qmail.h"
6#include "strerr.h"
7#include "substdio.h"
8#include "fmt.h"
9
10#define FATAL "forward: fatal: "
11
12void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
13
14struct qmail qqt;
15
16int mywrite(fd,buf,len) int fd; char *buf; int len;
17{
18 qmail_put(&qqt,buf,len);
19 return len;
20}
21
22char inbuf[SUBSTDIO_INSIZE];
23char outbuf[1];
24substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
25substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf);
26
27char num[FMT_ULONG];
28
29void main(argc,argv)
30int argc;
31char **argv;
32{
33 char *sender;
34 char *dtline;
35 char *qqx;
36
37 sig_pipeignore();
38
39 sender = env_get("NEWSENDER");
40 if (!sender)
41 strerr_die2x(100,FATAL,"NEWSENDER not set");
42 dtline = env_get("DTLINE");
43 if (!dtline)
44 strerr_die2x(100,FATAL,"DTLINE not set");
45
46 if (qmail_open(&qqt) == -1)
47 strerr_die2sys(111,FATAL,"unable to fork: ");
48 qmail_puts(&qqt,dtline);
49 if (substdio_copy(&ssout,&ssin) != 0)
50 strerr_die2sys(111,FATAL,"unable to read message: ");
51 substdio_flush(&ssout);
52
53 num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
54
55 qmail_from(&qqt,sender);
56 while (*++argv) qmail_to(&qqt,*argv);
57 qqx = qmail_close(&qqt);
58 if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
59 strerr_die2x(0,"forward: qp ",num);
60}
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 @@
1#ifndef GEN_ALLOC_H
2#define GEN_ALLOC_H
3
4#define GEN_ALLOC_typedef(ta,type,field,len,a) \
5 typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
6
7#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 @@
1#ifndef GEN_ALLOC_DEFS_H
2#define GEN_ALLOC_DEFS_H
3
4#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
5int ta_ready(x,n) register ta *x; register unsigned int n; \
6{ register unsigned int i; \
7 if (x->field) { \
8 i = x->a; \
9 if (n > i) { \
10 x->a = base + n + (n >> 3); \
11 if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
12 x->a = i; return 0; } \
13 return 1; } \
14 x->len = 0; \
15 return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
16
17#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
18int ta_rplus(x,n) register ta *x; register unsigned int n; \
19{ register unsigned int i; \
20 if (x->field) { \
21 i = x->a; n += x->len; \
22 if (n > i) { \
23 x->a = base + n + (n >> 3); \
24 if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
25 x->a = i; return 0; } \
26 return 1; } \
27 x->len = 0; \
28 return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
29
30#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
31int ta_append(x,i) register ta *x; register type *i; \
32{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
33
34#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 @@
1.TH getln 3
2.SH NAME
3getln \- read one line of data
4.SH SYNTAX
5.B #include <getln.h>
6
7int \fBgetln\fP(&\fIss\fR,&\fIsa\fR,&\fImatch\fR,\fIsep\fR);
8
9substdio \fIss\fR;
10.br
11stralloc \fIsa\fR;
12.br
13int \fImatch\fR;
14.br
15int \fIsep\fR;
16.SH DESCRIPTION
17.B getln
18reads a line of characters, terminated by a
19.I sep
20character,
21from
22.IR ss .
23It returns the line in
24.I sa
25and sets
26.I match
27to 1.
28
29If
30.B getln
31sees end-of-input before it sees
32.IR sep ,
33it returns the partial line in
34.I sa
35and sets
36.I match
37to 0.
38
39.B getln
40normally returns 0.
41If it runs out of memory,
42or encounters an error from
43.IR ss ,
44it returns -1,
45setting
46.B errno
47appropriately.
48.SH "SEE ALSO"
49stralloc(3),
50substdio(3),
51getln2(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 @@
1#include "substdio.h"
2#include "byte.h"
3#include "stralloc.h"
4#include "getln.h"
5
6int getln(ss,sa,match,sep)
7register substdio *ss;
8register stralloc *sa;
9int *match;
10int sep;
11{
12 char *cont;
13 unsigned int clen;
14
15 if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1;
16 if (!clen) { *match = 0; return 0; }
17 if (!stralloc_catb(sa,cont,clen)) return -1;
18 *match = 1;
19 return 0;
20}
diff --git a/getln.h b/getln.h
new file mode 100644
index 0000000..cf4f934
--- /dev/null
+++ b/getln.h
@@ -0,0 +1,7 @@
1#ifndef GETLN_H
2#define GETLN_H
3
4extern int getln();
5extern int getln2();
6
7#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 @@
1.TH getln2 3
2.SH NAME
3getln2 \- read one line of data
4.SH SYNTAX
5.B #include <getln.h>
6
7int \fBgetln2\fP(&\fIss\fR,&\fIsa\fR,&\fIcont\fR,&\fIclen\fR,\fIsep\fR);
8
9substdio \fIss\fR;
10.br
11stralloc \fIsa\fR;
12.br
13char *\fIcont\fR;
14.br
15unsigned int \fIclen\fR;
16.br
17int \fIsep\fR;
18.SH DESCRIPTION
19.B getln2
20reads a line of characters, terminated by a
21.I sep
22character,
23from
24.IR ss .
25
26The line is returned in two pieces.
27The first piece is stored in
28.IR sa .
29The second piece is
30.IR cont ,
31a pointer to
32.I clen
33characters inside the
34.I ss
35buffer.
36The second piece must be copied somewhere else
37before
38.I ss
39is used again.
40
41If
42.B getln2
43sees end-of-input before it sees
44.IR sep ,
45it sets
46.I clen
47to 0 and does not set
48.IR cont .
49It puts the partial line into
50.IR sa .
51
52.B getln2
53normally returns 0.
54If it runs out of memory,
55or encounters an error from
56.IR ss ,
57it returns -1,
58setting
59.B errno
60appropriately.
61.SH "SEE ALSO"
62stralloc(3),
63substdio(3),
64getln(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 @@
1#include "substdio.h"
2#include "stralloc.h"
3#include "byte.h"
4#include "getln.h"
5
6int getln2(ss,sa,cont,clen,sep)
7register substdio *ss;
8register stralloc *sa;
9/*@out@*/char **cont;
10/*@out@*/unsigned int *clen;
11int sep;
12{
13 register char *x;
14 register unsigned int i;
15 int n;
16
17 if (!stralloc_ready(sa,0)) return -1;
18 sa->len = 0;
19
20 for (;;) {
21 n = substdio_feed(ss);
22 if (n < 0) return -1;
23 if (n == 0) { *clen = 0; return 0; }
24 x = substdio_PEEK(ss);
25 i = byte_chr(x,n,sep);
26 if (i < n) { substdio_SEEK(ss,*clen = i + 1); *cont = x; return 0; }
27 if (!stralloc_readyplus(sa,n)) return -1;
28 i = sa->len;
29 sa->len = i + substdio_get(ss,sa->s + i,n);
30 }
31}
diff --git a/gfrom.c b/gfrom.c
new file mode 100644
index 0000000..87f9ad4
--- /dev/null
+++ b/gfrom.c
@@ -0,0 +1,10 @@
1#include "str.h"
2#include "gfrom.h"
3
4int gfrom(s,len)
5char *s;
6int len;
7{
8 while ((len > 0) && (*s == '>')) { ++s; --len; }
9 return (len >= 5) && !str_diffn(s,"From ",5);
10}
diff --git a/gfrom.h b/gfrom.h
new file mode 100644
index 0000000..488193c
--- /dev/null
+++ b/gfrom.h
@@ -0,0 +1,6 @@
1#ifndef GFROM_H
2#define GFROM_H
3
4extern int gfrom();
5
6#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 @@
1#include "stralloc.h"
2#include "substdio.h"
3#include "getln.h"
4#include "hfield.h"
5#include "headerbody.h"
6
7static int getsa(ss,sa,match)
8substdio *ss;
9stralloc *sa;
10int *match;
11{
12 if (!*match) return 0;
13 if (getln(ss,sa,match,'\n') == -1) return -1;
14 if (*match) return 1;
15 if (!sa->len) return 0;
16 if (!stralloc_append(sa,"\n")) return -1;
17 return 1;
18}
19
20static stralloc line = {0};
21static stralloc nextline = {0};
22
23int headerbody(ss,dohf,hdone,dobl)
24substdio *ss;
25void (*dohf)();
26void (*hdone)();
27void (*dobl)();
28{
29 int match;
30 int flaglineok;
31 match = 1;
32 flaglineok = 0;
33 for (;;)
34 {
35 switch(getsa(ss,&nextline,&match))
36 {
37 case -1:
38 return -1;
39 case 0:
40 if (flaglineok) dohf(&line);
41 hdone();
42 /* no message body; could insert blank line here */
43 return 0;
44 }
45 if (flaglineok)
46 {
47 if ((nextline.s[0] == ' ') || (nextline.s[0] == '\t'))
48 {
49 if (!stralloc_cat(&line,&nextline)) return -1;
50 continue;
51 }
52 dohf(&line);
53 }
54 if (nextline.len == 1)
55 {
56 hdone();
57 dobl(&nextline);
58 break;
59 }
60 if (stralloc_starts(&nextline,"From "))
61 {
62 if (!stralloc_copys(&line,"MBOX-Line: ")) return -1;
63 if (!stralloc_cat(&line,&nextline)) return -1;
64 }
65 else
66 if (hfield_valid(nextline.s,nextline.len))
67 {
68 if (!stralloc_copy(&line,&nextline)) return -1;
69 }
70 else
71 {
72 hdone();
73 if (!stralloc_copys(&line,"\n")) return -1;
74 dobl(&line);
75 dobl(&nextline);
76 break;
77 }
78 flaglineok = 1;
79 }
80 for (;;)
81 switch(getsa(ss,&nextline,&match))
82 {
83 case -1: return -1;
84 case 0: return 0;
85 case 1: dobl(&nextline);
86 }
87}
diff --git a/headerbody.h b/headerbody.h
new file mode 100644
index 0000000..3bb2e2f
--- /dev/null
+++ b/headerbody.h
@@ -0,0 +1,6 @@
1#ifndef HEADERBODY_H
2#define HEADERBODY_H
3
4extern int headerbody();
5
6#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 @@
1#include "hfield.h"
2
3static char *(hname[]) = {
4 "unknown-header"
5, "sender"
6, "from"
7, "reply-to"
8, "to"
9, "cc"
10, "bcc"
11, "date"
12, "message-id"
13, "subject"
14, "resent-sender"
15, "resent-from"
16, "resent-reply-to"
17, "resent-to"
18, "resent-cc"
19, "resent-bcc"
20, "resent-date"
21, "resent-message-id"
22, "return-receipt-to"
23, "errors-to"
24, "apparently-to"
25, "received"
26, "return-path"
27, "delivered-to"
28, "content-length"
29, "content-type"
30, "content-transfer-encoding"
31, "notice-requested-upon-delivery-to"
32, "mail-followup-to"
33, 0
34};
35
36static int hmatch(s,len,t)
37char *s;
38int len;
39char *t;
40{
41 int i;
42 char ch;
43
44 for (i = 0;ch = t[i];++i)
45 {
46 if (i >= len) return 0;
47 if (ch != s[i])
48 {
49 if (ch == '-') return 0;
50 if (ch - 32 != s[i]) return 0;
51 }
52 }
53 for (;;)
54 {
55 if (i >= len) return 0;
56 ch = s[i];
57 if (ch == ':') return 1;
58 if ((ch != ' ') && (ch != '\t')) return 0;
59 ++i;
60 }
61}
62
63int hfield_known(s,len)
64char *s;
65int len;
66{
67 int i;
68 char *t;
69
70 for (i = 1;t = hname[i];++i)
71 if (hmatch(s,len,t))
72 return i;
73 return 0;
74}
75
76int hfield_valid(s,len)
77char *s;
78int len;
79{
80 int i;
81 int j;
82 char ch;
83
84 for (j = 0;j < len;++j)
85 if (s[j] == ':')
86 break;
87 if (j >= len) return 0;
88 while (j)
89 {
90 ch = s[j - 1];
91 if ((ch != ' ') && (ch != '\t'))
92 break;
93 --j;
94 }
95 if (!j) return 0;
96
97 for (i = 0;i < j;++i)
98 {
99 ch = s[i];
100 if (ch <= 32) return 0;
101 if (ch >= 127) return 0;
102 }
103 return 1;
104}
105
106unsigned int hfield_skipname(s,len)
107char *s;
108int len;
109{
110 int i;
111 char ch;
112
113 for (i = 0;i < len;++i)
114 if (s[i] == ':')
115 break;
116 if (i < len) ++i;
117 while (i < len)
118 {
119 ch = s[i];
120 if ((ch != '\t') && (ch != '\n') && (ch != '\r') && (ch != ' '))
121 break;
122 ++i;
123 }
124 return i;
125}
diff --git a/hfield.h b/hfield.h
new file mode 100644
index 0000000..20b30a5
--- /dev/null
+++ b/hfield.h
@@ -0,0 +1,38 @@
1#ifndef HFIELD_H
2#define HFIELD_H
3
4extern unsigned int hfield_skipname();
5extern int hfield_known();
6extern int hfield_valid();
7
8#define H_SENDER 1
9#define H_FROM 2
10#define H_REPLYTO 3
11#define H_TO 4
12#define H_CC 5
13#define H_BCC 6
14#define H_DATE 7
15#define H_MESSAGEID 8
16#define H_SUBJECT 9
17#define H_R_SENDER 10
18#define H_R_FROM 11
19#define H_R_REPLYTO 12
20#define H_R_TO 13
21#define H_R_CC 14
22#define H_R_BCC 15
23#define H_R_DATE 16
24#define H_R_MESSAGEID 17
25#define H_RETURNRECEIPTTO 18
26#define H_ERRORSTO 19
27#define H_APPARENTLYTO 20
28#define H_RECEIVED 21
29#define H_RETURNPATH 22
30#define H_DELIVEREDTO 23
31#define H_CONTENTLENGTH 24
32#define H_CONTENTTYPE 25
33#define H_CONTENTTRANSFERENCODING 26
34#define H_NOTICEREQUESTEDUPONDELIVERYTO 27
35#define H_MAILFOLLOWUPTO 28
36#define H_NUM 29
37
38#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 @@
1#include "auto_qmail.h"
2#include "auto_split.h"
3#include "auto_uids.h"
4#include "fmt.h"
5#include "fifo.h"
6
7char buf[100 + FMT_ULONG];
8
9void dsplit(base,uid,mode)
10char *base; /* must be under 100 bytes */
11int uid;
12int mode;
13{
14 char *x;
15 unsigned long i;
16
17 d(auto_qmail,base,uid,auto_gidq,mode);
18
19 for (i = 0;i < auto_split;++i) {
20 x = buf;
21 x += fmt_str(x,base);
22 x += fmt_str(x,"/");
23 x += fmt_ulong(x,i);
24 *x = 0;
25
26 d(auto_qmail,buf,uid,auto_gidq,mode);
27 }
28}
29
30void hier()
31{
32 h(auto_qmail,auto_uido,auto_gidq,0755);
33
34 d(auto_qmail,"control",auto_uido,auto_gidq,0755);
35 d(auto_qmail,"users",auto_uido,auto_gidq,0755);
36 d(auto_qmail,"bin",auto_uido,auto_gidq,0755);
37 d(auto_qmail,"boot",auto_uido,auto_gidq,0755);
38 d(auto_qmail,"doc",auto_uido,auto_gidq,0755);
39 d(auto_qmail,"man",auto_uido,auto_gidq,0755);
40 d(auto_qmail,"man/cat1",auto_uido,auto_gidq,0755);
41 d(auto_qmail,"man/cat5",auto_uido,auto_gidq,0755);
42 d(auto_qmail,"man/cat7",auto_uido,auto_gidq,0755);
43 d(auto_qmail,"man/cat8",auto_uido,auto_gidq,0755);
44 d(auto_qmail,"man/man1",auto_uido,auto_gidq,0755);
45 d(auto_qmail,"man/man5",auto_uido,auto_gidq,0755);
46 d(auto_qmail,"man/man7",auto_uido,auto_gidq,0755);
47 d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755);
48
49 d(auto_qmail,"alias",auto_uida,auto_gidq,02755);
50
51 d(auto_qmail,"queue",auto_uidq,auto_gidq,0750);
52 d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700);
53 d(auto_qmail,"queue/intd",auto_uidq,auto_gidq,0700);
54 d(auto_qmail,"queue/todo",auto_uidq,auto_gidq,0750);
55 d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700);
56
57 dsplit("queue/mess",auto_uidq,0750);
58 dsplit("queue/info",auto_uids,0700);
59 dsplit("queue/local",auto_uids,0700);
60 dsplit("queue/remote",auto_uids,0700);
61
62 d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750);
63 z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644);
64 z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600);
65 p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622);
66
67 c(auto_qmail,"boot","home",auto_uido,auto_gidq,0755);
68 c(auto_qmail,"boot","home+df",auto_uido,auto_gidq,0755);
69 c(auto_qmail,"boot","proc",auto_uido,auto_gidq,0755);
70 c(auto_qmail,"boot","proc+df",auto_uido,auto_gidq,0755);
71 c(auto_qmail,"boot","binm1",auto_uido,auto_gidq,0755);
72 c(auto_qmail,"boot","binm1+df",auto_uido,auto_gidq,0755);
73 c(auto_qmail,"boot","binm2",auto_uido,auto_gidq,0755);
74 c(auto_qmail,"boot","binm2+df",auto_uido,auto_gidq,0755);
75 c(auto_qmail,"boot","binm3",auto_uido,auto_gidq,0755);
76 c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755);
77
78 c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644);
79 c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644);
80 c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644);
81 c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644);
82 c(auto_qmail,"doc","INSTALL.alias",auto_uido,auto_gidq,0644);
83 c(auto_qmail,"doc","INSTALL.ctl",auto_uido,auto_gidq,0644);
84 c(auto_qmail,"doc","INSTALL.ids",auto_uido,auto_gidq,0644);
85 c(auto_qmail,"doc","INSTALL.maildir",auto_uido,auto_gidq,0644);
86 c(auto_qmail,"doc","INSTALL.mbox",auto_uido,auto_gidq,0644);
87 c(auto_qmail,"doc","INSTALL.vsm",auto_uido,auto_gidq,0644);
88 c(auto_qmail,"doc","TEST.deliver",auto_uido,auto_gidq,0644);
89 c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
90 c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
91 c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
92 c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
93 c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
94 c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
95 c(auto_qmail,"doc","PIC.local2rem",auto_uido,auto_gidq,0644);
96 c(auto_qmail,"doc","PIC.local2virt",auto_uido,auto_gidq,0644);
97 c(auto_qmail,"doc","PIC.nullclient",auto_uido,auto_gidq,0644);
98 c(auto_qmail,"doc","PIC.relaybad",auto_uido,auto_gidq,0644);
99 c(auto_qmail,"doc","PIC.relaygood",auto_uido,auto_gidq,0644);
100 c(auto_qmail,"doc","PIC.rem2local",auto_uido,auto_gidq,0644);
101
102 c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711);
103 c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700);
104 c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
105 c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
106 c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
107 c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
108 c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
109 c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
110 c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
111 c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
112 c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
113 c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
114 c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
115 c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
116 c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
117 c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755);
118 c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755);
119 c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755);
120 c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755);
121 c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755);
122 c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755);
123 c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755);
124 c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755);
125 c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711);
126 c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755);
127 c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
128 c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
129 c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
130 c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
131 c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
132 c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
133 c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
134 c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
135 c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
136 c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
137 c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
138 c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755);
139 c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755);
140 c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755);
141 c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755);
142 c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755);
143 c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
144 c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
145 c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
146
147 c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
148 c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
149 c(auto_qmail,"man/man5","envelopes.5",auto_uido,auto_gidq,0644);
150 c(auto_qmail,"man/cat5","envelopes.0",auto_uido,auto_gidq,0644);
151 c(auto_qmail,"man/man5","maildir.5",auto_uido,auto_gidq,0644);
152 c(auto_qmail,"man/cat5","maildir.0",auto_uido,auto_gidq,0644);
153 c(auto_qmail,"man/man5","mbox.5",auto_uido,auto_gidq,0644);
154 c(auto_qmail,"man/cat5","mbox.0",auto_uido,auto_gidq,0644);
155 c(auto_qmail,"man/man5","dot-qmail.5",auto_uido,auto_gidq,0644);
156 c(auto_qmail,"man/cat5","dot-qmail.0",auto_uido,auto_gidq,0644);
157 c(auto_qmail,"man/man5","qmail-control.5",auto_uido,auto_gidq,0644);
158 c(auto_qmail,"man/cat5","qmail-control.0",auto_uido,auto_gidq,0644);
159 c(auto_qmail,"man/man5","qmail-header.5",auto_uido,auto_gidq,0644);
160 c(auto_qmail,"man/cat5","qmail-header.0",auto_uido,auto_gidq,0644);
161 c(auto_qmail,"man/man5","qmail-log.5",auto_uido,auto_gidq,0644);
162 c(auto_qmail,"man/cat5","qmail-log.0",auto_uido,auto_gidq,0644);
163 c(auto_qmail,"man/man5","qmail-users.5",auto_uido,auto_gidq,0644);
164 c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644);
165 c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644);
166 c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644);
167
168 c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644);
169 c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644);
170 c(auto_qmail,"man/man7","qmail-limits.7",auto_uido,auto_gidq,0644);
171 c(auto_qmail,"man/cat7","qmail-limits.0",auto_uido,auto_gidq,0644);
172 c(auto_qmail,"man/man7","qmail.7",auto_uido,auto_gidq,0644);
173 c(auto_qmail,"man/cat7","qmail.0",auto_uido,auto_gidq,0644);
174
175 c(auto_qmail,"man/man1","forward.1",auto_uido,auto_gidq,0644);
176 c(auto_qmail,"man/cat1","forward.0",auto_uido,auto_gidq,0644);
177 c(auto_qmail,"man/man1","condredirect.1",auto_uido,auto_gidq,0644);
178 c(auto_qmail,"man/cat1","condredirect.0",auto_uido,auto_gidq,0644);
179 c(auto_qmail,"man/man1","bouncesaying.1",auto_uido,auto_gidq,0644);
180 c(auto_qmail,"man/cat1","bouncesaying.0",auto_uido,auto_gidq,0644);
181 c(auto_qmail,"man/man1","except.1",auto_uido,auto_gidq,0644);
182 c(auto_qmail,"man/cat1","except.0",auto_uido,auto_gidq,0644);
183 c(auto_qmail,"man/man1","maildirmake.1",auto_uido,auto_gidq,0644);
184 c(auto_qmail,"man/cat1","maildirmake.0",auto_uido,auto_gidq,0644);
185 c(auto_qmail,"man/man1","maildir2mbox.1",auto_uido,auto_gidq,0644);
186 c(auto_qmail,"man/cat1","maildir2mbox.0",auto_uido,auto_gidq,0644);
187 c(auto_qmail,"man/man1","maildirwatch.1",auto_uido,auto_gidq,0644);
188 c(auto_qmail,"man/cat1","maildirwatch.0",auto_uido,auto_gidq,0644);
189 c(auto_qmail,"man/man1","mailsubj.1",auto_uido,auto_gidq,0644);
190 c(auto_qmail,"man/cat1","mailsubj.0",auto_uido,auto_gidq,0644);
191 c(auto_qmail,"man/man1","qreceipt.1",auto_uido,auto_gidq,0644);
192 c(auto_qmail,"man/cat1","qreceipt.0",auto_uido,auto_gidq,0644);
193 c(auto_qmail,"man/man1","qbiff.1",auto_uido,auto_gidq,0644);
194 c(auto_qmail,"man/cat1","qbiff.0",auto_uido,auto_gidq,0644);
195 c(auto_qmail,"man/man1","preline.1",auto_uido,auto_gidq,0644);
196 c(auto_qmail,"man/cat1","preline.0",auto_uido,auto_gidq,0644);
197 c(auto_qmail,"man/man1","tcp-env.1",auto_uido,auto_gidq,0644);
198 c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
199
200 c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
201 c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
202 c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
203 c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
204 c(auto_qmail,"man/man8","qmail-getpw.8",auto_uido,auto_gidq,0644);
205 c(auto_qmail,"man/cat8","qmail-getpw.0",auto_uido,auto_gidq,0644);
206 c(auto_qmail,"man/man8","qmail-remote.8",auto_uido,auto_gidq,0644);
207 c(auto_qmail,"man/cat8","qmail-remote.0",auto_uido,auto_gidq,0644);
208 c(auto_qmail,"man/man8","qmail-rspawn.8",auto_uido,auto_gidq,0644);
209 c(auto_qmail,"man/cat8","qmail-rspawn.0",auto_uido,auto_gidq,0644);
210 c(auto_qmail,"man/man8","qmail-clean.8",auto_uido,auto_gidq,0644);
211 c(auto_qmail,"man/cat8","qmail-clean.0",auto_uido,auto_gidq,0644);
212 c(auto_qmail,"man/man8","qmail-send.8",auto_uido,auto_gidq,0644);
213 c(auto_qmail,"man/cat8","qmail-send.0",auto_uido,auto_gidq,0644);
214 c(auto_qmail,"man/man8","qmail-start.8",auto_uido,auto_gidq,0644);
215 c(auto_qmail,"man/cat8","qmail-start.0",auto_uido,auto_gidq,0644);
216 c(auto_qmail,"man/man8","splogger.8",auto_uido,auto_gidq,0644);
217 c(auto_qmail,"man/cat8","splogger.0",auto_uido,auto_gidq,0644);
218 c(auto_qmail,"man/man8","qmail-queue.8",auto_uido,auto_gidq,0644);
219 c(auto_qmail,"man/cat8","qmail-queue.0",auto_uido,auto_gidq,0644);
220 c(auto_qmail,"man/man8","qmail-inject.8",auto_uido,auto_gidq,0644);
221 c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644);
222 c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644);
223 c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644);
224 c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644);
225 c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644);
226 c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644);
227 c(auto_qmail,"man/cat8","qmail-newu.0",auto_uido,auto_gidq,0644);
228 c(auto_qmail,"man/man8","qmail-pw2u.8",auto_uido,auto_gidq,0644);
229 c(auto_qmail,"man/cat8","qmail-pw2u.0",auto_uido,auto_gidq,0644);
230 c(auto_qmail,"man/man8","qmail-qread.8",auto_uido,auto_gidq,0644);
231 c(auto_qmail,"man/cat8","qmail-qread.0",auto_uido,auto_gidq,0644);
232 c(auto_qmail,"man/man8","qmail-qstat.8",auto_uido,auto_gidq,0644);
233 c(auto_qmail,"man/cat8","qmail-qstat.0",auto_uido,auto_gidq,0644);
234 c(auto_qmail,"man/man8","qmail-tcpok.8",auto_uido,auto_gidq,0644);
235 c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644);
236 c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644);
237 c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644);
238 c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644);
239 c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644);
240 c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644);
241 c(auto_qmail,"man/cat8","qmail-popup.0",auto_uido,auto_gidq,0644);
242 c(auto_qmail,"man/man8","qmail-qmqpc.8",auto_uido,auto_gidq,0644);
243 c(auto_qmail,"man/cat8","qmail-qmqpc.0",auto_uido,auto_gidq,0644);
244 c(auto_qmail,"man/man8","qmail-qmqpd.8",auto_uido,auto_gidq,0644);
245 c(auto_qmail,"man/cat8","qmail-qmqpd.0",auto_uido,auto_gidq,0644);
246 c(auto_qmail,"man/man8","qmail-qmtpd.8",auto_uido,auto_gidq,0644);
247 c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644);
248 c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644);
249 c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644);
250 c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644);
251 c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644);
252}
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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using dot-forward to support sendmail-style ~/.forward files.
5# Using qmail-local to deliver messages to ~/Mailbox by default.
6
7exec env - PATH="QMAIL/bin:$PATH" \
8qmail-start '|dot-forward .forward
9./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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using qmail-local to deliver messages to ~/Mailbox by default.
5
6exec env - PATH="QMAIL/bin:$PATH" \
7qmail-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 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "readwrite.h"
4#include "exit.h"
5
6char host[256];
7
8void main()
9{
10 host[0] = 0; /* sigh */
11 gethostname(host,sizeof(host));
12 host[sizeof(host) - 1] = 0;
13 substdio_puts(subfdoutsmall,host);
14 substdio_puts(subfdoutsmall,"\n");
15 substdio_flush(subfdoutsmall);
16 _exit(0);
17}
diff --git a/idedit.c b/idedit.c
new file mode 100644
index 0000000..e6747b5
--- /dev/null
+++ b/idedit.c
@@ -0,0 +1,147 @@
1#include <sys/types.h>
2#include <pwd.h>
3#include <grp.h>
4#include "readwrite.h"
5#include "exit.h"
6#include "scan.h"
7#include "fmt.h"
8#include "strerr.h"
9#include "open.h"
10#include "seek.h"
11#include "fork.h"
12
13#define FATAL "idedit: fatal: "
14#define WARNING "idedit: warning: "
15
16int fd;
17
18void byte(pos,value)
19char *pos;
20unsigned int value;
21{
22 unsigned long u;
23 unsigned char ch;
24
25 if (pos[scan_ulong(pos,&u)]) return;
26
27 if (seek_set(fd,(seek_pos) u) == -1)
28 strerr_die2sys(111,FATAL,"unable to seek: ");
29
30 ch = value;
31 if (write(fd,&ch,1) != 1)
32 strerr_die2sys(111,FATAL,"unable to write: ");
33}
34
35char *args[10];
36
37void run()
38{
39 int pid;
40 int wstat;
41
42 pid = fork();
43 if (pid == -1)
44 strerr_die2sys(111,FATAL,"unable to fork: ");
45
46 if (pid == 0) {
47 execv(*args,args);
48 strerr_die4sys(111,WARNING,"unable to run ",*args,": ");
49 }
50
51 if (wait_pid(&wstat,pid) != pid)
52 strerr_die2sys(111,FATAL,"waitpid surprise");
53}
54
55void u(account,group,home,pos0,pos1,pos2,pos3)
56char *account;
57char *group;
58char *home;
59char *pos0;
60char *pos1;
61char *pos2;
62char *pos3;
63{
64 struct passwd *pw;
65 unsigned int value;
66
67 pw = getpwnam(account);
68
69 if (!pw && group) {
70 args[0] = "add-account";
71 args[1] = account;
72 args[2] = group;
73 args[3] = home;
74 args[4] = 0;
75 run();
76 pw = getpwnam(account);
77 }
78
79 if (!pw)
80 strerr_die3x(111,FATAL,"unable to find uid for ",account);
81
82 value = pw->pw_uid;
83 byte(pos0,value); value >>= 8;
84 byte(pos1,value); value >>= 8;
85 byte(pos2,value); value >>= 8;
86 byte(pos3,value); value >>= 8;
87 if (value)
88 strerr_die3x(111,FATAL,"excessively large uid for ",account);
89}
90
91void g(group,pos0,pos1,pos2,pos3)
92char *group;
93char *pos0;
94char *pos1;
95char *pos2;
96char *pos3;
97{
98 struct group *gr;
99 unsigned int value;
100
101 gr = getgrnam(group);
102
103 if (!gr) {
104 args[0] = "add-group";
105 args[1] = group;
106 args[2] = 0;
107 run();
108 gr = getgrnam(group);
109 }
110
111 if (!gr)
112 strerr_die3x(111,FATAL,"unable to find gid for ",group);
113
114 value = gr->gr_gid;
115 byte(pos0,value); value >>= 8;
116 byte(pos1,value); value >>= 8;
117 byte(pos2,value); value >>= 8;
118 byte(pos3,value); value >>= 8;
119 if (value)
120 strerr_die3x(111,FATAL,"excessively large gid for ",group);
121}
122
123void main(argc,argv)
124int argc;
125char **argv;
126{
127 if (argc < 42) _exit(100);
128
129 fd = open_write(argv[1]);
130 if (fd == -1) strerr_die4sys(111,FATAL,"unable to open ",argv[1],": ");
131
132 g("qmail",argv[34],argv[35],argv[36],argv[37]);
133 g("nofiles",argv[38],argv[39],argv[40],argv[41]);
134
135 u("root",(char *) 0,"/",argv[14],argv[15],argv[16],argv[17]);
136
137 u("qmaild","nofiles","/var/qmail",argv[6],argv[7],argv[8],argv[9]);
138 u("qmaill","nofiles","/var/qmail",argv[10],argv[11],argv[12],argv[13]);
139 u("qmailp","nofiles","/var/qmail",argv[18],argv[19],argv[20],argv[21]);
140 u("alias","nofiles","/var/qmail/alias",argv[2],argv[3],argv[4],argv[5]);
141
142 u("qmailq","qmail","/var/qmail",argv[22],argv[23],argv[24],argv[25]);
143 u("qmailr","qmail","/var/qmail",argv[26],argv[27],argv[28],argv[29]);
144 u("qmails","qmail","/var/qmail",argv[30],argv[31],argv[32],argv[33]);
145
146 _exit(0);
147}
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 @@
1#include "auto_qmail.h"
2#include "auto_split.h"
3#include "auto_uids.h"
4#include "fmt.h"
5#include "fifo.h"
6
7char buf[100 + FMT_ULONG];
8
9void dsplit(base,uid,mode)
10char *base; /* must be under 100 bytes */
11int uid;
12int mode;
13{
14 char *x;
15 unsigned long i;
16
17 d(auto_qmail,base,uid,auto_gidq,mode);
18
19 for (i = 0;i < auto_split;++i) {
20 x = buf;
21 x += fmt_str(x,base);
22 x += fmt_str(x,"/");
23 x += fmt_ulong(x,i);
24 *x = 0;
25
26 d(auto_qmail,buf,uid,auto_gidq,mode);
27 }
28}
29
30void hier()
31{
32 h(auto_qmail,auto_uido,auto_gidq,0755);
33
34 d(auto_qmail,"control",auto_uido,auto_gidq,0755);
35 d(auto_qmail,"users",auto_uido,auto_gidq,0755);
36 d(auto_qmail,"bin",auto_uido,auto_gidq,0755);
37 d(auto_qmail,"boot",auto_uido,auto_gidq,0755);
38 d(auto_qmail,"doc",auto_uido,auto_gidq,0755);
39 d(auto_qmail,"man",auto_uido,auto_gidq,0755);
40 d(auto_qmail,"man/cat1",auto_uido,auto_gidq,0755);
41 d(auto_qmail,"man/cat5",auto_uido,auto_gidq,0755);
42 d(auto_qmail,"man/cat7",auto_uido,auto_gidq,0755);
43 d(auto_qmail,"man/cat8",auto_uido,auto_gidq,0755);
44 d(auto_qmail,"man/man1",auto_uido,auto_gidq,0755);
45 d(auto_qmail,"man/man5",auto_uido,auto_gidq,0755);
46 d(auto_qmail,"man/man7",auto_uido,auto_gidq,0755);
47 d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755);
48
49 d(auto_qmail,"alias",auto_uida,auto_gidq,02755);
50
51 d(auto_qmail,"queue",auto_uidq,auto_gidq,0750);
52 d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700);
53 d(auto_qmail,"queue/intd",auto_uidq,auto_gidq,0700);
54 d(auto_qmail,"queue/todo",auto_uidq,auto_gidq,0750);
55 d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700);
56
57 dsplit("queue/mess",auto_uidq,0750);
58 dsplit("queue/info",auto_uids,0700);
59 dsplit("queue/local",auto_uids,0700);
60 dsplit("queue/remote",auto_uids,0700);
61
62 d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750);
63 z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644);
64 z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600);
65 p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622);
66
67 c(auto_qmail,"boot","home",auto_uido,auto_gidq,0755);
68 c(auto_qmail,"boot","home+df",auto_uido,auto_gidq,0755);
69 c(auto_qmail,"boot","proc",auto_uido,auto_gidq,0755);
70 c(auto_qmail,"boot","proc+df",auto_uido,auto_gidq,0755);
71 c(auto_qmail,"boot","binm1",auto_uido,auto_gidq,0755);
72 c(auto_qmail,"boot","binm1+df",auto_uido,auto_gidq,0755);
73 c(auto_qmail,"boot","binm2",auto_uido,auto_gidq,0755);
74 c(auto_qmail,"boot","binm2+df",auto_uido,auto_gidq,0755);
75 c(auto_qmail,"boot","binm3",auto_uido,auto_gidq,0755);
76 c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755);
77
78 c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644);
79 c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644);
80 c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644);
81 c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644);
82 c(auto_qmail,"doc","INSTALL.alias",auto_uido,auto_gidq,0644);
83 c(auto_qmail,"doc","INSTALL.ctl",auto_uido,auto_gidq,0644);
84 c(auto_qmail,"doc","INSTALL.ids",auto_uido,auto_gidq,0644);
85 c(auto_qmail,"doc","INSTALL.maildir",auto_uido,auto_gidq,0644);
86 c(auto_qmail,"doc","INSTALL.mbox",auto_uido,auto_gidq,0644);
87 c(auto_qmail,"doc","INSTALL.vsm",auto_uido,auto_gidq,0644);
88 c(auto_qmail,"doc","TEST.deliver",auto_uido,auto_gidq,0644);
89 c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
90 c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
91 c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
92 c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
93 c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
94 c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
95 c(auto_qmail,"doc","PIC.local2rem",auto_uido,auto_gidq,0644);
96 c(auto_qmail,"doc","PIC.local2virt",auto_uido,auto_gidq,0644);
97 c(auto_qmail,"doc","PIC.nullclient",auto_uido,auto_gidq,0644);
98 c(auto_qmail,"doc","PIC.relaybad",auto_uido,auto_gidq,0644);
99 c(auto_qmail,"doc","PIC.relaygood",auto_uido,auto_gidq,0644);
100 c(auto_qmail,"doc","PIC.rem2local",auto_uido,auto_gidq,0644);
101
102 c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711);
103 c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700);
104 c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
105 c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
106 c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
107 c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
108 c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
109 c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
110 c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
111 c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
112 c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
113 c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
114 c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
115 c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
116 c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
117 c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755);
118 c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755);
119 c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755);
120 c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755);
121 c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755);
122 c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755);
123 c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755);
124 c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755);
125 c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711);
126 c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755);
127 c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
128 c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
129 c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
130 c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
131 c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
132 c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
133 c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
134 c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
135 c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
136 c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
137 c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
138 c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755);
139 c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755);
140 c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755);
141 c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755);
142 c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755);
143 c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
144 c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
145 c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
146
147 c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
148 c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
149 c(auto_qmail,"man/man5","envelopes.5",auto_uido,auto_gidq,0644);
150 c(auto_qmail,"man/cat5","envelopes.0",auto_uido,auto_gidq,0644);
151 c(auto_qmail,"man/man5","maildir.5",auto_uido,auto_gidq,0644);
152 c(auto_qmail,"man/cat5","maildir.0",auto_uido,auto_gidq,0644);
153 c(auto_qmail,"man/man5","mbox.5",auto_uido,auto_gidq,0644);
154 c(auto_qmail,"man/cat5","mbox.0",auto_uido,auto_gidq,0644);
155 c(auto_qmail,"man/man5","dot-qmail.5",auto_uido,auto_gidq,0644);
156 c(auto_qmail,"man/cat5","dot-qmail.0",auto_uido,auto_gidq,0644);
157 c(auto_qmail,"man/man5","qmail-control.5",auto_uido,auto_gidq,0644);
158 c(auto_qmail,"man/cat5","qmail-control.0",auto_uido,auto_gidq,0644);
159 c(auto_qmail,"man/man5","qmail-header.5",auto_uido,auto_gidq,0644);
160 c(auto_qmail,"man/cat5","qmail-header.0",auto_uido,auto_gidq,0644);
161 c(auto_qmail,"man/man5","qmail-log.5",auto_uido,auto_gidq,0644);
162 c(auto_qmail,"man/cat5","qmail-log.0",auto_uido,auto_gidq,0644);
163 c(auto_qmail,"man/man5","qmail-users.5",auto_uido,auto_gidq,0644);
164 c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644);
165 c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644);
166 c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644);
167
168 c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644);
169 c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644);
170 c(auto_qmail,"man/man7","qmail-limits.7",auto_uido,auto_gidq,0644);
171 c(auto_qmail,"man/cat7","qmail-limits.0",auto_uido,auto_gidq,0644);
172 c(auto_qmail,"man/man7","qmail.7",auto_uido,auto_gidq,0644);
173 c(auto_qmail,"man/cat7","qmail.0",auto_uido,auto_gidq,0644);
174
175 c(auto_qmail,"man/man1","forward.1",auto_uido,auto_gidq,0644);
176 c(auto_qmail,"man/cat1","forward.0",auto_uido,auto_gidq,0644);
177 c(auto_qmail,"man/man1","condredirect.1",auto_uido,auto_gidq,0644);
178 c(auto_qmail,"man/cat1","condredirect.0",auto_uido,auto_gidq,0644);
179 c(auto_qmail,"man/man1","bouncesaying.1",auto_uido,auto_gidq,0644);
180 c(auto_qmail,"man/cat1","bouncesaying.0",auto_uido,auto_gidq,0644);
181 c(auto_qmail,"man/man1","except.1",auto_uido,auto_gidq,0644);
182 c(auto_qmail,"man/cat1","except.0",auto_uido,auto_gidq,0644);
183 c(auto_qmail,"man/man1","maildirmake.1",auto_uido,auto_gidq,0644);
184 c(auto_qmail,"man/cat1","maildirmake.0",auto_uido,auto_gidq,0644);
185 c(auto_qmail,"man/man1","maildir2mbox.1",auto_uido,auto_gidq,0644);
186 c(auto_qmail,"man/cat1","maildir2mbox.0",auto_uido,auto_gidq,0644);
187 c(auto_qmail,"man/man1","maildirwatch.1",auto_uido,auto_gidq,0644);
188 c(auto_qmail,"man/cat1","maildirwatch.0",auto_uido,auto_gidq,0644);
189 c(auto_qmail,"man/man1","mailsubj.1",auto_uido,auto_gidq,0644);
190 c(auto_qmail,"man/cat1","mailsubj.0",auto_uido,auto_gidq,0644);
191 c(auto_qmail,"man/man1","qreceipt.1",auto_uido,auto_gidq,0644);
192 c(auto_qmail,"man/cat1","qreceipt.0",auto_uido,auto_gidq,0644);
193 c(auto_qmail,"man/man1","qbiff.1",auto_uido,auto_gidq,0644);
194 c(auto_qmail,"man/cat1","qbiff.0",auto_uido,auto_gidq,0644);
195 c(auto_qmail,"man/man1","preline.1",auto_uido,auto_gidq,0644);
196 c(auto_qmail,"man/cat1","preline.0",auto_uido,auto_gidq,0644);
197 c(auto_qmail,"man/man1","tcp-env.1",auto_uido,auto_gidq,0644);
198 c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
199
200 c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
201 c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
202 c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
203 c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
204 c(auto_qmail,"man/man8","qmail-getpw.8",auto_uido,auto_gidq,0644);
205 c(auto_qmail,"man/cat8","qmail-getpw.0",auto_uido,auto_gidq,0644);
206 c(auto_qmail,"man/man8","qmail-remote.8",auto_uido,auto_gidq,0644);
207 c(auto_qmail,"man/cat8","qmail-remote.0",auto_uido,auto_gidq,0644);
208 c(auto_qmail,"man/man8","qmail-rspawn.8",auto_uido,auto_gidq,0644);
209 c(auto_qmail,"man/cat8","qmail-rspawn.0",auto_uido,auto_gidq,0644);
210 c(auto_qmail,"man/man8","qmail-clean.8",auto_uido,auto_gidq,0644);
211 c(auto_qmail,"man/cat8","qmail-clean.0",auto_uido,auto_gidq,0644);
212 c(auto_qmail,"man/man8","qmail-send.8",auto_uido,auto_gidq,0644);
213 c(auto_qmail,"man/cat8","qmail-send.0",auto_uido,auto_gidq,0644);
214 c(auto_qmail,"man/man8","qmail-start.8",auto_uido,auto_gidq,0644);
215 c(auto_qmail,"man/cat8","qmail-start.0",auto_uido,auto_gidq,0644);
216 c(auto_qmail,"man/man8","splogger.8",auto_uido,auto_gidq,0644);
217 c(auto_qmail,"man/cat8","splogger.0",auto_uido,auto_gidq,0644);
218 c(auto_qmail,"man/man8","qmail-queue.8",auto_uido,auto_gidq,0644);
219 c(auto_qmail,"man/cat8","qmail-queue.0",auto_uido,auto_gidq,0644);
220 c(auto_qmail,"man/man8","qmail-inject.8",auto_uido,auto_gidq,0644);
221 c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644);
222 c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644);
223 c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644);
224 c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644);
225 c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644);
226 c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644);
227 c(auto_qmail,"man/cat8","qmail-newu.0",auto_uido,auto_gidq,0644);
228 c(auto_qmail,"man/man8","qmail-pw2u.8",auto_uido,auto_gidq,0644);
229 c(auto_qmail,"man/cat8","qmail-pw2u.0",auto_uido,auto_gidq,0644);
230 c(auto_qmail,"man/man8","qmail-qread.8",auto_uido,auto_gidq,0644);
231 c(auto_qmail,"man/cat8","qmail-qread.0",auto_uido,auto_gidq,0644);
232 c(auto_qmail,"man/man8","qmail-qstat.8",auto_uido,auto_gidq,0644);
233 c(auto_qmail,"man/cat8","qmail-qstat.0",auto_uido,auto_gidq,0644);
234 c(auto_qmail,"man/man8","qmail-tcpok.8",auto_uido,auto_gidq,0644);
235 c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644);
236 c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644);
237 c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644);
238 c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644);
239 c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644);
240 c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644);
241 c(auto_qmail,"man/cat8","qmail-popup.0",auto_uido,auto_gidq,0644);
242 c(auto_qmail,"man/man8","qmail-qmqpc.8",auto_uido,auto_gidq,0644);
243 c(auto_qmail,"man/cat8","qmail-qmqpc.0",auto_uido,auto_gidq,0644);
244 c(auto_qmail,"man/man8","qmail-qmqpd.8",auto_uido,auto_gidq,0644);
245 c(auto_qmail,"man/cat8","qmail-qmqpd.0",auto_uido,auto_gidq,0644);
246 c(auto_qmail,"man/man8","qmail-qmtpd.8",auto_uido,auto_gidq,0644);
247 c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644);
248 c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644);
249 c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644);
250 c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644);
251 c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644);
252
253 c(auto_qmail,"bin","dot-forward",auto_uido,auto_gidq,0755);
254
255 c(auto_qmail,"man/man1","dot-forward.1",auto_uido,auto_gidq,0644);
256 c(auto_qmail,"man/cat1","dot-forward.0",auto_uido,auto_gidq,0644);
257
258 d(auto_qmail,"doc/fastforward",auto_uido,auto_gidq,0755);
259
260 c(auto_qmail,"bin","fastforward",auto_uido,auto_gidq,0755);
261 c(auto_qmail,"bin","printforward",auto_uido,auto_gidq,0755);
262 c(auto_qmail,"bin","setforward",auto_uido,auto_gidq,0755);
263 c(auto_qmail,"bin","newaliases",auto_uido,auto_gidq,0755);
264 c(auto_qmail,"bin","printmaillist",auto_uido,auto_gidq,0755);
265 c(auto_qmail,"bin","setmaillist",auto_uido,auto_gidq,0755);
266 c(auto_qmail,"bin","newinclude",auto_uido,auto_gidq,0755);
267
268 c(auto_qmail,"doc/fastforward","ALIASES",auto_uido,auto_gidq,0644);
269
270 c(auto_qmail,"man/man1","fastforward.1",auto_uido,auto_gidq,0644);
271 c(auto_qmail,"man/man1","printforward.1",auto_uido,auto_gidq,0644);
272 c(auto_qmail,"man/man1","setforward.1",auto_uido,auto_gidq,0644);
273 c(auto_qmail,"man/man1","newaliases.1",auto_uido,auto_gidq,0644);
274 c(auto_qmail,"man/man1","printmaillist.1",auto_uido,auto_gidq,0644);
275 c(auto_qmail,"man/man1","setmaillist.1",auto_uido,auto_gidq,0644);
276 c(auto_qmail,"man/man1","newinclude.1",auto_uido,auto_gidq,0644);
277
278 c(auto_qmail,"man/cat1","fastforward.0",auto_uido,auto_gidq,0644);
279 c(auto_qmail,"man/cat1","printforward.0",auto_uido,auto_gidq,0644);
280 c(auto_qmail,"man/cat1","setforward.0",auto_uido,auto_gidq,0644);
281 c(auto_qmail,"man/cat1","newaliases.0",auto_uido,auto_gidq,0644);
282 c(auto_qmail,"man/cat1","printmaillist.0",auto_uido,auto_gidq,0644);
283 c(auto_qmail,"man/cat1","setmaillist.0",auto_uido,auto_gidq,0644);
284 c(auto_qmail,"man/cat1","newinclude.0",auto_uido,auto_gidq,0644);
285}
diff --git a/install.c b/install.c
new file mode 100644
index 0000000..95034f2
--- /dev/null
+++ b/install.c
@@ -0,0 +1,164 @@
1#include "substdio.h"
2#include "strerr.h"
3#include "error.h"
4#include "open.h"
5#include "readwrite.h"
6#include "exit.h"
7
8extern void hier();
9
10#define FATAL "install: fatal: "
11
12int fdsourcedir = -1;
13
14void h(home,uid,gid,mode)
15char *home;
16int uid;
17int gid;
18int mode;
19{
20 if (mkdir(home,0700) == -1)
21 if (errno != error_exist)
22 strerr_die4sys(111,FATAL,"unable to mkdir ",home,": ");
23 if (chown(home,uid,gid) == -1)
24 strerr_die4sys(111,FATAL,"unable to chown ",home,": ");
25 if (chmod(home,mode) == -1)
26 strerr_die4sys(111,FATAL,"unable to chmod ",home,": ");
27}
28
29void d(home,subdir,uid,gid,mode)
30char *home;
31char *subdir;
32int uid;
33int gid;
34int mode;
35{
36 if (chdir(home) == -1)
37 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
38 if (mkdir(subdir,0700) == -1)
39 if (errno != error_exist)
40 strerr_die6sys(111,FATAL,"unable to mkdir ",home,"/",subdir,": ");
41 if (chown(subdir,uid,gid) == -1)
42 strerr_die6sys(111,FATAL,"unable to chown ",home,"/",subdir,": ");
43 if (chmod(subdir,mode) == -1)
44 strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",subdir,": ");
45}
46
47void p(home,fifo,uid,gid,mode)
48char *home;
49char *fifo;
50int uid;
51int gid;
52int mode;
53{
54 if (chdir(home) == -1)
55 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
56 if (fifo_make(fifo,0700) == -1)
57 if (errno != error_exist)
58 strerr_die6sys(111,FATAL,"unable to mkfifo ",home,"/",fifo,": ");
59 if (chown(fifo,uid,gid) == -1)
60 strerr_die6sys(111,FATAL,"unable to chown ",home,"/",fifo,": ");
61 if (chmod(fifo,mode) == -1)
62 strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",fifo,": ");
63}
64
65char inbuf[SUBSTDIO_INSIZE];
66char outbuf[SUBSTDIO_OUTSIZE];
67substdio ssin;
68substdio ssout;
69
70void c(home,subdir,file,uid,gid,mode)
71char *home;
72char *subdir;
73char *file;
74int uid;
75int gid;
76int mode;
77{
78 int fdin;
79 int fdout;
80
81 if (fchdir(fdsourcedir) == -1)
82 strerr_die2sys(111,FATAL,"unable to switch back to source directory: ");
83
84 fdin = open_read(file);
85 if (fdin == -1)
86 strerr_die4sys(111,FATAL,"unable to read ",file,": ");
87 substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof inbuf);
88
89 if (chdir(home) == -1)
90 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
91 if (chdir(subdir) == -1)
92 strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
93
94 fdout = open_trunc(file);
95 if (fdout == -1)
96 strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
97 substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf);
98
99 switch(substdio_copy(&ssout,&ssin)) {
100 case -2:
101 strerr_die4sys(111,FATAL,"unable to read ",file,": ");
102 case -3:
103 strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
104 }
105
106 close(fdin);
107 if (substdio_flush(&ssout) == -1)
108 strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
109 if (fsync(fdout) == -1)
110 strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
111 if (close(fdout) == -1) /* NFS silliness */
112 strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
113
114 if (chown(file,uid,gid) == -1)
115 strerr_die6sys(111,FATAL,"unable to chown .../",subdir,"/",file,": ");
116 if (chmod(file,mode) == -1)
117 strerr_die6sys(111,FATAL,"unable to chmod .../",subdir,"/",file,": ");
118}
119
120void z(home,file,len,uid,gid,mode)
121char *home;
122char *file;
123int len;
124int uid;
125int gid;
126int mode;
127{
128 int fdout;
129
130 if (chdir(home) == -1)
131 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
132
133 fdout = open_trunc(file);
134 if (fdout == -1)
135 strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
136 substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf);
137
138 while (len-- > 0)
139 if (substdio_put(&ssout,"",1) == -1)
140 strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
141
142 if (substdio_flush(&ssout) == -1)
143 strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
144 if (fsync(fdout) == -1)
145 strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
146 if (close(fdout) == -1) /* NFS silliness */
147 strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": ");
148
149 if (chown(file,uid,gid) == -1)
150 strerr_die6sys(111,FATAL,"unable to chown ",home,"/",file,": ");
151 if (chmod(file,mode) == -1)
152 strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",file,": ");
153}
154
155void main()
156{
157 fdsourcedir = open_read(".");
158 if (fdsourcedir == -1)
159 strerr_die2sys(111,FATAL,"unable to open current directory: ");
160
161 umask(077);
162 hier();
163 _exit(0);
164}
diff --git a/instcheck.c b/instcheck.c
new file mode 100644
index 0000000..d41efda
--- /dev/null
+++ b/instcheck.c
@@ -0,0 +1,108 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "strerr.h"
4#include "error.h"
5#include "readwrite.h"
6#include "exit.h"
7
8extern void hier();
9
10#define FATAL "instcheck: fatal: "
11#define WARNING "instcheck: warning: "
12
13void perm(prefix1,prefix2,prefix3,file,type,uid,gid,mode)
14char *prefix1;
15char *prefix2;
16char *prefix3;
17char *file;
18int type;
19int uid;
20int gid;
21int mode;
22{
23 struct stat st;
24
25 if (stat(file,&st) == -1) {
26 if (errno == error_noent)
27 strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," does not exist",0);
28 else
29 strerr_warn4(WARNING,"unable to stat .../",file,": ",&strerr_sys);
30 return;
31 }
32
33 if ((uid != -1) && (st.st_uid != uid))
34 strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong owner",0);
35 if ((gid != -1) && (st.st_gid != gid))
36 strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong group",0);
37 if ((st.st_mode & 07777) != mode)
38 strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong permissions",0);
39 if ((st.st_mode & S_IFMT) != type)
40 strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong type",0);
41}
42
43void h(home,uid,gid,mode)
44char *home;
45int uid;
46int gid;
47int mode;
48{
49 perm("","","",home,S_IFDIR,uid,gid,mode);
50}
51
52void d(home,subdir,uid,gid,mode)
53char *home;
54char *subdir;
55int uid;
56int gid;
57int mode;
58{
59 if (chdir(home) == -1)
60 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
61 perm("",home,"/",subdir,S_IFDIR,uid,gid,mode);
62}
63
64void p(home,fifo,uid,gid,mode)
65char *home;
66char *fifo;
67int uid;
68int gid;
69int mode;
70{
71 if (chdir(home) == -1)
72 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
73 perm("",home,"/",fifo,S_IFIFO,uid,gid,mode);
74}
75
76void c(home,subdir,file,uid,gid,mode)
77char *home;
78char *subdir;
79char *file;
80int uid;
81int gid;
82int mode;
83{
84 if (chdir(home) == -1)
85 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
86 if (chdir(subdir) == -1)
87 strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
88 perm(".../",subdir,"/",file,S_IFREG,uid,gid,mode);
89}
90
91void z(home,file,len,uid,gid,mode)
92char *home;
93char *file;
94int len;
95int uid;
96int gid;
97int mode;
98{
99 if (chdir(home) == -1)
100 strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
101 perm("",home,"/",file,S_IFREG,uid,gid,mode);
102}
103
104void main()
105{
106 hier();
107 _exit(0);
108}
diff --git a/ip.c b/ip.c
new file mode 100644
index 0000000..5528ad5
--- /dev/null
+++ b/ip.c
@@ -0,0 +1,53 @@
1#include "fmt.h"
2#include "scan.h"
3#include "ip.h"
4
5unsigned int ip_fmt(s,ip)
6char *s;
7struct ip_address *ip;
8{
9 unsigned int len;
10 unsigned int i;
11
12 len = 0;
13 i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i;
14 i = fmt_str(s,"."); len += i; if (s) s += i;
15 i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i;
16 i = fmt_str(s,"."); len += i; if (s) s += i;
17 i = fmt_ulong(s,(unsigned long) ip->d[2]); len += i; if (s) s += i;
18 i = fmt_str(s,"."); len += i; if (s) s += i;
19 i = fmt_ulong(s,(unsigned long) ip->d[3]); len += i; if (s) s += i;
20 return len;
21}
22
23unsigned int ip_scan(s,ip)
24char *s;
25struct ip_address *ip;
26{
27 unsigned int i;
28 unsigned int len;
29 unsigned long u;
30
31 len = 0;
32 i = scan_ulong(s,&u); if (!i) return 0; ip->d[0] = u; s += i; len += i;
33 if (*s != '.') return 0; ++s; ++len;
34 i = scan_ulong(s,&u); if (!i) return 0; ip->d[1] = u; s += i; len += i;
35 if (*s != '.') return 0; ++s; ++len;
36 i = scan_ulong(s,&u); if (!i) return 0; ip->d[2] = u; s += i; len += i;
37 if (*s != '.') return 0; ++s; ++len;
38 i = scan_ulong(s,&u); if (!i) return 0; ip->d[3] = u; s += i; len += i;
39 return len;
40}
41
42unsigned int ip_scanbracket(s,ip)
43char *s;
44struct ip_address *ip;
45{
46 unsigned int len;
47
48 if (*s != '[') return 0;
49 len = ip_scan(s + 1,ip);
50 if (!len) return 0;
51 if (s[len + 1] != ']') return 0;
52 return len + 2;
53}
diff --git a/ip.h b/ip.h
new file mode 100644
index 0000000..b99002f
--- /dev/null
+++ b/ip.h
@@ -0,0 +1,11 @@
1#ifndef IP_H
2#define IP_H
3
4struct ip_address { unsigned char d[4]; } ;
5
6extern unsigned int ip_fmt();
7#define IPFMT 19
8extern unsigned int ip_scan();
9extern unsigned int ip_scanbracket();
10
11#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 @@
1#include "alloc.h"
2#include "gen_allocdefs.h"
3#include "ip.h"
4#include "ipalloc.h"
5
6GEN_ALLOC_readyplus(ipalloc,struct ip_mx,ix,len,a,i,n,x,10,ipalloc_readyplus)
7GEN_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 @@
1#ifndef IPALLOC_H
2#define IPALLOC_H
3
4#include "ip.h"
5
6struct ip_mx { struct ip_address ip; int pref; } ;
7
8#include "gen_alloc.h"
9
10GEN_ALLOC_typedef(ipalloc,struct ip_mx,ix,len,a)
11extern int ipalloc_readyplus();
12extern int ipalloc_append();
13
14#endif
diff --git a/ipme.c b/ipme.c
new file mode 100644
index 0000000..1f190b7
--- /dev/null
+++ b/ipme.c
@@ -0,0 +1,95 @@
1#include <sys/types.h>
2#include <sys/param.h>
3#include <sys/time.h>
4#include <sys/ioctl.h>
5#include <sys/socket.h>
6#include <net/if.h>
7#include <netinet/in.h>
8#ifndef SIOCGIFCONF /* whatever works */
9#include <sys/sockio.h>
10#endif
11#include "hassalen.h"
12#include "byte.h"
13#include "ip.h"
14#include "ipalloc.h"
15#include "stralloc.h"
16#include "ipme.h"
17
18static int ipmeok = 0;
19ipalloc ipme = {0};
20
21int ipme_is(ip)
22struct ip_address *ip;
23{
24 int i;
25 if (ipme_init() != 1) return -1;
26 for (i = 0;i < ipme.len;++i)
27 if (byte_equal(&ipme.ix[i].ip,4,ip))
28 return 1;
29 return 0;
30}
31
32static stralloc buf = {0};
33
34int ipme_init()
35{
36 struct ifconf ifc;
37 char *x;
38 struct ifreq *ifr;
39 struct sockaddr_in *sin;
40 int len;
41 int s;
42 struct ip_mx ix;
43
44 if (ipmeok) return 1;
45 if (!ipalloc_readyplus(&ipme,0)) return 0;
46 ipme.len = 0;
47 ix.pref = 0;
48
49 if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
50
51 len = 256;
52 for (;;) {
53 if (!stralloc_ready(&buf,len)) { close(s); return 0; }
54 buf.len = 0;
55 ifc.ifc_buf = buf.s;
56 ifc.ifc_len = len;
57 if (ioctl(s,SIOCGIFCONF,&ifc) >= 0) /* > is for System V */
58 if (ifc.ifc_len + sizeof(*ifr) + 64 < len) { /* what a stupid interface */
59 buf.len = ifc.ifc_len;
60 break;
61 }
62 if (len > 200000) { close(s); return -1; }
63 len += 100 + (len >> 2);
64 }
65 x = buf.s;
66 while (x < buf.s + buf.len) {
67 ifr = (struct ifreq *) x;
68#ifdef HASSALEN
69 len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
70 if (len < sizeof(*ifr))
71 len = sizeof(*ifr);
72 if (ifr->ifr_addr.sa_family == AF_INET) {
73 sin = (struct sockaddr_in *) &ifr->ifr_addr;
74 byte_copy(&ix.ip,4,&sin->sin_addr);
75 if (ioctl(s,SIOCGIFFLAGS,x) == 0)
76 if (ifr->ifr_flags & IFF_UP)
77 if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
78 }
79#else
80 len = sizeof(*ifr);
81 if (ioctl(s,SIOCGIFFLAGS,x) == 0)
82 if (ifr->ifr_flags & IFF_UP)
83 if (ioctl(s,SIOCGIFADDR,x) == 0)
84 if (ifr->ifr_addr.sa_family == AF_INET) {
85 sin = (struct sockaddr_in *) &ifr->ifr_addr;
86 byte_copy(&ix.ip,4,&sin->sin_addr);
87 if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
88 }
89#endif
90 x += len;
91 }
92 close(s);
93 ipmeok = 1;
94 return 1;
95}
diff --git a/ipme.h b/ipme.h
new file mode 100644
index 0000000..5bbf515
--- /dev/null
+++ b/ipme.h
@@ -0,0 +1,12 @@
1#ifndef IPME_H
2#define IPME_H
3
4#include "ip.h"
5#include "ipalloc.h"
6
7extern ipalloc ipme;
8
9extern int ipme_init();
10extern int ipme_is();
11
12#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 @@
1#include "subfd.h"
2#include "substdio.h"
3#include "ip.h"
4#include "ipme.h"
5#include "exit.h"
6
7char temp[IPFMT];
8
9void main()
10{
11 int j;
12 switch(ipme_init())
13 {
14 case 0: substdio_putsflush(subfderr,"out of memory\n"); _exit(111);
15 case -1: substdio_putsflush(subfderr,"hard error\n"); _exit(100);
16 }
17 for (j = 0;j < ipme.len;++j)
18 {
19 substdio_put(subfdout,temp,ip_fmt(temp,&ipme.ix[j].ip));
20 substdio_puts(subfdout,"\n");
21 }
22 substdio_flush(subfdout);
23 _exit(0);
24}
diff --git a/lock.h b/lock.h
new file mode 100644
index 0000000..a7dee41
--- /dev/null
+++ b/lock.h
@@ -0,0 +1,8 @@
1#ifndef LOCK_H
2#define LOCK_H
3
4extern int lock_ex();
5extern int lock_un();
6extern int lock_exnb();
7
8#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 @@
1#include <sys/types.h>
2#include <sys/file.h>
3#include <fcntl.h>
4#include "hasflock.h"
5#include "lock.h"
6
7#ifdef HASFLOCK
8int lock_ex(fd) int fd; { return flock(fd,LOCK_EX); }
9#else
10int lock_ex(fd) int fd; { return lockf(fd,1,0); }
11#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 @@
1#include <sys/types.h>
2#include <sys/file.h>
3#include <fcntl.h>
4#include "hasflock.h"
5#include "lock.h"
6
7#ifdef HASFLOCK
8int lock_exnb(fd) int fd; { return flock(fd,LOCK_EX | LOCK_NB); }
9#else
10int lock_exnb(fd) int fd; { return lockf(fd,2,0); }
11#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 @@
1#include <sys/types.h>
2#include <sys/file.h>
3#include <fcntl.h>
4#include "hasflock.h"
5#include "lock.h"
6
7#ifdef HASFLOCK
8int lock_un(fd) int fd; { return flock(fd,LOCK_UN); }
9#else
10int lock_un(fd) int fd; { return lockf(fd,0,0); }
11#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 @@
1.TH maildir 5
2.SH "NAME"
3maildir \- directory for incoming mail messages
4.SH "INTRODUCTION"
5.I maildir
6is a structure for
7directories of incoming mail messages.
8It solves the reliability problems that plague
9.I mbox
10files and
11.I mh
12folders.
13.SH "RELIABILITY ISSUES"
14A machine may crash while it is delivering a message.
15For both
16.I mbox
17files and
18.I mh
19folders this means that the message will be silently truncated.
20Even worse: for
21.I mbox
22format, if the message is truncated in the middle of a line,
23it will be silently joined to the next message.
24The mail transport agent will try again later to deliver the message,
25but it is unacceptable that a corrupted message should show up at all.
26In
27.IR maildir ,
28every message is guaranteed complete upon delivery.
29
30A machine may have two programs simultaneously delivering mail
31to the same user.
32The
33.I mbox
34and
35.I mh
36formats require the programs to update a single central file.
37If the programs do not use some locking mechanism,
38the central file will be corrupted.
39There are several
40.I mbox
41and
42.I mh
43locking mechanisms,
44none of which work portably and reliably.
45In contrast, in
46.IR maildir ,
47no locks are ever necessary.
48Different delivery processes never touch the same file.
49
50A user may try to delete messages from his mailbox at the same
51moment that the machine delivers a new message.
52For
53.I mbox
54and
55.I mh
56formats, the user's mail-reading program must know
57what locking mechanism the mail-delivery programs use.
58In contrast, in
59.IR maildir ,
60any delivered message
61can be safely updated or deleted by a mail-reading program.
62
63Many sites use Sun's
64.B Network F\fPa\fBil\fPur\fBe System
65(NFS),
66presumably because the operating system vendor does not offer
67anything else.
68NFS exacerbates all of the above problems.
69Some NFS implementations don't provide
70.B any
71reliable locking mechanism.
72With
73.I mbox
74and
75.I mh
76formats,
77if two machines deliver mail to the same user,
78or if a user reads mail anywhere except the delivery machine,
79the user's mail is at risk.
80.I maildir
81works without trouble over NFS.
82.SH "THE MAILDIR STRUCTURE"
83A directory in
84.I maildir
85format has three subdirectories,
86all on the same filesystem:
87.BR tmp ,
88.BR new ,
89and
90.BR cur .
91
92Each file in
93.B new
94is a newly delivered mail message.
95The modification time of the file is the delivery date of the message.
96The message is delivered
97.I without
98an extra UUCP-style
99.B From_
100line,
101.I without
102any
103.B >From
104quoting,
105and
106.I without
107an extra blank line at the end.
108The message is normally in RFC 822 format,
109starting with a
110.B Return-Path
111line and a
112.B Delivered-To
113line,
114but it could contain arbitrary binary data.
115It might not even end with a newline.
116
117Files in
118.B cur
119are just like files in
120.BR new .
121The big difference is that files in
122.B cur
123are no longer new mail:
124they have been seen by the user's mail-reading program.
125.SH "HOW A MESSAGE IS DELIVERED"
126The
127.B tmp
128directory is used to ensure reliable delivery,
129as discussed here.
130
131A program delivers a mail message in six steps.
132First, it
133.B chdir()\fPs
134to the
135.I maildir
136directory.
137Second, it
138.B stat()s
139the name
140.BR tmp/\fItime.pid.host ,
141where
142.I time
143is the number of seconds since the beginning of 1970 GMT,
144.I pid
145is the program's process ID,
146and
147.I host
148is the host name.
149Third, if
150.B stat()
151returned anything other than ENOENT,
152the program sleeps for two seconds, updates
153.IR time ,
154and tries the
155.B stat()
156again, a limited number of times.
157Fourth, the program
158creates
159.BR tmp/\fItime.pid.host .
160Fifth, the program
161.I NFS-writes
162the message to the file.
163Sixth, the program
164.BR link() s
165the file to
166.BR new/\fItime.pid.host .
167At that instant the message has been successfully delivered.
168
169The delivery program is required to start a 24-hour timer before
170creating
171.BR tmp/\fItime.pid.host ,
172and to abort the delivery
173if the timer expires.
174Upon error, timeout, or normal completion,
175the delivery program may attempt to
176.B unlink()
177.BR tmp/\fItime.pid.host .
178
179.I NFS-writing
180means
181(1) as usual, checking the number of bytes returned from each
182.B write()
183call;
184(2) calling
185.B fsync()
186and checking its return value;
187(3) calling
188.B close()
189and checking its return value.
190(Standard NFS implementations handle
191.B fsync()
192incorrectly
193but make up for it by abusing
194.BR close() .)
195.SH "HOW A MESSAGE IS READ"
196A mail reader operates as follows.
197
198It looks through the
199.B new
200directory for new messages.
201Say there is a new message,
202.BR new/\fIunique .
203The reader may freely display the contents of
204.BR new/\fIunique ,
205delete
206.BR new/\fIunique ,
207or rename
208.B new/\fIunique
209as
210.BR cur/\fIunique:info .
211See
212.B http://pobox.com/~djb/proto/maildir.html
213for the meaning of
214.IR info .
215
216The reader is also expected to look through the
217.B tmp
218directory and to clean up any old files found there.
219A file in
220.B tmp
221may be safely removed if it
222has not been accessed in 36 hours.
223
224It is a good idea for readers to skip all filenames in
225.B new
226and
227.B cur
228starting with a dot.
229Other than this, readers should not attempt to parse filenames.
230.SH "ENVIRONMENT VARIABLES"
231Mail readers supporting
232.I maildir
233use the
234.B MAILDIR
235environment variable
236as the name of the user's primary mail directory.
237.SH "SEE ALSO"
238mbox(5),
239qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "prioq.h"
4#include "env.h"
5#include "stralloc.h"
6#include "direntry.h"
7#include "datetime.h"
8#include "now.h"
9#include "str.h"
10#include "maildir.h"
11
12struct strerr maildir_chdir_err;
13struct strerr maildir_scan_err;
14
15int maildir_chdir()
16{
17 char *maildir;
18 maildir = env_get("MAILDIR");
19 if (!maildir)
20 STRERR(-1,maildir_chdir_err,"MAILDIR not set")
21 if (chdir(maildir) == -1)
22 STRERR_SYS3(-1,maildir_chdir_err,"unable to chdir to ",maildir,": ")
23 return 0;
24}
25
26void maildir_clean(tmpname)
27stralloc *tmpname;
28{
29 DIR *dir;
30 direntry *d;
31 datetime_sec time;
32 struct stat st;
33
34 time = now();
35
36 dir = opendir("tmp");
37 if (!dir) return;
38
39 while (d = readdir(dir))
40 {
41 if (d->d_name[0] == '.') continue;
42 if (!stralloc_copys(tmpname,"tmp/")) break;
43 if (!stralloc_cats(tmpname,d->d_name)) break;
44 if (!stralloc_0(tmpname)) break;
45 if (stat(tmpname->s,&st) == 0)
46 if (time > st.st_atime + 129600)
47 unlink(tmpname->s);
48 }
49 closedir(dir);
50}
51
52static int append(pq,filenames,subdir,time)
53prioq *pq;
54stralloc *filenames;
55char *subdir;
56datetime_sec time;
57{
58 DIR *dir;
59 direntry *d;
60 struct prioq_elt pe;
61 unsigned int pos;
62 struct stat st;
63
64 dir = opendir(subdir);
65 if (!dir)
66 STRERR_SYS3(-1,maildir_scan_err,"unable to scan $MAILDIR/",subdir,": ")
67
68 while (d = readdir(dir))
69 {
70 if (d->d_name[0] == '.') continue;
71 pos = filenames->len;
72 if (!stralloc_cats(filenames,subdir)) break;
73 if (!stralloc_cats(filenames,"/")) break;
74 if (!stralloc_cats(filenames,d->d_name)) break;
75 if (!stralloc_0(filenames)) break;
76 if (stat(filenames->s + pos,&st) == 0)
77 if (st.st_mtime < time) /* don't want to mix up the order */
78 {
79 pe.dt = st.st_mtime;
80 pe.id = pos;
81 if (!prioq_insert(pq,&pe)) break;
82 }
83 }
84
85 closedir(dir);
86 if (d) STRERR_SYS3(-1,maildir_scan_err,"unable to read $MAILDIR/",subdir,": ")
87 return 0;
88}
89
90int maildir_scan(pq,filenames,flagnew,flagcur)
91prioq *pq;
92stralloc *filenames;
93int flagnew;
94int flagcur;
95{
96 struct prioq_elt pe;
97 datetime_sec time;
98 int r;
99
100 if (!stralloc_copys(filenames,"")) return 0;
101 while (prioq_min(pq,&pe)) prioq_delmin(pq);
102
103 time = now();
104
105 if (flagnew) if (append(pq,filenames,"new",time) == -1) return -1;
106 if (flagcur) if (append(pq,filenames,"cur",time) == -1) return -1;
107 return 0;
108}
diff --git a/maildir.h b/maildir.h
new file mode 100644
index 0000000..7dd8826
--- /dev/null
+++ b/maildir.h
@@ -0,0 +1,12 @@
1#ifndef MAILDIR_H
2#define MAILDIR_H
3
4#include "strerr.h"
5extern struct strerr maildir_chdir_err;
6extern struct strerr maildir_scan_err;
7
8extern int maildir_chdir();
9extern void maildir_clean();
10extern int maildir_scan();
11
12#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 @@
1.TH maildir2mbox 1
2.SH NAME
3maildir2mbox \- move mail from a maildir to an mbox
4.SH SYNOPSIS
5.B maildir2mbox
6.SH DESCRIPTION
7.B maildir2mbox
8moves mail from a
9.IR maildir -format
10directory to an
11.IR mbox -format
12file.
13
14You must supply three environment variables to
15.BR maildir2mbox :
16.B MAILDIR
17is the name of your
18.I maildir
19directory;
20.B MAIL
21is the name of your
22.I mbox
23file;
24and
25.B MAILTMP
26is a temporary file that
27.B maildir2mbox
28can overwrite.
29.B MAILTMP
30and
31.B MAIL
32must be on the same filesystem.
33
34.B maildir2mbox
35is reliable:
36it will not remove messages
37from
38.B MAILDIR
39until the messages have been successfully appended to
40.BR MAIL .
41
42.B maildir2mbox
43locks
44.B MAIL
45to protect against simultaneous access by a mail reader.
46This locking system does not protect against simultaneous access
47by another
48.BR maildir2mbox ;
49you should run only one
50.B maildir2mbox
51at a time.
52.SH "SEE ALSO"
53maildir(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 @@
1#include "readwrite.h"
2#include "prioq.h"
3#include "env.h"
4#include "stralloc.h"
5#include "subfd.h"
6#include "substdio.h"
7#include "getln.h"
8#include "error.h"
9#include "open.h"
10#include "lock.h"
11#include "gfrom.h"
12#include "str.h"
13#include "exit.h"
14#include "myctime.h"
15#include "maildir.h"
16
17char *mbox;
18char *mboxtmp;
19
20stralloc filenames = {0};
21prioq pq = {0};
22prioq pq2 = {0};
23
24stralloc line = {0};
25
26stralloc ufline = {0};
27
28char inbuf[SUBSTDIO_INSIZE];
29char outbuf[SUBSTDIO_OUTSIZE];
30
31#define FATAL "maildir2mbox: fatal: "
32#define WARNING "maildir2mbox: warning: "
33
34void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
35
36void main()
37{
38 substdio ssin;
39 substdio ssout;
40 struct prioq_elt pe;
41 int fdoldmbox;
42 int fdnewmbox;
43 int fd;
44 int match;
45 int fdlock;
46
47 umask(077);
48
49 mbox = env_get("MAIL");
50 if (!mbox) strerr_die2x(111,FATAL,"MAIL not set");
51 mboxtmp = env_get("MAILTMP");
52 if (!mboxtmp) strerr_die2x(111,FATAL,"MAILTMP not set");
53
54 if (maildir_chdir() == -1)
55 strerr_die1(111,FATAL,&maildir_chdir_err);
56 maildir_clean(&filenames);
57 if (maildir_scan(&pq,&filenames,1,1) == -1)
58 strerr_die1(111,FATAL,&maildir_scan_err);
59
60 if (!prioq_min(&pq,&pe)) _exit(0); /* nothing new */
61
62 fdlock = open_append(mbox);
63 if (fdlock == -1)
64 strerr_die4sys(111,FATAL,"unable to lock ",mbox,": ");
65 if (lock_ex(fdlock) == -1)
66 strerr_die4sys(111,FATAL,"unable to lock ",mbox,": ");
67
68 fdoldmbox = open_read(mbox);
69 if (fdoldmbox == -1)
70 strerr_die4sys(111,FATAL,"unable to read ",mbox,": ");
71
72 fdnewmbox = open_trunc(mboxtmp);
73 if (fdnewmbox == -1)
74 strerr_die4sys(111,FATAL,"unable to create ",mboxtmp,": ");
75
76 substdio_fdbuf(&ssin,read,fdoldmbox,inbuf,sizeof(inbuf));
77 substdio_fdbuf(&ssout,write,fdnewmbox,outbuf,sizeof(outbuf));
78
79 switch(substdio_copy(&ssout,&ssin))
80 {
81 case -2: strerr_die4sys(111,FATAL,"unable to read ",mbox,": ");
82 case -3: strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
83 }
84
85 while (prioq_min(&pq,&pe))
86 {
87 prioq_delmin(&pq);
88 if (!prioq_insert(&pq2,&pe)) die_nomem();
89
90 fd = open_read(filenames.s + pe.id);
91 if (fd == -1)
92 strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": ");
93 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
94
95 if (getln(&ssin,&line,&match,'\n') != 0)
96 strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": ");
97
98 if (!stralloc_copys(&ufline,"From XXX ")) die_nomem();
99 if (match)
100 if (stralloc_starts(&line,"Return-Path: <"))
101 {
102 if (line.s[14] == '>')
103 {
104 if (!stralloc_copys(&ufline,"From MAILER-DAEMON ")) die_nomem();
105 }
106 else
107 {
108 int i;
109 if (!stralloc_ready(&ufline,line.len)) die_nomem();
110 if (!stralloc_copys(&ufline,"From ")) die_nomem();
111 for (i = 14;i < line.len - 2;++i)
112 if ((line.s[i] == ' ') || (line.s[i] == '\t'))
113 ufline.s[ufline.len++] = '-';
114 else
115 ufline.s[ufline.len++] = line.s[i];
116 if (!stralloc_cats(&ufline," ")) die_nomem();
117 }
118 }
119 if (!stralloc_cats(&ufline,myctime(pe.dt))) die_nomem();
120 if (substdio_put(&ssout,ufline.s,ufline.len) == -1)
121 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
122
123 while (match && line.len)
124 {
125 if (gfrom(line.s,line.len))
126 if (substdio_puts(&ssout,">") == -1)
127 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
128 if (substdio_put(&ssout,line.s,line.len) == -1)
129 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
130 if (!match)
131 {
132 if (substdio_puts(&ssout,"\n") == -1)
133 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
134 break;
135 }
136 if (getln(&ssin,&line,&match,'\n') != 0)
137 strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": ");
138 }
139 if (substdio_puts(&ssout,"\n"))
140 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
141
142 close(fd);
143 }
144
145 if (substdio_flush(&ssout) == -1)
146 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
147 if (fsync(fdnewmbox) == -1)
148 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
149 if (close(fdnewmbox) == -1) /* NFS dorks */
150 strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": ");
151 if (rename(mboxtmp,mbox) == -1)
152 strerr_die6(111,FATAL,"unable to move ",mboxtmp," to ",mbox,": ",&strerr_sys);
153
154 while (prioq_min(&pq2,&pe))
155 {
156 prioq_delmin(&pq2);
157 if (unlink(filenames.s + pe.id) == -1)
158 strerr_warn4(WARNING,"$MAILDIR/",filenames.s + pe.id," will be delivered twice; unable to unlink: ",&strerr_sys);
159 }
160
161 _exit(0);
162}
diff --git a/maildirmake.1 b/maildirmake.1
new file mode 100644
index 0000000..e5bd042
--- /dev/null
+++ b/maildirmake.1
@@ -0,0 +1,15 @@
1.TH maildirmake 1
2.SH NAME
3maildirmake \- create a maildir for incoming mail
4.SH SYNOPSIS
5.B maildirmake
6.I dir
7.SH DESCRIPTION
8.B maildirmake
9makes a new directory,
10.IR dir ,
11in
12.B maildir
13format.
14.SH "SEE ALSO"
15maildir(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 @@
1#include "strerr.h"
2#include "exit.h"
3
4#define FATAL "maildirmake: fatal: "
5
6void main(argc,argv)
7int argc;
8char **argv;
9{
10 umask(077);
11 if (!argv[1])
12 strerr_die1x(100,"maildirmake: usage: maildirmake name");
13 if (mkdir(argv[1],0700) == -1)
14 strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],": ");
15 if (chdir(argv[1]) == -1)
16 strerr_die4sys(111,FATAL,"unable to chdir to ",argv[1],": ");
17 if (mkdir("tmp",0700) == -1)
18 strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/tmp: ");
19 if (mkdir("new",0700) == -1)
20 strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/new: ");
21 if (mkdir("cur",0700) == -1)
22 strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/cur: ");
23 _exit(0);
24}
diff --git a/maildirwatch.1 b/maildirwatch.1
new file mode 100644
index 0000000..d1d70a0
--- /dev/null
+++ b/maildirwatch.1
@@ -0,0 +1,23 @@
1.TH maildirwatch 1
2.SH NAME
3maildirwatch \- look for new mail in a maildir
4.SH SYNOPSIS
5.B maildirwatch
6.SH DESCRIPTION
7.B maildirwatch
8watches your
9.I maildir
10for new mail.
11You must supply a
12.B MAILDIR
13environment variable
14with the name of your
15.I maildir
16directory.
17
18.B maildirwatch
19prints a new mail summary twice per minute.
20It is designed to run inside a (VT100-compatible) window;
21it clears the window before each summary.
22.SH "SEE ALSO"
23maildir(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 @@
1#include "getln.h"
2#include "substdio.h"
3#include "subfd.h"
4#include "prioq.h"
5#include "stralloc.h"
6#include "str.h"
7#include "exit.h"
8#include "hfield.h"
9#include "readwrite.h"
10#include "open.h"
11#include "headerbody.h"
12#include "maildir.h"
13
14#define FATAL "maildirwatch: fatal: "
15
16void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
17
18stralloc recipient = {0};
19stralloc sender = {0};
20stralloc fromline = {0};
21stralloc text = {0};
22
23void addtext(s,n) char *s; int n;
24{
25 if (!stralloc_catb(&text,s,n)) die_nomem();
26 if (text.len > 158) text.len = 158;
27}
28void dobody(h) stralloc *h; { addtext(h->s,h->len); }
29void doheader(h) stralloc *h;
30{
31 int i;
32 switch(hfield_known(h->s,h->len))
33 {
34 case H_SUBJECT:
35 i = hfield_skipname(h->s,h->len);
36 addtext(h->s + i,h->len - i);
37 break;
38 case H_DELIVEREDTO:
39 i = hfield_skipname(h->s,h->len);
40 if (i < h->len)
41 if (!stralloc_copyb(&recipient,h->s + i,h->len - i - 1)) die_nomem();
42 break;
43 case H_RETURNPATH:
44 i = hfield_skipname(h->s,h->len);
45 if (i < h->len)
46 if (!stralloc_copyb(&sender,h->s + i,h->len - i - 1)) die_nomem();
47 break;
48 case H_FROM:
49 if (!stralloc_copyb(&fromline,h->s,h->len - 1)) die_nomem();
50 break;
51 }
52}
53void finishheader() { ; }
54
55stralloc filenames = {0};
56prioq pq = {0};
57
58char inbuf[SUBSTDIO_INSIZE];
59substdio ssin;
60
61void main()
62{
63 struct prioq_elt pe;
64 int fd;
65 int i;
66
67 if (maildir_chdir() == -1)
68 strerr_die1(111,FATAL,&maildir_chdir_err);
69
70 for (;;)
71 {
72 maildir_clean(&filenames);
73 if (maildir_scan(&pq,&filenames,1,0) == -1)
74 strerr_die1(111,FATAL,&maildir_scan_err);
75
76 substdio_putsflush(subfdout,"\033[;H\033[;J");
77
78 while (prioq_min(&pq,&pe))
79 {
80 prioq_delmin(&pq);
81
82 fd = open_read(filenames.s + pe.id);
83 if (fd == -1) continue;
84 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
85
86 if (!stralloc_copys(&sender,"?")) die_nomem();
87 if (!stralloc_copys(&recipient,"?")) die_nomem();
88 if (!stralloc_copys(&fromline,"")) die_nomem();
89 if (!stralloc_copys(&text,"")) die_nomem();
90 if (headerbody(&ssin,doheader,finishheader,dobody) == -1)
91 strerr_die2x(111,FATAL,"trouble reading new message");
92
93 for (i = 0;i < fromline.len;++i)
94 if ((fromline.s[i] < 32) || (fromline.s[i] > 126))
95 fromline.s[i] = '/';
96 for (i = 0;i < sender.len;++i)
97 if ((sender.s[i] < 32) || (sender.s[i] > 126))
98 sender.s[i] = '?';
99 for (i = 0;i < recipient.len;++i)
100 if ((recipient.s[i] < 32) || (recipient.s[i] > 126))
101 recipient.s[i] = '?';
102 for (i = 0;i < text.len;++i)
103 if ((text.s[i] < 32) || (text.s[i] > 126))
104 text.s[i] = '/';
105 substdio_puts(subfdout,"FROM ");
106 substdio_put(subfdout,sender.s,sender.len);
107 substdio_puts(subfdout," TO <");
108 substdio_put(subfdout,recipient.s,recipient.len);
109 substdio_puts(subfdout,">\n");
110 if (fromline.len)
111 {
112 substdio_puts(subfdout,"\033[1m");
113 substdio_put(subfdout,fromline.s,fromline.len);
114 substdio_puts(subfdout,"\033[0m\n");
115 }
116 substdio_put(subfdout,text.s,text.len);
117 substdio_puts(subfdout,"\n\n");
118
119 close(fd);
120 }
121
122 substdio_flush(subfdout);
123 sleep(30);
124 }
125}
diff --git a/mailsubj.1 b/mailsubj.1
new file mode 100644
index 0000000..687b8e8
--- /dev/null
+++ b/mailsubj.1
@@ -0,0 +1,38 @@
1.TH mailsubj 1
2.SH NAME
3mailsubj \- send a mail message with a subject line
4.SH SYNOPSIS
5.B mailsubj
6.I subject
7.I recip ...
8.SH DESCRIPTION
9.B mailsubj
10inserts
11.I subject
12and the list of
13.IR recip s
14into a mail message:
15
16.EX
17 Subject: subject
18.br
19 To: recip ...
20.br
21
22.br
23 body
24.EE
25
26.B mailsubj
27reads the body of the message from its standard input.
28Then it sends the message.
29
30Note that
31.I subject
32and
33.I recip
34must be quoted properly for the message header.
35.SH "SEE ALSO"
36addresses(5),
37qmail-header(8),
38qmail-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 @@
1subject="$1"
2shift
3( echo Subject: "$subject"
4 echo To: ${1+"$@"}
5 echo ''
6 cat
7) | 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 @@
1echo 'main="$1"; shift'
2echo 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 @@
1echo 'main="$1"; shift'
2echo 'rm -f "$main"'
3echo 'ar cr "$main" ${1+"$@"}'
4
5case "$1" in
6sunos-5.*) ;;
7unix_sv*) ;;
8irix64-*) ;;
9irix-*) ;;
10dgux-*) ;;
11hp-ux-*) ;;
12sco*) ;;
13*)
14 echo 'ranlib "$main"'
15 ;;
16esac
diff --git a/mbox.5 b/mbox.5
new file mode 100644
index 0000000..de1f837
--- /dev/null
+++ b/mbox.5
@@ -0,0 +1,235 @@
1.TH mbox 5
2.SH "NAME"
3mbox \- file containing mail messages
4.SH "INTRODUCTION"
5The most common format for storage of mail messages is
6.I mbox
7format.
8An
9.I mbox
10is a single file containing zero or more mail messages.
11.SH "MESSAGE FORMAT"
12A message encoded in
13.I mbox
14format begins with a
15.B From_
16line, continues with a series of
17.B \fRnon-\fBFrom_
18lines,
19and ends with a blank line.
20A
21.B From_
22line means any line that begins with the characters
23F, r, o, m, space:
24
25.EX
26 From god@heaven.af.mil Sat Jan 3 01:05:34 1996
27.br
28 Return-Path: <god@heaven.af.mil>
29.br
30 Delivered-To: djb@silverton.berkeley.edu
31.br
32 Date: 3 Jan 1996 01:05:34 -0000
33.br
34 From: God <god@heaven.af.mil>
35.br
36 To: djb@silverton.berkeley.edu (D. J. Bernstein)
37.br
38
39.br
40 How's that mail system project coming along?
41.br
42
43.EE
44
45The final line is a completely blank line (no spaces or tabs).
46Notice that blank lines may also appear elsewhere in the message.
47
48The
49.B From_
50line always looks like
51.B From
52.I envsender
53.I date
54.IR moreinfo .
55.I envsender
56is one word, without spaces or tabs;
57it is usually the envelope sender of the message.
58.I date
59is the delivery date of the message.
60It always contains exactly 24 characters in
61.B asctime
62format.
63.I moreinfo
64is optional; it may contain arbitrary information.
65
66Between the
67.B From_
68line and the blank line is a message in RFC 822 format,
69as described in
70.BR qmail-header(5) ,
71subject to
72.B >From quoting
73as described below.
74.SH "HOW A MESSAGE IS DELIVERED"
75Here is how a program appends a message to an
76.I mbox
77file.
78
79It first creates a
80.B From_
81line given the message's envelope sender and the current date.
82If the envelope sender is empty (i.e., if this is a bounce message),
83the program uses
84.B MAILER-DAEMON
85instead.
86If the envelope sender contains spaces, tabs, or newlines,
87the program replaces them with hyphens.
88
89The program then copies the message, applying
90.B >From quoting
91to each line.
92.B >From quoting
93ensures that the resulting lines are not
94.B From_
95lines:
96the program prepends a
97.B >
98to any
99.B From_
100line,
101.B >From_
102line,
103.B >>From_
104line,
105.B >>>From_
106line,
107etc.
108
109Finally the program appends a blank line to the message.
110If the last line of the message was a partial line,
111it writes two newlines;
112otherwise it writes one.
113.SH "HOW A MESSAGE IS READ"
114A reader scans through an
115.I mbox
116file looking for
117.B From_
118lines.
119Any
120.B From_
121line marks the beginning of a message.
122The reader should not attempt to take advantage of the fact that every
123.B From_
124line (past the beginning of the file)
125is preceded by a blank line.
126
127Once the reader finds a message,
128it extracts a (possibly corrupted) envelope sender
129and delivery date out of the
130.B From_
131line.
132It then reads until the next
133.B From_
134line or end of file, whichever comes first.
135It strips off the final blank line
136and
137deletes the
138quoting of
139.B >From_
140lines and
141.B >>From_
142lines and so on.
143The result is an RFC 822 message.
144.SH "COMMON MBOX VARIANTS"
145There are many variants of
146.I mbox
147format.
148The variant described above is
149.I mboxrd
150format, popularized by Rahul Dhesi in June 1995.
151
152The original
153.I mboxo
154format quotes only
155.B From_
156lines, not
157.B >From_
158lines.
159As a result it is impossible to tell whether
160
161.EX
162 From: djb@silverton.berkeley.edu (D. J. Bernstein)
163.br
164 To: god@heaven.af.mil
165.br
166
167.br
168 >From now through August I'll be doing beta testing.
169.br
170 Thanks for your interest.
171.EE
172
173was quoted in the original message.
174An
175.I mboxrd
176reader will always strip off the quoting.
177
178.I mboxcl
179format is like
180.I mboxo
181format, but includes a Content-Length field with the
182number of bytes in the message.
183.I mboxcl2
184format is like
185.I mboxcl
186but has no
187.B >From
188quoting.
189These formats are used by SVR4 mailers.
190.I mboxcl2
191cannot be read safely by
192.I mboxrd
193readers.
194.SH "UNSPECIFIED DETAILS"
195There are many locking mechanisms for
196.I mbox
197files.
198.B qmail-local
199always uses
200.B flock
201on systems that have it, otherwise
202.BR lockf .
203
204The delivery date in a
205.B From_
206line does not specify a time zone.
207.B qmail-local
208always creates the delivery date in GMT
209so that
210.I mbox
211files can be safely transported from one time zone to another.
212
213If the mtime on a nonempty
214.I mbox
215file is greater than the atime,
216the file has new mail.
217If the mtime is smaller than the atime,
218the new mail has been read.
219If the atime equals the mtime,
220there is no way to tell whether the file has new mail,
221since
222.B qmail-local
223takes much less than a second to run.
224One solution is for a mail reader to artificially set the
225atime to the mtime plus 1.
226Then the file has new mail if and only if the atime is
227less than or equal to the mtime.
228
229Some mail readers place
230.B Status
231fields in each message to indicate which messages have been read.
232.SH "SEE ALSO"
233maildir(5),
234qmail-header(5),
235qmail-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 @@
1#include "datetime.h"
2#include "fmt.h"
3#include "myctime.h"
4
5static char *daytab[7] = {
6"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
7};
8static char *montab[12] = {
9"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
10};
11
12static char result[30];
13
14char *myctime(t)
15datetime_sec t;
16{
17 struct datetime dt;
18 unsigned int len;
19 datetime_tai(&dt,t);
20 len = 0;
21 len += fmt_str(result + len,daytab[dt.wday]);
22 result[len++] = ' ';
23 len += fmt_str(result + len,montab[dt.mon]);
24 result[len++] = ' ';
25 len += fmt_uint0(result + len,dt.mday,2);
26 result[len++] = ' ';
27 len += fmt_uint0(result + len,dt.hour,2);
28 result[len++] = ':';
29 len += fmt_uint0(result + len,dt.min,2);
30 result[len++] = ':';
31 len += fmt_uint0(result + len,dt.sec,2);
32 result[len++] = ' ';
33 len += fmt_uint(result + len,1900 + dt.year);
34 result[len++] = '\n';
35 result[len++] = 0;
36 return result;
37}
diff --git a/myctime.h b/myctime.h
new file mode 100644
index 0000000..ec1d03c
--- /dev/null
+++ b/myctime.h
@@ -0,0 +1,6 @@
1#ifndef MYCTIME_H
2#define MYCTIME_H
3
4extern char *myctime();
5
6#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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "ndelay.h"
4
5#ifndef O_NONBLOCK
6#define O_NONBLOCK O_NDELAY
7#endif
8
9int ndelay_on(fd)
10int fd;
11{
12 return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
13}
diff --git a/ndelay.h b/ndelay.h
new file mode 100644
index 0000000..68c6bce
--- /dev/null
+++ b/ndelay.h
@@ -0,0 +1,7 @@
1#ifndef NDELAY_H
2#define NDELAY_H
3
4extern int ndelay_on();
5extern int ndelay_off();
6
7#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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "ndelay.h"
4
5#ifndef O_NONBLOCK
6#define O_NONBLOCK O_NDELAY
7#endif
8
9int ndelay_off(fd)
10int fd;
11{
12 return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
13}
diff --git a/newfield.c b/newfield.c
new file mode 100644
index 0000000..78d6bb8
--- /dev/null
+++ b/newfield.c
@@ -0,0 +1,68 @@
1#include "fmt.h"
2#include "datetime.h"
3#include "stralloc.h"
4#include "date822fmt.h"
5#include "newfield.h"
6
7/* "Date: 26 Sep 1995 04:46:53 -0000\n" */
8stralloc newfield_date = {0};
9/* "Message-ID: <19950926044653.12345.qmail@silverton.berkeley.edu>\n" */
10stralloc newfield_msgid = {0};
11
12static unsigned int datefmt(s,when)
13char *s;
14datetime_sec when;
15{
16 unsigned int i;
17 unsigned int len;
18 struct datetime dt;
19 datetime_tai(&dt,when);
20 len = 0;
21 i = fmt_str(s,"Date: "); len += i; if (s) s += i;
22 i = date822fmt(s,&dt); len += i; if (s) s += i;
23 return len;
24}
25
26static unsigned int msgidfmt(s,idhost,idhostlen,when)
27char *s;
28char *idhost;
29int idhostlen;
30datetime_sec when;
31{
32 unsigned int i;
33 unsigned int len;
34 struct datetime dt;
35 datetime_tai(&dt,when);
36 len = 0;
37 i = fmt_str(s,"Message-ID: <"); len += i; if (s) s += i;
38 i = fmt_uint(s,dt.year + 1900); len += i; if (s) s += i;
39 i = fmt_uint0(s,dt.mon + 1,2); len += i; if (s) s += i;
40 i = fmt_uint0(s,dt.mday,2); len += i; if (s) s += i;
41 i = fmt_uint0(s,dt.hour,2); len += i; if (s) s += i;
42 i = fmt_uint0(s,dt.min,2); len += i; if (s) s += i;
43 i = fmt_uint0(s,dt.sec,2); len += i; if (s) s += i;
44 i = fmt_str(s,"."); len += i; if (s) s += i;
45 i = fmt_uint(s,getpid()); len += i; if (s) s += i;
46 i = fmt_str(s,".qmail@"); len += i; if (s) s += i;
47 i = fmt_strn(s,idhost,idhostlen); len += i; if (s) s += i;
48 i = fmt_str(s,">\n"); len += i; if (s) s += i;
49 return len;
50}
51
52int newfield_datemake(when)
53datetime_sec when;
54{
55 if (!stralloc_ready(&newfield_date,datefmt(FMT_LEN,when))) return 0;
56 newfield_date.len = datefmt(newfield_date.s,when);
57 return 1;
58}
59
60int newfield_msgidmake(idhost,idhostlen,when)
61char *idhost;
62int idhostlen;
63datetime_sec when;
64{
65 if (!stralloc_ready(&newfield_msgid,msgidfmt(FMT_LEN,idhost,idhostlen,when))) return 0;
66 newfield_msgid.len = msgidfmt(newfield_msgid.s,idhost,idhostlen,when);
67 return 1;
68}
diff --git a/newfield.h b/newfield.h
new file mode 100644
index 0000000..a51352a
--- /dev/null
+++ b/newfield.h
@@ -0,0 +1,12 @@
1#ifndef NEWFIELD_H
2#define NEWFIELD_H
3
4#include "stralloc.h"
5
6extern stralloc newfield_date;
7extern int newfield_datemake();
8
9extern stralloc newfield_msgid;
10extern int newfield_msgidmake();
11
12#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 @@
1.TH now 3
2.SH NAME
3now \- get current time, in seconds
4.SH SYNTAX
5.B #include <now.h>
6
7datetime_sec \fBnow\fP();
8.SH DESCRIPTION
9.B now
10returns the number of real-time seconds that have elapsed
11since the end of 1969 TAI.
12.SH "SEE ALSO"
13datetime(3),
14time(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 @@
1#include <time.h>
2#include "datetime.h"
3#include "now.h"
4
5datetime_sec now()
6{
7 return time((long *) 0);
8}
diff --git a/now.h b/now.h
new file mode 100644
index 0000000..b8be182
--- /dev/null
+++ b/now.h
@@ -0,0 +1,8 @@
1#ifndef NOW_H
2#define NOW_H
3
4#include "datetime.h"
5
6extern datetime_sec now();
7
8#endif
diff --git a/open.h b/open.h
new file mode 100644
index 0000000..c903113
--- /dev/null
+++ b/open.h
@@ -0,0 +1,10 @@
1#ifndef OPEN_H
2#define OPEN_H
3
4extern int open_read();
5extern int open_excl();
6extern int open_append();
7extern int open_trunc();
8extern int open_write();
9
10#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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "open.h"
4
5int open_append(fn) char *fn;
6{ 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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "open.h"
4
5int open_excl(fn) char *fn;
6{ 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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "open.h"
4
5int open_read(fn) char *fn;
6{ 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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "open.h"
4
5int open_trunc(fn) char *fn;
6{ 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 @@
1#include <sys/types.h>
2#include <fcntl.h>
3#include "open.h"
4
5int open_write(fn) char *fn;
6{ return open(fn,O_WRONLY | O_NDELAY); }
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 @@
1#include <sys/types.h>
2#include <time.h>
3#include "datetime.h"
4#include "fork.h"
5#include "wait.h"
6#include "fd.h"
7#include "fmt.h"
8#include "strerr.h"
9#include "substdio.h"
10#include "subfd.h"
11#include "readwrite.h"
12#include "exit.h"
13
14#define FATAL "predate: fatal: "
15
16static char *montab[12] = {
17"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
18};
19
20char num[FMT_ULONG];
21char outbuf[1024];
22
23void main(argc,argv)
24int argc;
25char **argv;
26{
27 time_t now;
28 struct tm *tm;
29 struct datetime dt;
30 datetime_sec utc;
31 datetime_sec local;
32 int minutes;
33 int pi[2];
34 substdio ss;
35 int wstat;
36 int pid;
37
38 sig_pipeignore();
39
40 if (!argv[1])
41 strerr_die1x(100,"predate: usage: predate child");
42
43 if (pipe(pi) == -1)
44 strerr_die2sys(111,FATAL,"unable to create pipe: ");
45
46 switch(pid = fork()) {
47 case -1:
48 strerr_die2sys(111,FATAL,"unable to fork: ");
49 case 0:
50 close(pi[1]);
51 if (fd_move(0,pi[0]) == -1)
52 strerr_die2sys(111,FATAL,"unable to set up fds: ");
53 sig_pipedefault();
54 execvp(argv[1],argv + 1);
55 strerr_die4sys(111,FATAL,"unable to run ",argv[1],": ");
56 }
57 close(pi[0]);
58 substdio_fdbuf(&ss,write,pi[1],outbuf,sizeof(outbuf));
59
60 time(&now);
61
62 tm = gmtime(&now);
63 dt.year = tm->tm_year;
64 dt.mon = tm->tm_mon;
65 dt.mday = tm->tm_mday;
66 dt.hour = tm->tm_hour;
67 dt.min = tm->tm_min;
68 dt.sec = tm->tm_sec;
69 utc = datetime_untai(&dt); /* utc == now, if gmtime ignores leap seconds */
70
71 tm = localtime(&now);
72 dt.year = tm->tm_year;
73 dt.mon = tm->tm_mon;
74 dt.mday = tm->tm_mday;
75 dt.hour = tm->tm_hour;
76 dt.min = tm->tm_min;
77 dt.sec = tm->tm_sec;
78 local = datetime_untai(&dt);
79
80 substdio_puts(&ss,"Date: ");
81 substdio_put(&ss,num,fmt_uint(num,dt.mday));
82 substdio_puts(&ss," ");
83 substdio_puts(&ss,montab[dt.mon]);
84 substdio_puts(&ss," ");
85 substdio_put(&ss,num,fmt_uint(num,dt.year + 1900));
86 substdio_puts(&ss," ");
87 substdio_put(&ss,num,fmt_uint0(num,dt.hour,2));
88 substdio_puts(&ss,":");
89 substdio_put(&ss,num,fmt_uint0(num,dt.min,2));
90 substdio_puts(&ss,":");
91 substdio_put(&ss,num,fmt_uint0(num,dt.sec,2));
92
93 if (local < utc) {
94 minutes = (utc - local + 30) / 60;
95 substdio_puts(&ss," -");
96 substdio_put(&ss,num,fmt_uint0(num,minutes / 60,2));
97 substdio_put(&ss,num,fmt_uint0(num,minutes % 60,2));
98 }
99 else {
100 minutes = (local - utc + 30) / 60;
101 substdio_puts(&ss," +");
102 substdio_put(&ss,num,fmt_uint0(num,minutes / 60,2));
103 substdio_put(&ss,num,fmt_uint0(num,minutes % 60,2));
104 }
105
106 substdio_puts(&ss,"\n");
107 substdio_copy(&ss,subfdin);
108 substdio_flush(&ss);
109 close(pi[1]);
110
111 if (wait_pid(&wstat,pid) == -1)
112 strerr_die2sys(111,FATAL,"wait failed: ");
113 if (wait_crashed(wstat))
114 strerr_die2x(111,FATAL,"child crashed");
115 _exit(wait_exitcode(wstat));
116}
diff --git a/preline.1 b/preline.1
new file mode 100644
index 0000000..69a757c
--- /dev/null
+++ b/preline.1
@@ -0,0 +1,57 @@
1.TH preline 1
2.SH NAME
3preline \- prepend lines to message
4.SH SYNOPSIS
5in
6.BR .qmail\fIext :
7.B | preline \fIcommand
8.SH DESCRIPTION
9.B preline
10feeds each incoming mail message through
11.IR command .
12At the top of each message it inserts
13a UUCP-style
14.B From_
15line, a
16.B Return-Path
17line, and a
18.B Delivered-To
19line.
20
21.B preline
22is useful for
23.B procmail
24and
25ELM's
26.BR filter ,
27which
28do not understand the
29.B qmail-command
30environment variables.
31.SH OPTIONS
32.TP
33.B \-d
34Do not include the
35.B Delivered-To
36line. You should use this option when the
37recipient of the incoming mail message is actually under remote control,
38but was sent here through
39.B control/virtualdomains
40for manual routing.
41.TP
42.B \-f
43Do not include the
44.B From_
45line. You should use this option except for
46.IR command s
47that create
48.I mbox
49files.
50.TP
51.B \-r
52Do not include the
53.B Return-Path
54line.
55.SH "SEE ALSO"
56mbox(5),
57qmail-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 @@
1#include "fd.h"
2#include "sgetopt.h"
3#include "readwrite.h"
4#include "strerr.h"
5#include "substdio.h"
6#include "exit.h"
7#include "fork.h"
8#include "wait.h"
9#include "env.h"
10#include "sig.h"
11#include "error.h"
12
13#define FATAL "preline: fatal: "
14
15void die_usage()
16{
17 strerr_die1x(100,"preline: usage: preline cmd [ arg ... ]");
18}
19
20int flagufline = 1; char *ufline;
21int flagrpline = 1; char *rpline;
22int flagdtline = 1; char *dtline;
23
24char outbuf[SUBSTDIO_OUTSIZE];
25char inbuf[SUBSTDIO_INSIZE];
26substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof outbuf);
27substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
28
29void main(argc,argv)
30int argc;
31char **argv;
32{
33 int opt;
34 int pi[2];
35 int pid;
36 int wstat;
37
38 sig_pipeignore();
39
40 if (!(ufline = env_get("UFLINE"))) die_usage();
41 if (!(rpline = env_get("RPLINE"))) die_usage();
42 if (!(dtline = env_get("DTLINE"))) die_usage();
43
44 while ((opt = getopt(argc,argv,"frdFRD")) != opteof)
45 switch(opt) {
46 case 'f': flagufline = 0; break;
47 case 'r': flagrpline = 0; break;
48 case 'd': flagdtline = 0; break;
49 case 'F': flagufline = 1; break;
50 case 'R': flagrpline = 1; break;
51 case 'D': flagdtline = 1; break;
52 default: die_usage();
53 }
54 argc -= optind;
55 argv += optind;
56 if (!*argv) die_usage();
57
58 if (pipe(pi) == -1)
59 strerr_die2sys(111,FATAL,"unable to create pipe: ");
60
61 pid = fork();
62 if (pid == -1)
63 strerr_die2sys(111,FATAL,"unable to fork: ");
64
65 if (pid == 0) {
66 close(pi[1]);
67 if (fd_move(0,pi[0]) == -1)
68 strerr_die2sys(111,FATAL,"unable to set up fds: ");
69 sig_pipedefault();
70 execvp(*argv,argv);
71 strerr_die4sys(error_temp(errno) ? 111 : 100,FATAL,"unable to run ",*argv,": ");
72 }
73 close(pi[0]);
74 if (fd_move(1,pi[1]) == -1)
75 strerr_die2sys(111,FATAL,"unable to set up fds: ");
76
77 if (flagufline) substdio_bputs(&ssout,ufline);
78 if (flagrpline) substdio_bputs(&ssout,rpline);
79 if (flagdtline) substdio_bputs(&ssout,dtline);
80 if (substdio_copy(&ssout,&ssin) != 0)
81 strerr_die2sys(111,FATAL,"unable to copy input: ");
82 substdio_flush(&ssout);
83 close(1);
84
85 if (wait_pid(&wstat,pid) == -1)
86 strerr_die2sys(111,FATAL,"wait failed: ");
87 if (wait_crashed(wstat))
88 strerr_die2x(111,FATAL,"child crashed");
89 _exit(wait_exitcode(wstat));
90}
diff --git a/prioq.c b/prioq.c
new file mode 100644
index 0000000..051aa45
--- /dev/null
+++ b/prioq.c
@@ -0,0 +1,58 @@
1#include "alloc.h"
2#include "gen_allocdefs.h"
3#include "prioq.h"
4
5GEN_ALLOC_readyplus(prioq,struct prioq_elt,p,len,a,i,n,x,100,prioq_readyplus)
6
7int prioq_insert(pq,pe)
8prioq *pq;
9struct prioq_elt *pe;
10{
11 int i;
12 int j;
13 if (!prioq_readyplus(pq,1)) return 0;
14 j = pq->len++;
15 while (j)
16 {
17 i = (j - 1)/2;
18 if (pq->p[i].dt <= pe->dt) break;
19 pq->p[j] = pq->p[i];
20 j = i;
21 }
22 pq->p[j] = *pe;
23 return 1;
24}
25
26int prioq_min(pq,pe)
27prioq *pq;
28struct prioq_elt *pe;
29{
30 if (!pq->p) return 0;
31 if (!pq->len) return 0;
32 *pe = pq->p[0];
33 return 1;
34}
35
36void prioq_delmin(pq)
37prioq *pq;
38{
39 int i;
40 int j;
41 int n;
42 if (!pq->p) return;
43 n = pq->len;
44 if (!n) return;
45 i = 0;
46 --n;
47 for (;;)
48 {
49 j = i + i + 2;
50 if (j > n) break;
51 if (pq->p[j - 1].dt <= pq->p[j].dt) --j;
52 if (pq->p[n].dt <= pq->p[j].dt) break;
53 pq->p[i] = pq->p[j];
54 i = j;
55 }
56 pq->p[i] = pq->p[n];
57 pq->len = n;
58}
diff --git a/prioq.h b/prioq.h
new file mode 100644
index 0000000..32a0d7c
--- /dev/null
+++ b/prioq.h
@@ -0,0 +1,15 @@
1#ifndef PRIOQ_H
2#define PRIOQ_H
3
4#include "datetime.h"
5#include "gen_alloc.h"
6
7struct prioq_elt { datetime_sec dt; unsigned long id; } ;
8
9GEN_ALLOC_typedef(prioq,struct prioq_elt,p,len,a)
10
11extern int prioq_insert();
12extern int prioq_min();
13extern void prioq_delmin();
14
15#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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using dot-forward to support sendmail-style ~/.forward files.
5# Using procmail to deliver messages to /var/spool/mail/$USER by default.
6
7exec env - PATH="QMAIL/bin:$PATH" \
8qmail-start '|dot-forward .forward
9|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 @@
1#!/bin/sh
2
3# Using splogger to send the log through syslog.
4# Using procmail to deliver messages to /var/spool/mail/$USER by default.
5
6exec env - PATH="QMAIL/bin:$PATH" \
7qmail-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 @@
1#include "hasshsgr.h"
2#include "prot.h"
3
4/* XXX: there are more portability problems here waiting to leap out at me */
5
6int prot_gid(gid) int gid;
7{
8#ifdef HASSHORTSETGROUPS
9 short x[2];
10 x[0] = gid; x[1] = 73; /* catch errors */
11 if (setgroups(1,x) == -1) return -1;
12#else
13 if (setgroups(1,&gid) == -1) return -1;
14#endif
15 return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
16}
17
18int prot_uid(uid) int uid;
19{
20 return setuid(uid);
21}
diff --git a/prot.h b/prot.h
new file mode 100644
index 0000000..8c5d81a
--- /dev/null
+++ b/prot.h
@@ -0,0 +1,7 @@
1#ifndef PROT_H
2#define PROT_H
3
4extern int prot_gid();
5extern int prot_uid();
6
7#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 @@
1.TH qbiff 1
2.SH NAME
3qbiff \- announce new mail the moment it arrives
4.SH SYNOPSIS
5in
6.BR .qmail :
7.B |qbiff
8.SH DESCRIPTION
9.B qbiff
10writes a message to your screen
11whenever a new mail message is delivered,
12if you ran
13.B biff y
14after logging in.
15
16.B WARNING:
17If you create a
18.B .qmail
19file to enable
20.BR qbiff ,
21make sure to also add a line specifying delivery to your normal mailbox.
22For example:
23
24.EX
25 /home/joe/Mailbox
26.br
27 |qbiff
28.EE
29.SH "SEE ALSO"
30biff(1),
31dot-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <utmp.h>
4#ifndef UTMP_FILE
5#ifdef _PATH_UTMP
6#define UTMP_FILE _PATH_UTMP
7#else
8#define UTMP_FILE "/etc/utmp"
9#endif
10#endif
11#include "readwrite.h"
12#include "stralloc.h"
13#include "substdio.h"
14#include "subfd.h"
15#include "open.h"
16#include "byte.h"
17#include "str.h"
18#include "headerbody.h"
19#include "hfield.h"
20#include "env.h"
21#include "exit.h"
22
23substdio ssutmp;
24char bufutmp[sizeof(struct utmp) * 16];
25int fdutmp;
26substdio sstty;
27char buftty[1024];
28int fdtty;
29
30struct utmp ut;
31char line[sizeof(ut.ut_line) + 1];
32stralloc woof = {0};
33stralloc tofrom = {0};
34stralloc text = {0};
35
36void doit(s,n) char *s; int n;
37{
38 if (!stralloc_catb(&text,s,n)) _exit(0);
39 if (text.len > 78) text.len = 78;
40}
41void dobody(h) stralloc *h; { doit(h->s,h->len); }
42void doheader(h) stralloc *h;
43{
44 int i;
45 if (hfield_known(h->s,h->len) == H_SUBJECT)
46 {
47 i = hfield_skipname(h->s,h->len);
48 doit(h->s + i,h->len - i);
49 }
50}
51void finishheader() { ; }
52
53void main()
54{
55 char *user;
56 char *sender;
57 char *userext;
58 struct stat st;
59 int i;
60
61 if (chdir("/dev") == -1) _exit(0);
62
63 if (!(user = env_get("USER"))) _exit(0);
64 if (!(sender = env_get("SENDER"))) _exit(0);
65 if (!(userext = env_get("LOCAL"))) _exit(0);
66 if (str_len(user) > sizeof(ut.ut_name)) _exit(0);
67
68 if (!stralloc_copys(&tofrom,"*** TO <")) _exit(0);
69 if (!stralloc_cats(&tofrom,userext)) _exit(0);
70 if (!stralloc_cats(&tofrom,"> FROM <")) _exit(0);
71 if (!stralloc_cats(&tofrom,sender)) _exit(0);
72 if (!stralloc_cats(&tofrom,">")) _exit(0);
73
74 for (i = 0;i < tofrom.len;++i)
75 if ((tofrom.s[i] < 32) || (tofrom.s[i] > 126))
76 tofrom.s[i] = '_';
77
78 if (!stralloc_copys(&text," ")) _exit(0);
79 if (headerbody(subfdin,doheader,finishheader,dobody) == -1) _exit(0);
80
81 for (i = 0;i < text.len;++i)
82 if ((text.s[i] < 32) || (text.s[i] > 126))
83 text.s[i] = '/';
84
85 if (!stralloc_copys(&woof,"\015\n\007")) _exit(0);
86 if (!stralloc_cat(&woof,&tofrom)) _exit(0);
87 if (!stralloc_cats(&woof,"\015\n")) _exit(0);
88 if (!stralloc_cat(&woof,&text)) _exit(0);
89 if (!stralloc_cats(&woof,"\015\n")) _exit(0);
90
91 fdutmp = open_read(UTMP_FILE);
92 if (fdutmp == -1) _exit(0);
93 substdio_fdbuf(&ssutmp,read,fdutmp,bufutmp,sizeof(bufutmp));
94
95 while (substdio_get(&ssutmp,&ut,sizeof(ut)) == sizeof(ut))
96 if (!str_diffn(ut.ut_name,user,sizeof(ut.ut_name)))
97 {
98 byte_copy(line,sizeof(ut.ut_line),ut.ut_line);
99 line[sizeof(ut.ut_line)] = 0;
100 if (line[0] == '/') continue;
101 if (!line[0]) continue;
102 if (line[str_chr(line,'.')]) continue;
103 fdtty = open_append(line);
104 if (fdtty == -1) continue;
105 if (fstat(fdtty,&st) == -1) { close(fdtty); continue; }
106 if (!(st.st_mode & 0100)) { close(fdtty); continue; }
107 if (st.st_uid != getuid()) { close(fdtty); continue; }
108 substdio_fdbuf(&sstty,write,fdtty,buftty,sizeof(buftty));
109 substdio_putflush(&sstty,woof.s,woof.len);
110 close(fdtty);
111 }
112 _exit(0);
113}
diff --git a/qlx.h b/qlx.h
new file mode 100644
index 0000000..713946d
--- /dev/null
+++ b/qlx.h
@@ -0,0 +1,18 @@
1#ifndef QLX_H
2#define QLX_H
3
4/* 0, 111, 100 are qmail-local success, soft, hard */
5
6#define QLX_USAGE 112
7#define QLX_BUG 101
8#define QLX_ROOT 113
9#define QLX_NFS 115
10#define QLX_NOALIAS 116
11#define QLX_CDB 117
12#define QLX_SYS 118
13#define QLX_NOMEM 119
14#define QLX_EXECSOFT 120
15#define QLX_EXECPW 121
16#define QLX_EXECHARD 126
17
18#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 @@
1.TH qmail-clean 8
2.SH NAME
3qmail-clean \- clean up the queue directory
4.SH SYNOPSIS
5.B qmail-clean
6.SH DESCRIPTION
7.B qmail-clean
8reads a cleanup command from descriptor 0,
9performs the cleanup,
10prints the results to descriptor 1,
11and repeats.
12.SH "SEE ALSO"
13qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "readwrite.h"
4#include "sig.h"
5#include "now.h"
6#include "str.h"
7#include "direntry.h"
8#include "getln.h"
9#include "stralloc.h"
10#include "substdio.h"
11#include "subfd.h"
12#include "byte.h"
13#include "scan.h"
14#include "fmt.h"
15#include "error.h"
16#include "exit.h"
17#include "fmtqfn.h"
18#include "auto_qmail.h"
19
20#define OSSIFIED 129600 /* see qmail-send.c */
21
22stralloc line = {0};
23
24void cleanuppid()
25{
26 DIR *dir;
27 direntry *d;
28 struct stat st;
29 datetime_sec time;
30
31 time = now();
32 dir = opendir("pid");
33 if (!dir) return;
34 while (d = readdir(dir))
35 {
36 if (str_equal(d->d_name,".")) continue;
37 if (str_equal(d->d_name,"..")) continue;
38 if (!stralloc_copys(&line,"pid/")) continue;
39 if (!stralloc_cats(&line,d->d_name)) continue;
40 if (!stralloc_0(&line)) continue;
41 if (stat(line.s,&st) == -1) continue;
42 if (time < st.st_atime + OSSIFIED) continue;
43 unlink(line.s);
44 }
45 closedir(dir);
46}
47
48char fnbuf[FMTQFN];
49
50void respond(s) char *s; { if (substdio_putflush(subfdoutsmall,s,1) == -1) _exit(100); }
51
52void main()
53{
54 int i;
55 int match;
56 int cleanuploop;
57 unsigned long id;
58
59 if (chdir(auto_qmail) == -1) _exit(111);
60 if (chdir("queue") == -1) _exit(111);
61
62 sig_pipeignore();
63
64 if (!stralloc_ready(&line,200)) _exit(111);
65
66 cleanuploop = 0;
67
68 for (;;)
69 {
70 if (cleanuploop) --cleanuploop; else { cleanuppid(); cleanuploop = 30; }
71 if (getln(subfdinsmall,&line,&match,'\0') == -1) break;
72 if (!match) break;
73 if (line.len < 7) { respond("x"); continue; }
74 if (line.len > 100) { respond("x"); continue; }
75 if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */
76 for (i = 5;i < line.len - 1;++i)
77 if ((unsigned char) (line.s[i] - '0') > 9)
78 { respond("x"); continue; }
79 if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; }
80 if (byte_equal(line.s,5,"foop/"))
81 {
82#define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \
83if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; }
84 U("intd/",0)
85 U("mess/",1)
86 respond("+");
87 }
88 else if (byte_equal(line.s,4,"todo/"))
89 {
90 U("intd/",0)
91 U("todo/",0)
92 respond("+");
93 }
94 else
95 respond("x");
96 }
97 _exit(0);
98}
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 @@
1.TH qmail-command 8
2.SH NAME
3qmail-command \- user-specified mail delivery program
4.SH SYNOPSIS
5in
6.BR .qmail\fIext :
7.B |\fIcommand
8.SH DESCRIPTION
9.B qmail-local
10will, upon your request,
11feed each incoming mail message through a program of your choice.
12
13When a mail message arrives,
14.B qmail-local
15runs
16.B sh -c \fIcommand
17in your home directory.
18It makes the message available on
19.IR command 's
20standard input.
21
22.B WARNING:
23The mail message does not begin with
24.BR qmail-local 's
25usual
26.B Return-Path
27and
28.B Delivered-To
29lines.
30
31Note that
32.B qmail-local
33uses the same file descriptor for every delivery
34in your
35.B .qmail
36file, so it is not safe for
37.I command
38to fork a child that
39reads the message in the background while the parent exits.
40.SH "EXIT CODES"
41.IR command 's
42exit codes are interpreted as follows:
430 means that the delivery was successful;
4499 means that the delivery was successful,
45but that
46.B qmail-local
47should ignore all further delivery instructions;
48100 means that the delivery failed permanently (hard error);
49111 means that the delivery failed but should be tried again
50in a little while (soft error).
51
52Currently 64, 65, 70, 76, 77, 78, and 112 are considered hard errors,
53and all other codes are considered soft errors,
54but
55.I command
56should avoid relying on this.
57.SH "ENVIRONMENT VARIABLES"
58.B qmail-local
59supplies several useful environment variables to
60.IR command .
61.B WARNING:
62These environment variables are not quoted.
63They may contain special characters.
64They are under the control of a possibly malicious remote user.
65
66.B SENDER
67is the envelope sender address.
68.B NEWSENDER
69is the forwarding envelope sender address,
70as described in
71.BR dot-qmail(5) .
72.B RECIPIENT
73is the envelope recipient address,
74.IR local@domain .
75.B USER
76is
77.IR user .
78.B HOME
79is your home directory,
80.IR homedir .
81.B HOST
82is the
83.I domain
84part of the recipient address.
85.B LOCAL
86is the
87.I local
88part.
89.B EXT
90is the
91address extension,
92.IR ext .
93
94.B HOST2
95is the portion of
96.B HOST
97preceding the last dot;
98.B HOST3
99is the portion of
100.B HOST
101preceding the second-to-last dot;
102.B HOST4
103is the portion of
104.B HOST
105preceding the third-to-last dot.
106
107.B EXT2
108is the portion of
109.B EXT
110following the first dash;
111.B EXT3
112is the portion
113following the second dash;
114.B EXT4
115is the portion
116following the third dash.
117.B DEFAULT
118is the portion
119corresponding to the
120.B default
121part of the
122.BR .qmail\- ...
123file name;
124.B DEFAULT
125is not set if
126the file name does not end with
127.BR default .
128
129.B DTLINE
130and
131.B RPLINE
132are the usual
133.B Delivered-To
134and
135.B Return-Path
136lines,
137including newlines.
138.B UFLINE
139is the UUCP-style
140.B From_
141line that
142.B qmail-local
143adds to
144.IR mbox -format
145files.
146.SH "SEE ALSO"
147dot-qmail(5),
148envelopes(5),
149qmail-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 @@
1.TH qmail-control 5
2.SH "NAME"
3qmail-control \- qmail configuration files
4.SH "INTRODUCTION"
5You can change the behavior of the
6.B qmail
7system by modifying
8.BR qmail 's
9.I control files
10in
11.BR QMAILHOME/control .
12
13.B qmail
14can survive with just one control file,
15.IR me ,
16containing the
17fully-qualified name of the current host.
18This file is used as the default for
19other hostname-related control files.
20
21Comments are allowed
22in
23.IR badmailfrom ,
24.IR locals ,
25.IR percenthack ,
26.IR qmqpservers ,
27.IR rcpthosts ,
28.IR smtproutes ,
29and
30.IR virtualdomains .
31Trailing spaces and tabs are allowed in any control file.
32
33The following table lists all control files
34other than
35.IR me .
36See the corresponding man pages for further details.
37
38.RS
39.nf
40.ta 5c 10c
41control default used by
42
43.I badmailfrom \fR(none) \fRqmail-smtpd
44.I bouncefrom \fRMAILER-DAEMON \fRqmail-send
45.I bouncehost \fIme \fRqmail-send
46.I concurrencylocal \fR10 \fRqmail-send
47.I concurrencyremote \fR20 \fRqmail-send
48.I defaultdomain \fIme \fRqmail-inject
49.I defaulthost \fIme \fRqmail-inject
50.I databytes \fR0 \fRqmail-smtpd
51.I doublebouncehost \fIme \fRqmail-send
52.I doublebounceto \fRpostmaster \fRqmail-send
53.I envnoathost \fIme \fRqmail-send
54.I helohost \fIme \fRqmail-remote
55.I idhost \fIme \fRqmail-inject
56.I localiphost \fIme \fRqmail-smtpd
57.I locals \fIme \fRqmail-send
58.I morercpthosts \fR(none) \fRqmail-smtpd
59.I percenthack \fR(none) \fRqmail-send
60.I plusdomain \fIme \fRqmail-inject
61.I qmqpservers \fR(none) \fRqmail-qmqpc
62.I queuelifetime \fR604800 \fRqmail-send
63.I rcpthosts \fR(none) \fRqmail-smtpd
64.I smtpgreeting \fIme \fRqmail-smtpd
65.I smtproutes \fR(none) \fRqmail-remote
66.I timeoutconnect \fR60 \fRqmail-remote
67.I timeoutremote \fR1200 \fRqmail-remote
68.I timeoutsmtpd \fR1200 \fRqmail-smtpd
69.I virtualdomains \fR(none) \fRqmail-send
70.fi
71.RE
72.SH "SEE ALSO"
73qmail-inject(8),
74qmail-qmqpc(8),
75qmail-remote(8),
76qmail-send(8),
77qmail-showctl(8),
78qmail-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 @@
1.TH qmail-getpw 8
2.SH NAME
3qmail-getpw \- give addresses to users
4.SH SYNOPSIS
5.B qmail-getpw
6.I local
7.SH DESCRIPTION
8In
9.BR qmail ,
10each user controls a vast array of local addresses.
11.B qmail-getpw
12finds the user that controls a particular address,
13.IR local .
14It prints six pieces of information,
15each terminated by NUL:
16.IR user ;
17.IR uid ;
18.IR gid ;
19.IR homedir ;
20.IR dash ;
21and
22.IR ext .
23The user's account name is
24.IR user ;
25the user's uid and gid in decimal are
26.I uid
27and
28.IR gid ;
29the user's home directory is
30.IR homedir ;
31and messages to
32.I local
33will be handled by
34.IR homedir\fB/.qmail\fIdashext .
35
36In case of trouble,
37.B qmail-getpw
38exits nonzero without printing anything.
39
40.B WARNING:
41The operating system's
42.B getpwnam
43function, which is at the heart of
44.BR qmail-getpw ,
45is inherently unreliable:
46it fails to distinguish between temporary errors and nonexistent users.
47Future versions of
48.B getpwnam
49should return ETXTBSY to indicate temporary errors
50and ESRCH to indicate nonexistent users.
51.SH "RULES"
52.B qmail-getpw
53considers an account in
54.B /etc/passwd
55to be a user if
56(1) the account has a nonzero uid,
57(2) the account's home directory exists (and is visible to
58.BR qmail-getpw ),
59and
60(3) the account owns its home directory.
61.B qmail-getpw
62ignores account names containing uppercase letters.
63.B qmail-getpw
64also assumes that all account names are shorter than 32 characters.
65
66.B qmail-getpw
67gives each user
68control over the basic
69.I user
70address and
71all addresses of the form
72.IR user\fBBREAK\fIanything .
73When
74.I local
75is
76.IR user ,
77.I dash
78and
79.I ext
80are both empty.
81When
82.I local
83is
84.IR user\fBBREAK\fIanything ,
85.I dash
86is a hyphen and
87.I ext
88is
89.IR anything .
90.I user
91may appear in any combination of uppercase and lowercase letters
92at the front of
93.IR local .
94
95A catch-all user,
96.BR alias ,
97controls all other addresses.
98In this case
99.I ext
100is
101.I local
102and
103.I dash
104is a hyphen.
105
106You can override all of
107.BR qmail-getpw 's
108decisions with the
109.B qmail-users
110mechanism, which is reliable, highly configurable, and much faster than
111.BR qmail-getpw .
112.SH "SEE ALSO"
113qmail-users(5),
114qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <pwd.h>
4#include "readwrite.h"
5#include "substdio.h"
6#include "subfd.h"
7#include "error.h"
8#include "exit.h"
9#include "byte.h"
10#include "str.h"
11#include "case.h"
12#include "fmt.h"
13#include "auto_usera.h"
14#include "auto_break.h"
15#include "qlx.h"
16
17#define GETPW_USERLEN 32
18
19char *local;
20struct passwd *pw;
21char *dash;
22char *extension;
23
24int userext()
25{
26 char username[GETPW_USERLEN];
27 struct stat st;
28
29 extension = local + str_len(local);
30 for (;;) {
31 if (extension - local < sizeof(username))
32 if (!*extension || (*extension == *auto_break)) {
33 byte_copy(username,extension - local,local);
34 username[extension - local] = 0;
35 case_lowers(username);
36 errno = 0;
37 pw = getpwnam(username);
38 if (errno == error_txtbsy) _exit(QLX_SYS);
39 if (pw)
40 if (pw->pw_uid)
41 if (stat(pw->pw_dir,&st) == 0) {
42 if (st.st_uid == pw->pw_uid) {
43 dash = "";
44 if (*extension) { ++extension; dash = "-"; }
45 return 1;
46 }
47 }
48 else
49 if (error_temp(errno)) _exit(QLX_NFS);
50 }
51 if (extension == local) return 0;
52 --extension;
53 }
54}
55
56char num[FMT_ULONG];
57
58void main(argc,argv)
59int argc;
60char **argv;
61{
62 local = argv[1];
63 if (!local) _exit(100);
64
65 if (!userext()) {
66 extension = local;
67 dash = "-";
68 pw = getpwnam(auto_usera);
69 }
70
71 if (!pw) _exit(QLX_NOALIAS);
72
73 substdio_puts(subfdoutsmall,pw->pw_name);
74 substdio_put(subfdoutsmall,"",1);
75 substdio_put(subfdoutsmall,num,fmt_ulong(num,(long) pw->pw_uid));
76 substdio_put(subfdoutsmall,"",1);
77 substdio_put(subfdoutsmall,num,fmt_ulong(num,(long) pw->pw_gid));
78 substdio_put(subfdoutsmall,"",1);
79 substdio_puts(subfdoutsmall,pw->pw_dir);
80 substdio_put(subfdoutsmall,"",1);
81 substdio_puts(subfdoutsmall,dash);
82 substdio_put(subfdoutsmall,"",1);
83 substdio_puts(subfdoutsmall,extension);
84 substdio_put(subfdoutsmall,"",1);
85 substdio_flush(subfdoutsmall);
86
87 _exit(0);
88}
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 @@
1.TH qmail-header 5
2.SH NAME
3qmail-header \- format of a mail message
4.SH OVERVIEW
5At the top of every mail message is a
6highly structured
7.BR header .
8Many programs expect the header to carry certain information,
9as described below.
10The main function of
11.B qmail-inject
12is to make sure that each outgoing message has an appropriate header.
13
14For more detailed information, see
15.BR http://pobox.com/~djb/proto/immhf.html .
16.SH "MESSAGE STRUCTURE"
17A message contains a series of
18.I header fields\fR,
19a blank line,
20and a
21.IR body :
22
23.EX
24 Received: (qmail-queue invoked by uid 666);
25.br
26 30 Jul 1996 11:54:54 -0000
27.br
28 From: djb@silverton.berkeley.edu (D. J. Bernstein)
29.br
30 To: fred@silverton.berkeley.edu
31.br
32 Date: 30 Jul 1996 11:54:54 -0000
33.br
34 Subject: Go, Bears!
35.br
36
37.br
38 I've got money on this one. How about you?
39.br
40
41.br
42 ---Dan (this is the third line of the body)
43.EE
44
45Each header field has a
46.IR name ,
47a colon,
48some
49.IR contents ,
50and a newline:
51
52.EX
53 Subject: Go, Bears!
54.EE
55
56The field contents may be folded across several lines.
57Each line past the first must begin with a space or tab:
58
59.EX
60 Received: (qmail-queue invoked by uid 666);
61.br
62 30 Jul 1996 11:54:54 -0000
63.EE
64
65The field name must not contain spaces, tabs, or colons.
66Also, an empty field name is illegal.
67.B qmail-inject
68does not allow field names with unprintable characters.
69
70Case is irrelevant in field names:
71.B subject
72and
73.B SUBJECT
74and
75.B SuBjEcT
76have the same meaning.
77.SH "ADDRESS LISTS"
78Certain fields, such as
79.BR To ,
80contain
81.I address lists\fR.
82
83An address list contains some number of
84.I addresses
85or
86.I address groups\fR,
87separated by commas:
88
89.EX
90 a@b, c@d (Somebody), A Person <e@f>,
91.br
92 random group: g@h, i@j;, k@l
93.EE
94
95An
96.I address group
97has some text, a colon, a list of addresses,
98and a semicolon:
99
100.EX
101 random group: g@h, i@j;
102.EE
103
104An address can appear in several forms.
105The most common form is
106.IR box@host .
107
108Every address must include a host name.
109If
110.B qmail-inject
111sees a lone box name
112it adds the
113.I default host name\fR.
114
115All host names should be fully qualified.
116.B qmail-inject
117appends the
118.I default domain name
119to any name without dots:
120
121.EX
122 djb@silverton -> djb@silverton.berkeley.edu
123.EE
124
125It appends the
126.I plus domain name
127to any name
128that ends with a plus sign:
129
130.EX
131 eric@mammoth.cs+ -> eric@mammoth.cs.berkeley.edu
132.EE
133
134A host name may be a dotted-decimal address:
135
136.EX
137 djb@[128.32.183.163]
138.EE
139
140RFC 822 allows mailbox names inside angle brackets
141to include
142.I source routes\fR,
143but
144.B qmail-inject
145strips all source routes out of addresses.
146.SH "SENDER ADDRESSES"
147.B qmail-inject
148looks for sender address lists in the following fields:
149.BR Sender ,
150.BR From ,
151.BR Reply-To ,
152.BR Return-Path ,
153.BR Return-Receipt-To ,
154.BR Errors-To ,
155.BR Resent-Sender ,
156.BR Resent-From ,
157.BR Resent-Reply-To .
158
159If there is no
160.B From
161field,
162.B qmail-inject
163adds a new
164.B From
165field with the name of the user invoking
166.B qmail-inject.
167
168RFC 822 requires that certain sender fields contain
169only a single address, but
170.B qmail-inject
171does not enforce this restriction.
172.SH "RECIPIENT ADDRESSES"
173.B qmail-inject
174looks for recipient address lists in the following fields:
175.BR To ,
176.BR Cc ,
177.BR Bcc ,
178.BR Apparently-To ,
179.BR Resent-To ,
180.BR Resent-Cc ,
181.BR Resent-Bcc .
182
183Every message must contain at least one
184.B To
185or
186.B Cc
187or
188.BR Bcc .
189.B qmail-inject
190deletes any
191.B Bcc
192field.
193If there is no
194.B To
195or
196.B Cc
197field,
198.B qmail-inject
199adds a line
200
201.EX
202 Cc: recipient list not shown: ;
203.EE
204
205This complies with RFC 822;
206it also works around some strange
207.B sendmail
208behavior, in case the message is passed through
209.B sendmail
210on another machine.
211.SH STAMPS
212Every message must contain a
213.B Date
214field, with the date in a strict format defined by RFC 822.
215If necessary
216.B qmail-inject
217creates a new
218.B Date
219field with the current date (in GMT).
220
221Every message should contain a
222.B Message-Id
223field.
224The field contents are a unique worldwide identifier for this message.
225If necessary
226.B qmail-inject
227creates a new
228.B Message-Id
229field.
230
231Another important field is
232.BR Received .
233Every time the message is sent from one system to another,
234a new
235.B Received
236field is added to the top of the message.
237.B qmail-inject
238does not create any
239.B Received
240fields.
241.SH "RESENT MESSAGES"
242A message is
243.I resent
244if it contains any of the following fields:
245.BR Resent-Sender ,
246.BR Resent-From ,
247.BR Resent-Reply-To ,
248.BR Resent-To ,
249.BR Resent-Cc ,
250.BR Resent-Bcc ,
251.BR Resent-Date ,
252.BR Resent-Message-ID .
253
254If a message is resent,
255.B qmail-inject
256changes its behavior as follows.
257
258It deletes any
259.B Resent-Bcc
260field (as well as any
261.B Bcc
262field);
263if there are no
264.B Resent-To
265or
266.B Resent-Cc
267fields,
268.B qmail-inject
269adds an appropriate
270.B Resent-Cc
271line.
272It does
273.I not
274add a
275.B Cc
276line,
277even if neither
278.B To
279nor
280.B Cc
281is present.
282
283If there is no
284.B Resent-From
285field,
286.B qmail-inject
287adds a new
288.B Resent-From
289field.
290It does
291.I not
292add a new
293.B From
294field.
295
296.B qmail-inject
297adds
298.B Resent-Date
299if one is not already present;
300same for
301.BR Resent-Message-Id .
302It does
303.I not
304add new
305.B Date
306or
307.B Message-Id
308fields.
309.SH "OTHER FEATURES"
310Addresses are separated by commas, not spaces.
311When
312.B qmail-inject
313sees an illegal space,
314it inserts a comma:
315
316.EX
317 djb fred -> djb, fred
318.EE
319
320.B qmail-inject
321removes all
322.B Return-Path
323header fields.
324
325.B qmail-inject
326also removes any
327.B Content-Length
328fields.
329.SH "SEE ALSO"
330addresses(5),
331envelopes(5),
332qmail-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 @@
1.TH qmail-inject 8
2.SH NAME
3qmail-inject \- preprocess and send a mail message
4.SH SYNOPSIS
5.B qmail-inject
6[
7.B \-nNaAhH
8] [
9.B \-f\fIsender
10] [
11.I recip ...
12]
13.SH DESCRIPTION
14.B qmail-inject
15reads a mail message from its standard input,
16adds appropriate information to the message header,
17and invokes
18.B qmail-queue
19to send the message
20to one or more recipients.
21
22See
23.B qmail-header(5)
24for information on how
25.B qmail-inject
26rewrites header fields.
27
28.B qmail-inject
29normally exits 0.
30It exits 100 if it was invoked improperly
31or if there is a severe syntax error in the message.
32It exits 111 for temporary errors.
33.SH "ENVIRONMENT VARIABLES"
34For the convenience of users who do not run
35.B qmail-inject
36directly,
37.B qmail-inject
38takes many options through environment variables.
39
40The user name in the
41.B From
42header field is set by
43.BR QMAILUSER ,
44.BR MAILUSER ,
45.BR USER ,
46or
47.BR LOGNAME ,
48whichever comes first.
49
50The host name is normally set by the
51.I defaulthost
52control
53but can be overridden with
54.B QMAILHOST
55or
56.BR MAILHOST .
57
58The personal name is
59.BR QMAILNAME ,
60.BR MAILNAME ,
61or
62.BR NAME .
63
64The default envelope sender address is the same as the
65default
66.B From
67address,
68but it can be overridden with
69.B QMAILSUSER
70and
71.BR QMAILSHOST .
72It may also be modified by the
73.B r
74and
75.B m
76letters described below.
77Bounces will be sent to this address.
78
79If
80.B QMAILMFTFILE
81is set,
82.B qmail-inject
83reads a list of mailing list addresses,
84one per line,
85from that file.
86If To+Cc includes one of those addresses (without regard to case),
87.B qmail-inject
88adds a Mail-Followup-To field
89with all the To+Cc addresses.
90.B qmail-inject
91does not add Mail-Followup-To
92to a message that already has one.
93
94The
95.B QMAILINJECT
96environment variable
97can contain any of the following letters:
98.TP
99.B c
100Use address-comment style for the
101.B From
102field.
103Normally
104.B qmail-inject
105uses name-address style.
106.TP
107.B s
108Do not look at any incoming
109.B Return-Path
110field.
111Normally, if
112.B Return-Path
113is supplied, it sets the envelope sender address,
114overriding all environment variables.
115.B Return-Path
116is deleted in any case.
117.TP
118.B f
119Delete any incoming
120.B From
121field.
122Normally, if
123.B From
124is supplied, it overrides the usual
125.B From
126field created by
127.BR qmail-inject .
128.TP
129.B i
130Delete any incoming
131.B Message-ID
132field.
133Normally, if
134.B Message-ID
135is supplied, it overrides the usual
136.B Message-ID
137field created by
138.BR qmail-inject .
139.TP
140.B r
141Use a per-recipient VERP.
142.B qmail-inject
143will append each recipient address to the envelope sender
144of the copy going to that recipient.
145.TP
146.B m
147Use a per-message VERP.
148.B qmail-inject
149will append the current date and process ID to the envelope sender.
150.SH OPTIONS
151.TP
152.B \-a
153Send the message to all addresses given as
154.I recip
155arguments;
156do not use header recipient addresses.
157.TP
158.B \-h
159Send the message to all header recipient addresses.
160For non-forwarded messages, this means
161the addresses listed under
162.BR To ,
163.BR Cc ,
164.BR Bcc ,
165.BR Apparently-To .
166For forwarded messages, this means
167the addresses listed under
168.BR Resent-To ,
169.BR Resent-Cc ,
170.BR Resent-Bcc .
171Do not use any
172.I recip
173arguments.
174.TP
175.B \-A
176(Default.)
177Send the message to all addresses given as
178.I recip
179arguments.
180If no
181.I recip
182arguments are supplied,
183send the message to all header recipient addresses.
184.TP
185.B \-H
186Send the message to all header recipient addresses,
187and to all addresses given as
188.I recip
189arguments.
190.TP
191.B \-f\fIsender
192Pass
193.I sender
194to
195.B qmail-queue
196as the envelope sender address.
197This overrides
198.B Return-Path
199and all environment variables.
200.TP
201.B \-N
202(Default.)
203Feed the resulting message to
204.BR qmail-queue .
205.TP
206.B \-n
207Print the message rather than feeding it to
208.BR qmail-queue .
209.SH "CONTROL FILES"
210.TP 5
211.I defaultdomain
212Default domain name.
213Default:
214.IR me ,
215if that is supplied;
216otherwise the literal name
217.BR defaultdomain ,
218which is probably not what you want.
219.B qmail-inject
220adds this name to any host name without dots,
221including
222.I defaulthost
223if
224.I defaulthost
225does not have dots.
226(Exception: see
227.IR plusdomain .)
228
229The
230.B QMAILDEFAULTDOMAIN
231environment variable
232overrides
233.IR defaultdomain .
234.TP 5
235.I defaulthost
236Default host name.
237Default:
238.IR me ,
239if that is supplied;
240otherwise the literal name
241.BR defaulthost ,
242which is probably not what you want.
243.B qmail-inject
244adds this name to any address without a host name.
245.I defaulthost
246need not be the current host's name.
247For example,
248you may prefer that outgoing mail show
249just your domain name.
250
251The
252.B QMAILDEFAULTHOST
253environment variable overrides
254.IR defaulthost .
255.TP 5
256.I idhost
257Host name for Message-IDs.
258Default:
259.IR me ,
260if that is supplied;
261otherwise the literal name
262.BR idhost ,
263which is certainly not what you want.
264.I idhost
265need not be the current host's name.
266For example, you may prefer to use fake
267host names in Message-IDs.
268However,
269.I idhost
270must be a fully-qualified name within your domain,
271and each host in your domain should use a different
272.IR idhost .
273
274The
275.B QMAILIDHOST
276environment variable overrides
277.IR idhost .
278.TP 5
279.I plusdomain
280Plus domain name.
281Default:
282.IR me ,
283if that is supplied;
284otherwise the literal name
285.BR plusdomain ,
286which is probably not what you want.
287.B qmail-inject
288adds this name to any host name that ends with a plus sign,
289including
290.I defaulthost
291if
292.I defaulthost
293ends with a plus sign.
294If a host name does not have dots but ends with a plus sign,
295.B qmail-inject
296uses
297.IR plusdomain ,
298not
299.IR defaultdomain .
300
301The
302.B QMAILPLUSDOMAIN
303environment variable overrides
304.IR plusdomain .
305.SH "SEE ALSO"
306addresses(5),
307qmail-control(5),
308qmail-header(5),
309qmail-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 @@
1#include "sig.h"
2#include "substdio.h"
3#include "stralloc.h"
4#include "subfd.h"
5#include "sgetopt.h"
6#include "getln.h"
7#include "alloc.h"
8#include "str.h"
9#include "fmt.h"
10#include "hfield.h"
11#include "token822.h"
12#include "control.h"
13#include "env.h"
14#include "gen_alloc.h"
15#include "gen_allocdefs.h"
16#include "error.h"
17#include "qmail.h"
18#include "now.h"
19#include "exit.h"
20#include "quote.h"
21#include "headerbody.h"
22#include "auto_qmail.h"
23#include "newfield.h"
24#include "constmap.h"
25
26#define LINELEN 80
27
28datetime_sec starttime;
29
30char *qmopts;
31int flagdeletesender = 0;
32int flagdeletefrom = 0;
33int flagdeletemessid = 0;
34int flagnamecomment = 0;
35int flaghackmess = 0;
36int flaghackrecip = 0;
37char *mailhost;
38char *mailuser;
39int mailusertokentype;
40char *mailrhost;
41char *mailruser;
42
43stralloc control_idhost = {0};
44stralloc control_defaultdomain = {0};
45stralloc control_defaulthost = {0};
46stralloc control_plusdomain = {0};
47
48stralloc sender = {0};
49stralloc envsbuf = {0};
50token822_alloc envs = {0};
51int flagrh;
52
53int flagqueue;
54struct qmail qqt;
55
56void put(s,len) char *s; int len;
57{ if (flagqueue) qmail_put(&qqt,s,len); else substdio_put(subfdout,s,len); }
58void puts(s) char *s; { put(s,str_len(s)); }
59
60void perm() { _exit(100); }
61void temp() { _exit(111); }
62void die_nomem() {
63 substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); }
64void die_invalid(sa) stralloc *sa; {
65 substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: ");
66 substdio_putflush(subfderr,sa->s,sa->len); perm(); }
67void die_qqt() {
68 substdio_putsflush(subfderr,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); }
69void die_chdir() {
70 substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); }
71void die_read() {
72 if (errno == error_nomem) die_nomem();
73 substdio_putsflush(subfderr,"qmail-inject: fatal: read error\n"); temp(); }
74void doordie(sa,r) stralloc *sa; int r; {
75 if (r == 1) return; if (r == -1) die_nomem();
76 substdio_putsflush(subfderr,"qmail-inject: fatal: unable to parse this line:\n");
77 substdio_putflush(subfderr,sa->s,sa->len); perm(); }
78
79GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
80GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
81
82static stralloc sauninit = {0};
83
84saa savedh = {0};
85saa hrlist = {0};
86saa tocclist = {0};
87saa hrrlist = {0};
88saa reciplist = {0};
89int flagresent;
90
91void exitnicely()
92{
93 char *qqx;
94
95 if (!flagqueue) substdio_flush(subfdout);
96
97 if (flagqueue)
98 {
99 int i;
100
101 if (!stralloc_0(&sender)) die_nomem();
102 qmail_from(&qqt,sender.s);
103
104 for (i = 0;i < reciplist.len;++i)
105 {
106 if (!stralloc_0(&reciplist.sa[i])) die_nomem();
107 qmail_to(&qqt,reciplist.sa[i].s);
108 }
109 if (flagrh)
110 if (flagresent)
111 for (i = 0;i < hrrlist.len;++i)
112 {
113 if (!stralloc_0(&hrrlist.sa[i])) die_nomem();
114 qmail_to(&qqt,hrrlist.sa[i].s);
115 }
116 else
117 for (i = 0;i < hrlist.len;++i)
118 {
119 if (!stralloc_0(&hrlist.sa[i])) die_nomem();
120 qmail_to(&qqt,hrlist.sa[i].s);
121 }
122
123 qqx = qmail_close(&qqt);
124 if (*qqx)
125 if (*qqx == 'D') {
126 substdio_puts(subfderr,"qmail-inject: fatal: ");
127 substdio_puts(subfderr,qqx + 1);
128 substdio_puts(subfderr,"\n");
129 substdio_flush(subfderr);
130 perm();
131 }
132 else {
133 substdio_puts(subfderr,"qmail-inject: fatal: ");
134 substdio_puts(subfderr,qqx + 1);
135 substdio_puts(subfderr,"\n");
136 substdio_flush(subfderr);
137 temp();
138 }
139 }
140
141 _exit(0);
142}
143
144void savedh_append(h)
145stralloc *h;
146{
147 if (!saa_readyplus(&savedh,1)) die_nomem();
148 savedh.sa[savedh.len] = sauninit;
149 if (!stralloc_copy(savedh.sa + savedh.len,h)) die_nomem();
150 ++savedh.len;
151}
152
153void savedh_print()
154{
155 int i;
156
157 for (i = 0;i < savedh.len;++i)
158 put(savedh.sa[i].s,savedh.sa[i].len);
159}
160
161stralloc defaultdomainbuf = {0};
162token822_alloc defaultdomain = {0};
163stralloc defaulthostbuf = {0};
164token822_alloc defaulthost = {0};
165stralloc plusdomainbuf = {0};
166token822_alloc plusdomain = {0};
167
168void rwroute(addr)
169token822_alloc *addr;
170{
171 if (addr->t[addr->len - 1].type == TOKEN822_AT)
172 while (addr->len)
173 if (addr->t[--addr->len].type == TOKEN822_COLON)
174 return;
175}
176
177void rwextraat(addr)
178token822_alloc *addr;
179{
180 int i;
181 if (addr->t[0].type == TOKEN822_AT)
182 {
183 --addr->len;
184 for (i = 0;i < addr->len;++i)
185 addr->t[i] = addr->t[i + 1];
186 }
187}
188
189void rwextradot(addr)
190token822_alloc *addr;
191{
192 int i;
193 if (addr->t[0].type == TOKEN822_DOT)
194 {
195 --addr->len;
196 for (i = 0;i < addr->len;++i)
197 addr->t[i] = addr->t[i + 1];
198 }
199}
200
201void rwnoat(addr)
202token822_alloc *addr;
203{
204 int i;
205 int shift;
206
207 for (i = 0;i < addr->len;++i)
208 if (addr->t[i].type == TOKEN822_AT)
209 return;
210 shift = defaulthost.len;
211 if (!token822_readyplus(addr,shift)) die_nomem();
212 for (i = addr->len - 1;i >= 0;--i)
213 addr->t[i + shift] = addr->t[i];
214 addr->len += shift;
215 for (i = 0;i < shift;++i)
216 addr->t[i] = defaulthost.t[shift - 1 - i];
217}
218
219void rwnodot(addr)
220token822_alloc *addr;
221{
222 int i;
223 int shift;
224 for (i = 0;i < addr->len;++i)
225 {
226 if (addr->t[i].type == TOKEN822_DOT)
227 return;
228 if (addr->t[i].type == TOKEN822_AT)
229 break;
230 }
231 for (i = 0;i < addr->len;++i)
232 {
233 if (addr->t[i].type == TOKEN822_LITERAL)
234 return;
235 if (addr->t[i].type == TOKEN822_AT)
236 break;
237 }
238 shift = defaultdomain.len;
239 if (!token822_readyplus(addr,shift)) die_nomem();
240 for (i = addr->len - 1;i >= 0;--i)
241 addr->t[i + shift] = addr->t[i];
242 addr->len += shift;
243 for (i = 0;i < shift;++i)
244 addr->t[i] = defaultdomain.t[shift - 1 - i];
245}
246
247void rwplus(addr)
248token822_alloc *addr;
249{
250 int i;
251 int shift;
252
253 if (addr->t[0].type != TOKEN822_ATOM) return;
254 if (!addr->t[0].slen) return;
255 if (addr->t[0].s[addr->t[0].slen - 1] != '+') return;
256
257 --addr->t[0].slen; /* remove + */
258
259 shift = plusdomain.len;
260 if (!token822_readyplus(addr,shift)) die_nomem();
261 for (i = addr->len - 1;i >= 0;--i)
262 addr->t[i + shift] = addr->t[i];
263 addr->len += shift;
264 for (i = 0;i < shift;++i)
265 addr->t[i] = plusdomain.t[shift - 1 - i];
266}
267
268void rwgeneric(addr)
269token822_alloc *addr;
270{
271 if (!addr->len) return; /* don't rewrite <> */
272 if (addr->len >= 2)
273 if (addr->t[1].type == TOKEN822_AT)
274 if (addr->t[0].type == TOKEN822_LITERAL)
275 if (!addr->t[0].slen) /* don't rewrite <foo@[]> */
276 return;
277 rwroute(addr);
278 if (!addr->len) return; /* <@foo:> -> <> */
279 rwextradot(addr);
280 if (!addr->len) return; /* <.> -> <> */
281 rwextraat(addr);
282 if (!addr->len) return; /* <@> -> <> */
283 rwnoat(addr);
284 rwplus(addr);
285 rwnodot(addr);
286}
287
288int setreturn(addr)
289token822_alloc *addr;
290{
291 if (!sender.s)
292 {
293 token822_reverse(addr);
294 if (token822_unquote(&sender,addr) != 1) die_nomem();
295 if (flaghackrecip)
296 if (!stralloc_cats(&sender,"-@[]")) die_nomem();
297 token822_reverse(addr);
298 }
299 return 1;
300}
301
302int rwreturn(addr)
303token822_alloc *addr;
304{
305 rwgeneric(addr);
306 setreturn(addr);
307 return 1;
308}
309
310int rwsender(addr)
311token822_alloc *addr;
312{
313 rwgeneric(addr);
314 return 1;
315}
316
317void rwappend(addr,xl)
318token822_alloc *addr;
319saa *xl;
320{
321 token822_reverse(addr);
322 if (!saa_readyplus(xl,1)) die_nomem();
323 xl->sa[xl->len] = sauninit;
324 if (token822_unquote(&xl->sa[xl->len],addr) != 1) die_nomem();
325 ++xl->len;
326 token822_reverse(addr);
327}
328
329int rwhrr(addr) token822_alloc *addr;
330{ rwgeneric(addr); rwappend(addr,&hrrlist); return 1; }
331int rwhr(addr) token822_alloc *addr;
332{ rwgeneric(addr); rwappend(addr,&hrlist); return 1; }
333int rwtocc(addr) token822_alloc *addr;
334{ rwgeneric(addr); rwappend(addr,&hrlist); rwappend(addr,&tocclist); return 1; }
335
336int htypeseen[H_NUM];
337stralloc hfbuf = {0};
338token822_alloc hfin = {0};
339token822_alloc hfrewrite = {0};
340token822_alloc hfaddr = {0};
341
342void doheaderfield(h)
343stralloc *h;
344{
345 int htype;
346 int (*rw)() = 0;
347
348 htype = hfield_known(h->s,h->len);
349 if (flagdeletefrom) if (htype == H_FROM) return;
350 if (flagdeletemessid) if (htype == H_MESSAGEID) return;
351 if (flagdeletesender) if (htype == H_RETURNPATH) return;
352
353 if (htype)
354 htypeseen[htype] = 1;
355 else
356 if (!hfield_valid(h->s,h->len))
357 die_invalid(h);
358
359 switch(htype) {
360 case H_TO: case H_CC:
361 rw = rwtocc; break;
362 case H_BCC: case H_APPARENTLYTO:
363 rw = rwhr; break;
364 case H_R_TO: case H_R_CC: case H_R_BCC:
365 rw = rwhrr; break;
366 case H_RETURNPATH:
367 rw = rwreturn; break;
368 case H_SENDER: case H_FROM: case H_REPLYTO:
369 case H_RETURNRECEIPTTO: case H_ERRORSTO:
370 case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO:
371 rw = rwsender; break;
372 }
373
374 if (rw) {
375 doordie(h,token822_parse(&hfin,h,&hfbuf));
376 doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rw));
377 if (token822_unparse(h,&hfrewrite,LINELEN) != 1)
378 die_nomem();
379 }
380
381 if (htype == H_BCC) return;
382 if (htype == H_R_BCC) return;
383 if (htype == H_RETURNPATH) return;
384 if (htype == H_CONTENTLENGTH) return; /* some things are just too stupid */
385 savedh_append(h);
386}
387
388void dobody(h)
389stralloc *h;
390{
391 put(h->s,h->len);
392}
393
394stralloc torecip = {0};
395token822_alloc tr = {0};
396
397void dorecip(s)
398char *s;
399{
400 if (!quote2(&torecip,s)) die_nomem();
401 switch(token822_parse(&tr,&torecip,&hfbuf))
402 {
403 case -1: die_nomem();
404 case 0:
405 substdio_puts(subfderr,"qmail-inject: fatal: unable to parse address: ");
406 substdio_puts(subfderr,s);
407 substdio_putsflush(subfderr,"\n");
408 perm();
409 }
410 token822_reverse(&tr);
411 rwgeneric(&tr);
412 rwappend(&tr,&reciplist);
413}
414
415stralloc defaultfrom = {0};
416token822_alloc df = {0};
417
418void defaultfrommake()
419{
420 char *fullname;
421 fullname = env_get("QMAILNAME");
422 if (!fullname) fullname = env_get("MAILNAME");
423 if (!fullname) fullname = env_get("NAME");
424 if (!token822_ready(&df,20)) die_nomem();
425 df.len = 0;
426 df.t[df.len].type = TOKEN822_ATOM;
427 df.t[df.len].s = "From";
428 df.t[df.len].slen = 4;
429 ++df.len;
430 df.t[df.len].type = TOKEN822_COLON;
431 ++df.len;
432 if (fullname && !flagnamecomment)
433 {
434 df.t[df.len].type = TOKEN822_QUOTE;
435 df.t[df.len].s = fullname;
436 df.t[df.len].slen = str_len(fullname);
437 ++df.len;
438 df.t[df.len].type = TOKEN822_LEFT;
439 ++df.len;
440 }
441 df.t[df.len].type = mailusertokentype;
442 df.t[df.len].s = mailuser;
443 df.t[df.len].slen = str_len(mailuser);
444 ++df.len;
445 if (mailhost)
446 {
447 df.t[df.len].type = TOKEN822_AT;
448 ++df.len;
449 df.t[df.len].type = TOKEN822_ATOM;
450 df.t[df.len].s = mailhost;
451 df.t[df.len].slen = str_len(mailhost);
452 ++df.len;
453 }
454 if (fullname && !flagnamecomment)
455 {
456 df.t[df.len].type = TOKEN822_RIGHT;
457 ++df.len;
458 }
459 if (fullname && flagnamecomment)
460 {
461 df.t[df.len].type = TOKEN822_COMMENT;
462 df.t[df.len].s = fullname;
463 df.t[df.len].slen = str_len(fullname);
464 ++df.len;
465 }
466 if (token822_unparse(&defaultfrom,&df,LINELEN) != 1) die_nomem();
467 doordie(&defaultfrom,token822_parse(&df,&defaultfrom,&hfbuf));
468 doordie(&defaultfrom,token822_addrlist(&hfrewrite,&hfaddr,&df,rwsender));
469 if (token822_unparse(&defaultfrom,&hfrewrite,LINELEN) != 1) die_nomem();
470}
471
472stralloc defaultreturnpath = {0};
473token822_alloc drp = {0};
474stralloc hackedruser = {0};
475char strnum[FMT_ULONG];
476
477void dodefaultreturnpath()
478{
479 if (!stralloc_copys(&hackedruser,mailruser)) die_nomem();
480 if (flaghackmess)
481 {
482 if (!stralloc_cats(&hackedruser,"-")) die_nomem();
483 if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) starttime))) die_nomem();
484 if (!stralloc_cats(&hackedruser,".")) die_nomem();
485 if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
486 }
487 if (flaghackrecip)
488 if (!stralloc_cats(&hackedruser,"-")) die_nomem();
489 if (!token822_ready(&drp,10)) die_nomem();
490 drp.len = 0;
491 drp.t[drp.len].type = TOKEN822_ATOM;
492 drp.t[drp.len].s = "Return-Path";
493 drp.t[drp.len].slen = 11;
494 ++drp.len;
495 drp.t[drp.len].type = TOKEN822_COLON;
496 ++drp.len;
497 drp.t[drp.len].type = TOKEN822_QUOTE;
498 drp.t[drp.len].s = hackedruser.s;
499 drp.t[drp.len].slen = hackedruser.len;
500 ++drp.len;
501 if (mailrhost)
502 {
503 drp.t[drp.len].type = TOKEN822_AT;
504 ++drp.len;
505 drp.t[drp.len].type = TOKEN822_ATOM;
506 drp.t[drp.len].s = mailrhost;
507 drp.t[drp.len].slen = str_len(mailrhost);
508 ++drp.len;
509 }
510 if (token822_unparse(&defaultreturnpath,&drp,LINELEN) != 1) die_nomem();
511 doordie(&defaultreturnpath,token822_parse(&drp,&defaultreturnpath,&hfbuf));
512 doordie(&defaultreturnpath
513 ,token822_addrlist(&hfrewrite,&hfaddr,&drp,rwreturn));
514 if (token822_unparse(&defaultreturnpath,&hfrewrite,LINELEN) != 1) die_nomem();
515}
516
517int flagmft = 0;
518stralloc mft = {0};
519struct constmap mapmft;
520
521void mft_init()
522{
523 char *x;
524 int r;
525
526 x = env_get("QMAILMFTFILE");
527 if (!x) return;
528
529 r = control_readfile(&mft,x,0);
530 if (r == -1) die_read(); /*XXX*/
531 if (!r) return;
532
533 if (!constmap_init(&mapmft,mft.s,mft.len,0)) die_nomem();
534 flagmft = 1;
535}
536
537void finishmft()
538{
539 int i;
540 static stralloc sa = {0};
541 static stralloc sa2 = {0};
542
543 if (!flagmft) return;
544 if (htypeseen[H_MAILFOLLOWUPTO]) return;
545
546 for (i = 0;i < tocclist.len;++i)
547 if (constmap(&mapmft,tocclist.sa[i].s,tocclist.sa[i].len))
548 break;
549
550 if (i == tocclist.len) return;
551
552 puts("Mail-Followup-To: ");
553 i = tocclist.len;
554 while (i--) {
555 if (!stralloc_copy(&sa,&tocclist.sa[i])) die_nomem();
556 if (!stralloc_0(&sa)) die_nomem();
557 if (!quote2(&sa2,sa.s)) die_nomem();
558 put(sa2.s,sa2.len);
559 if (i) puts(",\n ");
560 }
561 puts("\n");
562}
563
564void finishheader()
565{
566 flagresent =
567 htypeseen[H_R_SENDER] || htypeseen[H_R_FROM] || htypeseen[H_R_REPLYTO]
568 || htypeseen[H_R_TO] || htypeseen[H_R_CC] || htypeseen[H_R_BCC]
569 || htypeseen[H_R_DATE] || htypeseen[H_R_MESSAGEID];
570
571 if (!sender.s)
572 dodefaultreturnpath();
573
574 if (!flagqueue)
575 {
576 static stralloc sa = {0};
577 static stralloc sa2 = {0};
578
579 if (!stralloc_copy(&sa,&sender)) die_nomem();
580 if (!stralloc_0(&sa)) die_nomem();
581 if (!quote2(&sa2,sa.s)) die_nomem();
582
583 puts("Return-Path: <");
584 put(sa2.s,sa2.len);
585 puts(">\n");
586 }
587
588 /* could check at this point whether there are any recipients */
589 if (flagqueue)
590 if (qmail_open(&qqt) == -1) die_qqt();
591
592 if (flagresent)
593 {
594 if (!htypeseen[H_R_DATE])
595 {
596 if (!newfield_datemake(starttime)) die_nomem();
597 puts("Resent-");
598 put(newfield_date.s,newfield_date.len);
599 }
600 if (!htypeseen[H_R_MESSAGEID])
601 {
602 if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem();
603 puts("Resent-");
604 put(newfield_msgid.s,newfield_msgid.len);
605 }
606 if (!htypeseen[H_R_FROM])
607 {
608 defaultfrommake();
609 puts("Resent-");
610 put(defaultfrom.s,defaultfrom.len);
611 }
612 if (!htypeseen[H_R_TO] && !htypeseen[H_R_CC])
613 puts("Resent-Cc: recipient list not shown: ;\n");
614 }
615 else
616 {
617 if (!htypeseen[H_DATE])
618 {
619 if (!newfield_datemake(starttime)) die_nomem();
620 put(newfield_date.s,newfield_date.len);
621 }
622 if (!htypeseen[H_MESSAGEID])
623 {
624 if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem();
625 put(newfield_msgid.s,newfield_msgid.len);
626 }
627 if (!htypeseen[H_FROM])
628 {
629 defaultfrommake();
630 put(defaultfrom.s,defaultfrom.len);
631 }
632 if (!htypeseen[H_TO] && !htypeseen[H_CC])
633 puts("Cc: recipient list not shown: ;\n");
634 finishmft();
635 }
636
637 savedh_print();
638}
639
640void getcontrols()
641{
642 static stralloc sa = {0};
643 char *x;
644
645 mft_init();
646
647 if (chdir(auto_qmail) == -1) die_chdir();
648 if (control_init() == -1) die_read();
649
650 if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1)
651 die_read();
652 x = env_get("QMAILDEFAULTDOMAIN");
653 if (x) if (!stralloc_copys(&control_defaultdomain,x)) die_nomem();
654 if (!stralloc_copys(&sa,".")) die_nomem();
655 if (!stralloc_cat(&sa,&control_defaultdomain)) die_nomem();
656 doordie(&sa,token822_parse(&defaultdomain,&sa,&defaultdomainbuf));
657
658 if (control_rldef(&control_defaulthost,"control/defaulthost",1,"defaulthost") != 1)
659 die_read();
660 x = env_get("QMAILDEFAULTHOST");
661 if (x) if (!stralloc_copys(&control_defaulthost,x)) die_nomem();
662 if (!stralloc_copys(&sa,"@")) die_nomem();
663 if (!stralloc_cat(&sa,&control_defaulthost)) die_nomem();
664 doordie(&sa,token822_parse(&defaulthost,&sa,&defaulthostbuf));
665
666 if (control_rldef(&control_plusdomain,"control/plusdomain",1,"plusdomain") != 1)
667 die_read();
668 x = env_get("QMAILPLUSDOMAIN");
669 if (x) if (!stralloc_copys(&control_plusdomain,x)) die_nomem();
670 if (!stralloc_copys(&sa,".")) die_nomem();
671 if (!stralloc_cat(&sa,&control_plusdomain)) die_nomem();
672 doordie(&sa,token822_parse(&plusdomain,&sa,&plusdomainbuf));
673
674 if (control_rldef(&control_idhost,"control/idhost",1,"idhost") != 1)
675 die_read();
676 x = env_get("QMAILIDHOST");
677 if (x) if (!stralloc_copys(&control_idhost,x)) die_nomem();
678}
679
680#define RECIP_DEFAULT 1
681#define RECIP_ARGS 2
682#define RECIP_HEADER 3
683#define RECIP_AH 4
684
685void main(argc,argv)
686int argc;
687char **argv;
688{
689 int i;
690 int opt;
691 int recipstrategy;
692
693 sig_pipeignore();
694
695 starttime = now();
696
697 qmopts = env_get("QMAILINJECT");
698 if (qmopts)
699 while (*qmopts)
700 switch(*qmopts++)
701 {
702 case 'c': flagnamecomment = 1; break;
703 case 's': flagdeletesender = 1; break;
704 case 'f': flagdeletefrom = 1; break;
705 case 'i': flagdeletemessid = 1; break;
706 case 'r': flaghackrecip = 1; break;
707 case 'm': flaghackmess = 1; break;
708 }
709
710 mailhost = env_get("QMAILHOST");
711 if (!mailhost) mailhost = env_get("MAILHOST");
712 mailrhost = env_get("QMAILSHOST");
713 if (!mailrhost) mailrhost = mailhost;
714
715 mailuser = env_get("QMAILUSER");
716 if (!mailuser) mailuser = env_get("MAILUSER");
717 if (!mailuser) mailuser = env_get("USER");
718 if (!mailuser) mailuser = env_get("LOGNAME");
719 if (!mailuser) mailuser = "anonymous";
720 mailusertokentype = TOKEN822_ATOM;
721 if (quote_need(mailuser,str_len(mailuser))) mailusertokentype = TOKEN822_QUOTE;
722 mailruser = env_get("QMAILSUSER");
723 if (!mailruser) mailruser = mailuser;
724
725 for (i = 0;i < H_NUM;++i) htypeseen[i] = 0;
726
727 recipstrategy = RECIP_DEFAULT;
728 flagqueue = 1;
729
730 getcontrols();
731
732 if (!saa_readyplus(&hrlist,1)) die_nomem();
733 if (!saa_readyplus(&tocclist,1)) die_nomem();
734 if (!saa_readyplus(&hrrlist,1)) die_nomem();
735 if (!saa_readyplus(&reciplist,1)) die_nomem();
736
737 while ((opt = getopt(argc,argv,"aAhHnNf:")) != opteof)
738 switch(opt)
739 {
740 case 'a': recipstrategy = RECIP_ARGS; break;
741 case 'A': recipstrategy = RECIP_DEFAULT; break;
742 case 'h': recipstrategy = RECIP_HEADER; break;
743 case 'H': recipstrategy = RECIP_AH; break;
744 case 'n': flagqueue = 0; break;
745 case 'N': flagqueue = 1; break;
746 case 'f':
747 if (!quote2(&sender,optarg)) die_nomem();
748 doordie(&sender,token822_parse(&envs,&sender,&envsbuf));
749 token822_reverse(&envs);
750 rwgeneric(&envs);
751 token822_reverse(&envs);
752 if (token822_unquote(&sender,&envs) != 1) die_nomem();
753 break;
754 case '?':
755 default:
756 perm();
757 }
758 argc -= optind;
759 argv += optind;
760
761 if (recipstrategy == RECIP_DEFAULT)
762 recipstrategy = (*argv ? RECIP_ARGS : RECIP_HEADER);
763
764 if (recipstrategy != RECIP_HEADER)
765 while (*argv)
766 dorecip(*argv++);
767
768 flagrh = (recipstrategy != RECIP_ARGS);
769
770 if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1)
771 die_read();
772 exitnicely();
773}
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 @@
1.TH qmail-limits 7
2.SH "NAME"
3qmail-limits \- artificial limits in the qmail system
4.SH "DESCRIPTION"
5The
6.B qmail
7system is able to handle messages of any size,
8addresses of any size, mailing lists of any size, and so on,
9except as limited by the available memory and disk space.
10
11However, it imposes certain artificial limits:
12.TP 5
131.
14.B qmail-lspawn
15silently limits the number of simultaneous local deliveries to SPAWN.
16.B qmail-rspawn
17silently limits the number of simultaneous remote deliveries to SPAWN.
18.TP 5
192.
20.B qmail-queue
21rejects any message with an envelope address longer than 1000 characters.
22.TP 5
233.
24.B qmail-lspawn
25truncates any overly long error report from a delivery program.
26It appends a note saying that it did so.
27.SH "SEE ALSO"
28qmail-lspawn(8),
29qmail-queue(8),
30qmail-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 @@
1.TH qmail-local 8
2.SH NAME
3qmail-local \- deliver or forward a mail message
4.SH SYNOPSIS
5.B qmail-local
6[
7.B \-nN
8]
9.I user
10.I homedir
11.I local
12.I dash
13.I ext
14.I domain
15.I sender
16.I defaultdelivery
17.SH DESCRIPTION
18.B qmail-local
19reads a mail message
20and delivers it to
21.I user
22by the procedure described in
23.BR dot-qmail(5) .
24
25The message's envelope recipient is
26.IR local@domain .
27.B qmail-local
28records
29.I local@domain
30in a new
31.B Delivered-To
32header field.
33If exactly the same
34.B Delivered-To: \fIlocal@domain
35already appears in the header,
36.B qmail-local
37bounces the message,
38to prevent mail forwarding loops.
39
40The message's envelope sender is
41.IR sender .
42.B qmail-local
43records
44.I sender
45in a new
46.B Return-Path
47header field.
48
49.I homedir
50is the user's home directory.
51It must be an absolute directory name.
52
53.I dash
54and
55.I ext
56identify the
57.B .qmail\fIdashext
58file used by
59.BR qmail-local ;
60see
61.BR dot-qmail(5) .
62Normally
63.I dash
64is either empty or a lone hyphen.
65If it is empty,
66.B qmail-local
67treats a nonexistent
68.B .qmail\fIext
69the same way as an empty
70.BR .qmail\fIext :
71namely, following the delivery instructions in
72.IR defaultdelivery .
73
74The standard input for
75.B qmail-local
76must be a seekable file,
77so that
78.B qmail-local
79can read it more than once.
80.SH "OPTIONS"
81.TP
82.B \-n
83Instead of reading and delivering the message,
84print a description of the delivery instructions.
85.TP
86.B \-N
87(Default.) Read and deliver the message.
88.SH "EXIT CODES"
890 if the delivery is completely successful;
90nonzero if any delivery instruction failed.
91Exit code 111
92indicates temporary failure.
93.SH "SEE ALSO"
94dot-qmail(5),
95envelopes(5),
96qmail-command(8),
97qmail-queue(8),
98qmail-send(8),
99qmail-lspawn(8)
diff --git a/qmail-local.c b/qmail-local.c
new file mode 100644
index 0000000..cd01602
--- /dev/null
+++ b/qmail-local.c
@@ -0,0 +1,698 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "readwrite.h"
4#include "sig.h"
5#include "env.h"
6#include "byte.h"
7#include "exit.h"
8#include "fork.h"
9#include "open.h"
10#include "wait.h"
11#include "lock.h"
12#include "seek.h"
13#include "substdio.h"
14#include "getln.h"
15#include "strerr.h"
16#include "subfd.h"
17#include "sgetopt.h"
18#include "alloc.h"
19#include "error.h"
20#include "stralloc.h"
21#include "fmt.h"
22#include "str.h"
23#include "now.h"
24#include "case.h"
25#include "quote.h"
26#include "qmail.h"
27#include "slurpclose.h"
28#include "myctime.h"
29#include "gfrom.h"
30#include "auto_patrn.h"
31
32void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
33
34void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); }
35void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
36void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
37void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); }
38void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno),". (#4.3.0)"); }
39void temp_slowlock()
40{ strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
41void temp_qmail(fn) char *fn;
42{ strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
43
44int flagdoit;
45int flag99;
46
47char *user;
48char *homedir;
49char *local;
50char *dash;
51char *ext;
52char *host;
53char *sender;
54char *aliasempty;
55
56stralloc safeext = {0};
57stralloc ufline = {0};
58stralloc rpline = {0};
59stralloc envrecip = {0};
60stralloc dtline = {0};
61stralloc qme = {0};
62stralloc ueo = {0};
63stralloc cmds = {0};
64stralloc messline = {0};
65stralloc foo = {0};
66
67char buf[1024];
68char outbuf[1024];
69
70/* child process */
71
72char fntmptph[80 + FMT_ULONG * 2];
73char fnnewtph[80 + FMT_ULONG * 2];
74void tryunlinktmp() { unlink(fntmptph); }
75void sigalrm() { tryunlinktmp(); _exit(3); }
76
77void maildir_child(dir)
78char *dir;
79{
80 unsigned long pid;
81 unsigned long time;
82 char host[64];
83 char *s;
84 int loop;
85 struct stat st;
86 int fd;
87 substdio ss;
88 substdio ssout;
89
90 sig_alarmcatch(sigalrm);
91 if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
92 pid = getpid();
93 host[0] = 0;
94 gethostname(host,sizeof(host));
95 for (loop = 0;;++loop)
96 {
97 time = now();
98 s = fntmptph;
99 s += fmt_str(s,"tmp/");
100 s += fmt_ulong(s,time); *s++ = '.';
101 s += fmt_ulong(s,pid); *s++ = '.';
102 s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
103 if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
104 /* really should never get to this point */
105 if (loop == 2) _exit(1);
106 sleep(2);
107 }
108 str_copy(fnnewtph,fntmptph);
109 byte_copy(fnnewtph,3,"new");
110
111 alarm(86400);
112 fd = open_excl(fntmptph);
113 if (fd == -1) _exit(1);
114
115 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
116 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
117 if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail;
118 if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail;
119
120 switch(substdio_copy(&ssout,&ss))
121 {
122 case -2: tryunlinktmp(); _exit(4);
123 case -3: goto fail;
124 }
125
126 if (substdio_flush(&ssout) == -1) goto fail;
127 if (fsync(fd) == -1) goto fail;
128 if (close(fd) == -1) goto fail; /* NFS dorks */
129
130 if (link(fntmptph,fnnewtph) == -1) goto fail;
131 /* if it was error_exist, almost certainly successful; i hate NFS */
132 tryunlinktmp(); _exit(0);
133
134 fail: tryunlinktmp(); _exit(1);
135}
136
137/* end child process */
138
139void maildir(fn)
140char *fn;
141{
142 int child;
143 int wstat;
144
145 if (seek_begin(0) == -1) temp_rewind();
146
147 switch(child = fork())
148 {
149 case -1:
150 temp_fork();
151 case 0:
152 maildir_child(fn);
153 _exit(111);
154 }
155
156 wait_pid(&wstat,child);
157 if (wait_crashed(wstat))
158 temp_childcrashed();
159 switch(wait_exitcode(wstat))
160 {
161 case 0: break;
162 case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
163 case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
164 case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
165 default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
166 }
167}
168
169void mailfile(fn)
170char *fn;
171{
172 int fd;
173 substdio ss;
174 substdio ssout;
175 int match;
176 seek_pos pos;
177 int flaglocked;
178
179 if (seek_begin(0) == -1) temp_rewind();
180
181 fd = open_append(fn);
182 if (fd == -1)
183 strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)");
184
185 sig_alarmcatch(temp_slowlock);
186 alarm(30);
187 flaglocked = (lock_ex(fd) != -1);
188 alarm(0);
189 sig_alarmdefault();
190
191 seek_end(fd);
192 pos = seek_cur(fd);
193
194 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
195 substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
196 if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs;
197 if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs;
198 if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs;
199 for (;;)
200 {
201 if (getln(&ss,&messline,&match,'\n') != 0)
202 {
203 strerr_warn3("Unable to read message: ",error_str(errno),". (#4.3.0)",0);
204 if (flaglocked) seek_trunc(fd,pos); close(fd);
205 _exit(111);
206 }
207 if (!match && !messline.len) break;
208 if (gfrom(messline.s,messline.len))
209 if (substdio_bput(&ssout,">",1)) goto writeerrs;
210 if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs;
211 if (!match)
212 {
213 if (substdio_bputs(&ssout,"\n")) goto writeerrs;
214 break;
215 }
216 }
217 if (substdio_bputs(&ssout,"\n")) goto writeerrs;
218 if (substdio_flush(&ssout)) goto writeerrs;
219 if (fsync(fd) == -1) goto writeerrs;
220 close(fd);
221 return;
222
223 writeerrs:
224 strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
225 if (flaglocked) seek_trunc(fd,pos);
226 close(fd);
227 _exit(111);
228}
229
230void mailprogram(prog)
231char *prog;
232{
233 int child;
234 char *(args[4]);
235 int wstat;
236
237 if (seek_begin(0) == -1) temp_rewind();
238
239 switch(child = fork())
240 {
241 case -1:
242 temp_fork();
243 case 0:
244 args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
245 sig_pipedefault();
246 execv(*args,args);
247 strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)");
248 }
249
250 wait_pid(&wstat,child);
251 if (wait_crashed(wstat))
252 temp_childcrashed();
253 switch(wait_exitcode(wstat))
254 {
255 case 100:
256 case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
257 case 0: break;
258 case 99: flag99 = 1; break;
259 default: _exit(111);
260 }
261}
262
263unsigned long mailforward_qp = 0;
264
265void mailforward(recips)
266char **recips;
267{
268 struct qmail qqt;
269 char *qqx;
270 substdio ss;
271 int match;
272
273 if (seek_begin(0) == -1) temp_rewind();
274 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
275
276 if (qmail_open(&qqt) == -1) temp_fork();
277 mailforward_qp = qmail_qp(&qqt);
278 qmail_put(&qqt,dtline.s,dtline.len);
279 do
280 {
281 if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; }
282 qmail_put(&qqt,messline.s,messline.len);
283 }
284 while (match);
285 qmail_from(&qqt,ueo.s);
286 while (*recips) qmail_to(&qqt,*recips++);
287 qqx = qmail_close(&qqt);
288 if (!*qqx) return;
289 strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,".");
290}
291
292void bouncexf()
293{
294 int match;
295 substdio ss;
296
297 if (seek_begin(0) == -1) temp_rewind();
298 substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
299 for (;;)
300 {
301 if (getln(&ss,&messline,&match,'\n') != 0) temp_read();
302 if (!match) break;
303 if (messline.len <= 1)
304 break;
305 if (messline.len == dtline.len)
306 if (!str_diffn(messline.s,dtline.s,dtline.len))
307 strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)");
308 }
309}
310
311void checkhome()
312{
313 struct stat st;
314
315 if (stat(".",&st) == -1)
316 strerr_die3x(111,"Unable to stat home directory: ",error_str(errno),". (#4.3.0)");
317 if (st.st_mode & auto_patrn)
318 strerr_die1x(111,"Uh-oh: home directory is writable. (#4.7.0)");
319 if (st.st_mode & 01000)
320 if (flagdoit)
321 strerr_die1x(111,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)");
322 else
323 strerr_warn1("Warning: home directory is sticky.",0);
324}
325
326int qmeox(dashowner)
327char *dashowner;
328{
329 struct stat st;
330
331 if (!stralloc_copys(&qme,".qmail")) temp_nomem();
332 if (!stralloc_cats(&qme,dash)) temp_nomem();
333 if (!stralloc_cat(&qme,&safeext)) temp_nomem();
334 if (!stralloc_cats(&qme,dashowner)) temp_nomem();
335 if (!stralloc_0(&qme)) temp_nomem();
336 if (stat(qme.s,&st) == -1)
337 {
338 if (error_temp(errno)) temp_qmail(qme.s);
339 return -1;
340 }
341 return 0;
342}
343
344int qmeexists(fd,cutable)
345int *fd;
346int *cutable;
347{
348 struct stat st;
349
350 if (!stralloc_0(&qme)) temp_nomem();
351
352 *fd = open_read(qme.s);
353 if (*fd == -1) {
354 if (error_temp(errno)) temp_qmail(qme.s);
355 if (errno == error_perm) temp_qmail(qme.s);
356 if (errno == error_acces) temp_qmail(qme.s);
357 return 0;
358 }
359
360 if (fstat(*fd,&st) == -1) temp_qmail(qme.s);
361 if ((st.st_mode & S_IFMT) == S_IFREG) {
362 if (st.st_mode & auto_patrn)
363 strerr_die1x(111,"Uh-oh: .qmail file is writable. (#4.7.0)");
364 *cutable = !!(st.st_mode & 0100);
365 return 1;
366 }
367 close(*fd);
368 return 0;
369}
370
371/* "" "": "" */
372/* "-/" "": "-/" "-/default" */
373/* "-/" "a": "-/a" "-/default" */
374/* "-/" "a-": "-/a-" "-/a-default" "-/default" */
375/* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */
376/* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */
377/* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */
378
379void qmesearch(fd,cutable)
380int *fd;
381int *cutable;
382{
383 int i;
384
385 if (!stralloc_copys(&qme,".qmail")) temp_nomem();
386 if (!stralloc_cats(&qme,dash)) temp_nomem();
387 if (!stralloc_cat(&qme,&safeext)) temp_nomem();
388 if (qmeexists(fd,cutable)) {
389 if (safeext.len >= 7) {
390 i = safeext.len - 7;
391 if (!byte_diff("default",7,safeext.s + i))
392 if (i <= str_len(ext)) /* paranoia */
393 if (!env_put2("DEFAULT",ext + i)) temp_nomem();
394 }
395 return;
396 }
397
398 for (i = safeext.len;i >= 0;--i)
399 if (!i || (safeext.s[i - 1] == '-')) {
400 if (!stralloc_copys(&qme,".qmail")) temp_nomem();
401 if (!stralloc_cats(&qme,dash)) temp_nomem();
402 if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem();
403 if (!stralloc_cats(&qme,"default")) temp_nomem();
404 if (qmeexists(fd,cutable)) {
405 if (i <= str_len(ext)) /* paranoia */
406 if (!env_put2("DEFAULT",ext + i)) temp_nomem();
407 return;
408 }
409 }
410
411 *fd = -1;
412}
413
414unsigned long count_file = 0;
415unsigned long count_forward = 0;
416unsigned long count_program = 0;
417char count_buf[FMT_ULONG];
418
419void count_print()
420{
421 substdio_puts(subfdoutsmall,"did ");
422 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file));
423 substdio_puts(subfdoutsmall,"+");
424 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward));
425 substdio_puts(subfdoutsmall,"+");
426 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program));
427 substdio_puts(subfdoutsmall,"\n");
428 if (mailforward_qp)
429 {
430 substdio_puts(subfdoutsmall,"qp ");
431 substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp));
432 substdio_puts(subfdoutsmall,"\n");
433 }
434 substdio_flush(subfdoutsmall);
435}
436
437void sayit(type,cmd,len)
438char *type;
439char *cmd;
440int len;
441{
442 substdio_puts(subfdoutsmall,type);
443 substdio_put(subfdoutsmall,cmd,len);
444 substdio_putsflush(subfdoutsmall,"\n");
445}
446
447void main(argc,argv)
448int argc;
449char **argv;
450{
451 int opt;
452 int i;
453 int j;
454 int k;
455 int fd;
456 int numforward;
457 char **recips;
458 datetime_sec starttime;
459 int flagforwardonly;
460 char *x;
461
462 umask(077);
463 sig_pipeignore();
464
465 if (!env_init()) temp_nomem();
466
467 flagdoit = 1;
468 while ((opt = getopt(argc,argv,"nN")) != opteof)
469 switch(opt)
470 {
471 case 'n': flagdoit = 0; break;
472 case 'N': flagdoit = 1; break;
473 default:
474 usage();
475 }
476 argc -= optind;
477 argv += optind;
478
479 if (!(user = *argv++)) usage();
480 if (!(homedir = *argv++)) usage();
481 if (!(local = *argv++)) usage();
482 if (!(dash = *argv++)) usage();
483 if (!(ext = *argv++)) usage();
484 if (!(host = *argv++)) usage();
485 if (!(sender = *argv++)) usage();
486 if (!(aliasempty = *argv++)) usage();
487 if (*argv) usage();
488
489 if (homedir[0] != '/') usage();
490 if (chdir(homedir) == -1)
491 strerr_die5x(111,"Unable to switch to ",homedir,": ",error_str(errno),". (#4.3.0)");
492 checkhome();
493
494 if (!env_put2("HOST",host)) temp_nomem();
495 if (!env_put2("HOME",homedir)) temp_nomem();
496 if (!env_put2("USER",user)) temp_nomem();
497 if (!env_put2("LOCAL",local)) temp_nomem();
498
499 if (!stralloc_copys(&envrecip,local)) temp_nomem();
500 if (!stralloc_cats(&envrecip,"@")) temp_nomem();
501 if (!stralloc_cats(&envrecip,host)) temp_nomem();
502
503 if (!stralloc_copy(&foo,&envrecip)) temp_nomem();
504 if (!stralloc_0(&foo)) temp_nomem();
505 if (!env_put2("RECIPIENT",foo.s)) temp_nomem();
506
507 if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem();
508 if (!stralloc_cat(&dtline,&envrecip)) temp_nomem();
509 for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_';
510 if (!stralloc_cats(&dtline,"\n")) temp_nomem();
511
512 if (!stralloc_copy(&foo,&dtline)) temp_nomem();
513 if (!stralloc_0(&foo)) temp_nomem();
514 if (!env_put2("DTLINE",foo.s)) temp_nomem();
515
516 if (flagdoit)
517 bouncexf();
518
519 if (!env_put2("SENDER",sender)) temp_nomem();
520
521 if (!quote2(&foo,sender)) temp_nomem();
522 if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem();
523 if (!stralloc_cat(&rpline,&foo)) temp_nomem();
524 for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_';
525 if (!stralloc_cats(&rpline,">\n")) temp_nomem();
526
527 if (!stralloc_copy(&foo,&rpline)) temp_nomem();
528 if (!stralloc_0(&foo)) temp_nomem();
529 if (!env_put2("RPLINE",foo.s)) temp_nomem();
530
531 if (!stralloc_copys(&ufline,"From ")) temp_nomem();
532 if (*sender)
533 {
534 int len; int i; char ch;
535
536 len = str_len(sender);
537 if (!stralloc_readyplus(&ufline,len)) temp_nomem();
538 for (i = 0;i < len;++i)
539 {
540 ch = sender[i];
541 if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-';
542 ufline.s[ufline.len + i] = ch;
543 }
544 ufline.len += len;
545 }
546 else
547 if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem();
548 if (!stralloc_cats(&ufline," ")) temp_nomem();
549 starttime = now();
550 if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem();
551
552 if (!stralloc_copy(&foo,&ufline)) temp_nomem();
553 if (!stralloc_0(&foo)) temp_nomem();
554 if (!env_put2("UFLINE",foo.s)) temp_nomem();
555
556 x = ext;
557 if (!env_put2("EXT",x)) temp_nomem();
558 x += str_chr(x,'-'); if (*x) ++x;
559 if (!env_put2("EXT2",x)) temp_nomem();
560 x += str_chr(x,'-'); if (*x) ++x;
561 if (!env_put2("EXT3",x)) temp_nomem();
562 x += str_chr(x,'-'); if (*x) ++x;
563 if (!env_put2("EXT4",x)) temp_nomem();
564
565 if (!stralloc_copys(&safeext,ext)) temp_nomem();
566 case_lowerb(safeext.s,safeext.len);
567 for (i = 0;i < safeext.len;++i)
568 if (safeext.s[i] == '.')
569 safeext.s[i] = ':';
570
571 i = str_len(host);
572 i = byte_rchr(host,i,'.');
573 if (!stralloc_copyb(&foo,host,i)) temp_nomem();
574 if (!stralloc_0(&foo)) temp_nomem();
575 if (!env_put2("HOST2",foo.s)) temp_nomem();
576 i = byte_rchr(host,i,'.');
577 if (!stralloc_copyb(&foo,host,i)) temp_nomem();
578 if (!stralloc_0(&foo)) temp_nomem();
579 if (!env_put2("HOST3",foo.s)) temp_nomem();
580 i = byte_rchr(host,i,'.');
581 if (!stralloc_copyb(&foo,host,i)) temp_nomem();
582 if (!stralloc_0(&foo)) temp_nomem();
583 if (!env_put2("HOST4",foo.s)) temp_nomem();
584
585 flagforwardonly = 0;
586 qmesearch(&fd,&flagforwardonly);
587 if (fd == -1)
588 if (*dash)
589 strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
590
591 if (!stralloc_copys(&ueo,sender)) temp_nomem();
592 if (str_diff(sender,""))
593 if (str_diff(sender,"#@[]"))
594 if (qmeox("-owner") == 0)
595 {
596 if (qmeox("-owner-default") == 0)
597 {
598 if (!stralloc_copys(&ueo,local)) temp_nomem();
599 if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem();
600 if (!stralloc_cats(&ueo,host)) temp_nomem();
601 if (!stralloc_cats(&ueo,"-@[]")) temp_nomem();
602 }
603 else
604 {
605 if (!stralloc_copys(&ueo,local)) temp_nomem();
606 if (!stralloc_cats(&ueo,"-owner@")) temp_nomem();
607 if (!stralloc_cats(&ueo,host)) temp_nomem();
608 }
609 }
610 if (!stralloc_0(&ueo)) temp_nomem();
611 if (!env_put2("NEWSENDER",ueo.s)) temp_nomem();
612
613 if (!stralloc_ready(&cmds,0)) temp_nomem();
614 cmds.len = 0;
615 if (fd != -1)
616 if (slurpclose(fd,&cmds,256) == -1) temp_nomem();
617
618 if (!cmds.len)
619 {
620 if (!stralloc_copys(&cmds,aliasempty)) temp_nomem();
621 flagforwardonly = 0;
622 }
623 if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
624 if (!stralloc_cats(&cmds,"\n")) temp_nomem();
625
626 numforward = 0;
627 i = 0;
628 for (j = 0;j < cmds.len;++j)
629 if (cmds.s[j] == '\n')
630 {
631 switch(cmds.s[i]) { case '#': case '.': case '/': case '|': break;
632 default: ++numforward; }
633 i = j + 1;
634 }
635
636 recips = (char **) alloc((numforward + 1) * sizeof(char *));
637 if (!recips) temp_nomem();
638 numforward = 0;
639
640 flag99 = 0;
641
642 i = 0;
643 for (j = 0;j < cmds.len;++j)
644 if (cmds.s[j] == '\n')
645 {
646 cmds.s[j] = 0;
647 k = j;
648 while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
649 cmds.s[--k] = 0;
650 switch(cmds.s[i])
651 {
652 case 0: /* k == i */
653 if (i) break;
654 strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)");
655 case '#':
656 break;
657 case '.':
658 case '/':
659 ++count_file;
660 if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)");
661 if (cmds.s[k - 1] == '/')
662 if (flagdoit) maildir(cmds.s + i);
663 else sayit("maildir ",cmds.s + i,k - i);
664 else
665 if (flagdoit) mailfile(cmds.s + i);
666 else sayit("mbox ",cmds.s + i,k - i);
667 break;
668 case '|':
669 ++count_program;
670 if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)");
671 if (flagdoit) mailprogram(cmds.s + i + 1);
672 else sayit("program ",cmds.s + i + 1,k - i - 1);
673 break;
674 case '+':
675 if (str_equal(cmds.s + i + 1,"list"))
676 flagforwardonly = 1;
677 break;
678 case '&':
679 ++i;
680 default:
681 ++count_forward;
682 if (flagdoit) recips[numforward++] = cmds.s + i;
683 else sayit("forward ",cmds.s + i,k - i);
684 break;
685 }
686 i = j + 1;
687 if (flag99) break;
688 }
689
690 if (numforward) if (flagdoit)
691 {
692 recips[numforward] = 0;
693 mailforward(recips);
694 }
695
696 count_print();
697 _exit(0);
698}
diff --git a/qmail-log.5 b/qmail-log.5
new file mode 100644
index 0000000..98cef63
--- /dev/null
+++ b/qmail-log.5
@@ -0,0 +1,261 @@
1.TH qmail-log 5
2.SH NAME
3qmail-log \- the qmail activity record
4.SH DESCRIPTION
5.B qmail-send
6prints a series of lines describing its activities.
7Each possible line is described below.
8.SH "STATUS"
9.TP
10.B status: local \fIl\fB/\fIL\fB remote \fIr\fB/\fIR\fB ...
11.B qmail-send
12is waiting for
13.I l
14local deliveries
15and
16.I r
17remote deliveries.
18The concurrency limits are
19.I L
20and
21.IR R .
22.TP
23.B status: exiting
24.B qmail-send
25is done.
26.SH "FATAL PROBLEMS"
27.TP
28.B alert: cannot start: ...
29.B qmail-send
30is unable to prepare itself for delivering messages;
31it is giving up.
32This normally indicates a serious configuration error,
33but it can be caused by a temporary lack of resources.
34.TP
35.B alert: oh no! lost ...
36One of the other daemons has died.
37.B qmail-send
38will exit as soon as possible.
39.SH "SERIOUS PROBLEMS"
40.TP
41.B alert: unable to append to bounce message...
42.B qmail-send
43is unable to record a permanent failure,
44usually because the disk is full.
45This is a very serious problem;
46.B qmail-send
47cannot proceed without recording the results.
48It will try again in ten seconds.
49.TP
50.B alert: out of memory...
51.B qmail-send
52tried to allocate more memory and failed.
53It will try again in ten seconds.
54.TP
55.B alert: unable to opendir...
56.B qmail-send
57is having trouble reading a file list from disk,
58usually because the system's file descriptor table is full,
59but possibly because permissions are set incorrectly.
60It will try again in ten seconds.
61.TP
62.B alert: unable to switch back...
63.B qmail-send
64was sent SIGHUP,
65and it is unable to reenter the queue directory.
66This is a very serious problem;
67.B qmail-send
68cannot proceed outside the queue directory.
69It will try again in ten seconds.
70.TP
71.B alert: unable to reread...
72.B qmail-send
73was sent SIGHUP,
74but it is unable to read the new controls.
75It will continue operating with the original controls.
76.SH "MESSAGES"
77.TP
78.B new msg \fIm\fB
79.B qmail-send
80is going to preprocess a queued message.
81The message number,
82.IR m ,
83is its disk inode number.
84After a message is removed from the queue,
85its number can be reused immediately.
86.TP
87.B info msg \fIm\fB: bytes \fIb\fB from <\fIs\fB> qp \fIq\fB uid \fIu\fB
88Message
89.I m
90contains
91.I b
92bytes;
93its envelope sender is
94.IR s ;
95it was queued by a user with user ID
96.IR u .
97.I q
98is a long-term queue identifier,
99the process ID of the
100.B qmail-queue
101that queued the message.
102.TP
103.B bounce msg \fIm\fB qp \fIq\fB
104Message
105.I m
106had some delivery failures.
107The long-term queue identifier of the bounce (or double-bounce) message
108is
109.IR q .
110.TP
111.B triple bounce: discarding ...
112Message
113.I m
114had some delivery failures,
115but it is already a double-bounce message,
116so it must be thrown away.
117Triple-bounce messages do not exist.
118.TP
119.B end msg \fIm\fB
120.B qmail-send
121is about to remove
122message
123.I m
124from the queue.
125.SH "DELIVERIES"
126.TP
127.B starting delivery \fId\fB: msg \fIm\fB to ...
128.B qmail-send
129is telling
130.B qmail-lspawn
131or
132.B qmail-rspawn
133to deliver message
134.I m
135to one recipient.
136The delivery number,
137.IR d ,
138starts at 1 and increases by 1 for each new delivery.
139.TP
140.B delivery \fId\fB: success: ...
141Delivery
142.I d
143was successful.
144.TP
145.B delivery \fId\fB: failure: ...
146Delivery
147.I d
148failed permanently.
149The message will bounce.
150.TP
151.B delivery \fId\fB: deferral: ...
152Delivery
153.I d
154failed temporarily.
155This recipient will be retried later.
156.TP
157.B delivery \fId\fB: report mangled, will defer
158There is a serious bug in
159.B qmail-lspawn
160or
161.BR qmail-rspawn .
162This recipient will be retried later.
163.SH "WARNINGS"
164.TP
165.B internal error: delivery report out of range
166.B qmail-lspawn
167or
168.B qmail-rspawn
169has supplied a report on a nonexistent delivery.
170This is a serious bug.
171.TP
172.B qmail-clean unable to clean up ...
173For some reason
174.B qmail-clean
175is unable to remove the indicated file.
176It will try again later.
177.TP
178.B trouble fsyncing ...
179.B qmail-send
180was unable to write to disk the results of preprocessing a queued message.
181It will try again later.
182.TP
183.B trouble in select
184There is an operating system bug.
185.TP
186.B trouble injecting bounce message...
187.B qmail-send
188was unable to queue a bounce message,
189usually because the disk is full.
190It will try again later.
191.TP
192.B trouble marking ...
193.B qmail-send
194was unable to record the result of a successful or permanently
195unsuccessful delivery.
196This means that the delivery will be tried again later.
197.TP
198.B trouble opening ...
199.B qmail-send
200was unable to open the list of local or remote recipients
201for a message.
202It will try again later.
203.TP
204.B trouble reading ...
205Either
206.B qmail-send
207is unable to read a recipient list,
208or it is unable to read the envelope of a queued
209message, or it is out of memory.
210Whatever it was doing, it will try again later.
211.TP
212.B trouble writing to ...
213.B qmail-send
214was unable to preprocess a queued message,
215usually because the disk is full.
216It will try again later.
217.TP
218.B unable to create ...
219.B qmail-send
220was unable to preprocess a queued message,
221usually because the disk is out of inodes.
222It will try again later.
223.TP
224.B unable to open ...
225.B qmail-send
226is unable to read the envelope of a queued message
227for preprocessing.
228It will try again later.
229.TP
230.B unable to start qmail-queue...
231.B qmail-send
232is unable to queue a bounce message,
233usually because the machine is almost out of memory.
234It will try again later.
235.TP
236.B unable to stat ...
237.B qmail-send
238is unable to obtain information about a file that should exist.
239It will try again later.
240.TP
241.B unable to unlink ...
242.B qmail-send
243is unable to remove a file.
244It will try again later.
245.TP
246.B unable to utime ...
247.B qmail-send
248is about to exit,
249and it is unable to record on disk
250the next scheduled delivery time for a message.
251The message will be retried as soon as
252.B qmail-send
253is restarted.
254.TP
255.B unknown record type in ...
256There is a serious bug in either
257.B qmail-queue
258or
259.BR qmail-send .
260.SH "SEE ALSO"
261qmail-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 @@
1.TH qmail-lspawn 8
2.SH NAME
3qmail-lspawn \- schedule local deliveries
4.SH SYNOPSIS
5.B qmail-lspawn
6.I defaultdelivery
7.SH DESCRIPTION
8.B qmail-lspawn
9reads a series of local delivery commands from descriptor 0,
10invokes
11.B qmail-local
12to perform the deliveries,
13and prints the results to descriptor 1.
14It passes
15.I defaultdelivery
16to
17.B qmail-local
18as the default delivery instruction.
19
20.B qmail-lspawn
21invokes
22.B qmail-local
23asynchronously,
24so the results may not be in the same order as the commands.
25
26For each recipient address,
27.B qmail-lspawn
28finds out which local user controls that address.
29It first checks the
30.B qmail-users
31mechanism; if the address is not listed there, it invokes
32.BR qmail-getpw .
33.B qmail-lspawn
34then runs
35.B qmail-local
36under the user's uid and gid.
37It does not set up any supplementary groups.
38
39.B qmail-lspawn
40treats an empty mailbox name as a trash address.
41.SH "SEE ALSO"
42envelopes(5),
43qmail-users(5),
44qmail-getpw(8),
45qmail-send(8),
46qmail-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 @@
1#include "fd.h"
2#include "wait.h"
3#include "prot.h"
4#include "substdio.h"
5#include "stralloc.h"
6#include "scan.h"
7#include "exit.h"
8#include "fork.h"
9#include "error.h"
10#include "cdb.h"
11#include "case.h"
12#include "slurpclose.h"
13#include "auto_qmail.h"
14#include "auto_uids.h"
15#include "qlx.h"
16
17char *aliasempty;
18
19void initialize(argc,argv)
20int argc;
21char **argv;
22{
23 aliasempty = argv[1];
24 if (!aliasempty) _exit(100);
25}
26
27int truncreport = 3000;
28
29void report(ss,wstat,s,len)
30substdio *ss;
31int wstat;
32char *s;
33int len;
34{
35 int i;
36 if (wait_crashed(wstat))
37 { substdio_puts(ss,"Zqmail-local crashed.\n"); return; }
38 switch(wait_exitcode(wstat))
39 {
40 case QLX_CDB:
41 substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); return;
42 case QLX_NOMEM:
43 substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); return;
44 case QLX_SYS:
45 substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); return;
46 case QLX_NOALIAS:
47 substdio_puts(ss,"ZUnable to find alias user!\n"); return;
48 case QLX_ROOT:
49 substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); return;
50 case QLX_USAGE:
51 substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); return;
52 case QLX_NFS:
53 substdio_puts(ss,"ZNFS failure in qmail-local.\n"); return;
54 case QLX_EXECHARD:
55 substdio_puts(ss,"DUnable to run qmail-local.\n"); return;
56 case QLX_EXECSOFT:
57 substdio_puts(ss,"ZUnable to run qmail-local.\n"); return;
58 case QLX_EXECPW:
59 substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); return;
60 case 111: case 71: case 74: case 75:
61 substdio_put(ss,"Z",1); break;
62 case 0:
63 substdio_put(ss,"K",1); break;
64 case 100:
65 default:
66 substdio_put(ss,"D",1); break;
67 }
68
69 for (i = 0;i < len;++i) if (!s[i]) break;
70 substdio_put(ss,s,i);
71}
72
73stralloc lower = {0};
74stralloc nughde = {0};
75stralloc wildchars = {0};
76
77void nughde_get(local)
78char *local;
79{
80 char *(args[3]);
81 int pi[2];
82 int gpwpid;
83 int gpwstat;
84 int r;
85 int fd;
86 int flagwild;
87
88 if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM);
89 if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM);
90 if (!stralloc_0(&lower)) _exit(QLX_NOMEM);
91 case_lowerb(lower.s,lower.len);
92
93 if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM);
94
95 fd = open_read("users/cdb");
96 if (fd == -1)
97 if (errno != error_noent)
98 _exit(QLX_CDB);
99
100 if (fd != -1)
101 {
102 uint32 dlen;
103 unsigned int i;
104
105 r = cdb_seek(fd,"",0,&dlen);
106 if (r != 1) _exit(QLX_CDB);
107 if (!stralloc_ready(&wildchars,(unsigned int) dlen)) _exit(QLX_NOMEM);
108 wildchars.len = dlen;
109 if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) _exit(QLX_CDB);
110
111 i = lower.len;
112 flagwild = 0;
113
114 do
115 {
116 /* i > 0 */
117 if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len))
118 {
119 r = cdb_seek(fd,lower.s,i,&dlen);
120 if (r == -1) _exit(QLX_CDB);
121 if (r == 1)
122 {
123 if (!stralloc_ready(&nughde,(unsigned int) dlen)) _exit(QLX_NOMEM);
124 nughde.len = dlen;
125 if (cdb_bread(fd,nughde.s,nughde.len) == -1) _exit(QLX_CDB);
126 if (flagwild)
127 if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM);
128 if (!stralloc_0(&nughde)) _exit(QLX_NOMEM);
129 close(fd);
130 return;
131 }
132 }
133 --i;
134 flagwild = 1;
135 }
136 while (i);
137
138 close(fd);
139 }
140
141 if (pipe(pi) == -1) _exit(QLX_SYS);
142 args[0] = "bin/qmail-getpw";
143 args[1] = local;
144 args[2] = 0;
145 switch(gpwpid = vfork())
146 {
147 case -1:
148 _exit(QLX_SYS);
149 case 0:
150 if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE);
151 if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE);
152 close(pi[0]);
153 if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS);
154 execv(*args,args);
155 _exit(QLX_EXECPW);
156 }
157 close(pi[1]);
158
159 if (slurpclose(pi[0],&nughde,128) == -1) _exit(QLX_SYS);
160
161 if (wait_pid(&gpwstat,gpwpid) != -1)
162 {
163 if (wait_crashed(gpwstat)) _exit(QLX_SYS);
164 if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat));
165 }
166}
167
168int spawn(fdmess,fdout,s,r,at)
169int fdmess; int fdout;
170char *s; char *r; int at;
171{
172 int f;
173
174 if (!(f = fork()))
175 {
176 char *(args[11]);
177 unsigned long u;
178 int n;
179 int uid;
180 int gid;
181 char *x;
182 unsigned int xlen;
183
184 r[at] = 0;
185 if (!r[0]) _exit(0); /* <> */
186
187 if (chdir(auto_qmail) == -1) _exit(QLX_USAGE);
188
189 nughde_get(r);
190
191 x = nughde.s;
192 xlen = nughde.len;
193
194 args[0] = "bin/qmail-local";
195 args[1] = "--";
196 args[2] = x;
197 n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
198
199 scan_ulong(x,&u);
200 uid = u;
201 n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
202
203 scan_ulong(x,&u);
204 gid = u;
205 n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
206
207 args[3] = x;
208 n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
209
210 args[4] = r;
211 args[5] = x;
212 n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
213
214 args[6] = x;
215 n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n;
216
217 args[7] = r + at + 1;
218 args[8] = s;
219 args[9] = aliasempty;
220 args[10] = 0;
221
222 if (fd_move(0,fdmess) == -1) _exit(QLX_SYS);
223 if (fd_move(1,fdout) == -1) _exit(QLX_SYS);
224 if (fd_copy(2,1) == -1) _exit(QLX_SYS);
225 if (prot_gid(gid) == -1) _exit(QLX_USAGE);
226 if (prot_uid(uid) == -1) _exit(QLX_USAGE);
227 if (!getuid()) _exit(QLX_ROOT);
228
229 execv(*args,args);
230 if (error_temp(errno)) _exit(QLX_EXECSOFT);
231 _exit(QLX_EXECHARD);
232 }
233 return f;
234}
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 @@
1.TH qmail-newmrh 8
2.SH NAME
3qmail-newmrh \- prepare morercpthosts for qmail-smtpd
4.SH SYNOPSIS
5.B qmail-newmrh
6.SH DESCRIPTION
7.B qmail-newmrh
8reads the instructions in
9.B QMAILHOME/control/morercpthosts
10and writes them into
11.B QMAILHOME/control/morercpthosts.cdb
12in a binary format suited
13for quick access by
14.BR qmail-smtpd .
15
16If there is a problem with
17.BR control/morercpthosts ,
18.B qmail-newmrh
19complains and leaves
20.B control/morercpthosts.cdb
21alone.
22
23.B qmail-newmrh
24ensures that
25.B control/morercpthosts.cdb
26is updated atomically,
27so
28.B qmail-smtpd
29never has to wait for
30.B qmail-newmrh
31to finish.
32However,
33.B qmail-newmrh
34makes no attempt to protect against two simultaneous updates of
35.BR control/morercpthosts.cdb .
36
37The binary
38.B control/morercpthosts.cdb
39format is portable across machines.
40.SH "SEE ALSO"
41qmail-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 @@
1#include "strerr.h"
2#include "stralloc.h"
3#include "substdio.h"
4#include "getln.h"
5#include "exit.h"
6#include "readwrite.h"
7#include "open.h"
8#include "auto_qmail.h"
9#include "cdbmss.h"
10
11#define FATAL "qmail-newmrh: fatal: "
12
13void die_read()
14{
15 strerr_die2sys(111,FATAL,"unable to read control/morercpthosts: ");
16}
17void die_write()
18{
19 strerr_die2sys(111,FATAL,"unable to write to control/morercpthosts.tmp: ");
20}
21
22char inbuf[1024];
23substdio ssin;
24
25int fd;
26int fdtemp;
27
28struct cdbmss cdbmss;
29stralloc line = {0};
30int match;
31
32void main()
33{
34 umask(033);
35 if (chdir(auto_qmail) == -1)
36 strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
37
38 fd = open_read("control/morercpthosts");
39 if (fd == -1) die_read();
40
41 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
42
43 fdtemp = open_trunc("control/morercpthosts.tmp");
44 if (fdtemp == -1) die_write();
45
46 if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write();
47
48 for (;;) {
49 if (getln(&ssin,&line,&match,'\n') != 0) die_read();
50 case_lowerb(line.s,line.len);
51 while (line.len) {
52 if (line.s[line.len - 1] == ' ') { --line.len; continue; }
53 if (line.s[line.len - 1] == '\n') { --line.len; continue; }
54 if (line.s[line.len - 1] == '\t') { --line.len; continue; }
55 if (line.s[0] != '#')
56 if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1)
57 die_write();
58 break;
59 }
60 if (!match) break;
61 }
62
63 if (cdbmss_finish(&cdbmss) == -1) die_write();
64 if (fsync(fdtemp) == -1) die_write();
65 if (close(fdtemp) == -1) die_write(); /* NFS stupidity */
66 if (rename("control/morercpthosts.tmp","control/morercpthosts.cdb") == -1)
67 strerr_die2sys(111,FATAL,"unable to move control/morercpthosts.tmp to control/morercpthosts.cdb");
68
69 _exit(0);
70}
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 @@
1.TH qmail-newu 8
2.SH NAME
3qmail-newu \- prepare address assignments for qmail-lspawn
4.SH SYNOPSIS
5.B qmail-newu
6.SH DESCRIPTION
7.B qmail-newu
8reads the assignments in
9.B QMAILHOME/users/assign
10and writes them into
11.B QMAILHOME/users/cdb
12in a binary format suited
13for quick access by
14.BR qmail-lspawn .
15
16If there is a problem with
17.BR users/assign ,
18.B qmail-newu
19complains and leaves
20.B users/cdb
21alone.
22
23.B qmail-newu
24ensures that
25.B users/cdb
26is updated atomically,
27so
28.B qmail-lspawn
29never has to wait for
30.B qmail-newu
31to finish.
32However,
33.B qmail-newu
34makes no attempt to protect against two simultaneous updates of
35.BR users/cdb .
36
37The binary
38.B users/cdb
39format is portable across machines.
40.SH "SEE ALSO"
41qmail-users(5),
42qmail-lspawn(8),
43qmail-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 @@
1#include "stralloc.h"
2#include "subfd.h"
3#include "getln.h"
4#include "substdio.h"
5#include "cdbmss.h"
6#include "exit.h"
7#include "readwrite.h"
8#include "open.h"
9#include "error.h"
10#include "case.h"
11#include "auto_qmail.h"
12
13void die_temp() { _exit(111); }
14
15void die_chdir()
16{
17 substdio_putsflush(subfderr,"qmail-newu: fatal: unable to chdir\n");
18 die_temp();
19}
20void die_nomem()
21{
22 substdio_putsflush(subfderr,"qmail-newu: fatal: out of memory\n");
23 die_temp();
24}
25void die_opena()
26{
27 substdio_putsflush(subfderr,"qmail-newu: fatal: unable to open users/assign\n");
28 die_temp();
29}
30void die_reada()
31{
32 substdio_putsflush(subfderr,"qmail-newu: fatal: unable to read users/assign\n");
33 die_temp();
34}
35void die_format()
36{
37 substdio_putsflush(subfderr,"qmail-newu: fatal: bad format in users/assign\n");
38 die_temp();
39}
40void die_opent()
41{
42 substdio_putsflush(subfderr,"qmail-newu: fatal: unable to open users/cdb.tmp\n");
43 die_temp();
44}
45void die_writet()
46{
47 substdio_putsflush(subfderr,"qmail-newu: fatal: unable to write users/cdb.tmp\n");
48 die_temp();
49}
50void die_rename()
51{
52 substdio_putsflush(subfderr,"qmail-newu: fatal: unable to move users/cdb.tmp to users/cdb\n");
53 die_temp();
54}
55
56struct cdbmss cdbmss;
57stralloc key = {0};
58stralloc data = {0};
59
60char inbuf[1024];
61substdio ssin;
62
63int fd;
64int fdtemp;
65
66stralloc line = {0};
67int match;
68
69stralloc wildchars = {0};
70
71void main()
72{
73 int i;
74 int numcolons;
75
76 umask(033);
77 if (chdir(auto_qmail) == -1) die_chdir();
78
79 fd = open_read("users/assign");
80 if (fd == -1) die_opena();
81
82 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
83
84 fdtemp = open_trunc("users/cdb.tmp");
85 if (fdtemp == -1) die_opent();
86
87 if (cdbmss_start(&cdbmss,fdtemp) == -1) die_writet();
88
89 if (!stralloc_copys(&wildchars,"")) die_nomem();
90
91 for (;;) {
92 if (getln(&ssin,&line,&match,'\n') != 0) die_reada();
93 if (line.len && (line.s[0] == '.')) break;
94 if (!match) die_format();
95
96 if (byte_chr(line.s,line.len,'\0') < line.len) die_format();
97 i = byte_chr(line.s,line.len,':');
98 if (i == line.len) die_format();
99 if (i == 0) die_format();
100 if (!stralloc_copys(&key,"!")) die_nomem();
101 if (line.s[0] == '+') {
102 if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem();
103 case_lowerb(key.s,key.len);
104 if (i >= 2)
105 if (byte_chr(wildchars.s,wildchars.len,line.s[i - 1]) == wildchars.len)
106 if (!stralloc_append(&wildchars,line.s + i - 1)) die_nomem();
107 }
108 else {
109 if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem();
110 if (!stralloc_0(&key)) die_nomem();
111 case_lowerb(key.s,key.len);
112 }
113
114 if (!stralloc_copyb(&data,line.s + i + 1,line.len - i - 1)) die_nomem();
115
116 numcolons = 0;
117 for (i = 0;i < data.len;++i)
118 if (data.s[i] == ':') {
119 data.s[i] = 0;
120 if (++numcolons == 6)
121 break;
122 }
123 if (numcolons < 6) die_format();
124 data.len = i;
125
126 if (cdbmss_add(&cdbmss,key.s,key.len,data.s,data.len) == -1) die_writet();
127 }
128
129 if (cdbmss_add(&cdbmss,"",0,wildchars.s,wildchars.len) == -1) die_writet();
130
131 if (cdbmss_finish(&cdbmss) == -1) die_writet();
132 if (fsync(fdtemp) == -1) die_writet();
133 if (close(fdtemp) == -1) die_writet(); /* NFS stupidity */
134 if (rename("users/cdb.tmp","users/cdb") == -1) die_rename();
135
136 _exit(0);
137}
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 @@
1.TH qmail-pop3d 8
2.SH NAME
3qmail-pop3d \- distribute mail via POP
4.SH SYNOPSIS
5.B qmail-pop3d
6.I maildirname
7.SH DESCRIPTION
8.B qmail-pop3d
9lets a user read and delete his mail through the network.
10
11Mail is stored in a
12.B maildir
13called
14.IR maildirname ,
15normally
16.BR Maildir ,
17in the user's home directory.
18
19.B qmail-pop3d
20is normally invoked
21under
22.BR qmail-popup ,
23which reads a username and password,
24and
25.BR /bin/checkpassword ,
26which checks the password and sets up environment variables.
27
28.B qmail-pop3d
29has a 20-minute idle timeout.
30
31.B qmail-pop3d
32supports UIDL, TOP, and LAST.
33
34.B qmail-pop3d
35appends an extra blank line to every message
36to work around serious bugs in certain clients.
37
38.B qmail-pop3d
39is based on a program contributed by Russ Nelson.
40.SH "SEE ALSO"
41maildir(5),
42qmail-local(8),
43qmail-popup(8)
diff --git a/qmail-pop3d.c b/qmail-pop3d.c
new file mode 100644
index 0000000..51976c2
--- /dev/null
+++ b/qmail-pop3d.c
@@ -0,0 +1,305 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "commands.h"
4#include "sig.h"
5#include "getln.h"
6#include "stralloc.h"
7#include "substdio.h"
8#include "alloc.h"
9#include "open.h"
10#include "prioq.h"
11#include "scan.h"
12#include "fmt.h"
13#include "str.h"
14#include "exit.h"
15#include "maildir.h"
16#include "readwrite.h"
17#include "timeoutread.h"
18#include "timeoutwrite.h"
19
20void die() { _exit(0); }
21
22int saferead(fd,buf,len) int fd; char *buf; int len;
23{
24 int r;
25 r = timeoutread(1200,fd,buf,len);
26 if (r <= 0) die();
27 return r;
28}
29
30int safewrite(fd,buf,len) int fd; char *buf; int len;
31{
32 int r;
33 r = timeoutwrite(1200,fd,buf,len);
34 if (r <= 0) die();
35 return r;
36}
37
38char ssoutbuf[1024];
39substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
40
41char ssinbuf[128];
42substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
43
44void put(buf,len) char *buf; int len;
45{
46 substdio_put(&ssout,buf,len);
47}
48void puts(s) char *s;
49{
50 substdio_puts(&ssout,s);
51}
52void flush()
53{
54 substdio_flush(&ssout);
55}
56void err(s) char *s;
57{
58 puts("-ERR ");
59 puts(s);
60 puts("\r\n");
61 flush();
62}
63
64void die_nomem() { err("out of memory"); die(); }
65void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
66void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
67
68void err_syntax() { err("syntax error"); }
69void err_unimpl() { err("unimplemented"); }
70void err_deleted() { err("already deleted"); }
71void err_nozero() { err("messages are counted from 1"); }
72void err_toobig() { err("not that many messages"); }
73void err_nosuch() { err("unable to open that message"); }
74void err_nounlink() { err("unable to unlink all deleted messages"); }
75
76void okay() { puts("+OK \r\n"); flush(); }
77
78void printfn(fn) char *fn;
79{
80 fn += 4;
81 put(fn,str_chr(fn,':'));
82}
83
84char strnum[FMT_ULONG];
85stralloc line = {0};
86
87void blast(ssfrom,limit)
88substdio *ssfrom;
89unsigned long limit;
90{
91 int match;
92 int inheaders = 1;
93
94 for (;;) {
95 if (getln(ssfrom,&line,&match,'\n') != 0) die();
96 if (!match && !line.len) break;
97 if (match) --line.len; /* no way to pass this info over POP */
98 if (limit) if (!inheaders) if (!--limit) break;
99 if (!line.len)
100 inheaders = 0;
101 else
102 if (line.s[0] == '.')
103 put(".",1);
104 put(line.s,line.len);
105 put("\r\n",2);
106 if (!match) break;
107 }
108 put("\r\n.\r\n",5);
109 flush();
110}
111
112stralloc filenames = {0};
113prioq pq = {0};
114
115struct message {
116 int flagdeleted;
117 unsigned long size;
118 char *fn;
119} *m;
120int numm;
121
122int last = 0;
123
124void getlist()
125{
126 struct prioq_elt pe;
127 struct stat st;
128 int i;
129
130 maildir_clean(&line);
131 if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan();
132
133 numm = pq.p ? pq.len : 0;
134 m = (struct message *) alloc(numm * sizeof(struct message));
135 if (!m) die_nomem();
136
137 for (i = 0;i < numm;++i) {
138 if (!prioq_min(&pq,&pe)) { numm = i; break; }
139 prioq_delmin(&pq);
140 m[i].fn = filenames.s + pe.id;
141 m[i].flagdeleted = 0;
142 if (stat(m[i].fn,&st) == -1)
143 m[i].size = 0;
144 else
145 m[i].size = st.st_size;
146 }
147}
148
149void pop3_stat()
150{
151 int i;
152 unsigned long total;
153
154 total = 0;
155 for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
156 puts("+OK ");
157 put(strnum,fmt_uint(strnum,numm));
158 puts(" ");
159 put(strnum,fmt_ulong(strnum,total));
160 puts("\r\n");
161 flush();
162}
163
164void pop3_rset()
165{
166 int i;
167 for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
168 last = 0;
169 okay();
170}
171
172void pop3_last()
173{
174 puts("+OK ");
175 put(strnum,fmt_uint(strnum,last));
176 puts("\r\n");
177 flush();
178}
179
180void pop3_quit()
181{
182 int i;
183 for (i = 0;i < numm;++i)
184 if (m[i].flagdeleted) {
185 if (unlink(m[i].fn) == -1) err_nounlink();
186 }
187 else
188 if (str_start(m[i].fn,"new/")) {
189 if (!stralloc_copys(&line,"cur/")) die_nomem();
190 if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem();
191 if (!stralloc_cats(&line,":2,")) die_nomem();
192 if (!stralloc_0(&line)) die_nomem();
193 rename(m[i].fn,line.s); /* if it fails, bummer */
194 }
195 okay();
196 die();
197}
198
199int msgno(arg) char *arg;
200{
201 unsigned long u;
202 if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
203 if (!u) { err_nozero(); return -1; }
204 --u;
205 if (u >= numm) { err_toobig(); return -1; }
206 if (m[u].flagdeleted) { err_deleted(); return -1; }
207 return u;
208}
209
210void pop3_dele(arg) char *arg;
211{
212 int i;
213 i = msgno(arg);
214 if (i == -1) return;
215 m[i].flagdeleted = 1;
216 if (i + 1 > last) last = i + 1;
217 okay();
218}
219
220void list(i,flaguidl)
221int i;
222int flaguidl;
223{
224 put(strnum,fmt_uint(strnum,i + 1));
225 puts(" ");
226 if (flaguidl) printfn(m[i].fn);
227 else put(strnum,fmt_ulong(strnum,m[i].size));
228 puts("\r\n");
229}
230
231void dolisting(arg,flaguidl) char *arg; int flaguidl;
232{
233 unsigned int i;
234 if (*arg) {
235 i = msgno(arg);
236 if (i == -1) return;
237 puts("+OK ");
238 list(i,flaguidl);
239 }
240 else {
241 okay();
242 for (i = 0;i < numm;++i)
243 if (!m[i].flagdeleted)
244 list(i,flaguidl);
245 puts(".\r\n");
246 }
247 flush();
248}
249
250void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
251void pop3_list(arg) char *arg; { dolisting(arg,0); }
252
253substdio ssmsg; char ssmsgbuf[1024];
254
255void pop3_top(arg) char *arg;
256{
257 int i;
258 unsigned long limit;
259 int fd;
260
261 i = msgno(arg);
262 if (i == -1) return;
263
264 arg += scan_ulong(arg,&limit);
265 while (*arg == ' ') ++arg;
266 if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
267
268 fd = open_read(m[i].fn);
269 if (fd == -1) { err_nosuch(); return; }
270 okay();
271 substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
272 blast(&ssmsg,limit);
273 close(fd);
274}
275
276struct commands pop3commands[] = {
277 { "quit", pop3_quit, 0 }
278, { "stat", pop3_stat, 0 }
279, { "list", pop3_list, 0 }
280, { "uidl", pop3_uidl, 0 }
281, { "dele", pop3_dele, 0 }
282, { "retr", pop3_top, 0 }
283, { "rset", pop3_rset, 0 }
284, { "last", pop3_last, 0 }
285, { "top", pop3_top, 0 }
286, { "noop", okay, 0 }
287, { 0, err_unimpl, 0 }
288} ;
289
290void main(argc,argv)
291int argc;
292char **argv;
293{
294 sig_alarmcatch(die);
295 sig_pipeignore();
296
297 if (!argv[1]) die_nomaildir();
298 if (chdir(argv[1]) == -1) die_nomaildir();
299
300 getlist();
301
302 okay();
303 commands(&ssin,pop3commands);
304 die();
305}
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 @@
1.TH qmail-popup 8
2.SH NAME
3qmail-popup \- read a POP username and password
4.SH SYNOPSIS
5.B qmail-popup
6.I hostname
7.I subprogram
8.SH DESCRIPTION
9.B qmail-popup
10reads a POP username and password from the network.
11It then runs
12.IR subprogram .
13
14.B qmail-popup
15is most commonly invoked from
16.B inetd
17as
18
19.EX
20 qmail-popup CHANGEME checkpassword qmail-pop3d Maildir
21.EE
22
23with
24CHANGEME
25replaced by the fully qualified domain name of the local host.
26
27.B qmail-popup
28expects descriptor 0 to read from the network
29and descriptor 1 to write to the network.
30It reads a username and password from descriptor 0
31in POP's USER-PASS style or APOP style.
32It invokes
33.IR subprogram ,
34with the same descriptors 0 and 1;
35descriptor 2 writing to the network;
36and descriptor 3 reading the username, a 0 byte, the password,
37another 0 byte,
38an APOP timestamp derived from
39.IR hostname ,
40and a final 0 byte.
41.B qmail-popup
42then waits for
43.I subprogram
44to finish.
45It prints an error message if
46.I subprogram
47crashes or exits nonzero.
48
49.B qmail-popup
50should be used only within
51a secure network.
52Otherwise an eavesdropper can steal passwords.
53Even if you use APOP,
54an active attacker can still take over the connection
55and wreak havoc.
56
57.B qmail-popup
58has a 20-minute idle timeout.
59
60.B qmail-popup
61is based on a program contributed by Russ Nelson.
62.SH "SEE ALSO"
63maildir(5),
64qmail-local(8),
65qmail-pop3d(8)
diff --git a/qmail-popup.c b/qmail-popup.c
new file mode 100644
index 0000000..fbcc99b
--- /dev/null
+++ b/qmail-popup.c
@@ -0,0 +1,183 @@
1#include "commands.h"
2#include "fd.h"
3#include "sig.h"
4#include "stralloc.h"
5#include "substdio.h"
6#include "alloc.h"
7#include "wait.h"
8#include "str.h"
9#include "byte.h"
10#include "now.h"
11#include "fmt.h"
12#include "exit.h"
13#include "readwrite.h"
14#include "timeoutread.h"
15#include "timeoutwrite.h"
16
17void die() { _exit(1); }
18
19int saferead(fd,buf,len) int fd; char *buf; int len;
20{
21 int r;
22 r = timeoutread(1200,fd,buf,len);
23 if (r <= 0) die();
24 return r;
25}
26
27int safewrite(fd,buf,len) int fd; char *buf; int len;
28{
29 int r;
30 r = timeoutwrite(1200,fd,buf,len);
31 if (r <= 0) die();
32 return r;
33}
34
35char ssoutbuf[128];
36substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
37
38char ssinbuf[128];
39substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
40
41void puts(s) char *s;
42{
43 substdio_puts(&ssout,s);
44}
45void flush()
46{
47 substdio_flush(&ssout);
48}
49void err(s) char *s;
50{
51 puts("-ERR ");
52 puts(s);
53 puts("\r\n");
54 flush();
55}
56
57void die_usage() { err("usage: popup hostname subprogram"); die(); }
58void die_nomem() { err("out of memory"); die(); }
59void die_pipe() { err("unable to open pipe"); die(); }
60void die_write() { err("unable to write pipe"); die(); }
61void die_fork() { err("unable to fork"); die(); }
62void die_childcrashed() { err("aack, child crashed"); }
63void die_badauth() { err("authorization failed"); }
64
65void err_syntax() { err("syntax error"); }
66void err_wantuser() { err("USER first"); }
67void err_authoriz() { err("authorization first"); }
68
69void okay() { puts("+OK \r\n"); flush(); }
70void pop3_quit() { okay(); die(); }
71
72
73char unique[FMT_ULONG + FMT_ULONG + 3];
74char *hostname;
75stralloc username = {0};
76int seenuser = 0;
77char **childargs;
78substdio ssup;
79char upbuf[128];
80
81
82void doanddie(user,userlen,pass)
83char *user;
84unsigned int userlen; /* including 0 byte */
85char *pass;
86{
87 int child;
88 int wstat;
89 int pi[2];
90
91 if (fd_copy(2,1) == -1) die_pipe();
92 close(3);
93 if (pipe(pi) == -1) die_pipe();
94 if (pi[0] != 3) die_pipe();
95 switch(child = fork()) {
96 case -1:
97 die_fork();
98 case 0:
99 close(pi[1]);
100 sig_pipedefault();
101 execvp(*childargs,childargs);
102 _exit(1);
103 }
104 close(pi[0]);
105 substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
106 if (substdio_put(&ssup,user,userlen) == -1) die_write();
107 if (substdio_put(&ssup,pass,str_len(pass) + 1) == -1) die_write();
108 if (substdio_puts(&ssup,"<") == -1) die_write();
109 if (substdio_puts(&ssup,unique) == -1) die_write();
110 if (substdio_puts(&ssup,hostname) == -1) die_write();
111 if (substdio_put(&ssup,">",2) == -1) die_write();
112 if (substdio_flush(&ssup) == -1) die_write();
113 close(pi[1]);
114 byte_zero(pass,str_len(pass));
115 byte_zero(upbuf,sizeof upbuf);
116 if (wait_pid(&wstat,child) == -1) die();
117 if (wait_crashed(wstat)) die_childcrashed();
118 if (wait_exitcode(wstat)) die_badauth();
119 die();
120}
121void pop3_greet()
122{
123 char *s;
124 s = unique;
125 s += fmt_uint(s,getpid());
126 *s++ = '.';
127 s += fmt_ulong(s,(unsigned long) now());
128 *s++ = '@';
129 *s++ = 0;
130 puts("+OK <");
131 puts(unique);
132 puts(hostname);
133 puts(">\r\n");
134 flush();
135}
136void pop3_user(arg) char *arg;
137{
138 if (!*arg) { err_syntax(); return; }
139 okay();
140 seenuser = 1;
141 if (!stralloc_copys(&username,arg)) die_nomem();
142 if (!stralloc_0(&username)) die_nomem();
143}
144void pop3_pass(arg) char *arg;
145{
146 if (!seenuser) { err_wantuser(); return; }
147 if (!*arg) { err_syntax(); return; }
148 doanddie(username.s,username.len,arg);
149}
150void pop3_apop(arg) char *arg;
151{
152 char *space;
153 space = arg + str_chr(arg,' ');
154 if (!*space) { err_syntax(); return; }
155 *space++ = 0;
156 doanddie(arg,space - arg,space);
157}
158
159struct commands pop3commands[] = {
160 { "user", pop3_user, 0 }
161, { "pass", pop3_pass, 0 }
162, { "apop", pop3_apop, 0 }
163, { "quit", pop3_quit, 0 }
164, { "noop", okay, 0 }
165, { 0, err_authoriz, 0 }
166} ;
167
168void main(argc,argv)
169int argc;
170char **argv;
171{
172 sig_alarmcatch(die);
173 sig_pipeignore();
174
175 hostname = argv[1];
176 if (!hostname) die_usage();
177 childargs = argv + 2;
178 if (!*childargs) die_usage();
179
180 pop3_greet();
181 commands(&ssin,pop3commands);
182 die();
183}
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 @@
1.TH qmail-pw2u 8
2.SH NAME
3qmail-pw2u \- build address assignments from a passwd file
4.SH SYNOPSIS
5.B qmail-pw2u
6[
7.B \-/ohHuUC
8]
9[
10.B \-c\fIchar
11]
12.SH DESCRIPTION
13.B qmail-pw2u
14reads a V7-format passwd file from standard input
15and prints a
16.BR qmail-users -format
17assignment file.
18
19A V7-format passwd file is a series of lines.
20Each line has the format
21
22.EX
23 user:password:uid:gid:gecos:home:shell
24.EE
25
26where
27.I user
28is an account name,
29.I uid
30and
31.I gid
32are the user id and group id of that account,
33and
34.I home
35is the account's home directory.
36.IR password ,
37.IR gecos ,
38and
39.I shell
40are ignored by
41.BR qmail-pw2u .
42
43If you put the output of
44.B qmail-pw2u
45into
46.BR QMAILHOME/users/assign ,
47and then run
48.BR qmail-newu ,
49.B qmail-lspawn
50will obey the assignments printed by
51.BR qmail-pw2u .
52.B WARNING:
53After changing any users, uids, gids, or home directories
54in your passwd file,
55you must run
56.B qmail-pw2u
57and
58.B qmail-newu
59again if you want
60.B qmail-lspawn
61to see the changes.
62.SH RULES
63By default,
64.B qmail-pw2u
65follows the same rules as
66.BR qmail-getpw .
67It skips
68.I user
69if (1)
70.I uid
71is zero,
72(2)
73.I home
74does not exist,
75(3)
76.I user
77does not own
78.IR home ,
79or
80(4)
81.I user
82contains uppercase letters.
83It then gives each remaining
84.I user
85control over the basic
86.I user
87address and
88all addresses of the form
89.IR user\fBBREAK\fIanything .
90A catch-all user,
91.BR alias ,
92controls all other addresses.
93
94You may change these rules by setting up files in
95.BR QMAILHOME/users :
96.TP
97.B include
98Allowed users, one per line.
99If
100.B include
101exists, and
102.I user
103is not listed in
104.BR include ,
105.I user
106is ignored.
107.TP
108.B exclude
109Ignored users, one per line.
110If
111.B exclude
112exists, and
113.I user
114is listed in
115.BR exclude ,
116.I user
117is ignored.
118.TP
119.B mailnames
120Replacement names for users.
121Each line has the form
122
123.EX
124 user:mailname1:mailname2:...
125.EE
126
127The addresses
128.I mailname1
129and
130.I mailname1\fBBREAK\fIext
131and
132.I mailname2
133and so on will be delivered
134to
135.IR user .
136
137.B WARNING:
138The addresses
139.I user
140and
141.I user\fBBREAK\fIext
142will not be delivered to
143.I user
144unless
145.I user
146is listed as one of the
147.IR mailname s.
148
149A line in
150.B mailnames
151is silently ignored if the user does not exist.
152.TP
153.B subusers
154Extra addresses.
155Each line has the form
156
157.EX
158 sub:user:pre:
159.EE
160
161.I sub
162will be handled by
163.IR home\fB/.qmail\-\fIpre ,
164where
165.I home
166is
167.IR user 's
168home directory;
169.I sub\fBBREAK\fIext
170will be handled by
171.IR home\fB/.qmail\-\fIpre\fB\-\fIext .
172.TP
173.B append
174Extra assignments,
175printed at the end of
176.BR qmail-pw2u 's
177output.
178.SH OPTIONS
179.TP
180.B \-o
181(Default.)
182Skip
183.I user
184if
185.I home
186does not exist (or is not visible to
187.BR qmail-pw2u ).
188Skip
189.I user
190if
191.I home
192is not owned by
193.IR user .
194.TP
195.B \-h
196Stop if
197.I home
198does not exist.
199This is appropriate if every user is supposed to have a home directory.
200Skip
201.I user
202if
203.I home
204is not owned by
205.IR user .
206.TP
207.B \-H
208Do not check the existence or ownership of
209.IR home .
210.TP
211.B \-U
212(Default.)
213Skip
214.I user
215if there are any uppercase letters in
216.IR user .
217.TP
218.B \-u
219Allow uppercase letters in
220.IR user .
221.TP
222.B \-c\fIchar
223Use
224.I char
225as the user-extension delimiter
226in place of
227.BR BREAK .
228.TP
229.B \-C
230Disable the user-extension mechanism.
231.TP
232.B \-/
233Use
234.IR home\fB/.qmail\-/ ...
235instead of
236.IR home\fB/.qmail\- ...
237.SH "SEE ALSO"
238qmail-users(5),
239qmail-lspawn(8),
240qmail-newu(8),
241qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "substdio.h"
4#include "readwrite.h"
5#include "subfd.h"
6#include "sgetopt.h"
7#include "control.h"
8#include "constmap.h"
9#include "stralloc.h"
10#include "fmt.h"
11#include "str.h"
12#include "scan.h"
13#include "open.h"
14#include "error.h"
15#include "getln.h"
16#include "auto_break.h"
17#include "auto_qmail.h"
18#include "auto_usera.h"
19
20void die_chdir()
21{
22 substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to chdir\n");
23 _exit(111);
24}
25void die_nomem()
26{
27 substdio_putsflush(subfderr,"qmail-pw2u: fatal: out of memory\n");
28 _exit(111);
29}
30void die_read()
31{
32 substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to read input\n");
33 _exit(111);
34}
35void die_write()
36{
37 substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to write output\n");
38 _exit(111);
39}
40void die_control()
41{
42 substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to read controls\n");
43 _exit(111);
44}
45void die_alias()
46{
47 substdio_puts(subfderr,"qmail-pw2u: fatal: unable to find ");
48 substdio_puts(subfderr,auto_usera);
49 substdio_puts(subfderr," user\n");
50 substdio_flush(subfderr);
51 _exit(111);
52}
53void die_home(fn) char *fn;
54{
55 substdio_puts(subfderr,"qmail-pw2u: fatal: unable to stat ");
56 substdio_puts(subfderr,fn);
57 substdio_puts(subfderr,"\n");
58 substdio_flush(subfderr);
59 _exit(111);
60}
61void die_user(s,len) char *s; unsigned int len;
62{
63 substdio_puts(subfderr,"qmail-pw2u: fatal: unable to find ");
64 substdio_put(subfderr,s,len);
65 substdio_puts(subfderr," user for subuser\n");
66 substdio_flush(subfderr);
67 _exit(111);
68}
69
70char *dashcolon = "-:";
71int flagalias = 0;
72int flagnoupper = 1;
73int homestrategy = 2;
74/* 2: skip if home does not exist; skip if home is not owned by user */
75/* 1: stop if home does not exist; skip if home is not owned by user */
76/* 0: don't worry about home */
77
78int okincl; stralloc incl = {0}; struct constmap mapincl;
79int okexcl; stralloc excl = {0}; struct constmap mapexcl;
80int okmana; stralloc mana = {0}; struct constmap mapmana;
81
82stralloc allusers = {0}; struct constmap mapuser;
83
84stralloc uugh = {0};
85stralloc user = {0};
86stralloc uidstr = {0};
87stralloc gidstr = {0};
88stralloc home = {0};
89unsigned long uid;
90
91stralloc line = {0};
92
93void doaccount()
94{
95 struct stat st;
96 int i;
97 char *mailnames;
98 char *x;
99 unsigned int xlen;
100
101 if (byte_chr(line.s,line.len,'\0') < line.len) return;
102
103 x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return;
104 if (!stralloc_copyb(&user,x,i)) die_nomem();
105 if (!stralloc_0(&user)) die_nomem();
106 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
107 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
108 if (!stralloc_copyb(&uidstr,x,i)) die_nomem();
109 if (!stralloc_0(&uidstr)) die_nomem();
110 scan_ulong(uidstr.s,&uid);
111 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
112 if (!stralloc_copyb(&gidstr,x,i)) die_nomem();
113 if (!stralloc_0(&gidstr)) die_nomem();
114 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
115 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
116 if (!stralloc_copyb(&home,x,i)) die_nomem();
117 if (!stralloc_0(&home)) die_nomem();
118
119 if (!uid) return;
120 if (flagnoupper)
121 for (i = 0;i < user.len;++i)
122 if ((user.s[i] >= 'A') && (user.s[i] <= 'Z'))
123 return;
124 if (okincl)
125 if (!constmap(&mapincl,user.s,user.len - 1))
126 return;
127 if (okexcl)
128 if (constmap(&mapexcl,user.s,user.len - 1))
129 return;
130 if (homestrategy) {
131 if (stat(home.s,&st) == -1) {
132 if (errno != error_noent) die_home(home.s);
133 if (homestrategy == 1) die_home(home.s);
134 return;
135 }
136 if (st.st_uid != uid) return;
137 }
138
139 if (!stralloc_copys(&uugh,":")) die_nomem();
140 if (!stralloc_cats(&uugh,user.s)) die_nomem();
141 if (!stralloc_cats(&uugh,":")) die_nomem();
142 if (!stralloc_cats(&uugh,uidstr.s)) die_nomem();
143 if (!stralloc_cats(&uugh,":")) die_nomem();
144 if (!stralloc_cats(&uugh,gidstr.s)) die_nomem();
145 if (!stralloc_cats(&uugh,":")) die_nomem();
146 if (!stralloc_cats(&uugh,home.s)) die_nomem();
147 if (!stralloc_cats(&uugh,":")) die_nomem();
148
149 /* XXX: avoid recording in allusers unless sub actually needs it */
150 if (!stralloc_cats(&allusers,user.s)) die_nomem();
151 if (!stralloc_cats(&allusers,":")) die_nomem();
152 if (!stralloc_catb(&allusers,uugh.s,uugh.len)) die_nomem();
153 if (!stralloc_0(&allusers)) die_nomem();
154
155 if (str_equal(user.s,auto_usera)) {
156 if (substdio_puts(subfdout,"+") == -1) die_write();
157 if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
158 if (substdio_puts(subfdout,dashcolon) == -1) die_write();
159 if (substdio_puts(subfdout,":\n") == -1) die_write();
160 flagalias = 1;
161 }
162
163 mailnames = 0;
164 if (okmana)
165 mailnames = constmap(&mapmana,user.s,user.len - 1);
166 if (!mailnames)
167 mailnames = user.s;
168
169 for (;;) {
170 while (*mailnames == ':') ++mailnames;
171 if (!*mailnames) break;
172
173 i = str_chr(mailnames,':');
174
175 if (substdio_puts(subfdout,"=") == -1) die_write();
176 if (substdio_put(subfdout,mailnames,i) == -1) die_write();
177 if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
178 if (substdio_puts(subfdout,"::\n") == -1) die_write();
179
180 if (*auto_break) {
181 if (substdio_puts(subfdout,"+") == -1) die_write();
182 if (substdio_put(subfdout,mailnames,i) == -1) die_write();
183 if (substdio_put(subfdout,auto_break,1) == -1) die_write();
184 if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write();
185 if (substdio_puts(subfdout,dashcolon) == -1) die_write();
186 if (substdio_puts(subfdout,":\n") == -1) die_write();
187 }
188
189 mailnames += i;
190 }
191}
192
193stralloc sub = {0};
194
195void dosubuser()
196{
197 int i;
198 char *x;
199 unsigned int xlen;
200 char *uugh;
201
202 x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return;
203 if (!stralloc_copyb(&sub,x,i)) die_nomem();
204 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
205 uugh = constmap(&mapuser,x,i);
206 if (!uugh) die_user(x,i);
207 ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return;
208
209 if (substdio_puts(subfdout,"=") == -1) die_write();
210 if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write();
211 if (substdio_puts(subfdout,uugh) == -1) die_write();
212 if (substdio_puts(subfdout,dashcolon) == -1) die_write();
213 if (substdio_put(subfdout,x,i) == -1) die_write();
214 if (substdio_puts(subfdout,":\n") == -1) die_write();
215
216 if (*auto_break) {
217 if (substdio_puts(subfdout,"+") == -1) die_write();
218 if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write();
219 if (substdio_put(subfdout,auto_break,1) == -1) die_write();
220 if (substdio_puts(subfdout,uugh) == -1) die_write();
221 if (substdio_puts(subfdout,dashcolon) == -1) die_write();
222 if (substdio_put(subfdout,x,i) == -1) die_write();
223 if (substdio_puts(subfdout,"-:\n") == -1) die_write();
224 }
225}
226
227int fd;
228substdio ss;
229char ssbuf[SUBSTDIO_INSIZE];
230
231void main(argc,argv)
232int argc;
233char **argv;
234{
235 int opt;
236 int match;
237
238 while ((opt = getopt(argc,argv,"/ohHuUc:C")) != opteof)
239 switch(opt) {
240 case '/': dashcolon = "-/:"; break;
241 case 'o': homestrategy = 2; break;
242 case 'h': homestrategy = 1; break;
243 case 'H': homestrategy = 0; break;
244 case 'u': flagnoupper = 0; break;
245 case 'U': flagnoupper = 1; break;
246 case 'c': *auto_break = *optarg; break;
247 case 'C': *auto_break = 0; break;
248 case '?':
249 default:
250 _exit(100);
251 }
252
253 if (chdir(auto_qmail) == -1) die_chdir();
254
255 /* no need for control_init() */
256
257 okincl = control_readfile(&incl,"users/include",0);
258 if (okincl == -1) die_control();
259 if (okincl) if (!constmap_init(&mapincl,incl.s,incl.len,0)) die_nomem();
260
261 okexcl = control_readfile(&excl,"users/exclude",0);
262 if (okexcl == -1) die_control();
263 if (okexcl) if (!constmap_init(&mapexcl,excl.s,excl.len,0)) die_nomem();
264
265 okmana = control_readfile(&mana,"users/mailnames",0);
266 if (okmana == -1) die_control();
267 if (okmana) if (!constmap_init(&mapmana,mana.s,mana.len,1)) die_nomem();
268
269 if (!stralloc_copys(&allusers,"")) die_nomem();
270
271 for (;;) {
272 if (getln(subfdin,&line,&match,'\n') == -1) die_read();
273 doaccount();
274 if (!match) break;
275 }
276 if (!flagalias) die_alias();
277
278 fd = open_read("users/subusers");
279 if (fd == -1) {
280 if (errno != error_noent) die_control();
281 }
282 else {
283 substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
284
285 if (!constmap_init(&mapuser,allusers.s,allusers.len,1)) die_nomem();
286
287 for (;;) {
288 if (getln(&ss,&line,&match,'\n') == -1) die_read();
289 dosubuser();
290 if (!match) break;
291 }
292
293 close(fd);
294 }
295
296 fd = open_read("users/append");
297 if (fd == -1) {
298 if (errno != error_noent) die_control();
299 }
300 else {
301 substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
302 for (;;) {
303 if (getln(&ss,&line,&match,'\n') == -1) die_read();
304 if (substdio_put(subfdout,line.s,line.len) == -1) die_write();
305 if (!match) break;
306 }
307 }
308
309 if (substdio_puts(subfdout,".\n") == -1) die_write();
310 if (substdio_flush(subfdout) == -1) die_write();
311 _exit(0);
312}
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 @@
1.TH qmail-qmqpc 8
2.SH NAME
3qmail-qmqpc \- queue a mail message via QMQP
4.SH SYNOPSIS
5.B qmail-qmqpc
6.SH DESCRIPTION
7.B qmail-qmqpc
8offers the same interface as
9.BR qmail-queue ,
10but it gives the message to a QMQP server
11instead of storing it locally.
12
13In a
14.B mini-qmail
15installation,
16.B qmail-queue
17is replaced with a symbolic link to
18.BR qmail-qmqpc .
19.SH "CONTROL FILES"
20.TP 5
21.I qmqpservers
22IP addresses of QMQP servers, one address per line.
23.B qmail-qmqpc
24will try each address in turn until it establishes a QMQP connection
25or runs out of addresses.
26.SH "SEE ALSO"
27qmail-control(5),
28qmail-queue(8),
29qmail-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 @@
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <netinet/in.h>
4#include <arpa/inet.h>
5#include "substdio.h"
6#include "getln.h"
7#include "readwrite.h"
8#include "exit.h"
9#include "stralloc.h"
10#include "slurpclose.h"
11#include "error.h"
12#include "sig.h"
13#include "ip.h"
14#include "timeoutconn.h"
15#include "timeoutread.h"
16#include "timeoutwrite.h"
17#include "auto_qmail.h"
18#include "control.h"
19#include "fmt.h"
20
21#define PORT_QMQP 628
22
23void die_success() { _exit(0); }
24void die_perm() { _exit(31); }
25void nomem() { _exit(51); }
26void die_read() { if (errno == error_nomem) nomem(); _exit(54); }
27void die_control() { _exit(55); }
28void die_socket() { _exit(56); }
29void die_home() { _exit(61); }
30void die_temp() { _exit(71); }
31void die_conn() { _exit(74); }
32void die_format() { _exit(91); }
33
34int lasterror = 55;
35int qmqpfd;
36
37int saferead(fd,buf,len) int fd; char *buf; int len;
38{
39 int r;
40 r = timeoutread(60,qmqpfd,buf,len);
41 if (r <= 0) die_conn();
42 return r;
43}
44int safewrite(fd,buf,len) int fd; char *buf; int len;
45{
46 int r;
47 r = timeoutwrite(60,qmqpfd,buf,len);
48 if (r <= 0) die_conn();
49 return r;
50}
51
52char buf[1024];
53substdio to = SUBSTDIO_FDBUF(safewrite,-1,buf,sizeof buf);
54substdio from = SUBSTDIO_FDBUF(saferead,-1,buf,sizeof buf);
55substdio envelope = SUBSTDIO_FDBUF(read,1,buf,sizeof buf);
56/* WARNING: can use only one of these at a time! */
57
58stralloc beforemessage = {0};
59stralloc message = {0};
60stralloc aftermessage = {0};
61
62char strnum[FMT_ULONG];
63stralloc line = {0};
64
65void getmess()
66{
67 int match;
68
69 if (slurpclose(0,&message,1024) == -1) die_read();
70
71 strnum[fmt_ulong(strnum,(unsigned long) message.len)] = 0;
72 if (!stralloc_copys(&beforemessage,strnum)) nomem();
73 if (!stralloc_cats(&beforemessage,":")) nomem();
74 if (!stralloc_copys(&aftermessage,",")) nomem();
75
76 if (getln(&envelope,&line,&match,'\0') == -1) die_read();
77 if (!match) die_format();
78 if (line.len < 2) die_format();
79 if (line.s[0] != 'F') die_format();
80
81 strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0;
82 if (!stralloc_cats(&aftermessage,strnum)) nomem();
83 if (!stralloc_cats(&aftermessage,":")) nomem();
84 if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem();
85 if (!stralloc_cats(&aftermessage,",")) nomem();
86
87 for (;;) {
88 if (getln(&envelope,&line,&match,'\0') == -1) die_read();
89 if (!match) die_format();
90 if (line.len < 2) break;
91 if (line.s[0] != 'T') die_format();
92
93 strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0;
94 if (!stralloc_cats(&aftermessage,strnum)) nomem();
95 if (!stralloc_cats(&aftermessage,":")) nomem();
96 if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem();
97 if (!stralloc_cats(&aftermessage,",")) nomem();
98 }
99}
100
101void doit(server)
102char *server;
103{
104 struct ip_address ip;
105 char ch;
106
107 if (!ip_scan(server,&ip)) return;
108
109 qmqpfd = socket(AF_INET,SOCK_STREAM,0);
110 if (qmqpfd == -1) die_socket();
111
112 if (timeoutconn(qmqpfd,&ip,PORT_QMQP,10) != 0) {
113 lasterror = 73;
114 if (errno == error_timeout) lasterror = 72;
115 close(qmqpfd);
116 return;
117 }
118
119 strnum[fmt_ulong(strnum,(unsigned long) (beforemessage.len + message.len + aftermessage.len))] = 0;
120 substdio_puts(&to,strnum);
121 substdio_puts(&to,":");
122 substdio_put(&to,beforemessage.s,beforemessage.len);
123 substdio_put(&to,message.s,message.len);
124 substdio_put(&to,aftermessage.s,aftermessage.len);
125 substdio_puts(&to,",");
126 substdio_flush(&to);
127
128 for (;;) {
129 substdio_get(&from,&ch,1);
130 if (ch == 'K') die_success();
131 if (ch == 'Z') die_temp();
132 if (ch == 'D') die_perm();
133 }
134}
135
136stralloc servers = {0};
137
138main()
139{
140 int i;
141 int j;
142
143 sig_pipeignore();
144
145 if (chdir(auto_qmail) == -1) die_home();
146 if (control_init() == -1) die_control();
147 if (control_readfile(&servers,"control/qmqpservers",0) != 1) die_control();
148
149 getmess();
150
151 i = 0;
152 for (j = 0;j < servers.len;++j)
153 if (!servers.s[j]) {
154 doit(servers.s + i);
155 i = j + 1;
156 }
157
158 _exit(lasterror);
159}
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 @@
1.TH qmail-qmqpd 8
2.SH NAME
3qmail-qmqpd \- receive mail via QMQP
4.SH SYNOPSIS
5.B qmail-qmqpd
6.SH DESCRIPTION
7.B qmail-qmqpd
8receives mail messages via the Quick Mail Queueing Protocol (QMQP)
9and invokes
10.B qmail-queue
11to deposit them into the outgoing queue.
12.B qmail-qmqpd
13must be supplied several environment variables;
14see
15.BR tcp-environ(5) .
16
17.B qmail-qmqpd
18will relay messages to any destination.
19It should be invoked only for connections from preauthorized users.
20.SH "SEE ALSO"
21tcp-env(1),
22tcpserver(1),
23tcp-environ(5),
24qmail-qmqpc(8),
25qmail-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 @@
1#include "auto_qmail.h"
2#include "qmail.h"
3#include "received.h"
4#include "sig.h"
5#include "substdio.h"
6#include "readwrite.h"
7#include "exit.h"
8#include "now.h"
9#include "fmt.h"
10#include "env.h"
11
12void resources() { _exit(111); }
13
14int safewrite(fd,buf,len) int fd; char *buf; int len;
15{
16 int r;
17 r = write(fd,buf,len);
18 if (r <= 0) _exit(0);
19 return r;
20}
21int saferead(fd,buf,len) int fd; char *buf; int len;
22{
23 int r;
24 r = read(fd,buf,len);
25 if (r <= 0) _exit(0);
26 return r;
27}
28
29char ssinbuf[512];
30substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
31char ssoutbuf[256];
32substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
33
34unsigned long bytesleft = 100;
35
36void getbyte(ch)
37char *ch;
38{
39 if (!bytesleft--) _exit(100);
40 substdio_get(&ssin,ch,1);
41}
42
43unsigned long getlen()
44{
45 unsigned long len = 0;
46 char ch;
47
48 for (;;) {
49 getbyte(&ch);
50 if (ch == ':') return len;
51 if (len > 200000000) resources();
52 len = 10 * len + (ch - '0');
53 }
54}
55
56void getcomma()
57{
58 char ch;
59 getbyte(&ch);
60 if (ch != ',') _exit(100);
61}
62
63struct qmail qq;
64
65void identify()
66{
67 char *remotehost;
68 char *remoteinfo;
69 char *remoteip;
70 char *local;
71
72 remotehost = env_get("TCPREMOTEHOST");
73 if (!remotehost) remotehost = "unknown";
74 remoteinfo = env_get("TCPREMOTEINFO");
75 remoteip = env_get("TCPREMOTEIP");
76 if (!remoteip) remoteip = "unknown";
77 local = env_get("TCPLOCALHOST");
78 if (!local) local = env_get("TCPLOCALIP");
79 if (!local) local = "unknown";
80
81 received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0);
82}
83
84char buf[1000];
85char strnum[FMT_ULONG];
86
87int getbuf()
88{
89 unsigned long len;
90 int i;
91
92 len = getlen();
93 if (len >= 1000) {
94 for (i = 0;i < len;++i) getbyte(buf);
95 getcomma();
96 buf[0] = 0;
97 return 0;
98 }
99
100 for (i = 0;i < len;++i) getbyte(buf + i);
101 getcomma();
102 buf[len] = 0;
103 return byte_chr(buf,len,'\0') == len;
104}
105
106int flagok = 1;
107
108main()
109{
110 char *result;
111 unsigned long qp;
112 unsigned long len;
113 char ch;
114
115 sig_pipeignore();
116 sig_alarmcatch(resources);
117 alarm(3600);
118
119 bytesleft = getlen();
120
121 len = getlen();
122
123 if (chdir(auto_qmail) == -1) resources();
124 if (qmail_open(&qq) == -1) resources();
125 qp = qmail_qp(&qq);
126 identify();
127
128 while (len > 0) { /* XXX: could speed this up */
129 getbyte(&ch);
130 --len;
131 qmail_put(&qq,&ch,1);
132 }
133 getcomma();
134
135 if (getbuf())
136 qmail_from(&qq,buf);
137 else {
138 qmail_from(&qq,"");
139 qmail_fail(&qq);
140 flagok = 0;
141 }
142
143 while (bytesleft)
144 if (getbuf())
145 qmail_to(&qq,buf);
146 else {
147 qmail_fail(&qq);
148 flagok = 0;
149 }
150
151 bytesleft = 1;
152 getcomma();
153
154 result = qmail_close(&qq);
155
156 if (!*result) {
157 len = fmt_str(buf,"Kok ");
158 len += fmt_ulong(buf + len,(unsigned long) now());
159 len += fmt_str(buf + len," qp ");
160 len += fmt_ulong(buf + len,qp);
161 buf[len] = 0;
162 result = buf;
163 }
164
165 if (!flagok)
166 result = "Dsorry, I can't accept addresses like that (#5.1.3)";
167
168 substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
169 substdio_puts(&ssout,":");
170 substdio_puts(&ssout,result);
171 substdio_puts(&ssout,",");
172 substdio_flush(&ssout);
173 _exit(0);
174}
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 @@
1.TH qmail-qmtpd 8
2.SH NAME
3qmail-qmtpd \- receive mail via QMTP
4.SH SYNOPSIS
5.B qmail-qmtpd
6.SH DESCRIPTION
7.B qmail-qmtpd
8receives mail messages via the Quick Mail Transfer Protocol (QMTP)
9and invokes
10.B qmail-queue
11to deposit them into the outgoing queue.
12.B qmail-qmtpd
13must be supplied several environment variables;
14see
15.BR tcp-environ(5) .
16
17.B qmail-qmtpd
18supports the
19.IR rcpthosts ,
20.IR morercpthosts ,
21.BR RELAYCLIENT ,
22.IR databytes ,
23and
24.B DATABYTES
25mechanisms described in
26.BR qmail-smtpd(8) .
27.SH "SEE ALSO"
28tcp-env(1),
29tcp-environ(5),
30qmail-control(5),
31qmail-queue(8),
32qmail-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 @@
1#include "stralloc.h"
2#include "substdio.h"
3#include "qmail.h"
4#include "now.h"
5#include "str.h"
6#include "fmt.h"
7#include "env.h"
8#include "sig.h"
9#include "rcpthosts.h"
10#include "auto_qmail.h"
11#include "readwrite.h"
12#include "control.h"
13#include "received.h"
14
15void badproto() { _exit(100); }
16void resources() { _exit(111); }
17
18int safewrite(fd,buf,len) int fd; char *buf; int len;
19{
20 int r;
21 r = write(fd,buf,len);
22 if (r <= 0) _exit(0);
23 return r;
24}
25
26char ssoutbuf[256];
27substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
28
29int saferead(fd,buf,len) int fd; char *buf; int len;
30{
31 int r;
32 substdio_flush(&ssout);
33 r = read(fd,buf,len);
34 if (r <= 0) _exit(0);
35 return r;
36}
37
38char ssinbuf[512];
39substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
40
41unsigned long getlen()
42{
43 unsigned long len = 0;
44 char ch;
45 for (;;) {
46 substdio_get(&ssin,&ch,1);
47 if (ch == ':') return len;
48 if (len > 200000000) resources();
49 len = 10 * len + (ch - '0');
50 }
51}
52
53void getcomma()
54{
55 char ch;
56 substdio_get(&ssin,&ch,1);
57 if (ch != ',') badproto();
58}
59
60unsigned int databytes = 0;
61unsigned int bytestooverflow = 0;
62struct qmail qq;
63
64char buf[1000];
65char buf2[100];
66
67char *remotehost;
68char *remoteinfo;
69char *remoteip;
70char *local;
71
72stralloc failure = {0};
73
74char *relayclient;
75int relayclientlen;
76
77main()
78{
79 char ch;
80 int i;
81 unsigned long biglen;
82 unsigned long len;
83 int flagdos;
84 int flagsenderok;
85 int flagbother;
86 unsigned long qp;
87 char *result;
88 char *x;
89 unsigned long u;
90
91 sig_pipeignore();
92 sig_alarmcatch(resources);
93 alarm(3600);
94
95 if (chdir(auto_qmail) == -1) resources();
96
97 if (control_init() == -1) resources();
98 if (rcpthosts_init() == -1) resources();
99 relayclient = env_get("RELAYCLIENT");
100 relayclientlen = relayclient ? str_len(relayclient) : 0;
101
102 if (control_readint(&databytes,"control/databytes") == -1) resources();
103 x = env_get("DATABYTES");
104 if (x) { scan_ulong(x,&u); databytes = u; }
105 if (!(databytes + 1)) --databytes;
106
107 remotehost = env_get("TCPREMOTEHOST");
108 if (!remotehost) remotehost = "unknown";
109 remoteinfo = env_get("TCPREMOTEINFO");
110 remoteip = env_get("TCPREMOTEIP");
111 if (!remoteip) remoteip = "unknown";
112 local = env_get("TCPLOCALHOST");
113 if (!local) local = env_get("TCPLOCALIP");
114 if (!local) local = "unknown";
115
116 for (;;) {
117 if (!stralloc_copys(&failure,"")) resources();
118 flagsenderok = 1;
119
120 len = getlen();
121 if (len == 0) badproto();
122
123 if (databytes) bytestooverflow = databytes + 1;
124 if (qmail_open(&qq) == -1) resources();
125 qp = qmail_qp(&qq);
126
127 substdio_get(&ssin,&ch,1);
128 --len;
129 if (ch == 10) flagdos = 0;
130 else if (ch == 13) flagdos = 1;
131 else badproto();
132
133 received(&qq,"QMTP",local,remoteip,remotehost,remoteinfo,(char *) 0);
134
135 /* XXX: check for loops? only if len is big? */
136
137 if (flagdos)
138 while (len > 0) {
139 substdio_get(&ssin,&ch,1);
140 --len;
141 while ((ch == 13) && len) {
142 substdio_get(&ssin,&ch,1);
143 --len;
144 if (ch == 10) break;
145 if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
146 qmail_put(&qq,"\015",1);
147 }
148 if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
149 qmail_put(&qq,&ch,1);
150 }
151 else {
152 if (databytes)
153 if (len > databytes) {
154 bytestooverflow = 0;
155 qmail_fail(&qq);
156 }
157 while (len > 0) { /* XXX: could speed this up, obviously */
158 substdio_get(&ssin,&ch,1);
159 --len;
160 qmail_put(&qq,&ch,1);
161 }
162 }
163 getcomma();
164
165 len = getlen();
166
167 if (len >= 1000) {
168 buf[0] = 0;
169 flagsenderok = 0;
170 for (i = 0;i < len;++i)
171 substdio_get(&ssin,&ch,1);
172 }
173 else {
174 for (i = 0;i < len;++i) {
175 substdio_get(&ssin,buf + i,1);
176 if (!buf[i]) flagsenderok = 0;
177 }
178 buf[len] = 0;
179 }
180 getcomma();
181
182 flagbother = 0;
183 qmail_from(&qq,buf);
184 if (!flagsenderok) qmail_fail(&qq);
185
186 biglen = getlen();
187 while (biglen > 0) {
188 if (!stralloc_append(&failure,"")) resources();
189
190 len = 0;
191 for (;;) {
192 if (!biglen) badproto();
193 substdio_get(&ssin,&ch,1);
194 --biglen;
195 if (ch == ':') break;
196 if (len > 200000000) resources();
197 len = 10 * len + (ch - '0');
198 }
199 if (len >= biglen) badproto();
200 if (len + relayclientlen >= 1000) {
201 failure.s[failure.len - 1] = 'L';
202 for (i = 0;i < len;++i)
203 substdio_get(&ssin,&ch,1);
204 }
205 else {
206 for (i = 0;i < len;++i) {
207 substdio_get(&ssin,buf + i,1);
208 if (!buf[i]) failure.s[failure.len - 1] = 'N';
209 }
210 buf[len] = 0;
211
212 if (relayclient)
213 str_copy(buf + len,relayclient);
214 else
215 switch(rcpthosts(buf,len)) {
216 case -1: resources();
217 case 0: failure.s[failure.len - 1] = 'D';
218 }
219
220 if (!failure.s[failure.len - 1]) {
221 qmail_to(&qq,buf);
222 flagbother = 1;
223 }
224 }
225 getcomma();
226 biglen -= (len + 1);
227 }
228 getcomma();
229
230 if (!flagbother) qmail_fail(&qq);
231 result = qmail_close(&qq);
232 if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)";
233 if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
234
235 if (*result)
236 len = str_len(result);
237 else {
238 /* success! */
239 len = 0;
240 len += fmt_str(buf2 + len,"Kok ");
241 len += fmt_ulong(buf2 + len,(unsigned long) now());
242 len += fmt_str(buf2 + len," qp ");
243 len += fmt_ulong(buf2 + len,qp);
244 buf2[len] = 0;
245 result = buf2;
246 }
247
248 len = fmt_ulong(buf,len);
249 buf[len++] = ':';
250 len += fmt_str(buf + len,result);
251 buf[len++] = ',';
252
253 for (i = 0;i < failure.len;++i)
254 switch(failure.s[i]) {
255 case 0:
256 substdio_put(&ssout,buf,len);
257 break;
258 case 'D':
259 substdio_puts(&ssout,"66:Dsorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),");
260 break;
261 default:
262 substdio_puts(&ssout,"46:Dsorry, I can't handle that recipient (#5.1.3),");
263 break;
264 }
265
266 /* ssout will be flushed when we read from the network again */
267 }
268}
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 @@
1.TH qmail-qread 8
2.SH NAME
3qmail-qread \- list outgoing messages and recipients
4.SH SYNOPSIS
5.B qmail-qread
6.SH DESCRIPTION
7.B qmail-qread
8scans the outgoing queue of messages.
9For each message it prints various human-readable information,
10including the date the message entered the queue,
11the number of bytes in the message,
12the message sender,
13and all the recipients still under consideration.
14
15.B qmail-qread
16must be run either as
17.B root
18or with user id
19.B qmails
20and group id
21.BR qmail .
22.SH "SEE ALSO"
23qmail-qstat(8),
24qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "stralloc.h"
4#include "substdio.h"
5#include "subfd.h"
6#include "fmt.h"
7#include "str.h"
8#include "getln.h"
9#include "fmtqfn.h"
10#include "readsubdir.h"
11#include "auto_qmail.h"
12#include "open.h"
13#include "datetime.h"
14#include "date822fmt.h"
15#include "readwrite.h"
16#include "error.h"
17#include "exit.h"
18
19readsubdir rs;
20
21void die(n) int n; { substdio_flush(subfdout); _exit(n); }
22
23void warn(s1,s2) char *s1; char *s2;
24{
25 char *x;
26 x = error_str(errno);
27 substdio_puts(subfdout,s1);
28 substdio_puts(subfdout,s2);
29 substdio_puts(subfdout,": ");
30 substdio_puts(subfdout,x);
31 substdio_puts(subfdout,"\n");
32}
33
34void die_nomem() { substdio_puts(subfdout,"fatal: out of memory\n"); die(111); }
35void die_chdir() { warn("fatal: unable to chdir",""); die(111); }
36void die_opendir(fn) char *fn; { warn("fatal: unable to opendir ",fn); die(111); }
37
38void err(id) unsigned long id;
39{
40 char foo[FMT_ULONG];
41 foo[fmt_ulong(foo,id)] = 0;
42 warn("warning: trouble with #",foo);
43}
44
45char fnmess[FMTQFN];
46char fninfo[FMTQFN];
47char fnlocal[FMTQFN];
48char fnremote[FMTQFN];
49char fnbounce[FMTQFN];
50
51char inbuf[1024];
52stralloc sender = {0};
53
54unsigned long id;
55datetime_sec qtime;
56int flagbounce;
57unsigned long size;
58
59unsigned int fmtstats(s)
60char *s;
61{
62 struct datetime dt;
63 unsigned int len;
64 unsigned int i;
65
66 len = 0;
67 datetime_tai(&dt,qtime);
68 i = date822fmt(s,&dt) - 7/*XXX*/; len += i; if (s) s += i;
69 i = fmt_str(s," GMT #"); len += i; if (s) s += i;
70 i = fmt_ulong(s,id); len += i; if (s) s += i;
71 i = fmt_str(s," "); len += i; if (s) s += i;
72 i = fmt_ulong(s,size); len += i; if (s) s += i;
73 i = fmt_str(s," <"); len += i; if (s) s += i;
74 i = fmt_str(s,sender.s + 1); len += i; if (s) s += i;
75 i = fmt_str(s,"> "); len += i; if (s) s += i;
76 if (flagbounce)
77 {
78 i = fmt_str(s," bouncing"); len += i; if (s) s += i;
79 }
80
81 return len;
82}
83
84stralloc stats = {0};
85
86void out(s,n) char *s; unsigned int n;
87{
88 while (n > 0)
89 {
90 substdio_put(subfdout,((*s >= 32) && (*s <= 126)) ? s : "_",1);
91 --n;
92 ++s;
93 }
94}
95void outs(s) char *s; { out(s,str_len(s)); }
96void outok(s) char *s; { substdio_puts(subfdout,s); }
97
98void putstats()
99{
100 if (!stralloc_ready(&stats,fmtstats(FMT_LEN))) die_nomem();
101 stats.len = fmtstats(stats.s);
102 out(stats.s,stats.len);
103 outok("\n");
104}
105
106stralloc line = {0};
107
108void main()
109{
110 int channel;
111 int match;
112 struct stat st;
113 int fd;
114 substdio ss;
115 int x;
116
117 if (chdir(auto_qmail) == -1) die_chdir();
118 if (chdir("queue") == -1) die_chdir();
119 readsubdir_init(&rs,"info",die_opendir);
120
121 while (x = readsubdir_next(&rs,&id))
122 if (x > 0)
123 {
124 fmtqfn(fnmess,"mess/",id,1);
125 fmtqfn(fninfo,"info/",id,1);
126 fmtqfn(fnlocal,"local/",id,1);
127 fmtqfn(fnremote,"remote/",id,1);
128 fmtqfn(fnbounce,"bounce/",id,0);
129
130 if (stat(fnmess,&st) == -1) { err(id); continue; }
131 size = st.st_size;
132 flagbounce = !stat(fnbounce,&st);
133
134 fd = open_read(fninfo);
135 if (fd == -1) { err(id); continue; }
136 substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
137 if (getln(&ss,&sender,&match,0) == -1) die_nomem();
138 if (fstat(fd,&st) == -1) { close(fd); err(id); continue; }
139 close(fd);
140 qtime = st.st_mtime;
141
142 putstats();
143
144 for (channel = 0;channel < 2;++channel)
145 {
146 fd = open_read(channel ? fnremote : fnlocal);
147 if (fd == -1)
148 {
149 if (errno != error_noent)
150 err(id);
151 }
152 else
153 {
154 for (;;)
155 {
156 if (getln(&ss,&line,&match,0) == -1) die_nomem();
157 if (!match) break;
158 switch(line.s[0])
159 {
160 case 'D':
161 outok(" done");
162 case 'T':
163 outok(channel ? "\tremote\t" : "\tlocal\t");
164 outs(line.s + 1);
165 outok("\n");
166 break;
167 }
168 }
169 close(fd);
170 }
171 }
172 }
173
174 die(0);
175}
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 @@
1.TH qmail-qstat 8
2.SH NAME
3qmail-qstat \- summarize status of mail queue
4.SH SYNOPSIS
5.B qmail-qstat
6.SH DESCRIPTION
7.B qmail-qstat
8gives a human-readable breakdown
9of the number of messages at various spots in the mail queue.
10
11.B qmail-qstat
12must be run either as
13.B root
14or with group id
15.BR qmail .
16.SH "SEE ALSO"
17qmail-qread(8),
18qmail-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 @@
1cd QMAIL
2messdirs=`echo queue/mess/* | wc -w`
3messfiles=`find queue/mess/* -print | wc -w`
4tododirs=`echo queue/todo | wc -w`
5todofiles=`find queue/todo -print | wc -w`
6echo messages in queue: `expr $messfiles - $messdirs`
7echo 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..12eea0c
--- /dev/null
+++ b/qmail-queue.8
@@ -0,0 +1,155 @@
1.TH qmail-queue 8
2.SH NAME
3qmail-queue \- queue a mail message for delivery
4.SH SYNOPSIS
5.B qmail-queue
6.SH DESCRIPTION
7.B qmail-queue
8reads a mail message from descriptor 0.
9It then reads envelope information from descriptor 1.
10It places the message into the outgoing queue
11for future delivery by
12.BR qmail-send .
13
14The envelope information is
15an envelope sender address
16followed by a list of envelope recipient addresses.
17The sender address is preceded by the letter F
18and terminated by a 0 byte.
19Each recipient address is preceded by the letter T
20and terminated by a 0 byte.
21The list of recipient addresses is terminated by an extra 0 byte.
22If
23.B qmail-queue
24sees end-of-file before the extra 0 byte,
25it aborts without placing the message into the queue.
26
27Every envelope recipient address
28should contain a username,
29an @ sign,
30and a fully qualified domain name.
31
32.B qmail-queue
33always adds a
34.B Received
35line to the top of the message.
36Other than this,
37.B qmail-queue
38does not inspect the message
39and does not enforce any restrictions on its contents.
40However, the recipients probably expect to see a proper header,
41as described in
42.BR qmail-header(5) .
43.SH "FILESYSTEM RESTRICTIONS"
44.B qmail-queue
45imposes two constraints on the queue structure:
46each
47.B mess
48subdirectory must be in the same filesystem as the
49.B pid
50directory; and each
51.B todo
52subdirectory must be in the same filesystem as the
53.B intd
54directory.
55.SH "EXIT CODES"
56.B qmail-queue
57does not print diagnostics.
58It exits
590 if
60it has successfully queued the message.
61It exits between 1 and 99 if
62it has failed to queue the message.
63
64All
65.B qmail-queue
66error codes between 11 and 40
67indicate permanent errors:
68.TP 5
69.B 11
70Address too long.
71.TP
72.B 31
73Mail server permanently refuses to send the message to any recipients.
74(Not used by
75.BR qmail-queue ,
76but can be used by programs offering the same interface.)
77.PP
78All other
79.B qmail-queue
80error codes indicate temporary errors:
81.TP 5
82.B 51
83Out of memory.
84.TP
85.B 52
86Timeout.
87.TP
88.B 53
89Write error; e.g., disk full.
90.TP
91.B 54
92Unable to read the message or envelope.
93.TP
94.B 55
95Unable to read a configuration file.
96(Not used by
97.BR qmail-queue .)
98.TP
99.B 56
100Problem making a network connection from this host.
101(Not used by
102.BR qmail-queue .)
103.TP
104.B 61
105Problem with the qmail home directory.
106.TP
107.B 62
108Problem with the queue directory.
109.TP
110.B 63
111Problem with queue/pid.
112.TP
113.B 64
114Problem with queue/mess.
115.TP
116.B 65
117Problem with queue/intd.
118.TP
119.B 66
120Problem with queue/todo.
121.TP
122.B 71
123Mail server temporarily refuses to send the message to any recipients.
124(Not used by
125.BR qmail-queue .)
126.TP
127.B 72
128Connection to mail server timed out.
129(Not used by
130.BR qmail-queue .)
131.TP
132.B 73
133Connection to mail server rejected.
134(Not used by
135.BR qmail-queue .)
136.TP
137.B 74
138Connection to mail server succeeded,
139but communication failed.
140(Not used by
141.BR qmail-queue .)
142.TP
143.B 81
144Internal bug; e.g., segmentation fault.
145.TP
146.B 91
147Envelope format error.
148.SH "SEE ALSO"
149addresses(5),
150envelopes(5),
151qmail-header(5),
152qmail-inject(8),
153qmail-qmqpc(8),
154qmail-send(8),
155qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "readwrite.h"
4#include "sig.h"
5#include "exit.h"
6#include "open.h"
7#include "seek.h"
8#include "fmt.h"
9#include "alloc.h"
10#include "substdio.h"
11#include "datetime.h"
12#include "now.h"
13#include "triggerpull.h"
14#include "extra.h"
15#include "auto_qmail.h"
16#include "auto_uids.h"
17#include "date822fmt.h"
18#include "fmtqfn.h"
19
20#define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
21#define ADDR 1003
22
23char inbuf[2048];
24struct substdio ssin;
25char outbuf[256];
26struct substdio ssout;
27
28datetime_sec starttime;
29struct datetime dt;
30unsigned long mypid;
31unsigned long uid;
32char *pidfn;
33struct stat pidst;
34unsigned long messnum;
35char *messfn;
36char *todofn;
37char *intdfn;
38int messfd;
39int intdfd;
40int flagmademess = 0;
41int flagmadeintd = 0;
42
43void cleanup()
44{
45 if (flagmadeintd)
46 {
47 seek_trunc(intdfd,0);
48 if (unlink(intdfn) == -1) return;
49 }
50 if (flagmademess)
51 {
52 seek_trunc(messfd,0);
53 if (unlink(messfn) == -1) return;
54 }
55}
56
57void die(e) int e; { _exit(e); }
58void die_write() { cleanup(); die(53); }
59void die_read() { cleanup(); die(54); }
60void sigalrm() { /* thou shalt not clean up here */ die(52); }
61void sigbug() { die(81); }
62
63unsigned int receivedlen;
64char *received;
65/* "Received: (qmail-queue invoked by alias); 26 Sep 1995 04:46:54 -0000\n" */
66
67static unsigned int receivedfmt(s)
68char *s;
69{
70 unsigned int i;
71 unsigned int len;
72 len = 0;
73 i = fmt_str(s,"Received: (qmail "); len += i; if (s) s += i;
74 i = fmt_ulong(s,mypid); len += i; if (s) s += i;
75 i = fmt_str(s," invoked "); len += i; if (s) s += i;
76 if (uid == auto_uida)
77 { i = fmt_str(s,"by alias"); len += i; if (s) s += i; }
78 else if (uid == auto_uidd)
79 { i = fmt_str(s,"from network"); len += i; if (s) s += i; }
80 else if (uid == auto_uids)
81 { i = fmt_str(s,"for bounce"); len += i; if (s) s += i; }
82 else
83 {
84 i = fmt_str(s,"by uid "); len += i; if (s) s += i;
85 i = fmt_ulong(s,uid); len += i; if (s) s += i;
86 }
87 i = fmt_str(s,"); "); len += i; if (s) s += i;
88 i = date822fmt(s,&dt); len += i; if (s) s += i;
89 return len;
90}
91
92void received_setup()
93{
94 receivedlen = receivedfmt((char *) 0);
95 received = alloc(receivedlen + 1);
96 if (!received) die(51);
97 receivedfmt(received);
98}
99
100unsigned int pidfmt(s,seq)
101char *s;
102unsigned long seq;
103{
104 unsigned int i;
105 unsigned int len;
106
107 len = 0;
108 i = fmt_str(s,"pid/"); len += i; if (s) s += i;
109 i = fmt_ulong(s,mypid); len += i; if (s) s += i;
110 i = fmt_str(s,"."); len += i; if (s) s += i;
111 i = fmt_ulong(s,starttime); len += i; if (s) s += i;
112 i = fmt_str(s,"."); len += i; if (s) s += i;
113 i = fmt_ulong(s,seq); len += i; if (s) s += i;
114 ++len; if (s) *s++ = 0;
115
116 return len;
117}
118
119char *fnnum(dirslash,flagsplit)
120char *dirslash;
121int flagsplit;
122{
123 char *s;
124
125 s = alloc(fmtqfn((char *) 0,dirslash,messnum,flagsplit));
126 if (!s) die(51);
127 fmtqfn(s,dirslash,messnum,flagsplit);
128 return s;
129}
130
131void pidopen()
132{
133 unsigned int len;
134 unsigned long seq;
135
136 seq = 1;
137 len = pidfmt((char *) 0,seq);
138 pidfn = alloc(len);
139 if (!pidfn) die(51);
140
141 for (seq = 1;seq < 10;++seq)
142 {
143 if (pidfmt((char *) 0,seq) > len) die(81); /* paranoia */
144 pidfmt(pidfn,seq);
145 messfd = open_excl(pidfn);
146 if (messfd != -1) return;
147 }
148
149 die(63);
150}
151
152char tmp[FMT_ULONG];
153
154void main()
155{
156 unsigned int len;
157 char ch;
158
159 sig_blocknone();
160 umask(033);
161 if (chdir(auto_qmail) == -1) die(61);
162 if (chdir("queue") == -1) die(62);
163
164 mypid = getpid();
165 uid = getuid();
166 starttime = now();
167 datetime_tai(&dt,starttime);
168
169 received_setup();
170
171 sig_pipeignore();
172 sig_miscignore();
173 sig_alarmcatch(sigalrm);
174 sig_bugcatch(sigbug);
175
176 alarm(DEATH);
177
178 pidopen();
179 if (fstat(messfd,&pidst) == -1) die(63);
180
181 messnum = pidst.st_ino;
182 messfn = fnnum("mess/",1);
183 todofn = fnnum("todo/",0);
184 intdfn = fnnum("intd/",0);
185
186 if (link(pidfn,messfn) == -1) die(64);
187 if (unlink(pidfn) == -1) die(63);
188 flagmademess = 1;
189
190 substdio_fdbuf(&ssout,write,messfd,outbuf,sizeof(outbuf));
191 substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
192
193 if (substdio_bput(&ssout,received,receivedlen) == -1) die_write();
194
195 switch(substdio_copy(&ssout,&ssin))
196 {
197 case -2: die_read();
198 case -3: die_write();
199 }
200
201 if (substdio_flush(&ssout) == -1) die_write();
202 if (fsync(messfd) == -1) die_write();
203
204 intdfd = open_excl(intdfn);
205 if (intdfd == -1) die(65);
206 flagmadeintd = 1;
207
208 substdio_fdbuf(&ssout,write,intdfd,outbuf,sizeof(outbuf));
209 substdio_fdbuf(&ssin,read,1,inbuf,sizeof(inbuf));
210
211 if (substdio_bput(&ssout,"u",1) == -1) die_write();
212 if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,uid)) == -1) die_write();
213 if (substdio_bput(&ssout,"",1) == -1) die_write();
214
215 if (substdio_bput(&ssout,"p",1) == -1) die_write();
216 if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,mypid)) == -1) die_write();
217 if (substdio_bput(&ssout,"",1) == -1) die_write();
218
219 if (substdio_get(&ssin,&ch,1) < 1) die_read();
220 if (ch != 'F') die(91);
221 if (substdio_bput(&ssout,&ch,1) == -1) die_write();
222 for (len = 0;len < ADDR;++len)
223 {
224 if (substdio_get(&ssin,&ch,1) < 1) die_read();
225 if (substdio_put(&ssout,&ch,1) == -1) die_write();
226 if (!ch) break;
227 }
228 if (len >= ADDR) die(11);
229
230 if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write();
231
232 for (;;)
233 {
234 if (substdio_get(&ssin,&ch,1) < 1) die_read();
235 if (!ch) break;
236 if (ch != 'T') die(91);
237 if (substdio_bput(&ssout,&ch,1) == -1) die_write();
238 for (len = 0;len < ADDR;++len)
239 {
240 if (substdio_get(&ssin,&ch,1) < 1) die_read();
241 if (substdio_bput(&ssout,&ch,1) == -1) die_write();
242 if (!ch) break;
243 }
244 if (len >= ADDR) die(11);
245 }
246
247 if (substdio_flush(&ssout) == -1) die_write();
248 if (fsync(intdfd) == -1) die_write();
249
250 if (link(intdfn,todofn) == -1) die(66);
251
252 triggerpull();
253 die(0);
254}
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 @@
1.TH qmail-remote 8
2.SH NAME
3qmail-remote \- send mail via SMTP
4.SH SYNOPSIS
5.B qmail-remote
6.I host
7.I sender
8.I recip
9[
10.I recip ...
11]
12.SH DESCRIPTION
13.B qmail-remote
14reads a mail message from its input
15and sends the message
16to one or more recipients
17at a remote host.
18
19The remote host is
20.BR qmail-remote 's
21first argument,
22.IR host .
23.B qmail-remote
24sends the message to
25.IR host ,
26or to a mail exchanger for
27.I host
28listed in the Domain Name System,
29via the Simple Mail Transfer Protocol (SMTP).
30.I host
31can be either a fully-qualified domain name:
32
33.EX
34 silverton.berkeley.edu
35.EE
36
37or an IP address enclosed in brackets:
38
39.EX
40 [128.32.183.163]
41.EE
42
43The envelope recipient addresses are listed as
44.I recip
45arguments to
46.BR qmail-remote .
47The envelope sender address is listed as
48.I sender\fP.
49
50Note that
51.B qmail-remote
52does not take options
53and does not follow the
54.B getopt
55standard.
56.SH TRANSPARENCY
57End-of-file in SMTP is encoded as dot CR LF.
58A dot at the beginning of a line is encoded as dot dot.
59It is impossible in SMTP to send a message that does not end with a newline.
60.B qmail-remote
61converts the UNIX newline convention into the SMTP newline convention
62by inserting CR before each LF.
63
64It is a violation of the SMTP protocol
65to send a message that contains long lines or non-ASCII characters.
66However,
67.B qmail-remote
68will happily send such messages.
69It is the user's responsibility to avoid generating illegal messages.
70.SH "RESULTS"
71.B qmail-remote
72prints some number of
73.I recipient reports\fP,
74followed by a
75.I message report\fR.
76Each report is terminated by a 0 byte.
77Each report begins with a single letter:
78.TP 5
79r
80Recipient report: acceptance.
81.TP 5
82h
83Recipient report: permanent rejection.
84.TP 5
85s
86Recipient report: temporary rejection.
87.TP 5
88K
89Message report: success.
90.I host
91has taken responsibility for delivering the message to each
92acceptable recipient.
93.TP 5
94Z
95Message report: temporary failure.
96.TP 5
97D
98Message report: permanent failure.
99.PP
100After this letter comes a human-readable description of
101what happened.
102
103The recipient reports will always be printed in the same order as
104.BR qmail-remote 's
105.I recip
106arguments.
107Note that in failure cases there may be fewer
108recipient reports
109than
110.I recip
111arguments.
112
113.B qmail-remote
114always exits zero.
115.SH "CONTROL FILES"
116.TP 5
117.I helohost
118Current host name,
119for use solely in saying hello to the remote SMTP server.
120Default:
121.IR me ,
122if that is supplied;
123otherwise
124.B qmail-remote
125refuses to run.
126.TP 5
127.I smtproutes
128Artificial SMTP routes.
129Each route has the form
130.IR domain\fB:\fIrelay ,
131without any extra spaces.
132If
133.I domain
134matches
135.IR host ,
136.B qmail-remote
137will connect to
138.IR relay ,
139as if
140.I host
141had
142.I relay
143as its only MX.
144(It will also avoid doing any CNAME lookups on
145.IR recip .)
146.I host
147may include a colon and a port number to use instead of the
148normal SMTP port, 25:
149
150.EX
151 inside.af.mil:firewall.af.mil:26
152.EE
153
154.I relay
155may be empty;
156this tells
157.B qmail-remote
158to look up MX records as usual.
159.I smtproutes
160may include wildcards:
161
162.EX
163 .af.mil:
164 :heaven.af.mil
165.EE
166
167Here
168any address ending with
169.B .af.mil
170(but not
171.B af.mil
172itself)
173is routed by its MX records;
174any other address is artificially routed to
175.BR heaven.af.mil .
176
177The
178.B qmail
179system does not protect you if you create an artificial
180mail loop between machines.
181However,
182you are always safe using
183.I smtproutes
184if you do not accept mail from the network.
185.TP 5
186.I timeoutconnect
187Number of seconds
188.B qmail-remote
189will wait for the remote SMTP server to accept a connection.
190Default: 60.
191The kernel normally imposes a 75-second upper limit.
192.TP 5
193.I timeoutremote
194Number of seconds
195.B qmail-remote
196will wait for each response from the remote SMTP server.
197Default: 1200.
198.SH "SEE ALSO"
199addresses(5),
200envelopes(5),
201qmail-control(5),
202qmail-send(8),
203qmail-smtpd(8),
204qmail-tcpok(8),
205qmail-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 @@
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <netinet/in.h>
4#include <arpa/inet.h>
5#include "sig.h"
6#include "stralloc.h"
7#include "substdio.h"
8#include "subfd.h"
9#include "scan.h"
10#include "case.h"
11#include "error.h"
12#include "auto_qmail.h"
13#include "control.h"
14#include "dns.h"
15#include "alloc.h"
16#include "quote.h"
17#include "ip.h"
18#include "ipalloc.h"
19#include "ipme.h"
20#include "gen_alloc.h"
21#include "gen_allocdefs.h"
22#include "str.h"
23#include "now.h"
24#include "exit.h"
25#include "constmap.h"
26#include "tcpto.h"
27#include "readwrite.h"
28#include "timeoutconn.h"
29#include "timeoutread.h"
30#include "timeoutwrite.h"
31
32#define HUGESMTPTEXT 5000
33
34#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
35unsigned long port = PORT_SMTP;
36
37GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
38GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
39static stralloc sauninit = {0};
40
41stralloc helohost = {0};
42stralloc routes = {0};
43struct constmap maproutes;
44stralloc host = {0};
45stralloc sender = {0};
46
47saa reciplist = {0};
48
49struct ip_address partner;
50
51void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
52void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
53void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); }
54void outsafe(sa) stralloc *sa; { int i; char ch;
55for (i = 0;i < sa->len;++i) {
56ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
57if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } }
58
59void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
60void temp_oserr() { out("Z\
61System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
62void temp_noconn() { out("Z\
63Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); }
64void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); }
65void temp_dnscanon() { out("Z\
66CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); }
67void temp_dns() { out("Z\
68Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); }
69void temp_chdir() { out("Z\
70Unable to switch to home directory. (#4.3.0)\n"); zerodie(); }
71void temp_control() { out("Z\
72Unable to read control files. (#4.3.0)\n"); zerodie(); }
73void perm_partialline() { out("D\
74SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); }
75void perm_usage() { out("D\
76I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); }
77void perm_dns() { out("D\
78Sorry, I couldn't find any host named ");
79outsafe(&host);
80out(". (#5.1.2)\n"); zerodie(); }
81void perm_nomx() { out("D\
82Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
83zerodie(); }
84void perm_ambigmx() { out("D\
85Sorry. Although I'm listed as a best-preference MX or A for that host,\n\
86it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
87zerodie(); }
88
89void outhost()
90{
91 char x[IPFMT];
92 if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0);
93}
94
95int flagcritical = 0;
96
97void dropped() {
98 out("ZConnected to ");
99 outhost();
100 out(" but connection died. ");
101 if (flagcritical) out("Possible duplicate! ");
102 out("(#4.4.2)\n");
103 zerodie();
104}
105
106int timeoutconnect = 60;
107int smtpfd;
108int timeout = 1200;
109
110int saferead(fd,buf,len) int fd; char *buf; int len;
111{
112 int r;
113 r = timeoutread(timeout,smtpfd,buf,len);
114 if (r <= 0) dropped();
115 return r;
116}
117int safewrite(fd,buf,len) int fd; char *buf; int len;
118{
119 int r;
120 r = timeoutwrite(timeout,smtpfd,buf,len);
121 if (r <= 0) dropped();
122 return r;
123}
124
125char inbuf[1024];
126substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
127char smtptobuf[1024];
128substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof smtptobuf);
129char smtpfrombuf[128];
130substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf);
131
132stralloc smtptext = {0};
133
134void get(ch)
135char *ch;
136{
137 substdio_get(&smtpfrom,ch,1);
138 if (*ch != '\r')
139 if (smtptext.len < HUGESMTPTEXT)
140 if (!stralloc_append(&smtptext,ch)) temp_nomem();
141}
142
143unsigned long smtpcode()
144{
145 unsigned char ch;
146 unsigned long code;
147
148 if (!stralloc_copys(&smtptext,"")) temp_nomem();
149
150 get(&ch); code = ch - '0';
151 get(&ch); code = code * 10 + (ch - '0');
152 get(&ch); code = code * 10 + (ch - '0');
153 for (;;) {
154 get(&ch);
155 if (ch != '-') break;
156 while (ch != '\n') get(&ch);
157 get(&ch);
158 get(&ch);
159 get(&ch);
160 }
161 while (ch != '\n') get(&ch);
162
163 return code;
164}
165
166void outsmtptext()
167{
168 int i;
169 if (smtptext.s) if (smtptext.len) {
170 out("Remote host said: ");
171 for (i = 0;i < smtptext.len;++i)
172 if (!smtptext.s[i]) smtptext.s[i] = '?';
173 if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0);
174 smtptext.len = 0;
175 }
176}
177
178void quit(prepend,append)
179char *prepend;
180char *append;
181{
182 substdio_putsflush(&smtpto,"QUIT\r\n");
183 /* waiting for remote side is just too ridiculous */
184 out(prepend);
185 outhost();
186 out(append);
187 out(".\n");
188 outsmtptext();
189 zerodie();
190}
191
192void blast()
193{
194 int r;
195 char ch;
196
197 for (;;) {
198 r = substdio_get(&ssin,&ch,1);
199 if (r == 0) break;
200 if (r == -1) temp_read();
201 if (ch == '.')
202 substdio_put(&smtpto,".",1);
203 while (ch != '\n') {
204 substdio_put(&smtpto,&ch,1);
205 r = substdio_get(&ssin,&ch,1);
206 if (r == 0) perm_partialline();
207 if (r == -1) temp_read();
208 }
209 substdio_put(&smtpto,"\r\n",2);
210 }
211
212 flagcritical = 1;
213 substdio_put(&smtpto,".\r\n",3);
214 substdio_flush(&smtpto);
215}
216
217stralloc recip = {0};
218
219void smtp()
220{
221 unsigned long code;
222 int flagbother;
223 int i;
224
225 if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
226
227 substdio_puts(&smtpto,"HELO ");
228 substdio_put(&smtpto,helohost.s,helohost.len);
229 substdio_puts(&smtpto,"\r\n");
230 substdio_flush(&smtpto);
231 if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
232
233 substdio_puts(&smtpto,"MAIL FROM:<");
234 substdio_put(&smtpto,sender.s,sender.len);
235 substdio_puts(&smtpto,">\r\n");
236 substdio_flush(&smtpto);
237 code = smtpcode();
238 if (code >= 500) quit("DConnected to "," but sender was rejected");
239 if (code >= 400) quit("ZConnected to "," but sender was rejected");
240
241 flagbother = 0;
242 for (i = 0;i < reciplist.len;++i) {
243 substdio_puts(&smtpto,"RCPT TO:<");
244 substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len);
245 substdio_puts(&smtpto,">\r\n");
246 substdio_flush(&smtpto);
247 code = smtpcode();
248 if (code >= 500) {
249 out("h"); outhost(); out(" does not like recipient.\n");
250 outsmtptext(); zero();
251 }
252 else if (code >= 400) {
253 out("s"); outhost(); out(" does not like recipient.\n");
254 outsmtptext(); zero();
255 }
256 else {
257 out("r"); zero();
258 flagbother = 1;
259 }
260 }
261 if (!flagbother) quit("DGiving up on ","");
262
263 substdio_putsflush(&smtpto,"DATA\r\n");
264 code = smtpcode();
265 if (code >= 500) quit("D"," failed on DATA command");
266 if (code >= 400) quit("Z"," failed on DATA command");
267
268 blast();
269 code = smtpcode();
270 flagcritical = 0;
271 if (code >= 500) quit("D"," failed after I sent the message");
272 if (code >= 400) quit("Z"," failed after I sent the message");
273 quit("K"," accepted message");
274}
275
276stralloc canonhost = {0};
277stralloc canonbox = {0};
278
279void addrmangle(saout,s,flagalias,flagcname)
280stralloc *saout; /* host has to be canonical, box has to be quoted */
281char *s;
282int *flagalias;
283int flagcname;
284{
285 int j;
286
287 *flagalias = flagcname;
288
289 j = str_rchr(s,'@');
290 if (!s[j]) {
291 if (!stralloc_copys(saout,s)) temp_nomem();
292 return;
293 }
294 if (!stralloc_copys(&canonbox,s)) temp_nomem();
295 canonbox.len = j;
296 if (!quote(saout,&canonbox)) temp_nomem();
297 if (!stralloc_cats(saout,"@")) temp_nomem();
298
299 if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
300 if (flagcname)
301 switch(dns_cname(&canonhost)) {
302 case 0: *flagalias = 0; break;
303 case DNS_MEM: temp_nomem();
304 case DNS_SOFT: temp_dnscanon();
305 case DNS_HARD: ; /* alias loop, not our problem */
306 }
307
308 if (!stralloc_cat(saout,&canonhost)) temp_nomem();
309}
310
311void getcontrols()
312{
313 if (control_init() == -1) temp_control();
314 if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
315 if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
316 temp_control();
317 if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1)
318 temp_control();
319 switch(control_readfile(&routes,"control/smtproutes",0)) {
320 case -1:
321 temp_control();
322 case 0:
323 if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break;
324 case 1:
325 if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
326 }
327}
328
329void main(argc,argv)
330int argc;
331char **argv;
332{
333 static ipalloc ip = {0};
334 int i;
335 unsigned long random;
336 char **recips;
337 unsigned long prefme;
338 int flagallaliases;
339 int flagalias;
340 char *relayhost;
341
342 sig_pipeignore();
343 if (argc < 4) perm_usage();
344 if (chdir(auto_qmail) == -1) temp_chdir();
345 getcontrols();
346
347
348 if (!stralloc_copys(&host,argv[1])) temp_nomem();
349
350 relayhost = 0;
351 for (i = 0;i <= host.len;++i)
352 if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
353 if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
354 break;
355 if (relayhost && !*relayhost) relayhost = 0;
356
357 if (relayhost) {
358 i = str_chr(relayhost,':');
359 if (relayhost[i]) {
360 scan_ulong(relayhost + i + 1,&port);
361 relayhost[i] = 0;
362 }
363 if (!stralloc_copys(&host,relayhost)) temp_nomem();
364 }
365
366
367 addrmangle(&sender,argv[2],&flagalias,0);
368
369 if (!saa_readyplus(&reciplist,0)) temp_nomem();
370 if (ipme_init() != 1) temp_oserr();
371
372 flagallaliases = 1;
373 recips = argv + 3;
374 while (*recips) {
375 if (!saa_readyplus(&reciplist,1)) temp_nomem();
376 reciplist.sa[reciplist.len] = sauninit;
377 addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost);
378 if (!flagalias) flagallaliases = 0;
379 ++reciplist.len;
380 ++recips;
381 }
382
383
384 random = now() + (getpid() << 16);
385 switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) {
386 case DNS_MEM: temp_nomem();
387 case DNS_SOFT: temp_dns();
388 case DNS_HARD: perm_dns();
389 case 1:
390 if (ip.len <= 0) temp_dns();
391 }
392
393 if (ip.len <= 0) perm_nomx();
394
395 prefme = 100000;
396 for (i = 0;i < ip.len;++i)
397 if (ipme_is(&ip.ix[i].ip))
398 if (ip.ix[i].pref < prefme)
399 prefme = ip.ix[i].pref;
400
401 if (relayhost) prefme = 300000;
402 if (flagallaliases) prefme = 500000;
403
404 for (i = 0;i < ip.len;++i)
405 if (ip.ix[i].pref < prefme)
406 break;
407
408 if (i >= ip.len)
409 perm_ambigmx();
410
411 for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) {
412 if (tcpto(&ip.ix[i].ip)) continue;
413
414 smtpfd = socket(AF_INET,SOCK_STREAM,0);
415 if (smtpfd == -1) temp_oserr();
416
417 if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
418 tcpto_err(&ip.ix[i].ip,0);
419 partner = ip.ix[i].ip;
420 smtp(); /* does not return */
421 }
422 tcpto_err(&ip.ix[i].ip,errno == error_timeout);
423 close(smtpfd);
424 }
425
426 temp_noconn();
427}
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 @@
1.TH qmail-rspawn 8
2.SH NAME
3qmail-rspawn \- schedule remote deliveries
4.SH SYNOPSIS
5.B qmail-rspawn
6.SH DESCRIPTION
7.B qmail-rspawn
8reads a series of remote delivery commands from descriptor 0,
9invokes
10.B qmail-remote
11to perform the deliveries,
12and prints the results to descriptor 1.
13
14.B qmail-rspawn
15invokes
16.B qmail-remote
17asynchronously,
18so the results may not be in the same order as the commands.
19.SH "SEE ALSO"
20qmail-send(8),
21qmail-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 @@
1#include "fd.h"
2#include "wait.h"
3#include "substdio.h"
4#include "exit.h"
5#include "fork.h"
6#include "error.h"
7#include "tcpto.h"
8
9void initialize(argc,argv)
10int argc;
11char **argv;
12{
13 tcpto_clean();
14}
15
16int truncreport = 0;
17
18void report(ss,wstat,s,len)
19substdio *ss;
20int wstat;
21char *s;
22int len;
23{
24 int j;
25 int k;
26 int result;
27 int orr;
28
29 if (wait_crashed(wstat))
30 { substdio_puts(ss,"Zqmail-remote crashed.\n"); return; }
31 switch(wait_exitcode(wstat))
32 {
33 case 0: break;
34 case 111: substdio_puts(ss,"ZUnable to run qmail-remote.\n"); return;
35 default: substdio_puts(ss,"DUnable to run qmail-remote.\n"); return;
36 }
37 if (!len)
38 { substdio_puts(ss,"Zqmail-remote produced no output.\n"); return; }
39
40 result = -1;
41 j = 0;
42 for (k = 0;k < len;++k)
43 if (!s[k])
44 {
45 if (s[j] == 'K') { result = 1; break; }
46 if (s[j] == 'Z') { result = 0; break; }
47 if (s[j] == 'D') break;
48 j = k + 1;
49 }
50
51 orr = result;
52 switch(s[0])
53 {
54 case 's': orr = 0; break;
55 case 'h': orr = -1;
56 }
57
58 switch(orr)
59 {
60 case 1: substdio_put(ss,"K",1); break;
61 case 0: substdio_put(ss,"Z",1); break;
62 case -1: substdio_put(ss,"D",1); break;
63 }
64
65 for (k = 1;k < len;)
66 if (!s[k++])
67 {
68 substdio_puts(ss,s + 1);
69 if (result <= orr)
70 if (k < len)
71 switch(s[k])
72 {
73 case 'Z': case 'D': case 'K':
74 substdio_puts(ss,s + k + 1);
75 }
76 break;
77 }
78}
79
80int spawn(fdmess,fdout,s,r,at)
81int fdmess; int fdout;
82char *s; char *r; int at;
83{
84 int f;
85 char *(args[5]);
86
87 args[0] = "qmail-remote";
88 args[1] = r + at + 1;
89 args[2] = s;
90 args[3] = r;
91 args[4] = 0;
92
93 if (!(f = vfork()))
94 {
95 if (fd_move(0,fdmess) == -1) _exit(111);
96 if (fd_move(1,fdout) == -1) _exit(111);
97 if (fd_copy(2,1) == -1) _exit(111);
98 execvp(*args,args);
99 if (error_temp(errno)) _exit(111);
100 _exit(100);
101 }
102 return f;
103}
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 @@
1.TH qmail-send 8
2.SH NAME
3qmail-send \- deliver mail messages from the queue
4.SH SYNOPSIS
5.B qmail-send
6.SH DESCRIPTION
7.B qmail-send
8handles messages placed into the outgoing queue by
9.BR qmail-queue .
10It uses
11.B qmail-lspawn
12to deliver messages to local recipients and
13.B qmail-rspawn
14to deliver messages to remote recipients.
15If a message is temporarily undeliverable to one or more addresses,
16.B qmail-send
17leaves it in the queue and tries the addresses again later.
18
19.B qmail-send
20prints a readable record of its activities to descriptor 0.
21It writes commands to
22.BR qmail-lspawn ,
23.BR qmail-rspawn ,
24and
25.B qmail-clean
26on descriptors 1, 3, and 5,
27and reads responses from descriptors 2, 4, and 6.
28.B qmail-send
29is responsible for avoiding deadlock.
30
31If
32.B qmail-send
33receives a TERM signal,
34it will exit cleanly, after waiting
35(possibly more than a minute)
36for current delivery attempts to finish.
37
38If
39.B qmail-send
40receives an ALRM signal,
41it will reschedule every message in the queue for immediate delivery.
42.SH "CONTROL FILES"
43.B WARNING:
44.B qmail-send
45reads its control files only when it starts.
46If you change the control files,
47you must stop and restart
48.BR qmail-send .
49Exception:
50If
51.B qmail-send
52receives a HUP signal,
53it will reread
54.I locals
55and
56.IR virtualdomains .
57.TP 5
58.I bouncefrom
59Bounce username.
60Default:
61.BR MAILER-DAEMON .
62.TP 5
63.I bouncehost
64Bounce host.
65Default:
66.IR me ,
67if that is supplied;
68otherwise the literal name
69.BR bouncehost ,
70which is probably not what you want.
71If a message is permanently undeliverable,
72.B qmail-send
73sends a
74.B single-bounce
75notice back to the message's envelope sender.
76The notice is
77.B From: \fIbouncefrom\fB@\fIbouncehost\fR,
78although its envelope sender is empty.
79.TP 5
80.I concurrencylocal
81Maximum number of simultaneous local delivery attempts.
82Default: 10.
83If 0, local deliveries will be put on hold.
84.I concurrencylocal
85is limited at compile time to
86SPAWN.
87.TP 5
88.I concurrencyremote
89Maximum number of simultaneous remote delivery attempts.
90Default: 20.
91If 0, remote deliveries will be put on hold.
92.I concurrencyremote
93is limited at compile time to
94SPAWN.
95.TP 5
96.I doublebouncehost
97Double-bounce host.
98Default:
99.IR me ,
100if that is supplied;
101otherwise the literal name
102.BR doublebouncehost ,
103which is probably not what you want.
104.TP 5
105.I doublebounceto
106User to receive double-bounces.
107Default:
108.BR postmaster .
109If a single-bounce notice is permanently undeliverable,
110.B qmail-send
111sends a
112.B double-bounce
113notice to
114.IR doublebounceto\fB@\fIdoublebouncehost .
115(If that bounces,
116.B qmail-send
117gives up.)
118.TP 5
119.I envnoathost
120Presumed domain name for addresses without @ signs.
121Default:
122.IR me ,
123if that is supplied;
124otherwise the literal name
125.BR envnoathost ,
126which is probably not what you want.
127If
128.B qmail-send
129sees an envelope recipient address without an @ sign,
130it appends
131.B @\fIenvnoathost\fR.
132.TP 5
133.I locals
134List of domain names that the current host
135receives mail for,
136one per line.
137Default:
138.IR me ,
139if that is supplied;
140otherwise
141.B qmail-send
142refuses to run.
143An address
144.I user@domain
145is considered local if
146.I domain
147is listed in
148.IR locals .
149.TP 5
150.I percenthack
151List of domain names where the percent hack is applied.
152If
153.I domain
154is listed in
155.IR percenthack ,
156any address of the form
157.I user%fqdn@domain
158is rewritten as
159.IR user@fqdn .
160.I user
161may contain %,
162so the percent hack may be applied repeatedly.
163.B qmail-send
164handles
165.I percenthack
166before
167.IR locals .
168.TP 5
169.I queuelifetime
170Number of seconds
171a message can stay in the queue.
172Default: 604800 (one week).
173After this time expires,
174.B qmail-send
175will try the message once more,
176but it will treat any temporary delivery failures as
177permanent failures.
178.TP 5
179.I virtualdomains
180List of virtual users or domains, one per line.
181A virtual user has the form
182.IR user\fB@\fIdomain\fB:\fIprepend ,
183without any extra spaces.
184When
185.B qmail-send
186sees the recipient address
187.IR user\fB@\fIdomain ,
188it converts it to
189.I prepend\fB-\fIuser\fB@\fIdomain
190and treats it as local.
191
192A virtual domain has the form
193.IR domain\fB:\fIprepend .
194It applies to any recipient address at
195.IR domain .
196For example, if
197
198.EX
199 nowhere.mil:joeBREAKfoo
200.EE
201
202is in
203.IR virtualdomains ,
204and a message arrives for
205.BR info@nowhere.mil ,
206.B qmail-send
207will rewrite the recipient address as
208.B joeBREAKfoo-info@nowhere.mil
209and deliver the message locally.
210
211.I virtualdomains
212may contain wildcards:
213
214.EX
215 .fax:uucpBREAKfax
216 :aliasBREAKcatchall
217 .nowhere.mil:joeBREAKfoo-host
218.EE
219
220.I virtualdomains
221may also contain exceptions:
222an empty
223.I prepend
224means that
225.I domain
226is not a virtual domain.
227
228.B qmail-send
229handles
230.I virtualdomains
231after
232.IR locals :
233if a domain is listed in
234.IR locals ,
235.I virtualdomains
236does not apply.
237.SH "SEE ALSO"
238nice(1),
239addresses(5),
240envelopes(5),
241qmail-control(5),
242qmail-log(5),
243qmail-queue(8),
244qmail-clean(8),
245qmail-lspawn(8),
246qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "readwrite.h"
4#include "sig.h"
5#include "direntry.h"
6#include "control.h"
7#include "select.h"
8#include "open.h"
9#include "seek.h"
10#include "exit.h"
11#include "lock.h"
12#include "ndelay.h"
13#include "now.h"
14#include "getln.h"
15#include "substdio.h"
16#include "alloc.h"
17#include "error.h"
18#include "stralloc.h"
19#include "str.h"
20#include "byte.h"
21#include "fmt.h"
22#include "scan.h"
23#include "case.h"
24#include "auto_qmail.h"
25#include "trigger.h"
26#include "newfield.h"
27#include "quote.h"
28#include "qmail.h"
29#include "qsutil.h"
30#include "prioq.h"
31#include "constmap.h"
32#include "fmtqfn.h"
33#include "readsubdir.h"
34
35/* critical timing feature #1: if not triggered, do not busy-loop */
36/* critical timing feature #2: if triggered, respond within fixed time */
37/* important timing feature: when triggered, respond instantly */
38#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
39#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
40#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
41#define SLEEP_CLEANUP 76431 /* time between cleanups */
42#define SLEEP_SYSFAIL 123
43#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */
44
45int lifetime = 604800;
46
47stralloc percenthack = {0};
48struct constmap mappercenthack;
49stralloc locals = {0};
50struct constmap maplocals;
51stralloc vdoms = {0};
52struct constmap mapvdoms;
53stralloc envnoathost = {0};
54stralloc bouncefrom = {0};
55stralloc bouncehost = {0};
56stralloc doublebounceto = {0};
57stralloc doublebouncehost = {0};
58
59char strnum2[FMT_ULONG];
60char strnum3[FMT_ULONG];
61
62#define CHANNELS 2
63char *chanaddr[CHANNELS] = { "local/", "remote/" };
64char *chanstatusmsg[CHANNELS] = { " local ", " remote " };
65char *tochan[CHANNELS] = { " to local ", " to remote " };
66int chanfdout[CHANNELS] = { 1, 3 };
67int chanfdin[CHANNELS] = { 2, 4 };
68int chanskip[CHANNELS] = { 10, 20 };
69
70int flagexitasap = 0; void sigterm() { flagexitasap = 1; }
71int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }
72int flagreadasap = 0; void sighup() { flagreadasap = 1; }
73
74void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n");
75 flagexitasap = 1; }
76
77int flagspawnalive[CHANNELS];
78void spawndied(c) int c; { log1("alert: oh no! lost spawn connection! dying...\n");
79 flagspawnalive[c] = 0; flagexitasap = 1; }
80
81#define REPORTMAX 10000
82
83datetime_sec recent;
84
85
86/* this file is too long ----------------------------------------- FILENAMES */
87
88stralloc fn = {0};
89stralloc fn2 = {0};
90char fnmake_strnum[FMT_ULONG];
91
92void fnmake_init()
93{
94 while (!stralloc_ready(&fn,FMTQFN)) nomem();
95 while (!stralloc_ready(&fn2,FMTQFN)) nomem();
96}
97
98void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }
99void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }
100void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }
101void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }
102void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }
103void fnmake2_bounce(id) unsigned long id;
104{ fn2.len = fmtqfn(fn2.s,"bounce/",id,0); }
105void fnmake_chanaddr(id,c) unsigned long id; int c;
106{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
107
108
109/* this file is too long ----------------------------------------- REWRITING */
110
111stralloc rwline = {0};
112
113/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
114/* may trash recip. must set up rwline, between a T and a \0. */
115int rewrite(recip)
116char *recip;
117{
118 int i;
119 int j;
120 char *x;
121 static stralloc addr = {0};
122 int at;
123
124 if (!stralloc_copys(&rwline,"T")) return 0;
125 if (!stralloc_copys(&addr,recip)) return 0;
126
127 i = byte_rchr(addr.s,addr.len,'@');
128 if (i == addr.len) {
129 if (!stralloc_cats(&addr,"@")) return 0;
130 if (!stralloc_cat(&addr,&envnoathost)) return 0;
131 }
132
133 while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
134 j = byte_rchr(addr.s,i,'%');
135 if (j == i) break;
136 addr.len = i;
137 i = j;
138 addr.s[i] = '@';
139 }
140
141 at = byte_rchr(addr.s,addr.len,'@');
142
143 if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
144 if (!stralloc_cat(&rwline,&addr)) return 0;
145 if (!stralloc_0(&rwline)) return 0;
146 return 1;
147 }
148
149 for (i = 0;i <= addr.len;++i)
150 if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
151 if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) {
152 if (!*x) break;
153 if (!stralloc_cats(&rwline,x)) return 0;
154 if (!stralloc_cats(&rwline,"-")) return 0;
155 if (!stralloc_cat(&rwline,&addr)) return 0;
156 if (!stralloc_0(&rwline)) return 0;
157 return 1;
158 }
159
160 if (!stralloc_cat(&rwline,&addr)) return 0;
161 if (!stralloc_0(&rwline)) return 0;
162 return 2;
163}
164
165void senderadd(sa,sender,recip)
166stralloc *sa;
167char *sender;
168char *recip;
169{
170 int i;
171 int j;
172 int k;
173
174 i = str_len(sender);
175 if (i >= 4)
176 if (str_equal(sender + i - 4,"-@[]"))
177 {
178 j = byte_rchr(sender,i - 4,'@');
179 k = str_rchr(recip,'@');
180 if (recip[k] && (j + 5 <= i))
181 {
182 /* owner-@host-@[] -> owner-recipbox=reciphost@host */
183 while (!stralloc_catb(sa,sender,j)) nomem();
184 while (!stralloc_catb(sa,recip,k)) nomem();
185 while (!stralloc_cats(sa,"=")) nomem();
186 while (!stralloc_cats(sa,recip + k + 1)) nomem();
187 while (!stralloc_cats(sa,"@")) nomem();
188 while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem();
189 return;
190 }
191 }
192 while (!stralloc_cats(sa,sender)) nomem();
193}
194
195
196/* this file is too long ---------------------------------------------- INFO */
197
198int getinfo(sa,dt,id)
199stralloc *sa;
200datetime_sec *dt;
201unsigned long id;
202{
203 int fdinfo;
204 struct stat st;
205 static stralloc line = {0};
206 int match;
207 substdio ss;
208 char buf[128];
209
210 fnmake_info(id);
211 fdinfo = open_read(fn.s);
212 if (fdinfo == -1) return 0;
213 if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; }
214 substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf));
215 if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; }
216 close(fdinfo);
217 if (!match) return 0;
218 if (line.s[0] != 'F') return 0;
219
220 *dt = st.st_mtime;
221 while (!stralloc_copys(sa,line.s + 1)) nomem();
222 while (!stralloc_0(sa)) nomem();
223 return 1;
224}
225
226
227/* this file is too long ------------------------------------- COMMUNICATION */
228
229substdio sstoqc; char sstoqcbuf[1024];
230substdio ssfromqc; char ssfromqcbuf[1024];
231stralloc comm_buf[CHANNELS] = { {0}, {0} };
232int comm_pos[CHANNELS];
233
234void comm_init()
235{
236 int c;
237 substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf));
238 substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf));
239 for (c = 0;c < CHANNELS;++c)
240 if (ndelay_on(chanfdout[c]) == -1)
241 /* this is so stupid: NDELAY semantics should be default on write */
242 spawndied(c); /* drastic, but better than risking deadlock */
243}
244
245int comm_canwrite(c)
246int c;
247{
248 /* XXX: could allow a bigger buffer; say 10 recipients */
249 if (comm_buf[c].s && comm_buf[c].len) return 0;
250 return 1;
251}
252
253void comm_write(c,delnum,id,sender,recip)
254int c;
255int delnum;
256unsigned long id;
257char *sender;
258char *recip;
259{
260 char ch;
261 if (comm_buf[c].s && comm_buf[c].len) return;
262 while (!stralloc_copys(&comm_buf[c],"")) nomem();
263 ch = delnum;
264 while (!stralloc_append(&comm_buf[c],&ch)) nomem();
265 fnmake_split(id);
266 while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
267 while (!stralloc_0(&comm_buf[c])) nomem();
268 senderadd(&comm_buf[c],sender,recip);
269 while (!stralloc_0(&comm_buf[c])) nomem();
270 while (!stralloc_cats(&comm_buf[c],recip)) nomem();
271 while (!stralloc_0(&comm_buf[c])) nomem();
272 comm_pos[c] = 0;
273}
274
275void comm_selprep(nfds,wfds)
276int *nfds;
277fd_set *wfds;
278{
279 int c;
280 for (c = 0;c < CHANNELS;++c)
281 if (flagspawnalive[c])
282 if (comm_buf[c].s && comm_buf[c].len)
283 {
284 FD_SET(chanfdout[c],wfds);
285 if (*nfds <= chanfdout[c])
286 *nfds = chanfdout[c] + 1;
287 }
288}
289
290void comm_do(wfds)
291fd_set *wfds;
292{
293 int c;
294 for (c = 0;c < CHANNELS;++c)
295 if (flagspawnalive[c])
296 if (comm_buf[c].s && comm_buf[c].len)
297 if (FD_ISSET(chanfdout[c],wfds))
298 {
299 int w;
300 int len;
301 len = comm_buf[c].len;
302 w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]);
303 if (w <= 0)
304 {
305 if ((w == -1) && (errno == error_pipe))
306 spawndied(c);
307 else
308 continue; /* kernel select() bug; can't avoid busy-looping */
309 }
310 else
311 {
312 comm_pos[c] += w;
313 if (comm_pos[c] == len)
314 comm_buf[c].len = 0;
315 }
316 }
317}
318
319
320/* this file is too long ------------------------------------------ CLEANUPS */
321
322int flagcleanup; /* if 1, cleanupdir is initialized and ready */
323readsubdir cleanupdir;
324datetime_sec cleanuptime;
325
326void cleanup_init()
327{
328 flagcleanup = 0;
329 cleanuptime = now();
330}
331
332void cleanup_selprep(wakeup)
333datetime_sec *wakeup;
334{
335 if (flagcleanup) *wakeup = 0;
336 if (*wakeup > cleanuptime) *wakeup = cleanuptime;
337}
338
339void cleanup_do()
340{
341 char ch;
342 struct stat st;
343 unsigned long id;
344
345 if (!flagcleanup)
346 {
347 if (recent < cleanuptime) return;
348 readsubdir_init(&cleanupdir,"mess",pausedir);
349 flagcleanup = 1;
350 }
351
352 switch(readsubdir_next(&cleanupdir,&id))
353 {
354 case 1:
355 break;
356 case 0:
357 flagcleanup = 0;
358 cleanuptime = recent + SLEEP_CLEANUP;
359 default:
360 return;
361 }
362
363 fnmake_mess(id);
364 if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */
365 if (recent <= st.st_atime + OSSIFIED) return;
366
367 fnmake_info(id);
368 if (stat(fn.s,&st) == 0) return;
369 if (errno != error_noent) return;
370 fnmake_todo(id);
371 if (stat(fn.s,&st) == 0) return;
372 if (errno != error_noent) return;
373
374 fnmake_foop(id);
375 if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
376 if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
377 if (ch != '+')
378 log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
379}
380
381
382/* this file is too long ----------------------------------- PRIORITY QUEUES */
383
384prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */
385prioq pqchan[CHANNELS] = { {0}, {0} };
386/* pqchan 0: -todo +info +local ?remote */
387/* pqchan 1: -todo +info ?local +remote */
388prioq pqfail = {0}; /* stat() failure; has to be pqadded again */
389
390void pqadd(id)
391unsigned long id;
392{
393 struct prioq_elt pe;
394 struct prioq_elt pechan[CHANNELS];
395 int flagchan[CHANNELS];
396 struct stat st;
397 int c;
398
399#define CHECKSTAT if (errno != error_noent) goto fail;
400
401 fnmake_info(id);
402 if (stat(fn.s,&st) == -1)
403 {
404 CHECKSTAT
405 return; /* someone yanking our chain */
406 }
407
408 fnmake_todo(id);
409 if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */
410 CHECKSTAT
411
412 for (c = 0;c < CHANNELS;++c)
413 {
414 fnmake_chanaddr(id,c);
415 if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT }
416 else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; }
417 }
418
419 for (c = 0;c < CHANNELS;++c)
420 if (flagchan[c])
421 while (!prioq_insert(&pqchan[c],&pechan[c])) nomem();
422
423 for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
424 if (c == CHANNELS)
425 {
426 pe.id = id; pe.dt = now();
427 while (!prioq_insert(&pqdone,&pe)) nomem();
428 }
429
430 return;
431
432 fail:
433 log3("warning: unable to stat ",fn.s,"; will try again later\n");
434 pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
435 while (!prioq_insert(&pqfail,&pe)) nomem();
436}
437
438void pqstart()
439{
440 readsubdir rs;
441 int x;
442 unsigned long id;
443
444 readsubdir_init(&rs,"info",pausedir);
445
446 while (x = readsubdir_next(&rs,&id))
447 if (x > 0)
448 pqadd(id);
449}
450
451void pqfinish()
452{
453 int c;
454 struct prioq_elt pe;
455 time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */
456
457 for (c = 0;c < CHANNELS;++c)
458 while (prioq_min(&pqchan[c],&pe))
459 {
460 prioq_delmin(&pqchan[c]);
461 fnmake_chanaddr(pe.id,c);
462 ut[0] = ut[1] = pe.dt;
463 if (utime(fn.s,ut) == -1)
464 log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n");
465 }
466}
467
468void pqrun()
469{
470 int c;
471 int i;
472 for (c = 0;c < CHANNELS;++c)
473 if (pqchan[c].p)
474 if (pqchan[c].len)
475 for (i = 0;i < pqchan[c].len;++i)
476 pqchan[c].p[i].dt = recent;
477}
478
479
480/* this file is too long ---------------------------------------------- JOBS */
481
482struct job
483 {
484 int refs; /* if 0, this struct is unused */
485 unsigned long id;
486 int channel;
487 datetime_sec retry;
488 stralloc sender;
489 int numtodo;
490 int flaghiteof;
491 int flagdying;
492 }
493;
494
495int numjobs;
496struct job *jo;
497
498void job_init()
499{
500 int j;
501 while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem();
502 for (j = 0;j < numjobs;++j)
503 {
504 jo[j].refs = 0;
505 jo[j].sender.s = 0;
506 }
507}
508
509int job_avail()
510{
511 int j;
512 for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1;
513 return 0;
514}
515
516int job_open(id,channel)
517unsigned long id;
518int channel;
519{
520 int j;
521 for (j = 0;j < numjobs;++j) if (!jo[j].refs) break;
522 if (j == numjobs) return -1;
523 jo[j].refs = 1;
524 jo[j].id = id;
525 jo[j].channel = channel;
526 jo[j].numtodo = 0;
527 jo[j].flaghiteof = 0;
528 return j;
529}
530
531void job_close(j)
532int j;
533{
534 struct prioq_elt pe;
535 struct stat st;
536
537 if (0 < --jo[j].refs) return;
538
539 pe.id = jo[j].id;
540 pe.dt = jo[j].retry;
541 if (jo[j].flaghiteof && !jo[j].numtodo)
542 {
543 fnmake_chanaddr(jo[j].id,jo[j].channel);
544 if (unlink(fn.s) == -1)
545 {
546 log3("warning: unable to unlink ",fn.s,"; will try again later\n");
547 pe.dt = now() + SLEEP_SYSFAIL;
548 }
549 else
550 {
551 int c;
552 for (c = 0;c < CHANNELS;++c) if (c != jo[j].channel)
553 {
554 fnmake_chanaddr(jo[j].id,c);
555 if (stat(fn.s,&st) == 0) return; /* more channels going */
556 if (errno != error_noent)
557 {
558 log3("warning: unable to stat ",fn.s,"\n");
559 break; /* this is the only reason for HOPEFULLY */
560 }
561 }
562 pe.dt = now();
563 while (!prioq_insert(&pqdone,&pe)) nomem();
564 return;
565 }
566 }
567
568 while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem();
569}
570
571
572/* this file is too long ------------------------------------------- BOUNCES */
573
574char *stripvdomprepend(recip)
575char *recip;
576{
577 int i;
578 char *domain;
579 int domainlen;
580 char *prepend;
581
582 i = str_rchr(recip,'@');
583 if (!recip[i]) return recip;
584 domain = recip + i + 1;
585 domainlen = str_len(domain);
586
587 for (i = 0;i <= domainlen;++i)
588 if ((i == 0) || (i == domainlen) || (domain[i] == '.'))
589 if (prepend = constmap(&mapvdoms,domain + i,domainlen - i))
590 {
591 if (!*prepend) break;
592 i = str_len(prepend);
593 if (str_diffn(recip,prepend,i)) break;
594 if (recip[i] != '-') break;
595 return recip + i + 1;
596 }
597 return recip;
598}
599
600stralloc bouncetext = {0};
601
602void addbounce(id,recip,report)
603unsigned long id;
604char *recip;
605char *report;
606{
607 int fd;
608 int pos;
609 int w;
610 while (!stralloc_copys(&bouncetext,"<")) nomem();
611 while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem();
612 for (pos = 0;pos < bouncetext.len;++pos)
613 if (bouncetext.s[pos] == '\n')
614 bouncetext.s[pos] = '_';
615 while (!stralloc_cats(&bouncetext,">:\n")) nomem();
616 while (!stralloc_cats(&bouncetext,report)) nomem();
617 if (report[0])
618 if (report[str_len(report) - 1] != '\n')
619 while (!stralloc_cats(&bouncetext,"\n")) nomem();
620 for (pos = bouncetext.len - 2;pos > 0;--pos)
621 if (bouncetext.s[pos] == '\n')
622 if (bouncetext.s[pos - 1] == '\n')
623 bouncetext.s[pos] = '/';
624 while (!stralloc_cats(&bouncetext,"\n")) nomem();
625 fnmake2_bounce(id);
626 for (;;)
627 {
628 fd = open_append(fn2.s);
629 if (fd != -1) break;
630 log1("alert: unable to append to bounce message; HELP! sleeping...\n");
631 sleep(10);
632 }
633 pos = 0;
634 while (pos < bouncetext.len)
635 {
636 w = write(fd,bouncetext.s + pos,bouncetext.len - pos);
637 if (w <= 0)
638 {
639 log1("alert: unable to append to bounce message; HELP! sleeping...\n");
640 sleep(10);
641 }
642 else
643 pos += w;
644 }
645 close(fd);
646}
647
648int injectbounce(id)
649unsigned long id;
650{
651 struct qmail qqt;
652 struct stat st;
653 char *bouncesender;
654 char *bouncerecip;
655 int r;
656 int fd;
657 substdio ssread;
658 char buf[128];
659 char inbuf[128];
660 static stralloc sender = {0};
661 static stralloc quoted = {0};
662 datetime_sec birth;
663 unsigned long qp;
664
665 if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */
666
667 /* owner-@host-@[] -> owner-@host */
668 if (sender.len >= 5)
669 if (str_equal(sender.s + sender.len - 5,"-@[]"))
670 {
671 sender.len -= 4;
672 sender.s[sender.len - 1] = 0;
673 }
674
675 fnmake2_bounce(id);
676 fnmake_mess(id);
677 if (stat(fn2.s,&st) == -1)
678 {
679 if (errno == error_noent)
680 return 1;
681 log3("warning: unable to stat ",fn2.s,"\n");
682 return 0;
683 }
684 if (str_equal(sender.s,"#@[]"))
685 log3("triple bounce: discarding ",fn2.s,"\n");
686 else
687 {
688 if (qmail_open(&qqt) == -1)
689 { log1("warning: unable to start qmail-queue, will try later\n"); return 0; }
690 qp = qmail_qp(&qqt);
691
692 if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
693 else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
694
695 while (!newfield_datemake(now())) nomem();
696 qmail_put(&qqt,newfield_date.s,newfield_date.len);
697 qmail_puts(&qqt,"From: ");
698 while (!quote(&quoted,&bouncefrom)) nomem();
699 qmail_put(&qqt,quoted.s,quoted.len);
700 qmail_puts(&qqt,"@");
701 qmail_put(&qqt,bouncehost.s,bouncehost.len);
702 qmail_puts(&qqt,"\nTo: ");
703 while (!quote2(&quoted,bouncerecip)) nomem();
704 qmail_put(&qqt,quoted.s,quoted.len);
705 qmail_puts(&qqt,"\n\
706Subject: failure notice\n\
707\n\
708Hi. This is the qmail-send program at ");
709 qmail_put(&qqt,bouncehost.s,bouncehost.len);
710 qmail_puts(&qqt,*sender.s ? ".\n\
711I'm afraid I wasn't able to deliver your message to the following addresses.\n\
712This is a permanent error; I've given up. Sorry it didn't work out.\n\
713\n\
714" : ".\n\
715I tried to deliver a bounce message to this address, but the bounce bounced!\n\
716\n\
717");
718
719 fd = open_read(fn2.s);
720 if (fd == -1)
721 qmail_fail(&qqt);
722 else
723 {
724 substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
725 while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
726 qmail_put(&qqt,buf,r);
727 close(fd);
728 if (r == -1)
729 qmail_fail(&qqt);
730 }
731
732 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");
733 qmail_puts(&qqt,"Return-Path: <");
734 while (!quote2(&quoted,sender.s)) nomem();
735 qmail_put(&qqt,quoted.s,quoted.len);
736 qmail_puts(&qqt,">\n");
737
738 fd = open_read(fn.s);
739 if (fd == -1)
740 qmail_fail(&qqt);
741 else
742 {
743 substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
744 while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
745 qmail_put(&qqt,buf,r);
746 close(fd);
747 if (r == -1)
748 qmail_fail(&qqt);
749 }
750
751 qmail_from(&qqt,bouncesender);
752 qmail_to(&qqt,bouncerecip);
753 if (*qmail_close(&qqt))
754 { log1("warning: trouble injecting bounce message, will try later\n"); return 0; }
755
756 strnum2[fmt_ulong(strnum2,id)] = 0;
757 log2("bounce msg ",strnum2);
758 strnum2[fmt_ulong(strnum2,qp)] = 0;
759 log3(" qp ",strnum2,"\n");
760 }
761 if (unlink(fn2.s) == -1)
762 {
763 log3("warning: unable to unlink ",fn2.s,"\n");
764 return 0;
765 }
766 return 1;
767}
768
769
770/* this file is too long ---------------------------------------- DELIVERIES */
771
772struct del
773 {
774 int used;
775 int j;
776 unsigned long delid;
777 seek_pos mpos;
778 stralloc recip;
779 }
780;
781
782unsigned long masterdelid = 1;
783unsigned int concurrency[CHANNELS] = { 10, 20 };
784unsigned int concurrencyused[CHANNELS] = { 0, 0 };
785struct del *d[CHANNELS];
786stralloc dline[CHANNELS];
787char delbuf[2048];
788
789void del_status()
790{
791 int c;
792
793 log1("status:");
794 for (c = 0;c < CHANNELS;++c) {
795 strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0;
796 strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0;
797 log2(chanstatusmsg[c],strnum2);
798 log2("/",strnum3);
799 }
800 if (flagexitasap) log1(" exitasap");
801 log1("\n");
802}
803
804void del_init()
805{
806 int c;
807 int i;
808 for (c = 0;c < CHANNELS;++c)
809 {
810 flagspawnalive[c] = 1;
811 while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del))))
812 nomem();
813 for (i = 0;i < concurrency[c];++i)
814 { d[c][i].used = 0; d[c][i].recip.s = 0; }
815 dline[c].s = 0;
816 while (!stralloc_copys(&dline[c],"")) nomem();
817 }
818 del_status();
819}
820
821int del_canexit()
822{
823 int c;
824 for (c = 0;c < CHANNELS;++c)
825 if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */
826 if (concurrencyused[c]) return 0;
827 return 1;
828}
829
830int del_avail(c)
831int c;
832{
833 return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]);
834}
835
836void del_start(j,mpos,recip)
837int j;
838seek_pos mpos;
839char *recip;
840{
841 int i;
842 int c;
843
844 c = jo[j].channel;
845 if (!flagspawnalive[c]) return;
846 if (!comm_canwrite(c)) return;
847
848 for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) break;
849 if (i == concurrency[c]) return;
850
851 if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; }
852 if (!stralloc_0(&d[c][i].recip)) { nomem(); return; }
853 d[c][i].j = j; ++jo[j].refs;
854 d[c][i].delid = masterdelid++;
855 d[c][i].mpos = mpos;
856 d[c][i].used = 1; ++concurrencyused[c];
857
858 comm_write(c,i,jo[j].id,jo[j].sender.s,recip);
859
860 strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0;
861 strnum3[fmt_ulong(strnum3,jo[j].id)] = 0;
862 log2("starting delivery ",strnum2);
863 log3(": msg ",strnum3,tochan[c]);
864 logsafe(recip);
865 log1("\n");
866 del_status();
867}
868
869void markdone(c,id,pos)
870int c;
871unsigned long id;
872seek_pos pos;
873{
874 struct stat st;
875 int fd;
876 fnmake_chanaddr(id,c);
877 for (;;)
878 {
879 fd = open_write(fn.s);
880 if (fd == -1) break;
881 if (fstat(fd,&st) == -1) { close(fd); break; }
882 if (seek_set(fd,pos) == -1) { close(fd); break; }
883 if (write(fd,"D",1) != 1) { close(fd); break; }
884 /* further errors -> double delivery without us knowing about it, oh well */
885 close(fd);
886 return;
887 }
888 log3("warning: trouble marking ",fn.s,"; message will be delivered twice!\n");
889}
890
891void del_dochan(c)
892int c;
893{
894 int r;
895 char ch;
896 int i;
897 int delnum;
898 r = read(chanfdin[c],delbuf,sizeof(delbuf));
899 if (r == -1) return;
900 if (r == 0) { spawndied(c); return; }
901 for (i = 0;i < r;++i)
902 {
903 ch = delbuf[i];
904 while (!stralloc_append(&dline[c],&ch)) nomem();
905 if (dline[c].len > REPORTMAX)
906 dline[c].len = REPORTMAX;
907 /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
908 /* but from a security point of view, we don't trust rspawn */
909 if (!ch && (dline[c].len > 1))
910 {
911 delnum = (unsigned int) (unsigned char) dline[c].s[0];
912 if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
913 log1("warning: internal error: delivery report out of range\n");
914 else
915 {
916 strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
917 if (dline[c].s[1] == 'Z')
918 if (jo[d[c][delnum].j].flagdying)
919 {
920 dline[c].s[1] = 'D';
921 --dline[c].len;
922 while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
923 while (!stralloc_0(&dline[c])) nomem();
924 }
925 switch(dline[c].s[1])
926 {
927 case 'K':
928 log3("delivery ",strnum3,": success: ");
929 logsafe(dline[c].s + 2);
930 log1("\n");
931 markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
932 --jo[d[c][delnum].j].numtodo;
933 break;
934 case 'Z':
935 log3("delivery ",strnum3,": deferral: ");
936 logsafe(dline[c].s + 2);
937 log1("\n");
938 break;
939 case 'D':
940 log3("delivery ",strnum3,": failure: ");
941 logsafe(dline[c].s + 2);
942 log1("\n");
943 addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
944 markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
945 --jo[d[c][delnum].j].numtodo;
946 break;
947 default:
948 log3("delivery ",strnum3,": report mangled, will defer\n");
949 }
950 job_close(d[c][delnum].j);
951 d[c][delnum].used = 0; --concurrencyused[c];
952 del_status();
953 }
954 dline[c].len = 0;
955 }
956 }
957}
958
959void del_selprep(nfds,rfds)
960int *nfds;
961fd_set *rfds;
962{
963 int c;
964 for (c = 0;c < CHANNELS;++c)
965 if (flagspawnalive[c])
966 {
967 FD_SET(chanfdin[c],rfds);
968 if (*nfds <= chanfdin[c])
969 *nfds = chanfdin[c] + 1;
970 }
971}
972
973void del_do(rfds)
974fd_set *rfds;
975{
976 int c;
977 for (c = 0;c < CHANNELS;++c)
978 if (flagspawnalive[c])
979 if (FD_ISSET(chanfdin[c],rfds))
980 del_dochan(c);
981}
982
983
984/* this file is too long -------------------------------------------- PASSES */
985
986struct
987 {
988 unsigned long id; /* if 0, need a new pass */
989 int j; /* defined if id; job number */
990 int fd; /* defined if id; reading from {local,remote} */
991 seek_pos mpos; /* defined if id; mark position */
992 substdio ss;
993 char buf[128];
994 }
995pass[CHANNELS];
996
997void pass_init()
998{
999 int c;
1000 for (c = 0;c < CHANNELS;++c) pass[c].id = 0;
1001}
1002
1003void pass_selprep(wakeup)
1004datetime_sec *wakeup;
1005{
1006 int c;
1007 struct prioq_elt pe;
1008 if (flagexitasap) return;
1009 for (c = 0;c < CHANNELS;++c)
1010 if (pass[c].id)
1011 if (del_avail(c))
1012 { *wakeup = 0; return; }
1013 if (job_avail())
1014 for (c = 0;c < CHANNELS;++c)
1015 if (!pass[c].id)
1016 if (prioq_min(&pqchan[c],&pe))
1017 if (*wakeup > pe.dt)
1018 *wakeup = pe.dt;
1019 if (prioq_min(&pqfail,&pe))
1020 if (*wakeup > pe.dt)
1021 *wakeup = pe.dt;
1022 if (prioq_min(&pqdone,&pe))
1023 if (*wakeup > pe.dt)
1024 *wakeup = pe.dt;
1025}
1026
1027static datetime_sec squareroot(x) /* result^2 <= x < (result + 1)^2 */
1028datetime_sec x; /* assuming: >= 0 */
1029{
1030 datetime_sec y;
1031 datetime_sec yy;
1032 datetime_sec y21;
1033 int j;
1034
1035 y = 0; yy = 0;
1036 for (j = 15;j >= 0;--j)
1037 {
1038 y21 = (y << (j + 1)) + (1 << (j + j));
1039 if (y21 <= x - yy) { y += (1 << j); yy += y21; }
1040 }
1041 return y;
1042}
1043
1044datetime_sec nextretry(birth,c)
1045datetime_sec birth;
1046int c;
1047{
1048 int n;
1049
1050 if (birth > recent) n = 0;
1051 else n = squareroot(recent - birth); /* no need to add fuzz to recent */
1052 n += chanskip[c];
1053 return birth + n * n;
1054}
1055
1056void pass_dochan(c)
1057int c;
1058{
1059 datetime_sec birth;
1060 struct prioq_elt pe;
1061 static stralloc line = {0};
1062 int match;
1063
1064 if (flagexitasap) return;
1065
1066 if (!pass[c].id)
1067 {
1068 if (!job_avail()) return;
1069 if (!prioq_min(&pqchan[c],&pe)) return;
1070 if (pe.dt > recent) return;
1071 fnmake_chanaddr(pe.id,c);
1072
1073 prioq_delmin(&pqchan[c]);
1074 pass[c].mpos = 0;
1075 pass[c].fd = open_read(fn.s);
1076 if (pass[c].fd == -1) goto trouble;
1077 if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; }
1078 pass[c].id = pe.id;
1079 substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf));
1080 pass[c].j = job_open(pe.id,c);
1081 jo[pass[c].j].retry = nextretry(birth,c);
1082 jo[pass[c].j].flagdying = (recent > birth + lifetime);
1083 while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem();
1084 }
1085
1086 if (!del_avail(c)) return;
1087
1088 if (getln(&pass[c].ss,&line,&match,'\0') == -1)
1089 {
1090 fnmake_chanaddr(pass[c].id,c);
1091 log3("warning: trouble reading ",fn.s,"; will try again later\n");
1092 close(pass[c].fd);
1093 job_close(pass[c].j);
1094 pass[c].id = 0;
1095 return;
1096 }
1097 if (!match)
1098 {
1099 close(pass[c].fd);
1100 jo[pass[c].j].flaghiteof = 1;
1101 job_close(pass[c].j);
1102 pass[c].id = 0;
1103 return;
1104 }
1105 switch(line.s[0])
1106 {
1107 case 'T':
1108 ++jo[pass[c].j].numtodo;
1109 del_start(pass[c].j,pass[c].mpos,line.s + 1);
1110 break;
1111 case 'D':
1112 break;
1113 default:
1114 fnmake_chanaddr(pass[c].id,c);
1115 log3("warning: unknown record type in ",fn.s,"!\n");
1116 close(pass[c].fd);
1117 job_close(pass[c].j);
1118 pass[c].id = 0;
1119 return;
1120 }
1121
1122 pass[c].mpos += line.len;
1123 return;
1124
1125 trouble:
1126 log3("warning: trouble opening ",fn.s,"; will try again later\n");
1127 pe.dt = recent + SLEEP_SYSFAIL;
1128 while (!prioq_insert(&pqchan[c],&pe)) nomem();
1129}
1130
1131void messdone(id)
1132unsigned long id;
1133{
1134 char ch;
1135 int c;
1136 struct prioq_elt pe;
1137 struct stat st;
1138
1139 for (c = 0;c < CHANNELS;++c)
1140 {
1141 fnmake_chanaddr(id,c);
1142 if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */
1143 if (errno != error_noent)
1144 {
1145 log3("warning: unable to stat ",fn.s,"; will try again later\n");
1146 goto fail;
1147 }
1148 }
1149
1150 fnmake_todo(id);
1151 if (stat(fn.s,&st) == 0) return;
1152 if (errno != error_noent)
1153 {
1154 log3("warning: unable to stat ",fn.s,"; will try again later\n");
1155 goto fail;
1156 }
1157
1158 fnmake_info(id);
1159 if (stat(fn.s,&st) == -1)
1160 {
1161 if (errno == error_noent) return;
1162 log3("warning: unable to stat ",fn.s,"; will try again later\n");
1163 goto fail;
1164 }
1165
1166 /* -todo +info -local -remote ?bounce */
1167 if (!injectbounce(id))
1168 goto fail; /* injectbounce() produced error message */
1169
1170 strnum3[fmt_ulong(strnum3,id)] = 0;
1171 log3("end msg ",strnum3,"\n");
1172
1173 /* -todo +info -local -remote -bounce */
1174 fnmake_info(id);
1175 if (unlink(fn.s) == -1)
1176 {
1177 log3("warning: unable to unlink ",fn.s,"; will try again later\n");
1178 goto fail;
1179 }
1180
1181 /* -todo -info -local -remote -bounce; we can relax */
1182 fnmake_foop(id);
1183 if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
1184 if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
1185 if (ch != '+')
1186 log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
1187
1188 return;
1189
1190 fail:
1191 pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
1192 while (!prioq_insert(&pqdone,&pe)) nomem();
1193}
1194
1195void pass_do()
1196{
1197 int c;
1198 struct prioq_elt pe;
1199
1200 for (c = 0;c < CHANNELS;++c) pass_dochan(c);
1201 if (prioq_min(&pqfail,&pe))
1202 if (pe.dt <= recent)
1203 {
1204 prioq_delmin(&pqfail);
1205 pqadd(pe.id);
1206 }
1207 if (prioq_min(&pqdone,&pe))
1208 if (pe.dt <= recent)
1209 {
1210 prioq_delmin(&pqdone);
1211 messdone(pe.id);
1212 }
1213}
1214
1215
1216/* this file is too long ---------------------------------------------- TODO */
1217
1218datetime_sec nexttodorun;
1219DIR *tododir; /* if 0, have to opendir again */
1220stralloc todoline = {0};
1221char todobuf[SUBSTDIO_INSIZE];
1222char todobufinfo[512];
1223char todobufchan[CHANNELS][1024];
1224
1225void todo_init()
1226{
1227 tododir = 0;
1228 nexttodorun = now();
1229 trigger_set();
1230}
1231
1232void todo_selprep(nfds,rfds,wakeup)
1233int *nfds;
1234fd_set *rfds;
1235datetime_sec *wakeup;
1236{
1237 if (flagexitasap) return;
1238 trigger_selprep(nfds,rfds);
1239 if (tododir) *wakeup = 0;
1240 if (*wakeup > nexttodorun) *wakeup = nexttodorun;
1241}
1242
1243void todo_do(rfds)
1244fd_set *rfds;
1245{
1246 struct stat st;
1247 substdio ss; int fd;
1248 substdio ssinfo; int fdinfo;
1249 substdio sschan[CHANNELS];
1250 int fdchan[CHANNELS];
1251 int flagchan[CHANNELS];
1252 struct prioq_elt pe;
1253 char ch;
1254 int match;
1255 unsigned long id;
1256 unsigned int len;
1257 direntry *d;
1258 int c;
1259 unsigned long uid;
1260 unsigned long pid;
1261
1262 fd = -1;
1263 fdinfo = -1;
1264 for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
1265
1266 if (flagexitasap) return;
1267
1268 if (!tododir)
1269 {
1270 if (!trigger_pulled(rfds))
1271 if (recent < nexttodorun)
1272 return;
1273 trigger_set();
1274 tododir = opendir("todo");
1275 if (!tododir)
1276 {
1277 pausedir("todo");
1278 return;
1279 }
1280 nexttodorun = recent + SLEEP_TODO;
1281 }
1282
1283 d = readdir(tododir);
1284 if (!d)
1285 {
1286 closedir(tododir);
1287 tododir = 0;
1288 return;
1289 }
1290 if (str_equal(d->d_name,".")) return;
1291 if (str_equal(d->d_name,"..")) return;
1292 len = scan_ulong(d->d_name,&id);
1293 if (!len || d->d_name[len]) return;
1294
1295 fnmake_todo(id);
1296
1297 fd = open_read(fn.s);
1298 if (fd == -1) { log3("warning: unable to open ",fn.s,"\n"); return; }
1299
1300 fnmake_mess(id);
1301 /* just for the statistics */
1302 if (stat(fn.s,&st) == -1)
1303 { log3("warning: unable to stat ",fn.s,"\n"); goto fail; }
1304
1305 for (c = 0;c < CHANNELS;++c)
1306 {
1307 fnmake_chanaddr(id,c);
1308 if (unlink(fn.s) == -1) if (errno != error_noent)
1309 { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
1310 }
1311
1312 fnmake_info(id);
1313 if (unlink(fn.s) == -1) if (errno != error_noent)
1314 { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
1315
1316 fdinfo = open_excl(fn.s);
1317 if (fdinfo == -1)
1318 { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
1319
1320 strnum3[fmt_ulong(strnum3,id)] = 0;
1321 log3("new msg ",strnum3,"\n");
1322
1323 for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
1324
1325 substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
1326 substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
1327
1328 uid = 0;
1329 pid = 0;
1330
1331 for (;;)
1332 {
1333 if (getln(&ss,&todoline,&match,'\0') == -1)
1334 {
1335 /* perhaps we're out of memory, perhaps an I/O error */
1336 fnmake_todo(id);
1337 log3("warning: trouble reading ",fn.s,"\n"); goto fail;
1338 }
1339 if (!match) break;
1340
1341 switch(todoline.s[0])
1342 {
1343 case 'u':
1344 scan_ulong(todoline.s + 1,&uid);
1345 break;
1346 case 'p':
1347 scan_ulong(todoline.s + 1,&pid);
1348 break;
1349 case 'F':
1350 if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
1351 {
1352 fnmake_info(id);
1353 log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
1354 }
1355 log2("info msg ",strnum3);
1356 strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0;
1357 log2(": bytes ",strnum2);
1358 log1(" from <"); logsafe(todoline.s + 1);
1359 strnum2[fmt_ulong(strnum2,pid)] = 0;
1360 log2("> qp ",strnum2);
1361 strnum2[fmt_ulong(strnum2,uid)] = 0;
1362 log2(" uid ",strnum2);
1363 log1("\n");
1364 break;
1365 case 'T':
1366 switch(rewrite(todoline.s + 1))
1367 {
1368 case 0: nomem(); goto fail;
1369 case 2: c = 1; break;
1370 default: c = 0; break;
1371 }
1372 if (fdchan[c] == -1)
1373 {
1374 fnmake_chanaddr(id,c);
1375 fdchan[c] = open_excl(fn.s);
1376 if (fdchan[c] == -1)
1377 { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
1378 substdio_fdbuf(&sschan[c]
1379 ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
1380 flagchan[c] = 1;
1381 }
1382 if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
1383 {
1384 fnmake_chanaddr(id,c);
1385 log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
1386 }
1387 break;
1388 default:
1389 fnmake_todo(id);
1390 log3("warning: unknown record type in ",fn.s,"\n"); goto fail;
1391 }
1392 }
1393
1394 close(fd); fd = -1;
1395
1396 fnmake_info(id);
1397 if (substdio_flush(&ssinfo) == -1)
1398 { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
1399 if (fsync(fdinfo) == -1)
1400 { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
1401 close(fdinfo); fdinfo = -1;
1402
1403 for (c = 0;c < CHANNELS;++c)
1404 if (fdchan[c] != -1)
1405 {
1406 fnmake_chanaddr(id,c);
1407 if (substdio_flush(&sschan[c]) == -1)
1408 { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
1409 if (fsync(fdchan[c]) == -1)
1410 { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
1411 close(fdchan[c]); fdchan[c] = -1;
1412 }
1413
1414 fnmake_todo(id);
1415 if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
1416 if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
1417 if (ch != '+')
1418 {
1419 log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
1420 return;
1421 }
1422
1423 pe.id = id; pe.dt = now();
1424 for (c = 0;c < CHANNELS;++c)
1425 if (flagchan[c])
1426 while (!prioq_insert(&pqchan[c],&pe)) nomem();
1427
1428 for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
1429 if (c == CHANNELS)
1430 while (!prioq_insert(&pqdone,&pe)) nomem();
1431
1432 return;
1433
1434 fail:
1435 if (fd != -1) close(fd);
1436 if (fdinfo != -1) close(fdinfo);
1437 for (c = 0;c < CHANNELS;++c)
1438 if (fdchan[c] != -1) close(fdchan[c]);
1439}
1440
1441
1442/* this file is too long ---------------------------------------------- MAIN */
1443
1444int getcontrols() { if (control_init() == -1) return 0;
1445 if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
1446 if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
1447 if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
1448 if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
1449 if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
1450 if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
1451 if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
1452 if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
1453 if (!stralloc_cats(&doublebounceto,"@")) return 0;
1454 if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
1455 if (!stralloc_0(&doublebounceto)) return 0;
1456 if (control_readfile(&locals,"control/locals",1) != 1) return 0;
1457 if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
1458 switch(control_readfile(&percenthack,"control/percenthack",0))
1459 {
1460 case -1: return 0;
1461 case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
1462 case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
1463 }
1464 switch(control_readfile(&vdoms,"control/virtualdomains",0))
1465 {
1466 case -1: return 0;
1467 case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
1468 case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
1469 }
1470 return 1; }
1471
1472stralloc newlocals = {0};
1473stralloc newvdoms = {0};
1474
1475void regetcontrols()
1476{
1477 int r;
1478
1479 if (control_readfile(&newlocals,"control/locals",1) != 1)
1480 { log1("alert: unable to reread control/locals\n"); return; }
1481 r = control_readfile(&newvdoms,"control/virtualdomains",0);
1482 if (r == -1)
1483 { log1("alert: unable to reread control/virtualdomains\n"); return; }
1484
1485 constmap_free(&maplocals);
1486 constmap_free(&mapvdoms);
1487
1488 while (!stralloc_copy(&locals,&newlocals)) nomem();
1489 while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
1490
1491 if (r)
1492 {
1493 while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
1494 while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
1495 }
1496 else
1497 while (!constmap_init(&mapvdoms,"",0,1)) nomem();
1498}
1499
1500void reread()
1501{
1502 if (chdir(auto_qmail) == -1)
1503 {
1504 log1("alert: unable to reread controls: unable to switch to home directory\n");
1505 return;
1506 }
1507 regetcontrols();
1508 while (chdir("queue") == -1)
1509 {
1510 log1("alert: unable to switch back to queue directory; HELP! sleeping...\n");
1511 sleep(10);
1512 }
1513}
1514
1515void main()
1516{
1517 int fd;
1518 datetime_sec wakeup;
1519 fd_set rfds;
1520 fd_set wfds;
1521 int nfds;
1522 struct timeval tv;
1523 int c;
1524
1525 if (chdir(auto_qmail) == -1)
1526 { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); }
1527 if (!getcontrols())
1528 { log1("alert: cannot start: unable to read controls\n"); _exit(111); }
1529 if (chdir("queue") == -1)
1530 { log1("alert: cannot start: unable to switch to queue directory\n"); _exit(111); }
1531 sig_pipeignore();
1532 sig_termcatch(sigterm);
1533 sig_alarmcatch(sigalrm);
1534 sig_hangupcatch(sighup);
1535 sig_childdefault();
1536 umask(077);
1537
1538 fd = open_write("lock/sendmutex");
1539 if (fd == -1)
1540 { log1("alert: cannot start: unable to open mutex\n"); _exit(111); }
1541 if (lock_exnb(fd) == -1)
1542 { log1("alert: cannot start: qmail-send is already running\n"); _exit(111); }
1543
1544 numjobs = 0;
1545 for (c = 0;c < CHANNELS;++c)
1546 {
1547 char ch;
1548 int u;
1549 int r;
1550 do
1551 r = read(chanfdin[c],&ch,1);
1552 while ((r == -1) && (errno == error_intr));
1553 if (r < 1)
1554 { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
1555 u = (unsigned int) (unsigned char) ch;
1556 if (concurrency[c] > u) concurrency[c] = u;
1557 numjobs += concurrency[c];
1558 }
1559
1560 fnmake_init();
1561
1562 comm_init();
1563
1564 pqstart();
1565 job_init();
1566 del_init();
1567 pass_init();
1568 todo_init();
1569 cleanup_init();
1570
1571 while (!flagexitasap || !del_canexit())
1572 {
1573 recent = now();
1574
1575 if (flagrunasap) { flagrunasap = 0; pqrun(); }
1576 if (flagreadasap) { flagreadasap = 0; reread(); }
1577
1578 wakeup = recent + SLEEP_FOREVER;
1579 FD_ZERO(&rfds);
1580 FD_ZERO(&wfds);
1581 nfds = 1;
1582
1583 comm_selprep(&nfds,&wfds);
1584 del_selprep(&nfds,&rfds);
1585 pass_selprep(&wakeup);
1586 todo_selprep(&nfds,&rfds,&wakeup);
1587 cleanup_selprep(&wakeup);
1588
1589 if (wakeup <= recent) tv.tv_sec = 0;
1590 else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
1591 tv.tv_usec = 0;
1592
1593 if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
1594 if (errno == error_intr)
1595 ;
1596 else
1597 log1("warning: trouble in select\n");
1598 else
1599 {
1600 recent = now();
1601
1602 comm_do(&wfds);
1603 del_do(&rfds);
1604 todo_do(&rfds);
1605 pass_do();
1606 cleanup_do();
1607 }
1608 }
1609 pqfinish();
1610 log1("status: exiting\n");
1611 _exit(0);
1612}
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 @@
1.TH qmail-showctl 8
2.SH NAME
3qmail-showctl \- analyze the qmail configuration files
4.SH SYNOPSIS
5.B qmail-showctl
6.SH DESCRIPTION
7.B qmail-showctl
8explains the current
9.B qmail
10configuration.
11.SH "SEE ALSO"
12qmail-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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "substdio.h"
4#include "subfd.h"
5#include "exit.h"
6#include "fmt.h"
7#include "str.h"
8#include "control.h"
9#include "constmap.h"
10#include "stralloc.h"
11#include "direntry.h"
12#include "auto_uids.h"
13#include "auto_qmail.h"
14#include "auto_break.h"
15#include "auto_patrn.h"
16#include "auto_spawn.h"
17#include "auto_split.h"
18
19stralloc me = {0};
20int meok;
21
22stralloc line = {0};
23char num[FMT_ULONG];
24
25void safeput(buf,len)
26char *buf;
27unsigned int len;
28{
29 char ch;
30
31 while (len > 0) {
32 ch = *buf;
33 if ((ch < 32) || (ch > 126)) ch = '?';
34 substdio_put(subfdout,&ch,1);
35 ++buf;
36 --len;
37 }
38}
39
40void do_int(fn,def,pre,post)
41char *fn;
42char *def;
43char *pre;
44char *post;
45{
46 int i;
47 substdio_puts(subfdout,"\n");
48 substdio_puts(subfdout,fn);
49 substdio_puts(subfdout,": ");
50 switch(control_readint(&i,fn)) {
51 case 0:
52 substdio_puts(subfdout,"(Default.) ");
53 substdio_puts(subfdout,pre);
54 substdio_puts(subfdout,def);
55 substdio_puts(subfdout,post);
56 substdio_puts(subfdout,".\n");
57 break;
58 case 1:
59 if (i < 0) i = 0;
60 substdio_puts(subfdout,pre);
61 substdio_put(subfdout,num,fmt_uint(num,i));
62 substdio_puts(subfdout,post);
63 substdio_puts(subfdout,".\n");
64 break;
65 default:
66 substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
67 break;
68 }
69}
70
71void do_str(fn,flagme,def,pre)
72char *fn;
73int flagme;
74char *def;
75char *pre;
76{
77 substdio_puts(subfdout,"\n");
78 substdio_puts(subfdout,fn);
79 substdio_puts(subfdout,": ");
80 switch(control_readline(&line,fn)) {
81 case 0:
82 substdio_puts(subfdout,"(Default.) ");
83 if (!stralloc_copys(&line,def)) {
84 substdio_puts(subfdout,"Oops! Out of memory.\n");
85 break;
86 }
87 if (flagme && meok)
88 if (!stralloc_copy(&line,&me)) {
89 substdio_puts(subfdout,"Oops! Out of memory.\n");
90 break;
91 }
92 case 1:
93 substdio_puts(subfdout,pre);
94 safeput(line.s,line.len);
95 substdio_puts(subfdout,".\n");
96 break;
97 default:
98 substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
99 break;
100 }
101}
102
103int do_lst(fn,def,pre,post)
104char *fn;
105char *def;
106char *pre;
107char *post;
108{
109 int i;
110 int j;
111
112 substdio_puts(subfdout,"\n");
113 substdio_puts(subfdout,fn);
114 substdio_puts(subfdout,": ");
115 switch(control_readfile(&line,fn)) {
116 case 0:
117 substdio_puts(subfdout,"(Default.) ");
118 substdio_puts(subfdout,def);
119 substdio_puts(subfdout,"\n");
120 return 0;
121 case 1:
122 substdio_puts(subfdout,"\n");
123 i = 0;
124 for (j = 0;j < line.len;++j)
125 if (!line.s[j]) {
126 substdio_puts(subfdout,pre);
127 safeput(line.s + i,j - i);
128 substdio_puts(subfdout,post);
129 substdio_puts(subfdout,"\n");
130 i = j + 1;
131 }
132 return 1;
133 default:
134 substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
135 return -1;
136 }
137}
138
139void main()
140{
141 DIR *dir;
142 direntry *d;
143 struct stat stmrh;
144 struct stat stmrhcdb;
145
146 substdio_puts(subfdout,"qmail home directory: ");
147 substdio_puts(subfdout,auto_qmail);
148 substdio_puts(subfdout,".\n");
149
150 substdio_puts(subfdout,"user-ext delimiter: ");
151 substdio_puts(subfdout,auto_break);
152 substdio_puts(subfdout,".\n");
153
154 substdio_puts(subfdout,"paternalism (in decimal): ");
155 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_patrn));
156 substdio_puts(subfdout,".\n");
157
158 substdio_puts(subfdout,"silent concurrency limit: ");
159 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_spawn));
160 substdio_puts(subfdout,".\n");
161
162 substdio_puts(subfdout,"subdirectory split: ");
163 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_split));
164 substdio_puts(subfdout,".\n");
165
166 substdio_puts(subfdout,"user ids: ");
167 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uida));
168 substdio_puts(subfdout,", ");
169 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidd));
170 substdio_puts(subfdout,", ");
171 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidl));
172 substdio_puts(subfdout,", ");
173 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uido));
174 substdio_puts(subfdout,", ");
175 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidp));
176 substdio_puts(subfdout,", ");
177 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidq));
178 substdio_puts(subfdout,", ");
179 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidr));
180 substdio_puts(subfdout,", ");
181 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uids));
182 substdio_puts(subfdout,".\n");
183
184 substdio_puts(subfdout,"group ids: ");
185 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidn));
186 substdio_puts(subfdout,", ");
187 substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidq));
188 substdio_puts(subfdout,".\n");
189
190 if (chdir(auto_qmail) == -1) {
191 substdio_puts(subfdout,"Oops! Unable to chdir to ");
192 substdio_puts(subfdout,auto_qmail);
193 substdio_puts(subfdout,".\n");
194 substdio_flush(subfdout);
195 _exit(111);
196 }
197 if (chdir("control") == -1) {
198 substdio_puts(subfdout,"Oops! Unable to chdir to control.\n");
199 substdio_flush(subfdout);
200 _exit(111);
201 }
202
203 dir = opendir(".");
204 if (!dir) {
205 substdio_puts(subfdout,"Oops! Unable to open current directory.\n");
206 substdio_flush(subfdout);
207 _exit(111);
208 }
209
210 meok = control_readline(&me,"me");
211 if (meok == -1) {
212 substdio_puts(subfdout,"Oops! Trouble reading control/me.");
213 substdio_flush(subfdout);
214 _exit(111);
215 }
216
217 do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
218 do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
219 do_str("bouncehost",1,"bouncehost","Bounce host name is ");
220 do_int("concurrencylocal","10","Local concurrency is ","");
221 do_int("concurrencyremote","20","Remote concurrency is ","");
222 do_int("databytes","0","SMTP DATA limit is "," bytes");
223 do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
224 do_str("defaulthost",1,"defaulthost","Default host name is ");
225 do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
226 do_str("doublebounceto",0,"postmaster","2B recipient user: ");
227 do_str("envnoathost",1,"envnoathost","Presumed domain name is ");
228 do_str("helohost",1,"helohost","SMTP client HELO host name is ");
229 do_str("idhost",1,"idhost","Message-ID host name is ");
230 do_str("localiphost",1,"localiphost","Local IP address becomes ");
231 do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally.");
232 do_str("me",0,"undefined! Uh-oh","My name is ");
233 do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
234 do_str("plusdomain",1,"plusdomain","Plus domain name is ");
235 do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
236 do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds");
237
238 if (do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ","."))
239 do_lst("morercpthosts","No effect.","SMTP clients may send messages to recipients at ",".");
240 else
241 do_lst("morercpthosts","No rcpthosts; morercpthosts is irrelevant.","No rcpthosts; doesn't matter that morercpthosts has ",".");
242 /* XXX: check morercpthosts.cdb contents */
243 substdio_puts(subfdout,"\nmorercpthosts.cdb: ");
244 if (stat("morercpthosts",&stmrh) == -1)
245 if (stat("morercpthosts.cdb",&stmrhcdb) == -1)
246 substdio_puts(subfdout,"(Default.) No effect.\n");
247 else
248 substdio_puts(subfdout,"Oops! morercpthosts.cdb exists but morercpthosts doesn't.\n");
249 else
250 if (stat("morercpthosts.cdb",&stmrhcdb) == -1)
251 substdio_puts(subfdout,"Oops! morercpthosts exists but morercpthosts.cdb doesn't.\n");
252 else
253 if (stmrh.st_mtime > stmrhcdb.st_mtime)
254 substdio_puts(subfdout,"Oops! morercpthosts.cdb is older than morercpthosts.\n");
255 else
256 substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n");
257
258 do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
259 do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
260 do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
261 do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
262 do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
263 do_lst("virtualdomains","No virtual domains.","Virtual domain: ","");
264
265 while (d = readdir(dir)) {
266 if (str_equal(d->d_name,".")) continue;
267 if (str_equal(d->d_name,"..")) continue;
268 if (str_equal(d->d_name,"bouncefrom")) continue;
269 if (str_equal(d->d_name,"bouncehost")) continue;
270 if (str_equal(d->d_name,"badmailfrom")) continue;
271 if (str_equal(d->d_name,"bouncefrom")) continue;
272 if (str_equal(d->d_name,"bouncehost")) continue;
273 if (str_equal(d->d_name,"concurrencylocal")) continue;
274 if (str_equal(d->d_name,"concurrencyremote")) continue;
275 if (str_equal(d->d_name,"databytes")) continue;
276 if (str_equal(d->d_name,"defaultdomain")) continue;
277 if (str_equal(d->d_name,"defaulthost")) continue;
278 if (str_equal(d->d_name,"doublebouncehost")) continue;
279 if (str_equal(d->d_name,"doublebounceto")) continue;
280 if (str_equal(d->d_name,"envnoathost")) continue;
281 if (str_equal(d->d_name,"helohost")) continue;
282 if (str_equal(d->d_name,"idhost")) continue;
283 if (str_equal(d->d_name,"localiphost")) continue;
284 if (str_equal(d->d_name,"locals")) continue;
285 if (str_equal(d->d_name,"me")) continue;
286 if (str_equal(d->d_name,"morercpthosts")) continue;
287 if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
288 if (str_equal(d->d_name,"percenthack")) continue;
289 if (str_equal(d->d_name,"plusdomain")) continue;
290 if (str_equal(d->d_name,"qmqpservers")) continue;
291 if (str_equal(d->d_name,"queuelifetime")) continue;
292 if (str_equal(d->d_name,"rcpthosts")) continue;
293 if (str_equal(d->d_name,"smtpgreeting")) continue;
294 if (str_equal(d->d_name,"smtproutes")) continue;
295 if (str_equal(d->d_name,"timeoutconnect")) continue;
296 if (str_equal(d->d_name,"timeoutremote")) continue;
297 if (str_equal(d->d_name,"timeoutsmtpd")) continue;
298 if (str_equal(d->d_name,"virtualdomains")) continue;
299 substdio_puts(subfdout,"\n");
300 substdio_puts(subfdout,d->d_name);
301 substdio_puts(subfdout,": I have no idea what this file does.\n");
302 }
303
304 substdio_flush(subfdout);
305 _exit(0);
306}
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 @@
1.TH qmail-smtpd 8
2.SH NAME
3qmail-smtpd \- receive mail via SMTP
4.SH SYNOPSIS
5.B qmail-smtpd
6.SH DESCRIPTION
7.B qmail-smtpd
8receives mail messages via the Simple Mail Transfer Protocol (SMTP)
9and invokes
10.B qmail-queue
11to deposit them into the outgoing queue.
12.B qmail-smtpd
13must be supplied several environment variables;
14see
15.BR tcp-environ(5) .
16
17.B qmail-smtpd
18is responsible for counting hops.
19It rejects any message with 100 or more
20.B Received
21or
22.B Delivered-To
23header fields.
24
25.B qmail-smtpd
26supports ESMTP, including the 8BITMIME and PIPELINING options.
27.SH TRANSPARENCY
28.B qmail-smtpd
29converts the SMTP newline convention into the UNIX newline convention
30by converting CR LF into LF.
31It returns a temporary error and drops the connection on bare LFs;
32see
33.BR http://pobox.com/~djb/docs/smtplf.html .
34
35.B qmail-smtpd
36accepts messages that contain long lines or non-ASCII characters,
37even though such messages violate the SMTP protocol.
38.SH "CONTROL FILES"
39.TP 5
40.I badmailfrom
41Unacceptable envelope sender addresses.
42.B qmail-smtpd
43will reject every recipient address for a message
44if the envelope sender address is listed in
45.IR badmailfrom .
46A line in
47.I badmailfrom
48may be of the form
49.BR @\fIhost ,
50meaning every address at
51.IR host .
52.TP 5
53.I databytes
54Maximum number of bytes allowed in a message,
55or 0 for no limit.
56Default: 0.
57If a message exceeds this limit,
58.B qmail-smtpd
59returns a permanent error code to the client;
60in contrast, if
61the disk is full or
62.B qmail-smtpd
63hits a resource limit,
64.B qmail-smtpd
65returns a temporary error code.
66
67.I databytes
68counts bytes as stored on disk, not as transmitted through the network.
69It does not count the
70.B qmail-smtpd
71Received line, the
72.B qmail-queue
73Received line, or the envelope.
74
75If the environment variable
76.B DATABYTES
77is set, it overrides
78.IR databytes .
79.TP 5
80.I localiphost
81Replacement host name for local IP addresses.
82Default:
83.IR me ,
84if that is supplied.
85.B qmail-smtpd
86is responsible for recognizing dotted-decimal addresses for the
87current host.
88When it sees a recipient address of the form
89.IR box@[d.d.d.d] ,
90where
91.I d.d.d.d
92is a local IP address,
93it replaces
94.IR [d.d.d.d]
95with
96.IR localiphost .
97This is done before
98.IR rcpthosts .
99.TP 5
100.I morercpthosts
101Extra allowed RCPT domains.
102If
103.I rcpthosts
104and
105.I morercpthosts
106both exist,
107.I morercpthosts
108is effectively appended to
109.IR rcpthosts .
110
111You must run
112.B qmail-newmrh
113whenever
114.I morercpthosts
115changes.
116
117Rule of thumb for large sites:
118Put your 50 most commonly used domains into
119.IR rcpthosts ,
120and the rest into
121.IR morercpthosts .
122.TP 5
123.I rcpthosts
124Allowed RCPT domains.
125If
126.I rcpthosts
127is supplied,
128.B qmail-smtpd
129will reject
130any envelope recipient address with a domain not listed in
131.IR rcpthosts .
132
133Exception:
134If the environment variable
135.B RELAYCLIENT
136is set,
137.B qmail-smtpd
138will ignore
139.IR rcpthosts ,
140and will append the value of
141.B RELAYCLIENT
142to each incoming recipient address.
143
144.I rcpthosts
145may include wildcards:
146
147.EX
148 heaven.af.mil
149 .heaven.af.mil
150.EE
151
152Envelope recipient addresses without @ signs are
153always allowed through.
154.TP 5
155.I smtpgreeting
156SMTP greeting message.
157Default:
158.IR me ,
159if that is supplied;
160otherwise
161.B qmail-smtpd
162will refuse to run.
163The first word of
164.I smtpgreeting
165should be the current host's name.
166.TP 5
167.I timeoutsmtpd
168Number of seconds
169.B qmail-smtpd
170will wait for each new buffer of data from the remote SMTP client.
171Default: 1200.
172.SH "SEE ALSO"
173tcp-env(1),
174tcp-environ(5),
175qmail-control(5),
176qmail-inject(8),
177qmail-newmrh(8),
178qmail-queue(8),
179qmail-remote(8)
diff --git a/qmail-smtpd.c b/qmail-smtpd.c
new file mode 100644
index 0000000..1e28c88
--- /dev/null
+++ b/qmail-smtpd.c
@@ -0,0 +1,421 @@
1#include "sig.h"
2#include "readwrite.h"
3#include "stralloc.h"
4#include "substdio.h"
5#include "alloc.h"
6#include "auto_qmail.h"
7#include "control.h"
8#include "received.h"
9#include "constmap.h"
10#include "error.h"
11#include "ipme.h"
12#include "ip.h"
13#include "qmail.h"
14#include "str.h"
15#include "fmt.h"
16#include "scan.h"
17#include "byte.h"
18#include "case.h"
19#include "env.h"
20#include "now.h"
21#include "exit.h"
22#include "rcpthosts.h"
23#include "timeoutread.h"
24#include "timeoutwrite.h"
25#include "commands.h"
26
27#define MAXHOPS 100
28unsigned int databytes = 0;
29int timeout = 1200;
30
31int safewrite(fd,buf,len) int fd; char *buf; int len;
32{
33 int r;
34 r = timeoutwrite(timeout,fd,buf,len);
35 if (r <= 0) _exit(1);
36 return r;
37}
38
39char ssoutbuf[512];
40substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
41
42void flush() { substdio_flush(&ssout); }
43void out(s) char *s; { substdio_puts(&ssout,s); }
44
45void die_read() { _exit(1); }
46void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
47void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
48void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
49void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
50void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
51
52void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
53void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
54void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
55void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
56void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
57void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
58void err_noop() { out("250 ok\r\n"); }
59void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
60void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
61
62
63stralloc greeting = {0};
64
65void smtp_greet(code) char *code;
66{
67 substdio_puts(&ssout,code);
68 substdio_put(&ssout,greeting.s,greeting.len);
69}
70void smtp_help()
71{
72 out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
73}
74void smtp_quit()
75{
76 smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
77}
78
79char *remoteip;
80char *remotehost;
81char *remoteinfo;
82char *local;
83char *relayclient;
84
85stralloc helohost = {0};
86char *fakehelo; /* pointer into helohost, or 0 */
87
88void dohelo(arg) char *arg; {
89 if (!stralloc_copys(&helohost,arg)) die_nomem();
90 if (!stralloc_0(&helohost)) die_nomem();
91 fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
92}
93
94int liphostok = 0;
95stralloc liphost = {0};
96int bmfok = 0;
97stralloc bmf = {0};
98struct constmap mapbmf;
99
100void setup()
101{
102 char *x;
103 unsigned long u;
104
105 if (control_init() == -1) die_control();
106 if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
107 die_control();
108 liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0);
109 if (liphostok == -1) die_control();
110 if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
111 if (timeout <= 0) timeout = 1;
112
113 if (rcpthosts_init() == -1) die_control();
114
115 bmfok = control_readfile(&bmf,"control/badmailfrom",0);
116 if (bmfok == -1) die_control();
117 if (bmfok)
118 if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
119
120 if (control_readint(&databytes,"control/databytes") == -1) die_control();
121 x = env_get("DATABYTES");
122 if (x) { scan_ulong(x,&u); databytes = u; }
123 if (!(databytes + 1)) --databytes;
124
125 remoteip = env_get("TCPREMOTEIP");
126 if (!remoteip) remoteip = "unknown";
127 local = env_get("TCPLOCALHOST");
128 if (!local) local = env_get("TCPLOCALIP");
129 if (!local) local = "unknown";
130 remotehost = env_get("TCPREMOTEHOST");
131 if (!remotehost) remotehost = "unknown";
132 remoteinfo = env_get("TCPREMOTEINFO");
133 relayclient = env_get("RELAYCLIENT");
134 dohelo(remotehost);
135}
136
137
138stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
139
140int addrparse(arg)
141char *arg;
142{
143 int i;
144 char ch;
145 char terminator;
146 struct ip_address ip;
147 int flagesc;
148 int flagquoted;
149
150 terminator = '>';
151 i = str_chr(arg,'<');
152 if (arg[i])
153 arg += i + 1;
154 else { /* partner should go read rfc 821 */
155 terminator = ' ';
156 arg += str_chr(arg,':');
157 if (*arg == ':') ++arg;
158 while (*arg == ' ') ++arg;
159 }
160
161 /* strip source route */
162 if (*arg == '@') while (*arg) if (*arg++ == ':') break;
163
164 if (!stralloc_copys(&addr,"")) die_nomem();
165 flagesc = 0;
166 flagquoted = 0;
167 for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */
168 if (flagesc) {
169 if (!stralloc_append(&addr,&ch)) die_nomem();
170 flagesc = 0;
171 }
172 else {
173 if (!flagquoted && (ch == terminator)) break;
174 switch(ch) {
175 case '\\': flagesc = 1; break;
176 case '"': flagquoted = !flagquoted; break;
177 default: if (!stralloc_append(&addr,&ch)) die_nomem();
178 }
179 }
180 }
181 /* could check for termination failure here, but why bother? */
182 if (!stralloc_append(&addr,"")) die_nomem();
183
184 if (liphostok) {
185 i = byte_rchr(addr.s,addr.len,'@');
186 if (i < addr.len) /* if not, partner should go read rfc 821 */
187 if (addr.s[i + 1] == '[')
188 if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])
189 if (ipme_is(&ip)) {
190 addr.len = i + 1;
191 if (!stralloc_cat(&addr,&liphost)) die_nomem();
192 if (!stralloc_0(&addr)) die_nomem();
193 }
194 }
195
196 if (addr.len > 900) return 0;
197 return 1;
198}
199
200int bmfcheck()
201{
202 int j;
203 if (!bmfok) return 0;
204 if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
205 j = byte_rchr(addr.s,addr.len,'@');
206 if (j < addr.len)
207 if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
208 return 0;
209}
210
211int addrallowed()
212{
213 int r;
214 r = rcpthosts(addr.s,str_len(addr.s));
215 if (r == -1) die_control();
216 return r;
217}
218
219
220int seenmail = 0;
221int flagbarf; /* defined if seenmail */
222stralloc mailfrom = {0};
223stralloc rcptto = {0};
224
225void smtp_helo(arg) char *arg;
226{
227 smtp_greet("250 "); out("\r\n");
228 seenmail = 0; dohelo(arg);
229}
230void smtp_ehlo(arg) char *arg;
231{
232 smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
233 seenmail = 0; dohelo(arg);
234}
235void smtp_rset()
236{
237 seenmail = 0;
238 out("250 flushed\r\n");
239}
240void smtp_mail(arg) char *arg;
241{
242 if (!addrparse(arg)) { err_syntax(); return; }
243 flagbarf = bmfcheck();
244 seenmail = 1;
245 if (!stralloc_copys(&rcptto,"")) die_nomem();
246 if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
247 if (!stralloc_0(&mailfrom)) die_nomem();
248 out("250 ok\r\n");
249}
250void smtp_rcpt(arg) char *arg; {
251 if (!seenmail) { err_wantmail(); return; }
252 if (!addrparse(arg)) { err_syntax(); return; }
253 if (flagbarf) { err_bmf(); return; }
254 if (relayclient) {
255 --addr.len;
256 if (!stralloc_cats(&addr,relayclient)) die_nomem();
257 if (!stralloc_0(&addr)) die_nomem();
258 }
259 else
260 if (!addrallowed()) { err_nogateway(); return; }
261 if (!stralloc_cats(&rcptto,"T")) die_nomem();
262 if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
263 if (!stralloc_0(&rcptto)) die_nomem();
264 out("250 ok\r\n");
265}
266
267
268int saferead(fd,buf,len) int fd; char *buf; int len;
269{
270 int r;
271 flush();
272 r = timeoutread(timeout,fd,buf,len);
273 if (r == -1) if (errno == error_timeout) die_alarm();
274 if (r <= 0) die_read();
275 return r;
276}
277
278char ssinbuf[1024];
279substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
280
281struct qmail qqt;
282unsigned int bytestooverflow = 0;
283
284void put(ch)
285char *ch;
286{
287 if (bytestooverflow)
288 if (!--bytestooverflow)
289 qmail_fail(&qqt);
290 qmail_put(&qqt,ch,1);
291}
292
293void blast(hops)
294int *hops;
295{
296 char ch;
297 int state;
298 int flaginheader;
299 int pos; /* number of bytes since most recent \n, if fih */
300 int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
301 int flagmaybey; /* 1 if this line might match \r\n, if fih */
302 int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
303
304 state = 1;
305 *hops = 0;
306 flaginheader = 1;
307 pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
308 for (;;) {
309 substdio_get(&ssin,&ch,1);
310 if (flaginheader) {
311 if (pos < 9) {
312 if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;
313 if (flagmaybez) if (pos == 8) ++*hops;
314 if (pos < 8)
315 if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;
316 if (flagmaybex) if (pos == 7) ++*hops;
317 if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
318 if (flagmaybey) if (pos == 1) flaginheader = 0;
319 }
320 ++pos;
321 if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
322 }
323 switch(state) {
324 case 0:
325 if (ch == '\n') straynewline();
326 if (ch == '\r') { state = 4; continue; }
327 break;
328 case 1: /* \r\n */
329 if (ch == '\n') straynewline();
330 if (ch == '.') { state = 2; continue; }
331 if (ch == '\r') { state = 4; continue; }
332 state = 0;
333 break;
334 case 2: /* \r\n + . */
335 if (ch == '\n') straynewline();
336 if (ch == '\r') { state = 3; continue; }
337 state = 0;
338 break;
339 case 3: /* \r\n + .\r */
340 if (ch == '\n') return;
341 put(".");
342 put("\r");
343 if (ch == '\r') { state = 4; continue; }
344 state = 0;
345 break;
346 case 4: /* + \r */
347 if (ch == '\n') { state = 1; break; }
348 if (ch != '\r') { put("\r"); state = 0; }
349 }
350 put(&ch);
351 }
352}
353
354char accept_buf[FMT_ULONG];
355void acceptmessage(qp) unsigned long qp;
356{
357 datetime_sec when;
358 when = now();
359 out("250 ok ");
360 accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
361 out(accept_buf);
362 out(" qp ");
363 accept_buf[fmt_ulong(accept_buf,qp)] = 0;
364 out(accept_buf);
365 out("\r\n");
366}
367
368void smtp_data() {
369 int hops;
370 unsigned long qp;
371 char *qqx;
372
373 if (!seenmail) { err_wantmail(); return; }
374 if (!rcptto.len) { err_wantrcpt(); return; }
375 seenmail = 0;
376 if (databytes) bytestooverflow = databytes + 1;
377 if (qmail_open(&qqt) == -1) { err_qqt(); return; }
378 qp = qmail_qp(&qqt);
379 out("354 go ahead\r\n");
380
381 received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
382 blast(&hops);
383 hops = (hops >= MAXHOPS);
384 if (hops) qmail_fail(&qqt);
385 qmail_from(&qqt,mailfrom.s);
386 qmail_put(&qqt,rcptto.s,rcptto.len);
387
388 qqx = qmail_close(&qqt);
389 if (!*qqx) { acceptmessage(qp); return; }
390 if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
391 if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
392 if (*qqx == 'D') out("554 "); else out("451 ");
393 out(qqx + 1);
394 out("\r\n");
395}
396
397struct commands smtpcommands[] = {
398 { "rcpt", smtp_rcpt, 0 }
399, { "mail", smtp_mail, 0 }
400, { "data", smtp_data, flush }
401, { "quit", smtp_quit, flush }
402, { "helo", smtp_helo, flush }
403, { "ehlo", smtp_ehlo, flush }
404, { "rset", smtp_rset, 0 }
405, { "help", smtp_help, flush }
406, { "noop", err_noop, flush }
407, { "vrfy", err_vrfy, flush }
408, { 0, err_unimpl, flush }
409} ;
410
411void main()
412{
413 sig_pipeignore();
414 if (chdir(auto_qmail) == -1) die_control();
415 setup();
416 if (ipme_init() != 1) die_ipme();
417 smtp_greet("220 ");
418 out(" ESMTP\r\n");
419 if (commands(&ssin,&smtpcommands) == 0) die_read();
420 die_nomem();
421}
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 @@
1.TH qmail-start 8
2.SH NAME
3qmail-start \- turn on mail delivery
4.SH SYNOPSIS
5.B qmail-start
6[
7.I defaultdelivery
8[
9.I logger arg ...
10]
11]
12.SH DESCRIPTION
13.B qmail-start
14invokes
15.BR qmail-send ,
16.BR qmail-lspawn ,
17.BR qmail-rspawn ,
18and
19.BR qmail-clean ,
20under the proper uids and gids.
21These four daemons cooperate to deliver messages from the queue.
22
23.B qmail-start
24arranges for
25.BR qmail-send 's
26activity record to be sent to
27.BR qmail-start 's
28output.
29See
30.B qmail-log(5)
31for the format of the activity record.
32Other than this,
33.B qmail-start
34does not print anything, even on failure.
35
36If
37.I defaultdelivery
38is supplied,
39.B qmail-start
40passes it to
41.BR qmail-lspawn .
42
43If
44.I logger
45is supplied,
46.B qmail-start
47invokes
48.I logger
49with the given arguments,
50and feeds
51.BR qmail-send 's
52activity record through
53.IR logger .
54
55Environment variables given to
56.B qmail-start
57will eventually be passed on to
58.BR qmail-local ,
59so make sure to clean up the environment if you run
60.B qmail-start
61manually:
62
63.EX
64 # env - PATH="QMAILHOME/bin:$PATH"
65.br
66 qmail-start ./Mailbox splogger qmail &
67.br
68 (all on one line)
69.EE
70
71Resource limits, controlling ttys, et al. are also passed from
72.B qmail-start
73to
74.BR qmail-local .
75
76Note that
77.B qmail-send
78normally juggles several simultaneous deliveries.
79To reduce
80.BR qmail-send 's
81impact on other programs,
82you can run
83.B qmail-start
84with a low priority.
85.SH "SEE ALSO"
86logger(1),
87splogger(1),
88nice(1),
89qmail-log(5),
90qmail-local(8),
91qmail-clean(8),
92qmail-lspawn(8),
93qmail-rspawn(8),
94qmail-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 @@
1#include "fd.h"
2#include "prot.h"
3#include "exit.h"
4#include "fork.h"
5#include "auto_uids.h"
6
7char *(qsargs[]) = { "qmail-send", 0 };
8char *(qcargs[]) = { "qmail-clean", 0 };
9char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 };
10char *(qrargs[]) = { "qmail-rspawn", 0 };
11
12void die() { _exit(111); }
13
14int pi0[2];
15int pi1[2];
16int pi2[2];
17int pi3[2];
18int pi4[2];
19int pi5[2];
20int pi6[2];
21
22void close23456() { close(2); close(3); close(4); close(5); close(6); }
23
24void closepipes() {
25 close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]);
26 close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]);
27 close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]);
28}
29
30void main(argc,argv)
31int argc;
32char **argv;
33{
34 if (chdir("/") == -1) die();
35 umask(077);
36 if (prot_gid(auto_gidq) == -1) die();
37
38 if (fd_copy(2,0) == -1) die();
39 if (fd_copy(3,0) == -1) die();
40 if (fd_copy(4,0) == -1) die();
41 if (fd_copy(5,0) == -1) die();
42 if (fd_copy(6,0) == -1) die();
43
44 if (argv[1]) {
45 qlargs[1] = argv[1];
46 ++argv;
47 }
48
49 if (argv[1]) {
50 if (pipe(pi0) == -1) die();
51 switch(fork()) {
52 case -1:
53 die();
54 case 0:
55 if (prot_gid(auto_gidn) == -1) die();
56 if (prot_uid(auto_uidl) == -1) die();
57 close(pi0[1]);
58 if (fd_move(0,pi0[0]) == -1) die();
59 close23456();
60 execvp(argv[1],argv + 1);
61 die();
62 }
63 close(pi0[0]);
64 if (fd_move(1,pi0[1]) == -1) die();
65 }
66
67 if (pipe(pi1) == -1) die();
68 if (pipe(pi2) == -1) die();
69 if (pipe(pi3) == -1) die();
70 if (pipe(pi4) == -1) die();
71 if (pipe(pi5) == -1) die();
72 if (pipe(pi6) == -1) die();
73
74 switch(fork()) {
75 case -1: die();
76 case 0:
77 if (fd_copy(0,pi1[0]) == -1) die();
78 if (fd_copy(1,pi2[1]) == -1) die();
79 close23456();
80 closepipes();
81 execvp(*qlargs,qlargs);
82 die();
83 }
84
85 switch(fork()) {
86 case -1: die();
87 case 0:
88 if (prot_uid(auto_uidr) == -1) die();
89 if (fd_copy(0,pi3[0]) == -1) die();
90 if (fd_copy(1,pi4[1]) == -1) die();
91 close23456();
92 closepipes();
93 execvp(*qrargs,qrargs);
94 die();
95 }
96
97 switch(fork()) {
98 case -1: die();
99 case 0:
100 if (prot_uid(auto_uidq) == -1) die();
101 if (fd_copy(0,pi5[0]) == -1) die();
102 if (fd_copy(1,pi6[1]) == -1) die();
103 close23456();
104 closepipes();
105 execvp(*qcargs,qcargs);
106 die();
107 }
108
109 if (prot_uid(auto_uids) == -1) die();
110 if (fd_copy(0,1) == -1) die();
111 if (fd_copy(1,pi1[1]) == -1) die();
112 if (fd_copy(2,pi2[0]) == -1) die();
113 if (fd_copy(3,pi3[1]) == -1) die();
114 if (fd_copy(4,pi4[0]) == -1) die();
115 if (fd_copy(5,pi5[1]) == -1) die();
116 if (fd_copy(6,pi6[0]) == -1) die();
117 closepipes();
118 execvp(*qsargs,qsargs);
119 die();
120}
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 @@
1.TH qmail-tcpok 8
2.SH NAME
3qmail-tcpok \- clear TCP timeout table
4.SH SYNOPSIS
5.B qmail-tcpok
6.SH DESCRIPTION
7.B qmail-tcpok
8erases
9.BR qmail-remote 's
10current list of timeouts,
11so that
12.B qmail-remote
13does not make any assumptions about failing addresses.
14
15.B qmail-tcpok
16must be run either as
17.B root
18or with user id
19.B qmailr
20and group id
21.BR qmail .
22.SH "SEE ALSO"
23qmail-remote(8),
24qmail-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 @@
1#include "strerr.h"
2#include "substdio.h"
3#include "lock.h"
4#include "open.h"
5#include "readwrite.h"
6#include "auto_qmail.h"
7#include "exit.h"
8
9#define FATAL "qmail-tcpok: fatal: "
10
11char buf[1024]; /* XXX: must match size in tcpto_clean.c, tcpto.c */
12substdio ss;
13
14void main()
15{
16 int fd;
17 int i;
18
19 if (chdir(auto_qmail) == -1)
20 strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
21 if (chdir("queue/lock") == -1)
22 strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,"/queue/lock: ");
23
24 fd = open_write("tcpto");
25 if (fd == -1)
26 strerr_die4sys(111,FATAL,"unable to write ",auto_qmail,"/queue/lock/tcpto: ");
27 if (lock_ex(fd) == -1)
28 strerr_die4sys(111,FATAL,"unable to lock ",auto_qmail,"/queue/lock/tcpto: ");
29
30 substdio_fdbuf(&ss,write,fd,buf,sizeof buf);
31 for (i = 0;i < sizeof buf;++i) substdio_put(&ss,"",1);
32 if (substdio_flush(&ss) == -1)
33 strerr_die4sys(111,FATAL,"unable to clear ",auto_qmail,"/queue/lock/tcpto: ");
34 _exit(0);
35}
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 @@
1.TH qmail-tcpto 8
2.SH NAME
3qmail-tcpto \- print TCP timeout table
4.SH SYNOPSIS
5.B qmail-tcpto
6.SH DESCRIPTION
7After an SMTP connection attempt times out,
8.B qmail-remote
9records the relevant IP address.
10If the same address fails again (after at least two minutes with
11no intervening successful connections),
12.B qmail-remote
13assumes that further attempts will fail for at least another hour.
14
15.B qmail-tcpto
16prints
17.BR qmail-remote 's
18current list of timeouts.
19
20.B qmail-tcpto
21must be run either as
22.B root
23or with user id
24.B qmailr
25and group id
26.BR qmail .
27.SH "SEE ALSO"
28qmail-qread(8),
29qmail-remote(8),
30qmail-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 @@
1/* XXX: this program knows quite a bit about tcpto's internals */
2
3#include "substdio.h"
4#include "subfd.h"
5#include "auto_qmail.h"
6#include "fmt.h"
7#include "ip.h"
8#include "lock.h"
9#include "error.h"
10#include "exit.h"
11#include "datetime.h"
12#include "now.h"
13
14void die(n) int n; { substdio_flush(subfdout); _exit(n); }
15
16void warn(s) char *s;
17{
18 char *x;
19 x = error_str(errno);
20 substdio_puts(subfdout,s);
21 substdio_puts(subfdout,": ");
22 substdio_puts(subfdout,x);
23 substdio_puts(subfdout,"\n");
24}
25
26void die_chdir() { warn("fatal: unable to chdir"); die(111); }
27void die_open() { warn("fatal: unable to open tcpto"); die(111); }
28void die_lock() { warn("fatal: unable to lock tcpto"); die(111); }
29void die_read() { warn("fatal: unable to read tcpto"); die(111); }
30
31char tcpto_buf[1024];
32
33char tmp[FMT_ULONG + IPFMT];
34
35void main()
36{
37 int fdlock;
38 int fd;
39 int r;
40 int i;
41 char *record;
42 struct ip_address ip;
43 datetime_sec when;
44 datetime_sec start;
45
46 if (chdir(auto_qmail) == -1) die_chdir();
47 if (chdir("queue/lock") == -1) die_chdir();
48
49 fdlock = open_write("tcpto");
50 if (fdlock == -1) die_open();
51 fd = open_read("tcpto");
52 if (fd == -1) die_open();
53 if (lock_ex(fdlock) == -1) die_lock();
54 r = read(fd,tcpto_buf,sizeof(tcpto_buf));
55 close(fd);
56 close(fdlock);
57
58 if (r == -1) die_read();
59 r >>= 4;
60
61 start = now();
62
63 record = tcpto_buf;
64 for (i = 0;i < r;++i)
65 {
66 if (record[4] >= 1)
67 {
68 byte_copy(&ip,4,record);
69 when = (unsigned long) (unsigned char) record[11];
70 when = (when << 8) + (unsigned long) (unsigned char) record[10];
71 when = (when << 8) + (unsigned long) (unsigned char) record[9];
72 when = (when << 8) + (unsigned long) (unsigned char) record[8];
73
74 substdio_put(subfdout,tmp,ip_fmt(tmp,&ip));
75 substdio_puts(subfdout," timed out ");
76 substdio_put(subfdout,tmp,fmt_ulong(tmp,(unsigned long) (start - when)));
77 substdio_puts(subfdout," seconds ago; # recent timeouts: ");
78 substdio_put(subfdout,tmp,fmt_ulong(tmp,(unsigned long) (unsigned char) record[4]));
79 substdio_puts(subfdout,"\n");
80 }
81 record += 16;
82 }
83
84 die(0);
85}
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 @@
1cd QMAIL
2cd queue
3for dir in mess info local remote
4do
5 ( cd $dir; find . -type f -print ) | (
6 cd $dir
7 while read path
8 do
9 id=`basename "$path"`
10 sub=`expr "$id" % SPLIT`
11 mv "$path" "$sub"/"$id"
12 done
13 )
14done
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 @@
1.TH qmail-users 5
2.SH NAME
3qmail-users \- assign mail addresses to users
4.SH OVERVIEW
5The file
6.B QMAILHOME/users/assign
7assigns addresses to users. For example,
8
9.EX
10 =joe.shmoe:joe:503:78:/home/joe:::
11.EE
12
13says that mail for
14.B joe.shmoe
15should be delivered to user
16.BR joe ,
17with uid 503 and gid 78,
18as specified by
19.BR /home/joe/.qmail .
20
21Assignments fed to
22.B qmail-newu
23will be used by
24.B qmail-lspawn
25to control
26.BR qmail-local 's
27deliveries.
28See
29.BR qmail-newu (8).
30A change to
31.B QMAILHOME/users/assign
32will have no effect until
33.B qmail-newu
34is run.
35.SH STRUCTURE
36.B QMAILHOME/users/assign
37is a series of assignments, one per line.
38It ends with a line containing a single dot.
39Lines must not contain NUL.
40.SH "SIMPLE ASSIGNMENTS"
41A simple assignment is a line of the form
42
43.EX
44 =local:user:uid:gid:homedir:dash:ext:
45.EE
46
47Here
48.I local
49is an address;
50.IR user ,
51.IR uid ,
52and
53.I gid
54are the account name, uid, and gid
55of the user in charge of
56.IR local ;
57and messages to
58.I local
59will be controlled by
60.IR homedir\fB/.qmail\fIdashext .
61
62If there are several assignments for the same
63.I local
64address,
65.B qmail-lspawn
66will use the first one.
67
68.I local
69is interpreted without regard to case.
70.SH "WILDCARD ASSIGNMENTS"
71A wildcard assignment is a line of the form
72
73.EX
74 +loc:user:uid:gid:homedir:dash:pre:
75.EE
76
77This assignment applies to any address beginning with
78.IR loc ,
79including
80.I loc
81itself.
82It means the same as
83
84.EX
85 =locext:user:uid:gid:homedir:dash:preext:
86.EE
87
88for every string
89.IR ext .
90
91A more specific wildcard assignment overrides a less specific
92assignment, and a simple assignment overrides any wildcard assignment.
93For example:
94
95.EX
96 +:alias:7790:2108:QMAILHOME/alias:-::
97 +joe-:joe:507:100:/home/joe:-::
98 =joe:joe:507:100:/home/joe:::
99.EE
100
101The address
102.B joe
103is handled by the third line;
104the address
105.B joe-direct
106is handled by the second line;
107the address
108.B bill
109is handled by the first line.
110.SH "SEE ALSO"
111qmail-pw2u(8),
112qmail-newu(8),
113qmail-lspawn(8)
diff --git a/qmail.7 b/qmail.7
new file mode 100644
index 0000000..a59e8d0
--- /dev/null
+++ b/qmail.7
@@ -0,0 +1,66 @@
1.TH qmail 7
2.SH "NAME"
3qmail \- overview of qmail documentation
4.SH "INTRODUCTION"
5.B qmail
6is a secure, reliable, efficient, simple message transfer agent.
7
8Users who want to control incoming messages
9should read
10.BR dot-qmail (5).
11Available commands for the
12.B .qmail
13file include
14.BR qbiff (1),
15.BR qreceipt (1),
16.BR forward (1),
17.BR bouncesaying (1),
18and
19.BR condredirect (1).
20Other helpful commands include
21.BR maildirmake (1),
22.BR maildir2mbox (1),
23and
24.BR maildirwatch (1).
25
26System administrators who want to control the entire
27.B qmail
28system should start with
29.BR qmail-control (5)
30and
31.BR qmail-start (8).
32There are three queue-monitoring tools:
33.BR qmail-qread (8),
34.BR qmail-qstat (8),
35and
36.BR qmail-tcpto (8).
37Incoming SMTP connections are handled by
38.BR qmail-smtpd (8).
39
40.B qmail
41offers two command-line message-sending interfaces:
42.BR qmail-inject (8)
43and
44.BR mailsubj (1).
45For background information on Internet mail messages,
46see
47.BR addresses (5),
48.BR envelopes (5),
49.BR qmail-header (5),
50and
51.BR forgeries (7).
52
53Miscellaneous documentation includes
54.BR qmail-limits (7)
55and
56.BR qmail-pop3d (8).
57
58This documentation describes version
591.03
60of
61.BR qmail .
62See
63.B http://pobox.com/~djb/qmail.html
64for other
65.BR qmail -related
66software.
diff --git a/qmail.c b/qmail.c
new file mode 100644
index 0000000..0fe0dfa
--- /dev/null
+++ b/qmail.c
@@ -0,0 +1,125 @@
1#include "substdio.h"
2#include "readwrite.h"
3#include "wait.h"
4#include "exit.h"
5#include "fork.h"
6#include "fd.h"
7#include "qmail.h"
8#include "auto_qmail.h"
9
10static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
11
12int qmail_open(qq)
13struct qmail *qq;
14{
15 int pim[2];
16 int pie[2];
17
18 if (pipe(pim) == -1) return -1;
19 if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
20
21 switch(qq->pid = vfork()) {
22 case -1:
23 close(pim[0]); close(pim[1]);
24 close(pie[0]); close(pie[1]);
25 return -1;
26 case 0:
27 close(pim[1]);
28 close(pie[1]);
29 if (fd_move(0,pim[0]) == -1) _exit(120);
30 if (fd_move(1,pie[0]) == -1) _exit(120);
31 if (chdir(auto_qmail) == -1) _exit(61);
32 execv(*binqqargs,binqqargs);
33 _exit(120);
34 }
35
36 qq->fdm = pim[1]; close(pim[0]);
37 qq->fde = pie[1]; close(pie[0]);
38 substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
39 qq->flagerr = 0;
40 return 0;
41}
42
43unsigned long qmail_qp(qq) struct qmail *qq;
44{
45 return qq->pid;
46}
47
48void qmail_fail(qq) struct qmail *qq;
49{
50 qq->flagerr = 1;
51}
52
53void qmail_put(qq,s,len) struct qmail *qq; char *s; int len;
54{
55 if (!qq->flagerr) if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1;
56}
57
58void qmail_puts(qq,s) struct qmail *qq; char *s;
59{
60 if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1;
61}
62
63void qmail_from(qq,s) struct qmail *qq; char *s;
64{
65 if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
66 close(qq->fdm);
67 substdio_fdbuf(&qq->ss,write,qq->fde,qq->buf,sizeof(qq->buf));
68 qmail_put(qq,"F",1);
69 qmail_puts(qq,s);
70 qmail_put(qq,"",1);
71}
72
73void qmail_to(qq,s) struct qmail *qq; char *s;
74{
75 qmail_put(qq,"T",1);
76 qmail_puts(qq,s);
77 qmail_put(qq,"",1);
78}
79
80char *qmail_close(qq)
81struct qmail *qq;
82{
83 int wstat;
84 int exitcode;
85
86 qmail_put(qq,"",1);
87 if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
88 close(qq->fde);
89
90 if (wait_pid(&wstat,qq->pid) != qq->pid)
91 return "Zqq waitpid surprise (#4.3.0)";
92 if (wait_crashed(wstat))
93 return "Zqq crashed (#4.3.0)";
94 exitcode = wait_exitcode(wstat);
95
96 switch(exitcode) {
97 case 115: /* compatibility */
98 case 11: return "Denvelope address too long for qq (#5.1.3)";
99 case 31: return "Dmail server permanently rejected message (#5.3.0)";
100 case 51: return "Zqq out of memory (#4.3.0)";
101 case 52: return "Zqq timeout (#4.3.0)";
102 case 53: return "Zqq write error or disk full (#4.3.0)";
103 case 0: if (!qq->flagerr) return ""; /* fall through */
104 case 54: return "Zqq read error (#4.3.0)";
105 case 55: return "Zqq unable to read configuration (#4.3.0)";
106 case 56: return "Zqq trouble making network connection (#4.3.0)";
107 case 61: return "Zqq trouble in home directory (#4.3.0)";
108 case 63:
109 case 64:
110 case 65:
111 case 66:
112 case 62: return "Zqq trouble creating files in queue (#4.3.0)";
113 case 71: return "Zmail server temporarily rejected message (#4.3.0)";
114 case 72: return "Zconnection to mail server timed out (#4.4.1)";
115 case 73: return "Zconnection to mail server rejected (#4.4.1)";
116 case 74: return "Zcommunication with mail server failed (#4.4.2)";
117 case 91: /* fall through */
118 case 81: return "Zqq internal bug (#4.3.0)";
119 case 120: return "Zunable to exec qq (#4.3.0)";
120 default:
121 if ((exitcode >= 11) && (exitcode <= 40))
122 return "Dqq permanent problem (#5.3.0)";
123 return "Zqq temporary problem (#4.3.0)";
124 }
125}
diff --git a/qmail.h b/qmail.h
new file mode 100644
index 0000000..7fa13e2
--- /dev/null
+++ b/qmail.h
@@ -0,0 +1,24 @@
1#ifndef QMAIL_H
2#define QMAIL_H
3
4#include "substdio.h"
5
6struct qmail {
7 int flagerr;
8 unsigned long pid;
9 int fdm;
10 int fde;
11 substdio ss;
12 char buf[1024];
13} ;
14
15extern int qmail_open();
16extern void qmail_put();
17extern void qmail_puts();
18extern void qmail_from();
19extern void qmail_to();
20extern void qmail_fail();
21extern char *qmail_close();
22extern unsigned long qmail_qp();
23
24#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 @@
1.TH qreceipt 1
2.SH NAME
3qreceipt \- respond to delivery notice requests
4.SH SYNOPSIS
5in
6.BR .qmail :
7.B |qreceipt
8.I youraddress
9.SH DESCRIPTION
10When a mail message arrives with
11.I youraddress
12listed in a
13.B Notice-Requested-Upon-Delivery-To
14header field,
15.B qreceipt
16sends a success notice back to the envelope sender.
17
18.B WARNING:
19If you create a
20.B .qmail
21file to enable
22.BR qreceipt ,
23make sure to also add a line specifying delivery to your normal mailbox.
24For example:
25
26.EX
27 /home/joe/Mailbox
28.br
29 |qreceipt joe@nowhere.mil
30.EE
31.SH "SEE ALSO"
32dot-qmail(5),
33envelopes(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 @@
1#include "sig.h"
2#include "env.h"
3#include "substdio.h"
4#include "stralloc.h"
5#include "subfd.h"
6#include "getln.h"
7#include "alloc.h"
8#include "str.h"
9#include "hfield.h"
10#include "token822.h"
11#include "error.h"
12#include "gen_alloc.h"
13#include "gen_allocdefs.h"
14#include "headerbody.h"
15#include "exit.h"
16#include "open.h"
17#include "quote.h"
18#include "qmail.h"
19
20void die_noreceipt() { _exit(0); }
21void die() { _exit(100); }
22void die_temp() { _exit(111); }
23void die_nomem() {
24 substdio_putsflush(subfderr,"qreceipt: fatal: out of memory\n"); die_temp(); }
25void die_fork() {
26 substdio_putsflush(subfderr,"qreceipt: fatal: unable to fork\n"); die_temp(); }
27void die_qqperm() {
28 substdio_putsflush(subfderr,"qreceipt: fatal: permanent qmail-queue error\n"); die(); }
29void die_qqtemp() {
30 substdio_putsflush(subfderr,"qreceipt: fatal: temporary qmail-queue error\n"); die_temp(); }
31void die_usage() {
32 substdio_putsflush(subfderr,
33 "qreceipt: usage: qreceipt deliveryaddress\n"); die(); }
34void die_read() {
35 if (errno == error_nomem) die_nomem();
36 substdio_putsflush(subfderr,"qreceipt: fatal: read error\n"); die_temp(); }
37void doordie(sa,r) stralloc *sa; int r; {
38 if (r == 1) return; if (r == -1) die_nomem();
39 substdio_putsflush(subfderr,"qreceipt: fatal: unable to parse this: ");
40 substdio_putflush(subfderr,sa->s,sa->len); die(); }
41
42char *target;
43
44int flagreceipt = 0;
45
46char *returnpath;
47stralloc messageid = {0};
48stralloc sanotice = {0};
49
50int rwnotice(addr) token822_alloc *addr; { token822_reverse(addr);
51 if (token822_unquote(&sanotice,addr) != 1) die_nomem();
52 if (sanotice.len == str_len(target))
53 if (!str_diffn(sanotice.s,target,sanotice.len))
54 flagreceipt = 1;
55 token822_reverse(addr); return 1; }
56
57struct qmail qqt;
58
59stralloc quoted = {0};
60
61void finishheader()
62{
63 char *qqx;
64
65 if (!flagreceipt) die_noreceipt();
66 if (str_equal(returnpath,"")) die_noreceipt();
67 if (str_equal(returnpath,"#@[]")) die_noreceipt();
68
69 if (!quote2(&quoted,returnpath)) die_nomem();
70
71 if (qmail_open(&qqt) == -1) die_fork();
72
73 qmail_puts(&qqt,"From: DELIVERY NOTICE SYSTEM <");
74 qmail_put(&qqt,quoted.s,quoted.len);
75 qmail_puts(&qqt,">\n");
76 qmail_puts(&qqt,"To: <");
77 qmail_put(&qqt,quoted.s,quoted.len);
78 qmail_puts(&qqt,">\n");
79 qmail_puts(&qqt,"Subject: success notice\n\
80\n\
81Hi! This is the qreceipt program. Your message was delivered to the\n\
82following address: ");
83 qmail_puts(&qqt,target);
84 qmail_puts(&qqt,". Thanks for asking.\n");
85 if (messageid.s)
86 {
87 qmail_puts(&qqt,"Your ");
88 qmail_put(&qqt,messageid.s,messageid.len);
89 }
90
91 qmail_from(&qqt,"");
92 qmail_to(&qqt,returnpath);
93 qqx = qmail_close(&qqt);
94
95 if (*qqx)
96 if (*qqx == 'D') die_qqperm();
97 else die_qqtemp();
98}
99
100stralloc hfbuf = {0};
101token822_alloc hfin = {0};
102token822_alloc hfrewrite = {0};
103token822_alloc hfaddr = {0};
104
105void doheaderfield(h)
106stralloc *h;
107{
108 switch(hfield_known(h->s,h->len))
109 {
110 case H_MESSAGEID:
111 if (!stralloc_copy(&messageid,h)) die_nomem();
112 break;
113 case H_NOTICEREQUESTEDUPONDELIVERYTO:
114 doordie(h,token822_parse(&hfin,h,&hfbuf));
115 doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwnotice));
116 break;
117 }
118}
119
120void dobody(h) stralloc *h; { ; }
121
122void main(argc,argv)
123int argc;
124char **argv;
125{
126 sig_pipeignore();
127 if (!(target = argv[1])) die_usage();
128 if (!(returnpath = env_get("SENDER"))) die_usage();
129 if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1) die_read();
130 die_noreceipt();
131}
diff --git a/qsmhook.c b/qsmhook.c
new file mode 100644
index 0000000..d5b38aa
--- /dev/null
+++ b/qsmhook.c
@@ -0,0 +1,137 @@
1#include "fd.h"
2#include "stralloc.h"
3#include "readwrite.h"
4#include "sgetopt.h"
5#include "wait.h"
6#include "env.h"
7#include "byte.h"
8#include "str.h"
9#include "alloc.h"
10#include "exit.h"
11#include "fork.h"
12#include "case.h"
13#include "subfd.h"
14#include "error.h"
15#include "substdio.h"
16#include "sig.h"
17
18void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
19void die_usage() { die(100,"qsmhook: fatal: incorrect usage\n"); }
20void die_temp() { die(111,"qsmhook: fatal: temporary problem\n"); }
21void die_read() { die(111,"qsmhook: fatal: unable to read message\n"); }
22void die_badcmd() { die(100,"qsmhook: fatal: command not found\n"); }
23
24int flagrpline = 0; char *rpline;
25int flagufline = 1; char *ufline;
26int flagdtline = 0; char *dtline;
27char *host;
28char *sender;
29char *recip;
30
31stralloc newarg = {0};
32
33substdio ssout;
34char outbuf[SUBSTDIO_OUTSIZE];
35substdio ssin;
36char inbuf[SUBSTDIO_INSIZE];
37
38void main(argc,argv)
39int argc;
40char **argv;
41{
42 int pid;
43 int wstat;
44 int pi[2];
45 int opt;
46 char **arg;
47 char *x;
48 int i;
49 int flagesc;
50
51 sig_pipeignore();
52
53 if (!(dtline = env_get("DTLINE"))) die_usage();
54 if (!(rpline = env_get("RPLINE"))) die_usage();
55 if (!(ufline = env_get("UFLINE"))) die_usage();
56 if (!(recip = env_get("LOCAL"))) die_usage();
57 if (!(host = env_get("HOST"))) die_usage();
58 if (!(sender = env_get("SENDER"))) die_usage();
59
60 while ((opt = getopt(argc,argv,"DFlMmnPsx:")) != opteof)
61 switch(opt)
62 {
63 case 'D': case 'F': case 'M': break; /* be serious */
64 case 'l': flagdtline = 1; break; /* also return-receipt-to? blech */
65 case 'm': break; /* we only handle one recipient anyway */
66 case 'n': flagufline = 0; break;
67 case 's': break; /* could call quote() otherwise, i suppose... */
68 case 'P': flagrpline = 1; break;
69 case 'x':
70 if (case_starts(recip,optarg))
71 recip += str_len(optarg);
72 break;
73 default:
74 _exit(100);
75 }
76 argc -= optind;
77 argv += optind;
78
79 if (!*argv) die_usage();
80
81 for (arg = argv;x = *arg;++arg)
82 {
83 if (!stralloc_copys(&newarg,"")) die_temp();
84 flagesc = 0;
85 for (i = 0;x[i];++i)
86 if (flagesc)
87 {
88 switch(x[i])
89 {
90 case '%': if (!stralloc_cats(&newarg,"%")) die_temp(); break;
91 case 'g': if (!stralloc_cats(&newarg,sender)) die_temp(); break;
92 case 'h': if (!stralloc_cats(&newarg,host)) die_temp(); break;
93 case 'u': if (!stralloc_cats(&newarg,recip)) die_temp(); break;
94 }
95 flagesc = 0;
96 }
97 else
98 if (x[i] == '%')
99 flagesc = 1;
100 else
101 if (!stralloc_append(&newarg,&x[i])) die_temp();
102 if (!stralloc_0(&newarg)) die_temp();
103 i = str_len(newarg.s) + 1;
104 if (!(x = alloc(i))) die_temp();
105 byte_copy(x,i,newarg.s);
106 *arg = x;
107 }
108
109 if (pipe(pi) == -1) die_temp();
110
111 switch(pid = fork())
112 {
113 case -1:
114 die_temp();
115 case 0:
116 close(pi[1]);
117 if (fd_move(0,pi[0]) == -1) die_temp();
118 sig_pipedefault();
119 execvp(*argv,argv);
120 if (error_temp(errno)) die_temp();
121 die_badcmd();
122 }
123 close(pi[0]);
124
125 substdio_fdbuf(&ssout,write,pi[1],outbuf,sizeof(outbuf));
126 substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
127 if (flagufline) substdio_bputs(&ssout,ufline);
128 if (flagrpline) substdio_bputs(&ssout,rpline);
129 if (flagdtline) substdio_bputs(&ssout,dtline);
130 if (substdio_copy(&ssout,&ssin) == -2) die_read();
131 substdio_flush(&ssout);
132 close(pi[1]);
133
134 if (wait_pid(&wstat,pid) == -1) die_temp();
135 if (wait_crashed(wstat)) die_temp();
136 _exit(wait_exitcode(wstat));
137}
diff --git a/qsutil.c b/qsutil.c
new file mode 100644
index 0000000..80c619d
--- /dev/null
+++ b/qsutil.c
@@ -0,0 +1,46 @@
1#include "stralloc.h"
2#include "readwrite.h"
3#include "substdio.h"
4#include "qsutil.h"
5
6static stralloc foo = {0};
7
8static char errbuf[1];
9static struct substdio sserr = SUBSTDIO_FDBUF(write,0,errbuf,1);
10
11void logsa(sa) stralloc *sa; {
12 substdio_putflush(&sserr,sa->s,sa->len); }
13void log1(s1) char *s1; {
14 substdio_putsflush(&sserr,s1); }
15void log2(s1,s2) char *s1; char *s2; {
16 substdio_putsflush(&sserr,s1);
17 substdio_putsflush(&sserr,s2); }
18void log3(s1,s2,s3) char *s1; char *s2; char *s3; {
19 substdio_putsflush(&sserr,s1);
20 substdio_putsflush(&sserr,s2);
21 substdio_putsflush(&sserr,s3); }
22void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); }
23
24void pausedir(dir) char *dir;
25{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); }
26
27static int issafe(ch) char ch;
28{
29 if (ch == '%') return 0; /* general principle: allman's code is crap */
30 if (ch < 33) return 0;
31 if (ch > 126) return 0;
32 return 1;
33}
34
35void logsafe(s) char *s;
36{
37 int i;
38 while (!stralloc_copys(&foo,s)) nomem();
39 for (i = 0;i < foo.len;++i)
40 if (foo.s[i] == '\n')
41 foo.s[i] = '/';
42 else
43 if (!issafe(foo.s[i]))
44 foo.s[i] = '_';
45 logsa(&foo);
46}
diff --git a/qsutil.h b/qsutil.h
new file mode 100644
index 0000000..a746845
--- /dev/null
+++ b/qsutil.h
@@ -0,0 +1,12 @@
1#ifndef QSUTIL_H
2#define QSUTIL_H
3
4extern void log1();
5extern void log2();
6extern void log3();
7extern void logsa();
8extern void nomem();
9extern void pausedir();
10extern void logsafe();
11
12#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 @@
1#include "stralloc.h"
2#include "str.h"
3#include "quote.h"
4
5/*
6quote() encodes a box as per rfc 821 and rfc 822,
7while trying to do as little quoting as possible.
8no, 821 and 822 don't have the same encoding. they're not even close.
9no special encoding here for bytes above 127.
10*/
11
12static char ok[128] = {
13 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
14,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
15,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
16,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
17} ;
18
19static int doit(saout,sain)
20stralloc *saout;
21stralloc *sain;
22{
23 char ch;
24 int i;
25 int j;
26
27 if (!stralloc_ready(saout,sain->len * 2 + 2)) return 0;
28 j = 0;
29 saout->s[j++] = '"';
30 for (i = 0;i < sain->len;++i)
31 {
32 ch = sain->s[i];
33 if ((ch == '\r') || (ch == '\n') || (ch == '"') || (ch == '\\'))
34 saout->s[j++] = '\\';
35 saout->s[j++] = ch;
36 }
37 saout->s[j++] = '"';
38 saout->len = j;
39 return 1;
40}
41
42int quote_need(s,n)
43char *s;
44unsigned int n;
45{
46 unsigned char uch;
47 int i;
48 if (!n) return 1;
49 for (i = 0;i < n;++i)
50 {
51 uch = s[i];
52 if (uch >= 128) return 1;
53 if (!ok[uch]) return 1;
54 }
55 if (s[0] == '.') return 1;
56 if (s[n - 1] == '.') return 1;
57 for (i = 0;i < n - 1;++i) if (s[i] == '.') if (s[i + 1] == '.') return 1;
58 return 0;
59}
60
61int quote(saout,sain)
62stralloc *saout;
63stralloc *sain;
64{
65 if (quote_need(sain->s,sain->len)) return doit(saout,sain);
66 return stralloc_copy(saout,sain);
67}
68
69static stralloc foo = {0};
70
71int quote2(sa,s)
72stralloc *sa;
73char *s;
74{
75 int j;
76 if (!*s) return stralloc_copys(sa,s);
77 j = str_rchr(s,'@');
78 if (!stralloc_copys(&foo,s)) return 0;
79 if (!s[j]) return quote(sa,&foo);
80 foo.len = j;
81 if (!quote(sa,&foo)) return 0;
82 return stralloc_cats(sa,s + j);
83}
diff --git a/quote.h b/quote.h
new file mode 100644
index 0000000..8ab7356
--- /dev/null
+++ b/quote.h
@@ -0,0 +1,8 @@
1#ifndef QUOTE_H
2#define QUOTE_H
3
4extern int quote_need();
5extern int quote();
6extern int quote2();
7
8#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 @@
1#include "cdb.h"
2#include "byte.h"
3#include "open.h"
4#include "error.h"
5#include "control.h"
6#include "constmap.h"
7#include "stralloc.h"
8#include "rcpthosts.h"
9
10static int flagrh = 0;
11static stralloc rh = {0};
12static struct constmap maprh;
13static int fdmrh;
14
15int rcpthosts_init()
16{
17 flagrh = control_readfile(&rh,"control/rcpthosts",0);
18 if (flagrh != 1) return flagrh;
19 if (!constmap_init(&maprh,rh.s,rh.len,0)) return flagrh = -1;
20 fdmrh = open_read("control/morercpthosts.cdb");
21 if (fdmrh == -1) if (errno != error_noent) return flagrh = -1;
22 return 0;
23}
24
25static stralloc host = {0};
26
27int rcpthosts(buf,len)
28char *buf;
29int len;
30{
31 int j;
32
33 if (flagrh != 1) return 1;
34
35 j = byte_rchr(buf,len,'@');
36 if (j >= len) return 1; /* presumably envnoathost is acceptable */
37
38 ++j; buf += j; len -= j;
39
40 if (!stralloc_copyb(&host,buf,len)) return -1;
41 buf = host.s;
42 case_lowerb(buf,len);
43
44 for (j = 0;j < len;++j)
45 if (!j || (buf[j] == '.'))
46 if (constmap(&maprh,buf + j,len - j)) return 1;
47
48 if (fdmrh != -1) {
49 uint32 dlen;
50 int r;
51
52 for (j = 0;j < len;++j)
53 if (!j || (buf[j] == '.')) {
54 r = cdb_seek(fdmrh,buf + j,len - j,&dlen);
55 if (r) return r;
56 }
57 }
58
59 return 0;
60}
diff --git a/rcpthosts.h b/rcpthosts.h
new file mode 100644
index 0000000..5e7e6cc
--- /dev/null
+++ b/rcpthosts.h
@@ -0,0 +1,7 @@
1#ifndef RCPTHOSTS_H
2#define RCPTHOSTS_H
3
4extern int rcpthosts_init();
5extern int rcpthosts();
6
7#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 @@
1#include "readsubdir.h"
2#include "fmt.h"
3#include "scan.h"
4#include "str.h"
5#include "auto_split.h"
6
7void readsubdir_init(rs,name,pause)
8readsubdir *rs;
9char *name;
10void (*pause)();
11{
12 rs->name = name;
13 rs->pause = pause;
14 rs->dir = 0;
15 rs->pos = 0;
16}
17
18static char namepos[FMT_ULONG + 4 + READSUBDIR_NAMELEN];
19
20int readsubdir_next(rs,id)
21readsubdir *rs;
22unsigned long *id;
23{
24 direntry *d;
25 unsigned int len;
26
27 if (!rs->dir)
28 {
29 if (rs->pos >= auto_split) return 0;
30 if (str_len(rs->name) > READSUBDIR_NAMELEN) { rs->pos++; return -1; }
31 len = 0;
32 len += fmt_str(namepos + len,rs->name);
33 namepos[len++] = '/';
34 len += fmt_ulong(namepos + len,(unsigned long) rs->pos);
35 namepos[len] = 0;
36 while (!(rs->dir = opendir(namepos))) rs->pause(namepos);
37 rs->pos++;
38 return -1;
39 }
40
41 d = readdir(rs->dir);
42 if (!d) { closedir(rs->dir); rs->dir = 0; return -1; }
43
44 if (str_equal(d->d_name,".")) return -1;
45 if (str_equal(d->d_name,"..")) return -1;
46 len = scan_ulong(d->d_name,id);
47 if (!len || d->d_name[len]) return -2;
48 return 1;
49}
diff --git a/readsubdir.h b/readsubdir.h
new file mode 100644
index 0000000..353942f
--- /dev/null
+++ b/readsubdir.h
@@ -0,0 +1,20 @@
1#ifndef READSUBDIR_H
2#define READSUBDIR_H
3
4#include "direntry.h"
5
6typedef struct readsubdir
7 {
8 DIR *dir;
9 int pos;
10 char *name;
11 void (*pause)();
12 }
13readsubdir;
14
15extern void readsubdir_init();
16extern int readsubdir_next();
17
18#define READSUBDIR_NAMELEN 10
19
20#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 @@
1#ifndef READWRITE_H
2#define READWRITE_H
3
4extern int read();
5extern int write();
6
7#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 @@
1#include "fmt.h"
2#include "qmail.h"
3#include "now.h"
4#include "datetime.h"
5#include "date822fmt.h"
6#include "received.h"
7
8static int issafe(ch) char ch;
9{
10 if (ch == '.') return 1;
11 if (ch == '@') return 1;
12 if (ch == '%') return 1;
13 if (ch == '+') return 1;
14 if (ch == '/') return 1;
15 if (ch == '=') return 1;
16 if (ch == ':') return 1;
17 if (ch == '-') return 1;
18 if ((ch >= 'a') && (ch <= 'z')) return 1;
19 if ((ch >= 'A') && (ch <= 'Z')) return 1;
20 if ((ch >= '0') && (ch <= '9')) return 1;
21 return 0;
22}
23
24void safeput(qqt,s)
25struct qmail *qqt;
26char *s;
27{
28 char ch;
29 while (ch = *s++) {
30 if (!issafe(ch)) ch = '?';
31 qmail_put(qqt,&ch,1);
32 }
33}
34
35static char buf[DATE822FMT];
36
37/* "Received: from relay1.uu.net (HELO uunet.uu.net) (7@192.48.96.5)\n" */
38/* " by silverton.berkeley.edu with SMTP; 26 Sep 1995 04:46:54 -0000\n" */
39
40void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo)
41struct qmail *qqt;
42char *protocol;
43char *local;
44char *remoteip;
45char *remotehost;
46char *remoteinfo;
47char *helo;
48{
49 struct datetime dt;
50
51 qmail_puts(qqt,"Received: from ");
52 safeput(qqt,remotehost);
53 if (helo) {
54 qmail_puts(qqt," (HELO ");
55 safeput(qqt,helo);
56 qmail_puts(qqt,")");
57 }
58 qmail_puts(qqt," (");
59 if (remoteinfo) {
60 safeput(qqt,remoteinfo);
61 qmail_puts(qqt,"@");
62 }
63 safeput(qqt,remoteip);
64 qmail_puts(qqt,")\n by ");
65 safeput(qqt,local);
66 qmail_puts(qqt," with ");
67 qmail_puts(qqt,protocol);
68 qmail_puts(qqt,"; ");
69 datetime_tai(&dt,now());
70 qmail_put(qqt,buf,date822fmt(buf,&dt));
71}
diff --git a/received.h b/received.h
new file mode 100644
index 0000000..4e39dda
--- /dev/null
+++ b/received.h
@@ -0,0 +1,6 @@
1#ifndef RECEIVED_H
2#define RECEIVED_H
3
4extern void received();
5
6#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 @@
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <netinet/in.h>
4#include <fcntl.h>
5#include "byte.h"
6#include "substdio.h"
7#include "ip.h"
8#include "fmt.h"
9#include "timeoutconn.h"
10#include "timeoutread.h"
11#include "timeoutwrite.h"
12#include "remoteinfo.h"
13
14static char line[999];
15static int t;
16
17static int mywrite(fd,buf,len) int fd; char *buf; int len;
18{
19 return timeoutwrite(t,fd,buf,len);
20}
21static int myread(fd,buf,len) int fd; char *buf; int len;
22{
23 return timeoutread(t,fd,buf,len);
24}
25
26char *remoteinfo_get(ipr,rp,ipl,lp,timeout)
27struct ip_address *ipr;
28unsigned long rp;
29struct ip_address *ipl;
30unsigned long lp;
31int timeout;
32{
33 char *x;
34 int s;
35 struct sockaddr_in sin;
36 substdio ss;
37 char buf[32];
38 unsigned int len;
39 int numcolons;
40 char ch;
41
42 t = timeout;
43
44 s = socket(AF_INET,SOCK_STREAM,0);
45 if (s == -1) return 0;
46
47 byte_zero(&sin,sizeof(sin));
48 sin.sin_family = AF_INET;
49 byte_copy(&sin.sin_addr,4,ipl);
50 sin.sin_port = 0;
51 if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; }
52 if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; }
53 fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY);
54
55 len = 0;
56 len += fmt_ulong(line + len,rp);
57 len += fmt_str(line + len," , ");
58 len += fmt_ulong(line + len,lp);
59 len += fmt_str(line + len,"\r\n");
60
61 substdio_fdbuf(&ss,mywrite,s,buf,sizeof buf);
62 if (substdio_putflush(&ss,line,len) == -1) { close(s); return 0; }
63
64 substdio_fdbuf(&ss,myread,s,buf,sizeof buf);
65 x = line;
66 numcolons = 0;
67 for (;;) {
68 if (substdio_get(&ss,&ch,1) != 1) { close(s); return 0; }
69 if ((ch == ' ') || (ch == '\t') || (ch == '\r')) continue;
70 if (ch == '\n') break;
71 if (numcolons < 3) { if (ch == ':') ++numcolons; }
72 else { *x++ = ch; if (x == line + sizeof(line) - 1) break; }
73 }
74 *x = 0;
75 close(s);
76 return line;
77}
diff --git a/remoteinfo.h b/remoteinfo.h
new file mode 100644
index 0000000..d5d9097
--- /dev/null
+++ b/remoteinfo.h
@@ -0,0 +1,6 @@
1#ifndef REMOTEINFO_H
2#define REMOTEINFO_H
3
4extern char *remoteinfo_get();
5
6#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 @@
1#ifndef SCAN_H
2#define SCAN_H
3
4extern unsigned int scan_uint();
5extern unsigned int scan_xint();
6extern unsigned int scan_nbbint();
7extern unsigned int scan_ushort();
8extern unsigned int scan_xshort();
9extern unsigned int scan_nbbshort();
10extern unsigned int scan_ulong();
11extern unsigned int scan_xlong();
12extern unsigned int scan_nbblong();
13
14extern unsigned int scan_plusminus();
15extern unsigned int scan_0x();
16
17extern unsigned int scan_whitenskip();
18extern unsigned int scan_nonwhitenskip();
19extern unsigned int scan_charsetnskip();
20extern unsigned int scan_noncharsetnskip();
21
22extern unsigned int scan_strncmp();
23extern unsigned int scan_memcmp();
24
25extern unsigned int scan_long();
26
27#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 @@
1#include "scan.h"
2
3unsigned int scan_8long(s,u) register char *s; register unsigned long *u;
4{
5 register unsigned int pos; register unsigned long result;
6 register unsigned long c;
7 pos = 0; result = 0;
8 while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 8)
9 { result = result * 8 + c; ++pos; }
10 *u = result; return pos;
11}
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 @@
1#include "scan.h"
2
3unsigned int scan_ulong(s,u) register char *s; register unsigned long *u;
4{
5 register unsigned int pos; register unsigned long result;
6 register unsigned long c;
7 pos = 0; result = 0;
8 while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10)
9 { result = result * 10 + c; ++pos; }
10 *u = result; return pos;
11}
diff --git a/seek.h b/seek.h
new file mode 100644
index 0000000..964fba3
--- /dev/null
+++ b/seek.h
@@ -0,0 +1,15 @@
1#ifndef SEEK_H
2#define SEEK_H
3
4typedef unsigned long seek_pos;
5
6extern seek_pos seek_cur();
7
8extern int seek_set();
9extern int seek_end();
10
11extern int seek_trunc();
12
13#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
14
15#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 @@
1#include <sys/types.h>
2#include "seek.h"
3
4#define CUR 1 /* sigh */
5
6seek_pos seek_cur(fd) int fd;
7{ 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 @@
1#include <sys/types.h>
2#include "seek.h"
3
4#define END 2 /* sigh */
5
6int seek_end(fd) int fd;
7{ 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 @@
1#include <sys/types.h>
2#include "seek.h"
3
4#define SET 0 /* sigh */
5
6int seek_set(fd,pos) int fd; seek_pos pos;
7{ 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 @@
1#include <sys/types.h>
2#include "seek.h"
3
4int seek_trunc(fd,pos) int fd; seek_pos pos;
5{ 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 @@
1#ifndef SELECT_H
2#define SELECT_H
3
4#include <sys/types.h>
5#include <sys/time.h>
6extern int select();
7
8#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 @@
1#ifndef SELECT_H
2#define SELECT_H
3
4#include <sys/types.h>
5#include <sys/time.h>
6#include <sys/select.h>
7extern int select();
8
9#endif
diff --git a/sendmail.c b/sendmail.c
new file mode 100644
index 0000000..46d0e4b
--- /dev/null
+++ b/sendmail.c
@@ -0,0 +1,129 @@
1#include "sgetopt.h"
2#include "substdio.h"
3#include "subfd.h"
4#include "alloc.h"
5#include "auto_qmail.h"
6#include "exit.h"
7#include "env.h"
8#include "str.h"
9
10void nomem()
11{
12 substdio_putsflush(subfderr,"sendmail: fatal: out of memory\n");
13 _exit(111);
14}
15
16void die_usage()
17{
18 substdio_putsflush(subfderr,"sendmail: usage: sendmail [ -t ] [ -fsender ] [ -Fname ] [ -bp ] [ -bs ] [ arg ... ]\n");
19 _exit(100);
20}
21
22char *smtpdarg[] = { "bin/qmail-smtpd", 0 };
23void smtpd()
24{
25 if (!env_get("PROTO")) {
26 if (!env_put("RELAYCLIENT=")) nomem();
27 if (!env_put("DATABYTES=0")) nomem();
28 if (!env_put("PROTO=TCP")) nomem();
29 if (!env_put("TCPLOCALIP=127.0.0.1")) nomem();
30 if (!env_put("TCPLOCALHOST=localhost")) nomem();
31 if (!env_put("TCPREMOTEIP=127.0.0.1")) nomem();
32 if (!env_put("TCPREMOTEHOST=localhost")) nomem();
33 if (!env_put("TCPREMOTEINFO=sendmail-bs")) nomem();
34 }
35 execv(*smtpdarg,smtpdarg);
36 substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-smtpd\n");
37 _exit(111);
38}
39
40char *qreadarg[] = { "bin/qmail-qread", 0 };
41void mailq()
42{
43 execv(*qreadarg,qreadarg);
44 substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-qread\n");
45 _exit(111);
46}
47
48int flagh;
49char *sender;
50
51void main(argc,argv)
52int argc;
53char **argv;
54{
55 int opt;
56 char **qiargv;
57 char **arg;
58 int i;
59
60 if (chdir(auto_qmail) == -1) {
61 substdio_putsflush(subfderr,"sendmail: fatal: unable to switch to qmail home directory\n");
62 _exit(111);
63 }
64
65 flagh = 0;
66 sender = 0;
67 while ((opt = getopt(argc,argv,"vimte:f:p:o:B:F:EJxb:")) != opteof)
68 switch(opt) {
69 case 'B': break;
70 case 't': flagh = 1; break;
71 case 'f': sender = optarg; break;
72 case 'F': if (!env_put2("MAILNAME",optarg)) nomem(); break;
73 case 'p': break; /* could generate a Received line from optarg */
74 case 'v': break;
75 case 'i': break; /* what an absurd concept */
76 case 'x': break; /* SVR4 stupidity */
77 case 'm': break; /* twisted-paper-path blindness, incompetent design */
78 case 'e': break; /* qmail has only one error mode */
79 case 'o':
80 switch(optarg[0]) {
81 case 'd': break; /* qmail has only one delivery mode */
82 case 'e': break; /* see 'e' above */
83 case 'i': break; /* see 'i' above */
84 case 'm': break; /* see 'm' above */
85 }
86 break;
87 case 'E': case 'J': /* Sony NEWS-OS */
88 while (argv[optind][optpos]) ++optpos; /* skip optional argument */
89 break;
90 case 'b':
91 switch(optarg[0]) {
92 case 'm': break;
93 case 'p': mailq();
94 case 's': smtpd();
95 default: die_usage();
96 }
97 break;
98 default:
99 die_usage();
100 }
101 argc -= optind;
102 argv += optind;
103
104 if (str_equal(optprogname,"mailq"))
105 mailq();
106
107 if (str_equal(optprogname,"newaliases")) {
108 substdio_putsflush(subfderr,"sendmail: fatal: please use fastforward/newaliases instead\n");
109 _exit(100);
110 }
111
112 qiargv = (char **) alloc((argc + 10) * sizeof(char *));
113 if (!qiargv) nomem();
114
115 arg = qiargv;
116 *arg++ = "bin/qmail-inject";
117 *arg++ = (flagh ? "-H" : "-a");
118 if (sender) {
119 *arg++ = "-f";
120 *arg++ = sender;
121 }
122 *arg++ = "--";
123 for (i = 0;i < argc;++i) *arg++ = argv[i];
124 *arg = 0;
125
126 execv(*qiargv,qiargv);
127 substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-inject\n");
128 _exit(111);
129}
diff --git a/sgetopt.3 b/sgetopt.3
new file mode 100644
index 0000000..bde0c2b
--- /dev/null
+++ b/sgetopt.3
@@ -0,0 +1,28 @@
1.TH sgetopt 3
2.SH NAME
3sgetopt \- get option character from command line
4.SH SYNTAX
5.B #include <sgetopt.h>
6.SH DESCRIPTION
7The
8.B sgetopt
9library is just like the
10.B getopt
11library,
12except that it prints errors using
13.B substdio
14rather than
15.BR stdio .
16
17See
18.B getopt(3)
19for interface details.
20.SH VERSION
21sgetopt version 1.9, 931201.
22.SH AUTHOR
23Placed into the public domain by Daniel J. Bernstein.
24.SH "SEE ALSO"
25getopt(3),
26subgetopt(3),
27subfd(3),
28substdio(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 @@
1/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer
2D. J. Bernstein, djb@pobox.com.
3Depends on subgetopt.h, substdio.h, subfd.h.
4No system requirements.
519970208: Cleanups.
6931201: Baseline.
7No known patent problems.
8
9Documentation in sgetopt.3.
10*/
11
12#include "substdio.h"
13#include "subfd.h"
14#define SGETOPTNOSHORT
15#include "sgetopt.h"
16#define SUBGETOPTNOSHORT
17#include "subgetopt.h"
18
19#define getopt sgetoptmine
20#define optind subgetoptind
21#define opterr sgetopterr
22#define optproblem subgetoptproblem
23#define optprogname sgetoptprogname
24
25int opterr = 1;
26char *optprogname = 0;
27
28int getopt(argc,argv,opts)
29int argc;
30char **argv;
31char *opts;
32{
33 int c;
34 char *s;
35
36 if (!optprogname) {
37 optprogname = *argv;
38 if (!optprogname) optprogname = "";
39 for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
40 }
41 c = subgetopt(argc,argv,opts);
42 if (opterr)
43 if (c == '?') {
44 char chp[2]; chp[0] = optproblem; chp[1] = '\n';
45 substdio_puts(subfderr,optprogname);
46 if (argv[optind] && (optind < argc))
47 substdio_puts(subfderr,": illegal option -- ");
48 else
49 substdio_puts(subfderr,": option requires an argument -- ");
50 substdio_put(subfderr,chp,2);
51 substdio_flush(subfderr);
52 }
53 return c;
54}
diff --git a/sgetopt.h b/sgetopt.h
new file mode 100644
index 0000000..5f89127
--- /dev/null
+++ b/sgetopt.h
@@ -0,0 +1,21 @@
1#ifndef SGETOPT_H
2#define SGETOPT_H
3
4#ifndef SGETOPTNOSHORT
5#define getopt sgetoptmine
6#define optarg subgetoptarg
7#define optind subgetoptind
8#define optpos subgetoptpos
9#define opterr sgetopterr
10#define optproblem subgetoptproblem
11#define optprogname sgetoptprogname
12#define opteof subgetoptdone
13#endif
14
15#include "subgetopt.h"
16
17extern int sgetoptmine();
18extern int sgetopterr;
19extern char *sgetoptprogname;
20
21#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 @@
1#ifndef SIG_H
2#define SIG_H
3
4extern void sig_catch();
5extern void sig_block();
6extern void sig_unblock();
7extern void sig_blocknone();
8extern void sig_pause();
9
10extern void sig_dfl();
11
12extern void sig_miscignore();
13extern void sig_bugcatch();
14
15extern void sig_pipeignore();
16extern void sig_pipedefault();
17
18extern void sig_contblock();
19extern void sig_contunblock();
20extern void sig_contcatch();
21extern void sig_contdefault();
22
23extern void sig_termblock();
24extern void sig_termunblock();
25extern void sig_termcatch();
26extern void sig_termdefault();
27
28extern void sig_alarmblock();
29extern void sig_alarmunblock();
30extern void sig_alarmcatch();
31extern void sig_alarmdefault();
32
33extern void sig_childblock();
34extern void sig_childunblock();
35extern void sig_childcatch();
36extern void sig_childdefault();
37
38extern void sig_hangupblock();
39extern void sig_hangupunblock();
40extern void sig_hangupcatch();
41extern void sig_hangupdefault();
42
43#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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_alarmblock() { sig_block(SIGALRM); }
5void sig_alarmunblock() { sig_unblock(SIGALRM); }
6void sig_alarmcatch(f) void (*f)(); { sig_catch(SIGALRM,f); }
7void 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 @@
1#include <signal.h>
2#include "sig.h"
3#include "hassgprm.h"
4
5void sig_block(sig)
6int sig;
7{
8#ifdef HASSIGPROCMASK
9 sigset_t ss;
10 sigemptyset(&ss);
11 sigaddset(&ss,sig);
12 sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
13#else
14 sigblock(1 << (sig - 1));
15#endif
16}
17
18void sig_unblock(sig)
19int sig;
20{
21#ifdef HASSIGPROCMASK
22 sigset_t ss;
23 sigemptyset(&ss);
24 sigaddset(&ss,sig);
25 sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
26#else
27 sigsetmask(sigsetmask(~0) & ~(1 << (sig - 1)));
28#endif
29}
30
31void sig_blocknone()
32{
33#ifdef HASSIGPROCMASK
34 sigset_t ss;
35 sigemptyset(&ss);
36 sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
37#else
38 sigsetmask(0);
39#endif
40}
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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_bugcatch(f) void (*f)();
5{
6 sig_catch(SIGILL,f);
7 sig_catch(SIGABRT,f);
8 sig_catch(SIGFPE,f);
9 sig_catch(SIGBUS,f);
10 sig_catch(SIGSEGV,f);
11#ifdef SIGSYS
12 sig_catch(SIGSYS,f);
13#endif
14#ifdef SIGEMT
15 sig_catch(SIGEMT,f);
16#endif
17}
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 @@
1#include <signal.h>
2#include "sig.h"
3#include "hassgact.h"
4
5void sig_catch(sig,f)
6int sig;
7void (*f)();
8{
9#ifdef HASSIGACTION
10 struct sigaction sa;
11 sa.sa_handler = f;
12 sa.sa_flags = 0;
13 sigemptyset(&sa.sa_mask);
14 sigaction(sig,&sa,(struct sigaction *) 0);
15#else
16 signal(sig,f); /* won't work under System V, even nowadays---dorks */
17#endif
18}
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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_childblock() { sig_block(SIGCHLD); }
5void sig_childunblock() { sig_unblock(SIGCHLD); }
6void sig_childcatch(f) void (*f)(); { sig_catch(SIGCHLD,f); }
7void 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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_hangupblock() { sig_block(SIGHUP); }
5void sig_hangupunblock() { sig_unblock(SIGHUP); }
6void sig_hangupcatch(f) void (*f)(); { sig_catch(SIGHUP,f); }
7void 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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_miscignore()
5{
6 sig_catch(SIGVTALRM,SIG_IGN);
7 sig_catch(SIGPROF,SIG_IGN);
8 sig_catch(SIGQUIT,SIG_IGN);
9 sig_catch(SIGINT,SIG_IGN);
10 sig_catch(SIGHUP,SIG_IGN);
11#ifdef SIGXCPU
12 sig_catch(SIGXCPU,SIG_IGN);
13#endif
14#ifdef SIGXFSZ
15 sig_catch(SIGXFSZ,SIG_IGN);
16#endif
17}
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 @@
1#include <signal.h>
2#include "sig.h"
3#include "hassgprm.h"
4
5void sig_pause()
6{
7#ifdef HASSIGPROCMASK
8 sigset_t ss;
9 sigemptyset(&ss);
10 sigsuspend(&ss);
11#else
12 sigpause(0);
13#endif
14}
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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
5void 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 @@
1#include <signal.h>
2#include "sig.h"
3
4void sig_termblock() { sig_block(SIGTERM); }
5void sig_termunblock() { sig_unblock(SIGTERM); }
6void sig_termcatch(f) void (*f)(); { sig_catch(SIGTERM,f); }
7void 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 @@
1#include "stralloc.h"
2#include "readwrite.h"
3#include "slurpclose.h"
4#include "error.h"
5
6int slurpclose(fd,sa,bufsize)
7int fd;
8stralloc *sa;
9int bufsize;
10{
11 int r;
12 for (;;) {
13 if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
14 r = read(fd,sa->s + sa->len,bufsize);
15 if (r == -1) if (errno == error_intr) continue;
16 if (r <= 0) { close(fd); return r; }
17 sa->len += r;
18 }
19}
diff --git a/slurpclose.h b/slurpclose.h
new file mode 100644
index 0000000..57e9eec
--- /dev/null
+++ b/slurpclose.h
@@ -0,0 +1,6 @@
1#ifndef SLURPCLOSE_H
2#define SLURPCLOSE_H
3
4extern int slurpclose();
5
6#endif
diff --git a/spawn.c b/spawn.c
new file mode 100644
index 0000000..b2aee78
--- /dev/null
+++ b/spawn.c
@@ -0,0 +1,259 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include "sig.h"
4#include "wait.h"
5#include "substdio.h"
6#include "byte.h"
7#include "str.h"
8#include "stralloc.h"
9#include "select.h"
10#include "exit.h"
11#include "coe.h"
12#include "open.h"
13#include "error.h"
14#include "auto_qmail.h"
15#include "auto_uids.h"
16#include "auto_spawn.h"
17
18extern int truncreport;
19extern int spawn();
20extern void report();
21extern void initialize();
22
23struct delivery
24 {
25 int used;
26 int fdin; /* pipe input */
27 int pid; /* zero if child is dead */
28 int wstat; /* if !pid: status of child */
29 int fdout; /* pipe output, -1 if !pid; delays eof until after death */
30 stralloc output;
31 }
32;
33
34struct delivery *d;
35
36void sigchld()
37{
38 int wstat;
39 int pid;
40 int i;
41 while ((pid = wait_nohang(&wstat)) > 0)
42 for (i = 0;i < auto_spawn;++i) if (d[i].used)
43 if (d[i].pid == pid)
44 {
45 close(d[i].fdout); d[i].fdout = -1;
46 d[i].wstat = wstat; d[i].pid = 0;
47 }
48}
49
50int flagwriting = 1;
51
52int okwrite(fd,buf,n) int fd; char *buf; int n;
53{
54 int w;
55 if (!flagwriting) return n;
56 w = write(fd,buf,n);
57 if (w != -1) return w;
58 if (errno == error_intr) return -1;
59 flagwriting = 0; close(fd);
60 return n;
61}
62
63int flagreading = 1;
64char outbuf[1024]; substdio ssout;
65
66int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */
67int flagabort = 0; /* if 1, everything except delnum is garbage */
68int delnum;
69stralloc messid = {0};
70stralloc sender = {0};
71stralloc recip = {0};
72
73void err(s) char *s;
74{
75 char ch; ch = delnum; substdio_put(&ssout,&ch,1);
76 substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1);
77}
78
79void docmd()
80{
81 int f;
82 int i;
83 int j;
84 int fdmess;
85 int pi[2];
86 struct stat st;
87
88 if (flagabort) { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; }
89 if (delnum < 0) { err("ZInternal error: delnum negative. (#4.3.5)\n"); return; }
90 if (delnum >= auto_spawn) { err("ZInternal error: delnum too big. (#4.3.5)\n"); return; }
91 if (d[delnum].used) { err("ZInternal error: delnum in use. (#4.3.5)\n"); return; }
92 for (i = 0;i < messid.len;++i)
93 if (messid.s[i])
94 if (!i || (messid.s[i] != '/'))
95 if ((unsigned char) (messid.s[i] - '0') > 9)
96 { err("DInternal error: messid has nonnumerics. (#5.3.5)\n"); return; }
97 if (messid.len > 100) { err("DInternal error: messid too long. (#5.3.5)\n"); return; }
98 if (!messid.s[0]) { err("DInternal error: messid too short. (#5.3.5)\n"); return; }
99
100 if (!stralloc_copys(&d[delnum].output,""))
101 { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; }
102
103 j = byte_rchr(recip.s,recip.len,'@');
104 if (j >= recip.len) { err("DSorry, address must include host name. (#5.1.3)\n"); return; }
105
106 fdmess = open_read(messid.s);
107 if (fdmess == -1) { err("Zqmail-spawn unable to open message. (#4.3.0)\n"); return; }
108
109 if (fstat(fdmess,&st) == -1)
110 { close(fdmess); err("Zqmail-spawn unable to fstat message. (#4.3.0)\n"); return; }
111 if ((st.st_mode & S_IFMT) != S_IFREG)
112 { close(fdmess); err("ZSorry, message has wrong type. (#4.3.5)\n"); return; }
113 if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */
114 /* your security is already toast at this point. damage control... */
115 { close(fdmess); err("ZSorry, message has wrong owner. (#4.3.5)\n"); return; }
116
117 if (pipe(pi) == -1)
118 { close(fdmess); err("Zqmail-spawn unable to create pipe. (#4.3.0)\n"); return; }
119
120 coe(pi[0]);
121
122 f = spawn(fdmess,pi[1],sender.s,recip.s,j);
123 close(fdmess);
124 if (f == -1)
125 { close(pi[0]); close(pi[1]); err("Zqmail-spawn unable to fork. (#4.3.0)\n"); return; }
126
127 d[delnum].fdin = pi[0];
128 d[delnum].fdout = pi[1]; coe(pi[1]);
129 d[delnum].pid = f;
130 d[delnum].used = 1;
131}
132
133char cmdbuf[1024];
134
135void getcmd()
136{
137 int i;
138 int r;
139 char ch;
140
141 r = read(0,cmdbuf,sizeof(cmdbuf));
142 if (r == 0)
143 { flagreading = 0; return; }
144 if (r == -1)
145 {
146 if (errno != error_intr)
147 flagreading = 0;
148 return;
149 }
150
151 for (i = 0;i < r;++i)
152 {
153 ch = cmdbuf[i];
154 switch(stage)
155 {
156 case 0:
157 delnum = (unsigned int) (unsigned char) ch;
158 messid.len = 0; stage = 1; break;
159 case 1:
160 if (!stralloc_append(&messid,&ch)) flagabort = 1;
161 if (ch) break;
162 sender.len = 0; stage = 2; break;
163 case 2:
164 if (!stralloc_append(&sender,&ch)) flagabort = 1;
165 if (ch) break;
166 recip.len = 0; stage = 3; break;
167 case 3:
168 if (!stralloc_append(&recip,&ch)) flagabort = 1;
169 if (ch) break;
170 docmd();
171 flagabort = 0; stage = 0; break;
172 }
173 }
174}
175
176char inbuf[128];
177
178void main(argc,argv)
179int argc;
180char **argv;
181{
182 char ch;
183 int i;
184 int r;
185 fd_set rfds;
186 int nfds;
187
188 if (chdir(auto_qmail) == -1) _exit(111);
189 if (chdir("queue/mess") == -1) _exit(111);
190 if (!stralloc_copys(&messid,"")) _exit(111);
191 if (!stralloc_copys(&sender,"")) _exit(111);
192 if (!stralloc_copys(&recip,"")) _exit(111);
193
194 d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery));
195 if (!d) _exit(111);
196
197 substdio_fdbuf(&ssout,okwrite,1,outbuf,sizeof(outbuf));
198
199 sig_pipeignore();
200 sig_childcatch(sigchld);
201
202 initialize(argc,argv);
203
204 ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
205
206 for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
207
208 for (;;)
209 {
210 if (!flagreading)
211 {
212 for (i = 0;i < auto_spawn;++i) if (d[i].used) break;
213 if (i >= auto_spawn) _exit(0);
214 }
215 sig_childunblock();
216
217 FD_ZERO(&rfds);
218 if (flagreading) FD_SET(0,&rfds);
219 nfds = 1;
220 for (i = 0;i < auto_spawn;++i) if (d[i].used)
221 { FD_SET(d[i].fdin,&rfds); if (d[i].fdin >= nfds) nfds = d[i].fdin + 1; }
222
223 r = select(nfds,&rfds,(fd_set *) 0,(fd_set *) 0,(struct timeval *) 0);
224 sig_childblock();
225
226 if (r != -1)
227 {
228 if (flagreading)
229 if (FD_ISSET(0,&rfds))
230 getcmd();
231 for (i = 0;i < auto_spawn;++i) if (d[i].used)
232 if (FD_ISSET(d[i].fdin,&rfds))
233 {
234 r = read(d[i].fdin,inbuf,128);
235 if (r == -1)
236 continue; /* read error on a readable pipe? be serious */
237 if (r == 0)
238 {
239 ch = i; substdio_put(&ssout,&ch,1);
240 report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
241 substdio_put(&ssout,"",1);
242 substdio_flush(&ssout);
243 close(d[i].fdin); d[i].used = 0;
244 continue;
245 }
246 while (!stralloc_readyplus(&d[i].output,r)) sleep(10); /*XXX*/
247 byte_copy(d[i].output.s + d[i].output.len,r,inbuf);
248 d[i].output.len += r;
249 if (truncreport > 100)
250 if (d[i].output.len > truncreport)
251 {
252 char *truncmess = "\nError report too long, sorry.\n";
253 d[i].output.len = truncreport - str_len(truncmess) - 3;
254 stralloc_cats(&d[i].output,truncmess);
255 }
256 }
257 }
258 }
259}
diff --git a/splogger.8 b/splogger.8
new file mode 100644
index 0000000..15096b4
--- /dev/null
+++ b/splogger.8
@@ -0,0 +1,60 @@
1.TH splogger 8
2.SH NAME
3splogger \- make entries in syslog
4.SH SYNOPSIS
5.B splogger
6[
7.I tag
8[
9.I fac
10]
11]
12.SH DESCRIPTION
13.B splogger
14reads a series of messages and feeds them to
15.BR syslog .
16At the front of each message it puts
17.I tag
18(default:
19.BR splogger )
20and a numerical timestamp.
21
22.B splogger
23checks for
24.B alert:
25or
26.B warning:
27at the beginning of each message.
28It selects a priority of
29LOG_ALERT, LOG_WARNING, or LOG_INFO accordingly.
30
31.B splogger
32logs messages with facility
33.IR fac .
34.I fac
35(default: 2)
36must be numeric.
37
38.B splogger
39converts unprintable characters to question marks.
40
41.B splogger
42does not log blank lines.
43
44.B splogger
45folds messages after 800 characters,
46since
47.B syslog
48can't handle long messages.
49.B splogger
50uses a + after the timestamp
51to mark folded lines.
52
53Note that the
54.B syslog
55mechanism is inherently unreliable:
56it does not guarantee that messages will be logged.
57It is also very slow.
58.SH "SEE ALSO"
59syslog(3),
60logger(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 @@
1#include <sys/types.h>
2#include <sys/time.h>
3#include <syslog.h>
4#include "error.h"
5#include "substdio.h"
6#include "subfd.h"
7#include "exit.h"
8#include "str.h"
9#include "scan.h"
10#include "fmt.h"
11
12char buf[800]; /* syslog truncates long lines (or crashes); GPACIC */
13int bufpos = 0; /* 0 <= bufpos < sizeof(buf) */
14int flagcont = 0;
15int priority; /* defined if flagcont */
16char stamp[FMT_ULONG + FMT_ULONG + 3]; /* defined if flagcont */
17
18void stamp_make()
19{
20 struct timeval tv;
21 char *s;
22 gettimeofday(&tv,(struct timezone *) 0);
23 s = stamp;
24 s += fmt_ulong(s,(unsigned long) tv.tv_sec);
25 *s++ = '.';
26 s += fmt_uint0(s,(unsigned int) tv.tv_usec,6);
27 *s = 0;
28}
29
30void flush()
31{
32 if (bufpos) {
33 buf[bufpos] = 0;
34 if (flagcont)
35 syslog(priority,"%s+%s",stamp,buf); /* logger folds invisibly; GPACIC */
36 else {
37 stamp_make();
38 priority = LOG_INFO;
39 if (str_start(buf,"warning:")) priority = LOG_WARNING;
40 if (str_start(buf,"alert:")) priority = LOG_ALERT;
41 syslog(priority,"%s %s",stamp,buf);
42 flagcont = 1;
43 }
44 }
45 bufpos = 0;
46}
47
48void main(argc,argv)
49int argc;
50char **argv;
51{
52 char ch;
53
54 if (argv[1])
55 if (argv[2]) {
56 unsigned long facility;
57 scan_ulong(argv[2],&facility);
58 openlog(argv[1],0,facility << 3);
59 }
60 else
61 openlog(argv[1],0,LOG_MAIL);
62 else
63 openlog("splogger",0,LOG_MAIL);
64
65 for (;;) {
66 if (substdio_get(subfdin,&ch,1) < 1) _exit(0);
67 if (ch == '\n') { flush(); flagcont = 0; continue; }
68 if (bufpos == sizeof(buf) - 1) flush();
69 if ((ch < 32) || (ch > 126)) ch = '?'; /* logger truncates at 0; GPACIC */
70 buf[bufpos++] = ch;
71 }
72}
diff --git a/str.h b/str.h
new file mode 100644
index 0000000..e00773c
--- /dev/null
+++ b/str.h
@@ -0,0 +1,14 @@
1#ifndef STR_H
2#define STR_H
3
4extern unsigned int str_copy();
5extern int str_diff();
6extern int str_diffn();
7extern unsigned int str_len();
8extern unsigned int str_chr();
9extern unsigned int str_rchr();
10extern int str_start();
11
12#define str_equal(s,t) (!str_diff((s),(t)))
13
14#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 @@
1#include "str.h"
2
3unsigned int str_chr(s,c)
4register char *s;
5int c;
6{
7 register char ch;
8 register char *t;
9
10 ch = c;
11 t = s;
12 for (;;) {
13 if (!*t) break; if (*t == ch) break; ++t;
14 if (!*t) break; if (*t == ch) break; ++t;
15 if (!*t) break; if (*t == ch) break; ++t;
16 if (!*t) break; if (*t == ch) break; ++t;
17 }
18 return t - s;
19}
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 @@
1#include "str.h"
2
3unsigned int str_copy(s,t)
4register char *s;
5register char *t;
6{
7 register int len;
8
9 len = 0;
10 for (;;) {
11 if (!(*s = *t)) return len; ++s; ++t; ++len;
12 if (!(*s = *t)) return len; ++s; ++t; ++len;
13 if (!(*s = *t)) return len; ++s; ++t; ++len;
14 if (!(*s = *t)) return len; ++s; ++t; ++len;
15 }
16}
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 @@
1#include "str.h"
2
3int str_diff(s,t)
4register char *s;
5register char *t;
6{
7 register char x;
8
9 for (;;) {
10 x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
11 x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
12 x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
13 x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
14 }
15 return ((int)(unsigned int)(unsigned char) x)
16 - ((int)(unsigned int)(unsigned char) *t);
17}
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 @@
1#include "str.h"
2
3int str_diffn(s,t,len)
4register char *s;
5register char *t;
6unsigned int len;
7{
8 register char x;
9
10 for (;;) {
11 if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
12 if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
13 if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
14 if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
15 }
16 return ((int)(unsigned int)(unsigned char) x)
17 - ((int)(unsigned int)(unsigned char) *t);
18}
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 @@
1#include "str.h"
2
3unsigned int str_len(s)
4register char *s;
5{
6 register char *t;
7
8 t = s;
9 for (;;) {
10 if (!*t) return t - s; ++t;
11 if (!*t) return t - s; ++t;
12 if (!*t) return t - s; ++t;
13 if (!*t) return t - s; ++t;
14 }
15}
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 @@
1#include "str.h"
2
3unsigned int str_rchr(s,c)
4register char *s;
5int c;
6{
7 register char ch;
8 register char *t;
9 register char *u;
10
11 ch = c;
12 t = s;
13 u = 0;
14 for (;;) {
15 if (!*t) break; if (*t == ch) u = t; ++t;
16 if (!*t) break; if (*t == ch) u = t; ++t;
17 if (!*t) break; if (*t == ch) u = t; ++t;
18 if (!*t) break; if (*t == ch) u = t; ++t;
19 }
20 if (!u) u = t;
21 return u - s;
22}
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 @@
1#include "str.h"
2
3int str_start(s,t)
4register char *s;
5register char *t;
6{
7 register char x;
8
9 for (;;) {
10 x = *t++; if (!x) return 1; if (x != *s++) return 0;
11 x = *t++; if (!x) return 1; if (x != *s++) return 0;
12 x = *t++; if (!x) return 1; if (x != *s++) return 0;
13 x = *t++; if (!x) return 1; if (x != *s++) return 0;
14 }
15}
diff --git a/stralloc.3 b/stralloc.3
new file mode 100644
index 0000000..3123521
--- /dev/null
+++ b/stralloc.3
@@ -0,0 +1,160 @@
1.TH stralloc 3
2.SH NAME
3stralloc \- dynamically allocated strings
4.SH SYNTAX
5.B #include <stralloc.h>
6
7int \fBstralloc_ready\fP(&\fIsa\fR,\fIlen\fR);
8.br
9int \fBstralloc_readyplus\fP(&\fIsa\fR,\fIlen\fR);
10
11int \fBstralloc_copy\fP(&\fIsa\fR,&\fIsa2\fR);
12.br
13int \fBstralloc_copys\fP(&\fIsa\fR,\fIbuf\fR);
14.br
15int \fBstralloc_copyb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
16
17int \fBstralloc_cat\fP(&\fIsa\fR,&\fIsa2\fR);
18.br
19int \fBstralloc_cats\fP(&\fIsa\fR,\fIbuf\fR);
20.br
21int \fBstralloc_catb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
22
23int \fBstralloc_append\fP(&\fIsa\fR,\fIbuf\fR);
24.br
25int \fBstralloc_0\fP(&\fIsa\fR);
26
27int \fBstralloc_starts\fP(&\fIsa\fR,\fIbuf\fR);
28
29stralloc \fIsa\fR = {0};
30.br
31stralloc \fIsa2\fR = {0};
32.br
33unsigned int \fIlen\fR;
34.br
35char *\fIbuf\fR;
36.SH DESCRIPTION
37A
38.B stralloc
39variable holds a string in dynamically allocated space.
40String length is limited only by memory.
41String contents are unrestricted.
42
43The
44.B stralloc
45structure has three components:
46.I sa\fB.s
47is a pointer to the string, or 0 if it is not allocated;
48.I sa\fB.len
49is the number of bytes in the string, if it is allocated;
50.I sa\fB.a
51is the number of bytes allocated for the string, if it is allocated.
52A
53.B stralloc
54variable should be initialized to {0},
55meaning unallocated.
56
57.B stralloc_ready
58makes sure that
59.I sa
60has enough space allocated for
61.I len
62characters.
63It allocates extra space if necessary.
64
65.B stralloc_readyplus
66makes sure that
67.I sa
68has enough space allocated for
69.I len
70characters more than its current length.
71If
72.I sa
73is unallocated,
74.B stralloc_readyplus
75is the same as
76.BR stralloc_ready .
77
78.B stralloc_copy
79copies
80.I sa2
81to
82.IR sa ,
83allocating space if necessary.
84Here
85.I sa2
86is an allocated
87.B stralloc
88variable.
89
90.B stralloc_copys
91copies a 0-terminated string,
92.IR buf ,
93to
94.IR sa ,
95without the 0.
96
97.B stralloc_copyb
98copies
99.I len
100characters from
101.I buf
102to
103.IR sa .
104
105.B stralloc_cat
106appends
107.I sa2
108to
109.IR sa ,
110allocating space if necessary.
111If
112.I sa
113is unallocated,
114.B stralloc_cat
115is the same as
116.BR stralloc_copy .
117
118.B stralloc_cats
119and
120.B stralloc_catb
121are analogous to
122.B stralloc_copys
123and
124.BR stralloc_copyb .
125
126.B stralloc_append
127adds a single character,
128.IR *buf ,
129to
130.IR sa ,
131allocating space if necessary.
132
133.B stralloc_0
134adds a single 0 character
135to
136.IR sa .
137
138.B stralloc_starts
139returns 1 if the 0-terminated string
140.IR buf ,
141without the 0,
142is a prefix of
143.IR sa .
144.SH "ERROR HANDLING"
145If a
146.B stralloc
147routine runs out of memory,
148it leaves
149.I sa
150alone and returns 0,
151setting
152.B errno
153appropriately.
154On success it returns 1;
155this guarantees that
156.I sa
157is allocated.
158.SH "SEE ALSO"
159alloc(3),
160error(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 @@
1#ifndef STRALLOC_H
2#define STRALLOC_H
3
4#include "gen_alloc.h"
5
6GEN_ALLOC_typedef(stralloc,char,s,len,a)
7
8extern int stralloc_ready();
9extern int stralloc_readyplus();
10extern int stralloc_copy();
11extern int stralloc_cat();
12extern int stralloc_copys();
13extern int stralloc_cats();
14extern int stralloc_copyb();
15extern int stralloc_catb();
16extern int stralloc_append(); /* beware: this takes a pointer to 1 char */
17extern int stralloc_starts();
18
19#define stralloc_0(sa) stralloc_append(sa,"")
20
21#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 @@
1#include "byte.h"
2#include "str.h"
3#include "stralloc.h"
4
5int stralloc_starts(sa,s)
6stralloc *sa;
7char *s;
8{
9 int len;
10 len = str_len(s);
11 return (sa->len >= len) && byte_equal(s,len,sa->s);
12}
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 @@
1#include "byte.h"
2#include "stralloc.h"
3
4int stralloc_cat(sato,safrom)
5stralloc *sato;
6stralloc *safrom;
7{
8 return stralloc_catb(sato,safrom->s,safrom->len);
9}
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 @@
1#include "stralloc.h"
2#include "byte.h"
3
4int stralloc_catb(sa,s,n)
5stralloc *sa;
6char *s;
7unsigned int n;
8{
9 if (!sa->s) return stralloc_copyb(sa,s,n);
10 if (!stralloc_readyplus(sa,n + 1)) return 0;
11 byte_copy(sa->s + sa->len,n,s);
12 sa->len += n;
13 sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
14 return 1;
15}
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 @@
1#include "byte.h"
2#include "str.h"
3#include "stralloc.h"
4
5int stralloc_cats(sa,s)
6stralloc *sa;
7char *s;
8{
9 return stralloc_catb(sa,s,str_len(s));
10}
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 @@
1#include "byte.h"
2#include "stralloc.h"
3
4int stralloc_copy(sato,safrom)
5stralloc *sato;
6stralloc *safrom;
7{
8 return stralloc_copyb(sato,safrom->s,safrom->len);
9}
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 @@
1#include "alloc.h"
2#include "stralloc.h"
3#include "gen_allocdefs.h"
4
5GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
6GEN_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 @@
1#include "stralloc.h"
2#include "byte.h"
3
4int stralloc_copyb(sa,s,n)
5stralloc *sa;
6char *s;
7unsigned int n;
8{
9 if (!stralloc_ready(sa,n + 1)) return 0;
10 byte_copy(sa->s,n,s);
11 sa->len = n;
12 sa->s[n] = 'Z'; /* ``offensive programming'' */
13 return 1;
14}
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 @@
1#include "byte.h"
2#include "str.h"
3#include "stralloc.h"
4
5int stralloc_copys(sa,s)
6stralloc *sa;
7char *s;
8{
9 return stralloc_copyb(sa,s,str_len(s));
10}
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 @@
1#include "alloc.h"
2#include "stralloc.h"
3#include "gen_allocdefs.h"
4
5GEN_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 @@
1#ifndef STRERR_H
2#define STRERR_H
3
4struct strerr
5 {
6 struct strerr *who;
7 char *x;
8 char *y;
9 char *z;
10 }
11;
12
13extern struct strerr strerr_sys;
14extern void strerr_sysinit();
15
16extern char *strerr();
17extern void strerr_warn();
18extern void strerr_die();
19
20#define STRERR(r,se,a) \
21{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
22
23#define STRERR_SYS(r,se,a) \
24{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; }
25#define STRERR_SYS3(r,se,a,b,c) \
26{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
27
28#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
29strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
30#define strerr_warn5(x1,x2,x3,x4,x5,se) \
31strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
32#define strerr_warn4(x1,x2,x3,x4,se) \
33strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
34#define strerr_warn3(x1,x2,x3,se) \
35strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
36#define strerr_warn2(x1,x2,se) \
37strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
38#define strerr_warn1(x1,se) \
39strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
40
41#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
42strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
43#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
44strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
45#define strerr_die4(e,x1,x2,x3,x4,se) \
46strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
47#define strerr_die3(e,x1,x2,x3,se) \
48strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
49#define strerr_die2(e,x1,x2,se) \
50strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
51#define strerr_die1(e,x1,se) \
52strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
53
54#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
55strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
56#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
57strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
58#define strerr_die4sys(e,x1,x2,x3,x4) \
59strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
60#define strerr_die3sys(e,x1,x2,x3) \
61strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
62#define strerr_die2sys(e,x1,x2) \
63strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
64#define strerr_die1sys(e,x1) \
65strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
66
67#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
68strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
69#define strerr_die5x(e,x1,x2,x3,x4,x5) \
70strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
71#define strerr_die4x(e,x1,x2,x3,x4) \
72strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
73#define strerr_die3x(e,x1,x2,x3) \
74strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
75#define strerr_die2x(e,x1,x2) \
76strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
77#define strerr_die1x(e,x1) \
78strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
79
80#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 @@
1#include "substdio.h"
2#include "subfd.h"
3#include "exit.h"
4#include "strerr.h"
5
6void strerr_warn(x1,x2,x3,x4,x5,x6,se)
7char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
8struct strerr *se;
9{
10 strerr_sysinit();
11
12 if (x1) substdio_puts(subfderr,x1);
13 if (x2) substdio_puts(subfderr,x2);
14 if (x3) substdio_puts(subfderr,x3);
15 if (x4) substdio_puts(subfderr,x4);
16 if (x5) substdio_puts(subfderr,x5);
17 if (x6) substdio_puts(subfderr,x6);
18
19 while(se) {
20 if (se->x) substdio_puts(subfderr,se->x);
21 if (se->y) substdio_puts(subfderr,se->y);
22 if (se->z) substdio_puts(subfderr,se->z);
23 se = se->who;
24 }
25
26 substdio_puts(subfderr,"\n");
27 substdio_flush(subfderr);
28}
29
30void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
31int e;
32char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
33struct strerr *se;
34{
35 strerr_warn(x1,x2,x3,x4,x5,x6,se);
36 _exit(e);
37}
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 @@
1#include "error.h"
2#include "strerr.h"
3
4struct strerr strerr_sys;
5
6void strerr_sysinit()
7{
8 strerr_sys.who = 0;
9 strerr_sys.x = error_str(errno);
10 strerr_sys.y = "";
11 strerr_sys.z = "";
12}
diff --git a/subfd.h b/subfd.h
new file mode 100644
index 0000000..bcb2e1e
--- /dev/null
+++ b/subfd.h
@@ -0,0 +1,15 @@
1#ifndef SUBFD_H
2#define SUBFD_H
3
4#include "substdio.h"
5
6extern substdio *subfdin;
7extern substdio *subfdinsmall;
8extern substdio *subfdout;
9extern substdio *subfdoutsmall;
10extern substdio *subfderr;
11
12extern int subfd_read();
13extern int subfd_readsmall();
14
15#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 @@
1#include "readwrite.h"
2#include "substdio.h"
3#include "subfd.h"
4
5char subfd_errbuf[256];
6static substdio it = SUBSTDIO_FDBUF(write,2,subfd_errbuf,256);
7substdio *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 @@
1#include "readwrite.h"
2#include "substdio.h"
3#include "subfd.h"
4
5int subfd_read(fd,buf,len) int fd; char *buf; int len;
6{
7 if (substdio_flush(subfdout) == -1) return -1;
8 return read(fd,buf,len);
9}
10
11char subfd_inbuf[SUBSTDIO_INSIZE];
12static substdio it = SUBSTDIO_FDBUF(subfd_read,0,subfd_inbuf,SUBSTDIO_INSIZE);
13substdio *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 @@
1#include "readwrite.h"
2#include "substdio.h"
3#include "subfd.h"
4
5int subfd_readsmall(fd,buf,len) int fd; char *buf; int len;
6{
7 if (substdio_flush(subfdoutsmall) == -1) return -1;
8 return read(fd,buf,len);
9}
10
11char subfd_inbufsmall[256];
12static substdio it = SUBSTDIO_FDBUF(subfd_readsmall,0,subfd_inbufsmall,256);
13substdio *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 @@
1#include "readwrite.h"
2#include "substdio.h"
3#include "subfd.h"
4
5char subfd_outbuf[SUBSTDIO_OUTSIZE];
6static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbuf,SUBSTDIO_OUTSIZE);
7substdio *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 @@
1#include "readwrite.h"
2#include "substdio.h"
3#include "subfd.h"
4
5char subfd_outbufsmall[256];
6static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbufsmall,256);
7substdio *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 @@
1.TH subgetopt 3
2.SH NAME
3subgetopt \- get option character from command line
4.SH SYNTAX
5.B #include <subgetopt.h>
6
7char *\fBsgoptarg\fP;
8.br
9int \fBsgoptind\fP;
10.br
11int \fBsgoptpos\fP;
12.br
13int \fBsgoptdone\fP;
14.br
15int \fBsgoptproblem\fP;
16
17int \fBsgopt(\fP\fIargc,argv,opts\fR\fB)\fP;
18
19int \fIargc\fR;
20.br
21char **\fIargv\fR;
22.br
23char *\fIopts\fR;
24.SH DESCRIPTION
25.B sgopt
26returns the next valid command-line option character
27from
28.IR argv .
29
30Valid option characters are listed in the
31.I opts
32string.
33.I opts
34may be empty.
35A character in
36.I opts
37may be followed by a colon,
38in which case it
39takes an
40.I option argument\fR.
41Avoid using the characters ?, :, and \- as option characters.
42
43Below
44.I option argument
45is abbreviated
46as
47.I optarg
48and
49.I command-line argument
50is abbreviated as
51.IR cmdarg .
52
53Options are listed in cmdargs which begin with
54a minus sign.
55Several options which do not take optargs may be combined
56into one cmdarg.
57
58An option which takes an optarg may be handled in two ways.
59If it appears at the very end of a cmdarg,
60then the entire next cmdarg is the optarg.
61But if there are any characters in the cmdarg
62after the option character,
63then those characters form the optarg.
64The optarg is returned in
65.BR sgoptarg .
66Next time
67.B sgopt
68looks at the cmdarg which follows the optarg.
69
70If a cmdarg does not begin with a hyphen,
71or if it is a lone hyphen not followed by any characters,
72or if it begins with two hyphens,
73then it terminates option processing,
74and
75.B sgopt
76returns an appropriate code.
77If there are two hyphens,
78.B sgopt
79will advance attention to the next cmdarg,
80so it can be called again to read further options.
81.SH "PROPER USAGE"
82.B sgoptproblem
83should be used only when
84.B sgopt
85returns ?.
86.B sgoptind
87and
88.B sgoptpos
89are defined all the time.
90.B sgoptarg
91is defined all the time;
92it is null unless
93.B sgopt
94has just returned an option with optarg.
95
96.B sgopt
97is typically used as follows.
98
99.EX
100#include <subgetopt.h>
101
102main(argc,argv) int argc; char **argv; { int opt;
103
104while ((opt = sgopt(argc,argv,"a:s")) != sgoptdone)
105.br
106 switch(opt) {
107.br
108 case 'a':
109.br
110 printf("opt a with optarg %s\\n",sgoptarg); break;
111.br
112 case 's':
113.br
114 printf("opt s with no optarg\\n"); break;
115.br
116 case '?':
117.br
118 if (argv[sgoptind] && (sgoptind < argc))
119.br
120 printf("illegal opt %c\\n",sgoptproblem);
121.br
122 else
123.br
124 printf("missing arg, opt %c\\n",sgoptproblem);
125.br
126 exit(1);
127.br
128 }
129
130argv += sgoptind;
131.br
132while (*argv) printf("argument %s\\n",*argv++);
133.br
134exit(0);
135.br
136}
137.EE
138
139The end of the command line is
140marked by either
141.IR argc ,
142or a null pointer in
143.IR argv ,
144whichever comes first.
145Normally
146these two markers coincide,
147so it is redundant
148to test for
149both
150.I argv\fB[sgoptind]
151and
152.B sgoptind < \fIargc\fR.
153The above code shows both tests as an illustration.
154
155.B Multiple option sets:
156One useful technique is to call
157.B sgopt
158with a primary
159.I opts
160until it returns EOF,
161then call
162.B sgopt
163with a secondary
164.I opts
165until it returns EOF.
166The user can provide primary options, then a double hyphen,
167and then secondary options.
168No special handling is needed if some or all of the options are
169omitted.
170The same technique can be used for any number of option sets
171in series.
172
173.B Multiple command lines:
174Before parsing a new
175.BR argv ,
176make sure to
177set
178.B sgoptind
179and
180.B sgoptpos
181back to
1821 and 0.
183.SH "PARSING STAGES"
184.B sgopt
185keeps track of its position in
186.I argv
187with
188.B sgoptind
189and
190.BR sgoptpos ,
191which are initialized to 1 and 0.
192It looks at
193.I argv\fB[sgoptind][sgoptpos]
194and following characters.
195
196.B sgopt
197indicates
198that no more options are available by
199returning
200.BR sgoptdone ,
201which is initialized to
202.BR SUBGETOPTDONE ,
203which is defined as \-1.
204
205.B sgopt
206begins by setting
207.B optarg
208to null.
209
210.B Ending conditions:
211If
212.I argv
213is null, or
214.B sgoptind
215is larger than
216.IR argc ,
217or the current cmdarg
218.I argv\fB[sgoptind]
219is null,
220then
221.B sgopt
222returns
223.BR optdone .
224
225.B Stage one:
226If the current character
227is zero,
228.B sgopt
229moves to the beginning of the next cmdarg.
230It then checks the ending conditions again.
231
232.B Stage two:
233If
234the current position is the begining of the cmdarg,
235.B sgopt
236checks whether
237the current character
238is a minus sign.
239If not it returns
240.BR optdone .
241It then
242moves
243to the next character.
244If that character is zero,
245.B sgopt
246moves
247back to the beginning of the cmdarg,
248and returns
249.BR sgoptdone .
250If the character is a minus sign,
251.B sgopt
252moves to the beginning of the next cmdarg,
253and returns
254.BR sgoptdone .
255
256.B Stage three:
257.B sgopt
258records the current character,
259.IR c ,
260and moves to the next character.
261There are three possibilities:
262(1)
263.I c
264is an option character without optarg in
265.IR opts ,
266or
267(2)
268.I c
269is an option character with optarg in
270.IR opts ,
271or
272(3)
273.I c
274does not appear in
275.IR opts .
276
277(1)
278If
279.I c
280appears as an option character without optarg in
281.IR opts ,
282.B sgopt
283returns
284.IR c .
285
286(2)
287If
288.I c
289appears as an option character with optarg in
290.IR opts ,
291.B sgopt
292sets
293.B sgoptarg
294to the current position,
295and moves to the next cmdarg.
296If
297.B sgoptarg
298is nonempty,
299.B sgopt
300returns
301.IR c .
302
303Then
304.B sgopt
305sets
306.B sgoptarg
307to
308the current cmdarg.
309If
310the current cmdarg is null,
311or past
312.IR argc ,
313.B sgopt
314sets
315.B sgoptproblem
316to
317.I c
318and returns ?.
319Otherwise
320.B sgopt
321moves to the next
322argument
323and returns
324.IR c .
325
326(2)
327If
328.I c
329does not appear in
330.IR opts ,
331.B sgopt
332sets
333.B sgoptproblem
334to
335.I c
336and returns ?.
337.SH "SYNTAX NOTE"
338.B sgopt
339is actually a macro abbreviation for
340.BR subgetopt .
341The external
342.B sg
343variables are also macros
344for
345.BR subget .
346These macros are defined in
347.BR <subgetopt.h> ,
348unless
349.B SUBGETOPTNOSHORT
350is defined
351when
352.B <subgetopt.h>
353is included.
354.SH VERSION
355subgetopt version 0.9, 931129.
356.SH AUTHOR
357Placed 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 @@
1/* subgetopt.c, subgetopt.h: (yet another) improved getopt clone, inner layer
2D. J. Bernstein, djb@pobox.com.
3No dependencies.
4No system requirements.
519970228: Cleanups.
6931129: Adapted from getopt.c.
7No known patent problems.
8
9Documentation in subgetopt.3.
10*/
11
12#define SUBGETOPTNOSHORT
13#include "subgetopt.h"
14
15#define sgopt subgetopt
16#define optind subgetoptind
17#define optpos subgetoptpos
18#define optarg subgetoptarg
19#define optproblem subgetoptproblem
20#define optdone subgetoptdone
21
22int optind = 1;
23int optpos = 0;
24char *optarg = 0;
25int optproblem = 0;
26int optdone = SUBGETOPTDONE;
27
28int sgopt(argc,argv,opts)
29int argc;
30char **argv;
31char *opts;
32{
33 int c;
34 char *s;
35
36 optarg = 0;
37 if (!argv || (optind >= argc) || !argv[optind]) return optdone;
38 if (optpos && !argv[optind][optpos]) {
39 ++optind;
40 optpos = 0;
41 if ((optind >= argc) || !argv[optind]) return optdone;
42 }
43 if (!optpos) {
44 if (argv[optind][0] != '-') return optdone;
45 ++optpos;
46 c = argv[optind][1];
47 if ((c == '-') || (c == 0)) {
48 if (c) ++optind;
49 optpos = 0;
50 return optdone;
51 }
52 /* otherwise c is reassigned below */
53 }
54 c = argv[optind][optpos];
55 ++optpos;
56 s = opts;
57 while (*s) {
58 if (c == *s) {
59 if (s[1] == ':') {
60 optarg = argv[optind] + optpos;
61 ++optind;
62 optpos = 0;
63 if (!*optarg) {
64 optarg = argv[optind];
65 if ((optind >= argc) || !optarg) { /* argument past end */
66 optproblem = c;
67 return '?';
68 }
69 ++optind;
70 }
71 }
72 return c;
73 }
74 ++s;
75 if (*s == ':') ++s;
76 }
77 optproblem = c;
78 return '?';
79}
diff --git a/subgetopt.h b/subgetopt.h
new file mode 100644
index 0000000..d26c62a
--- /dev/null
+++ b/subgetopt.h
@@ -0,0 +1,24 @@
1#ifndef SUBGETOPT_H
2#define SUBGETOPT_H
3
4#ifndef SUBGETOPTNOSHORT
5#define sgopt subgetopt
6#define sgoptarg subgetoptarg
7#define sgoptind subgetoptind
8#define sgoptpos subgetoptpos
9#define sgoptproblem subgetoptproblem
10#define sgoptprogname subgetoptprogname
11#define sgoptdone subgetoptdone
12#endif
13
14#define SUBGETOPTDONE -1
15
16extern int subgetopt();
17extern char *subgetoptarg;
18extern int subgetoptind;
19extern int subgetoptpos;
20extern int subgetoptproblem;
21extern char *subgetoptprogname;
22extern int subgetoptdone;
23
24#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 @@
1#include "substdio.h"
2#include "byte.h"
3#include "error.h"
4
5static int oneread(op,fd,buf,len)
6register int (*op)();
7register int fd;
8register char *buf;
9register int len;
10{
11 register int r;
12
13 for (;;) {
14 r = op(fd,buf,len);
15 if (r == -1) if (errno == error_intr) continue;
16 return r;
17 }
18}
19
20static int getthis(s,buf,len)
21register substdio *s;
22register char *buf;
23register int len;
24{
25 register int r;
26 register int q;
27
28 r = s->p;
29 q = r - len;
30 if (q > 0) { r = len; s->p = q; } else s->p = 0;
31 byte_copy(buf,r,s->x + s->n);
32 s->n += r;
33 return r;
34}
35
36int substdio_feed(s)
37register substdio *s;
38{
39 register int r;
40 register int q;
41
42 if (s->p) return s->p;
43 q = s->n;
44 r = oneread(s->op,s->fd,s->x,q);
45 if (r <= 0) return r;
46 s->p = r;
47 q -= r;
48 s->n = q;
49 if (q > 0) /* damn, gotta shift */ byte_copyr(s->x + q,r,s->x);
50 return r;
51}
52
53int substdio_bget(s,buf,len)
54register substdio *s;
55register char *buf;
56register int len;
57{
58 register int r;
59
60 if (s->p > 0) return getthis(s,buf,len);
61 r = s->n; if (r <= len) return oneread(s->op,s->fd,buf,r);
62 r = substdio_feed(s); if (r <= 0) return r;
63 return getthis(s,buf,len);
64}
65
66int substdio_get(s,buf,len)
67register substdio *s;
68register char *buf;
69register int len;
70{
71 register int r;
72
73 if (s->p > 0) return getthis(s,buf,len);
74 if (s->n <= len) return oneread(s->op,s->fd,buf,len);
75 r = substdio_feed(s); if (r <= 0) return r;
76 return getthis(s,buf,len);
77}
78
79char *substdio_peek(s)
80register substdio *s;
81{
82 return s->x + s->n;
83}
84
85void substdio_seek(s,len)
86register substdio *s;
87register int len;
88{
89 s->n += len;
90 s->p -= len;
91}
diff --git a/substdio.c b/substdio.c
new file mode 100644
index 0000000..d03dff2
--- /dev/null
+++ b/substdio.c
@@ -0,0 +1,15 @@
1#include "substdio.h"
2
3void substdio_fdbuf(s,op,fd,buf,len)
4register substdio *s;
5register int (*op)();
6register int fd;
7register char *buf;
8register int len;
9{
10 s->x = buf;
11 s->fd = fd;
12 s->op = op;
13 s->p = 0;
14 s->n = len;
15}
diff --git a/substdio.h b/substdio.h
new file mode 100644
index 0000000..c3f7f7d
--- /dev/null
+++ b/substdio.h
@@ -0,0 +1,47 @@
1#ifndef SUBSTDIO_H
2#define SUBSTDIO_H
3
4typedef struct substdio {
5 char *x;
6 int p;
7 int n;
8 int fd;
9 int (*op)();
10} substdio;
11
12#define SUBSTDIO_FDBUF(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
13
14extern void substdio_fdbuf();
15
16extern int substdio_flush();
17extern int substdio_put();
18extern int substdio_bput();
19extern int substdio_putflush();
20extern int substdio_puts();
21extern int substdio_bputs();
22extern int substdio_putsflush();
23
24extern int substdio_get();
25extern int substdio_bget();
26extern int substdio_feed();
27
28extern char *substdio_peek();
29extern void substdio_seek();
30
31#define substdio_fileno(s) ((s)->fd)
32
33#define SUBSTDIO_INSIZE 8192
34#define SUBSTDIO_OUTSIZE 8192
35
36#define substdio_PEEK(s) ( (s)->x + (s)->n )
37#define substdio_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
38
39#define substdio_BPUTC(s,c) \
40 ( ((s)->n != (s)->p) \
41 ? ( (s)->x[(s)->p++] = (c), 0 ) \
42 : substdio_bput((s),&(c),1) \
43 )
44
45extern int substdio_copy();
46
47#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 @@
1#include "substdio.h"
2
3int substdio_copy(ssout,ssin)
4register substdio *ssout;
5register substdio *ssin;
6{
7 register int n;
8 register char *x;
9
10 for (;;) {
11 n = substdio_feed(ssin);
12 if (n < 0) return -2;
13 if (!n) return 0;
14 x = substdio_PEEK(ssin);
15 if (substdio_put(ssout,x,n) == -1) return -3;
16 substdio_SEEK(ssin,n);
17 }
18}
diff --git a/substdo.c b/substdo.c
new file mode 100644
index 0000000..fb616f7
--- /dev/null
+++ b/substdo.c
@@ -0,0 +1,108 @@
1#include "substdio.h"
2#include "str.h"
3#include "byte.h"
4#include "error.h"
5
6static int allwrite(op,fd,buf,len)
7register int (*op)();
8register int fd;
9register char *buf;
10register int len;
11{
12 register int w;
13
14 while (len) {
15 w = op(fd,buf,len);
16 if (w == -1) {
17 if (errno == error_intr) continue;
18 return -1; /* note that some data may have been written */
19 }
20 if (w == 0) ; /* luser's fault */
21 buf += w;
22 len -= w;
23 }
24 return 0;
25}
26
27int substdio_flush(s)
28register substdio *s;
29{
30 register int p;
31
32 p = s->p;
33 if (!p) return 0;
34 s->p = 0;
35 return allwrite(s->op,s->fd,s->x,p);
36}
37
38int substdio_bput(s,buf,len)
39register substdio *s;
40register char *buf;
41register int len;
42{
43 register int n;
44
45 while (len > (n = s->n - s->p)) {
46 byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
47 if (substdio_flush(s) == -1) return -1;
48 }
49 /* now len <= s->n - s->p */
50 byte_copy(s->x + s->p,len,buf);
51 s->p += len;
52 return 0;
53}
54
55int substdio_put(s,buf,len)
56register substdio *s;
57register char *buf;
58register int len;
59{
60 register int n;
61
62 n = s->n;
63 if (len > n - s->p) {
64 if (substdio_flush(s) == -1) return -1;
65 /* now s->p == 0 */
66 if (n < SUBSTDIO_OUTSIZE) n = SUBSTDIO_OUTSIZE;
67 while (len > s->n) {
68 if (n > len) n = len;
69 if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
70 buf += n;
71 len -= n;
72 }
73 }
74 /* now len <= s->n - s->p */
75 byte_copy(s->x + s->p,len,buf);
76 s->p += len;
77 return 0;
78}
79
80int substdio_putflush(s,buf,len)
81register substdio *s;
82register char *buf;
83register int len;
84{
85 if (substdio_flush(s) == -1) return -1;
86 return allwrite(s->op,s->fd,buf,len);
87}
88
89int substdio_bputs(s,buf)
90register substdio *s;
91register char *buf;
92{
93 return substdio_bput(s,buf,str_len(buf));
94}
95
96int substdio_puts(s,buf)
97register substdio *s;
98register char *buf;
99{
100 return substdio_put(s,buf,str_len(buf));
101}
102
103int substdio_putsflush(s,buf)
104register substdio *s;
105register char *buf;
106{
107 return substdio_putflush(s,buf,str_len(buf));
108}
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 @@
1.TH tcp-env 1
2.SH NAME
3tcp-env \- set up TCP-related environment variables
4.SH SYNOPSIS
5.B tcp-env
6[
7.B \-rR
8]
9[
10.B \-t\fItimeout
11]
12.I program
13[
14.I arg ...
15]
16.SH DESCRIPTION
17The input for
18.B tcp-env
19must be a TCP connection.
20.B tcp-env
21finds out information about that connection,
22puts the information into several environment variables
23as described in
24.B tcp-environ(5),
25and runs
26.I program
27with the given arguments.
28
29Usually
30.B tcp-env
31is run from
32.BR inetd .
33It might instead be run from another server
34that already sets up the right environment variables;
35if
36.B PROTO
37is set to
38.B TCP
39when
40.B tcp-env
41is invoked,
42.B tcp-env
43assumes that all the other variables are set up properly,
44and it does not check whether the input is a TCP connection.
45.SH OPTIONS
46.TP
47.B \-r
48(Default.)
49Attempt to obtain
50.B TCPREMOTEINFO
51from the remote host.
52.TP
53.B \-R
54Do not attempt to obtain
55.B TCPREMOTEINFO
56from the remote host.
57.TP
58.B \-t\fItimeout
59Give up on the
60.B TCPREMOTEINFO
61connection attempt after
62.I timeout
63seconds.
64Default: 30.
65.SH "SEE ALSO"
66tcp-environ(5),
67inetd(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 @@
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <sys/param.h>
4#include <netinet/in.h>
5#include "sig.h"
6#include "stralloc.h"
7#include "str.h"
8#include "env.h"
9#include "fmt.h"
10#include "scan.h"
11#include "subgetopt.h"
12#include "ip.h"
13#include "dns.h"
14#include "byte.h"
15#include "remoteinfo.h"
16#include "exit.h"
17#include "case.h"
18
19void die() { _exit(111); }
20
21struct sockaddr_in salocal;
22unsigned long localport;
23struct ip_address iplocal;
24stralloc localname = {0};
25
26struct sockaddr_in saremote;
27unsigned long remoteport;
28struct ip_address ipremote;
29stralloc remotename = {0};
30
31char temp[IPFMT + FMT_ULONG];
32
33void main(argc,argv)
34int argc;
35char *argv[];
36{
37 int dummy;
38 char *proto;
39 int opt;
40 int flagremoteinfo;
41 unsigned long timeout;
42
43 sig_pipeignore();
44
45 flagremoteinfo = 1;
46 timeout = 30;
47 while ((opt = sgopt(argc,argv,"rRt:")) != sgoptdone)
48 switch(opt)
49 {
50 case 'r': flagremoteinfo = 1; break;
51 case 'R': flagremoteinfo = 0; break;
52 case 't': scan_ulong(sgoptarg,&timeout); break;
53 }
54
55 argv += sgoptind;
56 argc -= sgoptind;
57
58 if (argc < 1) die();
59 if (!env_init()) die();
60
61 proto = env_get("PROTO");
62 if (!proto || str_diff(proto,"TCP"))
63 {
64 if (!env_put("PROTO=TCP")) die();
65
66 dummy = sizeof(salocal);
67 if (getsockname(0,(struct sockaddr *) &salocal,&dummy) == -1) die();
68
69 localport = ntohs(salocal.sin_port);
70 temp[fmt_ulong(temp,localport)] = 0;
71 if (!env_put2("TCPLOCALPORT",temp)) die();
72
73 byte_copy(&iplocal,4,&salocal.sin_addr);
74 temp[ip_fmt(temp,&iplocal)] = 0;
75 if (!env_put2("TCPLOCALIP",temp)) die();
76
77 switch(dns_ptr(&localname,&iplocal))
78 {
79 case DNS_MEM: die();
80 case DNS_SOFT:
81 if (!stralloc_copys(&localname,"softdnserror")) die();
82 case 0:
83 if (!stralloc_0(&localname)) die();
84 case_lowers(localname.s);
85 if (!env_put2("TCPLOCALHOST",localname.s)) die();
86 break;
87 default:
88 if (!env_unset("TCPLOCALHOST")) die();
89 }
90
91 dummy = sizeof(saremote);
92 if (getpeername(0,(struct sockaddr *) &saremote,&dummy) == -1) die();
93
94 remoteport = ntohs(saremote.sin_port);
95 temp[fmt_ulong(temp,remoteport)] = 0;
96 if (!env_put2("TCPREMOTEPORT",temp)) die();
97
98 byte_copy(&ipremote,4,&saremote.sin_addr);
99 temp[ip_fmt(temp,&ipremote)] = 0;
100 if (!env_put2("TCPREMOTEIP",temp)) die();
101
102 switch(dns_ptr(&remotename,&ipremote))
103 {
104 case DNS_MEM: die();
105 case DNS_SOFT:
106 if (!stralloc_copys(&remotename,"softdnserror")) die();
107 case 0:
108 if (!stralloc_0(&remotename)) die();
109 case_lowers(remotename.s);
110 if (!env_put2("TCPREMOTEHOST",remotename.s)) die();
111 break;
112 default:
113 if (!env_unset("TCPREMOTEHOST")) die();
114 }
115
116 if (!env_unset("TCPREMOTEINFO")) die();
117 if (flagremoteinfo)
118 {
119 char *rinfo;
120 rinfo = remoteinfo_get(&ipremote,remoteport,&iplocal,localport,(int) timeout);
121 if (rinfo)
122 if (!env_put2("TCPREMOTEINFO",rinfo)) die();
123 }
124 }
125
126 sig_pipedefault();
127 execvp(*argv,argv);
128 die();
129}
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 @@
1.TH tcp-environ 5
2.SH NAME
3tcp-environ \- TCP-related environment variables
4.SH DESCRIPTION
5The following environment variables
6describe a TCP connection.
7They are set up by
8.BR tcp-env ,
9.BR tcpclient ,
10and
11.BR tcpserver .
12Note that
13.BR TCPLOCALHOST ,
14.BR TCPREMOTEHOST ,
15and
16.B TCPREMOTEINFO
17can contain arbitrary characters.
18.TP 5
19PROTO
20The string
21.BR TCP .
22.TP 5
23TCPLOCALHOST
24The domain name of the local host,
25with uppercase letters converted to lowercase.
26If there is no currently available domain name
27for the local IP address,
28.B TCPLOCALHOST
29is not set.
30.TP 5
31TCPLOCALIP
32The IP address of the local host, in dotted-decimal form.
33.TP 5
34TCPLOCALPORT
35The local TCP port number, in decimal.
36.TP 5
37TCPREMOTEHOST
38The domain name of the remote host,
39with uppercase letters converted to lowercase.
40If there is no currently available domain name
41for the remote IP address,
42.B TCPREMOTEHOST
43is not set.
44.TP 5
45TCPREMOTEINFO
46A connection-specific string, perhaps a username,
47supplied by the remote host
48via 931/1413/IDENT/TAP.
49If the remote host did not supply connection information,
50.B TCPREMOTEINFO
51is not set.
52.TP 5
53TCPREMOTEIP
54The IP address of the remote host.
55.TP 5
56TCPREMOTEPORT
57The remote TCP port number.
58.SH "SEE ALSO"
59tcpclient(1),
60tcpserver(1),
61tcp-env(1),
62tcp(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 @@
1#include "tcpto.h"
2#include "open.h"
3#include "lock.h"
4#include "seek.h"
5#include "now.h"
6#include "ip.h"
7#include "byte.h"
8#include "datetime.h"
9#include "readwrite.h"
10
11char tcpto_buf[1024];
12
13static int flagwasthere;
14static int fdlock;
15
16static int getbuf()
17{
18 int r;
19 int fd;
20
21 fdlock = open_write("queue/lock/tcpto");
22 if (fdlock == -1) return 0;
23 fd = open_read("queue/lock/tcpto");
24 if (fd == -1) { close(fdlock); return 0; }
25 if (lock_ex(fdlock) == -1) { close(fdlock); close(fd); return 0; }
26 r = read(fd,tcpto_buf,sizeof(tcpto_buf));
27 close(fd);
28 if (r < 0) { close(fdlock); return 0; }
29 r >>= 4;
30 if (!r) close(fdlock);
31 return r;
32}
33
34int tcpto(ip) struct ip_address *ip;
35{
36 int n;
37 int i;
38 char *record;
39 datetime_sec when;
40
41 flagwasthere = 0;
42
43 n = getbuf();
44 if (!n) return 0;
45 close(fdlock);
46
47 record = tcpto_buf;
48 for (i = 0;i < n;++i)
49 {
50 if (byte_equal(ip->d,4,record))
51 {
52 flagwasthere = 1;
53 if (record[4] >= 2)
54 {
55 when = (unsigned long) (unsigned char) record[11];
56 when = (when << 8) + (unsigned long) (unsigned char) record[10];
57 when = (when << 8) + (unsigned long) (unsigned char) record[9];
58 when = (when << 8) + (unsigned long) (unsigned char) record[8];
59
60 if (now() - when < ((60 + (getpid() & 31)) << 6))
61 return 1;
62 }
63 return 0;
64 }
65 record += 16;
66 }
67 return 0;
68}
69
70void tcpto_err(ip,flagerr) struct ip_address *ip; int flagerr;
71{
72 int n;
73 int i;
74 char *record;
75 datetime_sec when;
76 datetime_sec firstwhen;
77 int firstpos;
78 datetime_sec lastwhen;
79
80 if (!flagerr)
81 if (!flagwasthere)
82 return; /* could have been added, but not worth the effort to check */
83
84 n = getbuf();
85 if (!n) return;
86
87 record = tcpto_buf;
88 for (i = 0;i < n;++i)
89 {
90 if (byte_equal(ip->d,4,record))
91 {
92 if (!flagerr)
93 record[4] = 0;
94 else
95 {
96 lastwhen = (unsigned long) (unsigned char) record[11];
97 lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[10];
98 lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[9];
99 lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[8];
100 when = now();
101
102 if (record[4] && (when < 120 + lastwhen)) { close(fdlock); return; }
103
104 if (++record[4] > 10) record[4] = 10;
105 record[8] = when; when >>= 8;
106 record[9] = when; when >>= 8;
107 record[10] = when; when >>= 8;
108 record[11] = when;
109 }
110 if (seek_set(fdlock,i << 4) == 0)
111 if (write(fdlock,record,16) < 16)
112 ; /*XXX*/
113 close(fdlock);
114 return;
115 }
116 record += 16;
117 }
118
119 if (!flagerr) { close(fdlock); return; }
120
121 record = tcpto_buf;
122 for (i = 0;i < n;++i)
123 {
124 if (!record[4]) break;
125 record += 16;
126 }
127
128 if (i >= n)
129 {
130 firstpos = -1;
131 record = tcpto_buf;
132 for (i = 0;i < n;++i)
133 {
134 when = (unsigned long) (unsigned char) record[11];
135 when = (when << 8) + (unsigned long) (unsigned char) record[10];
136 when = (when << 8) + (unsigned long) (unsigned char) record[9];
137 when = (when << 8) + (unsigned long) (unsigned char) record[8];
138 when += (record[4] << 10);
139 if ((firstpos < 0) || (when < firstwhen))
140 {
141 firstpos = i;
142 firstwhen = when;
143 }
144 record += 16;
145 }
146 i = firstpos;
147 }
148
149 if (i >= 0)
150 {
151 record = tcpto_buf + (i << 4);
152 byte_copy(record,4,ip->d);
153 when = now();
154 record[8] = when; when >>= 8;
155 record[9] = when; when >>= 8;
156 record[10] = when; when >>= 8;
157 record[11] = when;
158 record[4] = 1;
159 if (seek_set(fdlock,i << 4) == 0)
160 if (write(fdlock,record,16) < 16)
161 ; /*XXX*/
162 }
163
164 close(fdlock);
165}
diff --git a/tcpto.h b/tcpto.h
new file mode 100644
index 0000000..cd44a42
--- /dev/null
+++ b/tcpto.h
@@ -0,0 +1,8 @@
1#ifndef TCPTO_H
2#define TCPTO_H
3
4extern int tcpto();
5extern void tcpto_err();
6extern void tcpto_clean();
7
8#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 @@
1#include "tcpto.h"
2#include "open.h"
3#include "substdio.h"
4#include "readwrite.h"
5
6char tcpto_cleanbuf[1024];
7
8void tcpto_clean() /* running from queue/mess */
9{
10 int fd;
11 int i;
12 substdio ss;
13
14 fd = open_write("../lock/tcpto");
15 if (fd == -1) return;
16 substdio_fdbuf(&ss,write,fd,tcpto_cleanbuf,sizeof(tcpto_cleanbuf));
17 for (i = 0;i < sizeof(tcpto_cleanbuf);++i) substdio_put(&ss,"",1);
18 substdio_flush(&ss); /* if it fails, bummer */
19 close(fd);
20}
diff --git a/timeoutconn.c b/timeoutconn.c
new file mode 100644
index 0000000..33a16d9
--- /dev/null
+++ b/timeoutconn.c
@@ -0,0 +1,59 @@
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <netinet/in.h>
4#include <arpa/inet.h>
5#include "ndelay.h"
6#include "select.h"
7#include "error.h"
8#include "readwrite.h"
9#include "ip.h"
10#include "byte.h"
11#include "timeoutconn.h"
12
13int timeoutconn(s,ip,port,timeout)
14int s;
15struct ip_address *ip;
16unsigned int port;
17int timeout;
18{
19 char ch;
20 struct sockaddr_in sin;
21 char *x;
22 fd_set wfds;
23 struct timeval tv;
24
25 byte_zero(&sin,sizeof(sin));
26 byte_copy(&sin.sin_addr,4,ip);
27 x = (char *) &sin.sin_port;
28 x[1] = port; port >>= 8; x[0] = port;
29 sin.sin_family = AF_INET;
30
31 if (ndelay_on(s) == -1) return -1;
32
33 /* XXX: could bind s */
34
35 if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) {
36 ndelay_off(s);
37 return 0;
38 }
39 if ((errno != error_inprogress) && (errno != error_wouldblock)) return -1;
40
41 FD_ZERO(&wfds);
42 FD_SET(s,&wfds);
43 tv.tv_sec = timeout; tv.tv_usec = 0;
44
45 if (select(s + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1) return -1;
46 if (FD_ISSET(s,&wfds)) {
47 int dummy;
48 dummy = sizeof(sin);
49 if (getpeername(s,(struct sockaddr *) &sin,&dummy) == -1) {
50 read(s,&ch,1);
51 return -1;
52 }
53 ndelay_off(s);
54 return 0;
55 }
56
57 errno = error_timeout; /* note that connect attempt is continuing */
58 return -1;
59}
diff --git a/timeoutconn.h b/timeoutconn.h
new file mode 100644
index 0000000..88aab06
--- /dev/null
+++ b/timeoutconn.h
@@ -0,0 +1,6 @@
1#ifndef TIMEOUTCONN_H
2#define TIMEOUTCONN_H
3
4extern int timeoutconn();
5
6#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 @@
1#include "timeoutread.h"
2#include "select.h"
3#include "error.h"
4#include "readwrite.h"
5
6int timeoutread(t,fd,buf,len) int t; int fd; char *buf; int len;
7{
8 fd_set rfds;
9 struct timeval tv;
10
11 tv.tv_sec = t;
12 tv.tv_usec = 0;
13
14 FD_ZERO(&rfds);
15 FD_SET(fd,&rfds);
16
17 if (select(fd + 1,&rfds,(fd_set *) 0,(fd_set *) 0,&tv) == -1) return -1;
18 if (FD_ISSET(fd,&rfds)) return read(fd,buf,len);
19
20 errno = error_timeout;
21 return -1;
22}
diff --git a/timeoutread.h b/timeoutread.h
new file mode 100644
index 0000000..20d3bfc
--- /dev/null
+++ b/timeoutread.h
@@ -0,0 +1,6 @@
1#ifndef TIMEOUTREAD_H
2#define TIMEOUTREAD_H
3
4extern int timeoutread();
5
6#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 @@
1#include "timeoutwrite.h"
2#include "select.h"
3#include "error.h"
4#include "readwrite.h"
5
6int timeoutwrite(t,fd,buf,len) int t; int fd; char *buf; int len;
7{
8 fd_set wfds;
9 struct timeval tv;
10
11 tv.tv_sec = t;
12 tv.tv_usec = 0;
13
14 FD_ZERO(&wfds);
15 FD_SET(fd,&wfds);
16
17 if (select(fd + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1) return -1;
18 if (FD_ISSET(fd,&wfds)) return write(fd,buf,len);
19
20 errno = error_timeout;
21 return -1;
22}
diff --git a/timeoutwrite.h b/timeoutwrite.h
new file mode 100644
index 0000000..4725861
--- /dev/null
+++ b/timeoutwrite.h
@@ -0,0 +1,6 @@
1#ifndef TIMEOUTWRITE_H
2#define TIMEOUTWRITE_H
3
4extern int timeoutwrite();
5
6#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 @@
1#include "stralloc.h"
2#include "alloc.h"
3#include "str.h"
4#include "token822.h"
5#include "gen_allocdefs.h"
6
7static struct token822 comma = { TOKEN822_COMMA };
8
9void token822_reverse(ta)
10token822_alloc *ta;
11{
12 int i;
13 int n;
14 struct token822 temp;
15
16 n = ta->len - 1;
17 for (i = 0;i + i < n;++i)
18 {
19 temp = ta->t[i];
20 ta->t[i] = ta->t[n - i];
21 ta->t[n - i] = temp;
22 }
23}
24
25GEN_ALLOC_ready(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_ready)
26GEN_ALLOC_readyplus(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus)
27GEN_ALLOC_append(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus,token822_append)
28
29static int needspace(t1,t2)
30int t1;
31int t2;
32{
33 if (!t1) return 0;
34 if (t1 == TOKEN822_COLON) return 1;
35 if (t1 == TOKEN822_COMMA) return 1;
36 if (t2 == TOKEN822_LEFT) return 1;
37 switch(t1)
38 {
39 case TOKEN822_ATOM: case TOKEN822_LITERAL:
40 case TOKEN822_QUOTE: case TOKEN822_COMMENT:
41 switch(t2)
42 {
43 case TOKEN822_ATOM: case TOKEN822_LITERAL:
44 case TOKEN822_QUOTE: case TOKEN822_COMMENT:
45 return 1;
46 }
47 }
48 return 0;
49}
50
51static int atomok(ch)
52char ch;
53{
54 switch(ch)
55 {
56 case ' ': case '\t': case '\r': case '\n':
57 case '(': case '[': case '"':
58 case '<': case '>': case ';': case ':':
59 case '@': case ',': case '.':
60 return 0;
61 }
62 return 1;
63}
64
65static void atomcheck(t)
66struct token822 *t;
67{
68 int i;
69 char ch;
70 for (i = 0;i < t->slen;++i)
71 {
72 ch = t->s[i];
73 if ((ch < 32) || (ch > 126) || (ch == ')') || (ch == ']') || (ch == '\\'))
74 {
75 t->type = TOKEN822_QUOTE;
76 return;
77 }
78 }
79}
80
81int token822_unparse(sa,ta,linelen)
82stralloc *sa;
83token822_alloc *ta;
84unsigned int linelen;
85{
86 struct token822 *t;
87 int len;
88 int ch;
89 int i;
90 int j;
91 int lasttype;
92 int newtype;
93 char *s;
94 char *lineb;
95 char *linee;
96
97 len = 0;
98 lasttype = 0;
99 for (i = 0;i < ta->len;++i)
100 {
101 t = ta->t + i;
102 newtype = t->type;
103 if (needspace(lasttype,newtype))
104 ++len;
105 lasttype = newtype;
106 switch(newtype)
107 {
108 case TOKEN822_COMMA:
109 len += 3; break;
110 case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: case TOKEN822_RIGHT:
111 case TOKEN822_SEMI: case TOKEN822_COLON:
112 ++len; break;
113 case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
114 if (t->type != TOKEN822_ATOM) len += 2;
115 for (j = 0;j < t->slen;++j)
116 switch(ch = t->s[j])
117 {
118 case '"': case '[': case ']': case '(': case ')':
119 case '\\': case '\r': case '\n': ++len;
120 default: ++len;
121 }
122 break;
123 }
124 }
125 len += 2;
126
127 if (!stralloc_ready(sa,len))
128 return -1;
129
130 s = sa->s;
131 lineb = s;
132 linee = 0;
133
134 lasttype = 0;
135 for (i = 0;i < ta->len;++i)
136 {
137 t = ta->t + i;
138 newtype = t->type;
139 if (needspace(lasttype,newtype))
140 *s++ = ' ';
141 lasttype = newtype;
142 switch(newtype)
143 {
144 case TOKEN822_COMMA:
145 *s++ = ',';
146#define NSUW \
147 s[0] = '\n'; s[1] = ' '; \
148 if (linee && (!linelen || (s - lineb <= linelen))) \
149 { while (linee < s) { linee[0] = linee[2]; ++linee; } linee -= 2; } \
150 else { if (linee) lineb = linee + 1; linee = s; s += 2; }
151 NSUW
152 break;
153 case TOKEN822_AT: *s++ = '@'; break;
154 case TOKEN822_DOT: *s++ = '.'; break;
155 case TOKEN822_LEFT: *s++ = '<'; break;
156 case TOKEN822_RIGHT: *s++ = '>'; break;
157 case TOKEN822_SEMI: *s++ = ';'; break;
158 case TOKEN822_COLON: *s++ = ':'; break;
159 case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
160 if (t->type == TOKEN822_QUOTE) *s++ = '"';
161 if (t->type == TOKEN822_LITERAL) *s++ = '[';
162 if (t->type == TOKEN822_COMMENT) *s++ = '(';
163 for (j = 0;j < t->slen;++j)
164 switch(ch = t->s[j])
165 {
166 case '"': case '[': case ']': case '(': case ')':
167 case '\\': case '\r': case '\n': *s++ = '\\';
168 default: *s++ = ch;
169 }
170 if (t->type == TOKEN822_QUOTE) *s++ = '"';
171 if (t->type == TOKEN822_LITERAL) *s++ = ']';
172 if (t->type == TOKEN822_COMMENT) *s++ = ')';
173 break;
174 }
175 }
176 NSUW
177 --s;
178 sa->len = s - sa->s;
179 return 1;
180}
181
182int token822_unquote(sa,ta)
183stralloc *sa;
184token822_alloc *ta;
185{
186 struct token822 *t;
187 int len;
188 int i;
189 int j;
190 char *s;
191
192 len = 0;
193 for (i = 0;i < ta->len;++i)
194 {
195 t = ta->t + i;
196 switch(t->type)
197 {
198 case TOKEN822_COMMA: case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT:
199 case TOKEN822_RIGHT: case TOKEN822_SEMI: case TOKEN822_COLON:
200 ++len; break;
201 case TOKEN822_LITERAL:
202 len += 2;
203 case TOKEN822_ATOM: case TOKEN822_QUOTE:
204 len += t->slen;
205 }
206 }
207
208 if (!stralloc_ready(sa,len))
209 return -1;
210
211 s = sa->s;
212
213 for (i = 0;i < ta->len;++i)
214 {
215 t = ta->t + i;
216 switch(t->type)
217 {
218 case TOKEN822_COMMA: *s++ = ','; break;
219 case TOKEN822_AT: *s++ = '@'; break;
220 case TOKEN822_DOT: *s++ = '.'; break;
221 case TOKEN822_LEFT: *s++ = '<'; break;
222 case TOKEN822_RIGHT: *s++ = '>'; break;
223 case TOKEN822_SEMI: *s++ = ';'; break;
224 case TOKEN822_COLON: *s++ = ':'; break;
225 case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
226 if (t->type == TOKEN822_LITERAL) *s++ = '[';
227 for (j = 0;j < t->slen;++j)
228 *s++ = t->s[j];
229 if (t->type == TOKEN822_LITERAL) *s++ = ']';
230 break;
231 case TOKEN822_COMMENT: break;
232 }
233 }
234 sa->len = s - sa->s;
235 return 1;
236}
237
238int token822_parse(ta,sa,buf)
239token822_alloc *ta;
240stralloc *sa;
241stralloc *buf;
242{
243 int i;
244 int salen;
245 int level;
246 struct token822 *t;
247 int numtoks;
248 int numchars;
249 char *cbuf;
250
251 salen = sa->len;
252
253 numchars = 0;
254 numtoks = 0;
255 for (i = 0;i < salen;++i)
256 switch(sa->s[i])
257 {
258 case '.': case ',': case '@': case '<': case '>': case ':': case ';':
259 ++numtoks; break;
260 case ' ': case '\t': case '\r': case '\n': break;
261 case ')': case ']': return 0;
262 /* other control chars and non-ASCII chars are also bad, in theory */
263 case '(':
264 level = 1;
265 while (level)
266 {
267 if (++i >= salen) return 0;
268 switch(sa->s[i])
269 {
270 case '(': ++level; break;
271 case ')': --level; break;
272 case '\\': if (++i >= salen) return 0;
273 default: ++numchars;
274 }
275 }
276 ++numtoks;
277 break;
278 case '"':
279 level = 1;
280 while (level)
281 {
282 if (++i >= salen) return 0;
283 switch(sa->s[i])
284 {
285 case '"': --level; break;
286 case '\\': if (++i >= salen) return 0;
287 default: ++numchars;
288 }
289 }
290 ++numtoks;
291 break;
292 case '[':
293 level = 1;
294 while (level)
295 {
296 if (++i >= salen) return 0;
297 switch(sa->s[i])
298 {
299 case ']': --level; break;
300 case '\\': if (++i >= salen) return 0;
301 default: ++numchars;
302 }
303 }
304 ++numtoks;
305 break;
306 default:
307 do
308 {
309 if (sa->s[i] == '\\') if (++i >= salen) break;
310 ++numchars;
311 if (++i >= salen)
312 break;
313 }
314 while (atomok(sa->s[i]));
315 --i;
316 ++numtoks;
317 }
318
319 if (!token822_ready(ta,numtoks))
320 return -1;
321 if (!stralloc_ready(buf,numchars))
322 return -1;
323 cbuf = buf->s;
324 ta->len = numtoks;
325
326 t = ta->t;
327 for (i = 0;i < salen;++i)
328 switch(sa->s[i])
329 {
330 case '.': t->type = TOKEN822_DOT; ++t; break;
331 case ',': t->type = TOKEN822_COMMA; ++t; break;
332 case '@': t->type = TOKEN822_AT; ++t; break;
333 case '<': t->type = TOKEN822_LEFT; ++t; break;
334 case '>': t->type = TOKEN822_RIGHT; ++t; break;
335 case ':': t->type = TOKEN822_COLON; ++t; break;
336 case ';': t->type = TOKEN822_SEMI; ++t; break;
337 case ' ': case '\t': case '\r': case '\n': break;
338 case '(':
339 t->type = TOKEN822_COMMENT; t->s = cbuf; t->slen = 0;
340 level = 1;
341 while (level)
342 {
343 ++i; /* assert: < salen */
344 switch(sa->s[i])
345 {
346 case '(': ++level; break;
347 case ')': --level; break;
348 case '\\': ++i; /* assert: < salen */
349 default: *cbuf++ = sa->s[i]; ++t->slen;
350 }
351 }
352 ++t;
353 break;
354 case '"':
355 t->type = TOKEN822_QUOTE; t->s = cbuf; t->slen = 0;
356 level = 1;
357 while (level)
358 {
359 ++i; /* assert: < salen */
360 switch(sa->s[i])
361 {
362 case '"': --level; break;
363 case '\\': ++i; /* assert: < salen */
364 default: *cbuf++ = sa->s[i]; ++t->slen;
365 }
366 }
367 ++t;
368 break;
369 case '[':
370 t->type = TOKEN822_LITERAL; t->s = cbuf; t->slen = 0;
371 level = 1;
372 while (level)
373 {
374 ++i; /* assert: < salen */
375 switch(sa->s[i])
376 {
377 case ']': --level; break;
378 case '\\': ++i; /* assert: < salen */
379 default: *cbuf++ = sa->s[i]; ++t->slen;
380 }
381 }
382 ++t;
383 break;
384 default:
385 t->type = TOKEN822_ATOM; t->s = cbuf; t->slen = 0;
386 do
387 {
388 if (sa->s[i] == '\\') if (++i >= salen) break;
389 *cbuf++ = sa->s[i]; ++t->slen;
390 if (++i >= salen)
391 break;
392 }
393 while (atomok(sa->s[i]));
394 atomcheck(t);
395 --i;
396 ++t;
397 }
398 return 1;
399}
400
401static int gotaddr(taout,taaddr,callback)
402token822_alloc *taout;
403token822_alloc *taaddr;
404int (*callback)();
405{
406 int i;
407
408 if (callback(taaddr) != 1)
409 return 0;
410
411 if (!token822_readyplus(taout,taaddr->len))
412 return 0;
413
414 for (i = 0;i < taaddr->len;++i)
415 taout->t[taout->len++] = taaddr->t[i];
416
417 taaddr->len = 0;
418 return 1;
419}
420
421int token822_addrlist(taout,taaddr,ta,callback)
422token822_alloc *taout;
423token822_alloc *taaddr;
424token822_alloc *ta;
425int (*callback)();
426{
427 struct token822 *t;
428 struct token822 *beginning;
429 int ingroup;
430 int wordok;
431
432 taout->len = 0;
433 taaddr->len = 0;
434
435 if (!token822_readyplus(taout,1)) return -1;
436 if (!token822_readyplus(taaddr,1)) return -1;
437
438 ingroup = 0;
439 wordok = 1;
440
441 beginning = ta->t + 2;
442 t = ta->t + ta->len - 1;
443
444 /* rfc 822 address lists are easy to parse from right to left */
445
446#define FLUSH if (taaddr->len) if (!gotaddr(taout,taaddr,callback)) return -1;
447#define FLUSHCOMMA if (taaddr->len) { \
448if (!gotaddr(taout,taaddr,callback)) return -1; \
449if (!token822_append(taout,&comma)) return -1; }
450#define ADDRLEFT if (!token822_append(taaddr,t--)) return -1;
451#define OUTLEFT if (!token822_append(taout,t--)) return -1;
452
453 while (t >= beginning)
454 {
455 switch(t->type)
456 {
457 case TOKEN822_SEMI:
458 FLUSHCOMMA
459 if (ingroup) return 0;
460 ingroup = 1;
461 wordok = 1;
462 break;
463 case TOKEN822_COLON:
464 FLUSH
465 if (!ingroup) return 0;
466 ingroup = 0;
467 while ((t >= beginning) && (t->type != TOKEN822_COMMA))
468 OUTLEFT
469 if (t >= beginning)
470 OUTLEFT
471 wordok = 1;
472 continue;
473 case TOKEN822_RIGHT:
474 FLUSHCOMMA
475 OUTLEFT
476 while ((t >= beginning) && (t->type != TOKEN822_LEFT))
477 ADDRLEFT
478 /* important to use address here even if it's empty: <> */
479 if (!gotaddr(taout,taaddr,callback)) return -1;
480 if (t < beginning) return 0;
481 OUTLEFT
482 while ((t >= beginning) && ((t->type == TOKEN822_COMMENT) || (t->type == TOKEN822_ATOM) || (t->type == TOKEN822_QUOTE) || (t->type == TOKEN822_AT) || (t->type == TOKEN822_DOT)))
483 OUTLEFT
484 wordok = 0;
485 continue;
486 case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
487 if (!wordok)
488 FLUSHCOMMA
489 wordok = 0;
490 ADDRLEFT
491 continue;
492 case TOKEN822_COMMENT:
493 /* comment is lexically a space; shouldn't affect wordok */
494 break;
495 case TOKEN822_COMMA:
496 FLUSH
497 wordok = 1;
498 break;
499 default:
500 wordok = 1;
501 ADDRLEFT
502 continue;
503 }
504 OUTLEFT
505 }
506 FLUSH
507 ++t;
508 while (t > ta->t)
509 if (!token822_append(taout,--t)) return -1;
510
511 token822_reverse(taout);
512 return 1;
513}
diff --git a/token822.h b/token822.h
new file mode 100644
index 0000000..9ca35cf
--- /dev/null
+++ b/token822.h
@@ -0,0 +1,37 @@
1#ifndef TOKEN822_H
2#define TOKEN822_H
3
4struct token822
5 {
6 int type;
7 char *s;
8 int slen;
9 }
10;
11
12#include "gen_alloc.h"
13GEN_ALLOC_typedef(token822_alloc,struct token822,t,len,a)
14
15extern int token822_parse();
16extern int token822_addrlist();
17extern int token822_unquote();
18extern int token822_unparse();
19extern void token822_free();
20extern void token822_reverse();
21extern int token822_ready();
22extern int token822_readyplus();
23extern int token822_append();
24
25#define TOKEN822_ATOM 1
26#define TOKEN822_QUOTE 2
27#define TOKEN822_LITERAL 3
28#define TOKEN822_COMMENT 4
29#define TOKEN822_LEFT 5
30#define TOKEN822_RIGHT 6
31#define TOKEN822_AT 7
32#define TOKEN822_COMMA 8
33#define TOKEN822_SEMI 9
34#define TOKEN822_COLON 10
35#define TOKEN822_DOT 11
36
37#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 @@
1#include "select.h"
2#include "open.h"
3#include "trigger.h"
4#include "hasnpbg1.h"
5
6static int fd = -1;
7#ifdef HASNAMEDPIPEBUG1
8static int fdw = -1;
9#endif
10
11void trigger_set()
12{
13 if (fd != -1)
14 close(fd);
15#ifdef HASNAMEDPIPEBUG1
16 if (fdw != -1)
17 close(fdw);
18#endif
19 fd = open_read("lock/trigger");
20#ifdef HASNAMEDPIPEBUG1
21 fdw = open_write("lock/trigger");
22#endif
23}
24
25void trigger_selprep(nfds,rfds)
26int *nfds;
27fd_set *rfds;
28{
29 if (fd != -1)
30 {
31 FD_SET(fd,rfds);
32 if (*nfds < fd + 1) *nfds = fd + 1;
33 }
34}
35
36int trigger_pulled(rfds)
37fd_set *rfds;
38{
39 if (fd != -1) if (FD_ISSET(fd,rfds)) return 1;
40 return 0;
41}
diff --git a/trigger.h b/trigger.h
new file mode 100644
index 0000000..dec24ef
--- /dev/null
+++ b/trigger.h
@@ -0,0 +1,8 @@
1#ifndef TRIGGER_H
2#define TRIGGER_H
3
4extern void trigger_set();
5extern void trigger_selprep();
6extern int trigger_pulled();
7
8#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 @@
1#include "ndelay.h"
2#include "open.h"
3#include "triggerpull.h"
4
5void triggerpull()
6{
7 int fd;
8
9 fd = open_write("lock/trigger");
10 if (fd >= 0)
11 {
12 ndelay_on(fd);
13 write(fd,"",1); /* if it fails, bummer */
14 close(fd);
15 }
16}
diff --git a/triggerpull.h b/triggerpull.h
new file mode 100644
index 0000000..6d097bb
--- /dev/null
+++ b/triggerpull.h
@@ -0,0 +1,6 @@
1#ifndef TRIGGERPULL_H
2#define TRIGGERPULL_H
3
4extern void triggerpull();
5
6#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 @@
1void main()
2{
3#ifdef NeXT
4 printf("nextstep\n"); exit(0);
5#endif
6 printf("unknown\n"); exit(0);
7}
diff --git a/trydrent.c b/trydrent.c
new file mode 100644
index 0000000..c778176
--- /dev/null
+++ b/trydrent.c
@@ -0,0 +1,8 @@
1#include <sys/types.h>
2#include <dirent.h>
3
4void foo()
5{
6 DIR *dir;
7 struct dirent *d;
8}
diff --git a/tryflock.c b/tryflock.c
new file mode 100644
index 0000000..8c8aa76
--- /dev/null
+++ b/tryflock.c
@@ -0,0 +1,8 @@
1#include <sys/types.h>
2#include <sys/file.h>
3#include <fcntl.h>
4
5void main()
6{
7 flock(0,LOCK_EX | LOCK_UN | LOCK_NB);
8}
diff --git a/trylsock.c b/trylsock.c
new file mode 100644
index 0000000..fbce408
--- /dev/null
+++ b/trylsock.c
@@ -0,0 +1,4 @@
1main()
2{
3 ;
4}
diff --git a/trymkffo.c b/trymkffo.c
new file mode 100644
index 0000000..0b119c6
--- /dev/null
+++ b/trymkffo.c
@@ -0,0 +1,7 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3
4void main()
5{
6 mkfifo("temp-trymkffo",0);
7}
diff --git a/trynpbg1.c b/trynpbg1.c
new file mode 100644
index 0000000..9d5f80b
--- /dev/null
+++ b/trynpbg1.c
@@ -0,0 +1,26 @@
1#include "select.h"
2#include "open.h"
3#include "fifo.h"
4
5#define FN "temp-trynpbg1.fifo"
6
7void main()
8{
9 int flagbug;
10 struct timeval instant;
11 fd_set rfds;
12
13 flagbug = 0;
14 if (fifo_make(FN,0600) != -1) {
15 close(0);
16 if (open_read(FN) == 0) {
17 FD_ZERO(&rfds);
18 FD_SET(0,&rfds);
19 instant.tv_sec = instant.tv_usec = 0;
20 if (select(1,&rfds,(fd_set *) 0,(fd_set *) 0,&instant) > 0)
21 flagbug = 1;
22 }
23 unlink(FN);
24 }
25 _exit(!flagbug);
26}
diff --git a/tryrsolv.c b/tryrsolv.c
new file mode 100644
index 0000000..fbce408
--- /dev/null
+++ b/tryrsolv.c
@@ -0,0 +1,4 @@
1main()
2{
3 ;
4}
diff --git a/trysalen.c b/trysalen.c
new file mode 100644
index 0000000..731a109
--- /dev/null
+++ b/trysalen.c
@@ -0,0 +1,11 @@
1#include <sys/types.h>
2#include <sys/param.h>
3#include <sys/time.h>
4#include <sys/ioctl.h>
5#include <sys/socket.h>
6
7void foo()
8{
9 struct sockaddr sa;
10 sa.sa_len = 0;
11}
diff --git a/trysgact.c b/trysgact.c
new file mode 100644
index 0000000..263cb21
--- /dev/null
+++ b/trysgact.c
@@ -0,0 +1,10 @@
1#include <signal.h>
2
3void main()
4{
5 struct sigaction sa;
6 sa.sa_handler = 0;
7 sa.sa_flags = 0;
8 sigemptyset(&sa.sa_mask);
9 sigaction(0,&sa,(struct sigaction *) 0);
10}
diff --git a/trysgprm.c b/trysgprm.c
new file mode 100644
index 0000000..ed28857
--- /dev/null
+++ b/trysgprm.c
@@ -0,0 +1,10 @@
1#include <signal.h>
2
3void main()
4{
5 sigset_t ss;
6
7 sigemptyset(&ss);
8 sigaddset(&ss,SIGCHLD);
9 sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
10}
diff --git a/tryshsgr.c b/tryshsgr.c
new file mode 100644
index 0000000..807e15d
--- /dev/null
+++ b/tryshsgr.c
@@ -0,0 +1,14 @@
1void main()
2{
3 short x[4];
4
5 x[0] = x[1] = 1;
6 if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
7
8 if (getgroups(1,x) == -1) _exit(1);
9 if (x[1] != 1) _exit(1);
10 x[1] = 2;
11 if (getgroups(1,x) == -1) _exit(1);
12 if (x[1] != 2) _exit(1);
13 _exit(0);
14}
diff --git a/trysysel.c b/trysysel.c
new file mode 100644
index 0000000..f6ed055
--- /dev/null
+++ b/trysysel.c
@@ -0,0 +1,8 @@
1#include <sys/types.h>
2#include <sys/time.h>
3#include <sys/select.h> /* SVR4 silliness */
4
5void foo()
6{
7 ;
8}
diff --git a/trysyslog.c b/trysyslog.c
new file mode 100644
index 0000000..4b99afc
--- /dev/null
+++ b/trysyslog.c
@@ -0,0 +1,9 @@
1#include <sys/types.h>
2#include <sys/time.h>
3#include <syslog.h>
4
5main()
6{
7 openlog("foo",0,LOG_MAIL);
8 syslog(0,"foo");
9}
diff --git a/tryulong32.c b/tryulong32.c
new file mode 100644
index 0000000..a108076
--- /dev/null
+++ b/tryulong32.c
@@ -0,0 +1,11 @@
1void main()
2{
3 unsigned long u;
4 u = 1;
5 u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
6 u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
7 u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
8 u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
9 if (!u) _exit(0);
10 _exit(1);
11}
diff --git a/tryvfork.c b/tryvfork.c
new file mode 100644
index 0000000..21387e4
--- /dev/null
+++ b/tryvfork.c
@@ -0,0 +1,4 @@
1void main()
2{
3 vfork();
4}
diff --git a/trywaitp.c b/trywaitp.c
new file mode 100644
index 0000000..7e73bfa
--- /dev/null
+++ b/trywaitp.c
@@ -0,0 +1,7 @@
1#include <sys/types.h>
2#include <sys/wait.h>
3
4void main()
5{
6 waitpid(0,0,0);
7}
diff --git a/uint32.h1 b/uint32.h1
new file mode 100644
index 0000000..6599aa0
--- /dev/null
+++ b/uint32.h1
@@ -0,0 +1,6 @@
1#ifndef UINT32_H
2#define UINT32_H
3
4typedef unsigned int uint32;
5
6#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 @@
1#ifndef UINT32_H
2#define UINT32_H
3
4typedef unsigned long uint32;
5
6#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 @@
1.TH wait 3
2.SH NAME
3wait \- check child process status
4.SH SYNTAX
5.B #include <wait.h>
6
7int \fBwait_nohang\fP(&\fIwstat\fR);
8.br
9int \fBwait_stop\fP(&\fIwstat\fR);
10.br
11int \fBwait_stopnohang\fP(&\fIwstat\fR);
12.br
13int \fBwait_pid\fP(&\fIwstat\fR,\fIpid\fR);
14
15int \fBwait_exitcode\fP(\fIwstat\fR);
16.br
17int \fBwait_crashed\fP(\fIwstat\fR);
18.br
19int \fBwait_stopped\fP(\fIwstat\fR);
20.br
21int \fBwait_stopsig\fP(\fIwstat\fR);
22
23int \fIpid\fR;
24.br
25int \fIwstat\fR;
26.SH DESCRIPTION
27.B wait_nohang
28looks for zombies (child processes that have exited).
29If it sees a zombie,
30it eliminates the zombie,
31puts the zombie's exit status into
32.IR wstat ,
33and returns the zombie's process ID.
34If there are several zombies,
35.B wait_nohang
36picks one.
37If there are children but no zombies,
38.B wait_nohang
39returns 0.
40If there are no children,
41.B wait_nohang
42returns -1,
43setting
44.B errno
45appropriately.
46
47.B wait_stopnohang
48is similar to
49.BR wait_nohang ,
50but it also looks for children that have stopped.
51
52.B wait_stop
53is similar to
54.BR wait_stopnohang ,
55but if there are children it will pause waiting for one of them
56to stop or exit.
57
58.B wait_pid
59waits for child process
60.I pid
61to exit.
62It eliminates any zombie that shows up in the meantime,
63discarding the exit status.
64
65.B wait_stop
66and
67.B wait_pid
68retry upon
69.BR error_intr .
70.SH "STATUS PARSING"
71If the child stopped,
72.B wait_stopped
73is nonzero;
74.B wait_stopsig
75is the signal that caused the child to stop.
76
77If the child exited by crashing,
78.B wait_stopped
79is zero;
80.B wait_crashed
81is nonzero.
82
83If the child exited normally,
84.B wait_stopped
85is zero;
86.B wait_crashed
87is zero;
88and
89.B wait_exitcode
90is the child's exit code.
91.SH "SEE ALSO"
92wait(2),
93error(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 @@
1#ifndef WAIT_H
2#define WAIT_H
3
4extern int wait_pid();
5extern int wait_nohang();
6extern int wait_stop();
7extern int wait_stopnohang();
8
9#define wait_crashed(w) ((w) & 127)
10#define wait_exitcode(w) ((w) >> 8)
11#define wait_stopsig(w) ((w) >> 8)
12#define wait_stopped(w) (((w) & 127) == 127)
13
14#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 @@
1#include <sys/types.h>
2#include <sys/wait.h>
3#include "haswaitp.h"
4
5int wait_nohang(wstat) int *wstat;
6{
7#ifdef HASWAITPID
8 return waitpid(-1,wstat,WNOHANG);
9#else
10 return wait3(wstat,WNOHANG,(struct rusage *) 0);
11#endif
12}
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 @@
1#include <sys/types.h>
2#include <sys/wait.h>
3#include "error.h"
4#include "haswaitp.h"
5
6#ifdef HASWAITPID
7
8int wait_pid(wstat,pid) int *wstat; int pid;
9{
10 int r;
11
12 do
13 r = waitpid(pid,wstat,0);
14 while ((r == -1) && (errno == error_intr));
15 return r;
16}
17
18#else
19
20/* XXX untested */
21/* XXX breaks down with more than two children */
22static int oldpid = 0;
23static int oldwstat; /* defined if(oldpid) */
24
25int wait_pid(wstat,pid) int *wstat; int pid;
26{
27 int r;
28
29 if (pid == oldpid) { *wstat = oldwstat; oldpid = 0; return pid; }
30
31 do {
32 r = wait(wstat);
33 if ((r != pid) && (r != -1)) { oldwstat = *wstat; oldpid = r; continue; }
34 }
35 while ((r == -1) && (errno == error_intr));
36 return r;
37}
38
39#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 @@
1#!/bin/sh
2# 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 @@
1Oops. Your getgroups() returned 0, and setgroups() failed; this means
2that I can't reliably do my shsgr test. Please either ``make'' as root
3or ``make'' while you're in one or more supplementary groups.